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

[インデックス 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を呼び出し、さらにgogotestを呼び出し、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/binGOPATH/pkgなどの標準的なインストールパスに配置します。
  • go/buildパッケージ: Goのソースコードツリーの構造を解析し、パッケージの依存関係やソースファイルの情報を取得するための標準ライブラリパッケージです。このパッケージは、goコマンドがビルドやテストの対象となるパッケージを特定するために内部的に利用します。
  • testingパッケージ: Go言語の標準ライブラリに含まれるテストフレームワークです。TestXxxという命名規則に従う関数をテスト関数として認識し、BenchmarkXxxをベンチマーク関数、ExampleXxxをサンプルコードとして扱います。
  • gotest: このコミット以前に存在した、Go言語のテストを実行するための独立したツールです。このコミットによって置き換えられます。
  • Makefile: ソフトウェアのビルドプロセスを自動化するためのツールであるmakeが使用する設定ファイルです。Go言語の初期のビルドシステムでは、Makefileが広く利用されていました。
  • フラグ(コマンドライン引数)のパース: コマンドラインから渡されるオプション(例: -v, -short)を解析し、プログラム内で利用可能な形式に変換する処理です。Goでは標準のflagパッケージが提供されていますが、このコミットのようにカスタムでパース処理を実装することもあります。
  • テストバイナリ: Goのテストを実行する際に生成される実行可能ファイルです。このバイナリは、テスト対象のパッケージのコードと、testingパッケージによって生成されるテストランナーのコードを含んでいます。

技術的詳細

このコミットは、go testコマンドの内部実装を大幅に変更しています。主な技術的変更点は以下の通りです。

  1. go testコマンドの導入とgotestの置き換え:

    • src/cmd/go/test.gocmdTestという新しいCommand構造体が定義され、runTest関数がその実行ロジックとして設定されています。
    • main.goのコマンドディスパッチロジックが更新され、testコマンドが認識されるようになります。
    • これにより、従来のgotestツールが不要となり、Goツールチェインにテスト機能が統合されます。
  2. カスタムフラグパースの導入:

    • 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が独自のフラグパースを行うことを示しています。
  3. テストバイナリのビルドプロセスの再構築:

    • 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アクションが、テストバイナリのビルドと配置に対応するように修正されています。
  4. テスト実行と結果の表示:

    • runTest関数は、ビルドされたテストバイナリを実行し、その標準出力と終了ステータスをキャプチャします。
    • テストバイナリの出力に基づいて、okFAIL?(テストファイルなし)などの結果を整形して表示します。
    • bytes.Equal(out, pass[1:]) || bytes.HasSuffix(out, pass)という条件で、テストが成功したかどうかを判断しています。これは、テストバイナリが\nPASS\nという文字列を出力した場合に成功とみなすことを意味します。
  5. go/astgo/parsergo/docの活用:

    • writeTestmain関数内で、go/parserを使ってテストソースファイルを解析し、go/astを使ってAST(抽象構文木)を走査します。
    • isTest関数は、関数名がTestBenchmarkExampleのいずれかで始まり、その後に小文字が続かない場合にテスト関数と判断するロジックを提供します。
    • go/docパッケージは、Example関数のドキュメントコメントから期待される出力を抽出するために使用されます。

この変更により、Goのテストシステムは、より統合され、自己完結型になり、外部のビルドツールへの依存が軽減されました。

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

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  • src/cmd/go/Makefile:

    • testflag.goGOFILESリストに追加され、新しいフラグパースロジックがビルド対象に含まれるようになります。
  • src/cmd/go/build.go:

    • builder構造体にgorootフィールドが追加されます。
    • action構造体にpkgdir(パッケージの出力ディレクトリ)とignoreFail(依存関係の失敗を無視するかどうか)フィールドが追加されます。
    • actionメソッド内で、pkgdirの初期化ロジックが追加されます。
    • doメソッド内で、依存関係が失敗した場合にignoreFailtrueであれば処理を続行するロジックが追加されます。
    • buildメソッドとinstallメソッドが、新しいpkgdiraction構造体の変更に合わせて調整されます。特に、installはビルドされたactionpkgbinまたはpkgobjを参照するように変更されます。
  • src/cmd/go/main.go:

    • Command構造体にCustomFlagsブール値フィールドが追加されます。これは、コマンドが独自のフラグパースを行うかどうかを示します。
    • main関数内のコマンドディスパッチロジックが変更され、cmd.CustomFlagstrueの場合、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ファイルを生成します。
    • testFuncstestFunc構造体、testFileSettestmainTmpl_testmain.goのテンプレート)が定義されます。
    • loadメソッドがtestFuncsに追加され、ソースファイルを解析してテスト、ベンチマーク、サンプル関数を抽出します。
  • src/cmd/go/testflag.go:

    • この新しいファイルには、go testコマンドのカスタムフラグパースロジックが含まれます。
    • usageMessagetestUsage関数が定義されます。
    • testFlagSpec構造体とtestFlagDefnスライスが定義され、認識されるフラグとその特性(ブール値か、テストバイナリに渡すかなど)を記述します。
    • testFlags関数が実装され、コマンドライン引数を解析し、go test自身のフラグとテストバイナリに渡すフラグを分離します。
    • testFlag関数が、個々の引数が既知のフラグであるかを判断し、その値と特性を返します。
    • setBoolFlag関数が、ブール値フラグの値を設定します。

コアとなるコードの解説

このコミットの核となるのは、src/cmd/go/test.gosrc/cmd/go/testflag.goに実装されたgo testコマンドのロジックです。

src/cmd/go/test.go

このファイルは、go testコマンドの主要な実行フローを定義しています。

  • cmdTestrunTest:

    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 // コマンド実行関数を設定
    }
    

    cmdTestgoコマンドのサブコマンドとして登録され、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/parsergo/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が自身の動作を制御するフラグと、生成されるテストバイナリの動作を制御するフラグを、単一のコマンドラインから効率的に分離・処理するために不可欠です。

関連リンク

参考にした情報源リンク