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

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

このコミットは、Go言語のツールチェイン、特にgo test -coverコマンドにおけるcgoコードのテストカバレッジに関するものです。具体的には、cgoを利用したGoパッケージのコードカバレッジが正しく機能するかどうかを検証するための新しいテストケースを追加しています。このテストケースは、以前の変更(CL 34680044)がcgoを含むプロジェクトのカバレッジ測定に与える影響を評価するために導入されました。

コミット

  • コミットハッシュ: 9abcd53eda0629d009941027ffa2dc680b5f1d00
  • Author: Russ Cox rsc@golang.org
  • Date: Thu Mar 6 18:36:32 2014 -0500

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

https://github.com/golang/go/commit/9abcd53eda0629d009941027ffa2dc680b5f1d00

元コミット内容

cmd/go: add test case for cgo coverage

This is a test case for CL 34680044.

Fixes #6333.

LGTM=bradfitz
R=golang-codereviews, bradfitz, minux.ma
CC=golang-codereviews
https://golang.org/cl/71230049

変更の背景

このコミットの主な背景は、Go言語のgo test -coverコマンドがcgoで記述されたコードに対して、期待通りにカバレッジを測定できるかを検証することにあります。コミットメッセージには「This is a test case for CL 34680044. Fixes #6333.」と記載されています。

CL 34680044は、おそらくcgoを含むGoパッケージのコードカバレッジ測定に関する修正や改善を導入した変更リスト(Change List)を指しています。このコミットは、その変更が意図した通りに機能するかを確認するためのテストを追加するものです。

Fixes #6333は、GoのIssueトラッカーにおける問題番号を示していますが、現在の検索ではこの特定のIssueに関する詳細な情報は見つかりませんでした。しかし、文脈から判断すると、このIssueはcgoとコードカバレッジの連携における何らかの問題を報告していた可能性が高いです。GoのコードカバレッジツールはGoコードの計測に特化しており、cgoを介して呼び出されるCコードの直接的なカバレッジ測定は、Goの標準ツールではサポートされていません。そのため、このテストケースは、cgoが関与するGoコード部分のカバレッジが正しく報告されることを確認することを目的としていると考えられます。

前提知識の解説

Go言語のコードカバレッジ

Go言語には、go test -coverコマンドを通じてコードカバレッジを測定する機能が組み込まれています。これは、テスト実行中にコードのどの部分が実行されたかを追跡し、その割合を報告するものです。開発者はこの情報を用いて、テストがコードベースのどの程度をカバーしているかを把握し、テストの網羅性を向上させることができます。

  • go test -cover: テストを実行し、カバレッジの概要(パーセンテージ)を出力します。
  • go test -coverprofile=coverage.out: 詳細なカバレッジプロファイルをファイルに保存します。
  • go tool cover -html=coverage.out: 保存されたカバレッジプロファイルから、ブラウザで閲覧可能なHTMLレポートを生成します。これにより、コードのどの行がカバーされているか、されていないかを視覚的に確認できます。

cgo

cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。これにより、既存のCライブラリをGoプロジェクトで利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。cgoを使用するGoファイルでは、特別なimport "C"ステートメントと、Cコードを記述するためのコメントブロックを使用します。

cgoの仕組みは、GoコンパイラがGoコードとCコードを連携させるための接着コードを生成することに基づいています。Goのビルドプロセス中に、cgoはCコードをコンパイルし、Goコードから呼び出せるようにリンクします。

cgoにおけるコードカバレッジの課題

Goの標準的なコードカバレッジツールは、Go言語で書かれたコードの計測に特化しています。そのため、cgoを介して呼び出されるC言語のコードについては、Goのカバレッジツールでは直接カバレッジを測定できません。Cコードのカバレッジを測定するには、gcovのようなC言語向けの専用ツールを使用し、Cコードを適切なフラグ(例: GCCの-fprofile-arcs -ftest-coverage)でコンパイルし、別途カバレッジデータを収集・分析する必要があります。

このコミットの文脈では、cgoを使用しているGoパッケージ全体のカバレッジが、Goのツールによって正しく計算されるかどうかが焦点となっています。特に、cgoの存在がGoコードのカバレッジ測定に悪影響を与えないことを確認することが重要です。

技術的詳細

このコミットは、go test -coverコマンドがcgoを含むGoパッケージに対して正しく機能するかを検証するための新しいテストケースを追加します。

  1. テストスクリプトの変更 (src/cmd/go/test.bash):

    • TEST coverage with cgoという新しいテストセクションが追加されています。
    • 一時ディレクトリを作成し、その中で./testgo test -short -cover ./testdata/cgocoverコマンドを実行します。これは、cgocoverというテストデータディレクトリ内のパッケージに対して、ショートモードでカバレッジ測定を行いながらテストを実行することを意味します。
    • テストの出力は一時ファイルにリダイレクトされます。
    • grep 'coverage: 0.0%' $d/cgo.outコマンドを使用して、出力に「coverage: 0.0%」という文字列が含まれていないかを確認します。もし含まれていれば、それはcgoパッケージのカバレッジが正しく測定されていないことを意味し、テストは失敗します。これは、cgoを含むパッケージでも0%ではないカバレッジが報告されることを期待しているためです。
  2. テスト対象のGoパッケージ (src/cmd/go/testdata/cgocover/p.go):

    • pという名前の新しいGoパッケージが作成されています。
    • このパッケージには、cgoを使用してC言語の関数fを呼び出すGo関数F()が含まれています。
    • C言語の関数fは、Goのコメントブロック内に定義されています。
    • Go関数F()内には、bというブール変数に基づいた条件分岐と無限ループ(for {})が含まれています。これは、カバレッジ測定の対象となるGoコードのパスを提供するためです。C.f()の呼び出しも含まれており、cgoの利用を示しています。
  3. テストファイル (src/cmd/go/testdata/cgocover/p_test.go):

    • pパッケージのテストファイルが作成されています。
    • TestF()というテスト関数が定義されており、これは単にp.F()関数を呼び出すだけです。これにより、p.F()内のGoコードが実行され、カバレッジデータが収集されるようになります。

この一連の変更により、go test -covercgoを含むGoパッケージに対して、少なくとも0%ではないカバレッジを報告することを確認する自動テストが追加されました。これは、cgoの存在がGoコードのカバレッジ測定を妨げないことを保証するための重要なステップです。

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

src/cmd/go/test.bash

--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -568,6 +568,16 @@ TEST coverage runs
 ./testgo test -short -coverpkg=strings strings regexp || ok=false
 ./testgo test -short -cover strings math regexp || ok=false
 
+TEST coverage with cgo
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+./testgo test -short -cover ./testdata/cgocover >$d/cgo.out 2>&1 || ok=false
+cat $d/cgo.out
+if grep 'coverage: 0.0%' $d/cgo.out >/dev/null; then 
+	ok=false
+	echo no coverage for cgo package
+	ok=false
+fi
+
 TEST cgo depends on syscall
 rm -rf $GOROOT/pkg/*_race
 d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)

src/cmd/go/testdata/cgocover/p.go

--- /dev/null
+++ b/src/cmd/go/testdata/cgocover/p.go
@@ -0,0 +1,19 @@
+package p
+
+/*
+void
+f(void)
+{
+}
+*/
+import "C"
+
+var b bool
+
+func F() {
+	if b {
+		for {
+		}
+	}
+	C.f()
+}

src/cmd/go/testdata/cgocover/p_test.go

--- /dev/null
+++ b/src/cmd/go/testdata/cgocover/p_test.go
@@ -0,0 +1,7 @@
+package p
+
+import "testing"
+
+func TestF(t *testing.T) {
+	F()
+}

コアとなるコードの解説

src/cmd/go/test.bashの変更

このシェルスクリプトの変更は、cgoを含むGoパッケージのテストカバレッジを検証するための新しいテストブロックを追加しています。

  • d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX): 一時ディレクトリを作成し、そのパスをd変数に格納します。これはテストの実行中に生成されるファイル(この場合はcgo.out)を格納するために使用されます。
  • ./testgo test -short -cover ./testdata/cgocover >$d/cgo.out 2>&1 || ok=false:
    • ./testgo: Goツールチェインのテスト実行バイナリです。
    • test -short -cover: テストをショートモードで実行し、コードカバレッジを有効にします。
    • ./testdata/cgocover: テスト対象のパッケージが格納されているディレクトリを指定します。
    • >$d/cgo.out 2>&1: 標準出力と標準エラー出力を$d/cgo.outファイルにリダイレクトします。
    • || ok=false: コマンドが失敗した場合(終了コードが0以外の場合)、ok変数をfalseに設定します。これは、テストスクリプト全体の成功/失敗を追跡するための一般的なパターンです。
  • cat $d/cgo.out: 生成されたカバレッジレポートの内容を標準出力に表示します。これはデバッグ目的で役立ちます。
  • if grep 'coverage: 0.0%' $d/cgo.out >/dev/null; then ... fi:
    • grep 'coverage: 0.0%' $d/cgo.out: cgo.outファイル内に「coverage: 0.0%」という文字列が含まれているかを検索します。
    • >/dev/null: grepの出力を破棄します。grepの終了コードのみを利用します(マッチすれば0、しなければ1)。
    • ok=false; echo no coverage for cgo package; ok=false: もし「coverage: 0.0%」が見つかった場合、okfalseに設定し、エラーメッセージを出力します。これは、cgoを含むパッケージでもカバレッジが0%ではないことを期待しているためです。

このスクリプトは、cgoパッケージのカバレッジが正しく測定され、0%ではない値が報告されることを保証するための基本的な健全性チェックとして機能します。

src/cmd/go/testdata/cgocover/p.goの新規追加

このファイルは、cgoを使用するGoパッケージの最小限の例です。

  • package p: pという名前のパッケージを定義します。
  • /* ... */ import "C": cgoの構文です。このコメントブロック内にC言語のコードを記述し、import "C"によってGoコードからそのCコードを呼び出せるようになります。ここでは、引数も戻り値もないシンプルなC関数f()が定義されています。
  • var b bool: カバレッジ測定の対象となるGoコードのパスを作成するために使用されるブール変数です。
  • func F() { ... }:
    • if b { for {} }: この条件分岐は、カバレッジツールがGoコードの実行パスを追跡できることを確認するために含まれています。btrueの場合、無限ループに入ります。実際のテストではbfalseであるため、このブロックは実行されず、カバレッジレポートで未カバーとしてマークされる可能性があります。
    • C.f(): cgoを介してC言語の関数f()を呼び出します。これが、このパッケージがcgoを使用していることを示す主要な部分です。

このファイルは、cgoとGoコードが混在する状況で、GoのカバレッジツールがGoコード部分を正しく計測できるかどうかのテスト対象となります。

src/cmd/go/testdata/cgocover/p_test.goの新規追加

このファイルは、p.goパッケージのテストファイルです。

  • package p: pパッケージのテストであることを示します。
  • import "testing": Goの標準テストパッケージをインポートします。
  • func TestF(t *testing.T) { F() }:
    • TestF: testingパッケージの規約に従ったテスト関数です。
    • F(): テスト対象のp.F()関数を呼び出します。この呼び出しにより、p.F()内のGoコードが実行され、カバレッジデータが収集されます。

このシンプルなテストは、p.F()関数が実行されることを保証し、その結果としてp.go内のGoコード(if b { ... }C.f()の呼び出しを含む)のカバレッジが測定されるようにします。

関連リンク

参考にした情報源リンク