[インデックス 19613] ファイルの概要
このコミットは、以前の変更(CL 107320046 / 97cd07dcb9d8)を取り消すものです。取り消された変更は、実行不可能なGoのテストファイル内のExample関数もビルド対象に含めることを目的としていましたが、この変更がビルドを壊す問題を引き起こしたため、本コミットで元に戻されました。
コミット
このコミット da1c2b182a029a4f3b050442573cd47abfe93291
は、Andrew Gerrand氏によって2014年6月25日に行われました。内容は、以前のコミット CL 107320046
(ハッシュ 97cd07dcb9d8
) を元に戻すものです。この取り消しは、元の変更がGoのビルドプロセスを破壊したために必要となりました。具体的には、src/cmd/go/test.go
ファイル内の1行の変更が元に戻されています。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/da1c2b182a029a4f3b050442573cd47abfe93291
元コミット内容
取り消された元のコミット(CL 107320046)の目的は以下の通りです。
cmd/go: build test files containing non-runnable examples
Even if we can't run them, we should at least check that they compile.
これは、go test
コマンドが、実行可能ではないExample関数を含むテストファイルもビルドするように変更することを意図していました。その目的は、たとえ実行できなくても、それらのExample関数が少なくともコンパイル可能であることを確認することでした。
変更の背景
元のコミット CL 107320046
は、Goのテストフレームワークにおいて、実行不可能なExample関数(例えば、Output
コメントがない、または EmptyOutput
が設定されていないExample)を含むテストファイルが、go test
実行時にビルドされないという挙動を修正しようとしました。開発者は、これらのExample関数が実行されなくても、少なくとも構文的に正しく、コンパイル可能であるべきだと考えました。
しかし、この変更が導入された結果、Goのビルドプロセスが壊れてしまいました。コミットメッセージには「Breaks the build」と明確に記載されており、これが本コミットで元の変更を取り消す直接的な理由となりました。ビルドが壊れる具体的な原因は、*seen = true
の位置変更が、特定の条件下で予期せぬ副作用を引き起こしたためと考えられます。
前提知識の解説
GoのテストとExample関数
Go言語には、標準ライブラリに組み込まれた強力なテストフレームワークがあります。go test
コマンドは、Goプロジェクトのテストを実行するための主要なツールです。
- テスト関数:
func TestXxx(*testing.T)
の形式で定義され、ユニットテストや結合テストに使用されます。 - ベンチマーク関数:
func BenchmarkXxx(*testing.B)
の形式で定義され、コードのパフォーマンス測定に使用されます。 - Example関数:
func ExampleXxx()
の形式で定義され、コードの使用例を示すために使用されます。Example関数は、その出力が// Output:
コメントに記述された期待される出力と一致するかどうかをgo test
が検証することで、テストとしても機能します。これにより、ドキュメントとテストが同時に提供されるというユニークな特徴があります。
Example関数には、以下のような種類があります。
- 実行可能なExample:
// Output:
コメントがあり、go test
実行時に実際にコードが実行され、出力が検証されます。 - 実行不可能なExample:
// Output:
コメントがない、または// Output:
コメントが空で// Unordered output:
や// Error:
などの特別なコメントもないExample関数です。これらは通常、ドキュメント目的で提供され、go test
によっては実行されません。
Goのビルドプロセスとgo test
go test
コマンドは、単にテストを実行するだけでなく、テスト対象のパッケージとその依存関係、そしてテストファイル自体をビルドする役割も担っています。go test
は、テストファイル(_test.go
で終わるファイル)を通常のソースコードとは異なる方法で処理することがあります。
CL (Change List)
Goプロジェクトでは、Google内部のコードレビューシステムであるGerritを使用しています。そこで提出される変更の単位を「Change List (CL)」と呼びます。各CLには一意の番号が割り当てられ、コードの変更、コミットメッセージ、レビューの履歴などが含まれます。コミットメッセージに CL 107320046
のように記載されているのは、Gerrit上の特定の変更を参照していることを意味します。
技術的詳細
このコミットが関連する src/cmd/go/test.go
ファイルは、go test
コマンドの内部ロジック、特にテストファイルの解析とExample関数の処理を担当しています。
元の変更(CL 107320046)は、testFuncs
構造体の load
メソッド内のロジックを変更しました。この load
メソッドは、指定されたテストファイルからExample関数をロードし、それらを t.Examples
リストに追加する役割を担っています。
重要なのは、*seen = true
という行です。この seen
変数は、現在のファイルがExample関数を含んでいるかどうかを示すフラグとして機能します。go test
の内部では、このフラグが true
に設定されると、そのファイルがビルド対象としてマークされます。
元の変更では、*seen = true
の行がExample関数のループの外側に移動されました。これは、Example関数が実行可能かどうかに関わらず、Example関数が存在するファイルは常にビルドされるべきだという意図がありました。
しかし、この変更がビルドを壊したということは、*seen = true
がExample関数のループの内側にあるべき、つまり、実際に処理されるExample関数がある場合にのみファイルがビルド対象としてマークされるべきだったことを示唆しています。
具体的には、元のコードでは e.Output == "" && !e.EmptyOutput
という条件で、出力を持たないExample関数はスキップされていました。*seen = true
がこの条件の外側にあったため、出力を持たないExample関数のみを含むファイルでも *seen
が true
になり、ビルド対象となりました。これが何らかのビルドエラーを引き起こしたと考えられます。
コアとなるコードの変更箇所
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -1177,12 +1177,12 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error {
ex := doc.Examples(f)
sort.Sort(byOrder(ex))
for _, e := range ex {
- *seen = true // Build the file even if the example is not runnable.
if e.Output == "" && !e.EmptyOutput {
// Don't run examples with no output.
continue
}
t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output})
+ *seen = true
}
return nil
}
コアとなるコードの解説
このコミットは、src/cmd/go/test.go
ファイル内の load
関数における *seen = true
の位置を元に戻しています。
-
変更前(取り消されたCL 107320046での変更):
for _, e := range ex { *seen = true // Build the file even if the example is not runnable. if e.Output == "" && !e.EmptyOutput { // Don't run examples with no output. continue } t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output}) }
このコードでは、
*seen = true
がfor
ループの先頭にありました。これは、Example関数が実行可能かどうか(e.Output
やe.EmptyOutput
の条件を満たすか)に関わらず、Example関数が見つかった時点で、そのファイルがビルド対象としてマークされることを意味します。元のCLのコメントにあるように、「実行可能でなくてもファイルをビルドする」という意図がありました。 -
変更後(本コミットでの修正):
for _, e := range ex { if e.Output == "" && !e.EmptyOutput { // Don't run examples with no output. continue } t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output}) *seen = true }
本コミットでは、
*seen = true
の行がif e.Output == "" && !e.EmptyOutput
条件の内側、かつt.Examples
にExample関数が追加された後に移動されました。 これにより、*seen = true
は、実際にt.Examples
リストに追加される(つまり、go test
によって実行または検証される)Example関数がある場合にのみ設定されるようになりました。出力を持たない実行不可能なExample関数はcontinue
でスキップされるため、それらのみを含むファイルは*seen = true
にならず、ビルド対象から外れます。
この変更は、元のCLが意図した「実行不可能なExampleもビルドする」という挙動を元に戻し、ビルドが壊れる問題を解決しました。これは、go test
のビルドプロセスが、実際に処理されるExample関数が存在する場合にのみ、関連ファイルをビルドする必要があることを示唆しています。
関連リンク
- 元の変更(取り消されたCL): https://golang.org/cl/107320046 (GerritのCL番号であり、直接アクセスできない場合があります)
- 本コミット(取り消しCL): https://golang.org/cl/110140044 (GerritのCL番号であり、直接アクセスできない場合があります)
参考にした情報源リンク
- Go言語の公式ドキュメント(
go test
コマンド、testing
パッケージ、Example関数に関する情報) - Go言語のソースコード(
src/cmd/go/test.go
の関連部分) - Gerrit (Goプロジェクトが使用するコードレビューシステム) に関する一般的な情報
- GoのExample関数に関するブログ記事やチュートリアル (一般的な知識として)
- Goのテストに関する公式ブログ記事 (一般的な知識として)