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

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

このコミットは、Go言語の初期のテストツールである gotest において、テスト関数の実行順序をアルファベット順からファイル内での定義順に変更するものです。これにより、テストの実行がより予測可能になり、開発者がコードを記述した意図に沿った順序でテストが実行されるようになります。

コミット

commit bd6e0bc8badbcb74b975adaceb8260cf4c7cf9ba
Author: Rob Pike <r@golang.org>
Date:   Thu Nov 20 15:22:32 2008 -0800

    run tests in file order, not alphabetical
    
    R=rsc
    DELTA=1  (0 added, 0 deleted, 1 changed)
    OCL=19723
    CL=19727

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

https://github.com/golang/go/commit/bd6e0bc8badbcb74b975adaceb8260cf4c7cf9ba

元コミット内容

run tests in file order, not alphabetical (テストをアルファベット順ではなく、ファイル順で実行する)

変更の背景

Go言語の初期開発段階において、テストの実行順序は重要な考慮事項でした。このコミット以前は、gotest ツールがテスト関数をアルファベット順に検出・実行していました。しかし、テストの実行順序がアルファベット順であることにはいくつかの問題がありました。

  1. 予測不可能性: テスト関数の名前を変更すると、実行順序が変わってしまう可能性があります。これは、テストが特定の順序に依存している場合に、予期せぬテストの失敗を引き起こす可能性があります。
  2. 依存関係の問題: テストの中には、前のテストが特定の状態を設定することを前提としている場合があります。アルファベット順では、このような依存関係を保証することが困難です。
  3. デバッグの困難さ: テストが失敗した際に、その原因がテストの順序に起因するものなのか、それとも実際のコードのバグなのかを特定するのが難しくなります。
  4. 開発者の意図との乖離: 開発者は通常、関連するテストをファイル内で連続して記述します。ファイル内での定義順にテストが実行されることは、開発者の意図と合致し、テストコードの読みやすさやメンテナンス性を向上させます。

これらの問題を解決し、テストの実行をより安定させ、開発者の期待に沿うようにするために、テストの実行順序をファイル内での定義順に変更する必要がありました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の初期のツールと概念について知っておく必要があります。

  • gotest: Go言語の初期に存在したテスト実行ツールです。現在の go test コマンドの前身にあたります。Goのソースコードをコンパイルし、テスト関数(Testで始まる関数)を検出し、実行する役割を担っていました。

  • 6nm: Go言語のツールチェーンの一部であり、Goのオブジェクトファイル(.6 拡張子を持つファイル、例えば _testmain.6)からシンボル(関数名、変数名など)をリストアップするためのコマンドです。これはUnix系の nm コマンドに似ています。 6nm は、オブジェクトファイル内のシンボル情報を解析し、その名前、型、アドレスなどを表示します。このコミットでは、6nm を使用してテスト関数を特定し、そのリストを生成していました。

  • grep: Unix系のコマンドで、テキストファイルから正規表現にマッチする行を検索するために使用されます。このコミットでは、6nm の出力から T .*·Test というパターン(T はテキストセクションのシンボル、·Test はGoのテスト関数を示すパターン)にマッチする行を抽出し、テスト関数を特定しています。

  • sed: Unix系のストリームエディタで、テキストの変換に使用されます。このコミットでは、grep で抽出された行からテスト関数名のみを抽出するために使用されています。

  • testing パッケージ: Go言語の標準ライブラリの一部で、ユニットテストやベンチマークテストを記述するための機能を提供します。testing.Test は、テスト関数を表現するための構造体です。gotest は、この testing.Test 構造体の配列を生成し、それを実行することでテストを行っていました。

  • テスト実行順序の重要性: 一般的に、ユニットテストは互いに独立しているべきですが、統合テストや特定のシナリオテストでは、テストケース間に論理的な順序が存在することがあります。また、テストの実行順序が固定されていることは、テストの再現性を高め、CI/CDパイプラインでの安定した結果を得る上で非常に重要です。

技術的詳細

このコミットの核心は、gotest がテスト関数を検出する際に使用する 6nm コマンドに -s オプションを追加した点にあります。

変更前:

for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')

変更後:

for i in $(6nm -s $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
  • 6nm コマンドの動作: 6nm コマンドは、Goのオブジェクトファイル(この場合はテスト用の _testmain.6 など)からシンボル情報を抽出します。デフォルトでは、6nm はシンボルをアルファベット順にソートして出力します。

  • -s オプションの追加: 6nm コマンドに -s オプションを追加すると、シンボルをアドレス順(数値順)にソートして出力するようになります。Goのコンパイラは、ソースファイル内で関数が定義された順序でそれらをオブジェクトファイルに配置する傾向があります。したがって、シンボルをアドレス順にソートすることは、結果的にソースファイル内での定義順にテスト関数をリストアップすることにつながります。

  • パイプラインの動作:

    1. 6nm -s $ofiles: テスト対象のオブジェクトファイルから、シンボルをアドレス順にリストアップします。
    2. grep ' T .*·Test': 6nm の出力から、Goのテスト関数(T タイプで ·Test を含むシンボル)に該当する行をフィルタリングします。
    3. sed 's/.* //; s/·/./': フィルタリングされた行から、テスト関数名のみを抽出し、Goの内部的なシンボル名表記(package.function の代わりに package·function)を一般的な表記に変換します。

この変更により、for ループで処理されるテスト関数のリストが、アルファベット順ではなく、ソースファイル内での定義順に並べ替えられることになります。これにより、gotest はテストをファイル内での定義順に実行するようになります。

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

--- a/src/cmd/gotest/gotest
+++ b/src/cmd/gotest/gotest
@@ -55,7 +55,7 @@ trap "rm -f _testmain.go _testmain.6" 0 1 2 3 14 15
 	# test array
 	echo
 	echo 'var tests = &[]testing.Test {'
-	for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
+	for i in $(6nm -s $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
 	do
 		echo '		testing.Test{ "'$i'", &'$i' },'
 	done

コアとなるコードの解説

変更は src/cmd/gotest/gotest スクリプトの56行目の一箇所のみです。

元のコード: for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')

変更後のコード: for i in $(6nm -s $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')

この変更は、6nm コマンドに -s フラグを追加しただけです。

  • $ofiles: テスト対象のGoオブジェクトファイル(例: _testmain.6)を指す変数です。
  • 6nm $ofiles: オブジェクトファイル内のシンボルをデフォルトのアルファベット順でリストアップします。
  • 6nm -s $ofiles: オブジェクトファイル内のシンボルをアドレス順(数値順)でリストアップします。これにより、Goコンパイラが関数を配置する順序(通常はソースファイル内での定義順)が反映されます。

このパイプラインの出力は、for ループの i 変数に順次代入され、testing.Test 構造体の配列が生成されます。この配列の順序が、テストが実行される順序を決定します。したがって、-s フラグの追加により、テストはファイル内での定義順に実行されるようになります。

この変更は、Goのテストフレームワークの初期段階における重要な改善であり、テストの信頼性と開発者の生産性向上に貢献しました。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/go/internal/test/test.gosrc/cmd/nm/main.go など、go testnm コマンドの実装に関連するファイル)
  • Go言語の初期のコミット履歴
  • Unix nm コマンドのドキュメント (6nm の挙動を理解するための類推)
  • Go言語の公式ドキュメントやブログ記事 (テストに関する初期の議論など)
  • Go言語の 6nm コマンドに関する情報 (Goのツールチェーンの進化に伴い、6nm は現在では go tool nm として統合されています)
  • Go言語のテストのベストプラクティスに関する一般的な情報I have read the file /home/orange/Project/comemo/commit_data/1207.txt. I will now generate the comprehensive technical explanation in Markdown format, adhering to all specified instructions and the chapter structure.

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

このコミットは、Go言語の初期のテストツールである gotest において、テスト関数の実行順序をアルファベット順からファイル内での定義順に変更するものです。これにより、テストの実行がより予測可能になり、開発者がコードを記述した意図に沿った順序でテストが実行されるようになります。

コミット

commit bd6e0bc8badbcb74b975adaceb8260cf4c7cf9ba
Author: Rob Pike <r@golang.org>
Date:   Thu Nov 20 15:22:32 2008 -0800

    run tests in file order, not alphabetical
    
    R=rsc
    DELTA=1  (0 added, 0 deleted, 1 changed)
    OCL=19723
    CL=19727

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

https://github.com/golang/go/commit/bd6e0bc8badbcb74b975adaceb8260cf4c7cf9ba

元コミット内容

run tests in file order, not alphabetical (テストをアルファベット順ではなく、ファイル順で実行する)

変更の背景

Go言語の初期開発段階において、テストの実行順序は重要な考慮事項でした。このコミット以前は、gotest ツールがテスト関数をアルファベット順に検出・実行していました。しかし、テストの実行順序がアルファベット順であることにはいくつかの問題がありました。

  1. 予測不可能性: テスト関数の名前を変更すると、実行順序が変わってしまう可能性があります。これは、テストが特定の順序に依存している場合に、予期せぬテストの失敗を引き起こす可能性があります。
  2. 依存関係の問題: テストの中には、前のテストが特定の状態を設定することを前提としている場合があります。アルファベット順では、このような依存関係を保証することが困難です。
  3. デバッグの困難さ: テストが失敗した際に、その原因がテストの順序に起因するものなのか、それとも実際のコードのバグなのかを特定するのが難しくなります。
  4. 開発者の意図との乖離: 開発者は通常、関連するテストをファイル内で連続して記述します。ファイル内での定義順にテストが実行されることは、開発者の意図と合致し、テストコードの読みやすさやメンテナンス性を向上させます。

これらの問題を解決し、テストの実行をより安定させ、開発者の期待に沿うようにするために、テストの実行順序をファイル内での定義順に変更する必要がありました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の初期のツールと概念について知っておく必要があります。

  • gotest: Go言語の初期に存在したテスト実行ツールです。現在の go test コマンドの前身にあたります。Goのソースコードをコンパイルし、テスト関数(Testで始まる関数)を検出し、実行する役割を担っていました。

  • 6nm: Go言語のツールチェーンの一部であり、Goのオブジェクトファイル(.6 拡張子を持つファイル、例えば _testmain.6)からシンボル(関数名、変数名など)をリストアップするためのコマンドです。これはUnix系の nm コマンドに似ています。 6nm は、オブジェクトファイル内のシンボル情報を解析し、その名前、型、アドレスなどを表示します。このコミットでは、6nm を使用してテスト関数を特定し、そのリストを生成していました。

  • grep: Unix系のコマンドで、テキストファイルから正規表現にマッチする行を検索するために使用されます。このコミットでは、6nm の出力から T .*·Test というパターン(T はテキストセクションのシンボル、·Test はGoのテスト関数を示すパターン)にマッチする行を抽出し、テスト関数を特定しています。

  • sed: Unix系のストリームエディタで、テキストの変換に使用されます。このコミットでは、grep で抽出された行からテスト関数名のみを抽出するために使用されています。

  • testing パッケージ: Go言語の標準ライブラリの一部で、ユニットテストやベンチマークテストを記述するための機能を提供します。testing.Test は、テスト関数を表現するための構造体です。gotest は、この testing.Test 構造体の配列を生成し、それを実行することでテストを行っていました。

  • テスト実行順序の重要性: 一般的に、ユニットテストは互いに独立しているべきですが、統合テストや特定のシナリオテストでは、テストケース間に論理的な順序が存在することがあります。また、テストの実行順序が固定されていることは、テストの再現性を高め、CI/CDパイプラインでの安定した結果を得る上で非常に重要です。

技術的詳細

このコミットの核心は、gotest がテスト関数を検出する際に使用する 6nm コマンドに -s オプションを追加した点にあります。

変更前:

for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')

変更後:

for i in $(6nm -s $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
  • 6nm コマンドの動作: 6nm コマンドは、Goのオブジェクトファイル(この場合はテスト用の _testmain.6 など)からシンボル情報を抽出します。デフォルトでは、6nm はシンボルをアルファベット順にソートして出力します。

  • -s オプションの追加: 6nm コマンドに -s オプションを追加すると、シンボルをアドレス順(数値順)にソートして出力するようになります。Goのコンパイラは、ソースファイル内で関数が定義された順序でそれらをオブジェクトファイルに配置する傾向があります。したがって、シンボルをアドレス順にソートすることは、結果的にソースファイル内での定義順にテスト関数をリストアップすることにつながります。

  • パイプラインの動作:

    1. 6nm -s $ofiles: テスト対象のオブジェクトファイルから、シンボルをアドレス順にリストアップします。
    2. grep ' T .*·Test': 6nm の出力から、Goのテスト関数(T タイプで ·Test を含むシンボル)に該当する行をフィルタリングします。
    3. sed 's/.* //; s/·/./': フィルタリングされた行から、テスト関数名のみを抽出し、Goの内部的なシンボル名表記(package.function の代わりに package·function)を一般的な表記に変換します。

この変更により、for ループで処理されるテスト関数のリストが、アルファベット順ではなく、ソースファイル内での定義順に並べ替えられることになります。これにより、gotest はテストをファイル内での定義順に実行するようになります。

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

--- a/src/cmd/gotest/gotest
+++ b/src/cmd/gotest/gotest
@@ -55,7 +55,7 @@ trap "rm -f _testmain.go _testmain.6" 0 1 2 3 14 15
 	# test array
 	echo
 	echo 'var tests = &[]testing.Test {'
-	for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
+	for i in $(6nm -s $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')
 	do
 		echo '		testing.Test{ "'$i'", &'$i' },'
 	done

コアとなるコードの解説

変更は src/cmd/gotest/gotest スクリプトの56行目の一箇所のみです。

元のコード: for i in $(6nm $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')

変更後のコード: for i in $(6nm -s $ofiles | grep ' T .*·Test' | sed 's/.* //; s/·/./')

この変更は、6nm コマンドに -s フラグを追加しただけです。

  • $ofiles: テスト対象のGoオブジェクトファイル(例: _testmain.6)を指す変数です。
  • 6nm $ofiles: オブジェクトファイル内のシンボルをデフォルトのアルファベット順でリストアップします。
  • 6nm -s $ofiles: オブジェクトファイル内のシンボルをアドレス順(数値順)でリストアップします。これにより、Goコンパイラが関数を配置する順序(通常はソースファイル内での定義順)が反映されます。

このパイプラインの出力は、for ループの i 変数に順次代入され、testing.Test 構造体の配列が生成されます。この配列の順序が、テストが実行される順序を決定します。したがって、-s フラグの追加により、テストはファイル内での定義順に実行されるようになります。

この変更は、Goのテストフレームワークの初期段階における重要な改善であり、テストの信頼性と開発者の生産性向上に貢献しました。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/go/internal/test/test.gosrc/cmd/nm/main.go など、go testnm コマンドの実装に関連するファイル)
  • Go言語の初期のコミット履歴
  • Unix nm コマンドのドキュメント (6nm の挙動を理解するための類推)
  • Go言語の公式ドキュメントやブログ記事 (テストに関する初期の議論など)
  • Go言語の 6nm コマンドに関する情報 (Goのツールチェーンの進化に伴い、6nm は現在では go tool nm として統合されています)
  • Go言語のテストのベストプラクティスに関する一般的な情報