[インデックス 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
コマンドのロジックを実装しています。
-
regexp
パッケージのインポート: カバレッジ情報を抽出するために、新たにregexp
パッケージがインポートされました。import ( // ... "regexp" // 追加 // ... )
-
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
からカバレッジ情報を抽出し、整形された文字列を返します。 -
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
コマンドの実行ロジックを定義しています。
-
import "regexp"
の追加: 正規表現を使用するために、regexp
パッケージがインポートされました。 -
runTest
関数の変更:runTest
関数は、個々のパッケージのテスト実行を管理し、その結果を標準出力に書き出す役割を担っています。 変更された行:fmt.Fprintf(a.testOutput, "ok \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out))
この行は、テストが成功した場合に
ok
で始まるサマリー行を生成します。以前はパッケージのインポートパスとテストにかかった時間のみが出力されていましたが、この変更により、新しく追加されたcoveragePercentage(out)
関数の戻り値が追加されるようになりました。out
引数は、テスト実行の標準出力全体(バイトスライス)を含んでおり、この中からカバレッジ情報が抽出されます。 -
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
の出力がより簡潔で情報密度の高いものとなり、開発者がテスト結果とカバレッジ情報を同時に把握しやすくなりました。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
go test
コマンドのドキュメント: https://go.dev/cmd/go/#hdr-Test_packages- Go言語のテストカバレッジに関するブログ記事 (Go 1.2での導入): https://go.dev/blog/cover
- このコミットが属するGoの変更リスト (CL): https://golang.org/cl/10413044
参考にした情報源リンク
- Go言語の
regexp
パッケージのドキュメント: https://pkg.go.dev/regexp - Go言語の
fmt
パッケージのドキュメント: https://pkg.go.dev/fmt - Go言語のテストに関する公式ブログ記事: https://go.dev/blog/testing
- Go言語のコードカバレッジツールに関する公式ブログ記事: https://go.dev/blog/cover
- GitHub上のGoリポジトリ: https://github.com/golang/go