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

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

このコミットは、Go言語のテストスイートにおけるテストファイルの実行指示方法を標準化するものです。具体的には、各テストファイルの先頭に記述されていた複雑なシェルコマンド形式の実行指示(例: // $G $F.go && $L $F.$A && ./$A.out)を、より簡潔で抽象的なディレクティブ(例: // run// errorcheck// compile// build)に置き換える変更を、最初の100ファイルに対して適用しています。これにより、テストインフラストラクチャの保守性と可読性が向上します。

コミット

commit 0b477ef17e184117922428a5a5ef15ffab12590a
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 16 23:48:57 2012 -0500

    test: use testlib (first 100)
    
    X ,s;^// $G ($D/)?$F.go *$;// compile;g
    X ,s;^// $G ($D/)?$F.go && $L $F.$A *$;// build;g
    X ,s;^// $G ($D/)?$F.go && $L $F.$A && ./$A.out *$;// run;g
    X ,s;^// errchk $G( -e)? ($D/)?$F.go *$;// errorcheck;g
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/5656082

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

https://github.com/golang/go/commit/0b477ef17e184117922428a5a5ef15ffab12590a

元コミット内容

このコミットの元のメッセージは以下の通りです。

test: use testlib (first 100)

X ,s;^// $G ($D/)?$F.go *$;// compile;g
X ,s;^// $G ($D/)?$F.go && $L $F.$A *$;// build;g
X ,s;^// $G ($D/)?$F.go && $L $F.$A && ./$A.out *$;// run;g
X ,s;^// errchk $G( -e)? ($D/)?$F.go *$;// errorcheck;g

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5656082

このメッセージは、変更内容を簡潔に示しています。「X ,s;...」の行は、sedコマンドのような置換パターンを示しており、古いテスト実行指示コメントを新しいディレクティブに置き換える意図を表現しています。

変更の背景

Go言語の初期のテストフレームワークでは、各テストファイルの先頭に、そのテストを実行するための具体的なシェルコマンドがコメントとして記述されていました。例えば、// $G $F.go && $L $F.$A && ./$A.out は、「Goコンパイラ($G)で現在のファイル($F.go)をコンパイルし、リンカ($L)で実行可能ファイル($F.$A)を生成し、その実行可能ファイル(./$A.out)を実行する」という一連の操作を指示していました。

この方式にはいくつかの課題がありました。

  1. 複雑性: テストの実行方法がシェルコマンドとして直接記述されているため、理解しにくく、新しいテストを追加する際に記述ミスが発生しやすい。
  2. 保守性: Goツールチェインのコマンドやオプションが変更された場合、多数のテストファイルを一括で更新する必要があり、メンテナンスコストが高い。
  3. 抽象度の低さ: テストの「目的」(コンパイルエラーをチェックするのか、実行結果を検証するのかなど)が、具体的な実行コマンドの羅列から読み取りにくかった。

これらの課題を解決するため、Goプロジェクトではテストインフラストラクチャの改善が進められました。その一環として、testlib と呼ばれる内部ライブラリやメカニズムが導入され、テストの実行指示をより抽象的なディレクティブとして記述できるようになりました。このコミットは、その新しいtestlibの利用を既存のテストファイルに適用する初期段階(最初の100ファイル)のものです。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびテストに関する基本的な知識が必要です。

  1. Go言語のビルドプロセス:

    • go build: Goソースコードをコンパイルして実行可能ファイルを生成するコマンド。
    • go run: Goソースコードをコンパイルし、すぐに実行するコマンド。
    • go test: Goのテストを実行するためのコマンド。
    • Goのコンパイラ(gc)やリンカ(ld)は、内部的にgo buildgo runによって呼び出されます。古いテスト指示コメントの$GはGoコンパイラ、$Lはリンカを指していました。$Fはファイル名、$Dはディレクトリ、$Aはアーキテクチャ(または実行可能ファイル名の一部)などの変数を表していました。
  2. Goのテストフレームワーク:

    • Goには標準でtestingパッケージが提供されており、go testコマンドを通じてテストを実行します。
    • Goのテストファイルは通常、_test.goというサフィックスを持ちます。
    • Goのテストスイートには、単体テストだけでなく、コンパイルエラーをチェックするテストや、特定のランタイム動作を検証するテストなど、様々な種類のテストが含まれています。
  3. testlib:

    • testlibはGoプロジェクト内部で使用されるテストヘルパーライブラリまたはメカニズムの総称です。これは、Goの標準ライブラリやツールチェインのテストを効率的かつ堅牢に実行するために開発されました。
    • testlibは、テストファイルの先頭に記述された特定のコメント行(ディレクティブ)を解釈し、それに基づいて適切なGoツールチェインコマンド(go build, go run, go tool compileなど)を内部的に実行します。
    • このコミットで導入されたディレクティブは以下の通りです。
      • // run: テストファイルをコンパイルして実行し、その出力や終了コードを検証します。
      • // errorcheck: テストファイルをコンパイルし、特定のコンパイルエラーが発生するかどうかを検証します。
      • // compile: テストファイルをコンパイルできるかどうかを検証します(実行はしません)。
      • // build: テストファイルをビルドできるかどうかを検証します(実行はしません)。

技術的詳細

このコミットの技術的な核心は、Goのテストインフラストラクチャが、テストファイルの実行方法を記述する際の「言語」を、具体的なシェルコマンドから抽象的なディレクティブへと移行した点にあります。

以前の方式では、テストランナー(Goのテストを実行する内部スクリプトやツール)は、各テストファイルの先頭コメントをシェルコマンドとして直接解釈し、実行していました。これは柔軟性がある一方で、シェルスクリプトの複雑性やプラットフォーム依存性、そして前述の保守性の問題を引き起こしていました。

新しいtestlibベースの方式では、テストランナーはテストファイルの先頭コメントを特定のキーワード(run, errorcheck, compile, buildなど)として認識します。これらのキーワードは、テストの「意図」を明確に示します。テストランナーは、これらのディレクティブを読み取ると、内部的に定義されたロジックに基づいて、適切なGoツールチェインコマンドを構築し、実行します。

例えば、// runディレクティブが検出された場合、テストランナーは内部でgo run <filename>に相当する処理を実行し、その結果を評価します。// errorcheckディレクティブの場合は、go tool compile -e <filename>のようなコマンドを実行し、コンパイラからのエラー出力を解析して、期待されるエラーメッセージが含まれているかを確認します。

この抽象化により、以下の技術的メリットがもたらされます。

  • 堅牢性: シェルコマンドの構文解析やエスケープ処理の複雑さがなくなり、テスト実行のロジックがGoコード内で一元的に管理されるため、より堅牢になります。
  • プラットフォーム非依存性: シェルコマンドの差異を吸収し、異なるオペレーティングシステム上でも一貫したテスト実行が可能になります。
  • 拡張性: 新しいテストシナリオや検証方法が必要になった場合、新しいディレクティブを追加し、testlib内でその処理ロジックを実装するだけで済みます。既存の全テストファイルを変更する必要がありません。
  • 可読性: テストファイルの先頭を見るだけで、そのテストが何を目的としているのか(実行、エラーチェック、コンパイルのみなど)が直感的に理解できるようになります。

このコミットは、Goのテストスイート全体をこの新しい方式に移行する大規模な作業の第一歩であり、テストインフラストラクチャの近代化と効率化に向けた重要なマイルストーンです。

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

このコミットでは、Goのテストスイート内の多数のGoソースファイル(99ファイル)が変更されています。各ファイルの変更は非常にシンプルで、ファイルの先頭にあるコメント行が1行削除され、新しいコメント行が1行追加されています。

具体的な変更パターンは以下の通りです。

  1. // $G $F.go && $L $F.$A && ./$A.out// run に変更

    • 例: test/235.go, test/append.go, test/bench/garbage/peano.go など多数
    • これは、Goファイルをコンパイルして実行し、その結果を検証するテストに適用されます。
  2. // errchk $G -e $D/$F.go または // errchk $G $D/$F.go// errorcheck に変更

    • 例: test/alias.go, test/assign.go, test/chan/perm.go など多数
    • これは、Goファイルをコンパイルした際に特定のコンパイルエラーが発生することを期待するテストに適用されます。-eオプションは、エラーチェックの厳密さに関連する可能性があります。
  3. // $G $D/$F.go// compile に変更

    • 例: test/empty.go, test/eof.go, test/eof1.go
    • これは、Goファイルをコンパイルできることのみを検証し、実行はしないテストに適用されます。

このコミットでは、// build ディレクティブへの変更は含まれていませんが、コミットメッセージの置換パターンには含まれているため、将来的に適用される可能性が示唆されています。

以下に、変更されたファイルの例をいくつか示します。

  • test/235.go:

    --- a/test/235.go
    +++ b/test/235.go
    @@ -1,4 +1,4 @@
    -// $G $F.go && $L $F.$A && ./$A.out
    +// run
     
     // Copyright 2009 The Go Authors. All rights reserved.
     // Use of this source code is governed by a BSD-style
    
  • test/alias.go:

    --- a/test/alias.go
    +++ b/test/alias.go
    @@ -1,4 +1,4 @@
    -// errchk $G -e $D/$F.go
    +// errorcheck
     
     // Copyright 2011 The Go Authors.  All rights reserved.
     // Use of this source code is governed by a BSD-style
    
  • test/empty.go:

    --- a/test/empty.go
    +++ b/test/empty.go
    @@ -1,4 +1,4 @@
    -// $G $D/$F.go
    +// compile
     
     // Copyright 2009 The Go Authors. All rights reserved.
     // Use of this source code is governed by a BSD-style
    

これらの変更は、各テストファイルの機能自体には影響を与えず、テストハーネスがそのテストをどのように実行すべきかを指示する方法のみを変更しています。

コアとなるコードの解説

このコミット自体は、Go言語のコアコード(コンパイラ、ランタイムなど)の変更ではなく、Goプロジェクトのテストスイート内のテストファイルに対する変更です。したがって、Go言語の機能や動作に直接的な影響を与えるものではありません。

しかし、この変更はGoのテストインフラストラクチャの進化を反映しており、テストの実行方法をより宣言的で抽象的なものに移行するものです。

  • // run: このディレクティブは、Goのテストファイルがコンパイルされ、実行されることを示します。テストランナーは、このディレクティブを検出すると、内部的にGoコンパイラとリンカを呼び出し、生成された実行可能ファイルを実行します。そして、その実行結果(標準出力、標準エラー出力、終了コードなど)を検証します。これは、Goの通常のプログラム実行フローをテストする際に使用されます。

  • // errorcheck: このディレクティブは、Goのテストファイルがコンパイル時に特定のエラーを生成することを期待していることを示します。テストランナーは、このディレクティブを検出すると、Goコンパイラを呼び出し、そのコンパイルエラー出力を解析します。テストは、期待されるエラーメッセージが実際に出力された場合に成功とみなされます。これは、Goコンパイラの診断機能や、意図的に不正なコードを記述してコンパイラの挙動を検証する際に使用されます。

  • // compile: このディレクティブは、Goのテストファイルがエラーなくコンパイルできることを示します。テストランナーは、Goコンパイラを呼び出し、コンパイルが成功し、エラーが出力されないことを検証します。実行は行いません。これは、コードの構文や型チェックが正しいことを確認する際に使用されます。

これらのディレクティブは、Goのテストハーネス(テストを実行・管理するシステム)によって解釈されます。テストハーネスは、これらのコメントをパースし、それに対応する内部的なテスト実行ロジックをトリガーします。これにより、テストの記述が簡潔になり、テストインフラストラクチャの変更が個々のテストファイルに影響を与えにくくなります。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(特にsrc/cmd/go/test.gosrc/cmd/go/internal/test/test.goなど、テスト実行に関連する部分)
  • Go言語のコミット履歴とGerritレビューシステム
  • Go言語のメーリングリストや開発者フォーラムでの議論(golang-devなど)

(注: testlibはGoプロジェクトの内部的なメカニズムであり、一般に公開されているドキュメントは少ないため、その詳細な挙動はGoのソースコードや関連するコミット履歴から読み解く必要があります。)