[インデックス 14788] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部である go/types
パッケージ内の gcimporter_test.go
ファイルに対する変更です。具体的には、gcimporter
のインポートテストのロギングと堅牢性を向上させることを目的としています。
コミット
go/types: gcインポートテストにロギングを追加
* デッドラインを30秒に延長
* 各パッケージインポートの所要時間のロギングを追加
* ディレクトリが読み取れない場合はテストを即座に失敗させる
R=gri, minux.ma
CC=golang-dev
https://golang.org/cl/7030055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/14b654369dcf62505ad9bf8650cbd19d95e1f026
元コミット内容
commit 14b654369dcf62505ad9bf8650cbd19d95e1f026
Author: Dave Cheney <dave@cheney.net>
Date: Thu Jan 3 16:30:25 2013 +1100
go/types: add more logging to gc import test
* Extended deadline to 30 seconds
* Added logging of the duration of each package import
* Fail the test immediately if directories cannot be read
R=gri, minux.ma
CC=golang-dev
https://golang.org/cl/7030055
---\n src/pkg/go/types/gcimporter_test.go | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/pkg/go/types/gcimporter_test.go b/src/pkg/go/types/gcimporter_test.go
index d1cf605fe9..5f3236e0f3 100644
--- a/src/pkg/go/types/gcimporter_test.go
+++ b/src/pkg/go/types/gcimporter_test.go
@@ -54,21 +54,23 @@ func compile(t *testing.T, dirname, filename string) string {
var imports = make(map[string]*ast.Object)
func testPath(t *testing.T, path string) bool {
+\tt0 := time.Now()\n \t_, err := GcImport(imports, path)\n \tif err != nil {\n \t\tt.Errorf(\"testPath(%s): %s\", path, err)\n \t\treturn false\n \t}\n+\tt.Logf(\"testPath(%s): %v\", path, time.Since(t0))\n \treturn true
}\n
-const maxTime = 3 * time.Second\n+const maxTime = 30 * time.Second\n \n func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {\n \tdirname := filepath.Join(runtime.GOROOT(), \"pkg\", runtime.GOOS+\"_\"+runtime.GOARCH, dir)\n \tlist, err := ioutil.ReadDir(dirname)\n \tif err != nil {\n-\t\tt.Errorf(\"testDir(%s): %s\", dirname, err)\n+\t\tt.Fatalf(\"testDir(%s): %s\", dirname, err)\n \t}\n \tfor _, f := range list {\n \t\tif time.Now().After(endTime) {\
変更の背景
このコミットは、Go言語の go/types
パッケージにおける gcimporter
のテストの信頼性とデバッグ可能性を向上させるために行われました。
gcimporter
は、Goコンパイラ(gc
)によって生成されたバイナリ形式のパッケージ情報をインポートする役割を担っています。このインポート処理は、コンパイラやその他のGoツールがGoのソースコードを解析し、型情報を利用するために不可欠です。
元のテストでは、以下の問題があった可能性があります。
- タイムアウトの発生:
maxTime
が3秒と短すぎたため、特に大規模なパッケージのインポートや、I/Oが遅い環境下でテストがタイムアウトし、誤って失敗する可能性がありました。 - デバッグ情報の不足: 各パッケージのインポートにかかる時間がログに出力されていなかったため、パフォーマンスの問題やボトルネックを特定することが困難でした。
- ディレクトリ読み取りエラーの不適切な処理:
ioutil.ReadDir
が失敗した場合にt.Errorf
を使用していたため、テストはエラーを報告しつつも続行されていました。これにより、後続のテストが不正な状態で行われ、さらなる誤った失敗を引き起こす可能性がありました。ディレクトリの読み取りはテストの前提条件であり、これが失敗した場合はテスト全体を即座に中止すべきです。
これらの問題を解決し、テストの安定性と診断能力を高めるために、今回の変更が導入されました。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と標準ライブラリの知識が必要です。
-
go/types
パッケージ: Go言語の型システムをプログラム的に扱うためのパッケージです。Goのコンパイラやリンター、IDEなどのツールが、Goのソースコードを解析し、変数、関数、型などの型情報を取得・操作するために利用します。このパッケージは、Goプログラムのセマンティックな解析(意味解析)の中核を担います。 -
gcimporter
:go/types
パッケージの一部であり、Goコンパイラ(gc
)が生成するバイナリ形式のパッケージファイル(通常は.a
または.o
拡張子を持つアーカイブファイル内に埋め込まれている)を読み込み、その中に含まれる型情報をインポートする役割を担います。これにより、Goのツールはコンパイル済みのパッケージの型情報を利用して、別のGoソースファイルを型チェックしたり、補完機能を提供したりすることができます。 -
testing
パッケージ: Go言語に組み込まれているテストフレームワークです。テスト関数はfunc TestXxx(*testing.T)
の形式で定義され、*testing.T
型の引数を通じてテストの実行を制御し、結果を報告します。t.Errorf(format string, args ...interface{})
: テスト中にエラーが発生したことを報告しますが、テストの実行は続行されます。t.Fatalf(format string, args ...interface{})
: テスト中に致命的なエラーが発生したことを報告し、現在のテスト関数を即座に終了させます。t.Logf(format string, args ...interface{})
: テストの実行中に情報をログに出力します。デフォルトでは、テストが失敗した場合にのみ出力されますが、go test -v
オプションを使用すると常に表示されます。
-
time
パッケージ: Go言語で時間と期間を扱うためのパッケージです。time.Second
: 1秒を表すtime.Duration
型の定数です。time.Now()
: 現在のローカル時刻を返します。time.Since(t time.Time)
:t
から現在までの経過時間をtime.Duration
として返します。
-
io/ioutil
パッケージ: I/O操作に関するユーティリティ関数を提供するパッケージです。ioutil.ReadDir(dirname string)
: 指定されたディレクトリ内のファイルとサブディレクトリの情報を[]os.FileInfo
のスライスとして返します。
-
filepath
パッケージ: ファイルパスを操作するためのユーティリティ関数を提供するパッケージです。filepath.Join(elem ...string)
: 複数のパス要素を結合して、プラットフォーム固有のパス区切り文字で区切られた単一のパスを生成します。
-
runtime
パッケージ: Goランタイムとの相互作用を提供するパッケージです。runtime.GOROOT()
: Goのインストールルートディレクトリのパスを返します。runtime.GOOS
: 実行中のオペレーティングシステム(例: "linux", "darwin", "windows")を表す文字列定数です。runtime.GOARCH
: 実行中のアーキテクチャ(例: "amd64", "arm")を表す文字列定数です。
技術的詳細
このコミットは、src/pkg/go/types/gcimporter_test.go
ファイル内のテストロジックを改善しています。
-
maxTime
の延長:const maxTime = 3 * time.Second
がconst maxTime = 30 * time.Second
に変更されました。これは、テストがタイムアウトするまでの許容時間を3秒から30秒に延長することを意味します。gcimporter
は、Goの標準ライブラリや大規模なプロジェクトのパッケージをインポートする際に、多くのファイルI/Oや解析処理を伴う可能性があります。特に、低速なディスクI/Oや高負荷なシステム環境では、3秒という短い時間では処理が完了しないことがあり、テストが不安定になる原因となっていました。この延長により、テストの安定性が向上し、より現実的な環境でのインポート処理をカバーできるようになります。 -
パッケージインポート時間のロギング:
testPath
関数内で、GcImport
の呼び出し前後にtime.Now()
とtime.Since()
を使用して、各パッケージのインポートにかかる時間を計測し、t.Logf
でログに出力するように変更されました。t0 := time.Now()
:GcImport
呼び出しの直前に現在の時刻を記録します。t.Logf("testPath(%s): %v", path, time.Since(t0))
:GcImport
が完了した後、t0
から現在までの経過時間をログに出力します。 この変更により、どのパッケージのインポートに時間がかかっているのかを具体的に把握できるようになり、パフォーマンスのボトルネックを特定したり、テストが遅い原因を診断したりする際に非常に役立ちます。go test -v
オプションを付けてテストを実行することで、これらのログを確認できます。
-
ディレクトリ読み取りエラー時の即時失敗:
testDir
関数内で、ioutil.ReadDir(dirname)
がエラーを返した場合の処理がt.Errorf
からt.Fatalf
に変更されました。- 変更前:
t.Errorf("testDir(%s): %s", dirname, err)
- 変更後:
t.Fatalf("testDir(%s): %s", dirname, err)
ioutil.ReadDir
は、指定されたディレクトリの内容を読み取る関数です。この操作が失敗するということは、テストが依存するファイルシステムの状態が期待通りではないことを意味します。例えば、テスト対象のディレクトリが存在しない、アクセス権がない、または破損しているなどの状況が考えられます。このような基本的な前提条件が満たされない場合、後続のテストを実行しても意味がなく、さらなるエラーを引き起こす可能性が高いため、t.Fatalf
を使用してテストを即座に終了させるのが適切です。これにより、テストの診断がより明確になり、根本的な問題の特定が容易になります。
- 変更前:
これらの変更は、Goのツールチェインの品質と信頼性を維持するために、テストの堅牢性とデバッグ能力を向上させるという点で重要です。
コアとなるコードの変更箇所
--- a/src/pkg/go/types/gcimporter_test.go
+++ b/src/pkg/go/types/gcimporter_test.go
@@ -54,21 +54,23 @@ func compile(t *testing.T, dirname, filename string) string {
var imports = make(map[string]*ast.Object)
func testPath(t *testing.T, path string) bool {
+\tt0 := time.Now()\n \t_, err := GcImport(imports, path)\n \tif err != nil {\n \t\tt.Errorf(\"testPath(%s): %s\", path, err)\n \t\treturn false\n \t}\n+\tt.Logf(\"testPath(%s): %v\", path, time.Since(t0))\n \treturn true
}\n
-const maxTime = 3 * time.Second\n+const maxTime = 30 * time.Second\n \n func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {\n \tdirname := filepath.Join(runtime.GOROOT(), \"pkg\", runtime.GOOS+\"_\"+runtime.GOARCH, dir)\n \tlist, err := ioutil.ReadDir(dirname)\n \tif err != nil {\n-\t\tt.Errorf(\"testDir(%s): %s\", dirname, err)\n+\t\tt.Fatalf(\"testDir(%s): %s\", dirname, err)\n \t}\n \tfor _, f := range list {\n \t\tif time.Now().After(endTime) {\
コアとなるコードの解説
-
maxTime
定数の変更:-const maxTime = 3 * time.Second +const maxTime = 30 * time.Second
maxTime
定数の値が3 * time.Second
(3秒) から30 * time.Second
(30秒) に変更されました。これは、テストが許容する最大実行時間を10倍に延長し、インポート処理が完了するまでの猶予を増やすことで、テストのタイムアウトによる誤った失敗を減らすことを目的としています。 -
testPath
関数におけるインポート時間のロギング追加:func testPath(t *testing.T, path string) bool { + t0 := time.Now() _, err := GcImport(imports, path) if err != nil { t.Errorf("testPath(%s): %s", path, err) return false } + t.Logf("testPath(%s): %v", path, time.Since(t0)) return true }
GcImport
関数が呼び出される直前にtime.Now()
を使って開始時刻t0
を記録し、GcImport
の実行後にtime.Since(t0)
を使って経過時間を計算しています。この経過時間はt.Logf
を使ってテストログに出力されます。これにより、個々のパッケージインポートにかかる時間を詳細に把握できるようになり、パフォーマンス分析やデバッグが容易になります。 -
testDir
関数におけるエラーハンドリングの変更:func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) { dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir) list, err := ioutil.ReadDir(dirname) if err != nil { - t.Errorf("testDir(%s): %s", dirname, err) + t.Fatalf("testDir(%s): %s", dirname, err) } for _, f := range list { if time.Now().After(endTime) {
ioutil.ReadDir
がエラーを返した場合の処理がt.Errorf
からt.Fatalf
に変更されました。t.Errorf
はエラーを報告しつつテストを続行しますが、t.Fatalf
はエラーを報告した上で現在のテスト関数を即座に終了させます。ディレクトリの読み取り失敗はテストの前提条件が満たされていないことを意味するため、テストを続行しても意味がないと判断され、即座に終了させることで、より明確なエラー報告と効率的なデバッグを可能にしています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/14b654369dcf62505ad9bf8650cbd19d95e1f026
- Gerrit Code Review (元の変更リスト): https://golang.org/cl/7030055
参考にした情報源リンク
- Go言語
testing
パッケージ公式ドキュメント: https://pkg.go.dev/testing - Go言語
time
パッケージ公式ドキュメント: https://pkg.go.dev/time - Go言語
io/ioutil
パッケージ公式ドキュメント (Go 1.16以降はio
およびos
パッケージに移行): https://pkg.go.dev/io/ioutil - Go言語
go/types
パッケージ公式ドキュメント: https://pkg.go.dev/go/types - Go言語
filepath
パッケージ公式ドキュメント: https://pkg.go.dev/path/filepath - Go言語
runtime
パッケージ公式ドキュメント: https://pkg.go.dev/runtime