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

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

このコミットは、Go言語のテストスイートにおいて、テストが失敗した場合に確実に非ゼロの終了コードを返すようにするための変更です。以前はエラーメッセージをprintlnで出力するだけでは、テストハーネスが失敗を適切に検出できない場合がありました。特に、新しいrun.goドライバーの導入により、以前の「ゴールデンファイル比較」のようなメカニズムが機能しなくなったため、テストの失敗を明示的に示すためにpanicまたはos.Exit(1)を使用するように修正されました。これにより、自動テストシステムがテストの成否を正確に判断できるようになります。

コミット

  • コミットインデックス: 15213
  • コミットハッシュ: 052c942e20576f01f72d226d11aaf11e721009f3
  • Author: Alan Donovan adonovan@google.com
  • Date: Tue Feb 12 13:17:49 2013 -0500

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

https://github.com/golang/go/commit/052c942e20576f01f72d226d11aaf11e721009f3

元コミット内容

test: ensure all failing tests exit nonzero.

Previously merely printing an error would cause the golden
file comparison (in 'bash run') to fail, but that is no longer
the case with the new run.go driver.

R=iant
CC=golang-dev
https://golang.org/cl/7310087

変更の背景

この変更の主な背景には、Go言語のテスト実行環境の進化があります。コミットメッセージによると、以前はテストの失敗が単にエラーメッセージをprintlnで出力することで示されており、これが「ゴールデンファイル比較」(bash runスクリプト内で実行されていたと推測される)によってテスト失敗として検出されていました。ゴールデンファイル比較とは、テストの出力があらかじめ定義された「ゴールデン」な出力と一致するかどうかを比較することで、テストの成否を判断する手法です。

しかし、新しいrun.goドライバーが導入されたことで、このゴールデンファイル比較のメカニズムが機能しなくなりました。つまり、テストが内部でエラーを検出してprintlnで出力しても、テスト実行プロセス自体は正常終了(終了コード0)してしまうため、自動テストシステムがその失敗を認識できなくなってしまったのです。

この問題を解決するため、テストが失敗した際には、プロセスが非ゼロの終了コードを返すように修正する必要がありました。これにより、テストハーネスやCI/CDパイプラインなどの外部システムが、テストの失敗を確実に検出できるようになります。

前提知識の解説

Go言語におけるエラー処理とプログラムの終了

Go言語では、プログラムの異常終了やエラーの通知に関して、主に以下のメカismsが用いられます。

  1. println / fmt.Printf: これらは標準出力や標準エラー出力にメッセージを出力するための関数です。プログラムの実行フローを中断せず、終了コードにも影響を与えません。単に情報を表示する目的で使用されます。テストにおいて、エラーメッセージをprintlnで出力するだけでは、プログラム自体は正常終了するため、外部から見るとテストは成功したと判断されてしまう問題がありました。

  2. panic: panicは、Goプログラムの通常の実行フローを中断し、パニック状態を引き起こします。パニックが発生すると、現在のゴルーチンは直ちに実行を停止し、遅延関数(defer)が実行され、その後呼び出し元の関数へとパニックが伝播していきます。最終的に、recoverによってパニックが捕捉されない限り、プログラム全体がクラッシュし、非ゼロの終了コード(通常は2)で終了します。テストにおいては、テストケース内でpanicを発生させることで、そのテストが失敗したことを明示的に示すことができます。

  3. os.Exit(code int): os.Exit関数は、Goプログラムを直ちに終了させ、指定された終了コード(code)を返します。os.Exit(0)は正常終了を意味し、os.Exit(1)やその他の非ゼロの値は異常終了を意味します。panicとは異なり、os.Exitは遅延関数(defer)を実行せず、スタックをアンワインドすることなくプログラムを終了させます。テストにおいて、特定の致命的なエラーが発生した場合に、プログラム全体を非ゼロの終了コードで終了させるために使用されます。

終了コード(Exit Code)の重要性

オペレーティングシステムにおいて、プログラムが終了する際に返す数値が「終了コード」または「終了ステータス」と呼ばれます。

  • 0: 慣例的に、プログラムが正常に終了したことを示します。
  • 非ゼロ(1以上): プログラムが何らかのエラーや異常な状態を検出して終了したことを示します。具体的な非ゼロの値は、エラーの種類を示すために使用されることがあります(例: 1は一般的なエラー、2はコマンドライン引数の誤りなど)。

自動テストシステム、ビルドシステム、CI/CDパイプラインなどは、実行されたプログラム(この場合はテストスイート)の終了コードをチェックすることで、その実行が成功したか失敗したかを判断します。テストスイートが失敗したにもかかわらず終了コード0を返してしまうと、これらのシステムはテストが成功したと誤認し、問題を見過ごしてしまう可能性があります。したがって、テストが失敗した際には必ず非ゼロの終了コードを返すことが、自動化された開発ワークフローにおいて極めて重要です。

技術的詳細

このコミットにおける技術的詳細は、Go言語のテストコード内でエラー検出時の挙動を、単なるメッセージ出力からプログラムの異常終了へと変更することに集約されます。

具体的には、多くのテストファイルで以下のような変更が行われています。

  • println("エラーメッセージ") または fmt.Printf("エラーメッセージ")
  • panic("エラーメッセージ") または panic(fmt.Sprintf("エラーメッセージ", ...))
  • os.Exit(1)

この変更により、テストケース内で期待される条件が満たされなかった場合、即座にpanicが発生するか、os.Exit(1)が呼び出されます。

  1. panicの使用:

    • panicは、テスト関数内でアサーションが失敗した場合に特に有効です。panicが発生すると、そのテスト関数は直ちに停止し、テストフレームワークによって捕捉され、テスト失敗として記録されます。最終的に、テストスイート全体が非ゼロの終了コードで終了します。
    • panicはスタックトレースを出力するため、どのテストのどの行で問題が発生したかを特定しやすくなります。これはデバッグにおいて非常に有用です。
  2. os.Exit(1)の使用:

    • 一部のテストでは、より直接的にプログラムを終了させるためにos.Exit(1)が使用されています。これは、テストの失敗が非常に致命的であり、それ以上テストを続行する意味がない場合や、テストの実行環境自体に問題がある場合などに適しています。
    • os.Exitdefer関数を実行しないため、リソースのクリーンアップが必要な場合には注意が必要です。しかし、テストの失敗を即座に外部に通知するという目的においては、非常に効果的です。

この変更は、Goのテストフレームワークがテスト結果をどのように集約し、最終的な終了コードを決定するかに依存しています。新しいrun.goドライバーは、個々のテストケースからのpanicos.Exitを適切に処理し、テストスイート全体の終了コードに反映させるように設計されていると考えられます。これにより、テストの信頼性と自動化された検証プロセスの堅牢性が向上します。

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

このコミットでは、Go言語のテストファイル(test/ディレクトリ以下の多数のファイル)において、テスト失敗時のエラー報告方法が変更されています。主な変更パターンは、エラーメッセージの出力(printlnfmt.Printf)を、プログラムを非ゼロで終了させるpanicまたはos.Exit(1)に置き換えることです。

以下に、いくつかの代表的な変更箇所を抜粋して示します。

test/alias1.go

--- a/test/alias1.go
+++ b/test/alias1.go
@@ -17,7 +17,7 @@ func main() {
 	case uint8:
 		// ok
 	default:
-		println("byte != uint8")
+		panic("byte != uint8")
 	}
 
 	tx = uint8(2)
@@ -25,7 +25,7 @@ func main() {
 	case byte:
 		// ok
 	default:
-		println("uint8 != byte")
+		panic("uint8 != byte")
 	}
 
 	rune32 := false
@@ -37,7 +37,7 @@ func main() {
 		// must be new code
 		rune32 = true
 	default:
-		println("rune != int and rune != int32")
+		panic("rune != int and rune != int32")
 	}
 
 	if rune32 {
@@ -49,6 +49,6 @@ func main() {
 	case rune:
 		// ok
 	default:
-		println("int (or int32) != rune")
+		panic("int (or int32) != rune")
 	}
 }

test/bigalg.go

--- a/test/bigalg.go
+++ b/test/bigalg.go
@@ -15,18 +15,21 @@ type T struct {
 	d byte
 }
 
-var a = []int{ 1, 2, 3 }
+var a = []int{1, 2, 3}
 var NIL []int
 
 func arraycmptest() {
 	if NIL != nil {
 		println("fail1:", NIL, "!= nil")
+		panic("bigalg")
 	}
 	if nil != NIL {
 		println("fail2: nil !=", NIL)
+		panic("bigalg")
 	}
 	if a == nil || nil == a {
 		println("fail3:", a, "== nil")
+		panic("bigalg")
 	}
 }

test/copy.go

--- a/test/copy.go
+++ b/test/copy.go
@@ -132,6 +132,7 @@ func verify8(length, in, out, m int) {
 	n := ncopied(length, in, out)
 	if m != n {
 		fmt.Printf("count bad(%d %d %d): %d not %d\n", length, in, out, m, n)
+		os.Exit(1)
 		return
 	}
 	// before

test/map.go

fmt.Printfpanic(fmt.Sprintf(...))に置き換える例が多く見られます。

--- a/test/map.go
+++ b/test/map.go
@@ -41,7 +41,7 @@ func testbasic() {
 	for i := 0; i < len(mlit); i++ {
 		s := string([]byte{byte(i) + '0'})
 		if mlit[s] != i {
-			fmt.Printf("mlit[%s] = %d\n", s, mlit[s])
+			panic(fmt.Sprintf("mlit[%s] = %d\n", s, mlit[s]))
 		}
 	}

コアとなるコードの解説

これらの変更は、Goのテストが失敗した際に、その失敗を外部のテスト実行システムに確実に伝えるためのものです。

  • printlnからpanicへの変更:

    • printlnは単にコンソールにメッセージを出力するだけで、プログラムの実行を中断しません。そのため、テストが失敗条件に合致しても、プログラム自体は正常終了し、テストハーネスはテストが成功したと誤認する可能性がありました。
    • panicを使用することで、テストが失敗条件に達した瞬間にプログラムの実行が中断され、非ゼロの終了コードで終了します。これにより、テストハーネスはテストが失敗したことを正確に検出できます。また、panicはスタックトレースを出力するため、デバッグ情報も提供されます。
  • fmt.Printfからpanic(fmt.Sprintf(...))への変更:

    • fmt.Printfprintlnと同様に、メッセージを出力するだけでプログラムの終了コードには影響しません。
    • panic(fmt.Sprintf(...))とすることで、フォーマットされたエラーメッセージをpanicの引数として渡し、プログラムを異常終了させます。これにより、詳細なエラー情報を伴ってテスト失敗を通知できます。
  • os.Exit(1)の追加:

    • 一部のテストでは、fmt.Printfでエラーメッセージを出力した後にos.Exit(1)を呼び出すことで、明示的に非ゼロの終了コードでプログラムを終了させています。これは、panicと同様にテスト失敗を外部に通知する効果がありますが、defer関数が実行されないという点でpanicとは異なります。テストの性質や、リソースクリーンアップの必要性に応じて使い分けられます。

これらの変更は、Goのテストスイートがより堅牢になり、自動化されたテスト環境において信頼性の高い結果を提供する上で不可欠な改善です。テストの失敗が適切に報告されることで、開発者は問題を早期に発見し、修正することができます。

関連リンク

  • Go CL 7310087: https://golang.org/cl/7310087 このコミットの変更リスト(Change List)へのリンクです。Goプロジェクトでは、Gitコミットの前にGerritなどのコードレビューシステムで変更が提案され、レビューされます。このCLリンクは、そのレビュープロセスにおける元の提案を示しています。

参考にした情報源リンク

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

このコミットは、Go言語のテストスイートにおいて、テストが失敗した場合に確実に非ゼロの終了コードを返すようにするための変更です。以前はエラーメッセージをprintlnで出力するだけでは、テストハーネスが失敗を適切に検出できない場合がありました。特に、新しいrun.goドライバーの導入により、以前の「ゴールデンファイル比較」のようなメカニズムが機能しなくなったため、テストの失敗を明示的に示すためにpanicまたはos.Exit(1)を使用するように修正されました。これにより、自動テストシステムがテストの成否を正確に判断できるようになります。

コミット

  • コミットインデックス: 15213
  • コミットハッシュ: 052c942e20576f01f72d226d11aaf11e721009f3
  • Author: Alan Donovan adonovan@google.com
  • Date: Tue Feb 12 13:17:49 2013 -0500

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

https://github.com/golang/go/commit/052c942e20576f01f72d226d11aaf11e721009f3

元コミット内容

test: ensure all failing tests exit nonzero.

Previously merely printing an error would cause the golden
file comparison (in 'bash run') to fail, but that is no longer
the case with the new run.go driver.

R=iant
CC=golang-dev
https://golang.org/cl/7310087

変更の背景

この変更の主な背景には、Go言語のテスト実行環境の進化があります。コミットメッセージによると、以前はテストの失敗が単にエラーメッセージをprintlnで出力することで示されており、これが「ゴールデンファイル比較」(bash runスクリプト内で実行されていたと推測される)によってテスト失敗として検出されていました。ゴールデンファイル比較とは、テストの出力があらかじめ定義された「ゴールデン」な出力と一致するかどうかを比較することで、テストの成否を判断する手法です。

しかし、新しいrun.goドライバーが導入されたことで、このゴールデンファイル比較のメカニズムが機能しなくなりました。つまり、テストが内部でエラーを検出してprintlnで出力しても、テスト実行プロセス自体は正常終了(終了コード0)してしまうため、自動テストシステムがその失敗を認識できなくなってしまったのです。

この問題を解決するため、テストが失敗した際には、プロセスが非ゼロの終了コードを返すように修正する必要がありました。これにより、テストハーネスやCI/CDパイプラインなどの外部システムが、テストの失敗を確実に検出できるようになります。

前提知識の解説

Go言語におけるエラー処理とプログラムの終了

Go言語では、プログラムの異常終了やエラーの通知に関して、主に以下のメカニズムが用いられます。

  1. println / fmt.Printf: これらは標準出力や標準エラー出力にメッセージを出力するための関数です。プログラムの実行フローを中断せず、終了コードにも影響を与えません。単に情報を表示する目的で使用されます。テストにおいて、エラーメッセージをprintlnで出力するだけでは、プログラム自体は正常終了するため、外部から見るとテストは成功したと判断されてしまう問題がありました。

  2. panic: panicは、Goプログラムの通常の実行フローを中断し、パニック状態を引き起こします。パニックが発生すると、現在のゴルーチンは直ちに実行を停止し、遅延関数(defer)が実行され、その後呼び出し元の関数へとパニックが伝播していきます。最終的に、recoverによってパニックが捕捉されない限り、プログラム全体がクラッシュし、非ゼロの終了コード(通常は2)で終了します。テストにおいては、テストケース内でpanicを発生させることで、そのテストが失敗したことを明示的に示すことができます。

  3. os.Exit(code int): os.Exit関数は、Goプログラムを直ちに終了させ、指定された終了コード(code)を返します。os.Exit(0)は正常終了を意味し、os.Exit(1)やその他の非ゼロの値は異常終了を意味します。panicとは異なり、os.Exitは遅延関数(defer)を実行せず、スタックをアンワインドすることなくプログラムを終了させます。テストにおいて、特定の致命的なエラーが発生した場合に、プログラム全体を非ゼロの終了コードで終了させるために使用されます。

終了コード(Exit Code)の重要性

オペレーティングシステムにおいて、プログラムが終了する際に返す数値が「終了コード」または「終了ステータス」と呼ばれます。

  • 0: 慣例的に、プログラムが正常に終了したことを示します。
  • 非ゼロ(1以上): プログラムが何らかのエラーや異常な状態を検出して終了したことを示します。具体的な非ゼロの値は、エラーの種類を示すために使用されることがあります(例: 1は一般的なエラー、2はコマンドライン引数の誤りなど)。

自動テストシステム、ビルドシステム、CI/CDパイプラインなどは、実行されたプログラム(この場合はテストスイート)の終了コードをチェックすることで、その実行が成功したか失敗したかを判断します。テストスイートが失敗したにもかかわらず終了コード0を返してしまうと、これらのシステムはテストが成功したと誤認し、問題を見過ごしてしまう可能性があります。したがって、テストが失敗した際には必ず非ゼロの終了コードを返すことが、自動化された開発ワークフローにおいて極めて重要です。

技術的詳細

このコミットにおける技術的詳細は、Go言語のテストコード内でエラー検出時の挙動を、単なるメッセージ出力からプログラムの異常終了へと変更することに集約されます。

具体的には、多くのテストファイルで以下のような変更が行われています。

  • println("エラーメッセージ") または fmt.Printf("エラーメッセージ")
  • panic("エラーメッセージ") または panic(fmt.Sprintf("エラーメッセージ", ...))
  • os.Exit(1)

この変更により、テストケース内で期待される条件が満たされなかった場合、即座にpanicが発生するか、os.Exit(1)が呼び出されます。

  1. panicの使用:

    • panicは、テスト関数内でアサーションが失敗した場合に特に有効です。panicが発生すると、そのテスト関数は直ちに停止し、テストフレームワークによって捕捉され、テスト失敗として記録されます。最終的に、テストスイート全体が非ゼロの終了コードで終了します。
    • panicはスタックトレースを出力するため、どのテストのどの行で問題が発生したかを特定しやすくなります。これはデバッグにおいて非常に有用です。
  2. os.Exit(1)の使用:

    • 一部のテストでは、より直接的にプログラムを終了させるためにos.Exit(1)が使用されています。これは、テストの失敗が非常に致命的であり、それ以上テストを続行する意味がない場合や、テストの実行環境自体に問題がある場合などに適しています。
    • os.Exitdefer関数を実行しないため、リソースのクリーンアップが必要な場合には注意が必要です。しかし、テストの失敗を即座に外部に通知するという目的においては、非常に効果的です。

この変更は、Goのテストフレームワークがテスト結果をどのように集約し、最終的な終了コードを決定するかに依存しています。新しいrun.goドライバーは、個々のテストケースからのpanicos.Exitを適切に処理し、テストスイート全体の終了コードに反映させるように設計されていると考えられます。これにより、テストの信頼性と自動化された検証プロセスの堅牢性が向上します。

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

このコミットでは、Go言語のテストファイル(test/ディレクトリ以下の多数のファイル)において、テスト失敗時のエラー報告方法が変更されています。主な変更パターンは、エラーメッセージの出力(printlnfmt.Printf)を、プログラムを非ゼロで終了させるpanicまたはos.Exit(1)に置き換えることです。

以下に、いくつかの代表的な変更箇所を抜粋して示します。

test/alias1.go

--- a/test/alias1.go
+++ b/test/alias1.go
@@ -17,7 +17,7 @@ func main() {
 	case uint8:
 		// ok
 	default:
-		println("byte != uint8")
+		panic("byte != uint8")
 	}
 
 	tx = uint8(2)
@@ -25,7 +25,7 @@ func main() {
 	case byte:
 		// ok
 	default:
-		println("uint8 != byte")
+		panic("uint8 != byte")
 	}
 
 	rune32 := false
@@ -37,7 +37,7 @@ func main() {
 		// must be new code
 		rune32 = true
 	default:
-		println("rune != int and rune != int32")
+		panic("rune != int and rune != int32")
 	}
 
 	if rune32 {
@@ -49,6 +49,6 @@ func main() {
 	case rune:
 		// ok
 	default:
-		println("int (or int32) != rune")
+		panic("int (or int32) != rune")
 	}
 }

test/bigalg.go

--- a/test/bigalg.go
+++ b/test/bigalg.go
@@ -15,18 +15,21 @@ type T struct {
 	d byte
 }
 
-var a = []int{ 1, 2, 3 }
+var a = []int{1, 2, 3}
 var NIL []int
 
 func arraycmptest() {
 	if NIL != nil {
 		println("fail1:", NIL, "!= nil")
+		panic("bigalg")
 	}
 	if nil != NIL {
 		println("fail2: nil !=", NIL)
+		panic("bigalg")
 	}
 	if a == nil || nil == a {
 		println("fail3:", a, "== nil")
+		panic("bigalg")
 	}
 }

test/copy.go

--- a/test/copy.go
+++ b/test/copy.go
@@ -132,6 +132,7 @@ func verify8(length, in, out, m int) {
 	n := ncopied(length, in, out)
 	if m != n {
 		fmt.Printf("count bad(%d %d %d): %d not %d\n", length, in, out, m, n)
+		os.Exit(1)
 		return
 	}
 	// before

test/map.go

fmt.Printfpanic(fmt.Sprintf(...))に置き換える例が多く見られます。

--- a/test/map.go
+++ b/test/map.go
@@ -41,7 +41,7 @@ func testbasic() {
 	for i := 0; i < len(mlit); i++ {
 		s := string([]byte{byte(i) + '0'})
 		if mlit[s] != i {
-			fmt.Printf("mlit[%s] = %d\n", s, mlit[s])
+			panic(fmt.Sprintf("mlit[%s] = %d\n", s, mlit[s]))
 		}
 	}

コアとなるコードの解説

これらの変更は、Goのテストが失敗した際に、その失敗を外部のテスト実行システムに確実に伝えるためのものです。

  • printlnからpanicへの変更:

    • printlnは単にコンソールにメッセージを出力するだけで、プログラムの実行を中断しません。そのため、テストが失敗条件に合致しても、プログラム自体は正常終了し、テストハーネスはテストが成功したと誤認する可能性がありました。
    • panicを使用することで、テストが失敗条件に達した瞬間にプログラムの実行が中断され、非ゼロの終了コードで終了します。これにより、テストハーネスはテストが失敗したことを正確に検出できます。また、panicはスタックトレースを出力するため、デバッグ情報も提供されます。
  • fmt.Printfからpanic(fmt.Sprintf(...))への変更:

    • fmt.Printfprintlnと同様に、メッセージを出力するだけでプログラムの終了コードには影響しません。
    • panic(fmt.Sprintf(...))とすることで、フォーマットされたエラーメッセージをpanicの引数として渡し、プログラムを異常終了させます。これにより、詳細なエラー情報を伴ってテスト失敗を通知できます。
  • os.Exit(1)の追加:

    • 一部のテストでは、fmt.Printfでエラーメッセージを出力した後にos.Exit(1)を呼び出すことで、明示的に非ゼロの終了コードでプログラムを終了させています。これは、panicと同様にテスト失敗を外部に通知する効果がありますが、defer関数が実行されないという点でpanicとは異なります。テストの性質や、リソースクリーンアップの必要性に応じて使い分けられます。

これらの変更は、Goのテストスイートがより堅牢になり、自動化されたテスト環境において信頼性の高い結果を提供する上で不可欠な改善です。テストの失敗が適切に報告されることで、開発者は問題を早期に発見し、修正することができます。

関連リンク

  • Go CL 7310087: https://golang.org/cl/7310087 このコミットの変更リスト(Change List)へのリンクです。Goプロジェクトでは、Gitコミットの前にGerritなどのコードレビューシステムで変更が提案され、レビューされます。このCLリンクは、そのレビュープロセスにおける元の提案を示しています。

参考にした情報源リンク