[インデックス 12335] ファイルの概要
このコミットは、Go言語の標準ライブラリnetパッケージ内のテストコードに対する修正です。具体的には、ネットワークタイムアウトのテストにおいて、testing.Short()フラグが有効な(つまり、短時間で実行されるべき)テストの場合に、期待よりも長いタイムアウトを許容しないようにする変更が加えられています。これにより、テストの実行効率が向上し、不要な待機時間が削減されます。
コミット
- コミットハッシュ:
a3caf073a584a17eb0a7a17e1ce2ec014bae9a5c - Author: Shenghou Ma minux.ma@gmail.com
- Date: Sat Mar 3 00:50:18 2012 +0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a3caf073a584a17eb0a7a17e1ce2ec014bae9a5c
元コミット内容
net: during short test, don't bother timeout longer than expected
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5716053
変更の背景
この変更の背景には、Go言語のテストフレームワークにおけるtesting.Short()フラグの利用と、ネットワーク関連のテストにおけるタイムアウト処理の効率化があります。
Goのテストでは、go test -shortコマンドを使用することで、実行時間の短いテストのみを実行するモードに切り替えることができます。これは、CI/CDパイプラインでの高速なフィードバックや、開発者がローカルで素早くテストを実行したい場合に非常に有用です。
src/pkg/net/timeout_test.goは、ネットワーク操作におけるタイムアウトが正しく機能するかを検証するためのテストファイルです。これらのテストは、意図的に一定時間(例: 0.1秒)のタイムアウトを発生させ、その時間が期待通りであることを確認します。
しかし、testing.Short()モードで実行される場合、テストの目的は「タイムアウトが機能すること」の基本的な確認であり、「タイムアウトの正確な時間」を厳密に検証することではありません。元のコードでは、dt > 250*time.Millisecondという条件で、タイムアウト時間が期待値(0.1秒)よりも大幅に長い場合にエラーとしていました。これは通常モードでは問題ありませんが、testing.Short()モードでは、システム負荷や環境要因によってわずかにタイムアウト時間が長引いたとしても、それがテストの失敗につながる可能性があります。
このコミットは、testing.Short()モードの場合には、タイムアウトが期待よりも長かったとしても、それをエラーとしないようにすることで、テストの安定性を向上させ、不要なテスト失敗を避けることを目的としています。これにより、go test -shortの実行がより信頼性の高いものになります。
前提知識の解説
Go言語のtestingパッケージとtesting.Short()
Go言語には、標準で強力なテストフレームワークが組み込まれています。testingパッケージは、ユニットテスト、ベンチマークテスト、例(Example)テストなどを記述するための機能を提供します。
- テスト関数:
func TestXxx(t *testing.T)という形式で定義され、go testコマンドで実行されます。 *testing.T: テストの状態を管理し、テストの失敗を報告したり、ログを出力したりするためのメソッドを提供します。testing.Short(): この関数は、go test -shortコマンドが指定された場合にtrueを返します。開発者はこのフラグを利用して、テストの実行時間を短縮するためのロジックをテストコード内に組み込むことができます。例えば、時間のかかるネットワークテストやファイルI/Oテストをtesting.Short()がtrueの場合はスキップする、あるいは簡略化するといった用途で使われます。
Go言語のnetパッケージとネットワークタイムアウト
netパッケージは、TCP/IP、UDP、IP、UnixドメインソケットなどのネットワークI/Oプリミティブを提供します。ネットワークアプリケーションを構築する上で非常に重要なパッケージです。
- ネットワーク接続:
net.Dialなどの関数を使用して、リモートホストへの接続を確立します。 - タイムアウト: ネットワーク操作(接続、読み込み、書き込みなど)は、相手の応答がない場合に無限に待機してしまう可能性があります。これを防ぐために、タイムアウトを設定することが一般的です。Goの
netパッケージでは、SetReadDeadlineやSetWriteDeadlineなどのメソッドを使用して、特定の時間内に操作が完了しない場合にエラーを返すように設定できます。 time.Duration: Goのtimeパッケージで提供される型で、時間の長さを表します。ミリ秒(time.Millisecond)や秒(time.Second)などの単位で時間の長さを表現できます。
t1.Sub(t0)と時間の計測
Go言語のtimeパッケージには、時間の計測に便利な機能が多数あります。
time.Now(): 現在の時刻をtime.Time型で返します。time.Time.Sub(u time.Time):time.Time型のメソッドで、別のtime.Time型の値uとの差をtime.Duration型で返します。このコミットでは、t1.Sub(t0)によって、ネットワーク操作にかかった実際の時間(dt)を計測しています。
技術的詳細
このコミットの技術的な核心は、testing.Short()フラグを利用して、テストの振る舞いを条件分岐させている点にあります。
元のコードでは、ネットワーク操作にかかった時間dtが50*time.Millisecond未満であるか、または250*time.Millisecondを超えている場合にエラーとしていました。これは、タイムアウトが約0.1秒(100ミリ秒)であることを期待しているため、その前後で許容範囲を設けていると考えられます。
// 変更前
if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 250*time.Millisecond {
errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
return
}
変更後のコードでは、dt > 250*time.Millisecondの条件に!testing.Short()が追加されています。
// 変更後
if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond {
errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
return
}
この論理式を分解すると以下のようになります。
dt < 50*time.Millisecond: これは変更されていません。タイムアウトが期待よりも短すぎる場合は、常にエラーとなります。これは、タイムアウトが全く機能していない可能性を示唆するため、shortモードであっても重要なチェックです。!testing.Short() && dt > 250*time.Millisecond:!testing.Short():go test -shortが指定されていない場合(つまり、フルテストモードの場合)にtrueとなります。dt > 250*time.Millisecond: タイムアウトが期待よりも長すぎる場合です。
したがって、この条件全体は「go test -shortが指定されておらず、かつタイムアウトが250ミリ秒を超えている場合」にtrueとなります。
これにより、以下の挙動が実現されます。
- 通常モード (
go test):!testing.Short()がtrueになるため、dt < 50*time.Millisecond || dt > 250*time.Millisecondという元の条件がそのまま適用されます。つまり、タイムアウトが短すぎても長すぎてもエラーになります。 - ショートモード (
go test -short):!testing.Short()がfalseになるため、!testing.Short() && dt > 250*time.Millisecondの部分全体がfalseになります。結果として、dt < 50*time.Millisecondの条件のみが評価されます。つまり、タイムアウトが短すぎる場合のみエラーとなり、長すぎる場合はエラーとはなりません。
この変更は、testing.Short()の意図、すなわち「高速なテスト実行」を尊重しつつ、テストの基本的な健全性チェック(タイムアウトが全く発生しないなど)は維持するという、バランスの取れたアプローチを示しています。
コアとなるコードの変更箇所
--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -40,7 +40,7 @@ func testTimeout(t *testing.T, network, addr string, readFrom bool) {
errc <- fmt.Errorf("fd.%s on %s %s did not return 0, timeout: %v, %v", what, network, addr, n, err1)
return
}
- if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 250*time.Millisecond {
+ if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond {
errc <- fmt.Errorf("fd.%s on %s %s took %s, expected 0.1s", what, network, addr, dt)
return
}
コアとなるコードの解説
変更はsrc/pkg/net/timeout_test.goファイルの1行のみです。
元の行:
if dt := t1.Sub(t0); dt < 50*time.Millisecond || dt > 250*time.Millisecond {
変更後の行:
if dt := t1.Sub(t0); dt < 50*time.Millisecond || !testing.Short() && dt > 250*time.Millisecond {
この変更は、dt > 250*time.Millisecondという条件の前に!testing.Short() &&を追加しています。
dt := t1.Sub(t0): ネットワーク操作にかかった時間(dt)を計測し、その場でdt変数に代入しています。dt < 50*time.Millisecond: タイムアウト時間が50ミリ秒未満の場合。これは、タイムアウトが期待よりも早く発生した、あるいは全く発生しなかった可能性を示唆します。この条件はtesting.Short()の有無にかかわらず常に評価されます。||: 論理OR演算子。左辺または右辺のどちらかがtrueであれば、全体がtrueになります。!testing.Short():testing.Short()がfalseの場合(つまり、go test -shortが指定されていない場合)にtrueとなります。&&: 論理AND演算子。左辺と右辺の両方がtrueの場合に全体がtrueになります。dt > 250*time.Millisecond: タイムアウト時間が250ミリ秒を超過した場合。これは、タイムアウトが期待よりも遅く発生したことを示します。
この修正により、go test -short実行時には、タイムアウトが250ミリ秒を超過してもテストは失敗しなくなります。これにより、テスト環境のわずかな変動による誤った失敗が減り、ショートテストの信頼性と実用性が向上します。
関連リンク
- Go言語の
testingパッケージに関する公式ドキュメント: https://pkg.go.dev/testing - Go言語の
netパッケージに関する公式ドキュメント: https://pkg.go.dev/net - Go言語の
timeパッケージに関する公式ドキュメント: https://pkg.go.dev/time
参考にした情報源リンク
- Go言語の公式ドキュメント (上記リンク)
- Go言語のテストに関する一般的な情報源 (例: Go by Example: Testing, A Tour of Go)
testing.Short()の利用例に関するGoコミュニティの議論やブログ記事 (具体的なURLは検索結果によるため省略)- Goのソースコードリポジトリ (GitHub)
- Goのコードレビューシステム (Gerrit) のCL (Change List) 5716053: https://golang.org/cl/5716053 (コミットメッセージに記載されているリンク)