[インデックス 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
という単一のファイルにまとめられていました。このアプローチにはいくつかの課題がありました。
- 保守性の問題:
golden.out
は非常に大きなファイルになりがちで、特定のテストの出力を変更する際に、ファイル全体を編集する必要がありました。これにより、意図しない変更が混入するリスクや、変更のレビューが困難になる問題がありました。 - 並行性の問題: 複数のテストが同時に実行される環境では、単一の
golden.out
ファイルへの書き込みや読み込みが競合を引き起こす可能性がありました。 - コンパイラ間の差異への対応: Go言語には公式コンパイラである
gc
(Go Compiler) の他に、GCCをバックエンドとするgccgo
のような代替コンパイラが存在します。これらのコンパイラは、最適化やランタイムの挙動の違いにより、全く同じソースコードからでも微妙に異なる出力を生成する場合があります。単一のgolden.out
ファイルでは、gc
とgccgo
の両方に対応した期待出力を管理するのが困難でした。特に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言語のエコシステムでは、gc
と gccgo
の両方で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
)。
これらの変数を組み合わせることで、テストスクリプトは柔軟にコンパイルと実行のコマンドを構築できます。
技術的詳細
このコミットの主要な技術的変更点は、テストの実行方法と期待出力の管理方法にあります。
-
golden.out
の廃止: 以前は単一のtest/golden.out
ファイルに集約されていたすべてのテストの期待出力が削除されました。これは、このファイルがもはやテストの検証に使用されないことを意味します。 -
テストスクリプトの変更: 各テストファイル (
.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
) の内容を比較します。
-
-
個別
.out
ファイルの導入:test/deferprint.out
,test/goprint.out
,test/helloworld.out
など、各テストファイルに対応する新しい.out
ファイルが作成されました。これらのファイルには、それぞれのテストが生成すべき期待される出力が格納されています。cmp
コマンドは、テストの実際の出力とこれらの個別の.out
ファイルの内容を比較することで、テストの合否を判定します。 -
テストロジックの調整: 一部のテストファイル (
test/ken/cplx3.go
,test/ken/cplx5.go
,test/ken/intervar.go
,test/ken/string.go
) では、print
ステートメントに依存する代わりに、より明示的な値の検証やpanic
を使用するように内部ロジックが変更されています。これは、出力比較に加えて、テスト内部での厳密なアサーションを強化する意図があると考えられます。例えば、cplx3.go
とcplx5.go
では、複素数計算の結果が期待値と一定の誤差範囲内で一致するかをif
文とpanic
で確認しています。intervar.go
では、インターフェースメソッドが文字列を返すように変更され、その戻り値が期待される文字列と一致するかを検証しています。これにより、テストの堅牢性が向上し、出力形式のわずかな違いに左右されにくくなります。
これらの変更により、各テストは自己完結型となり、自身の期待出力を個別に管理するようになります。これにより、gccgo
のような代替コンパイラがGoのテストスイートをより簡単に実行し、その出力を検証できるようになります。gccgo
は、gc
とは異なる出力形式を持つ可能性があるため、個別の .out
ファイルを持つことで、gccgo
固有の期待出力を提供したり、gccgo
のテスト実行環境をより柔軟に設定したりすることが可能になります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主に以下の2つのパターンに集約されます。
-
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
ファイルの内容がすべて削除されています。 -
テストファイルの実行コマンドの変更と対応する
.out
ファイルの追加: 例としてtest/deferprint.go
とtest/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
ファイルに対して行われています。 -
一部のテストロジックの変更: 例として
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
この行は、以下の処理フローを定義しています。
$G $D/$F.go
: Goコンパイラ ($G
) を使用して、現在のテストファイル ($D/$F.go
) をコンパイルします。&& $L $F.$A
: コンパイルが成功した場合 (&&
)、Goリンカ ($L
) を使用して、コンパイルされたオブジェクトファイル ($F.$A
は通常、コンパイル後のオブジェクトファイル名を表す) をリンクし、実行可能ファイル (./$A.out
) を生成します。&& ./$A.out
: リンクが成功した場合 (&&
)、生成された実行可能ファイルを実行します。2>&1
: 実行可能ファイルの標準エラー出力 (ファイルディスクリプタ2
) を標準出力 (ファイルディスクリプタ1
) にリダイレクトします。これにより、テストプログラムが標準出力と標準エラー出力の両方にメッセージを出力する場合でも、それらが単一のストリームとしてcmp
コマンドに渡されます。| 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言語の公式ウェブサイト: https://golang.org/
- Go言語のテストパッケージ
testing
: https://pkg.go.dev/testing - GCCGo プロジェクトページ (GCCのGoフロントエンド): https://gcc.gnu.org/onlinedocs/gccgo/
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/5554056
はGerritのChange-Idへのリンクです)
参考にした情報源リンク
- 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.go
やsrc/cmd/go/internal/test/test.go
などに関連するコードが含まれている可能性があります。 - Goのテスト実行の仕組みについては、
go help test
やgo doc cmd/go
などのコマンドラインヘルプも参考になります。 - Goのテストの歴史や進化に関するブログ記事やメーリングリストのアーカイブも、背景を理解する上で役立ちます。
- 特に、Goのテストスイートがどのように構築され、異なるコンパイラ(
gc
とgccgo
)をサポートしているかについての情報は、Goの公式ブログや開発者向けメーリングリストで議論されていることがあります。
- Goのテストハーネスの具体的な実装は、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
という単一のファイルにまとめられていました。このアプローチにはいくつかの課題がありました。
- 保守性の問題:
golden.out
は非常に大きなファイルになりがちで、特定のテストの出力を変更する際に、ファイル全体を編集する必要がありました。これにより、意図しない変更が混入するリスクや、変更のレビューが困難になる問題がありました。 - 並行性の問題: 複数のテストが同時に実行される環境では、単一の
golden.out
ファイルへの書き込みや読み込みが競合を引き起こす可能性がありました。 - コンパイラ間の差異への対応: Go言語には公式コンパイラである
gc
(Go Compiler) の他に、GCCをバックエンドとするgccgo
のような代替コンパイラが存在します。これらのコンパイラは、最適化やランタイムの挙動の違いにより、全く同じソースコードからでも微妙に異なる出力を生成する場合があります。単一のgolden.out
ファイルでは、gc
とgccgo
の両方に対応した期待出力を管理するのが困難でした。特に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言語のエコシステムでは、gc
と gccgo
の両方で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
)。
これらの変数を組み合わせることで、テストスクリプトは柔軟にコンパイルと実行のコマンドを構築できます。
技術的詳細
このコミットの主要な技術的変更点は、テストの実行方法と期待出力の管理方法にあります。
-
golden.out
の廃止: 以前は単一のtest/golden.out
ファイルに集約されていたすべてのテストの期待出力が削除されました。これは、このファイルがもはやテストの検証に使用されないことを意味します。 -
テストスクリプトの変更: 各テストファイル (
.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
) の内容を比較します。
-
-
個別
.out
ファイルの導入:test/deferprint.out
,test/goprint.out
,test/helloworld.out
など、各テストファイルに対応する新しい.out
ファイルが作成されました。これらのファイルには、それぞれのテストが生成すべき期待される出力が格納されています。cmp
コマンドは、テストの実際の出力とこれらの個別の.out
ファイルの内容を比較することで、テストの合否を判定します。 -
テストロジックの調整: 一部のテストファイル (
test/ken/cplx3.go
,test/ken/cplx5.go
,test/ken/intervar.go
,test/ken/string.go
) では、print
ステートメントに依存する代わりに、より明示的な値の検証やpanic
を使用するように内部ロジックが変更されています。これは、出力比較に加えて、テスト内部での厳密なアサーションを強化する意図があると考えられます。例えば、cplx3.go
とcplx5.go
では、複素数計算の結果が期待値と一定の誤差範囲内で一致するかをif
文とpanic
で確認しています。intervar.go
では、インターフェースメソッドが文字列を返すように変更され、その戻り値が期待される文字列と一致するかを検証しています。これにより、テストの堅牢性が向上し、出力形式のわずかな違いに左右されにくくなります。
これらの変更により、各テストは自己完結型となり、自身の期待出力を個別に管理するようになります。これにより、gccgo
のような代替コンパイラがGoのテストスイートをより簡単に実行し、その出力を検証できるようになります。gccgo
は、gc
とは異なる出力形式を持つ可能性があるため、個別の .out
ファイルを持つことで、gccgo
固有の期待出力を提供したり、gccgo
のテスト実行環境をより柔軟に設定したりすることが可能になります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主に以下の2つのパターンに集約されます。
-
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
ファイルの内容がすべて削除されています。 -
テストファイルの実行コマンドの変更と対応する
.out
ファイルの追加: 例としてtest/deferprint.go
とtest/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
ファイルに対して行われています。 -
一部のテストロジックの変更: 例として
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
この行は、以下の処理フローを定義しています。
$G $D/$F.go
: Goコンパイラ ($G
) を使用して、現在のテストファイル ($D/$F.go
) をコンパイルします。&& $L $F.$A
: コンパイルが成功した場合 (&&
)、Goリンカ ($L
) を使用して、コンパイルされたオブジェクトファイル ($F.$A
は通常、コンパイル後のオブジェクトファイル名を表す) をリンクし、実行可能ファイル (./$A.out
) を生成します。&& ./$A.out
: リンクが成功した場合 (&&
)、生成された実行可能ファイルを実行します。2>&1
: 実行可能ファイルの標準エラー出力 (ファイルディスクリプタ2
) を標準出力 (ファイルディスクリプタ1
) にリダイレクトします。これにより、テストプログラムが標準出力と標準エラー出力の両方にメッセージを出力する場合でも、それらが単一のストリームとしてcmp
コマンドに渡されます。| 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言語の公式ウェブサイト: https://golang.org/
- Go言語のテストパッケージ
testing
: https://pkg.go.dev/testing - GCCGo プロジェクトページ (GCCのGoフロントエンド): https://gcc.gnu.org/onlinedocs/gccgo/
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/5554056
はGerritのChange-Idへのリンクです)
参考にした情報源リンク
- 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.go
やsrc/cmd/go/internal/test/test.go
などに関連するコードが含まれている可能性があります。 - Goのテスト実行の仕組みについては、
go help test
やgo doc cmd/go
などのコマンドラインヘルプも参考になります。 - Goのテストの歴史や進化に関するブログ記事やメーリングリストのアーカイブも、背景を理解する上で役立ちます。
- 特に、Goのテストスイートがどのように構築され、異なるコンパイラ(
gc
とgccgo
)をサポートしているかについての情報は、Goの公式ブログや開発者向けメーリングリストで議論されていることがあります。
- Goのテストハーネスの具体的な実装は、Goのソースツリー内の