Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11239] ファイルの概要

このコミットは、Go言語のテストインフラストラクチャにおける重要な改善を導入しています。具体的には、これまで複数のテストの期待される出力を一元的に管理していた golden.out ファイルを廃止し、各テストが個別の期待出力ファイル (.out 拡張子) を持つように変更しています。これにより、特に gccgo のような代替コンパイラでのテスト結果の検証が容易になり、テストの独立性と保守性が向上しました。

コミット

commit 5e77b009d0e9dc8c92fe91d0a2d3182e9fff10ae
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Jan 18 16:12:24 2012 -0800

    test: split golden.out into expected output per test
    
    This will permit gccgo to check test output.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5554056

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5e77b009d0e9dc8c92fe91d0a2d3182e9fff10ae

元コミット内容

test: split golden.out into expected output per test

This will permit gccgo to check test output.

変更の背景

Go言語のテストスイートでは、プログラムの標準出力や標準エラー出力を検証する形式のテストが多数存在します。以前は、これらのテストの期待される出力が golden.out という単一のファイルにまとめられていました。このアプローチにはいくつかの課題がありました。

  1. 保守性の問題: golden.out は非常に大きなファイルになりがちで、特定のテストの出力を変更する際に、ファイル全体を編集する必要がありました。これにより、意図しない変更が混入するリスクや、変更のレビューが困難になる問題がありました。
  2. 並行性の問題: 複数のテストが同時に実行される環境では、単一の golden.out ファイルへの書き込みや読み込みが競合を引き起こす可能性がありました。
  3. コンパイラ間の差異への対応: Go言語には公式コンパイラである gc (Go Compiler) の他に、GCCをバックエンドとする gccgo のような代替コンパイラが存在します。これらのコンパイラは、最適化やランタイムの挙動の違いにより、全く同じソースコードからでも微妙に異なる出力を生成する場合があります。単一の golden.out ファイルでは、gcgccgo の両方に対応した期待出力を管理するのが困難でした。特に gccgo がテスト出力を検証できるようにするためには、より柔軟なメカニズムが必要とされていました。

このコミットは、これらの課題を解決し、特に gccgo がGoのテストスイートをより効果的に利用できるようにするために、テスト出力の管理方法を根本的に変更することを目的としています。

前提知識の解説

Go言語のテストフレームワーク

Go言語は、標準ライブラリに testing パッケージを提供しており、ユニットテストやベンチマークテストを簡単に記述できます。しかし、コンパイラやランタイムの挙動を検証するような低レベルのテストでは、Goプログラムをコンパイル・実行し、その標準出力や標準エラー出力を外部のシェルコマンドで検証する手法が用いられることがあります。これは、Goの testing パッケージが提供するアサーション機能だけでは表現しきれない、より複雑な実行環境の検証に適しています。

golden.out ファイル

golden.out は、Goのテストスイートにおいて、複数のテストケースの期待される標準出力(または標準エラー出力)を連結して格納していたファイルです。各テストの出力は、ファイル内で特定の区切り文字(例: =========== ./testname.go)によって識別されていました。テスト実行時には、各テストの実際の出力をこの golden.out の対応するセクションと比較することで、テストの合否を判定していました。

gccgo

gccgo は、Go言語のプログラムをコンパイルするための代替コンパイラです。Goの公式コンパイラである gc とは異なり、gccgo はGCC (GNU Compiler Collection) のフロントエンドとして実装されており、GCCの最適化バックエンドを利用します。これにより、gccgo は特定のプラットフォームで gc よりも優れたパフォーマンスを発揮したり、既存のGCCベースのツールチェーンとの統合が容易になったりする利点があります。Go言語のエコシステムでは、gcgccgo の両方でGoプログラムが正しく動作することを保証することが重要であり、そのためには両方のコンパイラでテストスイートが実行可能である必要があります。

cmp コマンド

cmp はUnix系のオペレーティングシステムで利用できる標準的なコマンドラインユーティリティです。2つのファイルをバイト単位で比較し、差異があればその情報を出力し、終了ステータスを非ゼロに設定します。差異がなければ何も出力せず、終了ステータスをゼロに設定します。この特性から、シェルスクリプトでファイルの同一性を検証する際によく利用されます。

シェルスクリプトにおける $G, $D, $F, $L, $A

Goのテストスクリプトでは、テストのコンパイルと実行を自動化するために、特定のシェル変数を使用することが一般的です。

  • $G: Goコンパイラへのパスまたはコマンド(例: go tool compile または gccgo)。
  • $D: 現在のテストファイルが存在するディレクトリへのパス。
  • $F: 現在のテストファイルのファイル名(拡張子を含む)。
  • $L: Goリンカへのパスまたはコマンド(例: go tool link)。
  • $A: コンパイルされた実行可能ファイルのデフォルト名(通常は a.out)。

これらの変数を組み合わせることで、テストスクリプトは柔軟にコンパイルと実行のコマンドを構築できます。

技術的詳細

このコミットの主要な技術的変更点は、テストの実行方法と期待出力の管理方法にあります。

  1. golden.out の廃止: 以前は単一の test/golden.out ファイルに集約されていたすべてのテストの期待出力が削除されました。これは、このファイルがもはやテストの検証に使用されないことを意味します。

  2. テストスクリプトの変更: 各テストファイル (.go ファイル) の先頭にあるコメント行が変更されました。このコメント行は、テストハーネスがテストを実行する際に使用するシェルコマンドを定義しています。

    • 変更前:

      // $G $D/$F.go && $L $F.$A && ./$A.out
      

      このコマンドは、Goソースファイルをコンパイルし、リンクして実行可能ファイルを生成し、その実行可能ファイルを実行するだけでした。出力の検証は、テストハーネスの外部ロジックで行われていました。

    • 変更後:

      // $G $D/$F.go && $L $F.$A && ./$A.out 2>&1 | cmp - $D/$F.out
      

      この新しいコマンドは、以下のステップを実行します。

      • $G $D/$F.go && $L $F.$A: 以前と同様にGoソースファイルをコンパイルし、リンクします。
      • ./$A.out: 生成された実行可能ファイルを実行します。
      • 2>&1: 実行可能ファイルの標準エラー出力 (stderr) を標準出力 (stdout) にリダイレクトします。これにより、プログラムが標準出力と標準エラー出力の両方に出力する場合でも、それらをまとめて処理できます。
      • | cmp - $D/$F.out: 実行可能ファイルの結合された出力(標準出力と標準エラー出力)を cmp コマンドの標準入力 (-) にパイプします。cmp コマンドは、この入力と、現在のテストファイルと同じディレクトリにある対応する .out ファイル ($D/$F.out) の内容を比較します。
  3. 個別 .out ファイルの導入: test/deferprint.out, test/goprint.out, test/helloworld.out など、各テストファイルに対応する新しい .out ファイルが作成されました。これらのファイルには、それぞれのテストが生成すべき期待される出力が格納されています。cmp コマンドは、テストの実際の出力とこれらの個別の .out ファイルの内容を比較することで、テストの合否を判定します。

  4. テストロジックの調整: 一部のテストファイル (test/ken/cplx3.go, test/ken/cplx5.go, test/ken/intervar.go, test/ken/string.go) では、print ステートメントに依存する代わりに、より明示的な値の検証や panic を使用するように内部ロジックが変更されています。これは、出力比較に加えて、テスト内部での厳密なアサーションを強化する意図があると考えられます。例えば、cplx3.gocplx5.go では、複素数計算の結果が期待値と一定の誤差範囲内で一致するかを if 文と panic で確認しています。intervar.go では、インターフェースメソッドが文字列を返すように変更され、その戻り値が期待される文字列と一致するかを検証しています。これにより、テストの堅牢性が向上し、出力形式のわずかな違いに左右されにくくなります。

これらの変更により、各テストは自己完結型となり、自身の期待出力を個別に管理するようになります。これにより、gccgo のような代替コンパイラがGoのテストスイートをより簡単に実行し、その出力を検証できるようになります。gccgo は、gc とは異なる出力形式を持つ可能性があるため、個別の .out ファイルを持つことで、gccgo 固有の期待出力を提供したり、gccgo のテスト実行環境をより柔軟に設定したりすることが可能になります。

コアとなるコードの変更箇所

このコミットのコアとなる変更は、主に以下の2つのパターンに集約されます。

  1. test/golden.out の削除:

    --- a/test/golden.out
    +++ /dev/null
    @@ -1,50 +0,0 @@
    - 
    - == ./
    - 
    - =========== ./deferprint.go
    - printing: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    - 42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
    - 
    - =========== ./goprint.go
    - 42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
    - 
    - =========== ./helloworld.go
    - hello, world
    - 
    - =========== ./printbig.go
    - -9223372036854775808
    - 9223372036854775807
    - 
    - =========== ./sigchld.go
    - survived SIGCHLD
    - 
    - == ken/
    - 
    - =========== ken/cplx0.go
    - (+5.000000e+000+6.000000e+000i)
    - (+5.000000e+000+6.000000e+000i)
    - (+5.000000e+000+6.000000e+000i)
    - (+5.000000e+000+6.000000e+000i)
    - 
    - =========== ken/cplx3.go
    - (+1.292308e+000-1.384615e-001i)
    - (+1.292308e+000-1.384615e-001i)
    - 
    - =========== ken/cplx5.go
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - 
    - =========== ken/intervar.go
    -  print 1 bio 2 file 3 -- abc
    - 
    - =========== ken/string.go
    - abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz
    - 
    - == chan/
    - 
    - == interface/
    - @@ -55,10 +13,4 @@ abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz
    - 
    - == fixedbugs/
    - 
    - =========== fixedbugs/bug067.go
    - ok
    - 
    - =========== fixedbugs/bug328.go
    - 0x0
    - 
    - == bugs/
    

    golden.out ファイルの内容がすべて削除されています。

  2. テストファイルの実行コマンドの変更と対応する .out ファイルの追加: 例として test/deferprint.gotest/deferprint.out の変更を示します。

    test/deferprint.go の変更:

    --- a/test/deferprint.go
    +++ b/test/deferprint.go
    @@ -1,4 +1,4 @@
    -// $G $D/$F.go && $L $F.$A && ./$A.out
    +// $G $D/$F.go && $L $F.$A && ./$A.out 2>&1 | cmp - $D/$F.out
     
     // Copyright 2010 The Go Authors.  All rights reserved.
     // Use of this source code is governed by a BSD-style
    

    test/deferprint.out の追加:

    --- /dev/null
    +++ b/test/deferprint.out
    @@ -0,0 +1,2 @@
    +printing: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    +42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
    

    同様の変更が、test/goprint.go, test/helloworld.go, test/fixedbugs/bug328.go, test/ken/cplx0.go, test/ken/string.go, test/printbig.go, test/sigchld.go およびそれらに対応する .out ファイルに対して行われています。

  3. 一部のテストロジックの変更: 例として test/ken/cplx3.go の変更を示します。

    --- a/test/ken/cplx3.go
    +++ b/test/ken/cplx3.go
    @@ -19,10 +19,29 @@ const (
     func main() {
     	c0 := C1
     	c0 = (c0 + c0 + c0) / (c0 + c0 + 3i)
    -	println(c0)
    +	r, i := real(c0), imag(c0)
    +	d := r - 1.292308
    +	if d < 0 {
    +		d = - d
    +	}
    +	if d > 1e-6 {
    +		println(r, "!= 1.292308")
    +		panic(0)
    +	}
    +	d = i + 0.1384615
    +	if d < 0 {
    +		d = - d
    +	}
    +	if d > 1e-6 {
    +		println(i, "!= -0.1384615")
    +		panic(0)
    +	}
     
     	c := *(*complex128)(unsafe.Pointer(&c0))
    -	println(c)
    +	if c != c0 {
    +		println(c, "!=", c)
    +		panic(0)
    +	}
     
     	var a interface{}
     	switch c := reflect.ValueOf(a); c.Kind() {
    

    println(c0)println(c) の代わりに、計算結果の複素数の実部と虚部を個別に検証し、許容誤差範囲外であれば panic を発生させるように変更されています。これにより、テストの出力に依存せず、プログラム内部で直接的な検証が行われるようになります。

コアとなるコードの解説

このコミットの核心は、Goのテストハーネスがテストの出力を検証する方法を、一元的な golden.out ファイルとの比較から、各テストが自身の期待出力ファイルを持つ独立した比較へと移行させた点にあります。

変更されたテストファイルの先頭にあるコメント行は、Goのテストシステムがテストを実行する際の「レシピ」として機能します。

// $G $D/$F.go && $L $F.$A && ./$A.out 2>&1 | cmp - $D/$F.out

この行は、以下の処理フローを定義しています。

  1. $G $D/$F.go: Goコンパイラ ($G) を使用して、現在のテストファイル ($D/$F.go) をコンパイルします。
  2. && $L $F.$A: コンパイルが成功した場合 (&&)、Goリンカ ($L) を使用して、コンパイルされたオブジェクトファイル ($F.$A は通常、コンパイル後のオブジェクトファイル名を表す) をリンクし、実行可能ファイル (./$A.out) を生成します。
  3. && ./$A.out: リンクが成功した場合 (&&)、生成された実行可能ファイルを実行します。
  4. 2>&1: 実行可能ファイルの標準エラー出力 (ファイルディスクリプタ 2) を標準出力 (ファイルディスクリプタ 1) にリダイレクトします。これにより、テストプログラムが標準出力と標準エラー出力の両方にメッセージを出力する場合でも、それらが単一のストリームとして cmp コマンドに渡されます。
  5. | cmp - $D/$F.out: 実行可能ファイルの結合された出力ストリームを cmp コマンドの標準入力 (-) にパイプします。cmp コマンドは、この標準入力の内容を、現在のテストファイルと同じ名前で .out 拡張子を持つファイル ($D/$F.out) の内容と比較します。

cmp コマンドは、比較結果に基づいて終了ステータスを返します。

  • 0: ファイルが同一である場合。
  • 1: ファイルが異なる場合。
  • 2: アクセスできないファイルなどのエラーが発生した場合。

Goのテストハーネスは、このシェルコマンドの終了ステータスを解釈し、cmp が非ゼロのステータスを返した場合(つまり、出力が期待と異なる場合)にテストを失敗と判断します。

この変更により、各テストは自身の期待出力を個別の .out ファイルとして持つため、テストの独立性が高まります。特定のテストの期待出力を変更する際に、他のテストの期待出力に影響を与える心配がなくなります。また、gccgo のような代替コンパイラがGoのテストスイートをより簡単に利用できるようになります。gccgo は、gc とは異なる出力形式を持つ可能性があるため、個別の .out ファイルを持つことで、gccgo 固有の期待出力を提供したり、gccgo のテスト実行環境をより柔軟に設定したりすることが可能になります。

さらに、一部のテストで print に依存する代わりに panic を使用した明示的な検証が導入されたことは、テストの堅牢性を高めるための追加の改善です。これにより、出力形式のわずかな変更がテストの失敗につながるリスクが減り、テストがより直接的にプログラムのロジックを検証できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に test ディレクトリ): https://github.com/golang/go/tree/master/test
  • cmp コマンドのマニュアルページ (man cmp): https://man7.org/linux/man-pages/man1/cmp.1.html
  • シェルスクリプトのリダイレクトとパイプに関する一般的な情報源。
  • Go言語のコンパイラとツールチェーンに関するドキュメント。
  • Go言語のテストに関する一般的なプラクティスや議論。
  • gccgo の開発に関する情報。
  • Goのテストハーネスがどのように機能するかについての内部ドキュメントやソースコード。
    • Goのテストハーネスの具体的な実装は、Goのソースツリー内の src/cmd/go/test.gosrc/cmd/go/internal/test/test.go などに関連するコードが含まれている可能性があります。
    • Goのテスト実行の仕組みについては、go help testgo doc cmd/go などのコマンドラインヘルプも参考になります。
    • Goのテストの歴史や進化に関するブログ記事やメーリングリストのアーカイブも、背景を理解する上で役立ちます。
    • 特に、Goのテストスイートがどのように構築され、異なるコンパイラ(gcgccgo)をサポートしているかについての情報は、Goの公式ブログや開発者向けメーリングリストで議論されていることがあります。

[インデックス 11239] ファイルの概要

このコミットは、Go言語のテストインフラストラクチャにおける重要な改善を導入しています。具体的には、これまで複数のテストの期待される出力を一元的に管理していた golden.out ファイルを廃止し、各テストが個別の期待出力ファイル (.out 拡張子) を持つように変更しています。これにより、特に gccgo のような代替コンパイラでのテスト結果の検証が容易になり、テストの独立性と保守性が向上しました。

コミット

commit 5e77b009d0e9dc8c92fe91d0a2d3182e9fff10ae
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Jan 18 16:12:24 2012 -0800

    test: split golden.out into expected output per test
    
    This will permit gccgo to check test output.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5554056

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5e77b009d0e9dc8c92fe91d0a2d3182e9fff10ae

元コミット内容

test: split golden.out into expected output per test

This will permit gccgo to check test output.

変更の背景

Go言語のテストスイートでは、プログラムの標準出力や標準エラー出力を検証する形式のテストが多数存在します。以前は、これらのテストの期待される出力が golden.out という単一のファイルにまとめられていました。このアプローチにはいくつかの課題がありました。

  1. 保守性の問題: golden.out は非常に大きなファイルになりがちで、特定のテストの出力を変更する際に、ファイル全体を編集する必要がありました。これにより、意図しない変更が混入するリスクや、変更のレビューが困難になる問題がありました。
  2. 並行性の問題: 複数のテストが同時に実行される環境では、単一の golden.out ファイルへの書き込みや読み込みが競合を引き起こす可能性がありました。
  3. コンパイラ間の差異への対応: Go言語には公式コンパイラである gc (Go Compiler) の他に、GCCをバックエンドとする gccgo のような代替コンパイラが存在します。これらのコンパイラは、最適化やランタイムの挙動の違いにより、全く同じソースコードからでも微妙に異なる出力を生成する場合があります。単一の golden.out ファイルでは、gcgccgo の両方に対応した期待出力を管理するのが困難でした。特に gccgo がテスト出力を検証できるようにするためには、より柔軟なメカニズムが必要とされていました。

このコミットは、これらの課題を解決し、特に gccgo がGoのテストスイートをより効果的に利用できるようにするために、テスト出力の管理方法を根本的に変更することを目的としています。

前提知識の解説

Go言語のテストフレームワーク

Go言語は、標準ライブラリに testing パッケージを提供しており、ユニットテストやベンチマークテストを簡単に記述できます。しかし、コンパイラやランタイムの挙動を検証するような低レベルのテストでは、Goプログラムをコンパイル・実行し、その標準出力や標準エラー出力を外部のシェルコマンドで検証する手法が用いられることがあります。これは、Goの testing パッケージが提供するアサーション機能だけでは表現しきれない、より複雑な実行環境の検証に適しています。

golden.out ファイル

golden.out は、Goのテストスイートにおいて、複数のテストケースの期待される標準出力(または標準エラー出力)を連結して格納していたファイルです。各テストの出力は、ファイル内で特定の区切り文字(例: =========== ./testname.go)によって識別されていました。テスト実行時には、各テストの実際の出力をこの golden.out の対応するセクションと比較することで、テストの合否を判定していました。

gccgo

gccgo は、Go言語のプログラムをコンパイルするための代替コンパイラです。Goの公式コンパイラである gc とは異なり、gccgo はGCC (GNU Compiler Collection) のフロントエンドとして実装されており、GCCの最適化バックエンドを利用します。これにより、gccgo は特定のプラットフォームで gc よりも優れたパフォーマンスを発揮したり、既存のGCCベースのツールチェーンとの統合が容易になったりする利点があります。Go言語のエコシステムでは、gcgccgo の両方でGoプログラムが正しく動作することを保証することが重要であり、そのためには両方のコンパイラでテストスイートが実行可能である必要があります。

cmp コマンド

cmp はUnix系のオペレーティングシステムで利用できる標準的なコマンドラインユーティリティです。2つのファイルをバイト単位で比較し、差異があればその情報を出力し、終了ステータスを非ゼロに設定します。差異がなければ何も出力せず、終了ステータスをゼロに設定します。この特性から、シェルスクリプトでファイルの同一性を検証する際によく利用されます。

シェルスクリプトにおける $G, $D, $F, $L, $A

Goのテストスクリプトでは、テストのコンパイルと実行を自動化するために、特定のシェル変数を使用することが一般的です。

  • $G: Goコンパイラへのパスまたはコマンド(例: go tool compile または gccgo)。
  • $D: 現在のテストファイルが存在するディレクトリへのパス。
  • $F: 現在のテストファイルのファイル名(拡張子を含む)。
  • $L: Goリンカへのパスまたはコマンド(例: go tool link)。
  • $A: コンパイルされた実行可能ファイルのデフォルト名(通常は a.out)。

これらの変数を組み合わせることで、テストスクリプトは柔軟にコンパイルと実行のコマンドを構築できます。

技術的詳細

このコミットの主要な技術的変更点は、テストの実行方法と期待出力の管理方法にあります。

  1. golden.out の廃止: 以前は単一の test/golden.out ファイルに集約されていたすべてのテストの期待出力が削除されました。これは、このファイルがもはやテストの検証に使用されないことを意味します。

  2. テストスクリプトの変更: 各テストファイル (.go ファイル) の先頭にあるコメント行が変更されました。このコメント行は、テストハーネスがテストを実行する際に使用するシェルコマンドを定義しています。

    • 変更前:

      // $G $D/$F.go && $L $F.$A && ./$A.out
      

      このコマンドは、Goソースファイルをコンパイルし、リンクして実行可能ファイルを生成し、その実行可能ファイルを実行するだけでした。出力の検証は、テストハーネスの外部ロジックで行われていました。

    • 変更後:

      // $G $D/$F.go && $L $F.$A && ./$A.out 2>&1 | cmp - $D/$F.out
      

      この新しいコマンドは、以下のステップを実行します。

      • $G $D/$F.go && $L $F.$A: 以前と同様にGoソースファイルをコンパイルし、リンクします。
      • ./$A.out: 生成された実行可能ファイルを実行します。
      • 2>&1: 実行可能ファイルの標準エラー出力 (stderr) を標準出力 (stdout) にリダイレクトします。これにより、プログラムが標準出力と標準エラー出力の両方に出力する場合でも、それらをまとめて処理できます。
      • | cmp - $D/$F.out: 実行可能ファイルの結合された出力(標準出力と標準エラー出力)を cmp コマンドの標準入力 (-) にパイプします。cmp コマンドは、この入力と、現在のテストファイルと同じディレクトリにある対応する .out ファイル ($D/$F.out) の内容を比較します。
  3. 個別 .out ファイルの導入: test/deferprint.out, test/goprint.out, test/helloworld.out など、各テストファイルに対応する新しい .out ファイルが作成されました。これらのファイルには、それぞれのテストが生成すべき期待される出力が格納されています。cmp コマンドは、テストの実際の出力とこれらの個別の .out ファイルの内容を比較することで、テストの合否を判定します。

  4. テストロジックの調整: 一部のテストファイル (test/ken/cplx3.go, test/ken/cplx5.go, test/ken/intervar.go, test/ken/string.go) では、print ステートメントに依存する代わりに、より明示的な値の検証や panic を使用するように内部ロジックが変更されています。これは、出力比較に加えて、テスト内部での厳密なアサーションを強化する意図があると考えられます。例えば、cplx3.gocplx5.go では、複素数計算の結果が期待値と一定の誤差範囲内で一致するかを if 文と panic で確認しています。intervar.go では、インターフェースメソッドが文字列を返すように変更され、その戻り値が期待される文字列と一致するかを検証しています。これにより、テストの堅牢性が向上し、出力形式のわずかな違いに左右されにくくなります。

これらの変更により、各テストは自己完結型となり、自身の期待出力を個別に管理するようになります。これにより、gccgo のような代替コンパイラがGoのテストスイートをより簡単に実行し、その出力を検証できるようになります。gccgo は、gc とは異なる出力形式を持つ可能性があるため、個別の .out ファイルを持つことで、gccgo 固有の期待出力を提供したり、gccgo のテスト実行環境をより柔軟に設定したりすることが可能になります。

コアとなるコードの変更箇所

このコミットのコアとなる変更は、主に以下の2つのパターンに集約されます。

  1. test/golden.out の削除:

    --- a/test/golden.out
    +++ /dev/null
    @@ -1,50 +0,0 @@
    - 
    - == ./
    - 
    - =========== ./deferprint.go
    - printing: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    - 42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
    - 
    - =========== ./goprint.go
    - 42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
    - 
    - =========== ./helloworld.go
    - hello, world
    - 
    - =========== ./printbig.go
    - -9223372036854775808
    - 9223372036854775807
    - 
    - =========== ./sigchld.go
    - survived SIGCHLD
    - 
    - == ken/
    - 
    - =========== ken/cplx0.go
    - (+5.000000e+000+6.000000e+000i)
    - (+5.000000e+000+6.000000e+000i)
    - (+5.000000e+000+6.000000e+000i)
    - (+5.000000e+000+6.000000e+000i)
    - 
    - =========== ken/cplx3.go
    - (+1.292308e+000-1.384615e-001i)
    - (+1.292308e+000-1.384615e-001i)
    - 
    - =========== ken/cplx5.go
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - (+5.000000e+000-5.000000e+000i)
    - 
    - =========== ken/intervar.go
    -  print 1 bio 2 file 3 -- abc
    - 
    - =========== ken/string.go
    - abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz
    - 
    - == chan/
    - 
    - == interface/
    - @@ -55,10 +13,4 @@ abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz-abcxyz
    - 
    - == fixedbugs/
    - 
    - =========== fixedbugs/bug067.go
    - ok
    - 
    - =========== fixedbugs/bug328.go
    - 0x0
    - 
    - == bugs/
    

    golden.out ファイルの内容がすべて削除されています。

  2. テストファイルの実行コマンドの変更と対応する .out ファイルの追加: 例として test/deferprint.gotest/deferprint.out の変更を示します。

    test/deferprint.go の変更:

    --- a/test/deferprint.go
    +++ b/test/deferprint.go
    @@ -1,4 +1,4 @@
    -// $G $D/$F.go && $L $F.$A && ./$A.out
    +// $G $D/$F.go && $L $F.$A && ./$A.out 2>&1 | cmp - $D/$F.out
     
     // Copyright 2010 The Go Authors.  All rights reserved.
     // Use of this source code is governed by a BSD-style
    

    test/deferprint.out の追加:

    --- /dev/null
    +++ b/test/deferprint.out
    @@ -0,0 +1,2 @@
    +printing: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    +42 true false true +1.500000e+000 world 0x0 [0/0]0x0 0x0 0x0 255
    

    同様の変更が、test/goprint.go, test/helloworld.go, test/fixedbugs/bug328.go, test/ken/cplx0.go, test/ken/string.go, test/printbig.go, test/sigchld.go およびそれらに対応する .out ファイルに対して行われています。

  3. 一部のテストロジックの変更: 例として test/ken/cplx3.go の変更を示します。

    --- a/test/ken/cplx3.go
    +++ b/test/ken/cplx3.go
    @@ -19,10 +19,29 @@ const (
     func main() {
     	c0 := C1
     	c0 = (c0 + c0 + c0) / (c0 + c0 + 3i)
    -	println(c0)
    +	r, i := real(c0), imag(c0)
    +	d := r - 1.292308
    +	if d < 0 {
    +		d = - d
    +	}
    +	if d > 1e-6 {
    +		println(r, "!= 1.292308")
    +		panic(0)
    +	}
    +	d = i + 0.1384615
    +	if d < 0 {
    +		d = - d
    +	}
    +	if d > 1e-6 {
    +		println(i, "!= -0.1384615")
    +		panic(0)
    +	}
     
     	c := *(*complex128)(unsafe.Pointer(&c0))
    -	println(c)
    +	if c != c0 {
    +		println(c, "!=", c)
    +		panic(0)
    +	}
     
     	var a interface{}
     	switch c := reflect.ValueOf(a); c.Kind() {
    

    println(c0)println(c) の代わりに、計算結果の複素数の実部と虚部を個別に検証し、許容誤差範囲外であれば panic を発生させるように変更されています。これにより、テストの出力に依存せず、プログラム内部で直接的な検証が行われるようになります。

コアとなるコードの解説

このコミットの核心は、Goのテストハーネスがテストの出力を検証する方法を、一元的な golden.out ファイルとの比較から、各テストが自身の期待出力ファイルを持つ独立した比較へと移行させた点にあります。

変更されたテストファイルの先頭にあるコメント行は、Goのテストシステムがテストを実行する際の「レシピ」として機能します。

// $G $D/$F.go && $L $F.$A && ./$A.out 2>&1 | cmp - $D/$F.out

この行は、以下の処理フローを定義しています。

  1. $G $D/$F.go: Goコンパイラ ($G) を使用して、現在のテストファイル ($D/$F.go) をコンパイルします。
  2. && $L $F.$A: コンパイルが成功した場合 (&&)、Goリンカ ($L) を使用して、コンパイルされたオブジェクトファイル ($F.$A は通常、コンパイル後のオブジェクトファイル名を表す) をリンクし、実行可能ファイル (./$A.out) を生成します。
  3. && ./$A.out: リンクが成功した場合 (&&)、生成された実行可能ファイルを実行します。
  4. 2>&1: 実行可能ファイルの標準エラー出力 (ファイルディスクリプタ 2) を標準出力 (ファイルディスクリプタ 1) にリダイレクトします。これにより、テストプログラムが標準出力と標準エラー出力の両方にメッセージを出力する場合でも、それらが単一のストリームとして cmp コマンドに渡されます。
  5. | cmp - $D/$F.out: 実行可能ファイルの結合された出力ストリームを cmp コマンドの標準入力 (-) にパイプします。cmp コマンドは、この標準入力の内容を、現在のテストファイルと同じ名前で .out 拡張子を持つファイル ($D/$F.out) の内容と比較します。

cmp コマンドは、比較結果に基づいて終了ステータスを返します。

  • 0: ファイルが同一である場合。
  • 1: ファイルが異なる場合。
  • 2: アクセスできないファイルなどのエラーが発生した場合。

Goのテストハーネスは、このシェルコマンドの終了ステータスを解釈し、cmp が非ゼロのステータスを返した場合(つまり、出力が期待と異なる場合)にテストを失敗と判断します。

この変更により、各テストは自身の期待出力を個別の .out ファイルとして持つため、テストの独立性が高まります。特定のテストの期待出力を変更する際に、他のテストの期待出力に影響を与える心配がなくなります。また、gccgo のような代替コンパイラがGoのテストスイートをより簡単に利用できるようになります。gccgo は、gc とは異なる出力形式を持つ可能性があるため、個別の .out ファイルを持つことで、gccgo 固有の期待出力を提供したり、gccgo のテスト実行環境をより柔軟に設定したりすることが可能になります。

さらに、一部のテストで print に依存する代わりに panic を使用した明示的な検証が導入されたことは、テストの堅牢性を高めるための追加の改善です。これにより、出力形式のわずかな変更がテストの失敗につながるリスクが減り、テストがより直接的にプログラムのロジックを検証できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に test ディレクトリ): https://github.com/golang/go/tree/master/test
  • cmp コマンドのマニュアルページ (man cmp): https://man7.org/linux/man-pages/man1/cmp.1.html
  • シェルスクリプトのリダイレクトとパイプに関する一般的な情報源。
  • Go言語のコンパイラとツールチェーンに関するドキュメント。
  • Go言語のテストに関する一般的なプラクティスや議論。
  • gccgo の開発に関する情報。
  • Goのテストハーネスがどのように機能するかについての内部ドキュメントやソースコード。
    • Goのテストハーネスの具体的な実装は、Goのソースツリー内の src/cmd/go/test.gosrc/cmd/go/internal/test/test.go などに関連するコードが含まれている可能性があります。
    • Goのテスト実行の仕組みについては、go help testgo doc cmd/go などのコマンドラインヘルプも参考になります。
    • Goのテストの歴史や進化に関するブログ記事やメーリングリストのアーカイブも、背景を理解する上で役立ちます。
    • 特に、Goのテストスイートがどのように構築され、異なるコンパイラ(gcgccgo)をサポートしているかについての情報は、Goの公式ブログや開発者向けメーリングリストで議論されていることがあります。