[インデックス 10838] ファイルの概要
このコミットは、Go言語の公式ツールチェインにgo test
コマンドを導入し、それまでのテストツールであるgotest
を置き換えるものです。これにより、Goプロジェクトのテスト実行がより統合され、ビルドプロセスとの連携が改善されます。
コミット
commit 20090df70fa018b1ac0fe15434c0dbd44151bb93
Author: Russ Cox <rsc@golang.org>
Date: Thu Dec 15 23:51:04 2011 -0500
go: implement test command
Gotest tries to build things, for which it invokes make,
and it was too hard to coordinate go invoking gotest
invoking go to build the test binary, so put all the code
here instead. Gotest will be deleted once we switch.
The only code that really made sense to copy verbatim
was the flag parsing.
This remains a work in progress. There are still plenty
of things to clean up and make better, but this is a good
checkpoint. It can run all the tests in the tree (except
runtime, which it can't build yet).
$ go test all -short
ok archive/tar
ok archive/zip
ok bufio
? builtin [no test files]
ok bytes
ok compress/bzip2
ok compress/flate
ok compress/gzip
ok compress/lzw
ok compress/zlib
ok container/heap
ok container/list
ok container/ring
? crypto [no test files]
ok crypto/aes
ok crypto/bcrypt
ok crypto/blowfish
ok crypto/cast5
ok crypto/cipher
ok crypto/des
ok crypto/dsa
ok crypto/ecdsa
ok crypto/elliptic
ok crypto/hmac
ok crypto/md4
ok crypto/md5
ok crypto/ocsp
ok crypto/openpgp
ok crypto/openpgp/armor
ok crypto/openpgp/elgamal
? crypto/openpgp/error [no test files]
ok crypto/openpgp/packet
ok crypto/openpgp/s2k
ok crypto/rand
ok crypto/rc4
ok crypto/ripemd160
ok crypto/rsa
ok crypto/sha1
ok crypto/sha256
ok crypto/sha512
ok crypto/subtle
ok crypto/tls
ok crypto/twofish
ok crypto/x509
? crypto/x509/pkix [no test files]
ok crypto/xtea
ok debug/dwarf
ok debug/elf
ok debug/gosym
ok debug/macho
ok debug/pe
ok encoding/ascii85
ok encoding/asn1
ok encoding/base32
ok encoding/base64
ok encoding/binary
ok encoding/csv
ok encoding/git85
ok encoding/gob
ok encoding/hex
ok encoding/json
ok encoding/pem
ok encoding/xml
ok errors
ok exp/ebnf
? exp/ebnflint [no test files]
ok exp/gotype
ok exp/norm
ok exp/spdy
ok exp/sql
ok exp/sql/driver
ok exp/ssh
ok exp/types
ok expvar
ok flag
ok fmt
ok go/ast
ok go/build
ok go/doc
ok go/parser
ok go/printer
ok go/scanner
ok go/token
? hash [no test files]
ok hash/adler32
ok hash/crc32
ok hash/crc64
ok hash/fnv
ok html
ok html/template
ok image
? image/bmp [no test files]
? image/color [no test files]
ok image/draw
? image/gif [no test files]
ok image/jpeg
ok image/png
ok image/tiff
ok image/ycbcr
ok index/suffixarray
ok io
ok io/ioutil
ok log
ok log/syslog
ok math
ok math/big
ok math/cmplx
ok math/rand
ok mime
ok mime/multipart
ok net
? net/dict [no test files]
ok net/http
ok net/http/cgi
ok net/http/fcgi
? net/http/httptest [no test files]
ok net/http/httputil
? net/http/pprof [no test files]
ok net/mail
ok net/rpc
ok net/rpc/jsonrpc
ok net/smtp
ok net/textproto
ok net/url
ok old/netchan
ok old/regexp
ok old/template
ok os
ok os/exec
ok os/signal
ok os/user
ok patch
ok path
ok path/filepath
ok reflect
ok regexp
ok regexp/syntax
# cd /Users/rsc/g/go/src/pkg/runtime; 6g -o /var/folders/mw/qfnx8hhd1_s9mm9wtbng0hw80000gn/T/go-build874847916/runtime_test/_obj/_go_.6 -p runtime_test -I /var/folders/mw/qfnx8hhd1_s9mm9wtbng0hw80000gn/T/go-build874847916 append_test.go chan_test.go closure_test.go gc_test.go mfinal_test.go proc_test.go sema_test.go softfloat64_test.go symtab_test.go
proc_test.go:87: undefined: runtime.Entersyscall
proc_test.go:88: undefined: runtime.Exitsyscall
proc_test.go:111: undefined: runtime.Entersyscall
proc_test.go:116: undefined: runtime.Exitsyscall
softfloat64_test.go:79: undefined: Fadd64
softfloat64_test.go:80: undefined: Fsub64
softfloat64_test.go:82: undefined: Fmul64
softfloat64_test.go:83: undefined: Fdiv64
softfloat64_test.go:94: undefined: F64to32
softfloat64_test.go:99: undefined: F32to64
softfloat64_test.go:99: too many errors
exit status 1
FAIL runtime [build failed]
? runtime/cgo [no test files]
ok runtime/debug
ok runtime/pprof
ok sort
ok strconv
ok strings
ok sync
ok sync/atomic
? syscall [no test files]
? testing [no test files]
? testing/iotest [no test files]
ok testing/quick
ok testing/script
ok text/scanner
ok text/tabwriter
ok text/template
ok text/template/parse
ok time
ok unicode
ok unicode/utf16
ok unicode/utf8
? unsafe [no test files]
ok websocket
$
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5495055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/20090df70fa018b1ac0fe15434c0dbd44151bb93
元コミット内容
このコミットは、Go言語の公式ツールであるgo
コマンドにtest
サブコマンドを実装するものです。これにより、既存のgotest
ツールが置き換えられます。コミットメッセージによると、gotest
はビルドのためにmake
を呼び出し、さらにgo
がgotest
を呼び出し、gotest
がテストバイナリをビルドするためにgo
を呼び出すという複雑な連携が発生しており、この循環的な依存関係が問題となっていました。このコミットでは、その複雑さを解消するために、テスト関連のすべてのコードをgo
コマンド自体に統合しています。
特に、フラグのパース処理はgotest
からそのままコピーされた唯一のコード部分であると述べられています。このコミットはまだ作業中(work in progress)であるものの、ツリー内のほとんどのテスト(runtime
パッケージを除く)を実行できる「良いチェックポイント」であるとされています。
コミットメッセージには、go test all -short
を実行した際の出力例も含まれており、多くのパッケージがok
(テスト成功)と表示されている一方で、一部のパッケージは[no test files]
(テストファイルなし)や[build failed]
(ビルド失敗)となっています。特にruntime
パッケージのテストがビルドに失敗していることが示されています。
変更の背景
Go言語の初期のテスト実行環境は、gotest
という独立したツールによって提供されていました。しかし、このgotest
ツールは、テストバイナリのビルドにmake
を利用し、さらにgo
コマンドとgotest
の間で相互に呼び出しが発生するような、複雑で扱いにくい依存関係を持っていました。具体的には、go
コマンドがgotest
を呼び出し、gotest
がテストバイナリをビルドするために再びgo
コマンドを呼び出すという、循環的なビルドプロセスが存在していました。
このような複雑な連携は、Goツールチェイン全体のビルドシステムを理解し、デバッグする上で大きな障壁となっていました。また、テストの実行効率や信頼性にも影響を与えていた可能性があります。
このコミットの主な目的は、この複雑なビルドプロセスを簡素化し、テスト実行をGoツールチェインのコア機能として統合することです。go test
コマンドをgo
ツール自体に組み込むことで、外部ツール(make
など)への依存を減らし、テストバイナリのビルドと実行をより直接的かつ効率的に制御できるようになります。これにより、開発者はよりスムーズにテストを実行できるようになり、Go言語のテストエコシステム全体の健全性が向上します。
前提知識の解説
このコミットを理解するためには、以下のGo言語および関連ツールの概念を理解しておく必要があります。
- Go言語のパッケージシステム: Goのコードはパッケージに分割され、
import
文によって他のパッケージを参照します。各パッケージは通常、独自のディレクトリに配置されます。 go
コマンド: Go言語の公式ツールチェインの中心となるコマンドで、コードのビルド、実行、テスト、フォーマットなど、様々なタスクを管理します。go build
: Goソースコードをコンパイルして実行可能バイナリやパッケージアーカイブ(.a
ファイル)を生成するコマンドです。go install
:go build
と同様にコンパイルを行いますが、生成されたバイナリやパッケージアーカイブをGOPATH/bin
やGOPATH/pkg
などの標準的なインストールパスに配置します。go/build
パッケージ: Goのソースコードツリーの構造を解析し、パッケージの依存関係やソースファイルの情報を取得するための標準ライブラリパッケージです。このパッケージは、go
コマンドがビルドやテストの対象となるパッケージを特定するために内部的に利用します。testing
パッケージ: Go言語の標準ライブラリに含まれるテストフレームワークです。TestXxx
という命名規則に従う関数をテスト関数として認識し、BenchmarkXxx
をベンチマーク関数、ExampleXxx
をサンプルコードとして扱います。gotest
: このコミット以前に存在した、Go言語のテストを実行するための独立したツールです。このコミットによって置き換えられます。Makefile
: ソフトウェアのビルドプロセスを自動化するためのツールであるmake
が使用する設定ファイルです。Go言語の初期のビルドシステムでは、Makefile
が広く利用されていました。- フラグ(コマンドライン引数)のパース: コマンドラインから渡されるオプション(例:
-v
,-short
)を解析し、プログラム内で利用可能な形式に変換する処理です。Goでは標準のflag
パッケージが提供されていますが、このコミットのようにカスタムでパース処理を実装することもあります。 - テストバイナリ: Goのテストを実行する際に生成される実行可能ファイルです。このバイナリは、テスト対象のパッケージのコードと、
testing
パッケージによって生成されるテストランナーのコードを含んでいます。
技術的詳細
このコミットは、go test
コマンドの内部実装を大幅に変更しています。主な技術的変更点は以下の通りです。
-
go test
コマンドの導入とgotest
の置き換え:src/cmd/go/test.go
にcmdTest
という新しいCommand
構造体が定義され、runTest
関数がその実行ロジックとして設定されています。main.go
のコマンドディスパッチロジックが更新され、test
コマンドが認識されるようになります。- これにより、従来の
gotest
ツールが不要となり、Goツールチェインにテスト機能が統合されます。
-
カスタムフラグパースの導入:
src/cmd/go/testflag.go
という新しいファイルが追加され、go test
コマンドのフラグをカスタムでパースするロジックが実装されています。- これは、
go test
に渡されるフラグの一部がgo
コマンド自身のためのものであり、残りが生成されるテストバイナリ(6.out
など)のためのものであるという、複雑な要件に対応するためです。 testFlagDefn
というtestFlagSpec
のスライスが定義され、go test
が認識するフラグ(-c
,-x
,-file
など)と、テストバイナリに渡されるフラグ(-bench
,-run
,-short
,-v
など)が区別されます。テストバイナリに渡されるフラグには、自動的に-test.
プレフィックスが付加されます(例:-v
が-test.v
になる)。Command
構造体にCustomFlags
フィールドが追加され、go test
が独自のフラグパースを行うことを示しています。
-
テストバイナリのビルドプロセスの再構築:
runTest
関数内で、go/build
パッケージを利用してテスト対象のパッケージを特定し、テストに必要なソースファイル(通常のGoファイル、_test.go
ファイル、_xtest.go
ファイル)を収集します。- テストバイナリをビルドするために、一時的な作業ディレクトリ(
b.work
)内に特別な構造を作成します。特に、テストパッケージのアーカイブ(.a
ファイル)は、他のパッケージと衝突しないように、$WORK/unicode/utf8/_test/unicode/utf8.a
のようなパスに配置されます。 writeTestmain
関数が導入され、テスト対象のパッケージのテスト関数(TestXxx
,BenchmarkXxx
,ExampleXxx
)を検出して、それらを呼び出すための_testmain.go
というGoソースファイルを生成します。この_testmain.go
ファイルは、testing.Main
関数を呼び出すことで、テストの実行をオーケストレートします。builder
構造体とaction
構造体が拡張され、テストビルドプロセスをサポートするための新しいフィールド(pkgdir
,ignoreFail
)とロジックが追加されています。build
アクションとinstall
アクションが、テストバイナリのビルドと配置に対応するように修正されています。
-
テスト実行と結果の表示:
runTest
関数は、ビルドされたテストバイナリを実行し、その標準出力と終了ステータスをキャプチャします。- テストバイナリの出力に基づいて、
ok
、FAIL
、?
(テストファイルなし)などの結果を整形して表示します。 bytes.Equal(out, pass[1:]) || bytes.HasSuffix(out, pass)
という条件で、テストが成功したかどうかを判断しています。これは、テストバイナリが\nPASS\n
という文字列を出力した場合に成功とみなすことを意味します。
-
go/ast
とgo/parser
、go/doc
の活用:writeTestmain
関数内で、go/parser
を使ってテストソースファイルを解析し、go/ast
を使ってAST(抽象構文木)を走査します。isTest
関数は、関数名がTest
、Benchmark
、Example
のいずれかで始まり、その後に小文字が続かない場合にテスト関数と判断するロジックを提供します。go/doc
パッケージは、Example
関数のドキュメントコメントから期待される出力を抽出するために使用されます。
この変更により、Goのテストシステムは、より統合され、自己完結型になり、外部のビルドツールへの依存が軽減されました。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は以下のファイルに集中しています。
-
src/cmd/go/Makefile
:testflag.go
がGOFILES
リストに追加され、新しいフラグパースロジックがビルド対象に含まれるようになります。
-
src/cmd/go/build.go
:builder
構造体にgoroot
フィールドが追加されます。action
構造体にpkgdir
(パッケージの出力ディレクトリ)とignoreFail
(依存関係の失敗を無視するかどうか)フィールドが追加されます。action
メソッド内で、pkgdir
の初期化ロジックが追加されます。do
メソッド内で、依存関係が失敗した場合にignoreFail
がtrue
であれば処理を続行するロジックが追加されます。build
メソッドとinstall
メソッドが、新しいpkgdir
とaction
構造体の変更に合わせて調整されます。特に、install
はビルドされたaction
のpkgbin
またはpkgobj
を参照するように変更されます。
-
src/cmd/go/main.go
:Command
構造体にCustomFlags
ブール値フィールドが追加されます。これは、コマンドが独自のフラグパースを行うかどうかを示します。main
関数内のコマンドディスパッチロジックが変更され、cmd.CustomFlags
がtrue
の場合、cmd.Flag.Parse
をスキップし、引数をそのまま渡すようになります。
-
src/cmd/go/pkg.go
:Package
構造体にpkgdir
フィールドが追加されます。これは、パッケージのビルド成果物が配置されるディレクトリをオーバーライドするために使用されます。
-
src/cmd/go/test.go
:- このファイルが大幅に拡張され、
go test
コマンドの主要なロジックが実装されます。 cmdTest
というCommand
構造体が定義され、CustomFlags: true
が設定されます。runTest
関数が実装され、テスト対象パッケージの特定、テストバイナリのビルド、実行、結果の表示を行います。test
メソッドがbuilder
構造体に追加され、個々のパッケージのテストビルドと実行のためのaction
を生成します。runTest
メソッドがbuilder
構造体に追加され、テストバイナリの実行と結果の処理を行います。notest
メソッドがbuilder
構造体に追加され、テストファイルがないパッケージの処理を行います。isTest
関数が追加され、関数名からテスト、ベンチマーク、サンプル関数を識別します。writeTestmain
関数が追加され、_testmain.go
ファイルを生成します。testFuncs
、testFunc
構造体、testFileSet
、testmainTmpl
(_testmain.go
のテンプレート)が定義されます。load
メソッドがtestFuncs
に追加され、ソースファイルを解析してテスト、ベンチマーク、サンプル関数を抽出します。
- このファイルが大幅に拡張され、
-
src/cmd/go/testflag.go
:- この新しいファイルには、
go test
コマンドのカスタムフラグパースロジックが含まれます。 usageMessage
、testUsage
関数が定義されます。testFlagSpec
構造体とtestFlagDefn
スライスが定義され、認識されるフラグとその特性(ブール値か、テストバイナリに渡すかなど)を記述します。testFlags
関数が実装され、コマンドライン引数を解析し、go test
自身のフラグとテストバイナリに渡すフラグを分離します。testFlag
関数が、個々の引数が既知のフラグであるかを判断し、その値と特性を返します。setBoolFlag
関数が、ブール値フラグの値を設定します。
- この新しいファイルには、
コアとなるコードの解説
このコミットの核となるのは、src/cmd/go/test.go
とsrc/cmd/go/testflag.go
に実装されたgo test
コマンドのロジックです。
src/cmd/go/test.go
このファイルは、go test
コマンドの主要な実行フローを定義しています。
-
cmdTest
とrunTest
:var cmdTest = &Command{ CustomFlags: true, // 独自のフラグパースを行うことを示す UsageLine: "test [importpath...] [-file a.go -file b.go ...] [-c] [-x] [flags for test binary]", Short: "test packages", // ... Long description ... } func init() { cmdTest.Run = runTest // コマンド実行関数を設定 }
cmdTest
はgo
コマンドのサブコマンドとして登録され、runTest
関数がそのエントリポイントとなります。CustomFlags: true
は、go test
が独自のフラグ処理を行うことを示しており、main.go
の汎用フラグパースロジックをバイパスします。 -
runTest
関数:func runTest(cmd *Command, args []string) { // ... フラグとパッケージの解析 ... pkgs := packages(args[:i]) // テスト対象のパッケージを特定 testArgs = testFlags(args[i:]) // テストバイナリに渡すフラグをパース var b builder b.init(false, false, testX) // ビルダーを初期化 var builds, runs []*action for _, p := range pkgs { buildTest, runTest, err := b.test(p) // 各パッケージのテストビルド/実行アクションを生成 // ... エラーハンドリング ... builds = append(builds, buildTest) runs = append(runs, runTest) } // ... ビルドと実行の依存関係を設定 ... allRuns := &action{f: (*builder).nop, deps: runs} b.do(allRuns) // 全てのアクションを実行 }
runTest
は、まずコマンドライン引数からテスト対象のパッケージと、テストバイナリに渡すフラグを分離します。次に、builder
を初期化し、各パッケージに対してb.test(p)
を呼び出して、テストバイナリのビルドと実行のためのaction
(Goのビルドシステムにおけるタスク単位)を生成します。最後に、これらのアクションを順次実行します。 -
builder.test
メソッド:func (b *builder) test(p *Package) (buildAction, runAction *action, err error) { // ... テストファイルがない場合の処理 ... // ptest, pxtest, pmain パッケージ構造体を構築 // ... testDir := filepath.Join(b.work, filepath.FromSlash(p.ImportPath+"/_test")) // 一時テストディレクトリ // ... if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), p); err != nil { return nil, nil, err } // ... ptest, pxtest の初期化 ... // pmain (テストバイナリ) のアクションを生成 pmainAction := b.action(modeBuild, modeBuild, pmain) pmainAction.pkgbin = filepath.Join(testDir, "test.out") // 出力バイナリパスを設定 if testC { // -c フラグが指定された場合 // ... コピーアクションを生成 ... } else { // テストを実行する場合 runAction = &action{ f: (*builder).runTest, // 実行関数は builder.runTest deps: []*action{pmainAction}, // pmainAction に依存 p: p, ignoreFail: true, // 依存関係の失敗を無視 } } return pmainAction, runAction, nil }
このメソッドは、与えられたパッケージ
p
に対して、テストバイナリをビルドし、実行するためのaction
を生成します。重要なのは、テストコードをコンパイルするために一時的なディレクトリ構造を作成し、_testmain.go
という特別なファイルを生成することです。この_testmain.go
が、実際のテストランナーとして機能します。 -
builder.runTest
メソッド:func (b *builder) runTest(a *action) error { // ... -n, -v フラグの処理 ... if a.failed { // ビルドが失敗した場合 // ... エラーメッセージ表示 ... return nil } cmd := exec.Command(a.deps[0].pkgbin, testArgs...) // テストバイナリを実行 cmd.Dir = a.p.Dir // 実行ディレクトリを設定 out, err := cmd.CombinedOutput() // 出力をキャプチャ if err == nil && (bytes.Equal(out, pass[1:]) || bytes.HasSuffix(out, pass)) { fmt.Printf("ok \t%s\n", a.p.ImportPath) // 成功 return nil } fmt.Printf("FAIL\t%s\n", a.p.ImportPath) // 失敗 exitStatus = 1 if len(out) > 0 { os.Stdout.Write(out) // エラー出力 } else { fmt.Printf("%s\n", err) } return nil }
このメソッドは、ビルドされたテストバイナリ(
a.deps[0].pkgbin
)をos/exec
パッケージを使って実行します。実行結果の標準出力とエラー出力をキャプチャし、\nPASS\n
という文字列が含まれているかどうかでテストの成功/失敗を判断し、適切なメッセージを表示します。 -
writeTestmain
関数:func writeTestmain(out string, p *Package) error { t := &testFuncs{ Package: p, Info: p.info, } // ... TestGoFiles と XTestGoFiles をロードし、テスト関数を抽出 ... f, err := os.Create(out) // ... if err := testmainTmpl.Execute(f, t); err != nil { return err } return nil }
この関数は、
_testmain.go
というファイルを生成します。このファイルは、testing
パッケージのMain
関数を呼び出し、検出されたすべてのテスト、ベンチマーク、サンプル関数を登録します。go/parser
とgo/ast
を使ってソースファイルを解析し、isTest
関数でテスト関数を識別します。testmainTmpl
というtext/template
を使って、この_testmain.go
のコードを生成します。
src/cmd/go/testflag.go
このファイルは、go test
コマンドの複雑なフラグパースを処理します。
-
testFlagDefn
:var testFlagDefn = []*testFlagSpec{ // local. {name: "c", isBool: true}, {name: "file", multiOK: true}, {name: "x", isBool: true}, // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. {name: "bench", passToTest: true}, {name: "benchtime", passToTest: true}, // ... {name: "v", isBool: true, passToTest: true}, }
このスライスは、
go test
が認識するすべてのフラグとそのメタデータを定義しています。passToTest: true
が設定されているフラグは、テストバイナリに渡される際に-test.
プレフィックスが付加されます。 -
testFlags
関数:func testFlags(args []string) (passToTest []string) { for i := 0; i < len(args); i++ { arg := args[i] f, value, extraWord := testFlag(args, i) // 個々の引数を解析 if f == nil { // 未知のフラグはそのまま残す args = append(args, arg) continue } switch f.name { // 既知のフラグの処理 case "c": setBoolFlag(&testC, value) case "x": setBoolFlag(&testX, value) case "file": testFiles = append(testFiles, value) } // ... if f.passToTest { // テストバイナリに渡すフラグ passToTest = append(passToTest, "-test."+f.name+"="+value) } } return }
この関数は、コマンドライン引数をループ処理し、
testFlag
関数を使って各引数を解析します。go test
自身が処理するフラグ(-c
,-x
,-file
)は内部変数に設定され、テストバイナリに渡すフラグは-test.
プレフィックスを付けてpassToTest
スライスに格納されます。
このカスタムフラグパースは、go test
が自身の動作を制御するフラグと、生成されるテストバイナリの動作を制御するフラグを、単一のコマンドラインから効率的に分離・処理するために不可欠です。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
go test
コマンドのドキュメント: https://go.dev/cmd/go/#hdr-Test_packagestesting
パッケージのドキュメント: https://pkg.go.dev/testinggo/build
パッケージのドキュメント: https://pkg.go.dev/go/build
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/go
ディレクトリ): https://github.com/golang/go - Go言語のコミット履歴: https://github.com/golang/go/commits/master
- Go言語のIssue Tracker (関連する議論や提案): https://go.dev/issue
- Go言語のメーリングリストアーカイブ (golang-devなど): https://groups.google.com/g/golang-dev
- Go言語の初期のテストツール
gotest
に関する情報 (歴史的経緯を理解するため):- Go issue 298:
go test
command: https://go.dev/issue/298 - Go issue 299:
go test
should build test binaries: https://go.dev/issue/299 - Go issue 2009:
go test
should usego build
for test binaries: https://go.dev/issue/2009 - Go issue 2010:
go test
should usego install
for test binaries: https://go.dev/issue/2010 - これらのIssueは、
go test
コマンドがどのように進化し、gotest
の課題を解決していったかを示す重要な情報源です。
- Go issue 298:
- Go言語のビルドシステムに関する一般的な解説記事やブログポスト。