[インデックス 14743] ファイルの概要
このコミットは、Go言語のランタイムにおけるデータ競合検出器(Race Detector)のテストドライバに関する修正です。具体的には、テスト対象のコードにコンパイルエラーが存在する場合に、テストドライバが「0個のテストを実行した」と誤って成功を報告する問題を解決します。この修正により、コンパイルエラーが発生した際には、テストドライバが適切に失敗し、コンパイルエラーメッセージを出力するようになります。
コミット
commit 43f2fc308b92cec6071a4af225af1dafd4d7ba54
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Mon Dec 24 15:33:32 2012 +0400
runtime/race: make test driver print compilation errors
Currently it silently "succeeds" saying that it run 0 tests
if there are compilations errors.
With this change it fails and outputs the compilation error.
R=golang-dev, remyoudompheng
CC=golang-dev
https://golang.org/cl/7002058
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/43f2fc308b92cec6071a4af225af1dafd4d7ba54
元コミット内容
runtime/race: make test driver print compilation errors
Currently it silently "succeeds" saying that it run 0 tests
if there are compilations errors.
With this change it fails and outputs the compilation error.
変更の背景
Go言語のランタイムには、並行処理におけるデータ競合(data race)を検出するための「Race Detector」という強力なツールが組み込まれています。このツールは、並行に実行されるゴルーチンが共有メモリにアクセスする際に、少なくとも一方が書き込み操作であり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に競合を報告します。
このコミットが作成された時点では、Race Detectorのテストスイート(runtime/race/race_test.go
)には問題がありました。テスト対象のコード自体にコンパイルエラーが含まれている場合、テストドライバはエラーを適切に捕捉せず、あたかもテストが正常に実行され、単に「0個のテストが実行された」かのように振る舞っていました。これは、開発者がテストコードのコンパイルエラーに気づきにくく、デバッグを困難にするという問題を引き起こしていました。
このコミットの目的は、このサイレントな成功を修正し、コンパイルエラーが発生した場合にはテストドライバが明確に失敗し、関連するコンパイルエラーメッセージを標準出力に表示するようにすることです。これにより、開発者はテストコードの問題を即座に特定できるようになります。
前提知識の解説
Go言語の testing
パッケージ
Go言語には、ユニットテストやベンチマークテストを記述するための標準パッケージ testing
が用意されています。
func TestXxx(*testing.T)
: テスト関数はTest
で始まり、*testing.T
型の引数を取ります。t.Fatalf(format string, args ...interface{})
: テストが致命的なエラーに遭遇した場合に呼び出されます。メッセージをフォーマットして出力し、テストの実行を停止し、テストを失敗としてマークします。
Go言語の Race Detector
GoのRace Detectorは、Go 1.1で導入された並行処理のデバッグツールです。
- 機能: 実行時にデータ競合を検出します。データ競合は、複数のゴルーチンが同じメモリ位置に同時にアクセスし、少なくとも1つのアクセスが書き込みであり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に発生します。
- 有効化:
go run -race
、go build -race
、go test -race
のように-race
フラグを付けてビルドまたは実行することで有効になります。 GORACE
環境変数: Race Detectorの挙動を制御するための環境変数です。様々なオプションを設定できます。suppress_equal_stacks=0
: 同じスタックトレースを持つ競合レポートを抑制しない(デフォルトは抑制)。suppress_equal_addresses=0
: 同じアドレスを持つ競合レポートを抑制しない(デフォルトは抑制)。exitcode=0
: Race Detectorが競合を検出した場合でも、プロセスが終了する際の終了コードを0にする。通常、競合が検出されると非ゼロの終了コードで終了しますが、テストドライバがRace Detectorの出力を解析する必要がある場合など、特定のシナリオでこのオプションが有用です。
os/exec
パッケージと cmd.CombinedOutput()
Go言語の標準ライブラリ os/exec
は、外部コマンドを実行するための機能を提供します。
cmd.CombinedOutput()
: 外部コマンドを実行し、その標準出力(stdout)と標準エラー出力(stderr)を結合したバイトスライスとして返します。コマンドの実行に失敗した場合(非ゼロの終了コードで終了した場合など)は、エラーも返します。
技術的詳細
このコミットは、src/pkg/runtime/race/race_test.go
内の TestRace
関数と runTests
関数に変更を加えています。
-
TestRace
関数におけるエラーメッセージの改善:- 変更前:
t.Fatalf("Failed to run tests: %v", err)
- 変更後:
t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
この変更により、runTests
関数がエラーを返した場合に、t.Fatalf
がエラーメッセージだけでなく、runTests
が返したtestOutput
(外部コマンドの標準出力と標準エラー出力の結合)も出力するようになります。これにより、コンパイルエラーメッセージがtestOutput
に含まれている場合、それが直接テストの失敗メッセージとして表示されるようになります。
- 変更前:
-
GORACE
環境変数の変更:- 変更前:
GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0"
- 変更後:
GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"
exitcode=0
オプションが追加されました。これは非常に重要です。通常、Race Detectorがデータ競合を検出すると、プロセスは非ゼロの終了コードで終了します。しかし、このテストドライバは、Race Detectorが検出した競合情報を解析するために、外部コマンド(テスト対象のGoプログラム)の出力を読み取る必要があります。もしRace Detectorが非ゼロの終了コードで終了してしまうと、cmd.CombinedOutput()
はエラーを返し、テストドライバは出力を解析する機会を失ってしまいます。exitcode=0
を設定することで、Race Detectorは競合を検出しても終了コードを0で返し、cmd.CombinedOutput()
がエラーを返さずに正常に出力を返すようになります。これにより、テストドライバはコンパイルエラーやRace Detectorのレポートを含む出力を常に取得できるようになります。
- 変更前:
-
runTests
関数におけるエラーハンドリングの改善:- 変更前:
ret, _ := cmd.CombinedOutput() return ret, nil
- 変更後:
return cmd.CombinedOutput()
変更前は、
cmd.CombinedOutput()
が返すエラーを無視し、常にnil
エラーを返していました。これは、外部コマンドが非ゼロの終了コードで終了した場合(例: コンパイルエラーが発生した場合)でも、runTests
関数がエラーを報告しない原因となっていました。変更後は、cmd.CombinedOutput()
が返すエラーをそのままrunTests
の呼び出し元に返すようになりました。これにより、外部コマンドの実行が失敗した場合(コンパイルエラーなど)に、そのエラーが適切にTestRace
関数に伝播され、t.Fatalf
によって報告されるようになります。 - 変更前:
これらの変更が組み合わさることで、テスト対象のGoプログラムにコンパイルエラーがあった場合、cmd.CombinedOutput()
はエラーを返し、そのエラーとコンパイルエラーメッセージを含む出力が TestRace
関数に伝播され、最終的に t.Fatalf
によって詳細なエラーメッセージとして表示されるようになります。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/race/race_test.go
+++ b/src/pkg/runtime/race/race_test.go
@@ -42,7 +42,7 @@ const (
func TestRace(t *testing.T) {
testOutput, err := runTests()
if err != nil {
- t.Fatalf("Failed to run tests: %v", err)
+ t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
}
reader := bufio.NewReader(bytes.NewBuffer(testOutput))
@@ -152,7 +152,6 @@ func runTests() ([]byte, error) {
}
cmd.Env = append(cmd.Env, env)
}
- cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0"`)
- ret, _ := cmd.CombinedOutput()
- return ret, nil
+ cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"`)
+ return cmd.CombinedOutput()
}
コアとなるコードの解説
TestRace
関数内の変更
- t.Fatalf("Failed to run tests: %v", err)
+ t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
この行は、runTests()
関数がエラーを返した場合に、テストを失敗させる t.Fatalf
の呼び出しを変更しています。
- 変更前は、単に
runTests()
から返されたエラー (err
) のみを出力していました。 - 変更後は、エラーメッセージに加えて、
runTests()
が返したtestOutput
(外部コマンドの標準出力と標準エラー出力の結合)も文字列として追加で出力するようにしました。これにより、コンパイルエラーメッセージやその他の診断情報がtestOutput
に含まれている場合、それが直接テストの失敗メッセージの一部として表示され、デバッグが容易になります。
runTests
関数内の変更
- cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0"`)
+ cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"`)
この行は、GORACE
環境変数の設定を変更しています。
- 変更前は、
suppress_equal_stacks=0
とsuppress_equal_addresses=0
のオプションのみを設定していました。これらは、Race Detectorが同じスタックトレースやアドレスを持つ競合レポートを抑制しないようにする設定です。 - 変更後は、これらに加えて
exitcode=0
オプションが追加されました。このオプションは、Race Detectorがデータ競合を検出した場合でも、外部コマンドが非ゼロの終了コードで終了するのを防ぎ、常に終了コード0で終了するようにします。これにより、cmd.CombinedOutput()
がエラーを返さずに、Race Detectorの出力(競合レポートやコンパイルエラーなど)を常に取得できるようになります。これは、テストドライバが外部コマンドの出力を解析するために不可欠です。
- ret, _ := cmd.CombinedOutput()
- return ret, nil
+ return cmd.CombinedOutput()
この行は、外部コマンドの実行結果の扱いを変更しています。
- 変更前は、
cmd.CombinedOutput()
が返すエラーをアンダースコア (_
) で破棄し、常にnil
エラーを返していました。これは、外部コマンドが非ゼロの終了コードで終了した場合(例: コンパイルエラーが発生した場合)でも、runTests
関数がエラーを報告しない原因となっていました。 - 変更後は、
cmd.CombinedOutput()
の戻り値を直接runTests
関数の戻り値として返しています。これにより、cmd.CombinedOutput()
が返すエラー(外部コマンドの実行失敗を示す)が適切にrunTests
の呼び出し元(TestRace
関数)に伝播されるようになります。結果として、コンパイルエラーなどによって外部コマンドが失敗した場合、TestRace
関数がそのエラーを捕捉し、t.Fatalf
を通じて報告できるようになります。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go Race Detectorのドキュメント: https://go.dev/doc/articles/race_detector
- このコミットのGo CL (Code Review): https://golang.org/cl/7002058
参考にした情報源リンク
- Go言語の
testing
パッケージ: https://pkg.go.dev/testing - Go言語の
os/exec
パッケージ: https://pkg.go.dev/os/exec - Go Race Detectorの環境変数
GORACE
についての議論やドキュメント(Goのソースコードや関連するIssue/CLから情報を収集)GORACE
環境変数の詳細なオプションは、Goのソースコード内のsrc/runtime/race/doc.go
や関連するテストファイルで確認できます。- GoのIssueトラッカーやメーリングリストのアーカイブも、特定のオプションの背景や意図を理解するのに役立ちます。