Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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)

この新しいフォーマット文字列では、以下の要素が追加されています。

  1. # 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スクリプト自身の引数として解釈されるようにします。
  2. 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.gomain 関数内で、個々のテストの結果を出力するループの中にあります。

        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 の行をコピーして実行するだけで、特定の失敗したテストを再実行し、デバッグ作業を効率的に進めることができます。

関連リンク

参考にした情報源リンク