[インデックス 18335] ファイルの概要
このコミットは、Go言語の標準ライブラリであるtesting
パッケージ内のtesting.go
ファイルに対する変更です。testing
パッケージは、Goプログラムの自動テスト、ベンチマーク、ファジングをサポートするための機能を提供します。testing.go
は、*testing.T
(テスト)および*testing.B
(ベンチマーク)型のコアロジック、テストの実行フロー、およびFailNow()
やSkipNow()
といったテスト制御メソッドの実装を含んでいます。
コミット
testing: fix SkipNow and FailNow to avoid panic(nil) check
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/91fbf6f159a099a273e6880a5fe40351d61270b6
元コミット内容
testing: fix SkipNow and FailNow to avoid panic(nil) check
Sorry, too many windows in which to run all.bash.
Fixes build.
TBR=r
CC=golang-codereviews
https://golang.org/cl/55790043
変更の背景
このコミットは、testing
パッケージにおけるSkipNow()
およびFailNow()
メソッドの挙動に関連するバグを修正します。これらのメソッドは、テストの実行を即座に停止し、それぞれテストをスキップまたは失敗としてマークするために使用されます。内部的には、これらのメソッドはruntime.Goexit()
を呼び出して、現在のゴルーチン(テストを実行しているゴルーチン)を終了させます。
問題は、tRunner
関数(個々のテストを実行する内部関数)のdefer
ブロックにありました。このdefer
ブロックは、テストがパニックを起こしたかどうかを検出し、もしパニックがnil
であった場合にfmt.Errorf("test executed panic(nil)")
というエラーを生成していました。しかし、runtime.Goexit()
が呼び出された場合、recover()
はnil
を返します。これは、panic(nil)
が発生した場合と同じ結果になります。
したがって、SkipNow()
やFailNow()
が呼び出された際に、tRunner
のdefer
ブロックが誤って「panic(nil)
が発生した」と解釈し、不必要なエラーを報告してしまう可能性がありました。この挙動は、テストのビルドプロセスに影響を与え、ビルドが失敗する原因となっていました。このコミットは、SkipNow()
やFailNow()
が正常にテストを終了させた場合と、実際にpanic(nil)
が発生した場合を区別できるようにすることで、この問題を解決します。
前提知識の解説
testing
パッケージ: Go言語の標準ライブラリで、ユニットテスト、ベンチマークテスト、ファジングテストを記述するためのフレームワークを提供します。*testing.T
: 個々のテスト関数に渡される型で、テストのステータス(成功、失敗、スキップ)を制御したり、ログを出力したりするためのメソッドを提供します。*testing.B
: ベンチマーク関数に渡される型で、ベンチマークの実行を制御するためのメソッドを提供します。t.FailNow()
:*testing.T
のメソッドで、現在のテストを失敗としてマークし、そのテストの実行を即座に停止します。テスト関数内の後続のコードは実行されません。t.SkipNow()
:*testing.T
のメソッドで、現在のテストをスキップとしてマークし、そのテストの実行を即座に停止します。テスト関数内の後続のコードは実行されません。runtime.Goexit()
:runtime
パッケージの関数で、現在のゴルーチンを終了させます。この関数が呼び出されると、現在のゴルーチン内のすべてのdefer
関数が実行された後、ゴルーチンが終了します。プログラム全体は終了せず、他のゴルーチンは引き続き実行されます。panic
とは異なり、recover()
では捕捉できません。panic()
: Go言語の組み込み関数で、現在のゴルーチンの通常の実行フローを停止させます。panic
が発生すると、スタックがアンワインドされ、defer
関数が実行されます。recover()
関数を使ってpanic
を捕捉し、プログラムのクラッシュを防ぐことができます。recover()
:defer
関数内で呼び出される組み込み関数で、panic
から回復するために使用されます。panic
が発生していない場合はnil
を返し、panic
が発生している場合はpanic
に渡された引数を返します。tRunner
関数:testing
パッケージの内部関数で、個々のテスト関数(func(t *T)
)を実行するゴルーチンです。テストの開始、終了、パニックの処理、およびFailNow()
やSkipNow()
による終了の管理を行います。
技術的詳細
このコミットの核心は、testing
パッケージがFailNow()
やSkipNow()
によってテストが終了したのか、それともpanic(nil)
によってテストが終了したのかを正確に区別できるようにすることです。
従来のtRunner
関数では、テストの実行が終了した際に、defer
関数内でrecover()
を呼び出し、その結果がnil
であるかどうかをチェックしていました。
var finished bool
defer func() {
t.duration = time.Now().Sub(t.start)
// If the test panicked, print any test output before dying.
err := recover()
if !finished && err == nil {
err = fmt.Errorf("test executed panic(nil)")
}
// ... (rest of the defer block)
}()
t.start = time.Now()
test.F(t)
finished = true
ここで、finished
というローカル変数が導入され、テスト関数test.F(t)
が正常に完了した場合にtrue
に設定されていました。しかし、FailNow()
やSkipNow()
がruntime.Goexit()
を呼び出すと、test.F(t)
の実行は途中で停止し、finished = true
の行は実行されません。このため、defer
ブロックが実行される際にはfinished
はfalse
のままであり、recover()
がnil
を返すため、!finished && err == nil
の条件が真となり、誤って"test executed panic(nil)"
というエラーが生成されていました。
この修正では、common
構造体にfinished
という新しいフィールドが追加されます。このフィールドは、testing.T
やtesting.B
のインスタンスに紐付けられ、テストのライフサイクル全体でその状態を追跡します。
FailNow()
とSkipNow()
が呼び出された際に、runtime.Goexit()
を呼び出す直前にc.finished = true
を設定するように変更されました。これにより、これらのメソッドがテストを正常に終了させたことを明示的にマークします。tRunner
関数内のdefer
ブロックでは、ローカル変数finished
の代わりにt.finished
(common
構造体のフィールド)を使用するように変更されました。- エラーメッセージもより正確に
"test executed panic(nil) or runtime.Goexit"
に変更されました。これは、runtime.Goexit()
による終了とpanic(nil)
による終了が、recover()
がnil
を返すという点で区別が難しいという事実を反映しています。しかし、t.finished
フラグの導入により、この区別は内部的に可能になりました。
この変更により、FailNow()
やSkipNow()
が呼び出された場合、t.finished
がtrue
になるため、!t.finished && err == nil
の条件は偽となり、誤ったpanic(nil)
エラーの生成が回避されます。
コアとなるコードの変更箇所
src/pkg/testing/testing.go
--- a/src/pkg/testing/testing.go
+++ b/src/pkg/testing/testing.go
@@ -143,10 +143,11 @@ var (
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
- mu sync.RWMutex // guards output and failed
- output []byte // Output generated by test or benchmark.
- failed bool // Test or benchmark has failed.
- skipped bool // Test of benchmark has been skipped.
+ mu sync.RWMutex // guards output and failed
+ output []byte // Output generated by test or benchmark.
+ failed bool // Test or benchmark has failed.
+ skipped bool // Test of benchmark has been skipped.
+ finished bool
start time.Time // Time test or benchmark started
duration time.Duration
@@ -275,6 +276,7 @@ func (c *common) FailNow() {
// it would run on a test failure. Because we send on c.signal during
// a top-of-stack deferred function now, we know that the send
// only happens after any other stacked defers have completed.
+ c.finished = true
runtime.Goexit()
}
@@ -338,6 +340,7 @@ func (c *common) Skipf(format string, args ...interface{}) {
// those other goroutines.
func (c *common) SkipNow() {
c.skip()
+ c.finished = true
runtime.Goexit()
}
@@ -376,13 +379,12 @@ func tRunner(t *T, test *InternalTest) {
// returned normally or because a test failure triggered
// a call to runtime.Goexit, record the duration and send
// a signal saying that the test is done.
- var finished bool
defer func() {
t.duration = time.Now().Sub(t.start)
// If the test panicked, print any test output before dying.
err := recover()
- if !finished && err == nil {
- err = fmt.Errorf("test executed panic(nil)")
+ if !t.finished && err == nil {
+ err = fmt.Errorf("test executed panic(nil) or runtime.Goexit")
}
if err != nil {
t.Fail()
@@ -394,7 +396,7 @@ func tRunner(t *T, test *InternalTest) {
t.start = time.Now()
test.F(t)
- finished = true
+ t.finished = true
}
// An internal function but exported because it is cross-package; part of the implementation
コアとなるコードの解説
-
common
構造体へのfinished
フィールドの追加:common
構造体は、*testing.T
と*testing.B
が共有する基盤となるデータとメソッドを保持します。ここにfinished bool
フィールドが追加されました。これは、テストの実行が正常に完了したか、またはFailNow()
/SkipNow()
によって意図的に終了したかを追跡するためのフラグです。 -
FailNow()
およびSkipNow()
でのc.finished = true
の設定:FailNow()
とSkipNow()
の内部でruntime.Goexit()
が呼び出される直前に、c.finished = true
が設定されるようになりました。これは、これらのメソッドがテストの実行を意図的に、かつ正常に終了させたことを示します。 -
tRunner
のdefer
ブロックの変更:var finished bool
というローカル変数の宣言が削除されました。defer
関数内で、!finished && err == nil
の条件が!t.finished && err == nil
に変更されました。これにより、ローカル変数ではなく、common
構造体のfinished
フィールドが参照されるようになります。test.F(t)
の呼び出し後、finished = true
の行がt.finished = true
に変更されました。これは、テスト関数が正常に最後まで実行された場合に、common
構造体のfinished
フィールドをtrue
に設定することを意味します。- エラーメッセージが
"test executed panic(nil)"
から"test executed panic(nil) or runtime.Goexit"
に変更されました。これは、recover()
がnil
を返すケースがpanic(nil)
とruntime.Goexit()
の両方で発生しうることをより正確に反映しています。しかし、t.finished
フラグによって、これらのケースは内部的に区別されます。
これらの変更により、FailNow()
やSkipNow()
が呼び出されてruntime.Goexit()
によってテストが終了した場合、c.finished
がtrue
に設定されているため、tRunner
のdefer
ブロックは!t.finished
の条件が偽となり、誤ってpanic(nil)
エラーを報告することがなくなります。これにより、テストのビルドが正しく行われるようになります。
関連リンク
- Go
testing
パッケージのドキュメント: https://pkg.go.dev/testing - Go
runtime.Goexit()
のドキュメント: https://pkg.go.dev/runtime#Goexit - Go
panic
とrecover
に関するブログ記事やドキュメント (一般的な概念): https://go.dev/blog/defer-panic-and-recover
参考にした情報源リンク
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEjseOuV2x3b1bSm5IWp9Tjc2Nj9HPkZIaZogbHqdYgJTpC80r4uM9UqcmEIA2ioesM1utc2o3p-iPl4gCgoqp9nRpQYFr05XsA6qC_RI5KiOtMUpAq
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEd2gudf_qWfX5xWSez7QZ99kH1JkKEyyE6_NYtAAgIkID_1jwA1aVZ7bFrYYu-65SLJfGQYqRNvZ5kVgNQfwGAFyUvs2OI7zR5wkiM-Rz_5vSVw8yUlxhhzqoonArZGegWKSZ8m5vO9gjdk9t1h3Yfwqja76X9s4aL8pK3qBekW2lj
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEmw2qxeKSmXXp2iRN0T--JhEPASGCqs9FOxE5XUIOFtO0d4CrX6rCsIE1PeHqckFrj4qScvTZhAy2UJdeb9hCmxmIIJlu5rjm6bjuqWLx5z21k4OhKSMFhBnCic5LRl0hdzl5peJnD0irJYttT7wn92yzMaXPkl5quT8ZHa_PX4jeoaZFT7AxzFJXQU48bD024bUZvnXOY
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG1jmv5vsO5mE7p6ye6-60Q4R1ybZ4G-tGEZjybrnoWJ_GPGHIOaGH0BssZYJ3KRef7wxX-4oHx0zB4UH8KY8ro75ywOFuMjkklnYjJ1rC8Ct9eFvN4
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFZL3sdyWyxQa7snQoGdsge1lRdFmbaJvCk5H6-HuAXH1_VltwmkX9aZChG3EbibQFpvcSXz_nbw9g9FyaQldruR-kNU_kALtENPXAzpKluQ9qhZUq5rI1o8e8hIP3XDKb-AE6k2UXABQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHRsoFqrsLDtCKg_zqnNXnngErcfCJaUXHvnNXWuvPMd3UBH00hzAXcKnYbs8Ipyd72_i1Ia01-eG1hKSwSwahSC4-uwlx8AZKpWmSkHIGGjHDgjkZ7ishD3KyelUNY3o7QtTK2PCX9T4ZoqVE_j6jP9ftZoAaeopJLuFjOY7r_HzPQ-l-6NQiCjqMrt4gpC5MfynrBD2VB
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHITrEJ5IGdg_xGpMJUsYos7UsOG_TRV5KFf7F4j9mvNIv6b5u9qR51HO8bvNphEu_5u4gs33Nzqu7ZAvSo4JBMFQkVNmP5SZA0aBEDWSbV1qdCf8JOgBxMB5X7i9GYSX8AWtb8cjpxdQ5M68v-XZYUKlE=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH2egINMl3og37NlVhNlegKLsOsSimcd0l65xp5mmULL_PseAJKjofo3V0PuVIrZzb9tvXCH3suItGo8MDE1fJwuTDW7_UFmfHbLwE9-LvgLxksRQ2g5YY8VyREamlRi3ma3Fyv_sxFIDL3YehhyKRNjcsdQrSxi25KIR9Sl9ZWf-o=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFcXi8PQACTcSFg4UUG1neEaArz-QkjNVrAB1aLnppmu1hAcXiOt91TBuQhs2Hq7zoRmoWmfEto4ci_HzB7ya_NA84BqJmfp__w6g_RJV9M-YhqOKFi5BcLSu5uUu8gLzUjrmFQ7moj2A8HhcNhVRDF4_gptTPdMnz-jSqEx6_4jwj4lK2qKxN6w4uo_oceKSmNK3D1W1IlpGOrO4luFujNVinOApsr2K7I5RI=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGfhMZsBsfW2uPB5Y6XhG9CrardfcZ65-kivUeeqbb9b2W5_NaRO7s55V2UEuHxdcaHaoHoaPYPlcsQmteXaX58oXwLsnwfQZytaArvW4FbETYj2i3ZsCvn-UXw1eg10M7t_p4HiA7EJN20CVdzM0uUzhgso1F2Oe5h
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFUWOGzJh6J8io7pqeI1SjJk0a95trnBukuZXDJxIfmrpknnE_BmBeYZ8z8w4m9xdCyblytOCT170N_oW-rGUmV8-fBxN-nSCL8z5L1KCrCB_lieLPdoJNtZVwgSyQsBdWmkp67QVeZrpHLS1kMyPhnwkHnFwNhIuI3YFCoE93mI5WYPNl1
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGurjeta-FqmXlV4ufwc2-3JExKfow36c2DSHuQHlHOfpQvh46Q9beWlJnRC_K3NyB_CRWB_SaH3BidWH0-eiyxWcEiy3S1I178hfkEG6r1hhvU_99gr9kP3Eagrf_nPpLgLN2i-mK6P_hc16JiasPUfsshFTu6UI-wE2z1zwrSKtOsF-E8dm5U38SFWG8PkxP81oeCBQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE7rscAsn_NEbJDp3Rgeh9eRZ5nzTvx02-Fgh1ubtphg70k8HnuCcPCAgwT8UhySUjBnWtG8vkkD18N8ytu43geGPxv3QCKftM_LVgzrCXe7bkpebgL5oUEPP7ersdV3r8J10q99XMt-5VYf3RJ_ZmUpiod8PkvW0NB4_gWLX3ebNmhccwZZFhZ7_dBtj9183He8pd3utHdequm51B_0OSuHAoi2zgPDAKrzyzuh02hX227NH1lslAdOPja_40Y9VHsj7s3dWGOEGuu6KTsNw==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFSLSbL_pc_cLE-R1ZiyvDTTs4nmI5o0p-bTZsHwnaHlLEoXaBD75jBiiXU-Womln6foXkPWIlFDUTRNMg6-8S_M2aF6mckD40R7no4yjAqUL9IKJbiOkryfc-o7WGhXQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6DhZZaEB9Hyb0hxWZmeM3lVckNUAOBZ8CVV7gFCsGBVrUenStb6j3j2AbNp4R8yCUryaonAMzUmk3SjPTmeZLFMOBzPKzC9aSM2dPmfyFlcZUlg_p3RsSRNQuRmSNPfbjTNU=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGz7LHzoedvzg3TRGZ91pc_yS-zCr6mCdPFp74MABlm9Fdv8UcBxX6NTAd1gdPltZfiYo2jPGiSTmVMxeZmEI3dD0MtEIxWTy11GArNmB9q-Cg6zB9h7leYkV6V_7u_VnIpYY-FpfgzBGCqupLy
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHuD6DCpfMXZZRp_ZhIhCWrZuDglVK34Xq38PGfVYEWgSKxKeId4JyNGDYCqMhT0kIW_TlASmgBGve4PrFWRlIdiqdvSgx_k-BEMPXAzpKluQ9qhZUq5rI1o8e8hIP3XDKb-AE6k2UXABQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGp7wmr4SmpdYKaXni0iVuBU2s2MQS8Ke3SlqZ8TnRwDHyQqrbV5Pqrke13SEeDifXJe8N8GGiANUXVy1rd4RwId9xr62-0TG-611dJ82CSrEl2epuGBH-L-43uztILIdp1_pD9X-sou3O-9fZI1IIsA7SBRysRwqkrFRvHod_npwzYZ1ugwvqKTU0nbnnU695N4oI=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQF5KfmaV_c_uottc2szGqyVfV9edePaENuQOwnlGFn56i0dZSWK6mg8N6zN_nh7QhuIfHkR3ivp2W341QC_PVzIoU5gCSDU4ndVS8SQHpd9V9CjgCHSS6WkVjAd3ZVIHQaP-2V_XnE-Kno1JZ5lkF_ZyA9sE5moLfm9_dGN-FiZxOgCaqCtQO9i5UL
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFSEGgcRH_ErIRaV79EtzU5NjIVCLuzZpkMfarkIfACcnd9sS6NRMd-slf62ZUGPmJEOugnlKKc_B1CQnoFNo7NWX7OHD5hbzp9UI9ozsOh98vep8fD
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHMt-uM4bYrD1qc9X4qT7NcG10djd7t5-6CNtWdEm-j43iTrurJGAPBQbd4BANN96121iiPdit6f-WtkKB02_vddD5e1GANM4U-ISadO3tRgMgljax8kcc8ILwedAAXhQBXOhxWJ39hNS5h85pw64Tq7IEx01vpS0DCoU0yGhEWQ91UnEOxRgnoGuIWV18X
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGHhwZxOL21_6t7MSuPlq8vPC7NfL0p1KgckZI4EzkZWfDd8akaolRGpB4r6q72fqdMWKDOjUDxnFU7C4W_34Ox693tTuC_fTahB4IKJEiMAYlyCHRpAStXyrjFsxfZiofgfE1E3azekgzMSHwARDBH7OwZjN-8ceFUzbE2KiHK0e85L1niFsoyTI3gs6j3pmWroKzR
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFW21rW02aC3upVmkIpBHl4U7aaW1gIx_5XrXN6en66KT7975wvQbcw0F-0sgv4LwVmAS_ILtu-JmHwrMPChKs5jZBM5B4LgIVFDryvYPfGkkeTpsA6c1_hAZ-I5xU0XEb0qDC_