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

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

コミット

このコミットは、Go言語のテストインフラストラクチャにおける変更であり、特にtest/dwarfディレクトリ下のテストがrun.goによって適切にサポートされるようにするためのものです。具体的には、テストの実行ディレクトリ(rundir)がコンパイルディレクトリ(compiledir)およびエラーチェックディレクトリ(errorcheckdir)と一致するように調整されています。これにより、Goのデバッグ情報フォーマットであるDwarfに関連するテストが、Goの標準テストランナーであるrun.goで実行可能になります。

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

https://github.com/golang/go/commit/c3836ed06ea30f5c9a5aacd90e3c48164065971c

元コミット内容

test: make rundir match compiledir/errorcheckdir.

This allows test/dwarf to be supported by run.go.

Update #4139.

R=golang-dev, bradfitz, iant
CC=golang-dev
https://golang.org/cl/7064067

変更の背景

Go言語のテストスイートは、様々な種類のテストを効率的に実行するためにrun.goという汎用的なテストランナーを使用しています。しかし、特定のテスト、特にDwarfデバッグ情報に関連するテスト(test/dwarfディレクトリ下)は、そのディレクトリ構造や実行時の要件が他のテストと異なっていたため、run.goによる自動実行が困難でした。

このコミットの背景には、run.goがテストを実行する際に、テストファイルのコンパイルが行われるディレクトリ(compiledir)と、テスト実行時に期待される出力やエラーがチェックされるディレクトリ(errorcheckdir)の概念が存在します。test/dwarfのテストは、これらのディレクトリの概念と実行時のカレントディレクトリ(rundir)との整合性が取れていなかったため、run.goが正しくテストを認識・実行できない問題がありました。

この変更は、test/dwarfテストをrun.goのフレームワークに統合し、テストの自動化とメンテナンス性を向上させることを目的としています。また、コミットメッセージにある「Update #4139」は、この問題に関連する内部トラッキングまたは以前の議論を示唆しています。

前提知識の解説

  • Go言語のテストフレームワーク: Go言語には、go testコマンドを通じて実行される組み込みのテストフレームワークがあります。大規模なプロジェクトでは、go testのラッパーとして、より複雑なテストシナリオや環境設定を扱うためのカスタムテストランナーが使用されることがあります。Goの標準ライブラリのテストでは、test/run.goがその役割を担っています。
  • run.go: Goプロジェクトのテストディレクトリ(test/)にあるカスタムテストランナーです。これは、Goのテストスイート全体を管理し、様々な種類のテスト(コンパイルテスト、実行テスト、エラーチェックテストなど)を実行するためのロジックを含んでいます。run.goは、テスト対象のソースコードを一時ディレクトリにコピーし、そこでコンパイル・実行を行うことで、テスト環境の分離とクリーンアップを容易にします。
  • Dwarf (Debugging With Arbitrary Record Formats): Unix系システムで広く使われているデバッグ情報フォーマットです。コンパイラやリンカが生成する実行ファイルに埋め込まれ、デバッガがソースコードレベルでのデバッグ(変数名の解決、ブレークポイントの設定、スタックトレースの表示など)を行うために必要な情報を提供します。Go言語のコンパイラもDwarf形式のデバッグ情報を生成します。
  • rundir, compiledir, errorcheckdir: run.goのようなテストランナーが内部的に使用する概念です。
    • compiledir: テスト対象のソースコードがコンパイルされる一時ディレクトリ。
    • errorcheckdir: テストの出力やエラーメッセージが期待されるパターンと照合されるディレクトリ。
    • rundir: テストが実際に実行されるカレントディレクトリ。 これらのディレクトリが一致しない場合、特にファイルパスの解決や相対パスの参照において問題が発生する可能性があります。
  • skipOkayマップ: test/run.go内で使用されるマップで、特定のテストファイルがスキップされても問題ない(つまり、テストが失敗しても全体の結果に影響しない)ことを示すために使われます。これは、特定の環境でのみ失敗するテストや、まだ完全に統合されていないテストに対して一時的に使用されることがあります。

技術的詳細

このコミットの主要な技術的変更点は、test/dwarfディレクトリの構造変更と、test/run.goにおけるテスト実行ロジックの修正です。

  1. test/dwarfディレクトリの再編成:

    • test/dwarf/main.gotest/dwarf/z1.goといった既存のテストファイルが、test/dwarf/dwarf.dir/main.gotest/dwarf/dwarf.dir/z1.goのように、dwarf.dirという新しいサブディレクトリに移動(リネーム)されています。
    • 同時に、test/dwarf/dwarf.goという新しいファイルが作成されています。このファイルは、// rundirというコメントを含んでおり、このディレクトリがテストの実行ディレクトリとして機能することを示唆しています。このファイル自体はpackage ignoredとなっており、直接実行されるコードではなく、ディレクトリ構造のマーカーやメタデータとして機能していると考えられます。 この再編成により、test/dwarfのテストは、run.goが期待するcompiledir/errorcheckdir/rundirの統一された構造に適合するようになります。
  2. test/run.goの変更:

    • goDirPackagesの利用: 以前はgoDirFilesを使ってディレクトリ内の個々のGoファイルを処理していましたが、変更後はgoDirPackagesを使ってパッケージ単位で処理するようになっています。これにより、Goのパッケージ構造をより適切に扱えるようになり、複数のファイルで構成されるパッケージのコンパイルが容易になります。
    • コンパイルとリンクのロジックの変更: run.gotest.run()メソッド内のcase "run"セクションで、テストのコンパイルと実行ロジックが修正されています。
      • 以前は、ディレクトリ内のすべてのGoファイルを個別にコンパイルし、最後にディレクトリ内の最後のファイルをメインパッケージとしてリンクしていました。
      • 変更後は、goDirPackagesが返すパッケージのリストを反復処理し、各パッケージをcompileInDirでコンパイルします。そして、pkgsの最後の要素(つまり最後のパッケージ)に対してのみlinkFileを実行し、その結果生成された実行ファイル(a.exe)を実行するように変更されています。これにより、test/dwarfのような複数のGoファイルで構成されるテストが、単一の実行可能ファイルとして正しくビルド・実行されるようになります。
    • skipOkayマップからのdwarf関連エントリの削除: test/run.goskipOkayマップから、dwarf/main.godwarf/z*.goといったdwarf関連のファイルパスが大量に削除されています。これは、上記の変更によってtest/dwarfのテストがrun.goで正常に実行可能になったため、もはやスキップする必要がなくなったことを意味します。

これらの変更により、test/dwarfのテストはrun.goのテスト実行フローに完全に統合され、GoのCI/CDパイプラインで自動的に実行・検証されるようになります。

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

test/dwarf/dwarf.go (新規追加)

// rundir

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// See issue 2241 and issue 1878: dwarf include stack size
// issues in linker.

package ignored

test/run.go

--- a/test/run.go
+++ b/test/run.go
@@ -292,7 +292,7 @@ func goDirPackages(longdir string) ([][]string, error) {
 	}
 	return pkgs, nil
 }
-		
+
 // run runs a test.
 func (t *test) run() {
 	defer close(t.donec)
@@ -459,31 +459,32 @@ func (t *test) run() {
 		// Compile all files in the directory in lexicographic order.
 		// then link as if the last file is the main package and run it
 		longdir := filepath.Join(cwd, t.goDirName())
-		files, err := goDirFiles(longdir)
+		pkgs, err := goDirPackages(longdir)
 		if err != nil {
 			t.err = err
 			return
 		}
-		var gofile os.FileInfo
-		for _, gofile = range files {
-			_, err := compileInDir(runcmd, longdir, gofile.Name())
+		for i, gofiles := range pkgs {
+			_, err := compileInDir(runcmd, longdir, gofiles...)
 			if err != nil {
 				t.err = err
 				return
 			}
-		}
-		err = linkFile(runcmd, gofile.Name())
-		if err != nil {
-			t.err = err
-			return
-		}
-		out, err := runcmd(append([]string{filepath.Join(t.tempDir, "a.exe")}, args...)...)
-		if err != nil {
-			t.err = err
-			return
-		}
-		if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
-			t.err = fmt.Errorf("incorrect output\n%s", out)
+			if i == len(pkgs)-1 {
+				err = linkFile(runcmd, gofiles[0])
+				if err != nil {
+					t.err = err
+					return
+				}
+				out, err := runcmd(append([]string{filepath.Join(t.tempDir, "a.exe")}, args...)...)
+				if err != nil {
+					t.err = err
+					return
+				}
+				if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
+					t.err = fmt.Errorf("incorrect output\n%s", out)
+				}
+			}
 		}
 
 	case "build":
@@ -603,7 +604,7 @@ func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
 		out[i] = strings.Replace(out[i], full, short, -1)
 		}
 	}
-	
+
 	var want []wantedError
 	for j := 0; j < len(fullshort); j += 2 {
 		full, short := fullshort[j], fullshort[j+1]
@@ -726,27 +727,6 @@ var skipOkay = map[string]bool{
 	"rotate.go":              true,
 	"sigchld.go":             true,
 	"sinit.go":               true,
-	"dwarf/main.go":          true,
-	"dwarf/z1.go":            true,
-	"dwarf/z10.go":           true,
-	"dwarf/z11.go":           true,
-	"dwarf/z12.go":           true,
-	"dwarf/z13.go":           true,
-	"dwarf/z14.go":           true,
-	"dwarf/z15.go":           true,
-	"dwarf/z16.go":           true,
-	"dwarf/z17.go":           true,
-	"dwarf/z18.go":           true,
-	"dwarf/z19.go":           true,
-	"dwarf/z2.go":            true,
-	"dwarf/z20.go":           true,
-	"dwarf/z3.go":            true,
-	"dwarf/z4.go":            true,
-	"dwarf/z5.go":            true,
-	"dwarf/z6.go":            true,
-	"dwarf/z7.go":            true,
-	"dwarf/z8.go":            true,
-	"dwarf/z9.go":            true,
 	"fixedbugs/bug248.go":    true, // combines errorcheckdir and rundir in the same dir.
 	"fixedbugs/bug302.go":    true, // tests both .$O and .a imports.
 	"fixedbugs/bug313.go":    true, // errorcheckdir with failures in the middle.

コアとなるコードの解説

test/dwarf/dwarf.go

このファイルは、test/dwarfディレクトリがrun.goによってテスト実行ディレクトリ(rundir)として認識されるためのマーカーとして機能します。// rundirというコメントは、このディレクトリの役割を明確に示しています。package ignoredであることから、このファイル自体がコンパイルされて実行されることはなく、主にディレクトリ構造のセマンティクスを定義するために存在します。また、Dwarfのリンカに関する既存の課題(issue 2241とissue 1878)への言及は、このテストスイートが特定のデバッグ情報関連の問題を検証するために設計されていることを示唆しています。

test/run.goの変更点

  1. goDirFilesからgoDirPackagesへの変更:

    • 変更前はfiles, err := goDirFiles(longdir)を使用してディレクトリ内の個々のGoファイルを取得していました。
    • 変更後はpkgs, err := goDirPackages(longdir)を使用しています。goDirPackagesは、ディレクトリ内のGoファイルをパッケージ単位でグループ化して返します。これにより、Goのモジュールシステムやパッケージの概念に沿ったテストのコンパイル・実行が可能になります。test/dwarfのテストが複数のGoファイルで構成される場合、これらを単一のパッケージとして扱うことで、コンパイルとリンクが正しく行われるようになります。
  2. コンパイルと実行ロジックの改善:

    • 以前のループでは、ディレクトリ内のすべてのGoファイルを個別にcompileInDirでコンパイルし、ループの最後にgofile変数に格納された最後のファイル名を使ってlinkFileを実行していました。これは、単一のGoファイルで構成されるテストには適していましたが、複数のファイルで構成されるパッケージには不十分でした。
    • 新しいロジックでは、for i, gofiles := range pkgsというループが導入されています。gofilesは、特定のパッケージに属するGoファイルのリストです。
      • 各パッケージ内のファイルはcompileInDir(runcmd, longdir, gofiles...)によってコンパイルされます。gofiles...は、スライスを展開して可変長引数として渡すGoの機能です。
      • 最も重要な変更は、if i == len(pkgs)-1という条件です。これは、pkgsリストの最後のパッケージに到達した場合にのみ、linkFileとテストの実行を行うことを意味します。これにより、複数のパッケージが存在する場合でも、テストスイート全体として単一の実行可能ファイルが生成され、それが実行されるという意図が明確になります。
      • 実行結果の検証(strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput())は、以前と同様に行われます。
  3. skipOkayマップからのエントリ削除:

    • skipOkayマップからdwarf/以下の多数のファイルパスが削除されたことは、これらのテストがもはやスキップされることなく、run.goによって正常に実行されるようになったことを明確に示しています。これは、test/dwarfテストの統合が成功したことの証です。

これらの変更は、Goのテストインフラストラクチャの堅牢性を高め、Dwarfデバッグ情報に関連する重要なテストが自動化されたテストスイートの一部として確実に実行されるようにするために不可欠です。

関連リンク

参考にした情報源リンク

  • Go言語のGitHubリポジトリ: https://github.com/golang/go
  • Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/ (コミットメッセージに記載されているhttps://golang.org/cl/7064067はこのシステムへのリンクです)
  • Go言語のIssueトラッカー (以前のIssue 4139に関する直接的な公開情報は見つかりませんでしたが、GoのIssueは通常GitHubまたはGoのGerritシステムで管理されます。)
  • Dwarfデバッグ情報に関する一般的な技術記事や仕様書。
  • Go言語のテストフレームワークの内部実装に関する分析記事やドキュメント。