[インデックス 14311] ファイルの概要
このコミットは、Go言語のnet
パッケージにおけるTestReadWriteDeadline
テストの信頼性を向上させるためのものです。具体的には、テストが短時間で実行される(testing.Short()
がtrueの場合)際に、タイムアウトの上限値の検証を行わないように変更し、タイムアウトの許容範囲(デルタ)を拡大しています。さらに、Plan 9オペレーティングシステム上ではこのテストを無効にすることで、プラットフォーム固有の問題によるテストの失敗を防いでいます。
コミット
- コミットハッシュ:
a906f9aa86c4305a1247391244e52b17e555f723
- Author: Alex Brainman alex.brainman@gmail.com
- Date: Sun Nov 4 12:41:49 2012 +1100
- コミットメッセージ:
net: do not test TestReadWriteDeadline timeout upper bound during short test It also increases timeout deltas to allow for longer wait. Also disables this test on plan9. R=golang-dev, minux.ma CC=golang-dev https://golang.org/cl/6821062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a906f9aa86c4305a1247391244e52b17e555f723
元コミット内容
net: do not test TestReadWriteDeadline timeout upper bound during short test
It also increases timeout deltas to allow for longer wait.
Also disables this test on plan9.
R=golang-dev, minux.ma
CC=golang-dev
https://golang.org/cl/6821062
変更の背景
このコミットの背景には、TestReadWriteDeadline
テストが特定の条件下で不安定であったという問題があります。具体的には、以下の点が挙げられます。
-
testing.Short()
モードでのタイムアウト上限値の検証の厳しさ: Goのテストフレームワークには、テストを高速に実行するための「ショートモード」が存在します。このモードでは、時間のかかるテストやネットワークに依存するテストの一部がスキップされたり、より緩い条件で実行されたりすることがあります。TestReadWriteDeadline
はネットワークI/Oのタイムアウトをテストするため、環境によっては正確なタイムアウト時間を保証するのが難しい場合があります。特に、ショートモードではテスト環境のリソースが限られている場合や、システム負荷が高い場合に、期待されるタイムアウト時間よりもわずかに長くかかってしまい、テストが失敗する可能性がありました。このコミットは、ショートモードではタイムアウトの上限値の検証を緩和することで、このような不安定性を解消しようとしています。 -
タイムアウトの許容範囲(デルタ)の不足: ネットワークI/Oのタイムアウトテストは、厳密な時間計測を伴うため、システムの状態(CPU負荷、ネットワーク遅延など)によって結果が変動しやすい性質があります。以前の
delta
値(許容誤差)が小さすぎたため、わずかな遅延でもテストが失敗する原因となっていました。これを拡大することで、テストの堅牢性を高めています。 -
Plan 9固有の問題: Plan 9オペレーティングシステムは、Goがサポートするプラットフォームの一つですが、そのI/Oキャンセルメカニズムが他のOS(Linux, Windowsなど)と異なる場合があります。
TestReadWriteDeadline
はI/Oのキャンセル機能に依存しているため、Plan 9環境ではテストが正しく動作しない、あるいは常に失敗する可能性がありました。このコミットは、Plan 9上でのテストを完全に無効にすることで、このプラットフォーム固有の互換性の問題を回避しています。
これらの変更は、テストの信頼性を高め、CI/CDパイプラインでの不必要な失敗を減らすことを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびネットワークプログラミングに関する知識が必要です。
-
Go言語の
net
パッケージ:- Goの標準ライブラリの一部で、TCP/IP、UDP、UnixドメインソケットなどのネットワークI/O機能を提供します。
net.Conn
インターフェースは、ネットワーク接続の一般的な振る舞いを定義しており、Read
、Write
、Close
メソッドの他に、SetReadDeadline
、SetWriteDeadline
、SetDeadline
といったタイムアウト設定のためのメソッドを提供します。- これらの
Set*Deadline
メソッドは、指定された時刻までにI/O操作が完了しない場合にエラー(通常はnet.OpError
で、Timeout()
メソッドがtrueを返す)を返すように設定します。
-
testing
パッケージとtesting.Short()
:- Goの標準テストフレームワークです。
testing.Short()
関数は、テストが「ショートモード」で実行されているかどうかを返します。ショートモードは、go test -short
コマンドで有効にできます。- 開発者がCI環境や日常的な開発で高速にテストを実行したい場合に、時間のかかるテスト(例: ネットワークテスト、ファイルI/Oテスト、ベンチマーク)の一部をスキップしたり、簡略化したりするために使用されます。
-
runtime.GOOS
:- Goの標準ライブラリ
runtime
パッケージの一部で、プログラムが実行されているオペレーティングシステムの名前(例: "linux", "windows", "darwin", "plan9"など)を文字列で返します。 - これを利用して、特定のOSに依存するコードやテストの挙動を条件分岐させることができます。
- Goの標準ライブラリ
-
time.Duration
:- Goの
time
パッケージで定義されている型で、時間の長さを表します。ナノ秒単位で内部的に表現されます。 time.Millisecond
、time.Second
などの定数を使って、人間が読みやすい形で時間を指定できます。
- Goの
-
I/Oキャンセル:
- ネットワークソケットなどのI/O操作中に、その操作を途中で中断するメカニズムです。
SetReadDeadline
やSetWriteDeadline
を設定すると、指定されたデッドラインまでにデータが読み書きされない場合、Goランタイムは内部的にI/O操作をキャンセルし、タイムアウトエラーを返します。このキャンセルメカニズムはOSの機能に依存します。
-
Plan 9:
- ベル研究所で開発された分散オペレーティングシステムです。Go言語の開発者の一部がPlan 9の設計思想に影響を受けており、GoはPlan 9を公式にサポートする数少ない主要言語の一つです。
- Plan 9のファイルシステムとI/Oモデルは、Unix系OSとは異なる特徴を持つため、特定のシステムコールやI/O操作の挙動が異なることがあります。
技術的詳細
このコミットは、主にsrc/pkg/net/timeout_test.go
とsrc/pkg/net/ipsock_plan9.go
の2つのファイルに変更を加えています。
src/pkg/net/ipsock_plan9.go
の変更
このファイルには、Plan 9環境でのネットワークソケットに関する実装が含まれています。
var canCancelIO = true // used for testing current package
の追加:- これは、
net
パッケージのテスト(特にtimeout_test.go
)が、現在のシステムでI/Oキャンセルが可能かどうかを判断するために使用するグローバル変数です。 - Plan 9では、この変数を
true
として初期化していますが、これはテストのコンテキストで利用されるものです。後述のtimeout_test.go
での変更と合わせて、Plan 9でのテストの挙動を制御します。 - この変数は、テストが実行される環境でI/Oキャンセルがサポートされているかどうかのフラグとして機能します。
- これは、
src/pkg/net/timeout_test.go
の変更
このファイルは、ネットワークI/Oのタイムアウト機能をテストするためのものです。
-
Plan 9でのテストのスキップ:
switch runtime.GOOS { case "plan9": t.Logf("skipping test on %q", runtime.GOOS) return }
TestReadWriteDeadline
関数の冒頭に、runtime.GOOS
が"plan9"である場合にテストをスキップするロジックが追加されました。- これにより、Plan 9環境でのI/Oキャンセルメカニズムの差異に起因するテストの失敗を根本的に回避します。
t.Logf
でスキップ理由をログに出力し、return
でテスト関数を終了します。
-
readTimeout
とwriteTimeout
の調整:- readTimeout = 100 * time.Millisecond - writeTimeout = 200 * time.Millisecond + readTimeout = 50 * time.Millisecond + writeTimeout = 250 * time.Millisecond
readTimeout
が100msから50msに短縮され、writeTimeout
が200msから250msに延長されました。- これは、テストの実行時間を最適化しつつ、より現実的なタイムアウトシナリオをカバーするための調整と考えられます。特に
readTimeout
の短縮は、テストがより早くタイムアウト条件に到達するように意図されている可能性があります。
-
delta
(許容誤差)の変更とtesting.Short()
の利用:- delta = 40 * time.Millisecond + if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
- 以前は固定の
delta
変数(40ms)を使用していましたが、このコミットではcheckTimeout
関数内の条件式が直接変更されました。 - 新しい条件式は以下の2つの部分から構成されます。
d < -30*time.Millisecond
: 実際のタイムアウト時間が期待値よりも30ms以上短い場合にエラーとします。これは、タイムアウトが早すぎる場合を検出します。!testing.Short() && 150*time.Millisecond < d
:!testing.Short()
: テストがショートモードで実行されていない場合(つまり、フルテストモードの場合)にのみ、この条件が適用されます。150*time.Millisecond < d
: 実際のタイムアウト時間が期待値よりも150ms以上長い場合にエラーとします。- この変更の最も重要な点は、ショートモード(
testing.Short()
がtrueの場合)では、タイムアウトの上限値(150msより長い遅延)の検証を行わないようにしたことです。これにより、ショートモードでのテストの不安定性が解消されます。フルテストモードでは、より厳密なタイムアウトの検証が維持されます。
- 以前は固定の
これらの変更により、TestReadWriteDeadline
はより堅牢になり、異なる実行環境やテストモードでの信頼性が向上しました。
コアとなるコードの変更箇所
src/pkg/net/ipsock_plan9.go
--- a/src/pkg/net/ipsock_plan9.go
+++ b/src/pkg/net/ipsock_plan9.go
@@ -24,6 +24,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
return false, false
}
+var canCancelIO = true // used for testing current package
+
// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).\
func parsePlan9Addr(s string) (ip IP, iport int, err error) {
addr := IPv4zero // address contains port only
src/pkg/net/timeout_test.go
--- a/src/pkg/net/timeout_test.go
+++ b/src/pkg/net/timeout_test.go
@@ -148,19 +148,24 @@ func TestTimeoutAccept(t *testing.T) {
}
func TestReadWriteDeadline(t *testing.T) {
+\tswitch runtime.GOOS {\n+\tcase "plan9":\n+\t\tt.Logf("skipping test on %q", runtime.GOOS)\n+\t\treturn\n+\t}\n+\n if !canCancelIO {
t.Logf("skipping test on this system")
return
}
const (
- readTimeout = 100 * time.Millisecond
- writeTimeout = 200 * time.Millisecond
- delta = 40 * time.Millisecond
+ readTimeout = 50 * time.Millisecond
+ writeTimeout = 250 * time.Millisecond
)
checkTimeout := func(command string, start time.Time, should time.Duration) {
is := time.Now().Sub(start)
d := should - is
- if d < -delta || delta < d {\n+ if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {\n t.Errorf("%s timeout test failed: is=%v should=%v\\n", command, is, should)\n }\n }
コアとなるコードの解説
src/pkg/net/ipsock_plan9.go
の変更点
var canCancelIO = true
の追加:- この変数は、
net
パッケージのテストがI/Oキャンセル機能の有無を判断するために使用されます。Plan 9環境では、この変数をtrue
とすることで、テストがI/Oキャンセルを試みることを許可します。しかし、これはあくまでテスト内部でのフラグであり、実際のPlan 9のI/Oキャンセル挙動が他のOSと異なるため、timeout_test.go
でPlan 9でのテスト自体をスキップする措置が取られています。この変数は、主にテストの条件分岐を簡潔にするためのものです。
- この変数は、
src/pkg/net/timeout_test.go
の変更点
-
Plan 9でのテストスキップ:
switch runtime.GOOS { case "plan9": t.Logf("skipping test on %q", runtime.GOOS) return }
runtime.GOOS
を使用して現在のOSを判定し、もしPlan 9であれば、TestReadWriteDeadline
テスト全体をスキップします。これは、Plan 9のI/Oキャンセルメカニズムが他のOSと異なり、このテストが意図した通りに動作しない、あるいは常に失敗する可能性があったためです。これにより、Plan 9環境でのテストの信頼性が向上します。
-
readTimeout
とwriteTimeout
の調整:readTimeout = 50 * time.Millisecond writeTimeout = 250 * time.Millisecond
readTimeout
が短縮され、writeTimeout
が延長されました。これは、テストの実行時間を最適化しつつ、より現実的なタイムアウトシナリオをカバーするための調整です。特にreadTimeout
の短縮は、テストがより早くタイムアウト条件に到達するように意図されている可能性があります。
-
checkTimeout
関数内のタイムアウト検証ロジックの変更:if d < -30*time.Millisecond || !testing.Short() && 150*time.Millisecond < d {
- この行がこのコミットの最も重要な変更点です。
d
は、実際のタイムアウト時間と期待されるタイムアウト時間の差(should - is
)を表します。d < -30*time.Millisecond
: 実際のタイムアウトが期待値よりも30ミリ秒以上短い場合(つまり、タイムアウトが早すぎる場合)はエラーとします。これは、タイムアウトが意図せず早く発生した場合を捕捉します。!testing.Short() && 150*time.Millisecond < d
:!testing.Short()
: この条件は、テストがショートモードで実行されていない場合にのみ評価されます。150*time.Millisecond < d
: 実際のタイムアウトが期待値よりも150ミリ秒以上長い場合(つまり、タイムアウトが遅すぎる場合)はエラーとします。- この組み合わせにより、ショートモードでテストを実行している場合(
testing.Short()
がtrueの場合)は、タイムアウトが遅すぎる場合の上限値チェック(150*time.Millisecond < d
)がスキップされます。これにより、ショートモードでのテストの不安定性が解消され、テストがより堅牢になります。フルテストモードでは、引き続き厳密なタイムアウトの検証が行われます。
これらの変更は、Goのnet
パッケージのタイムアウトテストが、様々な環境やテストモードでより信頼性高く動作するようにするための重要な改善です。
関連リンク
- Go言語の
net
パッケージに関する公式ドキュメント: https://pkg.go.dev/net - Go言語の
testing
パッケージに関する公式ドキュメント: https://pkg.go.dev/testing - Go言語の
runtime
パッケージに関する公式ドキュメント: https://pkg.go.dev/runtime - Go言語の
time
パッケージに関する公式ドキュメント: https://pkg.go.dev/time
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/14311.txt
- Go言語の公式ドキュメント (pkg.go.dev)
- Go言語のソースコード (GitHub)
- 一般的なネットワークプログラミングとテストの知識