[インデックス 14967] ファイルの概要
このコミットは、Go言語の標準ライブラリ time
パッケージ内の sleep_test.go
ファイルに対する変更です。具体的には、TestReset
というテスト関数の信頼性を向上させることを目的としています。
コミット
- コミットハッシュ:
86a8d59a014287c899a14ef9ed6fdfb7d1b8d586
- 作者: Brad Fitzpatrick bradfitz@golang.org
- コミット日時: 2013年1月22日 火曜日 17:25:58 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/86a8d59a014287c899a14ef9ed6fdfb7d1b8d586
元コミット内容
time: make TestReset more reliable
Fixes #4690
R=golang-dev, alex.brainman, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/7181052
変更の背景
このコミットの背景には、Go言語の time
パッケージにおける Timer.Reset
メソッドのテスト TestReset
が、特定の環境下で不安定(flaky)になるという問題がありました。コミットメッセージにある Fixes #4690
は、この問題がGoのIssueトラッカーで報告されていたことを示しています。
不安定なテスト(flaky test)とは、コードの変更がないにもかかわらず、実行するたびに成功したり失敗したりするテストのことです。このようなテストは、CI/CDパイプラインの信頼性を損ない、開発者が実際のバグとテストの不安定さを区別するのを困難にします。time
パッケージのテストは、時間的な要素に依存するため、システム負荷、CPUスケジューリング、タイマーの精度など、環境要因によって結果が変動しやすい傾向があります。
元の TestReset
は、固定されたミリ秒単位のDuration(例: 100 * Millisecond
)を使用してタイマーをリセットし、その動作を検証していました。しかし、これは実行環境のパフォーマンスやタイマーの粒度によっては、期待通りのタイミングでタイマーが発火しない、あるいは発火しすぎるなどの問題を引き起こす可能性がありました。特に、低速なハードウェアや高負荷なシステムでは、テストが意図した時間間隔で実行されず、誤って失敗するケースが考えられます。
このコミットは、このような環境依存の不安定さを解消し、TestReset
がより堅牢に、かつ様々な実行環境で安定して動作するように改善することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とテストに関する知識が必要です。
-
time
パッケージ: Go言語の標準ライブラリで、時間に関する機能(時刻、期間、タイマー、Tickerなど)を提供します。time.Duration
: 時間の長さを表す型です。ミリ秒、秒、分などの単位で期間を指定できます。time.NewTimer(d Duration)
: 指定された期間d
が経過した後に、現在の時刻をチャネルC
に送信する新しいTimer
を作成します。タイマーは一度だけ発火します。Timer.C
:Timer
が発火したときに時刻が送信される読み取り専用チャネルです。Timer.Reset(d Duration)
: タイマーをリセットし、新しい期間d
が経過した後に発火するように設定します。Reset
メソッドはbool
値を返します。true
を返す場合: タイマーがまだ発火しておらず、チャネルC
に値が送信されていない状態でリセットに成功したことを意味します。false
を返す場合: タイマーが既に発火しているか、チャネルC
から値が読み取られた後にリセットされたことを意味します。この場合、Reset
は新しいタイマーを作成するのと同等に動作します。
time.Sleep(d Duration)
: 指定された期間d
だけ現在のゴルーチンをスリープさせます。
-
select
ステートメント: 複数のチャネル操作を待機するために使用されます。case <-t0.C:
:t0.C
チャネルから値が受信できるまで待機します。default:
:select
ブロック内のどのチャネル操作もすぐに実行できない場合に実行されます。これにより、チャネル操作がブロックされるのを防ぎ、非ブロック的なチャネルの読み取りや書き込みが可能になります。
-
Go言語のテスト:
testing
パッケージを使用してテストを記述します。func TestXxx(t *testing.T)
: テスト関数はTest
で始まり、*testing.T
型の引数を取ります。t.Fatalf(...)
: テストを失敗させ、メッセージを出力してテストの実行を停止します。t.Error(...)
: テストを失敗させ、メッセージを出力しますが、テストの実行は継続します。t.Logf(...)
: テストのログにメッセージを出力します。テストが成功した場合でも出力されます。
-
Flaky Test (不安定なテスト): 前述の通り、実行するたびに結果が変わるテストのことです。時間依存のテスト、並行処理のテスト、外部サービスに依存するテストなどで発生しやすいです。これを解決するためには、テストのロジックをより堅牢にするか、テスト環境の変動を吸収するメカニズムを導入する必要があります。
技術的詳細
元の TestReset
関数は、固定された Duration
値(例: 100 * Millisecond
)を使用してタイマーの動作を検証していました。このアプローチは、システムクロックの粒度やスケジューリングの変動に対して脆弱でした。例えば、Sleep(50 * Millisecond)
の後に Reset(150 * Millisecond)
を呼び出し、その後 Sleep(100 * Millisecond)
を行うというシーケンスでは、厳密なタイミングが要求されます。もし Sleep
がわずかに長くかかったり、タイマーの発火が遅れたりすると、テストが意図せず失敗する可能性がありました。
新しいアプローチでは、この不安定さを解消するために以下の変更が導入されました。
-
testReset
ヘルパー関数の導入:- 元の
TestReset
のロジックをtestReset(d Duration) error
というヘルパー関数に切り出しました。 - このヘルパー関数は、テストで使用する基本となる時間単位
d
を引数として受け取ります。 - タイマーの期間やスリープ時間は、この
d
の倍数として定義されます(例:2 * d
,3 * d
)。これにより、テストの実行時間を柔軟に調整できるようになります。 - テストが失敗した場合、
t.Fatalf
の代わりにerrors.New
を使用してエラーを返します。これにより、呼び出し元のTestReset
関数がエラーを捕捉し、再試行のロジックを実装できるようになります。
- 元の
-
複数回の試行による堅牢化:
- 新しい
TestReset
関数は、testReset
ヘルパー関数を異なるDuration
値で複数回試行するロジックを導入しました。 tries := []Duration{1 * unit, 3 * unit, 7 * unit, 15 * unit}
という配列が定義されています。unit
は25 * Millisecond
です。- テストは、最も短い
Duration
(25ms
) から順にtestReset
を実行します。 - もし
testReset
がエラーなく成功した場合、そのDuration
でテストがパスしたと判断し、それ以上の試行は行いません。 - これにより、高速なマシンでは短い
Duration
でテストがすぐに完了し、低速なマシンや高負荷な環境ではより長いDuration
を試すことで、テストが安定してパスする可能性が高まります。これは、テストが「遅い、負荷の高いハードウェアでも不安定にならないように、しかし高速なマシンでは不必要に遅くならないように」という設計思想に基づいています。 - すべての
Duration
で試行してもテストが成功しなかった場合のみ、最終的にt.Error(err)
を呼び出してテストを失敗させます。
- 新しい
この変更により、TestReset
は環境の変動に対してより耐性を持つようになり、Goのテストスイート全体の信頼性向上に貢献しています。
コアとなるコードの変更箇所
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -247,26 +247,49 @@ func TestSleepZeroDeadlock(t *testing.T) {
<-c
}
-func TestReset(t *testing.T) {
- t0 := NewTimer(100 * Millisecond)
- Sleep(50 * Millisecond)
- if t0.Reset(150*Millisecond) != true {
- t.Fatalf("resetting unfired timer returned false")
+func testReset(d Duration) error {
+ t0 := NewTimer(2 * d)
+ Sleep(d)
+ if t0.Reset(3*d) != true {
+ return errors.New("resetting unfired timer returned false")
}
- Sleep(100 * Millisecond)
+ Sleep(2 * d)
select {
case <-t0.C:
- t.Fatalf("timer fired early")
+ return errors.New("timer fired early")
default:
}
- Sleep(100 * Millisecond)
+ Sleep(2 * d)
select {
case <-t0.C:
default:
- t.Fatalf("reset timer did not fire")
+ return errors.New("reset timer did not fire")
}
if t0.Reset(50*Millisecond) != false {
- t.Fatalf("resetting expired timer returned true")
+ return errors.New("resetting expired timer returned true")
+ }
+ return nil
+}
+
+func TestReset(t *testing.T) {
+ // We try to run this test with increasingly larger multiples
+ // until one works so slow, loaded hardware isn't as flaky,
+ // but without slowing down fast machines unnecessarily.
+ const unit = 25 * Millisecond
+ tries := []Duration{
+ 1 * unit,
+ 3 * unit,
+ 7 * unit,
+ 15 * unit,
+ }
+ var err error
+ for _, d := range tries {
+ err = testReset(d)
+ if err == nil {
+ t.Logf("passed using duration %v", d)
+ return
+ }
}
+ t.Error(err)
}
コアとなるコードの解説
testReset(d Duration) error
関数
この関数は、TestReset
の主要なテストロジックをカプセル化しています。引数 d
は、テストで使用される時間単位の基準となります。
t0 := NewTimer(2 * d)
:2 * d
の期間で新しいタイマーt0
を作成します。Sleep(d)
:d
の期間だけスリープします。この時点でタイマーはまだ発火していないはずです。if t0.Reset(3*d) != true
: タイマーを3 * d
の期間でリセットします。タイマーがまだ発火していないため、Reset
はtrue
を返すはずです。もしfalse
を返した場合、エラーを返します。Sleep(2 * d)
: さらに2 * d
だけスリープします。この時点で、リセットされたタイマーはまだ発火していないはずです(合計d + 2*d = 3*d
のスリープで、リセット後の期間3*d
に達する)。select { case <-t0.C: ... default: }
:case <-t0.C:
: もしタイマーがこの時点で発火していたら(つまり、3*d
よりも早く発火したら)、"timer fired early"
というエラーを返します。default:
: タイマーがまだ発火していないことを確認します。
Sleep(2 * d)
: さらに2 * d
だけスリープします。この時点で、リセットされたタイマーは発火しているはずです(合計d + 2*d + 2*d = 5*d
のスリープで、リセット後の期間3*d
を超えている)。select { case <-t0.C: ... default: }
:case <-t0.C:
: タイマーが発火したことを確認します。default:
: もしタイマーが発火していなかったら、"reset timer did not fire"
というエラーを返します。
if t0.Reset(50*Millisecond) != false
: 既に発火したタイマーをリセットしようとします。この場合、Reset
はfalse
を返すはずです。もしtrue
を返した場合、"resetting expired timer returned true"
というエラーを返します。return nil
: すべてのチェックが成功した場合、nil
を返して成功を示します。
TestReset(t *testing.T)
関数
この関数は、testReset
ヘルパー関数を呼び出し、テストの堅牢性を高めるためのロジックを含んでいます。
const unit = 25 * Millisecond
: 基本となる時間単位を25ミリ秒
と定義します。tries := []Duration{1 * unit, 3 * unit, 7 * unit, 15 * unit}
:testReset
を試行する異なる時間単位の倍数を定義します。これにより、25ms
,75ms
,175ms
,375ms
の順でテストが試行されます。var err error
:testReset
から返されるエラーを保持するための変数です。for _, d := range tries
:tries
配列の各Duration
d
に対してループを実行します。err = testReset(d)
:testReset
ヘルパー関数を現在のd
で呼び出します。if err == nil
: もしtestReset
がエラーなく成功した場合(nil
を返した場合)、t.Logf("passed using duration %v", d)
: どのDuration
でテストがパスしたかをログに出力します。return
: テストを終了し、成功とします。
t.Error(err)
: ループが終了してもtestReset
が一度も成功しなかった場合(つまり、すべてのDuration
でエラーが返された場合)、最後に発生したエラーをt.Error
で出力し、テストを失敗とします。
この構造により、テストはまず短い時間間隔で迅速に実行を試み、もしそれが不安定な環境で失敗した場合には、より長い時間間隔で再試行することで、テストの信頼性を大幅に向上させています。
関連リンク
- Go Issue #4690: https://code.google.com/p/go/issues/detail?id=4690 (古いGoogle Codeのリンクですが、コミットメッセージに記載されています)
- Gerrit Change-Id:
https://golang.org/cl/7181052
(GoのコードレビューシステムGerritのリンク)
参考にした情報源リンク
- Go言語の
time
パッケージに関する公式ドキュメント: https://pkg.go.dev/time - Go言語の
testing
パッケージに関する公式ドキュメント: https://pkg.go.dev/testing - Go言語の
select
ステートメントに関する公式ドキュメント: https://go.dev/tour/concurrency/5 - Flaky Testに関する一般的な情報 (例: Martin Fowlerの記事など)
[インデックス 14967] ファイルの概要
このコミットは、Go言語の標準ライブラリ time
パッケージ内の sleep_test.go
ファイルに対する変更です。具体的には、TestReset
というテスト関数の信頼性を向上させることを目的としています。
コミット
- コミットハッシュ:
86a8d59a014287c899a14ef9ed6fdfb7d1b8d586
- 作者: Brad Fitzpatrick bradfitz@golang.org
- コミット日時: 2013年1月22日 火曜日 17:25:58 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/86a8d59a014287c899a14ef9ed6fdfb7d1b8d586
元コミット内容
time: make TestReset more reliable
Fixes #4690
R=golang-dev, alex.brainman, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/7181052
変更の背景
このコミットの背景には、Go言語の time
パッケージにおける Timer.Reset
メソッドのテスト TestReset
が、特定の環境下で不安定(flaky)になるという問題がありました。コミットメッセージにある Fixes #4690
は、この問題がGoのIssueトラッカーで報告されていたことを示しています。
不安定なテスト(flaky test)とは、コードの変更がないにもかかわらず、実行するたびに成功したり失敗したりするテストのことです。このようなテストは、CI/CDパイプラインの信頼性を損ない、開発者が実際のバグとテストの不安定さを区別するのを困難にします。time
パッケージのテストは、時間的な要素に依存するため、システム負荷、CPUスケジューリング、タイマーの精度など、環境要因によって結果が変動しやすい傾向があります。
元の TestReset
は、固定されたミリ秒単位のDuration(例: 100 * Millisecond
)を使用してタイマーをリセットし、その動作を検証していました。しかし、これは実行環境のパフォーマンスやタイマーの粒度によっては、期待通りのタイミングでタイマーが発火しない、あるいは発火しすぎるなどの問題を引き起こす可能性がありました。特に、低速なハードウェアや高負荷なシステムでは、テストが意図した時間間隔で実行されず、誤って失敗するケースが考えられます。
このコミットは、このような環境依存の不安定さを解消し、TestReset
がより堅牢に、かつ様々な実行環境で安定して動作するように改善することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とテストに関する知識が必要です。
-
time
パッケージ: Go言語の標準ライブラリで、時間に関する機能(時刻、期間、タイマー、Tickerなど)を提供します。time.Duration
: 時間の長さを表す型です。ミリ秒、秒、分などの単位で期間を指定できます。time.NewTimer(d Duration)
: 指定された期間d
が経過した後に、現在の時刻をチャネルC
に送信する新しいTimer
を作成します。タイマーは一度だけ発火します。Timer.C
:Timer
が発火したときに時刻が送信される読み取り専用チャネルです。Timer.Reset(d Duration)
: タイマーをリセットし、新しい期間d
が経過した後に発火するように設定します。Reset
メソッドはbool
値を返します。true
を返す場合: タイマーがまだ発火しておらず、チャネルC
に値が送信されていない状態でリセットに成功したことを意味します。false
を返す場合: タイマーが既に発火しているか、チャネルC
から値が読み取られた後にリセットされたことを意味します。この場合、Reset
は新しいタイマーを作成するのと同等に動作します。
time.Sleep(d Duration)
: 指定された期間d
だけ現在のゴルーチンをスリープさせます。
-
select
ステートメント: 複数のチャネル操作を待機するために使用されます。case <-t0.C:
:t0.C
チャネルから値が受信できるまで待機します。default:
:select
ブロック内のどのチャネル操作もすぐに実行できない場合に実行されます。これにより、チャネル操作がブロックされるのを防ぎ、非ブロック的なチャネルの読み取りや書き込みが可能になります。
-
Go言語のテスト:
testing
パッケージを使用してテストを記述します。func TestXxx(t *testing.T)
: テスト関数はTest
で始まり、*testing.T
型の引数を取ります。t.Fatalf(...)
: テストを失敗させ、メッセージを出力してテストの実行を停止します。t.Error(...)
: テストを失敗させ、メッセージを出力しますが、テストの実行は継続します。t.Logf(...)
: テストのログにメッセージを出力します。テストが成功した場合でも出力されます。
-
Flaky Test (不安定なテスト): 前述の通り、実行するたびに結果が変わるテストのことです。時間依存のテスト、並行処理のテスト、外部サービスに依存するテストなどで発生しやすいです。これを解決するためには、テストのロジックをより堅牢にするか、テスト環境の変動を吸収するメカニズムを導入する必要があります。
技術的詳細
元の TestReset
関数は、固定された Duration
値(例: 100 * Millisecond
)を使用してタイマーの動作を検証していました。このアプローチは、システムクロックの粒度やスケジューリングの変動に対して脆弱でした。例えば、Sleep(50 * Millisecond)
の後に Reset(150 * Millisecond)
を呼び出し、その後 Sleep(100 * Millisecond)
を行うというシーケンスでは、厳密なタイミングが要求されます。もし Sleep
がわずかに長くかかったり、タイマーの発火が遅れたりすると、テストが意図せず失敗する可能性がありました。
新しいアプローチでは、この不安定さを解消するために以下の変更が導入されました。
-
testReset
ヘルパー関数の導入:- 元の
TestReset
のロジックをtestReset(d Duration) error
というヘルパー関数に切り出しました。 - このヘルパー関数は、テストで使用する基本となる時間単位
d
を引数として受け取ります。 - タイマーの期間やスリープ時間は、この
d
の倍数として定義されます(例:2 * d
,3 * d
)。これにより、テストの実行時間を柔軟に調整できるようになります。 - テストが失敗した場合、
t.Fatalf
の代わりにerrors.New
を使用してエラーを返します。これにより、呼び出し元のTestReset
関数がエラーを捕捉し、再試行のロジックを実装できるようになります。
- 元の
-
複数回の試行による堅牢化:
- 新しい
TestReset
関数は、testReset
ヘルパー関数を異なるDuration
値で複数回試行するロジックを導入しました。 tries := []Duration{1 * unit, 3 * unit, 7 * unit, 15 * unit}
という配列が定義されています。unit
は25 * Millisecond
です。- テストは、最も短い
Duration
(25ms
) から順にtestReset
を実行します。 - もし
testReset
がエラーなく成功した場合、そのDuration
でテストがパスしたと判断し、それ以上の試行は行いません。 - これにより、高速なマシンでは短い
Duration
でテストがすぐに完了し、低速なマシンや高負荷な環境ではより長いDuration
を試すことで、テストが安定してパスする可能性が高まります。これは、テストが「遅い、負荷の高いハードウェアでも不安定にならないように、しかし高速なマシンでは不必要に遅くならないように」という設計思想に基づいています。 - すべての
Duration
で試行してもテストが成功しなかった場合のみ、最終的にt.Error(err)
を呼び出してテストを失敗させます。
- 新しい
この変更により、TestReset
は環境の変動に対してより耐性を持つようになり、Goのテストスイート全体の信頼性向上に貢献しています。
コアとなるコードの変更箇所
--- a/src/pkg/time/sleep_test.go
+++ b/src/pkg/time/sleep_test.go
@@ -247,26 +247,49 @@ func TestSleepZeroDeadlock(t *testing.T) {
<-c
}
-func TestReset(t *testing.T) {
- t0 := NewTimer(100 * Millisecond)
- Sleep(50 * Millisecond)
- if t0.Reset(150*Millisecond) != true {
- t.Fatalf("resetting unfired timer returned false")
+func testReset(d Duration) error {
+ t0 := NewTimer(2 * d)
+ Sleep(d)
+ if t0.Reset(3*d) != true {
+ return errors.New("resetting unfired timer returned false")
}
- Sleep(100 * Millisecond)
+ Sleep(2 * d)
select {
case <-t0.C:
- t.Fatalf("timer fired early")
+ return errors.New("timer fired early")
default:
}
- Sleep(100 * Millisecond)
+ Sleep(2 * d)
select {
case <-t0.C:
default:
- t.Fatalf("reset timer did not fire")
+ return errors.New("reset timer did not fire")
}
if t0.Reset(50*Millisecond) != false {
- t.Fatalf("resetting expired timer returned true")
+ return errors.New("resetting expired timer returned true")
+ }
+ return nil
+}
+
+func TestReset(t *testing.T) {
+ // We try to run this test with increasingly larger multiples
+ // until one works so slow, loaded hardware isn't as flaky,
+ // but without slowing down fast machines unnecessarily.
+ const unit = 25 * Millisecond
+ tries := []Duration{
+ 1 * unit,
+ 3 * unit,
+ 7 * unit,
+ 15 * unit,
+ }
+ var err error
+ for _, d := range tries {
+ err = testReset(d)
+ if err == nil {
+ t.Logf("passed using duration %v", d)
+ return
+ }
}
+ t.Error(err)
}
コアとなるコードの解説
testReset(d Duration) error
関数
この関数は、TestReset
の主要なテストロジックをカプセル化しています。引数 d
は、テストで使用される時間単位の基準となります。
t0 := NewTimer(2 * d)
:2 * d
の期間で新しいタイマーt0
を作成します。Sleep(d)
:d
の期間だけスリープします。この時点でタイマーはまだ発火していないはずです。if t0.Reset(3*d) != true
: タイマーを3 * d
の期間でリセットします。タイマーがまだ発火していないため、Reset
はtrue
を返すはずです。もしfalse
を返した場合、エラーを返します。Sleep(2 * d)
: さらに2 * d
だけスリープします。この時点で、リセットされたタイマーはまだ発火していないはずです(合計d + 2*d = 3*d
のスリープで、リセット後の期間3*d
に達する)。select { case <-t0.C: ... default: }
:case <-t0.C:
: もしタイマーがこの時点で発火していたら(つまり、3*d
よりも早く発火したら)、"timer fired early"
というエラーを返します。default:
: タイマーがまだ発火していないことを確認します。
Sleep(2 * d)
: さらに2 * d
だけスリープします。この時点で、リセットされたタイマーは発火しているはずです(合計d + 2*d + 2*d = 5*d
のスリープで、リセット後の期間3*d
を超えている)。select { case <-t0.C: ... default: }
:case <-t0.C:
: タイマーが発火したことを確認します。default:
: もしタイマーが発火していなかったら、"reset timer did not fire"
というエラーを返します。
if t0.Reset(50*Millisecond) != false
: 既に発火したタイマーをリセットしようとします。この場合、Reset
はfalse
を返すはずです。もしtrue
を返した場合、"resetting expired timer returned true"
というエラーを返します。return nil
: すべてのチェックが成功した場合、nil
を返して成功を示します。
TestReset(t *testing.T)
関数
この関数は、testReset
ヘルパー関数を呼び出し、テストの堅牢性を高めるためのロジックを含んでいます。
const unit = 25 * Millisecond
: 基本となる時間単位を25ミリ秒
と定義します。tries := []Duration{1 * unit, 3 * unit, 7 * unit, 15 * unit}
:testReset
を試行する異なる時間単位の倍数を定義します。これにより、25ms
,75ms
,175ms
,375ms
の順でテストが試行されます。var err error
:testReset
から返されるエラーを保持するための変数です。for _, d := range tries
:tries
配列の各Duration
d
に対してループを実行します。err = testReset(d)
:testReset
ヘルパー関数を現在のd
で呼び出します。if err == nil
: もしtestReset
がエラーなく成功した場合(nil
を返した場合)、t.Logf("passed using duration %v", d)
: どのDuration
でテストがパスしたかをログに出力します。return
: テストを終了し、成功とします。
t.Error(err)
: ループが終了してもtestReset
が一度も成功しなかった場合(つまり、すべてのDuration
でエラーが返された場合)、最後に発生したエラーをt.Error
で出力し、テストを失敗とします。
この構造により、テストはまず短い時間間隔で迅速に実行を試み、もしそれが不安定な環境で失敗した場合には、より長い時間間隔で再試行することで、テストの信頼性を大幅に向上させています。
関連リンク
- Go Issue #4690: https://code.google.com/p/go/issues/detail?id=4690 (古いGoogle Codeのリンクですが、コミットメッセージに記載されています)
- Gerrit Change-Id:
https://golang.org/cl/7181052
(GoのコードレビューシステムGerritのリンク)
参考にした情報源リンク
- Go言語の
time
パッケージに関する公式ドキュメント: https://pkg.go.dev/time - Go言語の
testing
パッケージに関する公式ドキュメント: https://pkg.go.dev/testing - Go言語の
select
ステートメントに関する公式ドキュメント: https://go.dev/tour/concurrency/5 - Go言語における
time.Timer.Reset
の不安定性に関する議論 (Go 1.23での改善など):- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEh1xPq62iAxYxFszvy20QTHxN_0KaThXpuD4ogpr8ZBtDsV0kmKtMu_uRy7WtIZKuJaNw4Upuyly7r-0UGEQS74uWMXCZXpOixkixpitdVVUVwNAAn5CDvxnhMRQFJtxALhAYOPukxrxJijcbScBgZ5bNfnvscmOdL
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEvcN6P4gam7twOJ69ThPndnXKjKRzMWLN-aEL4ISn-9tc7mVL-Tz_MqfzBGZnjjPlNk3CzDwq1mva5MvkNj9ABLl78SKoHWRNHb1CY2YqNAk-5Ck_4d6qAwaNSeLenB_fNE66q
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG9zsf1ITiPzgwDyeGYD-HZR6uHVgFhkFiuT8QHisa1LlfaZejzQQLL6WJoHq8SWzNAQK2q4VI9WuYcAJ_LjPvkvPEERHF1gXYMflbVq05dv6XF3vraMCihnZc-iIER6v7EQHtF
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFqg3cYLHd5HB1uoOIvuLvcQ7TWBEg0PHKJcNa716CpZBSGw2BMl6N6d9nGcs76FFhIevaPalOweVwP4CobDtStOADw8l5BuUsfPbiTzl2CbISa-Cfg5z7E4X0=