[インデックス 19430] ファイルの概要
このコミットは、Go言語のコマンドラインツールcmd/go
において、package foo_test
形式の外部テストパッケージに対するコードカバレッジの計測が正しく行われない問題を修正するものです。具体的には、テスト実行時に生成される_testmain.go
ファイルの生成ロジックを改善し、外部テストパッケージのカバレッジ情報が適切に収集されるように変更しています。
影響を受けるファイルは以下の通りです。
src/cmd/go/test.bash
: カバレッジテストのシェルスクリプトsrc/cmd/go/test.go
:go test
コマンドのテスト実行ロジック
コミット
commit f9c6ad9b6bad25bff480b95d3ebf6a780e86964d
Author: Russ Cox <rsc@golang.org>
Date: Wed May 21 13:59:14 2014 -0400
cmd/go: fix coverage for 'package foo_test' tests
Fixes #8062.
LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/91610046
---
src/cmd/go/test.bash | 36 ++++++++++++++++++++-------------\n src/cmd/go/test.go | 56 ++++++++++++++++++++++++++++++----------------------
2 files changed, 54 insertions(+), 38 deletions(-)
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index 9a4fc1fa66..0060ce2185 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -563,37 +563,50 @@ TEST source file name order preserved
./testgo test testdata/example[12]_test.go || ok=false
# Check that coverage analysis works at all.
-# Don't worry about the exact numbers
+# Don't worry about the exact numbers but require not 0.0%.
+checkcoverage() {
+\tif grep '[^0-9]0\.0%' testdata/cover.txt >/dev/null; then
+\t\techo 'some coverage results are 0.0%'
+\t\tok=false
+\tfi
+\tcat testdata/cover.txt
+\trm -f testdata/cover.txt
+}
+
TEST coverage runs
-./testgo test -short -coverpkg=strings strings regexp || ok=false
-./testgo test -short -cover strings math regexp || ok=false
+./testgo test -short -coverpkg=strings strings regexp >testdata/cover.txt 2>&1 || ok=false
+./testgo test -short -cover strings math regexp >>testdata/cover.txt 2>&1 || ok=false
+checkcoverage
# Check that coverage analysis uses set mode.
TEST coverage uses set mode
-if ./testgo test -short -coverpkg=encoding/binary -coverprofile=testdata/cover.out; then
+if ./testgo test -short -cover encoding/binary -coverprofile=testdata/cover.out >testdata/cover.txt 2>&1; then
\tif ! grep -q 'mode: set' testdata/cover.out; then
\t\tok=false
\tfi
+\tcheckcoverage
else
\tok=false
fi
-rm -f testdata/cover.out
+rm -f testdata/cover.out testdata/cover.txt
TEST coverage uses atomic mode for -race.
-if ./testgo test -short -race -coverpkg=encoding/binary -coverprofile=testdata/cover.out; then
+if ./testgo test -short -race -cover encoding/binary -coverprofile=testdata/cover.out >testdata/cover.txt 2>&1; then
\tif ! grep -q 'mode: atomic' testdata/cover.out; then
\t\tok=false
\tfi
+\tcheckcoverage
else
\tok=false
fi
rm -f testdata/cover.out
TEST coverage uses actual setting to override even for -race.
-if ./testgo test -short -race -coverpkg=encoding/binary -covermode=count -coverprofile=testdata/cover.out; then
+if ./testgo test -short -race -cover encoding/binary -covermode=count -coverprofile=testdata/cover.out >testdata/cover.txt 2>&1; then
\tif ! grep -q 'mode: count' testdata/cover.out; then
\t\tok=false
\tfi
+\tcheckcoverage
else
\tok=false
fi
@@ -601,13 +614,8 @@ rm -f testdata/cover.out
TEST coverage with cgo
d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)\n-./testgo test -short -cover ./testdata/cgocover >$d/cgo.out 2>&1 || ok=false
-cat $d/cgo.out
-if grep 'coverage: 0.0%' $d/cgo.out >/dev/null; then
-\tok=false
-\techo no coverage for cgo package
-\tok=false
-fi
+./testgo test -short -cover ./testdata/cgocover >testdata/cover.txt 2>&1 || ok=false
+checkcoverage
TEST cgo depends on syscall
rm -rf $GOROOT/pkg/*_race
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index b30182791e..5935c98db9 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -715,13 +715,20 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\n }\n }\n \n-\t// writeTestmain writes _testmain.go but also updates\n-\t// pmain.imports to reflect the import statements written\n-\t// to _testmain.go. This metadata is needed for recompileForTest\n-\t// and the builds below.\n-\tif err := writeTestmain(filepath.Join(testDir, "_testmain.go"), pmain, ptest, pxtest); err != nil {\n+\t// Do initial scan for metadata needed for writing _testmain.go\n+\t// Use that metadata to update the list of imports for package main.\n+\t// The list of imports is used by recompileForTest and by the loop\n+\t// afterward that gathers t.Cover information.\n+\tt, err := loadTestFuncs(ptest)\n+\tif err != nil {\n \t\treturn nil, nil, nil, err\n \t}\n+\tif t.NeedTest || ptest.coverMode != "" {\n+\t\tpmain.imports = append(pmain.imports, ptest)\n+\t}\n+\tif t.NeedXtest {\n+\t\tpmain.imports = append(pmain.imports, pxtest)\n+\t}\n \n \tif ptest != p && localCover {\n \t\t// We have made modifications to the package p being tested\n@@ -739,6 +746,18 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\n \t\trecompileForTest(pmain, p, ptest, testDir)\n \t}\n \n+\tfor _, cp := range pmain.imports {\n+\t\tif len(cp.coverVars) > 0 {\n+\t\t\tt.Cover = append(t.Cover, coverInfo{cp, cp.coverVars})\n+\t\t}\n+\t}\n+\n+\t// writeTestmain writes _testmain.go. This must happen after recompileForTest,\n+\t// because recompileForTest modifies XXX.\n+\tif err := writeTestmain(filepath.Join(testDir, "_testmain.go"), t); err != nil {\n+\t\treturn nil, nil, nil, err\n+\t}\n+\n \tcomputeStale(pmain)\n \n \tif ptest != p {\n@@ -1057,37 +1076,26 @@ type coverInfo struct {\n \tVars map[string]*CoverVar\n }\n \n-// writeTestmain writes the _testmain.go file for package p to\n-// the file named out. It also updates pmain.imports to include\n-// ptest and/or pxtest, depending on what it writes to _testmain.go.\n-func writeTestmain(out string, pmain, ptest, pxtest *Package) error {\n+// loadTestFuncs returns the testFuncs describing the tests that will be run.\n+func loadTestFuncs(ptest *Package) (*testFuncs, error) {\n \tt := &testFuncs{\n \t\tPackage: ptest,\n \t}\n \tfor _, file := range ptest.TestGoFiles {\n \t\tif err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.NeedTest); err != nil {\n-\t\t\treturn err\n+\t\t\treturn nil, err\n \t\t}\n \t}\n \tfor _, file := range ptest.XTestGoFiles {\n \t\tif err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.NeedXtest); err != nil {\n-\t\t\treturn err\n-\t\t}\n-\t}\n-\n-\tif t.NeedTest {\n-\t\tpmain.imports = append(pmain.imports, ptest)\n-\t}\n-\tif t.NeedXtest {\n-\t\tpmain.imports = append(pmain.imports, pxtest)\n-\t}\n-\n-\tfor _, cp := range pmain.imports {\n-\t\tif len(cp.coverVars) > 0 {\n-\t\t\tt.Cover = append(t.Cover, coverInfo{cp, cp.coverVars})\n+\t\t\treturn nil, err\n \t\t}\n \t}\n+\treturn t, nil\n+}\n \n+// writeTestmain writes the _testmain.go file for t to the file named out.\n+func writeTestmain(out string, t *testFuncs) error {\n \tf, err := os.Create(out)\n \tif err != nil {\n \t\treturn err\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/f9c6ad9b6bad25bff480b95d3ebf6a780e86964d](https://github.com/golang/go/commit/f9c6ad9b6bad25bff480b95d3ebf6a780e86964d)
## 元コミット内容
cmd/go: fix coverage for 'package foo_test' tests
Fixes #8062.
LGTM=r R=r CC=golang-codereviews https://golang.org/cl/91610046
## 変更の背景
このコミットは、GoのIssue #8062を修正するために行われました。この問題は、`package foo_test`という形式で定義される外部テストパッケージ(external test package)において、コードカバレッジが正しく計測されないというものでした。
Goのテストでは、テスト対象のパッケージと同じパッケージ名を持つ内部テスト(`package foo`)と、テスト対象のパッケージとは異なるパッケージ名(通常は`_test`サフィックスが付く、例: `package foo_test`)を持つ外部テストがあります。外部テストは、テスト対象のパッケージをインポートして利用するため、そのパッケージがエクスポートしているAPIのみをテストできます。
問題の根源は、`go test`コマンドがテスト実行時に自動生成する`_testmain.go`ファイルが、外部テストパッケージのカバレッジ情報を適切に収集するための設定を欠いていたことにあります。これにより、外部テストが実行されても、そのテストがカバーしたコードの割合が正しくレポートされない、あるいは全くレポートされないという不具合が発生していました。開発者は、外部テストの網羅性を確認するために正確なカバレッジ情報が必要であるため、この問題はGoのテストエコシステムにおける重要な欠陥でした。
## 前提知識の解説
### Goのテストとパッケージ
Go言語には、標準でテストフレームワークが組み込まれており、`go test`コマンドを使用してテストを実行します。Goのテストファイルは、通常、テスト対象のGoファイルと同じディレクトリに配置され、ファイル名の末尾に`_test.go`が付きます。
Goのテストには大きく分けて2種類あります。
1. **内部テスト (Internal Tests)**:
* テスト対象のパッケージと同じパッケージ名(例: `package foo`)を持つテストファイルです。
* テスト対象パッケージの内部(非エクスポート)の関数や変数にもアクセスできます。
* テスト対象パッケージのコンパイル済みバイナリに結合されて実行されます。
2. **外部テスト (External Tests)**:
* テスト対象のパッケージとは異なるパッケージ名(例: `package foo_test`)を持つテストファイルです。
* テスト対象パッケージをインポートして利用するため、エクスポートされたAPIのみをテストできます。
* テスト対象パッケージとは独立したバイナリとしてコンパイルされ、実行されます。これにより、ユーザーがパッケージをインポートして利用するのと同様の環境でテストを行うことができます。
### Goのコードカバレッジ
Goは、`go test -cover`コマンドを使用することで、テストによって実行されたコードの割合(コードカバレッジ)を計測する機能を提供します。この機能は、コンパイル時にソースコードに計測用のコード(インストゥルメンテーション)を挿入することで実現されます。
* **`_testmain.go`**: `go test`コマンドがカバレッジ計測を行う際、テスト対象のパッケージやテストファイル、そしてカバレッジ計測に必要な情報を集約した`_testmain.go`という特殊なGoファイルを自動生成します。このファイルは、テストの実行とカバレッジデータの収集をオーケストレーションする役割を担います。
* **`coverprofile`**: `-coverprofile`オプションを使用すると、カバレッジの結果をファイルに保存できます。このファイルは、`go tool cover`コマンドで解析し、HTMLレポートとして可視化したり、他のツールで利用したりできます。
* **`coverpkg`**: `-coverpkg`オプションを使用すると、カバレッジを計測する対象パッケージを明示的に指定できます。
### `_testmain.go`の役割
`_testmain.go`は、`go test`がテストバイナリをビルドする際に生成されるGoソースファイルです。このファイルは、以下の主要な役割を担います。
* **テスト関数の登録と実行**: テストファイル内で定義された`TestXxx`、`BenchmarkXxx`、`ExampleXxx`などのテスト関数を検出し、それらをテストランナーに登録します。
* **カバレッジ計測の統合**: カバレッジ計測が有効な場合、`_testmain.go`は、計測対象のパッケージから生成されたカバレッジ変数を参照し、テスト実行中にそれらの変数を更新することで、どのコードが実行されたかを記録します。テスト終了後、これらのカバレッジ変数の値に基づいてカバレッジプロファイルが生成されます。
* **パッケージのインポート**: テスト対象のパッケージ、テストパッケージ(内部・外部)、およびカバレッジ計測対象のパッケージをインポートします。
このコミットの文脈では、特に外部テストパッケージが`_testmain.go`に適切にインポートされ、そのカバレッジ変数が正しく扱われるかどうかが問題でした。
## 技術的詳細
このコミットの技術的な変更は、主に`src/cmd/go/test.go`における`go test`コマンドの内部ロジックの改善と、`src/cmd/go/test.bash`におけるカバレッジテストの強化に分けられます。
### `src/cmd/go/test.bash`の変更
`test.bash`は、`go`コマンドのテストスイートの一部であり、様々な`go`コマンドの機能が正しく動作するかを検証します。このコミットでは、カバレッジ計測に関するテストが強化されています。
* **`checkcoverage`関数の導入**: 新しいシェル関数`checkcoverage`が導入されました。この関数は、生成されたカバレッジレポート(`testdata/cover.txt`)をチェックし、カバレッジ率が`0.0%`でないことを確認します。これは、カバレッジ計測が全く機能していない場合にテストが失敗するようにするためのものです。以前は「正確な数値は気にしない」とされていましたが、この変更により「0.0%でないこと」が必須条件となりました。
* **出力のリダイレクト**: `testgo test`コマンドの出力が`testdata/cover.txt`にリダイレクトされるようになりました。これにより、`checkcoverage`関数がカバレッジレポートの内容を容易に検査できるようになります。
* **クリーンアップの強化**: `testdata/cover.out`だけでなく、`testdata/cover.txt`もテスト後に削除されるようになりました。
これらの変更は、`go test -cover`機能自体のテストの堅牢性を高めることを目的としています。
### `src/cmd/go/test.go`の変更
`test.go`は、`go test`コマンドの主要なロジックを実装しています。このコミットの核心的な変更は、`_testmain.go`の生成とカバレッジ情報の収集方法の改善にあります。
1. **`writeTestmain`関数のシグネチャ変更**:
* 変更前: `func writeTestmain(out string, pmain, ptest, pxtest *Package) error`
* 変更後: `func writeTestmain(out string, t *testFuncs) error`
この変更は、`_testmain.go`の生成に必要な情報が、`pmain`、`ptest`、`pxtest`といった個別の`*Package`ポインタとして渡されるのではなく、`testFuncs`という単一の構造体にカプセル化されて渡されるようにリファクタリングされたことを示しています。これにより、関数のインターフェースがよりクリーンになり、関連するデータがまとまって扱われるようになります。
2. **`loadTestFuncs`関数の導入**:
* 新しく`loadTestFuncs(ptest *Package) (*testFuncs, error)`関数が導入されました。
* この関数は、テストパッケージ(`ptest`)内のテストファイル(`TestGoFiles`、`XTestGoFiles`)をスキャンし、`testFuncs`構造体を生成して返します。
* `testFuncs`構造体には、テストが存在するかどうかを示す`NeedTest`、`NeedXtest`フラグや、カバレッジ情報(`Cover`)などが含まれます。
* この関数の導入により、`_testmain.go`の生成に必要なメタデータ(どのテスト関数が存在するか、外部テストが必要かなど)の収集が、`_testmain.go`の実際の書き込み処理から分離されました。
3. **`pmain.imports`の処理順序の変更とカバレッジ変数の収集**:
* 変更前は、`writeTestmain`が`pmain.imports`を更新する役割も持っていました。
* 変更後、`test`関数内で最初に`loadTestFuncs`が呼び出され、`testFuncs`構造体`t`が取得されます。
* 次に、`t.NeedTest`または`ptest.coverMode`が設定されている場合に`ptest`が`pmain.imports`に追加され、`t.NeedXtest`が設定されている場合に`pxtest`が`pmain.imports`に追加されます。これにより、`_testmain.go`にインポートされるパッケージが、実際にテストやカバレッジが必要なものに限定されるようになりました。
* さらに、`pmain.imports`に含まれる各パッケージから`coverVars`(カバレッジ変数)を収集し、`t.Cover`スライスに追加するループが明示的に追加されました。これは、`_testmain.go`が生成される前に、すべての必要なカバレッジ情報が`testFuncs`構造体に集約されることを保証します。
* この`coverVars`の収集は、`recompileForTest`の後に、かつ新しい`writeTestmain`の呼び出しの前に実行されるようになりました。この順序は、`recompileForTest`が`pmain`を変更する可能性があるため重要です。
これらの変更により、`go test`コマンドは、外部テストパッケージ(`package foo_test`)が持つカバレッジ情報を、`_testmain.go`を通じて正しく収集し、最終的なカバレッジレポートに反映できるようになりました。特に、`loadTestFuncs`の導入と`writeTestmain`への`testFuncs`構造体の引き渡しは、テスト実行ロジックのモジュール化と堅牢化に貢献しています。
## コアとなるコードの変更箇所
### `src/cmd/go/test.bash`
```diff
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -563,37 +563,50 @@ TEST source file name order preserved
./testgo test testdata/example[12]_test.go || ok=false
# Check that coverage analysis works at all.
-# Don't worry about the exact numbers
+# Don't worry about the exact numbers but require not 0.0%.
+checkcoverage() {
+\tif grep '[^0-9]0\.0%' testdata/cover.txt >/dev/null; then
+\t\techo 'some coverage results are 0.0%'
+\t\tok=false
+\tfi
+\tcat testdata/cover.txt
+\trm -f testdata/cover.txt
+}
+
TEST coverage runs
-./testgo test -short -coverpkg=strings strings regexp || ok=false
-./testgo test -short -cover strings math regexp || ok=false
+./testgo test -short -coverpkg=strings strings regexp >testdata/cover.txt 2>&1 || ok=false
+./testgo test -short -cover strings math regexp >>testdata/cover.txt 2>&1 || ok=false
+checkcoverage
checkcoverage
関数の追加と、カバレッジ結果が0.0%でないことを確認するロジックの導入。testgo
コマンドの出力をtestdata/cover.txt
にリダイレクトし、checkcoverage
で検証する形式に変更。
src/cmd/go/test.go
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -715,13 +715,20 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\n }\n }\n \n-\t// writeTestmain writes _testmain.go but also updates\n-\t// pmain.imports to reflect the import statements written\n-\t// to _testmain.go. This metadata is needed for recompileForTest\n-\t// and the builds below.\n-\tif err := writeTestmain(filepath.Join(testDir, "_testmain.go"), pmain, ptest, pxtest); err != nil {\n+\t// Do initial scan for metadata needed for writing _testmain.go\n+\t// Use that metadata to update the list of imports for package main.\n+\t// The list of imports is used by recompileForTest and by the loop\n+\t// afterward that gathers t.Cover information.\n+\tt, err := loadTestFuncs(ptest)\n+\tif err != nil {\n \t\treturn nil, nil, nil, err\n \t}\n+\tif t.NeedTest || ptest.coverMode != "" {\n+\t\tpmain.imports = append(pmain.imports, ptest)\n+\t}\n+\tif t.NeedXtest {\n+\t\tpmain.imports = append(pmain.imports, pxtest)\n+\t}\n \n \tif ptest != p && localCover {\n \t\t// We have made modifications to the package p being tested\n@@ -739,6 +746,18 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\n \t\trecompileForTest(pmain, p, ptest, testDir)\n \t}\n \n+\tfor _, cp := range pmain.imports {\n+\t\tif len(cp.coverVars) > 0 {\n+\t\t\tt.Cover = append(t.Cover, coverInfo{cp, cp.coverVars})\n+\t\t}\n+\t}\n+\n+\t// writeTestmain writes _testmain.go. This must happen after recompileForTest,\n+\t// because recompileForTest modifies XXX.\n+\tif err := writeTestmain(filepath.Join(testDir, "_testmain.go"), t); err != nil {\n+\t\treturn nil, nil, nil, err\n+\t}\n+\n \tcomputeStale(pmain)\n \n \tif ptest != p {\n@@ -1057,37 +1076,26 @@ type coverInfo struct {\n \tVars map[string]*CoverVar\n }\n \n-// writeTestmain writes the _testmain.go file for package p to\n-// the file named out. It also updates pmain.imports to include\n-// ptest and/or pxtest, depending on what it writes to _testmain.go.\n-func writeTestmain(out string, pmain, ptest, pxtest *Package) error {\n+// loadTestFuncs returns the testFuncs describing the tests that will be run.\n+func loadTestFuncs(ptest *Package) (*testFuncs, error) {\n \tt := &testFuncs{\n \t\tPackage: ptest,\n \t}\n \tfor _, file := range ptest.TestGoFiles {\n \t\tif err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.NeedTest); err != nil {\n-\t\t\treturn err\n+\t\t\treturn nil, err\n \t\t}\n \t}\n \tfor _, file := range ptest.XTestGoFiles {\n \t\tif err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.NeedXtest); err != nil {\n-\t\t\treturn err\n-\t\t}\n-\t}\n-\n-\tif t.NeedTest {\n-\t\tpmain.imports = append(pmain.imports, ptest)\n-\t}\n-\tif t.NeedXtest {\n-\t\tpmain.imports = append(pmain.imports, pxtest)\n-\t}\n-\n-\tfor _, cp := range pmain.imports {\n-\t\tif len(cp.coverVars) > 0 {\n-\t\t\tt.Cover = append(t.Cover, coverInfo{cp, cp.coverVars})\n+\t\t\treturn nil, err\n \t\t}\n \t}\n+\treturn t, nil\n+}\n \n+// writeTestmain writes the _testmain.go file for t to the file named out.\n+func writeTestmain(out string, t *testFuncs) error {\n \tf, err := os.Create(out)\n \tif err != nil {\n \t\treturn err\n```
- `test`関数内で`loadTestFuncs`を呼び出し、`testFuncs`構造体を取得するロジックの追加。
- `pmain.imports`への`ptest`および`pxtest`の追加ロジックが、`loadTestFuncs`の結果に基づいて行われるように変更。
- `pmain.imports`内のパッケージから`coverVars`を収集し、`t.Cover`に追加するループの追加。
- `writeTestmain`関数のシグネチャが変更され、`testFuncs`構造体を受け取るように修正。
- `loadTestFuncs`関数の新規追加。この関数はテストファイルからテスト関数や外部テストの必要性などのメタデータを読み込む。
## コアとなるコードの解説
このコミットの核心は、`go test`コマンドが`_testmain.go`ファイルを生成する際の、テストパッケージとカバレッジ情報の取り扱いを改善した点にあります。
以前の実装では、`writeTestmain`関数が`_testmain.go`を書き込むだけでなく、`pmain.imports`(`_testmain.go`がインポートするパッケージのリスト)を更新する役割も担っていました。このアプローチでは、特に`package foo_test`のような外部テストパッケージの場合に、カバレッジ計測に必要な情報が`_testmain.go`に適切に反映されない問題がありました。外部テストは独立したパッケージとしてコンパイルされるため、そのカバレッジ変数を`_testmain.go`が正しく参照できるようにするための連携が不十分だったと考えられます。
新しい実装では、この処理がより明確に分離され、順序付けられています。
1. **`loadTestFuncs`によるメタデータ収集**:
* まず、`loadTestFuncs`関数が導入されました。この関数は、テストパッケージ(`ptest`)内のすべてのテストファイル(`TestGoFiles`、`XTestGoFiles`)を解析し、`testFuncs`という構造体にテストに関するメタデータ(例: 内部テストが必要か`NeedTest`、外部テストが必要か`NeedXtest`)を収集します。
* このステップにより、`_testmain.go`を生成する前に、どの種類のテストが存在し、どのパッケージがカバレッジ計測の対象となるべきかという情報が事前に確定されます。
2. **`pmain.imports`の更新**:
* `loadTestFuncs`が返した`testFuncs`構造体の情報に基づいて、`pmain.imports`(`_testmain.go`がインポートするパッケージのリスト)が更新されます。具体的には、`NeedTest`または`NeedXtest`が真の場合に、それぞれ`ptest`や`pxtest`が`pmain.imports`に追加されます。これにより、外部テストパッケージが`_testmain.go`のインポートリストに確実に含まれるようになります。
3. **カバレッジ変数の集約**:
* `pmain.imports`が更新された後、そのリストに含まれるすべてのパッケージからカバレッジ変数(`coverVars`)が収集され、`testFuncs`構造体`t`の`Cover`フィールドに集約されます。これは、`_testmain.go`がカバレッジ計測を行うために必要なすべての変数を一元的に管理するための重要なステップです。
4. **`writeTestmain`による`_testmain.go`の生成**:
* 最後に、`writeTestmain`関数が呼び出されますが、そのシグネチャは`testFuncs`構造体`t`のみを受け取るように変更されています。これにより、`writeTestmain`は、すでに必要なすべての情報が`t`に集約されている状態で、`_testmain.go`を生成することに専念できます。
この一連の変更により、外部テストパッケージのカバレッジ情報が`_testmain.go`に正しく組み込まれるようになり、`go test -cover`コマンドが`package foo_test`形式のテストに対しても正確なカバレッジレポートを生成できるようになりました。これは、Goのテストツールチェーンの信頼性と機能性を向上させる重要な修正です。
## 関連リンク
* GitHubコミットページ: [https://github.com/golang/go/commit/f9c6ad9b6bad25bff480b95d3ebf6a780e86964d](https://github.com/golang/go/commit/f9c6ad9b6bad25bff480b95d3ebf6a780e86964d)
* Gerrit Change-ID: [https://golang.org/cl/91610046](https://golang.org/cl/91610046)
## 参考にした情報源リンク
* [https://golang.org/cl/91610046](https://golang.org/cl/91610046) (Gerrit Code Review)
* Go言語の公式ドキュメント(`go test`コマンド、パッケージ、カバレッジに関する一般的な情報)
* Go言語のテストに関する一般的な知識