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

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

このコミットは、Go言語のテストツールであるgotestが生成する_testmain.goファイルが、gofmtの整形ルールに準拠するように修正するものです。具体的には、_testmain.go内でテストやベンチマークの定義を開始する際に、gofmtが期待する改行を追加することで、gotest実行後にgofmt -d .を実行した際に不要な差分("spurious changes")が発生する問題を解消します。

コミット

commit 8e9e75f0082390a2dc2238adf12eca01ac68ec47
Author: Benny Siegert <bsiegert@gmail.com>
Date:   Tue Jan 3 12:00:39 2012 +1100

    gotest: make _testmain.go conform to gofmt rules
    
    Otherwise, running "gofmt -d ." after "gotest" gives some
    spurious changes.
    
    R=golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/5504101

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

https://github.com/golang/go/commit/8e9e75f0082390a2dc2238adf12eca01ac68ec47

元コミット内容

gotestが生成する_testmain.goファイルがgofmtのルールに準拠するように修正します。 この修正を行わないと、gotestの実行後にgofmt -d .を実行した際に、いくつかの不要な変更("spurious changes")が報告されてしまいます。

変更の背景

Go言語の開発において、コードのフォーマットはgofmtというツールによって厳密に管理されています。gofmtは、Goのソースコードを標準的なスタイルに自動的に整形するツールであり、Goコミュニティ全体で一貫したコードスタイルを維持するために不可欠です。

gotestコマンドは、Goのテストを実行する際に、内部的に_testmain.goという一時的なGoソースファイルを生成します。このファイルは、テストの実行に必要なすべてのテスト関数やベンチマーク関数をまとめ、testingパッケージの内部関数を呼び出すためのエントリポイントとなります。

このコミットが作成された時点では、gotestが生成する_testmain.goファイルの一部が、gofmtの期待するフォーマットと異なっていました。具体的には、var tests = []testing.InternalTest{var benchmarks = []testing.InternalBenchmark{ といった配列の初期化部分の直後に改行がないため、gofmtがこれを整形対象とみなし、不要な差分を報告していました。

開発者がgotestを実行した後、プロジェクト全体のgofmt -d .(差分表示モード)を実行すると、この_testmain.goファイルが常に変更されたと表示され、ノイズとなっていました。このコミットは、この「ノイズ」を排除し、開発ワークフローをスムーズにすることを目的としています。

前提知識の解説

  • gofmt: Go言語の公式なコードフォーマッタです。Goのソースコードを自動的に整形し、Goコミュニティ全体で一貫したコードスタイルを強制します。gofmt -d .は、現在のディレクトリ以下のGoファイルでgofmtによる整形が必要な箇所を差分として表示します。
  • gotest: Go言語のテストを実行するためのコマンドです。Goのテストは、ファイル名が_test.goで終わるファイルに記述されたTestXxx関数やBenchmarkXxx関数によって定義されます。gotestはこれらのテストを検出し、実行します。
  • _testmain.go: gotestがテスト実行時に一時的に生成するGoソースファイルです。このファイルは、テスト対象のパッケージとテストコードをリンクし、testingパッケージのMain関数を呼び出すことで、すべてのテストやベンチマークを実行するためのエントリポイントとなります。通常、このファイルはユーザーが直接編集するものではなく、テスト実行後に削除されるか、次のテスト実行時に上書きされます。
  • testing.InternalTest / testing.InternalBenchmark: Goのtestingパッケージ内部で使用される構造体です。これらは、テスト関数やベンチマーク関数のメタデータ(名前、関数へのポインタなど)を保持するために使用されます。_testmain.goファイル内で、実行されるべきテストやベンチマークのリストを定義するためにこれらの構造体の配列が利用されます。

技術的詳細

このコミットの技術的な核心は、gotest_testmain.goを生成する際に、特定の文字列の末尾に改行文字\nを追加することです。

gofmtは、コードブロックや配列の初期化リストなど、特定の構文要素の後に改行があることを期待する場合があります。このコミット以前のgotestは、_testmain.go内でvar tests = []testing.InternalTest{var benchmarks = []testing.InternalBenchmark{ といった行を出力した後、すぐに次の要素(テスト関数やベンチマーク関数の定義)を出力していました。これにより、gofmtはこれらの行を「整形されていない」と判断し、改行を追加しようとしていました。

このコミットでは、src/cmd/gotest/gotest.go内のwriteTestmainGo関数において、これらの配列初期化の行を出力する際に、文字列リテラル自体に改行文字\nを埋め込むように変更されました。

具体的には、以下の行が変更されました。

  • fmt.Fprintln(b, "var tests = []testing.InternalTest{")fmt.Fprintln(b, "var tests = []testing.InternalTest{\\n")
  • fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{")fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{\\n")

fmt.Fprintlnは、与えられた文字列の後に自動的に改行を追加しますが、この変更はFprintlnが追加する改行とは別に、文字列リテラル内部に改行文字\nを追加しています。これにより、_testmain.goの出力結果は以下のようになります(簡略化された例):

変更前:

var tests = []testing.InternalTest{
	{"pkg.TestFunc", pkg.TestFunc},
}

変更後:

var tests = []testing.InternalTest{
// ここに改行が挿入される
	{"pkg.TestFunc", pkg.TestFunc},
}

この変更により、gofmtが期待するフォーマットに合致するようになり、gotest実行後にgofmt -d .を実行しても、_testmain.goに関する不要な差分が報告されなくなりました。これは、Go開発におけるCI/CDパイプラインやローカルでの開発体験の向上に寄与します。

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

変更はsrc/cmd/gotest/gotest.goファイルに集中しています。

--- a/src/cmd/gotest/gotest.go
+++ b/src/cmd/gotest/gotest.go
@@ -401,7 +401,7 @@ func writeTestmainGo() {
 	fmt.Fprintln(b) // for gofmt
 
 	// Tests.
-	fmt.Fprintln(b, "var tests = []testing.InternalTest{\")
+	fmt.Fprintln(b, "var tests = []testing.InternalTest{\\n")
 	for _, f := range files {
 		for _, t := range f.tests {
 			fmt.Fprintf(b, "\\t{\\\"%s.%s\\\", %s.%s},\\n\", f.pkg, t, renamedPackage(f.pkg), t)
@@ -411,7 +411,7 @@ func writeTestmainGo() {
 
 	// Benchmarks.
-	fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{\")
+	fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{\\n")
 	for _, f := range files {
 		for _, bm := range f.benchmarks {
 			fmt.Fprintf(b, "\\t{\\\"%s.%s\\\", %s.%s},\\n\", f.pkg, bm, renamedPackage(f.pkg), bm)\

コアとなるコードの解説

src/cmd/gotest/gotest.go内のwriteTestmainGo関数は、_testmain.goファイルの内容を生成する役割を担っています。

変更された2つの行は、それぞれテストとベンチマークの配列を初期化する部分です。

  1. テスト配列の初期化: fmt.Fprintln(b, "var tests = []testing.InternalTest{\\n") この行は、_testmain.goファイルにvar tests = []testing.InternalTest{という文字列を出力します。重要なのは、文字列リテラルの最後に\nが追加されている点です。これにより、この行の出力後に、gofmtが期待する追加の改行が挿入され、整形上の問題が解消されます。

  2. ベンチマーク配列の初期化: fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{\\n") 同様に、この行はベンチマーク配列の初期化部分を出力します。ここでも、文字列リテラルの最後に\nが追加されており、gofmtの整形ルールに準拠するようになります。

これらの変更は、gotestが生成するコードの見た目には小さな変更ですが、gofmtとの連携において重要な意味を持ち、開発者の体験を向上させるための細やかな配慮がなされています。

関連リンク

参考にした情報源リンク

  • コミットデータ: /home/orange/Project/comemo/commit_data/11013.txt
  • GitHub上のコミットページ: https://github.com/golang/go/commit/8e9e75f0082390a2dc2238adf12eca01ac68ec47
  • Go言語の公式ドキュメントおよびパッケージドキュメント (一般的な情報源として)
  • (注: コミットメッセージに記載されているhttps://golang.org/cl/5504101は、Web検索の結果、このコミットの内容とは異なる情報を示しているようです。そのため、このリンクは直接的な参考情報源としては使用していません。)