[インデックス 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つの問題がありました。
-
gofmt
のtest.sh
スクリプトの挙動: 以前のtest.sh
スクリプトは、テスト中に最初のエラーが発生すると即座にexit 1
を呼び出して終了していました。これは、テストスイート全体を実行して全ての問題を一度に把握したい場合には非効率的でした。特にCI/CD環境や開発者がローカルで多数のテストを実行する際に、最初のエラーで停止してしまうと、残りのエラーを見つけるために何度もテストを再実行する必要がありました。この変更は、テストの網羅性とデバッグ効率を向上させることを目的としています。 -
test/fixedbugs/bug377.go
のgofmt
非準拠: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
でスクリプトを終了させていました。この変更では、この挙動を修正し、全てのエラーを収集してから最後にまとめて終了ステータスを返すようにしました。
- エラーフラグファイルの導入:
_failed
という名前の一時ファイルがエラーフラグとして導入されました。- スクリプトの開始時に
rm -f _failed
が追加され、前回の実行で残った_failed
ファイルを確実に削除します。 error()
関数内でexit 1
の代わりにtouch _failed
が追加されました。これにより、エラーが発生してもスクリプトは終了せず、_failed
ファイルが作成されるだけになります。
- スクリプトの開始時に
- 最終的なエラーチェック:
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コードに対してのみテストを実行するという前提を満たすために行われました。
関連リンク
- Go CL 5400045: https://golang.org/cl/5400045
参考にした情報源リンク
gofmt
の公式ドキュメントやGo言語のパッケージに関する一般的な情報(Go言語の公式ドキュメント、Go言語のチュートリアルなど)- シェルスクリプトのエラーハンドリングに関する一般的な知識
- Gitのdiff形式に関する知識