[インデックス 1228] ファイルの概要
このコミットは、Go言語の標準ライブラリであるtestingパッケージ内のexample.goファイルに対する変更です。具体的には、テスト実行時に使用されるコマンドラインフラグ-runと-exampleの挙動を調整し、特定のデバッグシナリオにおけるExampleの実行制御を改善することを目的としています。
コミット
commit 28668c3a28c8eee186362692af981d9f4fc4fc96
Author: Rob Pike <r@golang.org>
Date: Mon Feb 27 16:23:22 2012 +1100
cmd/go: run examples even if -run is set if -example is also set
Allows one to disable everything but the example being debugged.
This time for sure.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5700079
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/28668c3a28c8eee186362692af981d9f4fc4fc96
元コミット内容
このコミットは、cmd/go(goコマンドの内部実装)がExampleを実行する際のロジックを修正するものです。以前の挙動では、go testコマンドに-runフラグが指定されている場合、Exampleは実行されませんでした。この変更により、-runフラグが指定されていても、同時に-exampleフラグも指定されている場合には、Exampleが実行されるようになります。これは、特定のExampleのみをデバッグしたい場合に、他のテストやExampleの実行を抑制しつつ、目的のExampleだけを実行できるようにするための改善です。
変更の背景
Go言語のテストフレームワークでは、go testコマンドを使用してテスト、ベンチマーク、Exampleを実行します。
-run <regexp>フラグは、実行するテストやベンチマークの名前を正規表現でフィルタリングするために使用されます。-example <regexp>フラグは、実行するExampleの名前を正規表現でフィルタリングするために使用されます。
このコミット以前は、-runフラグが設定されていると、testingパッケージはExampleの実行を完全にスキップしていました。これは、開発者が特定のテストケースに集中したい場合に、Exampleが意図せず実行されるのを防ぐためのものでした。しかし、この挙動は、特定のExampleをデバッグしたい場合に不便をもたらしました。例えば、go test -run MyTest -example MyExampleのように、特定のテストと特定のExampleを同時に実行したい場合、-run MyTestが存在するためにMyExampleが実行されないという問題がありました。
このコミットの目的は、この制約を緩和し、開発者が-runフラグでテストをフィルタリングしつつも、-exampleフラグを明示的に指定することで、特定のExampleを強制的に実行できるようにすることです。これにより、デバッグ作業の柔軟性が向上します。コミットメッセージにある「This time for sure.」という表現は、同様の意図を持つ以前の試みがあったことを示唆しており、今回の変更でその問題が確実に解決されることを強調しています。
前提知識の解説
Go言語のtestingパッケージとExample
Go言語の標準ライブラリには、テスト、ベンチマーク、Exampleを記述するためのtestingパッケージが含まれています。
- テスト関数:
func TestXxx(*testing.T)というシグネチャを持つ関数で、コードの正確性を検証します。 - ベンチマーク関数:
func BenchmarkXxx(*testing.B)というシグネチャを持つ関数で、コードのパフォーマンスを測定します。 - Example関数:
func ExampleXxx()またはfunc ExampleXxx_Yyy()というシグネチャを持つ関数で、コードの使用例を示します。Example関数は、その出力がコメントとして記述された期待される出力と一致するかどうかをgo testコマンドが検証します。これにより、ドキュメントとコードの整合性を保つことができます。Exampleは、パッケージのドキュメントやgodocに自動的に組み込まれるため、コードの利用方法を理解する上で非常に重要です。
go testコマンドのフラグ
go testコマンドは、テスト実行を制御するための様々なコマンドラインフラグを提供します。
-run <regexp>: テスト関数やベンチマーク関数の名前を正規表現でフィルタリングします。指定された正規表現にマッチする名前の関数のみが実行されます。-example <regexp>: Example関数の名前を正規表現でフィルタリングします。指定された正規表現にマッチする名前のExample関数のみが実行されます。-v: 詳細なテスト結果を表示します。-bench <regexp>: ベンチマークを実行し、指定された正規表現にマッチするベンチマーク関数のみを実行します。
*matchと*matchExamples
src/pkg/testing/example.goのような内部ファイルでは、go testコマンドのフラグは、flagパッケージによってパースされ、グローバル変数としてアクセスされます。
*match:-runフラグの値に対応するstring型のポインタ変数です。*matchExamples:-exampleフラグの値に対応するstring型のポインタ変数です。
これらの変数は、テスト実行ロジック内で、どのテストやExampleを実行すべきかを判断するために使用されます。
技術的詳細
この変更は、src/pkg/testing/example.goファイル内のRunExamples関数にあります。この関数は、GoのテストフレームワークがExampleを実行する際に呼び出される内部関数です。
変更前のコードは以下のようになっていました。
func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
if *match != "" {
return // Don't run examples if testing is restricted: we're debugging.
}
ok = true
// ... (Example実行ロジック)
}
このロジックでは、*match(つまり-runフラグの値)が空文字列でなければ、即座にreturnし、Exampleの実行をスキップしていました。これは、「テストが制限されている(-runが指定されている)場合は、デバッグ中なのでExampleは実行しない」という意図でした。
変更後のコードは以下のようになります。
func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
if *match != "" && *matchExamples == "" {
return // Don't run examples if testing is restricted: we're debugging.
}
ok = true
// ... (Example実行ロジック)
}
この変更により、Exampleの実行をスキップする条件が*match != ""から*match != "" && *matchExamples == ""に変わりました。
この新しい条件は、以下の論理を意味します。
-runフラグが設定されている(*match != "")- かつ
-exampleフラグが設定されていない(*matchExamples == "") この両方の条件が真である場合にのみ、Exampleの実行がスキップされます。
したがって、もし-runフラグが設定されていても、-exampleフラグが同時に設定されている場合(つまり*matchExamples != ""の場合)、*matchExamples == ""の条件が偽になるため、if文のブロックは実行されず、RunExamples関数はExampleの実行ロジックに進むことになります。
この修正は、Goのテストフレームワークがコマンドライン引数をどのように解釈し、テスト実行フローを制御しているかを示す良い例です。特に、複数のフラグが組み合わされた場合の挙動を細かく調整することで、開発者の利便性を高めています。
コアとなるコードの変更箇所
変更はsrc/pkg/testing/example.goファイルのRunExamples関数内の一行です。
--- a/src/pkg/testing/example.go
+++ b/src/pkg/testing/example.go
@@ -23,7 +23,7 @@ type InternalExample struct {
}
func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
- if *match != "" {
+ if *match != "" && *matchExamples == "" {
return // Don't run examples if testing is restricted: we're debugging.
}
ok = true
コアとなるコードの解説
変更された行は、RunExamples関数の冒頭にある条件分岐です。
-
変更前:
if *match != ""- これは、「もし
-runフラグが指定されていたら(*matchが空でなければ)、Exampleの実行をスキップする」という意味でした。このロジックは、-runが指定されている限り、-exampleが指定されていてもExampleが実行されないという副作用がありました。
- これは、「もし
-
変更後:
if *match != "" && *matchExamples == ""- これは、「もし
-runフラグが指定されていて(*matchが空でなく)、かつ-exampleフラグが指定されていない(*matchExamplesが空である)ならば、Exampleの実行をスキップする」という意味になります。 - この新しい条件により、
-runが指定されていても、-exampleが明示的に指定されていれば(例えばgo test -run MyTest -example MyExampleのように)、*matchExamples == ""の部分が偽となり、if文のブロックがスキップされ、Exampleが実行されるようになります。 - これにより、開発者は
-runで他のテストをフィルタリングしつつ、-exampleで特定のExampleのみを実行するという、より柔軟なデバッグワークフローを実現できます。
- これは、「もし
この修正は、Goのテストツールが提供するコマンドラインフラグの相互作用を改善し、ユーザーがより細かくテスト実行を制御できるようにするための、小さなしかし重要な変更です。
関連リンク
- Go言語の
testingパッケージのドキュメント: https://pkg.go.dev/testing go testコマンドのドキュメント: https://pkg.go.dev/cmd/go#hdr-Test_packages- このコミットのChange-ID (Gerrit): https://golang.org/cl/5700079
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/pkg/testing/example.go) - Go言語のIssueトラッカーやGerritのレビューコメント(コミットメッセージに記載されたCLリンクから辿れる情報)
- Go言語のテストに関する一般的な知識