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

[インデックス 16605] ファイルの概要

このコミットは、Go言語のコマンドラインツールcmd/goにおけるgo testコマンドの出力形式を改善するものです。具体的には、テストカバレッジ情報(go test -cover実行時)を各パッケージのテスト結果のサマリー行に直接表示するように変更しています。これにより、テスト結果とカバレッジ情報を一目で確認できるようになり、開発者の利便性が向上します。

コミット

commit 9824b018cedcb636cc1210a3a9f0249d1d44fe49
Author: Rob Pike <r@golang.org>
Date:   Thu Jun 20 10:27:44 2013 -0700

    cmd/go: put the coverage information on the summary line.
    Output now:
    ok      crypto/aes      0.060s  coverage: 89.8% of statements
    ok      crypto/des      0.074s  coverage: 92.2% of statements
    ok      crypto/dsa      0.056s  coverage: 34.5% of statements
    ok      crypto/ecdsa    0.058s  coverage: 86.8% of statements
    ok      crypto/elliptic 0.039s  coverage: 94.6% of statements
    ok      crypto/hmac     0.037s  coverage: 93.5% of statements
    ok      crypto/md5      0.031s  coverage: 96.2% of statements
    ok      crypto/rand     0.074s  coverage: 9.9% of statements
    ok      crypto/rc4      0.090s  coverage: 66.7% of statements
    ok      crypto/rsa      0.253s  coverage: 83.5% of statements
    
    R=rsc, adg
    CC=golang-dev
    https://golang.org/cl/10413044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/9824b018cedcb636cc1210a3a9f0249d1d44fe49

元コミット内容

cmd/go: put the coverage information on the summary line.

このコミットは、go test -coverコマンドの出力形式を変更し、テストカバレッジ情報を各パッケージのテスト結果のサマリー行に含めるようにします。変更後の出力例が示されており、各ok行の末尾にcoverage: XX.X% of statementsという形式でカバレッジ情報が追加されています。

変更の背景

Go言語のテストツールgo testは、-coverフラグを付けることでコードカバレッジを測定し、その結果を出力することができます。しかし、このコミット以前は、カバレッジ情報はテスト出力の別の場所に表示されるか、あるいはサマリー行とは分離されていました。

開発者がテスト結果を確認する際、各パッケージのテストが成功したかどうかだけでなく、そのカバレッジ率も同時に把握できることは非常に重要です。カバレッジ情報がサマリー行に統合されることで、テスト結果の可読性が向上し、開発者はより迅速にコードの品質状態を把握できるようになります。これにより、テスト実行後の情報収集の手間が省け、開発ワークフローが効率化されます。

前提知識の解説

Go言語のgo testコマンドとカバレッジ

Go言語には、標準でテストフレームワークが組み込まれており、go testコマンドを使用してテストを実行します。

  • go test: 指定されたパッケージ内のテスト関数(Testで始まる関数)を実行します。
  • go test -cover: テストを実行するだけでなく、コードカバレッジ(テストによって実行されたコードの割合)を測定します。通常、このコマンドを実行すると、テスト結果に加えてカバレッジの統計情報が出力されます。

テスト出力の構造

go testコマンドの標準的な出力は、各パッケージのテスト結果を要約した行を含みます。 例: ok crypto/aes 0.060s これは、crypto/aesパッケージのテストが成功し、0.060秒かかったことを示します。

正規表現(Regular Expression)

正規表現は、文字列のパターンを記述するための強力なツールです。このコミットでは、テスト出力の中からカバレッジ情報を抽出するために正規表現が使用されています。 Go言語では、標準ライブラリのregexpパッケージが正規表現をサポートしています。

  • regexp.MustCompile(pattern string): 指定されたパターン文字列から正規表現オブジェクトをコンパイルします。パターンが無効な場合はパニックを起こします。
  • regexp.Regexp.FindSubmatch(b []byte): 入力バイトスライスbの中から、正規表現にマッチする最初の部分を見つけ、そのマッチ全体とキャプチャグループ(サブマッチ)をバイトスライスのスライスとして返します。マッチが見つからない場合はnilを返します。

fmt.Fprintf関数

fmtパッケージは、Go言語におけるフォーマットされたI/Oを実装します。

  • fmt.Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error): 指定されたio.Writer(この場合はテスト出力先)に、フォーマット文字列formatに従って引数aを書き込みます。

技術的詳細

このコミットの主要な変更点は、src/cmd/go/test.goファイルに集中しています。このファイルは、go testコマンドのロジックを実装しています。

  1. regexpパッケージのインポート: カバレッジ情報を抽出するために、新たにregexpパッケージがインポートされました。

    import (
        // ...
        "regexp" // 追加
        // ...
    )
    
  2. runTest関数の出力変更: runTest関数は、各パッケージのテストが完了した際のサマリー行を出力する役割を担っています。この関数内のfmt.Fprintf呼び出しが変更され、新たにcoveragePercentage(out)関数の戻り値が追加されるようになりました。

    変更前:

    fmt.Fprintf(a.testOutput, "ok  \t%s\t%s\n", a.p.ImportPath, t)
    

    変更後:

    fmt.Fprintf(a.testOutput, "ok  \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out))
    

    ここで、outはテスト実行の標準出力(バイトスライス)を含んでいます。coveragePercentage関数はこのoutからカバレッジ情報を抽出し、整形された文字列を返します。

  3. coveragePercentage関数の追加: このコミットの核心となるのが、新しく追加されたcoveragePercentage関数です。この関数は、テスト実行の出力(out []byte)を解析し、カバレッジのパーセンテージを抽出して整形された文字列として返します。

    • カバレッジが有効でない場合: testCoverグローバル変数がfalse(つまり-coverフラグが指定されていない)の場合、この関数は空文字列を返し、カバレッジ情報は出力されません。

    • 正規表現による抽出:

      re := regexp.MustCompile(`test coverage for [^ ]+: (.*)\n`)
      

      この正規表現は、テスト出力に含まれるカバレッジ情報を示す行にマッチするように設計されています。

      • test coverage for : 固定文字列
      • [^ ]+: スペース以外の文字が1回以上続く部分(パッケージ名に相当)
      • : : 固定文字列
      • (.*): 任意の文字が0回以上続く部分。これがカバレッジのパーセンテージと「of statements」を含む部分であり、キャプチャグループとして抽出されます。
      • \n: 改行文字

      re.FindSubmatch(out)は、この正規表現にマッチする最初の部分を見つけ、キャプチャグループの内容をmatchesスライスに格納します。matches[1]が、カバレッジのパーセンテージを含む文字列(例: "89.8% of statements")に対応します。

    • 結果の整形: 抽出されたカバレッジ情報がfmt.Sprintf("\tcoverage: %s", matches[1])によって整形され、サマリー行に追加される形式(例: "\tcoverage: 89.8% of statements")になります。

    • カバレッジ情報が見つからない場合: もし正規表現がマッチしなかった場合(例: テスト出力にカバレッジ情報が含まれていない、または形式が異なる場合)、(missing coverage statistics)という文字列が返されます。

この変更により、go test -coverを実行した際に、各パッケージのテスト結果のサマリー行に直接カバレッジ情報が表示されるようになり、開発者はより効率的にテスト結果を把握できるようになりました。

コアとなるコードの変更箇所

diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index 2e23526530..32f342288e 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -16,6 +16,7 @@ import (
 	"os/exec"
 	"path"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"sort"
 	"strings"
@@ -781,7 +782,7 @@ func (b *builder) runTest(a *action) error {
 		if testShowPass {
 			a.testOutput.Write(out)
 		}
-		fmt.Fprintf(a.testOutput, "ok  \t%s\t%s\n", a.p.ImportPath, t)
+		fmt.Fprintf(a.testOutput, "ok  \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out))\n
 		return nil
 	}
 
@@ -797,6 +798,23 @@ func (b *builder) runTest(a *action) error {
 	return nil
 }
 
+// coveragePercentage returns the coverage results (if enabled) for the
+// test. It uncovers the data by scanning the output from the test run.
+func coveragePercentage(out []byte) string {
+	if !testCover {
+		return ""
+	}
+	// The string looks like
+	//	test coverage for encoding/binary: 79.9% of statements
+	// Extract the piece from the percentage to the end of the line.
+	re := regexp.MustCompile(`test coverage for [^ ]+: (.*)\n`)
+	matches := re.FindSubmatch(out)
+	if matches == nil {
+		return "(missing coverage statistics)"
+	}
+	return fmt.Sprintf("\tcoverage: %s", matches[1])
+}
+
 // cleanTest is the action for cleaning up after a test.
 func (b *builder) cleanTest(a *action) error {
 	if buildWork {

コアとなるコードの解説

src/cmd/go/test.go

このファイルは、go testコマンドの実行ロジックを定義しています。

  1. import "regexp" の追加: 正規表現を使用するために、regexpパッケージがインポートされました。

  2. runTest関数の変更: runTest関数は、個々のパッケージのテスト実行を管理し、その結果を標準出力に書き出す役割を担っています。 変更された行:

    fmt.Fprintf(a.testOutput, "ok  \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out))
    

    この行は、テストが成功した場合にokで始まるサマリー行を生成します。以前はパッケージのインポートパスとテストにかかった時間のみが出力されていましたが、この変更により、新しく追加されたcoveragePercentage(out)関数の戻り値が追加されるようになりました。out引数は、テスト実行の標準出力全体(バイトスライス)を含んでおり、この中からカバレッジ情報が抽出されます。

  3. coveragePercentage関数の追加: この新しい関数は、テスト実行の出力からカバレッジ情報を解析し、整形された文字列を返します。

    func coveragePercentage(out []byte) string {
        if !testCover {
            return ""
        }
        // The string looks like
        //	test coverage for encoding/binary: 79.9% of statements
        // Extract the piece from the percentage to the end of the line.
        re := regexp.MustCompile(`test coverage for [^ ]+: (.*)\n`)
        matches := re.FindSubmatch(out)
        if matches == nil {
            return "(missing coverage statistics)"
        }
        return fmt.Sprintf("\tcoverage: %s", matches[1])
    }
    
    • if !testCover { return "" }: go test -coverが実行されていない場合(testCoverフラグがfalseの場合)、カバレッジ情報は不要なので空文字列を返します。
    • re := regexp.MustCompile(...): テスト出力の中からカバレッジ情報を含む行を特定するための正規表現をコンパイルします。この正規表現は、test coverage for <package_name>: <percentage>% of statementsという形式の行にマッチし、特に<percentage>% of statementsの部分をキャプチャグループとして抽出します。
    • matches := re.FindSubmatch(out): out(テスト出力全体)に対して正規表現マッチングを実行します。matchesは、マッチした部分文字列の配列であり、matches[0]は全体のマッチ、matches[1]は最初のキャプチャグループ(カバレッジのパーセンテージと「of statements」を含む部分)に対応します。
    • if matches == nil { ... }: マッチが見つからなかった場合(例えば、カバレッジ情報が全く出力されていない、または予期せぬ形式の場合)、(missing coverage statistics)というメッセージを返します。
    • return fmt.Sprintf("\tcoverage: %s", matches[1]): 抽出されたカバレッジ情報(matches[1])をcoverage: というプレフィックスとタブ文字で整形し、最終的な出力文字列として返します。

この一連の変更により、go test -coverの出力がより簡潔で情報密度の高いものとなり、開発者がテスト結果とカバレッジ情報を同時に把握しやすくなりました。

関連リンク

参考にした情報源リンク