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

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

このコミットは、Go言語のテストツール go test におけるコードカバレッジ計測時のパッケージ再ビルドの挙動を改善するものです。特に、go test -cover コマンド実行時に、カバレッジ計測のためにソースコードが書き換えられたパッケージに依存する他のパッケージが適切に再ビルドされない問題を解決し、テストの正確性と信頼性を向上させます。これにより、標準ライブラリのテストが go test -short -cover std で正常に実行できるようになりました。

コミット

commit 8b9c1a224be4100b44df9c83aabcf6ec4a8f7832
Author: Russ Cox <rsc@golang.org>
Date:   Wed Jun 26 14:31:12 2013 -0400

    cmd/go: proper rebuild of affected packages during go test
    
    With this CL, go test -short -cover std successfully builds and
    runs all the standard package tests. The tests that look a file
    line numbers (log and runtime/debug) fail, because cover is
    not inserting //line directives. Everything else passes.
    
    ok      cmd/api 0.038s  coverage: 66.6% of statements
    ?       cmd/cgo [no test files]
    ok      cmd/fix 0.043s  coverage: 27.2% of statements
    ok      cmd/go  0.063s  coverage: 2.4% of statements
    ?       cmd/godoc       [no test files]
    ok      cmd/gofmt       0.085s  coverage: 61.3% of statements
    ?       cmd/yacc        [no test files]\n    ok      archive/tar     0.023s  coverage: 74.2% of statements
    ok      archive/zip     0.075s  coverage: 71.8% of statements
    ok      bufio   0.149s  coverage: 88.2% of statements
    ok      bytes   0.135s  coverage: 90.4% of statements
    ok      compress/bzip2  0.087s  coverage: 85.1% of statements
    ok      compress/flate  0.632s  coverage: 79.3% of statements
    ok      compress/gzip   0.027s  coverage: 76.7% of statements
    ok      compress/lzw    0.141s  coverage: 71.2% of statements
    ok      compress/zlib   1.123s  coverage: 77.2% of statements
    ok      container/heap  0.020s  coverage: 85.8% of statements
    ok      container/list  0.021s  coverage: 92.5% of statements
    ok      container/ring  0.030s  coverage: 86.5% of statements
    ?       crypto  [no test files]
    ok      crypto/aes      0.054s  coverage: 54.3% of statements
    ok      crypto/cipher   0.027s  coverage: 68.8% of statements
    ok      crypto/des      0.041s  coverage: 83.8% of statements
    ok      crypto/dsa      0.027s  coverage: 33.1% of statements
    ok      crypto/ecdsa    0.048s  coverage: 48.7% of statements
    ok      crypto/elliptic 0.030s  coverage: 91.6% of statements
    ok      crypto/hmac     0.019s  coverage: 83.3% of statements
    ok      crypto/md5      0.020s  coverage: 78.7% of statements
    ok      crypto/rand     0.057s  coverage: 20.8% of statements
    ok      crypto/rc4      0.092s  coverage: 70.8% of statements
    ok      crypto/rsa      0.261s  coverage: 80.8% of statements
    ok      crypto/sha1     0.019s  coverage: 83.9% of statements
    ok      crypto/sha256   0.021s  coverage: 89.0% of statements
    ok      crypto/sha512   0.023s  coverage: 88.7% of statements
    ok      crypto/subtle   0.027s  coverage: 83.9% of statements
    ok      crypto/tls      0.833s  coverage: 79.7% of statements
    ok      crypto/x509     0.961s  coverage: 74.9% of statements
    ?       crypto/x509/pkix        [no test files]
    ok      database/sql    0.033s  coverage: 75.0% of statements
    ok      database/sql/driver     0.020s  coverage: 46.2% of statements
    ok      debug/dwarf     0.023s  coverage: 71.5% of statements
    ok      debug/elf       0.035s  coverage: 58.2% of statements
    ok      debug/gosym     0.022s  coverage: 1.8% of statements
    ok      debug/macho     0.023s  coverage: 63.7% of statements
    ok      debug/pe        0.024s  coverage: 50.5% of statements
    ok      encoding/ascii85        0.021s  coverage: 89.7% of statements
    ok      encoding/asn1   0.022s  coverage: 77.9% of statements
    ok      encoding/base32 0.022s  coverage: 91.4% of statements
    ok      encoding/base64 0.020s  coverage: 90.7% of statements
    ok      encoding/binary 0.022s  coverage: 66.2% of statements
    ok      encoding/csv    0.022s  coverage: 88.5% of statements
    ok      encoding/gob    0.064s  coverage: 82.2% of statements
    ok      encoding/hex    0.019s  coverage: 86.3% of statements
    ok      encoding/json   0.047s  coverage: 77.3% of statements
    ok      encoding/pem    0.026s  coverage: 80.5% of statements
    ok      encoding/xml    0.039s  coverage: 85.0% of statements
    ok      errors  0.022s  coverage: 100.0% of statements
    ok      expvar  0.048s  coverage: 72.0% of statements
    ok      flag    0.019s  coverage: 86.9% of statements
    ok      fmt     0.062s  coverage: 91.2% of statements
    ok      go/ast  0.028s  coverage: 46.3% of statements
    ok      go/build        0.190s  coverage: 75.4% of statements
    ok      go/doc  0.095s  coverage: 76.7% of statements
    ok      go/format       0.036s  coverage: 79.8% of statements
    ok      go/parser       0.075s  coverage: 82.0% of statements
    ok      go/printer      0.733s  coverage: 88.6% of statements
    ok      go/scanner      0.031s  coverage: 86.5% of statements
    ok      go/token        0.062s  coverage: 79.7% of statements
    ?       hash    [no test files]
    ok      hash/adler32    0.029s  coverage: 49.0% of statements
    ok      hash/crc32      0.020s  coverage: 64.2% of statements
    ok      hash/crc64      0.021s  coverage: 53.5% of statements
    ok      hash/fnv        0.018s  coverage: 75.5% of statements
    ok      html    0.022s  coverage: 4.5% of statements
    ok      html/template   0.087s  coverage: 83.9% of statements
    ok      image   0.108s  coverage: 67.1% of statements
    ok      image/color     0.026s  coverage: 20.1% of statements
    ok      image/draw      0.049s  coverage: 69.6% of statements
    ok      image/gif       0.019s  coverage: 65.2% of statements
    ok      image/jpeg      0.197s  coverage: 78.6% of statements
    ok      image/png       0.055s  coverage: 56.5% of statements
    ok      index/suffixarray       0.027s  coverage: 82.4% of statements
    ok      io      0.037s  coverage: 83.4% of statements
    ok      io/ioutil       0.022s  coverage: 70.1% of statements
    FAIL    log     0.020s
    ok      log/syslog      2.063s  coverage: 71.1% of statements
    ok      math    0.023s  coverage: 76.5% of statements
    ok      math/big        0.235s  coverage: 79.2% of statements
    ok      math/cmplx      0.020s  coverage: 66.5% of statements
    ok      math/rand       0.031s  coverage: 69.9% of statements
    ok      mime    0.022s  coverage: 83.0% of statements
    ok      mime/multipart  0.389s  coverage: 76.1% of statements
    ok      net     2.219s  coverage: 58.0% of statements
    ok      net/http        4.744s  coverage: 82.9% of statements
    ok      net/http/cgi    0.593s  coverage: 68.5% of statements
    ok      net/http/cookiejar      0.038s  coverage: 90.3% of statements
    ok      net/http/fcgi   0.047s  coverage: 37.6% of statements
    ok      net/http/httptest       0.068s  coverage: 68.9% of statements
    ok      net/http/httputil       0.058s  coverage: 52.8% of statements
    ?       net/http/pprof  [no test files]
    ok      net/mail        0.025s  coverage: 80.3% of statements
    ok      net/rpc 0.063s  coverage: 71.5% of statements
    ok      net/rpc/jsonrpc 0.047s  coverage: 81.3% of statements
    ok      net/smtp        0.032s  coverage: 74.1% of statements
    ok      net/textproto   0.023s  coverage: 66.0% of statements
    ok      net/url 0.020s  coverage: 78.2% of statements
    ok      os      4.729s  coverage: 73.3% of statements
    ok      os/exec 39.620s coverage: 65.1% of statements
    ok      os/signal       0.541s  coverage: 89.9% of statements
    ok      os/user 0.022s  coverage: 62.2% of statements
    ok      path    0.018s  coverage: 90.8% of statements
    ok      path/filepath   10.834s coverage: 88.4% of statements
    ok      reflect 0.055s  coverage: 83.2% of statements
    ok      regexp  0.084s  coverage: 75.5% of statements
    ok      regexp/syntax   0.547s  coverage: 85.2% of statements
    ok      runtime 4.755s  coverage: 75.9% of statements
    ?       runtime/cgo     [no test files]
    FAIL    runtime/debug   0.018s
    ok      runtime/pprof   0.368s  coverage: 8.5% of statements
    ?       runtime/race    [no test files]
    ok      sort    0.059s  coverage: 97.7% of statements
    ok      strconv 0.315s  coverage: 95.6% of statements
    ok      strings 0.147s  coverage: 96.1% of statements
    ok      sync    0.083s  coverage: 56.7% of statements
    ok      sync/atomic     0.035s  coverage: 0.0% of statements
    ok      syscall 0.043s  coverage: 24.0% of statements
    ok      testing 0.018s  coverage: 24.0% of statements
    ?       testing/iotest  [no test files]
    ok      testing/quick   0.062s  coverage: 83.2% of statements
    ok      text/scanner    0.020s  coverage: 91.5% of statements
    ok      text/tabwriter  0.021s  coverage: 90.4% of statements
    ok      text/template   0.052s  coverage: 81.1% of statements
    ok      text/template/parse     0.024s  coverage: 86.1% of statements
    ok      time    2.431s  coverage: 88.8% of statements
    ok      unicode 0.024s  coverage: 92.1% of statements
    ok      unicode/utf16   0.017s  coverage: 97.3% of statements
    ok      unicode/utf8    0.019s  coverage: 97.4% of statements
    ?       unsafe  [no test files]
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/10586043

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

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

元コミット内容

cmd/go: proper rebuild of affected packages during go test

With this CL, go test -short -cover std successfully builds and
runs all the standard package tests. The tests that look a file
line numbers (log and runtime/debug) fail, because cover is
not inserting //line directives. Everything else passes.

変更の背景

Go言語の go test コマンドは、パッケージのテストを実行するための主要なツールです。これに -cover フラグを付けることで、コードカバレッジを計測できます。コードカバレッジを計測するために、go test -cover はテスト対象のGoソースファイルを一時的に書き換えます。具体的には、各ステートメントの実行を記録するための計測コード(プロファイリングフック)を挿入します。

このコミットが導入される以前は、go test -cover を実行した際に、カバレッジ計測のために書き換えられたパッケージに依存する他のパッケージが、その変更を認識して適切に再ビルドされないという問題がありました。Goのビルドシステムは、依存関係が変更された場合にのみ再ビルドを行うように最適化されていますが、カバレッジ計測によるソースコードの内部的な変更が、ビルドシステムに依存関係の変更として適切に伝わらないケースがあったと考えられます。

この問題は、特に標準ライブラリのような相互に複雑な依存関係を持つ大規模なコードベースで顕著でした。コミットメッセージにあるように、go test -short -cover std(標準ライブラリのテストをカバレッジ計測付きで短時間実行する)が正常に動作しないという具体的な問題が発生していました。これは、カバレッジ計測が有効な状態でテストを実行すると、ビルドエラーや、誤ったカバレッジ結果、あるいはテストの失敗につながる可能性があったことを示唆しています。

このコミットの目的は、go test -cover が実行された際に、カバレッジ計測のために変更されたパッケージだけでなく、そのパッケージに推移的に依存するすべてのパッケージが、テスト環境内で正しく再ビルドされるように go コマンドのビルドロジックを修正することでした。これにより、カバレッジ計測の正確性と、テスト実行の信頼性が向上します。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とツールに関する知識が必要です。

  1. Goパッケージと依存関係:

    • Goのコードは「パッケージ」という単位で整理されます。各パッケージは、import ステートメントを通じて他のパッケージに依存することができます。
    • Goのビルドシステムは、これらの依存関係を解決し、必要なパッケージを適切な順序でビルドします。
    • パッケージは、そのソースコードが置かれているディレクトリによって識別されます(例: fmt, net/http)。
  2. go test コマンド:

    • go test は、Goパッケージのテストを実行するためのコマンドです。
    • テストファイルは、_test.go というサフィックスを持ち、テスト対象のパッケージと同じディレクトリに配置されます。
    • go test は、テスト対象のパッケージとテストファイルをコンパイルし、テストバイナリを生成して実行します。
    • 内部テストと外部テスト:
      • 内部テスト: テスト対象のパッケージと同じパッケージ名を持つテストファイル(例: package mypackage)。これらのテストは、テスト対象パッケージの内部要素(非エクスポートされた関数や変数)にアクセスできます。
      • 外部テスト: テスト対象のパッケージ名に _test サフィックスを付けたパッケージ名を持つテストファイル(例: package mypackage_test)。これらのテストは、テスト対象パッケージを外部からインポートする形でテストし、エクスポートされた要素のみにアクセスできます。これは、ユーザーがパッケージをインポートして使用する際の挙動をシミュレートするのに役立ちます。
  3. コードカバレッジ (go test -cover):

    • -cover フラグは、go test にコードカバレッジ計測を有効にするよう指示します。
    • カバレッジ計測では、Goツールチェーンがテスト対象のソースコードを一時的に書き換え、各ステートメントが実行されたかどうかを追跡するコードを挿入します。このプロセスは「インストルメンテーション」と呼ばれます。
    • インストルメンテーションされたコードは、実行時にカバレッジデータを生成し、テスト終了後にそのデータがレポートされます。
    • このインストルメンテーションは、ソースコードの抽象構文木(AST)レベルで行われ、元のソースコードとは異なるバイナリが生成されます。
  4. Goのビルドキャッシュと再ビルドの仕組み:

    • Goのビルドシステムは、ビルド時間を短縮するためにビルドキャッシュを利用します。
    • パッケージの依存関係やソースコードが変更されていない場合、Goは以前のビルド結果を再利用します。
    • しかし、ソースコードが変更された場合、Goは影響を受けるパッケージとその依存パッケージを再ビルドする必要があります。この再ビルドのトリガーが適切に機能しないと、古いバージョンの依存パッケージが使用されたり、ビルドエラーが発生したりする可能性があります。
  5. //line ディレクティブ:

    • //line ディレクティブは、Goコンパイラに対して、その後のコードの行番号とファイル名を変更するよう指示する特殊なコメントです。
    • コード生成ツール(例えば、go generate や、このケースでのカバレッジツール)が元のソースコードを変換する際に、デバッグ情報やエラーメッセージが元のソースコードの正確な位置を指すようにするために使用されます。
    • コミットメッセージで「logruntime/debug のテストが失敗するのは、cover//line ディレクティブを挿入していないため」とあるのは、カバレッジツールがソースコードを書き換えた際に、元の行番号情報を保持するための //line ディレクティブを適切に挿入していなかったため、行番号に依存するテストが誤動作したことを示しています。このコミットの直接の修正範囲外ですが、カバレッジツールの課題の一つとして言及されています。

技術的詳細

このコミットの核心は、go test -cover が有効な場合に、テスト対象パッケージのソースコードがインストルメンテーションによって変更された際に、その変更が依存パッケージに適切に伝播し、再ビルドがトリガーされるようにすることです。

以前の go test の実装では、カバレッジ計測のためにパッケージ P のソースコードが書き換えられた場合、P を直接インポートするテストバイナリは新しい P を使用するようにビルドされました。しかし、P をインポートするパッケージ Q があり、さらに Q をインポートするパッケージ R があるような推移的な依存関係の場合、RQ をビルドする際に、Q が古いバージョンの P に依存していると見なしてしまう可能性がありました。これは、Goのビルドシステムが、パッケージの変更を検出するために、主にエクスポートされたメタデータ(APIシグネチャなど)やファイルハッシュを使用するためです。カバレッジ計測による内部的な変更は、必ずしもこれらの検出メカニズムによって「変更」として認識されるとは限りませんでした。

このコミットは、この問題を解決するために src/cmd/go/test.gorecompileForTest という新しい関数を導入しました。

recompileForTest 関数の主なロジックは以下の通りです。

  1. パッケージのクローンと再マッピング:

    • recompileForTest は、テスト対象のパッケージ preal(元のパッケージ)と、カバレッジ計測のためにインストルメンテーションされた ptest(テスト用のパッケージ)を受け取ります。
    • pmain(テストメインパッケージ)と pxtest(外部テストパッケージ、もしあれば)の依存関係グラフを走査します。
    • preal に推移的に依存するすべてのパッケージ p について、そのパッケージの「クローン」を作成します。このクローンは、元のパッケージの情報をコピーしつつ、いくつかのビルド関連のプロパティを変更します。
    • m マップを使用して、元のパッケージからクローンされたパッケージへのマッピングを保持し、重複してクローンを作成しないようにします。
  2. ビルドプロパティの書き換え:

    • クローンされたパッケージ p1pkgdir を、テスト用の作業ディレクトリ (testDir) に設定します。これにより、これらのパッケージが通常のGoモジュールキャッシュではなく、テスト専用のディレクトリにビルドされるようになります。
    • target を空に設定し、faketrue に、forceLibrarytrue に設定します。これらは、Goのビルドシステムに対して、これらのパッケージが一時的なテスト目的でビルドされるものであり、ライブラリとして扱われるべきであることを示唆します。
    • Stale フラグを true に設定します。これは、Goのビルドシステムに対して、このパッケージが「古くなっている」と見なし、強制的に再ビルドする必要があることを明示的に伝えます。
    • 最も重要なのは、クローンされたパッケージの imports リストを再帰的に走査し、依存するパッケージも必要に応じてクローンされたバージョンに置き換えることです。これにより、preal に依存する Q が、ptest に依存するように変更され、さらに Q に依存する R も、変更された Q に依存するように連鎖的に変更されます。
  3. 強制的な再ビルド:

    • recompileForTest は、pmainpxtest(もしあれば)に対して rewrite 関数を適用します。これにより、テスト実行に必要なすべてのパッケージが、カバレッジ計測のために変更されたパッケージの正しいバージョンに依存するように、その依存関係グラフが「書き換え」られます。
    • この書き換えと Stale = true の設定により、Goのビルドシステムは、これらのパッケージが変更されたと認識し、テスト実行のために必要なすべての依存パッケージを、カバレッジ計測が適用された状態で再ビルドします。

このアプローチにより、カバレッジ計測が有効な場合でも、Goのビルドシステムが依存関係の変更を正確に検出し、必要なすべてのパッケージを再ビルドするようになります。これにより、go test -cover がより堅牢になり、複雑な依存関係を持つプロジェクトでも正確なカバレッジ結果が得られるようになりました。

src/cmd/go/build.go の変更は、builder.action メソッドにおける objdirobjpkg の計算ロジックを微調整するものです。p.pkgdir が設定されている場合はそれを使用し、そうでない場合は b.work を使用するように変更されています。これは、recompileForTestpkgdir がテスト用の作業ディレクトリに設定されるようになったことと関連しており、ビルドアーティファクトの出力先をより柔軟に制御できるようにするための変更と考えられます。

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

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

  1. src/cmd/go/build.go:

    • func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action メソッド内で、a.objdira.objpkg の計算ロジックが変更されました。
    • 変更前:
      a.objdir = filepath.Join(b.work, a.p.ImportPath, "_obj") + string(filepath.Separator)
      a.objpkg = buildToolchain.pkgpath(b.work, a.p)
      
    • 変更後:
      work := p.pkgdir
      if work == "" {
          work = b.work
      }
      a.objdir = filepath.Join(work, a.p.ImportPath, "_obj") + string(filepath.Separator)
      a.objpkg = buildToolchain.pkgpath(work, a.p)
      
    • この変更により、objdirobjpkg のパスを決定する際に、パッケージの pkgdir フィールドが優先的に使用されるようになりました。pkgdir が空の場合は、従来の b.work がフォールバックとして使用されます。
  2. src/cmd/go/test.go:

    • func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, err error) メソッド内で、複数の変更が行われました。
    • 削除されたコードブロック:
      • 以前は、カバレッジモードでパッケージ p のソースが書き換えられた場合に、p をインポートする他のパッケージが p に推移的に依存していると、カバレッジ分析が不可能であるというエラーを返すロジックがありました。このブロックが削除されました。
      // In coverage mode, we rewrite the package p's sources.
      // All code that imports p must be rebuilt with the updated
      // copy, or else coverage will at the least be incomplete
      // (and sometimes we get link errors due to the mismatch as well).
      // The external test itself imports package p, of course, but
      // we make sure that sees the new p. Any other code in the test
      // - that is, any code imported by the external test that in turn
      // imports p - needs to be rebuilt too. For now, just report
      // that coverage is unavailable.
      if testCover && contains(p1.Deps, p.ImportPath) {
          return nil, nil, nil, fmt.Errorf("coverage analysis cannot handle package (%s_test imports %s imports %s)", p.Name, path, p.ImportPath)
      }
      
    • testCoverdeclareCoverVars の適用箇所の変更:
      • testCoverdeclareCoverVars のロジックが、元のパッケージ p ではなく、テストパッケージ ptest に適用されるように移動されました。
    • recompileForTest 関数の追加と呼び出し:
      • func recompileForTest(pmain, preal, ptest, pxtest *Package, testDir string) という新しい関数が追加されました。
      • この関数は、test メソッド内で ptest != p && testCover の条件が満たされる場合に呼び出されます。これは、テスト対象のパッケージがカバレッジ計測のために変更され、かつカバレッジが有効な場合に、依存パッケージの再ビルドが必要であることを意味します。
      • recompileForTest の内部では、clonerewrite という2つのヘルパー関数が定義されています。
        • clone は、指定されたパッケージのコピーを作成し、そのコピーを m マップに保存します。
        • rewrite は、パッケージの pkgdirtestDir に設定し、target を空、faketrueforceLibrarytrueStaletrue に設定します。また、そのパッケージがインポートする他のパッケージに対しても再帰的に clone を呼び出します。
      • recompileForTest は、pmainpxtest(もしあれば)に対して rewrite を呼び出すことで、テスト環境内の依存関係グラフを再構築し、必要なパッケージが強制的に再ビルドされるようにします。

コアとなるコードの解説

このコミットの主要な変更は、go test -cover が有効な場合のGoのビルドシステムの挙動を根本的に改善することにあります。

  1. src/cmd/go/build.go の変更:

    • objdirobjpkg は、Goのビルドプロセスにおいて、コンパイルされたオブジェクトファイルやパッケージアーカイブが一時的に保存されるディレクトリと、そのパッケージの内部的なパスを決定するために使用されます。
    • work := p.pkgdir; if work == "" { work = b.work } という変更は、パッケージ ppkgdir が設定されている場合(これは通常、テストのために一時的に作成されたパッケージの場合に設定されます)は、その pkgdir を優先的に使用することを意味します。
    • これにより、recompileForTest 関数によってテスト専用の作業ディレクトリに再マッピングされたパッケージが、その新しい場所でビルドされるようになります。これは、通常のビルドキャッシュとは分離された、テスト固有のビルド環境を確立するために重要です。
  2. src/cmd/go/test.go の変更:

    • 旧カバレッジ分析の制限の削除: 以前のコードブロックは、カバレッジ計測が推移的な依存関係を持つパッケージで正しく機能しないという既知の制限を回避するためのものでした。このコミットがその問題を解決したため、この制限は不要となり、関連するエラーチェックが削除されました。これは、新しい recompileForTest ロジックがこの問題を解決したことの直接的な証拠です。
    • testCoverdeclareCoverVars の適用箇所の移動:
      • testCover はカバレッジ計測が有効かどうかを示すフラグです。
      • declareCoverVars は、カバレッジ計測のためにソースコードに挿入される変数(どのステートメントが実行されたかを記録するためのもの)を宣言する処理です。
      • これらのロジックが、元のパッケージ p ではなく、テストパッケージ ptest に適用されるように変更されたのは、カバレッジ計測のインストルメンテーションが、テスト実行のために特別に用意された ptest のインスタンスに対して行われるべきであることを明確にするためです。これにより、元のパッケージのビルドには影響を与えず、テスト実行時のみカバレッジ計測が適用されるようになります。
    • recompileForTest 関数の導入:
      • これがこのコミットの最も重要な部分です。この関数は、go test -cover が有効な場合に、テスト対象のパッケージ preal がインストルメンテーションによって変更された際に、その変更が依存関係グラフ全体に適切に伝播するようにします。
      • clone 関数は、パッケージのディープコピーを作成し、そのコピーを testDir(テスト専用の作業ディレクトリ)にマッピングします。これにより、元のパッケージとは独立した、カバレッジ計測済みのパッケージのバージョンが作成されます。
      • rewrite 関数は、クローンされたパッケージのビルド関連のメタデータ(pkgdir, target, fake, forceLibrary, Stale)を更新します。
        • pkgdir = testDir: パッケージのビルド出力先をテスト専用ディレクトリに強制します。
        • target = "": ビルドターゲットをリセットします。
        • fake = true: このパッケージが一時的な目的で作成された「偽の」パッケージであることを示します。
        • forceLibrary = true: このパッケージが実行可能ファイルではなく、ライブラリとしてビルドされるべきであることを強制します。
        • Stale = true: これが最も重要で、Goのビルドシステムに対して、このパッケージが「古くなっている」と見なし、強制的に再ビルドする必要があることを明示的に伝えます。これにより、依存関係グラフ内のすべての関連パッケージが、カバレッジ計測済みのバージョンにリンクされるようになります。
      • recompileForTest は、pmain(テストのメインパッケージ)と pxtest(外部テストパッケージ)の依存関係を再帰的に走査し、preal に推移的に依存するすべてのパッケージをクローンし、その依存関係を ptest(カバレッジ計測済みの preal のバージョン)にリダイレクトします。これにより、テスト実行時にすべてのコードがカバレッジ計測が有効な状態でビルドされ、実行されることが保証されます。

この一連の変更により、go test -cover は、複雑な依存関係を持つGoプロジェクトにおいても、より正確で信頼性の高いコードカバレッジ計測を提供できるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード(特に src/cmd/go/ ディレクトリ)
  • Go言語のコミット履歴とコードレビューコメント
  • Go Code Coverageのブログ記事
  • Goのビルドシステムに関する一般的な知識