[インデックス 1157] ファイルの概要
このコミットは、Go言語の初期開発段階において、ユニットテストの自動化を目的とした新しいシェルスクリプト gotest
を導入するものです。これは、現在の go test
コマンドの原型となる非常に初期の実装であり、テストファイルのコンパイル、テスト実行用のメイン関数の動的生成、そしてテストの実行という一連のプロセスを自動化します。
コミット
commit d4953725099792e625decc1a812bff44356dce37
Author: Rob Pike <r@golang.org>
Date: Tue Nov 18 14:12:14 2008 -0800
new gotest shell script (will be a proper command some day, probably)
automates construction and execution of unit tests.
R=rsc
DELTA=60 (58 added, 0 deleted, 2 changed)
OCL=19482
CL=19484
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d4953725099792e625decc1a812bff44356dce37
元コミット内容
新しい gotest
シェルスクリプト(おそらく将来的に適切なコマンドになるだろう)は、ユニットテストの構築と実行を自動化する。
変更の背景
このコミットは2008年11月に行われており、Go言語がまだ公開される前の非常に初期の段階に当たります。当時のGo言語には、現在のような統合された go test
コマンドは存在せず、テストの実行は手動で行うか、個別のスクリプトで対応する必要がありました。
Go言語の設計思想の一つに、テストの容易さがあります。標準ライブラリに testing
パッケージが提供され、go test
コマンドによってテストが自動的に発見・実行される仕組みは、Go言語の大きな特徴です。このコミットは、その自動テスト実行の基盤を築くための最初の一歩として、gotest
というシェルスクリプトを導入しました。コミットメッセージにある「will be a proper command some day, probably」という記述は、このスクリプトが将来的にGoツールチェインの一部として統合されることを示唆しており、現在の go test
コマンドへの進化を予見させるものです。
当時のGo言語のビルドシステムは、6g
(Goコンパイラ for AMD64), 6l
(Goリンカ for AMD64), 6nm
(シンボルリストユーティリティ for AMD64) といった、Plan 9由来のアーキテクチャ固有のコマンドに依存していました。この gotest
スクリプトは、これらの低レベルなツールを直接呼び出すことで、テストのコンパイルと実行を実現しています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の初期のツールチェインとテストに関する概念を理解しておく必要があります。
6g
,6l
,6nm
: これらはGo言語の初期のビルドツールチェーンの一部でした。6g
: Goコンパイラ。Goのソースコードをオブジェクトファイル(.6
拡張子)にコンパイルします。6
はAMD64アーキテクチャを指します。6l
: Goリンカ。オブジェクトファイルを結合し、実行可能ファイルを生成します。6nm
: オブジェクトファイルや実行可能ファイルからシンボルをリストアップするユーティリティ。特に、テスト関数を識別するために使用されます。 これらのツールは、Go 1.0以降のgo build
やgo test
といった統合コマンドに置き換えられましたが、Goのビルドプロセスの根幹をなすものでした。
testing
パッケージ: Go言語の標準ライブラリに含まれる、ユニットテスト、ベンチマーク、サンプルコードテストを記述するためのパッケージです。テスト関数はTestXxx
という命名規則に従い、*testing.T
型の引数を取ります。testing.Main
関数:testing
パッケージが提供する関数で、テストの実行をオーケストレーションします。通常、テスト実行可能ファイルのmain
関数から呼び出され、発見されたすべてのテスト関数を実行します。- シェルスクリプト: このコミットで導入される
gotest
は、Bashシェルスクリプトとして実装されています。これは、Go言語自体でテストツールがまだ十分に成熟していなかったため、既存のシェル機能を利用してテストプロセスを自動化したものです。
技術的詳細
このコミットの主要な技術的詳細は、src/cmd/gotest/gotest
シェルスクリプトに集約されています。このスクリプトは、以下のステップでユニットテストを自動化します。
- テストファイルの特定: カレントディレクトリ内の
test*.go
というパターンにマッチするすべてのGoファイルをテストファイルとして識別します。 - 個別のコンパイル: 識別された各
test*.go
ファイルを6g
コンパイラを使用して個別にコンパイルします。これにより、各テストファイルに対応するオブジェクトファイル(.6
拡張子)が生成されます。 _testmain.go
の動的生成: ここがこのスクリプトの最も重要な部分です。Goのテストフレームワークは、すべてのテスト関数を呼び出す単一のmain
関数を持つ実行可能ファイルを必要とします。このスクリプトは、以下の内容を持つ_testmain.go
というGoソースファイルを動的に生成します。package main
: 実行可能ファイルのエントリポイントとなるmain
パッケージを宣言します。- テストファイルのインポート: 各テストファイル(例:
test_foo.go
)を./test_foo
のようにパッケージとしてインポートします。これにより、各テストファイル内で定義されたテスト関数が_testmain.go
から参照可能になります。 import "testing"
: Goの標準テストパッケージをインポートします。- テスト関数の発見とリスト化:
6nm
コマンドを使用して、コンパイル済みのオブジェクトファイル(.6
ファイル)からテスト関数を抽出します。具体的には、6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./'
というコマンドチェーンが使用されます。6nm $ofiles
: すべてのオブジェクトファイルからシンボルをリストアップします。grep ' T .*·Test'
: グローバルなテキストセクション (T
) にあり、Goの内部的なシンボル名で·Test
を含むもの(例:main·TestMyFunction
)をフィルタリングします。これはGoのテスト関数の命名規則と内部表現に基づいています。sed 's/.* //; s/·/./'
: シンボル名から不要な部分を削除し、main·TestMyFunction
のような形式をmain.TestMyFunction
のような、Goのコードで直接参照できる形式に変換します。
testing.Test
構造体の配列生成: 抽出された各テスト関数に対して、testing.Test
構造体のインスタンスを生成し、それらをvar tests = &[]testing.Test { ... }
という配列に格納します。この配列はtesting.Main
関数に渡されます。main
関数の定義:func main() { testing.Main(tests) }
というmain
関数を定義します。この関数が実行されると、testing.Main
がtests
配列内のすべてのテスト関数を順次実行します。
_testmain.go
のコンパイルとリンク: 生成された_testmain.go
を6g
でコンパイルし、6l
でリンクして実行可能ファイルを生成します。このリンクプロセスでは、ステップ2でコンパイルされた個々のテストファイルのオブジェクトファイルも自動的に結合されます。- テストの実行: 最後に、生成された実行可能ファイル(
6.out
)を実行します。これにより、すべてのユニットテストが実行され、結果が標準出力に表示されます。
この一連のプロセスは、現在の go test
コマンドが内部的に行っていることと非常に似ており、Go言語のテストフレームワークの設計思想が初期段階から確立されていたことを示しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルです。
-
src/cmd/gotest/Makefile
(新規追加):# 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. include ../../Make.conf TARG=gotest clean: @true install: $(TARG) cp $(TARG) $(BIN)/$(TARG)
このMakefileは、
gotest
コマンドのビルドとインストール方法を定義しています。clean
ターゲットは@true
で何もしませんが、これはsrc/cmd/clean.bash
でgotest
のクリーンアップが処理されるためです。 -
src/cmd/gotest/gotest
(新規追加):#!/bin/bash # 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. # Using all the test*.go files in the current directory, write out a file # _testmain.go that runs all its tests. Compile everything and run the # tests. set -e gofiles=$(echo test*.go) ofiles=$(echo $gofiles | sed 's/\.go/.6/g') files=$(echo $gofiles | sed 's/\.go//g') echo $ofiles for i in $gofiles do 6g $i done # They all compile; now generate the code to call them. >{ # package spec echo 'package main' echo # imports for i in $files do echo 'import "./'$i'"' done echo 'import "testing"' # test array echo echo 'var tests = &[]testing.Test {' for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./') do echo ' testing.Test{ "'$i'", &'$i' },' done echo '}' # body echo echo 'func main() {' echo ' testing.Main(tests)' echo '}' }>_testmain.go 6g _testmain.go 6l _testmain.6 6.out
このシェルスクリプトが、テストの自動化ロジックの全てを実装しています。
-
src/cmd/clean.bash
:--- a/src/cmd/clean.bash +++ b/src/cmd/clean.bash @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof +for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof gotest do cd $i make clean
gotest
がクリーンアップ対象のコマンドリストに追加されました。 -
src/cmd/make.bash
:--- a/src/cmd/make.bash +++ b/src/cmd/make.bash @@ -12,7 +12,7 @@ bash mkenam make enam.o cd .. -for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof +for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof gotest do echo; echo; echo %%%% making $i %%%%; echo cd $i
gotest
がビルド対象のコマンドリストに追加されました。 -
src/lib/make.bash
:--- a/src/lib/make.bash +++ b/src/lib/make.bash @@ -48,6 +48,7 @@ buildfiles flag.go\ bufio.go\ once.go\ bignum.go\ +\t\ttesting.go\ builddirs net\ time\
testing.go
が標準ライブラリのビルド対象ファイルに追加されました。これは、gotest
がtesting
パッケージに依存するため、そのパッケージがビルドシステムによって適切に処理されるようにするための変更です。
コアとなるコードの解説
src/cmd/gotest/gotest
シェルスクリプトは、Go言語のテスト実行の基本的なメカニズムを初期段階でどのように実現していたかを示す貴重な例です。
スクリプトの冒頭では、カレントディレクトリ内の test*.go
ファイルを特定し、それらのファイル名からオブジェクトファイル名(.6
)とパッケージ名(.go
拡張子なし)を生成しています。
gofiles=$(echo test*.go)
ofiles=$(echo $gofiles | sed 's/\.go/.6/g')
files=$(echo $gofiles | sed 's/\.go//g')
次に、各テストGoファイルを 6g
コンパイラで個別にコンパイルします。
for i in $gofiles
do
6g $i
done
この部分が最も重要で、Goのテスト実行可能ファイルのエントリポイントとなる _testmain.go
を動的に生成しています。
>{
# package spec
echo 'package main'
echo
# imports
for i in $files
do
echo 'import "./'$i'"'
done
echo 'import "testing"'
# test array
echo
echo 'var tests = &[]testing.Test {'
for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
do
echo ' testing.Test{ "'$i'", &'$i' },'
done
echo '}'
# body
echo
echo 'func main() {'
echo ' testing.Main(tests)'
echo '}'
}>_testmain.go
このブロックでは、echo
コマンドとシェルスクリプトのループ、パイプを組み合わせて、Goのソースコードを文字列として出力し、それを _testmain.go
ファイルにリダイレクトしています。
package main
とimport "testing"
は、テスト実行可能ファイルの基本的な構造を定義します。for i in $files; do echo 'import "./'$i'"'; done
の部分は、各テストファイル(例:test_my_feature.go
)を./test_my_feature
というローカルパッケージとしてインポートします。これにより、_testmain.go
から各テストファイル内の関数にアクセスできるようになります。for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
の部分は、6nm
コマンドとgrep
、sed
を組み合わせて、コンパイル済みのオブジェクトファイルからテスト関数(TestXxx
という命名規則に従う関数)のシンボル名を抽出し、Goのコードで参照可能な形式に整形しています。- 抽出されたテスト関数名を使って、
testing.Test
構造体の配列tests
を動的に構築します。各要素はtesting.Test{ "テスト関数名", &テスト関数名 }
の形式を取ります。 - 最後に、
func main() { testing.Main(tests) }
というmain
関数を定義し、生成されたtests
配列をtesting.Main
関数に渡してテスト実行を委ねます。
_testmain.go
が生成された後、スクリプトはそれを 6g
でコンパイルし、6l
でリンクして実行可能ファイル 6.out
を作成します。
6g _testmain.go
6l _testmain.6
6.out
そして、最後に 6.out
を実行することで、すべてのテストが実行されます。
この一連の処理は、現在の go test
コマンドが内部的に行っていることと本質的に同じであり、Go言語のテストフレームワークが、テストコードを通常のGoプログラムとしてコンパイル・リンク・実行するというシンプルな設計原則に基づいていることを示しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の
testing
パッケージ: https://pkg.go.dev/testing - Go言語の
go test
コマンド: https://go.dev/cmd/go/#hdr-Test_packages
参考にした情報源リンク
- Go言語の初期のツールチェインに関する情報 (6g, 6l, 6nm):
- https://go.dev/doc/go1.0#toolchain (Go 1.0のリリースノートに、これらのツールが統合されたことが記載されています)
- https://go.dev/blog/go1.1 (Go 1.1で
go test
がさらに成熟したことが示唆されています)
- Go言語のテストの歴史と哲学に関する一般的な情報:
- https://go.dev/blog/testing (Go言語のテストに関する公式ブログ記事)
- https://go.dev/doc/code#Testing (Go言語のコードの書き方ガイドにおけるテストのセクション)
- Web検索結果 (Google Search): "Go language early testing framework history gotest 6g 6l 6nm"
- この検索結果は、
go test
とtesting
パッケージがGoのテストの核であり、6g
,6l
,6nm
が初期のビルドシステムの一部であったことを確認するのに役立ちました。# [インデックス 1157] ファイルの概要
- この検索結果は、
このコミットは、Go言語の初期開発段階において、ユニットテストの自動化を目的とした新しいシェルスクリプト gotest
を導入するものです。これは、現在の go test
コマンドの原型となる非常に初期の実装であり、テストファイルのコンパイル、テスト実行用のメイン関数の動的生成、そしてテストの実行という一連のプロセスを自動化します。
コミット
commit d4953725099792e625decc1a812bff44356dce37
Author: Rob Pike <r@golang.org>
Date: Tue Nov 18 14:12:14 2008 -0800
new gotest shell script (will be a proper command some day, probably)
automates construction and execution of unit tests.
R=rsc
DELTA=60 (58 added, 0 deleted, 2 changed)
OCL=19482
CL=19484
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d4953725099792e625decc1a812bff44356dce37
元コミット内容
新しい gotest
シェルスクリプト(おそらく将来的に適切なコマンドになるだろう)は、ユニットテストの構築と実行を自動化する。
変更の背景
このコミットは2008年11月に行われており、Go言語がまだ公開される前の非常に初期の段階に当たります。当時のGo言語には、現在のような統合された go test
コマンドは存在せず、テストの実行は手動で行うか、個別のスクリプトで対応する必要がありました。
Go言語の設計思想の一つに、テストの容易さがあります。標準ライブラリに testing
パッケージが提供され、go test
コマンドによってテストが自動的に発見・実行される仕組みは、Go言語の大きな特徴です。このコミットは、その自動テスト実行の基盤を築くための最初の一歩として、gotest
というシェルスクリプトを導入しました。コミットメッセージにある「will be a proper command some day, probably」という記述は、このスクリプトが将来的にGoツールチェインの一部として統合されることを示唆しており、現在の go test
コマンドへの進化を予見させるものです。
当時のGo言語のビルドシステムは、6g
(Goコンパイラ for AMD64), 6l
(Goリンカ for AMD64), 6nm
(シンボルリストユーティリティ for AMD64) といった、Plan 9由来のアーキテクチャ固有のコマンドに依存していました。この gotest
スクリプトは、これらの低レベルなツールを直接呼び出すことで、テストのコンパイルと実行を実現しています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の初期のツールチェインとテストに関する概念を理解しておく必要があります。
6g
,6l
,6nm
: これらはGo言語の初期のビルドツールチェーンの一部でした。6g
: Goコンパイラ。Goのソースコードをオブジェクトファイル(.6
拡張子)にコンパイルします。6
はAMD64アーキテクチャを指します。6l
: Goリンカ。オブジェクトファイルを結合し、実行可能ファイルを生成します。6nm
: オブジェクトファイルや実行可能ファイルからシンボルをリストアップするユーティリティ。特に、テスト関数を識別するために使用されます。 これらのツールは、Go 1.0以降のgo build
やgo test
といった統合コマンドに置き換えられましたが、Goのビルドプロセスの根幹をなすものでした。
testing
パッケージ: Go言語の標準ライブラリに含まれる、ユニットテスト、ベンチマーク、サンプルコードテストを記述するためのパッケージです。テスト関数はTestXxx
という命名規則に従い、*testing.T
型の引数を取ります。testing.Main
関数:testing
パッケージが提供する関数で、テストの実行をオーケストレーションします。通常、テスト実行可能ファイルのmain
関数から呼び出され、発見されたすべてのテスト関数を実行します。- シェルスクリプト: このコミットで導入される
gotest
は、Bashシェルスクリプトとして実装されています。これは、Go言語自体でテストツールがまだ十分に成熟していなかったため、既存のシェル機能を利用してテストプロセスを自動化したものです。
技術的詳細
このコミットの主要な技術的詳細は、src/cmd/gotest/gotest
シェルスクリプトに集約されています。このスクリプトは、以下のステップでユニットテストを自動化します。
- テストファイルの特定: カレントディレクトリ内の
test*.go
というパターンにマッチするすべてのGoファイルをテストファイルとして識別します。 - 個別のコンパイル: 識別された各
test*.go
ファイルを6g
コンパイラを使用して個別にコンパイルします。これにより、各テストファイルに対応するオブジェクトファイル(.6
拡張子)が生成されます。 _testmain.go
の動的生成: ここがこのスクリプトの最も重要な部分です。Goのテストフレームワークは、すべてのテスト関数を呼び出す単一のmain
関数を持つ実行可能ファイルを必要とします。このスクリプトは、以下の内容を持つ_testmain.go
というGoソースファイルを動的に生成します。package main
: 実行可能ファイルのエントリポイントとなるmain
パッケージを宣言します。- テストファイルのインポート: 各テストファイル(例:
test_foo.go
)を./test_foo
のようにパッケージとしてインポートします。これにより、各テストファイル内で定義されたテスト関数が_testmain.go
から参照可能になります。 import "testing"
: Goの標準テストパッケージをインポートします。- テスト関数の発見とリスト化:
6nm
コマンドを使用して、コンパイル済みのオブジェクトファイル(.6
ファイル)からテスト関数を抽出します。具体的には、6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./'
というコマンドチェーンが使用されます。6nm $ofiles
: すべてのオブジェクトファイルからシンボルをリストアップします。grep ' T .*·Test'
: グローバルなテキストセクション (T
) にあり、Goの内部的なシンボル名で·Test
を含むもの(例:main·TestMyFunction
)をフィルタリングします。これはGoのテスト関数の命名規則と内部表現に基づいています。sed 's/.* //; s/·/./'
: シンボル名から不要な部分を削除し、main·TestMyFunction
のような形式をmain.TestMyFunction
のような、Goのコードで直接参照できる形式に変換します。
testing.Test
構造体の配列生成: 抽出された各テスト関数に対して、testing.Test
構造体のインスタンスを生成し、それらをvar tests = &[]testing.Test { ... }
という配列に格納します。この配列はtesting.Main
関数に渡されます。main
関数の定義:func main() { testing.Main(tests) }
というmain
関数を定義します。この関数が実行されると、testing.Main
がtests
配列内のすべてのテスト関数を順次実行します。
_testmain.go
のコンパイルとリンク: 生成された_testmain.go
を6g
でコンパイルし、6l
でリンクして実行可能ファイルを生成します。このリンクプロセスでは、ステップ2でコンパイルされた個々のテストファイルのオブジェクトファイルも自動的に結合されます。- テストの実行: 最後に、生成された実行可能ファイル(
6.out
)を実行します。これにより、すべてのユニットテストが実行され、結果が標準出力に表示されます。
この一連のプロセスは、現在の go test
コマンドが内部的に行っていることと非常に似ており、Go言語のテストフレームワークの設計思想が初期段階から確立されていたことを示しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルです。
-
src/cmd/gotest/Makefile
(新規追加):# 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. include ../../Make.conf TARG=gotest clean: @true install: $(TARG) cp $(TARG) $(BIN)/$(TARG)
このMakefileは、
gotest
コマンドのビルドとインストール方法を定義しています。clean
ターゲットは@true
で何もしませんが、これはsrc/cmd/clean.bash
でgotest
のクリーンアップが処理されるためです。 -
src/cmd/gotest/gotest
(新規追加):#!/bin/bash # 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. # Using all the test*.go files in the current directory, write out a file # _testmain.go that runs all its tests. Compile everything and run the # tests. set -e gofiles=$(echo test*.go) ofiles=$(echo $gofiles | sed 's/\.go/.6/g') files=$(echo $gofiles | sed 's/\.go//g') echo $ofiles for i in $gofiles do 6g $i done # They all compile; now generate the code to call them. >{ # package spec echo 'package main' echo # imports for i in $files do echo 'import "./'$i'"' done echo 'import "testing"' # test array echo echo 'var tests = &[]testing.Test {' for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./') do echo ' testing.Test{ "'$i'", &'$i' },' done echo '}' # body echo echo 'func main() {' echo ' testing.Main(tests)' echo '}'
}>_testmain.go
6g _testmain.go
6l _testmain.6
6.out
```
このシェルスクリプトが、テストの自動化ロジックの全てを実装しています。
-
src/cmd/clean.bash
:--- a/src/cmd/clean.bash +++ b/src/cmd/clean.bash @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof +for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof gotest do cd $i make clean
gotest
がクリーンアップ対象のコマンドリストに追加されました。 -
src/cmd/make.bash
:--- a/src/cmd/make.bash +++ b/src/cmd/make.bash @@ -12,7 +12,7 @@ bash mkenam make enam.o cd .. -for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof +for i in cc 6l 6a 6c gc 6g ar db nm blyacc acid cov gobuild prof gotest do echo; echo; echo %%%% making $i %%%%; echo cd $i
gotest
がビルド対象のコマンドリストに追加されました。 -
src/lib/make.bash
:--- a/src/lib/make.bash +++ b/src/lib/make.bash @@ -48,6 +48,7 @@ buildfiles flag.go\ bufio.go\ once.go\ bignum.go\ +\t\ttesting.go\ builddirs net\ time\
testing.go
が標準ライブラリのビルド対象ファイルに追加されました。これは、gotest
がtesting
パッケージに依存するため、そのパッケージがビルドシステムによって適切に処理されるようにするための変更です。
コアとなるコードの解説
src/cmd/gotest/gotest
シェルスクリプトは、Go言語のテスト実行の基本的なメカニズムを初期段階でどのように実現していたかを示す貴重な例です。
スクリプトの冒頭では、カレントディレクトリ内の test*.go
ファイルを特定し、それらのファイル名からオブジェクトファイル名(.6
)とパッケージ名(.go
拡張子なし)を生成しています。
gofiles=$(echo test*.go)
ofiles=$(echo $gofiles | sed 's/\.go/.6/g')
files=$(echo $gofiles | sed 's/\.go//g')
次に、各テストGoファイルを 6g
コンパイラで個別にコンパイルします。
for i in $gofiles
do
6g $i
done
この部分が最も重要で、Goのテスト実行可能ファイルのエントリポイントとなる _testmain.go
を動的に生成しています。
>{
# package spec
echo 'package main'
echo
# imports
for i in $files
do
echo 'import "./'$i'"'
done
echo 'import "testing"'
# test array
echo
echo 'var tests = &[]testing.Test {'
for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
do
echo ' testing.Test{ "'$i'", &'$i' },'
done
echo '}'
# body
echo
echo 'func main() {'
echo ' testing.Main(tests)'
echo '}'
}>_testmain.go
このブロックでは、echo
コマンドとシェルスクリプトのループ、パイプを組み合わせて、Goのソースコードを文字列として出力し、それを _testmain.go
ファイルにリダイレクトしています。
package main
とimport "testing"
は、テスト実行可能ファイルの基本的な構造を定義します。for i in $files; do echo 'import "./'$i'"'; done
の部分は、各テストファイル(例:test_my_feature.go
)を./test_my_feature
というローカルパッケージとしてインポートします。これにより、_testmain.go
から各テストファイル内の関数にアクセスできるようになります。for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
の部分は、6nm
コマンドとgrep
、sed
を組み合わせて、コンパイル済みのオブジェクトファイルからテスト関数(TestXxx
という命名規則に従う関数)のシンボル名を抽出し、Goのコードで参照可能な形式に整形しています。- 抽出されたテスト関数名を使って、
testing.Test
構造体の配列tests
を動的に構築します。各要素はtesting.Test{ "テスト関数名", &テスト関数名 }
の形式を取ります。 - 最後に、
func main() { testing.Main(tests) }
というmain
関数を定義し、生成されたtests
配列をtesting.Main
関数に渡してテスト実行を委ねます。
_testmain.go
が生成された後、スクリプトはそれを 6g
でコンパイルし、6l
でリンクして実行可能ファイル 6.out
を作成します。
6g _testmain.go
6l _testmain.6
6.out
そして、最後に 6.out
を実行することで、すべてのテストが実行されます。
この一連の処理は、現在の go test
コマンドが内部的に行っていることと本質的に同じであり、Go言語のテストフレームワークが、テストコードを通常のGoプログラムとしてコンパイル・リンク・実行するというシンプルな設計原則に基づいていることを示しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語の
testing
パッケージ: https://pkg.go.dev/testing - Go言語の
go test
コマンド: https://go.dev/cmd/go/#hdr-Test_packages
参考にした情報源リンク
- Go言語の初期のツールチェインに関する情報 (6g, 6l, 6nm):
- https://go.dev/doc/go1.0#toolchain (Go 1.0のリリースノートに、これらのツールが統合されたことが記載されています)
- https://go.dev/blog/go1.1 (Go 1.1で
go test
がさらに成熟したことが示唆されています)
- Go言語のテストの歴史と哲学に関する一般的な情報:
- https://go.dev/blog/testing (Go言語のテストに関する公式ブログ記事)
- https://go.dev/doc/code#Testing (Go言語のコードの書き方ガイドにおけるテストのセクション)
- Web検索結果 (Google Search): "Go language early testing framework history gotest 6g 6l 6nm"
- この検索結果は、
go test
とtesting
パッケージがGoのテストの核であり、6g
,6l
,6nm
が初期のビルドシステムの一部であったことを確認するのに役立ちました。
- この検索結果は、