[インデックス 17662] ファイルの概要
このコミットは、Go言語のテスト実行ツール test/run.go
における改善です。具体的には、個別のテストが失敗した際に、そのテストを単独で再実行するためのコマンドラインを標準出力に表示するように変更されています。これにより、開発者がテストの失敗原因を特定し、デバッグする際の利便性が向上します。
コミット
commit 8ce9a4fd26167c8abc912808bb632c357013573d
Author: Russ Cox <rsc@golang.org>
Date: Fri Sep 20 15:25:59 2013 -0400
test/run: print command line for running an individual test
Fixes #5087.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13812043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8ce9a4fd26167c8abc912808bb632c357013573d
元コミット内容
test/run: print command line for running an individual test
Fixes #5087.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13812043
変更の背景
この変更は、Go言語のIssue #5087 に対応するものです。Goのテストスイートは非常に広範であり、多数のテストケースを含んでいます。テストが失敗した場合、通常はテストスイート全体を実行して結果を確認しますが、特定のテストが失敗した際に、そのテストだけを効率的に再実行してデバッグしたいというニーズがありました。
以前の test/run.go
の挙動では、テストが失敗しても、そのテストを単独で実行するための具体的なコマンドラインが示されませんでした。そのため、開発者は手動でテストファイル名や関連する引数を調べてコマンドを構築する必要があり、これは特に複雑なテストや多数のテストがある場合に手間がかかる作業でした。
このコミットは、このデバッグプロセスを簡素化し、開発者の生産性を向上させることを目的としています。失敗したテストの隣に、そのテストを直接実行するための go run run.go -- <test_file_path>
コマンドを表示することで、開発者はすぐにそのコマンドをコピー&ペーストしてテストを再実行し、問題の切り分けやデバッグを迅速に行えるようになります。
前提知識の解説
- Go言語のテストフレームワーク: Go言語には標準で
testing
パッケージが提供されており、これを用いてユニットテストやベンチマークテストを記述します。go test
コマンドがこれらのテストを実行するための主要なツールです。 test/run.go
: Goプロジェクトのルートディレクトリにあるtest/run.go
は、Go言語の標準ライブラリやツールのテストスイート全体を実行するための内部的なスクリプトです。これはgo test
コマンドとは異なり、より低レベルで、特定のテスト環境のセットアップや、複数のテストパッケージの実行をオーケストレーションする役割を担っています。Goのソースコードリポジトリ内で、all.bash
などのスクリプトから呼び出され、Goの様々なコンポーネントのテストを実行するために使用されます。go run
コマンド:go run
コマンドは、Goのソースファイルをコンパイルして実行するコマンドです。このコミットでは、test/run.go
スクリプト自体をgo run
で実行し、その引数として個別のテストファイルパスを渡すことで、特定のテストを単独で実行する方法が示されています。- Issueトラッキングシステム:
Fixes #5087
という記述は、このコミットがGoプロジェクトのIssueトラッキングシステム(通常はGoのGitHubリポジトリのIssuesセクション)における5087番の課題を解決することを示しています。これにより、コミットと関連する課題が紐付けられ、変更の目的が明確になります。 fmt.Printf
: Go言語の標準ライブラリfmt
パッケージに含まれる関数で、フォーマットされた文字列を標準出力に出力するために使用されます。C言語のprintf
に似ています。path.Join
: Go言語の標準ライブラリpath
パッケージに含まれる関数で、プラットフォームに依存しない形でパスの要素を結合します。このコミットでは、テストのディレクトリとファイル名を結合して完全なパスを生成するために使用されています。
技術的詳細
このコミットの技術的な変更は、test/run.go
スクリプトのmain
関数内で行われています。具体的には、テスト結果の出力部分、特にテストが失敗した場合のメッセージ出力ロジックが修正されています。
変更前は、テストが失敗した場合、fmt.Printf
を使ってテストのアクション、ファイル名、エラー文字列のみが出力されていました。
fmt.Printf("%-20s %-20s: %s\n", test.action, test.goFileName(), errStr)
この出力だけでは、失敗したテストを再実行するために必要な情報(テストファイルの完全なパス)が直接提供されていませんでした。
変更後は、fmt.Printf
のフォーマット文字列が拡張され、失敗したテストの前に、そのテストを単独で実行するためのgo run
コマンドの例が追加されました。
fmt.Printf("# go run run.go -- %s\n%-20s %-20s: %s\n", path.Join(test.dir, test.gofile), test.action, test.goFileName(), errStr)
この新しいフォーマット文字列では、以下の要素が追加されています。
# go run run.go -- %s\n
: これはコメント行として、go run run.go -- <test_file_path>
という形式のコマンドラインの例を示しています。%s
の部分には、path.Join(test.dir, test.gofile)
によって生成されたテストファイルの完全なパスが挿入されます。--
は、go run run.go
に対する引数の終わりを示し、それ以降の引数(この場合はテストファイルのパス)がrun.go
スクリプト自身の引数として解釈されるようにします。path.Join(test.dir, test.gofile)
:test
構造体から取得したテストのディレクトリ(test.dir
)とテストのGoファイル名(test.gofile
)を結合し、テストファイルの絶対パスを生成します。これにより、どのディレクトリからgo run run.go
を実行しても、正しいテストファイルが指定されるようになります。
この変更により、テストが失敗した際に、開発者は出力されたコマンドラインをそのままコピーしてターミナルに貼り付けるだけで、特定の失敗したテストを再実行できるようになり、デバッグの効率が大幅に向上します。
コアとなるコードの変更箇所
test/run.go
ファイルの main
関数内の fmt.Printf
の呼び出し箇所が変更されています。
--- a/test/run.go
+++ b/test/run.go
@@ -135,7 +135,7 @@ func main() {
if !*verbose && test.err == nil {
continue
}
- fmt.Printf("%-20s %-20s: %s\n", test.action, test.goFileName(), errStr)
+ fmt.Printf("# go run run.go -- %s\n%-20s %-20s: %s\n", path.Join(test.dir, test.gofile), test.action, test.goFileName(), errStr)
}
if *summary {
コアとなるコードの解説
変更された行は、test/run.go
の main
関数内で、個々のテストの結果を出力するループの中にあります。
if !*verbose && test.err == nil {
continue
}
// 変更前:
// fmt.Printf("%-20s %-20s: %s\n", test.action, test.goFileName(), errStr)
// 変更後:
fmt.Printf("# go run run.go -- %s\n%-20s %-20s: %s\n", path.Join(test.dir, test.gofile), test.action, test.goFileName(), errStr)
このコードブロックは、各テストの実行結果を処理する部分です。
if !*verbose && test.err == nil { continue }
の条件は、verbose
フラグが設定されておらず、かつテストがエラーなく成功した場合には、そのテストの結果を出力せずに次のテストに進むことを意味します。つまり、このfmt.Printf
は、verbose
フラグが設定されている場合、またはテストが失敗した場合にのみ実行されます。
変更の核心は、fmt.Printf
の第一引数であるフォーマット文字列と、それに渡される引数にあります。
-
変更前:
fmt.Printf("%-20s %-20s: %s\n", test.action, test.goFileName(), errStr)
test.action
: テストが実行されたアクション(例:run
)test.goFileName()
: テストが記述されているGoファイルの名前(例:foo.go
)errStr
: エラーメッセージ(テストが失敗した場合)- この出力は、どのテストが失敗したかは示しますが、そのテストを再実行するための具体的なコマンドは示しませんでした。
-
変更後:
fmt.Printf("# go run run.go -- %s\n%-20s %-20s: %s\n", path.Join(test.dir, test.gofile), test.action, test.goFileName(), errStr)
- 新しいフォーマット文字列の最初の部分
"# go run run.go -- %s\n"
が追加されました。#
: コメントとして表示されることを意図しています。go run run.go --
:test/run.go
スクリプトをgo run
で実行するための基本的なコマンドです。--
は、これ以降の引数がrun.go
スクリプト自身の引数として扱われることを示します。%s
: ここに、path.Join(test.dir, test.gofile)
によって生成された、失敗したテストのGoファイルの完全なパスが挿入されます。\n
: 改行。これにより、コマンドラインの例が独立した行として表示されます。
- 続く
%-20s %-20s: %s\n
とそれに続く引数 (test.action
,test.goFileName()
,errStr
) は、変更前の出力形式を維持し、テストのアクション、ファイル名、エラーメッセージを引き続き表示します。
この変更により、テストが失敗した際に、以下のような出力が得られるようになります(例):
# go run run.go -- /path/to/go/src/pkg/net/http/httptest/server_test.go
FAIL net/http/httptest: server_test.go: some error message
開発者は、# go run run.go -- /path/to/go/src/pkg/net/http/httptest/server_test.go
の行をコピーして実行するだけで、特定の失敗したテストを再実行し、デバッグ作業を効率的に進めることができます。
関連リンク
- Go Issue 5087: https://github.com/golang/go/issues/5087
- Go CL 13812043: https://go.dev/cl/13812043 (Gerrit Code Review)
参考にした情報源リンク
- Go言語の公式ドキュメント (testingパッケージ, go command): https://go.dev/pkg/testing/, https://go.dev/cmd/go/
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- Go言語のIssueトラッカー: https://github.com/golang/go/issues
- Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/