[インデックス 14693] ファイルの概要
このコミットは、Go言語のgo test
コマンドにおけるExample
関数の実行時の標準出力(stdout)と標準エラー出力(stderr)のキャプチャ方法を変更するものです。具体的には、Example
関数の出力検証において、標準エラー出力のキャプチャを停止し、標準出力のみをキャプチャするように修正されました。これにより、Example
関数が標準エラー出力に書き込んだ内容は、テストの出力比較の対象外となり、直接コンソールに出力されるようになります。また、このコミットには、go build
およびgo test
コマンドにいくつかの新しいフラグ(データ競合検出、コンパイラ引数、ベンチマークメモリプロファイリング、ゴルーチンブロッキングプロファイリングなど)を追加するドキュメントの更新も含まれています。
コミット
commit ff5d47ebbaad42862e97e93d46fc89c768c098a3
Author: Andrew Gerrand <adg@golang.org>
Date: Thu Dec 20 10:48:33 2012 +1100
testing: only capture stdout when running examples
Fixes #4550.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6973048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ff5d47ebbaad42862e97e93d46fc89c768c098a3
元コミット内容
testing: only capture stdout when running examples
Fixes #4550.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6973048
変更の背景
この変更の背景には、Go言語のtesting
パッケージにおけるExample
関数の振る舞いの明確化と、テスト実行時の出力管理の改善があります。Example
関数は、Goのドキュメント生成ツール(go doc
)によって自動的にテストされ、その出力が期待される出力(Output:
コメントで指定)と一致するかどうかが検証されます。
以前の実装では、Example
関数が実行される際に、標準出力(os.Stdout
)と標準エラー出力(os.Stderr
)の両方がキャプチャされ、その内容がOutput:
コメントと比較されていました。しかし、Example
関数の主な目的は、コードの正しい使用方法を標準出力で示すことであり、標準エラー出力は通常、エラーメッセージやデバッグ情報のために使用されます。標準エラー出力の内容までOutput:
コメントで検証しようとすると、テストの記述が複雑になったり、意図しないエラーメッセージがテストを失敗させたりする可能性がありました。
コミットメッセージにある "Fixes #4550" は、この問題に関連する内部的な課題追跡システムのエントリを指しています。この変更は、Example
関数のテストがより直感的で、その本来の目的に合致するように、標準出力のみを検証対象とすることで、この問題を解決することを目的としています。これにより、開発者はExample
関数をより簡単に記述し、その出力が期待通りであることを確認できるようになります。
また、このコミットには、go build
およびgo test
コマンドの機能拡張として、データ競合検出、コンパイラ引数指定、ベンチマークのメモリ割り当て統計、ゴルーチンブロッキングプロファイリングといった、より高度な開発・デバッグ支援機能に関するドキュメントの追加も含まれています。これらは、Goアプリケーションのパフォーマンス分析やデバッグ能力を向上させるための重要な機能です。
前提知識の解説
このコミットの理解を深めるために、以下の前提知識が役立ちます。
- Go言語の
testing
パッケージ:- Go言語には、ユニットテスト、ベンチマークテスト、そして
Example
関数をサポートする標準ライブラリtesting
パッケージが組み込まれています。 go test
コマンドは、このパッケージを利用してテストを実行します。
- Go言語には、ユニットテスト、ベンチマークテスト、そして
Example
関数:Example
関数は、func ExampleName()
のような形式で定義され、go test
コマンドによって実行されます。- これらの関数は、コードの利用例を示すために使用され、通常は
fmt.Println
などを用いて標準出力に結果を出力します。 Example
関数の本体の最後のコメント行にOutput:
というプレフィックスを付けて期待される出力を記述することで、go test
は実行時の標準出力とこのコメントの内容を比較し、一致すればテストが成功したとみなします。- 例:
func ExampleHello() { fmt.Println("hello") // Output: hello }
- 標準出力(stdout)と標準エラー出力(stderr):
- ほとんどのオペレーティングシステムにおいて、プログラムはデフォルトで2つの主要な出力ストリームを持っています。
- 標準出力 (stdout): プログラムの通常の出力(結果、情報メッセージなど)が書き込まれる場所です。Goでは
os.Stdout
を通じてアクセスできます。 - 標準エラー出力 (stderr): プログラムのエラーメッセージや診断情報が書き込まれる場所です。Goでは
os.Stderr
を通じてアクセスできます。
- 標準出力 (stdout): プログラムの通常の出力(結果、情報メッセージなど)が書き込まれる場所です。Goでは
- これらは通常、ターミナルやコンソールに表示されますが、ファイルにリダイレクトすることも可能です。
- ほとんどのオペレーティングシステムにおいて、プログラムはデフォルトで2つの主要な出力ストリームを持っています。
os.Pipe()
:- Go言語の
os
パッケージにあるPipe()
関数は、読み取り側と書き込み側の2つの*os.File
を返します。これらはパイプを形成し、一方に書き込まれたデータがもう一方から読み取れるようになります。 - テストフレームワークでは、プログラムの標準出力や標準エラー出力を一時的にこのパイプにリダイレクトすることで、その出力をキャプチャし、後で検証するために使用します。
- Go言語の
go build
およびgo test
コマンドのフラグ:go
コマンドは、ビルド、テスト、ドキュメント生成など、Goプロジェクトを管理するための多機能なツールです。- これらのコマンドには、その動作を制御するための様々なコマンドラインフラグが用意されています。例えば、
-v
は詳細な出力を表示し、-race
はデータ競合検出を有効にします。
技術的詳細
このコミットの技術的な核心は、src/pkg/testing/example.go
ファイル内のRunExamples
関数の変更にあります。この関数は、go test
コマンドがExample
関数を実行する際に呼び出され、その出力をキャプチャして検証する役割を担っています。
変更前は、RunExamples
関数内でos.Stdout
とos.Stderr
の両方が一時的にパイプにリダイレクトされていました。これは、Example
関数が標準出力と標準エラー出力のどちらに書き込んでも、その内容をキャプチャしてOutput:
コメントと比較できるようにするためでした。
// 変更前 (src/pkg/testing/example.go)
stdout, stderr := os.Stdout, os.Stderr
// ...
os.Stdout, os.Stderr = w, w // w はパイプの書き込み側
// ...
os.Stdout, os.Stderr = stdout, stderr // 元に戻す
このコミットでは、この挙動が変更され、os.Stdout
のみがパイプにリダイレクトされるようになりました。os.Stderr
はリダイレクトされず、Example
関数がos.Stderr
に書き込んだ内容は、直接親プロセスの標準エラー出力(通常はコンソール)に流れるようになります。
// 変更後 (src/pkg/testing/example.go)
stdout := os.Stdout // stderr はキャプチャしない
// ...
os.Stdout = w // os.Stderr は変更しない
// ...
os.Stdout = stdout // os.Stderr は変更しない
この変更により、Example
関数のOutput:
コメントは、純粋に標準出力の内容のみを期待するようになります。標準エラー出力に意図しないデバッグメッセージやエラーが出力されても、それがExample
関数のテスト失敗の原因となることはなくなります。これは、Example
関数の目的がコードの「正しい使用例」を示すことであり、エラー処理のデモンストレーションは通常、別のテストケースで行われるべきであるという設計思想に合致しています。
また、src/cmd/go/doc.go
とsrc/cmd/go/test.go
の変更は、この主要なロジック変更に付随するドキュメントの更新と、go
コマンドの機能拡張に関するものです。
src/cmd/go/doc.go
:go build
およびgo test
コマンドに新しいフラグの説明が追加されました。-race
: データ競合検出を有効にします。並行処理におけるバグ(データ競合)を特定するのに役立ちます。-ccflags 'arg list'
: Cコンパイラ(5c, 6c, 8cなど)に渡す引数を指定します。GoプログラムがCコードと連携する場合に有用です。-test.benchmem
: ベンチマーク実行時にメモリ割り当て統計を出力します。パフォーマンスチューニングに役立ちます。-test.blockprofile block.out
: ゴルーチンのブロッキングプロファイルを指定されたファイルに書き込みます。ゴルーチンがブロックされる原因(チャネル操作、ミューテックスなど)を分析するのに役立ちます。-test.blockprofilerate n
: ブロッキングプロファイルのサンプリングレートを制御します。
go list
コマンドの-f
フラグに関する説明が更新され、template
パッケージの構文に加えて、strings.Join
を呼び出す"join"
という追加のテンプレート関数が利用可能になったことが明記されました。- SWIGによって生成される
*.so
ファイルが、go build
によって生成されるファイルタイプとしてリストに追加されました。
src/cmd/go/test.go
:Example
関数の説明が更新され、「*testing.T
を使って成功または失敗を報告する代わりに、os.Stdout
に出力する」という表現に修正されました。これは、os.Stderr
への出力がテストの比較対象から外れたことを反映しています。
これらのドキュメント変更は、Goツールチェーンの進化と、開発者がより高度なデバッグおよびプロファイリングツールを利用できるようにするためのものです。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更は、src/pkg/testing/example.go
ファイルの RunExamples
関数に集中しています。
--- a/src/pkg/testing/example.go
+++ b/src/pkg/testing/example.go
@@ -24,7 +24,7 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
var eg InternalExample
- stdout, stderr := os.Stdout, os.Stderr
+ stdout := os.Stdout
for _, eg = range examples {
matched, err := matchString(*match, eg.Name)
@@ -39,19 +39,19 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
fmt.Printf("=== RUN: %s\\n", eg.Name)
}
- // capture stdout and stderr
+ // capture stdout
r, w, err := os.Pipe()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
- os.Stdout, os.Stderr = w, w
+ os.Stdout = w
outC := make(chan string)
go func() {
buf := new(bytes.Buffer)
_, err := io.Copy(buf, r)
if err != nil {
- fmt.Fprintf(stderr, "testing: copying pipe: %v\\n", err)
+ fmt.Fprintf(os.Stderr, "testing: copying pipe: %v\\n", err)
os.Exit(1)
}
outC <- buf.String()
@@ -62,9 +62,9 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
eg.F()
dt := time.Now().Sub(t0)
- // close pipe, restore stdout/stderr, get output
+ // close pipe, restore stdout, get output
w.Close()
- os.Stdout, os.Stderr = stdout, stderr
+ os.Stdout = stdout
out := <-outC
// report any errors
コアとなるコードの解説
上記のコード変更は、Example
関数の実行時に標準出力と標準エラー出力のどちらをキャプチャするかという、testing
パッケージの基本的な動作を変更しています。
-
stdout, stderr := os.Stdout, os.Stderr
からstdout := os.Stdout
へ:- 変更前は、元の
os.Stdout
とos.Stderr
のファイルディスクリプタを両方とも変数stdout
とstderr
に保存していました。これは、Example
関数の実行後に元の状態に戻すためです。 - 変更後は、
os.Stderr
をキャプチャしないため、元のos.Stderr
を保存する必要がなくなりました。したがって、stdout
のみを保存するように簡略化されています。
- 変更前は、元の
-
os.Stdout, os.Stderr = w, w
からos.Stdout = w
へ:- この行は、
Example
関数が実行される間、プログラムの標準出力と標準エラー出力を、新しく作成されたパイプの書き込み側w
にリダイレクトする部分です。 - 変更前は、
os.Stdout
とos.Stderr
の両方をw
に設定していました。これにより、Example
関数がfmt.Println
(標準出力)やfmt.Fprintln(os.Stderr, ...)
(標準エラー出力)のどちらを使っても、その出力はパイプを通じてキャプチャされていました。 - 変更後は、
os.Stdout
のみをw
に設定しています。os.Stderr
は元の状態のまま(通常はコンソール)に保たれます。これにより、Example
関数がos.Stderr
に書き込んだ内容は、パイプを介さずに直接コンソールに出力されるようになります。
- この行は、
-
fmt.Fprintf(stderr, "testing: copying pipe: %v\\n", err)
からfmt.Fprintf(os.Stderr, "testing: copying pipe: %v\\n", err)
へ:- これは、パイプからのコピー中にエラーが発生した場合に、そのエラーメッセージを標準エラー出力に書き込む部分です。
- 変更前は、ローカル変数
stderr
(元のos.Stderr
を指す)を使用していましたが、stderr
変数が削除されたため、直接グローバルなos.Stderr
を使用するように変更されました。これは、os.Stderr
がキャプチャ対象外となったことと整合しています。
-
os.Stdout, os.Stderr = stdout, stderr
からos.Stdout = stdout
へ:Example
関数の実行が完了した後、標準出力と標準エラー出力を元の状態に戻す部分です。- 変更前は、保存しておいた
stdout
とstderr
の値を両方とも復元していました。 - 変更後は、
os.Stderr
はリダイレクトされていなかったため、os.Stdout
のみを元の値に復元すればよくなりました。
これらの変更により、go test
コマンドがExample
関数を実行する際の出力キャプチャのセマンティクスが明確化され、Output:
コメントは純粋に標準出力の検証に特化されることになります。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/ff5d47ebbaad42862e97e93d46fc89c768c098a3
- Go CL (Code Review): https://golang.org/cl/6973048
参考にした情報源リンク
- Go言語の公式ドキュメント(
testing
パッケージ、go
コマンドなど) - Go言語のソースコード(特に
src/pkg/testing/example.go
、src/cmd/go/doc.go
、src/cmd/go/test.go
)