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

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

このコミットは、Go言語の公式フォーマッタである gofmt のテストスクリプト test.sh の挙動を改善し、単一のエラーでテストが停止しないように変更したものです。これにより、テストスイート全体が実行され、全てのエラーが報告されるようになります。また、gofmt のフォーマット規則に準拠していなかった test/fixedbugs/bug377.go ファイルを修正し、gofmt が正しく処理できるようにしました。

コミット

commit 4d27f64863ab8518f4ac9f20539bd20c16e77433
Author: Russ Cox <rsc@golang.org>
Date:   Wed Nov 16 18:44:21 2011 -0500

    gofmt: do not stop test.sh after 1 error

    Fix bug377.go to be gofmt-compliant.

    R=gri, r, r
    CC=golang-dev
    https://golang.org/cl/5400045

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

https://github.com/golang/go/commit/4d27f64863ab8518f4ac9f20539bd20c16e77433

元コミット内容

gofmt: do not stop test.sh after 1 error

Fix bug377.go to be gofmt-compliant.

R=gri, r, r
CC=golang-dev
https://golang.org/cl/5400045

変更の背景

このコミットが行われた背景には、主に2つの問題がありました。

  1. gofmttest.sh スクリプトの挙動: 以前の test.sh スクリプトは、テスト中に最初のエラーが発生すると即座に exit 1 を呼び出して終了していました。これは、テストスイート全体を実行して全ての問題を一度に把握したい場合には非効率的でした。特にCI/CD環境や開発者がローカルで多数のテストを実行する際に、最初のエラーで停止してしまうと、残りのエラーを見つけるために何度もテストを再実行する必要がありました。この変更は、テストの網羅性とデバッグ効率を向上させることを目的としています。

  2. test/fixedbugs/bug377.gogofmt 非準拠: gofmt はGo言語のソースコードを解析し、標準的なフォーマットに整形するツールです。しかし、test/fixedbugs/bug377.go ファイルの内容がGo言語の有効な構文として認識されない状態(具体的には package 宣言が欠落している)であったため、gofmt がこのファイルを処理しようとするとエラーを発生させていました。gofmt は有効なGoソースコードに対してのみ動作するため、このファイル自体を gofmt が処理できるように修正する必要がありました。

これらの問題を解決することで、gofmt のテストプロセスをより堅牢にし、同時に gofmt が処理できるGoコードの範囲を広げることが意図されています。

前提知識の解説

gofmt

gofmt は、Go言語のソースコードを自動的にフォーマットするためのツールです。Go言語のツールチェインに標準で含まれており、Goコミュニティ全体でコードのスタイルを統一するために広く利用されています。gofmt の主な特徴は以下の通りです。

  • 規範的なフォーマット: gofmt は、Go言語のコードに対して単一の規範的なフォーマットを適用します。これにより、開発者間のスタイル論争を排除し、コードレビューの焦点を内容そのものに集中させることができます。
  • 構文解析に基づく整形: 単純なテキスト置換ではなく、Goの構文木を解析してコードを整形します。これにより、コメントの位置やインデント、空白の挿入などがGoの慣習に従って自動的に調整されます。
  • 開発効率の向上: コードの整形に時間を費やす必要がなくなり、開発者はロジックの実装に集中できます。

シェルスクリプト (test.sh) とエラーハンドリング

test.sh は、Unix/Linux環境でテストを実行するために使用されるシェルスクリプトです。シェルスクリプトでは、コマンドの実行結果に基づいて次のアクションを決定することがよくあります。

  • exit コマンド: シェルスクリプトの実行を終了し、指定された終了ステータス(通常は0が成功、0以外がエラー)を返します。exit 1 は、スクリプトがエラーで終了したことを示します。
  • エラー発生時の即時終了: 多くのテストスクリプトでは、最初のエラーで即座に exit 1 を呼び出すことで、問題が早期に発見された場合に無駄な後続のテスト実行を避けることができます。しかし、このコミットの背景で述べたように、全てのエラーを一度に報告したい場合には不便です。
  • エラーフラグの利用: 複数のエラーを検出したい場合、エラーが発生するたびにスクリプトを終了させるのではなく、エラーが発生したことを示すフラグ(ファイルや変数)を設定し、スクリプトの最後にそのフラグをチェックして最終的な終了ステータスを決定する手法が用いられます。このコミットでは、_failed という一時ファイルがこのフラグとして使用されています。

Go言語の package 宣言

Go言語の全てのソースファイルは、そのファイルが属するパッケージを宣言する必要があります。これはファイルの先頭に package <パッケージ名> という形式で記述されます。

  • package main: 実行可能なプログラムのエントリポイントとなるパッケージです。main パッケージには main 関数が含まれ、プログラムの実行開始点となります。
  • package <任意の名前>: ライブラリとして機能するパッケージです。他のGoプログラムからインポートして利用されます。

package 宣言がないファイルは、Goコンパイラや gofmt のようなツールにとっては有効なGoソースコードとして認識されません。gofmt は有効なGoコードを期待するため、package 宣言の欠落はエラーの原因となります。

技術的詳細

このコミットの技術的な変更は、gofmt のテストスクリプト src/cmd/gofmt/test.sh と、テスト対象のGoファイル test/fixedbugs/bug377.go の2箇所にわたります。

src/cmd/gofmt/test.sh の変更

以前の test.sh は、error() 関数が呼び出されると即座に exit 1 でスクリプトを終了させていました。この変更では、この挙動を修正し、全てのエラーを収集してから最後にまとめて終了ステータスを返すようにしました。

  1. エラーフラグファイルの導入: _failed という名前の一時ファイルがエラーフラグとして導入されました。
    • スクリプトの開始時に rm -f _failed が追加され、前回の実行で残った _failed ファイルを確実に削除します。
    • error() 関数内で exit 1 の代わりに touch _failed が追加されました。これにより、エラーが発生してもスクリプトは終了せず、_failed ファイルが作成されるだけになります。
  2. 最終的なエラーチェック: runtests 関数の実行後、スクリプトの最後に以下のブロックが追加されました。
    if [ -f _failed ]; then
        rm _failed
        exit 1
    fi
    
    このコードは、_failed ファイルが存在するかどうかをチェックします。もし存在すれば(つまり、テスト中に少なくとも1つのエラーが発生していれば)、_failed ファイルを削除し、exit 1 でスクリプトをエラー終了させます。_failed ファイルが存在しなければ、スクリプトは正常終了(exit 0 に相当)し、「PASSED」メッセージが表示されます。

この変更により、test.sh は全てのエラーを検出し、その全てを報告できるようになりました。これは、大規模なテストスイートやCI環境において、問題の全体像を把握するために非常に有用です。

test/fixedbugs/bug377.go の変更

このファイルは、gofmt が処理できないGoコードの例として存在していました。元のファイルは単に ignored というテキストを含んでおり、これはGo言語の有効な構文ではありませんでした。

  • ignored から package ignored への変更: ファイルの内容が package ignored に変更されました。
    --- a/test/fixedbugs/bug377.go
    +++ b/test/fixedbugs/bug377.go
    @@ -6,4 +6,4 @@
    
     // Issue 1802
    
    -ignored
    +package ignored
    
    この変更により、bug377.go はGo言語の有効なパッケージ宣言を持つファイルとなり、gofmt が正しく解析・処理できるようになりました。gofmt は、有効なGoコードに対してのみフォーマットを適用するため、この修正は gofmt のテストがこのファイルを適切に扱えるようにするために不可欠でした。

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

src/cmd/gofmt/test.sh

--- a/src/cmd/gofmt/test.sh
+++ b/src/cmd/gofmt/test.sh
@@ -14,6 +14,7 @@ TMP1=test_tmp1.go
 TMP2=test_tmp2.go
 TMP3=test_tmp3.go
 COUNT=0
+rm -f _failed

 count() {
 	#echo $1
@@ -27,10 +28,9 @@ count() {

 error() {
 	echo $1
-	exit 1
+	touch _failed
 }

-
 # apply to one file
 apply1() {
 	# the following files are skipped because they are test cases
@@ -157,6 +157,11 @@ runtests() {
 runtests "$@"
 cleanup

+if [ -f _failed ]; then
+	rm _failed
+	exit 1
+fi
+
 # done
 echo
 echo "PASSED ($COUNT tests)"

test/fixedbugs/bug377.go

--- a/test/fixedbugs/bug377.go
+++ b/test/fixedbugs/bug377.go
@@ -6,4 +6,4 @@

 // Issue 1802

-ignored
+package ignored

コアとなるコードの解説

src/cmd/gofmt/test.sh の変更点

  • rm -f _failed の追加:

    • +rm -f _failed
    • スクリプトの冒頭にこの行が追加されました。これは、テスト実行前に、エラーを示すフラグファイル _failed がもし存在していれば、それを強制的に削除するものです。これにより、以前のテスト実行の結果が現在のテストに影響を与えないように、クリーンな状態からテストを開始できます。
  • error() 関数の変更:

    • - exit 1
    • + touch _failed
    • error() 関数は、テスト中にエラーが検出された際に呼び出されます。以前は exit 1 を実行してスクリプトを即座に終了させていましたが、この変更により touch _failed が実行されるようになりました。touch _failed は、_failed という名前の空のファイルを作成します。このファイルが存在すること自体が「エラーが発生した」という状態を示すフラグとなります。スクリプトはエラーが発生しても即座には終了せず、残りのテストも実行し続けます。
  • 最終的なエラーチェックブロックの追加:

    • +if [ -f _failed ]; then
    • + rm _failed
    • + exit 1
    • +fi
    • スクリプトの最後にこの if ブロックが追加されました。全てのテスト(runtests "$@")が完了し、クリーンアップ(cleanup)が実行された後、このブロックが実行されます。
    • [ -f _failed ] は、_failed という名前のファイルが存在するかどうかをチェックする条件式です。
    • もし _failed ファイルが存在すれば(つまり、テスト中に少なくとも1つのエラーが error() 関数によって記録されていれば)、rm _failed でそのファイルを削除し、exit 1 でスクリプト全体をエラー終了させます。
    • もし _failed ファイルが存在しなければ、この if ブロックはスキップされ、スクリプトは正常に終了し、「PASSED」メッセージが表示されます。

これらの変更により、gofmt のテストスクリプトは、最初のエラーで停止することなく、全てのテストを実行し、最終的に全てのエラーをまとめて報告する「非停止型」のテスト実行フローを実現しました。

test/fixedbugs/bug377.go の変更点

  • ignored から package ignored への変更:
    • -ignored
    • +package ignored
    • この変更は非常にシンプルですが、Go言語の構文規則においては非常に重要です。Goのソースファイルは必ず package 宣言で始まる必要があります。元のファイルは単に ignored というテキストを含んでおり、これはGoの有効な構文ではありませんでした。
    • package ignored とすることで、このファイルは ignored という名前のパッケージに属するGoのソースファイルとして有効になります。これにより、gofmt はこのファイルを正しく解析し、フォーマット処理を適用できるようになります。この修正は、gofmt のテストスイートが、有効なGoコードに対してのみテストを実行するという前提を満たすために行われました。

関連リンク

参考にした情報源リンク

  • gofmt の公式ドキュメントやGo言語のパッケージに関する一般的な情報(Go言語の公式ドキュメント、Go言語のチュートリアルなど)
  • シェルスクリプトのエラーハンドリングに関する一般的な知識
  • Gitのdiff形式に関する知識