[インデックス 1201] ファイルの概要
このコミットは、Go言語のテストファイル命名規則を、従来の test*.go
から *test.go
へと変更するものです。これはGo言語の初期段階における重要な変更であり、現在のGoの標準的なテストファイル命名規則の基礎を築きました。この変更は、テストファイルの識別方法を統一し、ビルドシステムやテストツールとの連携をより効率的かつ直感的にすることを目的としています。
コミット
commit 12254b6c0bc9d3f6689f12d64f7bd4cb4d20d53f
Author: Rob Pike <r@golang.org>
Date: Wed Nov 19 19:11:01 2008 -0800
change naming convention for tests from
test*.go
to
*test.go
R=rsc
DELTA=1747 (864 added, 855 deleted, 28 changed)
OCL=19666
CL=19666
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/12254b6c0bc9d3f6689f12d64f7bd4cb4d20d53f
元コミット内容
このコミットの目的は、Go言語のテストファイルの命名規則を test*.go
から *test.go
へと変更することです。これに伴い、テストファイルを検出するビルドツールやスクリプト、Makefile内のパターンも更新されています。また、src/lib/reflect/test.go
のような一部のテストファイルでは、パッケージ名の変更や testing
パッケージの導入といった、よりGoらしいテストフレームワークへの移行を示す変更も含まれています。
変更の背景
Go言語は、その設計思想の一つとして「シンプルさ」と「一貫性」を重視しています。初期のGo開発において、テストファイルの命名規則はまだ確立されていませんでした。test*.go
という命名は、他の言語やフレームワークでよく見られるパターンですが、Goの設計者たちは、よりGoらしい、あるいはより明確な命名規則を模索していました。
*test.go
という命名規則は、以下の点で優れています。
- 明確性: ファイル名が
_test.go
で終わることで、そのファイルがテストコードであることを一目で識別できます。これは、パッケージ内の通常のコードとテストコードを明確に区別するのに役立ちます。 - ツールとの連携:
go test
コマンドやその他のビルドツールが、この命名規則に基づいてテストファイルを自動的に発見し、実行することを容易にします。これにより、開発者はテストの実行方法について特別な設定をすることなく、標準的な方法でテストを管理できるようになります。 - 一貫性: Goのエコシステム全体でこの命名規則が採用されることで、異なるプロジェクト間でのコードの可読性と保守性が向上します。
このコミットは、Go言語がまだ初期段階にあった2008年に行われたものであり、Goの標準ライブラリやツールチェーンの基盤を形成する上で、このような基本的な規約の確立が非常に重要であったことを示しています。
前提知識の解説
このコミットを理解するためには、以下のGo言語および関連ツールの基本的な知識が必要です。
- Go言語のパッケージシステム: Goのコードはパッケージにまとめられ、ディレクトリ構造がパッケージパスに対応します。テストファイルも特定のパッケージに属します。
- Goのビルドシステム:
go build
やgo install
といったコマンドがどのようにソースファイルをコンパイルし、実行可能ファイルを生成するか。 - Goのテストフレームワーク (
testing
パッケージ): Goには標準でtesting
パッケージが提供されており、これを用いてテストを記述します。テスト関数はTestXxx
という形式で命名され、*testing.T
型の引数を取ります。 go test
コマンド: Goのテストを実行するための標準コマンドです。このコマンドは、デフォルトで*test.go
という命名規則に従うファイルを検索し、その中のテスト関数を実行します。- Makefile: 当時のGoプロジェクトでは、ビルドやテストの自動化にMakefileが広く使用されていました。Makefileは、コマンドの依存関係を定義し、特定のターゲット(例:
test
,coverage
)を実行するためのルールを提供します。 - 正規表現 (Regular Expressions): ファイル名のパターンマッチングや、ログ出力のフィルタリングに正規表現が使用されています。特に
grep
コマンドでの利用が顕著です。 - シェルスクリプト:
src/cmd/gotest/gotest
やsrc/run.bash
のようなファイルはシェルスクリプトであり、ファイル操作やコマンド実行のロジックを記述しています。 6cov
: 当時のGoのコードカバレッジツールの一つです。Goの初期には、6g
(Goコンパイラ),6l
(Goリンカ) といったツールが存在し、6cov
もその一部でした。
技術的詳細
このコミットの技術的な変更は、主に以下の3つの側面から構成されています。
-
テストファイル名の変更: 既存の
test*.go
形式のテストファイルが、*test.go
形式に一括でリネームされています。これはgit mv
コマンドに相当する操作で、ファイルの内容自体は変更されずに名前だけが変わっています。例えば、src/lib/container/array/testarray.go
はsrc/lib/container/array/array_test.go
に変更されています。 -
ビルドスクリプトとMakefileの更新:
src/cmd/gobuild/gobuild.c
: このC言語で書かれたビルドツールは、テストファイルを識別するためのロジックを変更しています。grep -v '^test.*\\.go:'
がgrep -v '^.*test\\.go:'
に変更されています。これは、コードカバレッジレポートからテストファイルを除外する際に、新しい命名規則に合致するファイルを無視するように正規表現を更新したものです。strncmp(argv[i], "test", 4)
がstrstr(argv[i], "test.go") != nil
に変更されています。これは、コマンドライン引数で渡されたファイルがテストファイルであるかどうかを判断するロジックを、より柔軟な文字列検索 (strstr
) に変更し、test.go
を含むファイルをテストファイルとして扱うようにしたものです。
src/cmd/gotest/gotest
: このシェルスクリプトは、テストファイルを検索する際に使用するパターンを変更しています。gofiles=$(echo test*.go)
がgofiles=$(echo *test.go)
に変更されています。これにより、gotest
コマンドが新しい命名規則に従ってテストファイルを正しく見つけられるようになります。
- 各ライブラリのMakefile (
src/lib/*/Makefile
):coverage
ターゲット内で使用されている6cov
コマンドのgrep
パターンが、^test.*\\.go:
から^.*test\\.go:
に変更されています。これにより、コードカバレッジツールが新しい命名規則のテストファイルを正しく除外できるようになります。
-
テストコード自体の変更と標準テストフレームワークへの移行:
src/lib/reflect/test.bash
の削除: このファイルは、reflect
パッケージのテストを実行するためのカスタムシェルスクリプトでした。この削除は、Goの標準テストフレームワーク (testing
パッケージとgo test
コマンド) への移行を示唆しています。src/lib/reflect/test.go
の変更:package main
からpackage reflect
への変更: テストファイルがテスト対象のパッケージと同じパッケージに属するというGoの慣習に従っています。import "testing"
の追加: Goの標準テストフレームワークであるtesting
パッケージを使用することを示しています。func main()
からexport func TestAll(tt *testing.T)
への変更:main
関数で直接テストを実行するのではなく、testing
パッケージの規約に従い、TestAll
という名前のテスト関数を定義しています。*testing.T
型の引数は、テストの実行状態やエラー報告に使用されます。- 文字列リテラル内のパッケージ名の変更:
main.T{...}
のような文字列がreflect.T{...}
に変更されており、これはパッケージ名の変更に伴うものです。
src/lib/testing.go
の変更:Main
関数に、テストが一つも存在しない場合に警告メッセージを表示するロジックが追加されています。これは、テスト実行時のユーザーエクスペリエンスを向上させるための小さな改善です。src/run.bash
の変更:lib/reflect
ディレクトリでのテスト実行方法がbash test.bash
からmake test
に変更されています。これは、カスタムスクリプトからMakefileベースの標準的なテスト実行フローへの移行を反映しています。
これらの変更は、Go言語のテストエコシステムが初期段階でどのように進化し、現在の洗練された形へと向かっていったかを示す貴重な証拠です。特に、カスタムのテスト実行スクリプトから testing
パッケージと go test
コマンドを中心とした標準的なアプローチへの移行は、Goのテスト文化を形成する上で極めて重要なステップでした。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に以下のファイルと行に集約されます。
-
src/cmd/gobuild/gobuild.c
:--- a/src/cmd/gobuild/gobuild.c +++ b/src/cmd/gobuild/gobuild.c @@ -290,7 +290,7 @@ char preamble[] = "coverage: packages\n" "\tgotest\n" -\t"\t6cov -g `pwd` | grep -v '^test.*\\\\.go:'\n" +\t"\t6cov -g `pwd` | grep -v '^.*test\\\\.go:'\n" "\n" "%%.$O: %%.go\n" "\t$(GC) $*.go\n" @@ -487,7 +487,7 @@ main(int argc, char **argv) njob = 0; job = emalloc(argc*sizeof job[0]); for(i=0; i<argc; i++) { -\t\tif(strncmp(argv[i], "test", 4) == 0) +\t\tif(strstr(argv[i], "test.go") != nil) continue; job[njob].name = argv[i]; job[njob].pass = -1;
-
src/cmd/gotest/gotest
:--- a/src/cmd/gotest/gotest +++ b/src/cmd/gotest/gotest @@ -27,7 +27,7 @@ done case "x$gofiles" in x) -\tgofiles=$(echo test*.go)\n" +\tgofiles=$(echo *test.go)\n" esac ofiles=$(echo $gofiles | sed 's/\.go/.6/g')
-
src/lib/reflect/test.go
:--- a/src/lib/reflect/test.go +++ b/src/lib/reflect/test.go @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package reflect import ( -\t"reflect"\n" +\t"reflect"; +\t"testing"\n" ) var doprint bool = false @@ -87,7 +88,7 @@ export type empty interface {} export type T struct { a int; b float64; c string; d *int } -func main() { +export func TestAll(tt *testing.T) { // TODO(r): wrap up better var s string; var t reflect.Type; @@ -168,30 +169,30 @@ func main() { var i int = 7; var tmp = &T{123, 456.75, "hello", &i}; value := reflect.NewValue(tmp); -\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.T{123, 456.75, hello, *int(@)}"); +\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "reflect.T{123, 456.75, hello, *int(@)}"); } { type C chan *T; // TODO: should not be necessary var tmp = new(C); value := reflect.NewValue(tmp); -\t\tassert(reflect.ValueToString(value), "*main.C·test(@)"); +\t\tassert(reflect.ValueToString(value), "*reflect.C·test(@)"); } { type A [10]int; var tmp A = A{1,2,3,4,5,6,7,8,9,10}; value := reflect.NewValue(&tmp); -\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.A·test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"); +\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "reflect.A·test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"); value.(reflect.PtrValue).Sub().(reflect.ArrayValue).Elem(4).(reflect.IntValue).Set(123); -\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.A·test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"); +\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "reflect.A·test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"); } { type AA []int; tmp1 := [10]int{1,2,3,4,5,6,7,8,9,10}; // TODO: should not be necessary to use tmp1 var tmp *AA = &tmp1; value := reflect.NewValue(tmp); -\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.AA·test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"); +\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "reflect.AA·test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"); value.(reflect.PtrValue).Sub().(reflect.ArrayValue).Elem(4).(reflect.IntValue).Set(123); -\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "main.AA·test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"); +\t\tassert(reflect.ValueToString(value.(reflect.PtrValue).Sub()), "reflect.AA·test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"); } {
-
src/lib/reflect/test.bash
の削除 -
src/run.bash
:--- a/src/run.bash +++ b/src/run.bash @@ -20,7 +20,7 @@ make test (xcd lib/reflect make clean time make -bash test.bash +make test ) || exit $? (xcd lib/regexp
コアとなるコードの解説
上記の変更箇所は、Go言語のテストシステムにおける根本的なパラダイムシフトを示しています。
-
gobuild.c
とgotest
の変更: これらの変更は、Goのビルドツールとテスト実行スクリプトが、新しい命名規則 (*test.go
) に従ってテストファイルを正確に識別し、処理できるようにするためのものです。正規表現の変更は、ファイルパスのどこにtest
が出現してもマッチするように柔軟性を高めています。strncmp
からstrstr
への変更も同様に、より広範なマッチングを可能にしています。これにより、システム全体でテストファイルの検出が一貫して行われるようになります。 -
src/lib/reflect/test.go
の変更: これは、Goのテストがカスタムスクリプトやmain
関数ベースのアプローチから、標準のtesting
パッケージとgo test
コマンドを利用する現代的なGoのテストスタイルへと移行する重要なステップです。package main
からpackage reflect
への変更は、テストコードがテスト対象のパッケージと同じパッケージに属するというGoの慣習を確立します。これにより、テストコードはテスト対象のパッケージ内の非エクスポートされた識別子にもアクセスできるようになります。import "testing"
とfunc TestAll(tt *testing.T)
の導入は、Goの標準テストフレームワークの採用を意味します。*testing.T
はテストの状態を管理し、エラー報告やヘルパー関数を提供します。- 文字列リテラル内のパッケージ名の変更は、
reflect
パッケージの内部構造を反映したものであり、テストが新しいパッケージコンテキストで正しく動作することを確認しています。
-
src/lib/reflect/test.bash
の削除とsrc/run.bash
の変更: これらは、カスタムのテスト実行スクリプトを廃止し、Makefileとgo test
コマンドを介した標準的なテスト実行フローに統一する動きを示しています。これにより、テストの実行方法が簡素化され、Goプロジェクト全体での一貫性が向上します。
これらの変更は、Go言語がその初期段階で、開発者がテストを記述し、実行し、管理する方法について、明確で一貫性のある標準を確立しようとしていたことを明確に示しています。このコミットは、現在のGoのテスト文化の基盤を築いたと言えるでしょう。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の
testing
パッケージ: https://pkg.go.dev/testing - Go言語の初期のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
参考にした情報源リンク
- Go言語の公式ドキュメントやパッケージドキュメントは、Goのテストに関する標準的な情報源です。
- Go言語のGitHubリポジトリのコミット履歴は、Goの進化を追跡するための主要な情報源です。
- Go言語の初期の設計に関する議論やメーリングリストのアーカイブは、特定の設計判断の背景を理解するのに役立ちますが、この特定のコミットに関する詳細な議論は公開されていません。