[インデックス 17542] ファイルの概要
このコミットは、Go言語のコマンドラインツール go test
の振る舞いを改善するものです。具体的には、テストのセットアップ中にエラーが発生した場合(例えば、x_test.go
ファイルに構文エラーがある場合)に、FAIL
という明確なメッセージを出力するように変更されています。これにより、ビルド失敗時と同様に、テスト関連のあらゆる失敗が統一された形式で報告されるようになります。
コミット
commit baed067d879410951279feeebc0ef67702415ffd
Author: Russ Cox <rsc@golang.org>
Date: Tue Sep 10 14:43:57 2013 -0400
cmd/go: show FAIL for errors during test setup
For example, if an x_test.go file contains a syntax error,
b.test fails with an error message. But it wasn't printing
the same FAIL line that a build failure later would print.
This makes all the test failures that happen (once we
decide to start running tests) consistently say FAIL.
Fixes #4701.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13431044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/baed067d879410951279feeebc0ef67702415ffd
元コミット内容
cmd/go: show FAIL for errors during test setup
このコミットは、テストのセットアップ中に発生するエラーに対して FAIL
メッセージを表示するように cmd/go
ツールを修正します。例えば、x_test.go
ファイルに構文エラーがある場合、go test
はエラーメッセージを出力しますが、その際にビルド失敗時と同じ FAIL
行が表示されていませんでした。この変更により、テスト実行が開始されると判断された後に発生するすべてのテスト失敗が、一貫して FAIL
と表示されるようになります。
この変更は Issue #4701 を修正します。
変更の背景
Go言語のテストツール go test
は、開発者がコードの正確性を検証するための重要な手段です。しかし、テストの実行プロセスにおいて、テストコード自体の問題(例えば構文エラー)によってテストが開始される前に失敗するケースがありました。
従来の go test
の挙動では、テスト対象のパッケージのビルドが成功し、テストバイナリが生成された後にテストが失敗した場合(例えば、テスト関数内でアサーションが失敗した場合)には、明確に FAIL
というステータスが表示されていました。しかし、テストバイナリのビルド自体が失敗するような、より早い段階でのエラー(例: _test.go
ファイル内の構文エラー)の場合、エラーメッセージは表示されるものの、統一された FAIL
というステータス表示がありませんでした。
この不整合は、CI/CDパイプラインやスクリプトで go test
の出力をパースしてテストの成否を判断する際に問題となる可能性がありました。また、開発者がテスト結果を一目で把握する上でも、一貫性のない出力は混乱を招く要因となります。
このコミットは、この出力の不整合を解消し、テストのセットアップ段階で発生するエラーも、テスト実行中のエラーと同様に FAIL
という統一されたステータスで報告することで、go test
の出力の一貫性と信頼性を向上させることを目的としています。これにより、テストの失敗原因がどこにあっても、開発者は FAIL
というキーワードによってテストが失敗したことを即座に認識できるようになります。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびテストに関する基本的な知識が必要です。
-
go test
コマンド:- Go言語に組み込まれているテスト実行ツールです。
- カレントディレクトリまたは指定されたパッケージ内の
_test.go
で終わるファイルを自動的に探し、テスト関数(TestXxx
)、ベンチマーク関数(BenchmarkXxx
)、サンプル関数(ExampleXxx
)を実行します。 - テストの成功/失敗、実行時間などの情報を標準出力に表示します。
- 通常、テストが成功すると
PASS
、失敗するとFAIL
と表示されます。
-
Goパッケージとビルドプロセス:
- Goのコードはパッケージとして組織されます。
go test
は、テストを実行する前に、テスト対象のパッケージとテストコードをコンパイルして実行可能なテストバイナリを生成します。- このコンパイルプロセス中に構文エラーや型エラーなどのビルドエラーが発生することがあります。
-
テストのライフサイクル:
go test
の実行は、大きく分けて以下のフェーズで構成されます。- パッケージの解決と依存関係の確認: テスト対象のパッケージとテストファイルの特定。
- コンパイル(ビルド): テスト対象のソースコードと
_test.go
ファイルをコンパイルし、テストバイナリを生成。この段階で構文エラーなどがあるとビルドが失敗します。 - テストバイナリの実行: 生成されたテストバイナリを実行し、テスト関数を呼び出します。
- 結果の集計と出力: テスト関数の実行結果を収集し、
PASS
またはFAIL
を含む最終的なレポートを出力します。
-
エラーハンドリングと出力の一貫性:
- CLIツールにおいて、異なる種類の失敗に対して一貫した出力形式を提供することは、自動化スクリプトでのパースやユーザーの理解を助ける上で非常に重要です。
- 特に、
FAIL
のような明確なキーワードは、テストの成否を判断するシグナルとして広く利用されます。
このコミットは、上記の「コンパイル(ビルド)」フェーズで発生するエラーの出力形式を、「テストバイナリの実行」フェーズで発生するエラーの出力形式と統一することを目指しています。
技術的詳細
このコミットの技術的詳細は、go test
コマンドの内部処理、特にテストバイナリのビルドとエラー報告のメカニズムに焦点を当てています。
変更は主に src/cmd/go/test.go
ファイルの runTest
関数にあります。この関数は go test
コマンドのテスト実行ロジックの中核を担っています。
コミット前の挙動では、go test
がテストバイナリをビルドする際に構文エラーなどの問題が発生した場合、b.test
(テストバイナリ) の実行が失敗し、エラーメッセージが標準エラー出力に表示されていました。しかし、このエラーメッセージには、テスト実行後の失敗時に表示されるような FAIL
という明確なステータス行が含まれていませんでした。
このコミットでは、runTest
関数内でエラーが発生し、それがテストのセットアップ段階(ビルドエラーなど)によるものであると判断された場合に、出力されるエラーメッセージに FAIL
という文字列を追加するように修正されています。
具体的には、runTest
関数内のエラー処理ブロックにおいて、errorf
関数(Goコマンドのエラー報告ユーティリティ)の呼び出しが変更されています。
-
変更前:
if p.ImportPath != "" { errorf("# %s\n%s", p.ImportPath, str) } else { errorf("%s", str) }
ここでは、インポートパスがあるかないかで異なる形式のエラーメッセージを出力していますが、いずれも
FAIL
というキーワードは含まれていません。 -
変更後:
failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath) if p.ImportPath != "" { errorf("# %s\n%s\n%s", p.ImportPath, str, failed) } else { errorf("%s\n%s", str, failed) }
変更後では、まず
failed
という文字列変数にFAIL\t%s [setup failed]\n
という形式でFAIL
ステータスを含むメッセージを生成しています。そして、このfailed
文字列を既存のエラーメッセージの末尾に追加して出力するようにerrorf
の引数が変更されています。これにより、テストのセットアップ段階でのエラーであっても、最終的な出力にはFAIL
が含まれるようになります。
また、この変更を検証するために、src/cmd/go/test.bash
に新しいテストケースが追加されています。このテストケースは、意図的に構文エラーを含む x_test.go
ファイル(src/cmd/go/testdata/src/syntaxerror/x_test.go
)を作成し、go test syntaxerror
を実行します。そして、その標準エラー出力に FAIL
という文字列が含まれていることを grep
コマンドで確認しています。これにより、期待通りの出力が得られることが保証されます。
この修正は、Goツールのユーザーエクスペリエンスを向上させ、自動化された環境でのテスト結果のパースを容易にするための、細かではあるが重要な改善と言えます。
コアとなるコードの変更箇所
変更は主に以下の2つのファイルに集中しています。
src/cmd/go/test.go
:go test
コマンドの主要なロジックが含まれるファイル。エラーメッセージの出力形式が変更されています。src/cmd/go/test.bash
:go
コマンドのテストスクリプト。今回の変更を検証するための新しいテストケースが追加されています。src/cmd/go/testdata/src/syntaxerror/x.go
: 新しいテストケースで使用される、構文エラーを含むテストデータ用のGoソースファイル。src/cmd/go/testdata/src/syntaxerror/x_test.go
: 新しいテストケースで使用される、構文エラーを含むテストデータ用のGoテストファイル。
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index b55989c207..52d2f08337 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -104,6 +104,19 @@ cp -R testdata/local \"testdata/$bad\"\n testlocal \"$bad\" \'with bad characters in path\'\n rm -rf \"testdata/$bad\"\n \n+TEST error message for syntax error in test go file says FAIL\n+export GOPATH=$(pwd)/testdata\n+if ./testgo test syntaxerror 2>testdata/err; then\n+\techo \'go test syntaxerror succeeded\'\n+\tok=false\n+elif ! grep FAIL testdata/err >/dev/null; then\n+\techo \'go test did not say FAIL:\'\n+\tcat testdata/err\n+\tok=false\n+fi\n+rm -f ./testdata/err\n+unset GOPATH\n+\n # Test tests with relative imports.\n TEST relative imports \'(go test)\'\n if ! ./testgo test ./testdata/testimport; then
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index ebc9d28548..eab075db7c 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -423,10 +423,12 @@ func runTest(cmd *Command, args []string) {\n \t\t\tif strings.HasPrefix(str, \"\\n\") {\n \t\t\t\tstr = str[1:]\n \t\t\t}\n+\t\t\tfailed := fmt.Sprintf(\"FAIL\\t%s [setup failed]\\n\", p.ImportPath)\n+\n \t\t\tif p.ImportPath != \"\" {\n-\t\t\t\terrorf(\"# %s\\n%s\", p.ImportPath, str)\n+\t\t\t\terrorf(\"# %s\\n%s\\n%s\", p.ImportPath, str, failed)\n \t\t\t} else {\n-\t\t\t\terrorf(\"%s\", str)\n+\t\t\t\terrorf(\"%s\\n%s\", str, failed)\n \t\t\t}\n \t\t\tcontinue\n \t\t}\
diff --git a/src/cmd/go/testdata/src/syntaxerror/x.go b/src/cmd/go/testdata/src/syntaxerror/x.go
new file mode 100644
index 0000000000..c89cd18d0f\n--- /dev/null
+++ b/src/cmd/go/testdata/src/syntaxerror/x.go
@@ -0,0 +1 @@
+package p
diff --git a/src/cmd/go/testdata/src/syntaxerror/x_test.go b/src/cmd/go/testdata/src/syntaxerror/x_test.go
new file mode 100644
index 0000000000..2460743e50
--- /dev/null
+++ b/src/cmd/go/testdata/src/syntaxerror/x_test.go
@@ -0,0 +1,4 @@
+package p
+\n+func f() (x.y, z int) {\n+}\n
コアとなるコードの解説
src/cmd/go/test.go
の変更
runTest
関数内のエラー処理部分が変更されています。
-
変更前: エラーメッセージは
errorf
関数によって出力されていましたが、FAIL
というキーワードは含まれていませんでした。インポートパスの有無によって出力形式が異なっていました。// 既存のエラーメッセージ出力ロジック if p.ImportPath != "" { errorf("# %s\n%s", p.ImportPath, str) } else { errorf("%s", str) }
-
変更後:
failed := fmt.Sprintf("FAIL\t%s [setup failed]\n", p.ImportPath)
: まず、FAIL
という文字列と、インポートパス、そして[setup failed]
という説明を含む新しいエラーメッセージの行を生成しています。fmt.Sprintf
を使用してフォーマットされた文字列を作成しています。\t
はタブ文字、\n
は改行文字です。if p.ImportPath != "" { errorf("# %s\n%s\n%s", p.ImportPath, str, failed) }
: インポートパスが存在する場合、元のエラーメッセージ(# %s\n%s
)に加えて、新しく生成したfailed
メッセージを改行で区切って追加しています。これにより、元のエラー詳細の後にFAIL
ステータスが表示されます。else { errorf("%s\n%s", str, failed) }
: インポートパスが存在しない場合も同様に、元のエラーメッセージ(%s
)の後にfailed
メッセージを追加しています。
この変更により、テストのセットアップ段階で発生したエラー(例えば、テストファイルのコンパイルエラー)であっても、go test
の出力の最後に FAIL
という明確なステータス行が追加されるようになります。これにより、テストの成否を判断する際の出力の一貫性が保たれます。
src/cmd/go/test.bash
の変更
新しいテストケースが追加され、go test
が構文エラーのあるテストファイルに対して FAIL
を出力するかどうかを検証しています。
TEST error message for syntax error in test go file says FAIL
: テストケースの目的を説明するコメント。export GOPATH=$(pwd)/testdata
: テスト用のGOPATH
を設定し、テストデータディレクトリを使用するようにします。if ./testgo test syntaxerror 2>testdata/err; then ...
:./testgo test syntaxerror
:go
コマンドのテスト版(testgo
)を使って、syntaxerror
パッケージのテストを実行します。2>testdata/err
: 標準エラー出力をtestdata/err
ファイルにリダイレクトします。ビルドエラーは通常、標準エラー出力に出力されます。if ...; then echo 'go test syntaxerror succeeded'; ok=false
:go test
が成功してしまった場合(期待しない挙動)、エラーメッセージを出力し、テストを失敗とマークします。
elif ! grep FAIL testdata/err >/dev/null; then ...
:! grep FAIL testdata/err >/dev/null
:testdata/err
ファイルにFAIL
という文字列が含まれていない場合(期待しない挙動)、エラーメッセージを出力し、テストを失敗とマークします。>/dev/null
はgrep
の出力を抑制します。echo 'go test did not say FAIL:'
とcat testdata/err
:FAIL
が見つからなかった場合、その旨を報告し、エラーファイルの内容を表示してデバッグを助けます。
rm -f ./testdata/err
: テスト後に一時ファイルを削除します。unset GOPATH
: 設定したGOPATH
を解除します。
このテストケースは、src/cmd/go/testdata/src/syntaxerror/x_test.go
に意図的に構文エラー(x.y
という無効な型宣言)を挿入することで、テストのセットアップ段階でのエラーをシミュレートし、その際に FAIL
が出力されることを確認しています。
src/cmd/go/testdata/src/syntaxerror/x.go
および x_test.go
これらは、上記のテストケースで使用されるダミーのGoソースファイルとテストファイルです。
-
x.go
:package p
これは単に
p
パッケージを定義するファイルです。 -
x_test.go
:package p func f() (x.y, z int) { }
このファイルには
x.y
という構文エラーが含まれています。x.y
はGo言語の有効な型名ではありません。このエラーにより、go test
はテストバイナリのビルドに失敗し、今回のコミットで修正されたエラーパスがトリガーされます。
これらの変更は、go test
の堅牢性とユーザーフレンドリーさを向上させるための、細部にわたる配慮を示しています。
関連リンク
- Go Issue #4701: cmd/go: show FAIL for errors during test setup
- Go CL 13431044: https://golang.org/cl/13431044 (Gerrit Code Review)
参考にした情報源リンク
- Go言語公式ドキュメント: https://golang.org/doc/
go test
コマンドのドキュメント: https://golang.org/cmd/go/#hdr-Test_packages- Go言語のソースコード (GitHub): https://github.com/golang/go
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/
fmt
パッケージのドキュメント: https://golang.org/pkg/fmt/strings
パッケージのドキュメント: https://golang.org/pkg/strings/