[インデックス 18173] ファイルの概要
このコミットは、Go言語の標準ライブラリtime
パッケージ内のTick
関数およびNewTicker
関数について、負の期間(duration)が与えられた場合の挙動を検証するテストを追加するものです。具体的には、src/pkg/time/tick_test.go
ファイルに新たなテストケースが追加されています。
コミット
commit 16134060dea378618b7fa4f2accd6cd4831541f0
Author: Shawn Smith <shawn.p.smith@gmail.com>
Date: Mon Jan 6 10:32:07 2014 -0800
time: add tests for Tick, NewTicker with negative duration
R=golang-codereviews, gobot, r, bradfitz
CC=golang-codereviews
https://golang.org/cl/37660044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/16134060dea378618b7fa4f2accd6cd4831541f0
元コミット内容
time: add tests for Tick, NewTicker with negative duration
変更の背景
このコミットの背景には、Go言語のtime
パッケージにおけるTick
およびNewTicker
関数の堅牢性を高める目的があります。これらの関数は、指定された期間ごとにイベントを発生させるためのタイマー(Ticker)を作成する際に使用されます。通常、期間は正の値であることが期待されますが、プログラマーが誤って負の期間を渡した場合の挙動が明確に定義され、テストによって保証されていることが重要です。
負の期間が与えられた場合、予期せぬ動作やパニック(プログラムの異常終了)が発生する可能性があります。このコミットは、そのようなエッジケースに対する関数の挙動を明確にし、それが期待通りであることをテストで確認することで、ライブラリの信頼性と安定性を向上させることを目的としています。特に、Tick
関数がnil
を返すこと、そしてNewTicker
関数がパニックを起こすことが、この時点での設計上の意図であったと考えられます。これらの挙動をテストで明示することで、将来の変更に対する回帰テストとしても機能します。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とtime
パッケージの機能に関する知識が必要です。
Go言語のtime
パッケージ
Go言語の標準ライブラリであるtime
パッケージは、時間に関する様々な機能を提供します。これには、現在時刻の取得、時間のフォーマット、時間間隔の計算、そしてタイマーやTickerの作成などが含まれます。
time.Duration
型
time.Duration
は、Go言語で時間間隔を表す型です。これはint64
のエイリアスであり、ナノ秒単位で時間を表現します。例えば、time.Second
は1秒、time.Minute
は1分を表すDuration
型の定数です。負の値も取り得ます。
time.Tick
関数
func Tick(d Duration) <-chan Time
Tick
関数は、指定された期間d
ごとに現在時刻を送信するチャネルを返します。このチャネルは、d
が非正(0または負)の場合にはnil
を返します。これは、無効な期間に対してTickerを作成しようとした場合に、チャネルが利用できないことを示すための設計上の挙動です。
time.NewTicker
関数
func NewTicker(d Duration) *Ticker
NewTicker
関数は、指定された期間d
ごとにイベントを発生させる新しいTicker
オブジェクトを返します。Ticker
オブジェクトは、C
という名前のチャネルを持っており、このチャネルを通じてイベントが送信されます。NewTicker
は、d
が非正の場合にパニック(panic)を引き起こします。これは、無効な期間でTickerを初期化しようとすること自体がプログラミングエラーであると見なされるためです。
Go言語のテスト(testing
パッケージ)
Go言語では、標準のtesting
パッケージを使用してユニットテストを記述します。テスト関数はTest
で始まり、*testing.T
型の引数を取ります。
t.Errorf(...)
: テストが失敗したことを報告し、エラーメッセージを出力します。テストは続行されます。t.Fatalf(...)
: テストが失敗したことを報告し、エラーメッセージを出力し、テストを即座に終了します。
defer
文とrecover
関数
Go言語のdefer
文は、そのdefer
文を含む関数がリターンする直前に実行される関数呼び出しをスケジュールします。
recover
関数は、パニックから回復するために使用されます。recover
は、defer
された関数内で呼び出された場合にのみ意味を持ちます。recover
がパニック中に呼び出されると、パニックの値が返され、通常の実行が再開されます。recover
がパニック中でないときに呼び出されると、nil
が返されます。このコミットでは、NewTicker
がパニックを起こすことをテストするためにdefer
とrecover
が使用されています。
技術的詳細
このコミットは、time
パッケージのTick
およびNewTicker
関数が、負の期間が与えられた場合にどのように振る舞うべきかという仕様をテストによって明確にしています。
Tick(-1)
のテスト (TestTick
)
Tick
関数は、期間が非正の場合にnil
チャネルを返すという仕様があります。このテストケースでは、Tick(-1)
(-1ナノ秒という負の期間)を呼び出し、その戻り値がnil
であることを確認しています。もしnil
でなければ、t.Errorf
を使ってテスト失敗を報告します。これは、Tick
関数が不正な入力に対して安全なnil
を返すことで、呼び出し元がチャネル操作を行う前にnil
チェックを行うことを促す設計思想に基づいています。
NewTicker(-1)
のテスト (TestNewTickerLtZeroDuration
)
NewTicker
関数は、期間が非正の場合にパニックを引き起こすという仕様があります。このテストケースでは、NewTicker(-1)
を呼び出すことでパニックが発生することを検証しています。
defer func() { ... }()
:NewTicker(-1)
がパニックを起こした場合に実行される匿名関数を遅延実行として登録しています。if err := recover(); err == nil
:recover()
を呼び出してパニックが発生したかどうかを確認します。もしパニックが発生していればerr
にはパニックの値が入り、nil
ではありません。もしパニックが発生していなければerr
はnil
になります。t.Errorf("NewTicker(-1) should have panicked")
:recover()
がnil
を返した場合(つまりパニックが発生しなかった場合)、それはテストの意図に反するため、エラーを報告します。
このテストは、NewTicker
が不正な入力に対してパニックを起こすことで、プログラマーに即座に問題があることを知らせ、プログラムの異常な状態での続行を防ぐという設計思想を反映しています。パニックは、回復不能なエラーやプログラミング上のバグを示すためにGo言語で用いられるメカニズムです。
これらのテストの追加により、time
パッケージのAPIが、負の期間というエッジケースに対して明確で予測可能な挙動を持つことが保証され、ライブラリの堅牢性が向上しています。
コアとなるコードの変更箇所
変更はsrc/pkg/time/tick_test.go
ファイルに集中しており、以下の2つの新しいテスト関数が追加されています。
--- a/src/pkg/time/tick_test.go
+++ b/src/pkg/time/tick_test.go
@@ -48,6 +48,24 @@ func TestTeardown(t *testing.T) {
}\n }\n \n+// Test the Tick convenience wrapper.\n+func TestTick(t *testing.T) {\n+\t// Test that giving a negative duration returns nil.\n+\tif got := Tick(-1); got != nil {\n+\t\tt.Errorf(\"Tick(-1) = %v; want nil\", got)\n+\t}\n+}\n+\n+// Test that NewTicker panics when given a duration less than zero.\n+func TestNewTickerLtZeroDuration(t *testing.T) {\n+\tdefer func() {\n+\t\tif err := recover(); err == nil {\n+\t\t\tt.Errorf(\"NewTicker(-1) should have panicked\")\n+\t\t}\n+\t}()\n+\tNewTicker(-1)\n+}\n+\n func BenchmarkTicker(b *testing.B) {\
コアとなるコードの解説
TestTick
関数
このテスト関数は、time.Tick
関数に負の期間(-1
)を渡した場合の挙動を検証します。
// Test the Tick convenience wrapper.
func TestTick(t *testing.T) {
// Test that giving a negative duration returns nil.
if got := Tick(-1); got != nil {
t.Errorf("Tick(-1) = %v; want nil", got)
}
}
Tick(-1)
:time.Tick
関数に-1
という負のDuration
を渡して呼び出します。if got := Tick(-1); got != nil
:Tick
関数の仕様では、期間が非正の場合にはnil
チャネルを返すことになっています。このif
文では、実際に返されたチャネルgot
がnil
でない場合にテストを失敗とします。t.Errorf("Tick(-1) = %v; want nil", got)
:Tick(-1)
がnil
以外の値を返した場合に、エラーメッセージを出力してテスト失敗を報告します。これは、Tick
関数が不正な入力に対して安全なnil
を返すという期待される挙動を保証するためのテストです。
TestNewTickerLtZeroDuration
関数
このテスト関数は、time.NewTicker
関数に負の期間(-1
)を渡した場合にパニックが発生することを検証します。
// Test that NewTicker panics when given a duration less than zero.
func TestNewTickerLtZeroDuration(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("NewTicker(-1) should have panicked")
}
}()
NewTicker(-1)
}
defer func() { ... }()
: このdefer
ブロックは、TestNewTickerLtZeroDuration
関数が終了する直前に実行されます。これは、NewTicker(-1)
がパニックを起こした場合に、そのパニックを捕捉するために使用されます。if err := recover(); err == nil
:recover()
関数は、パニックが発生している場合にそのパニックの値を返します。パニックが発生していない場合はnil
を返します。このif
文は、NewTicker(-1)
がパニックを起こさなかった場合(recover()
がnil
を返した場合)に真となります。t.Errorf("NewTicker(-1) should have panicked")
:NewTicker(-1)
がパニックを起こさなかった場合に、エラーメッセージを出力してテスト失敗を報告します。これは、NewTicker
関数が不正な入力に対してパニックを起こすという期待される挙動を保証するためのテストです。NewTicker(-1)
: 実際にtime.NewTicker
関数に-1
という負のDuration
を渡して呼び出します。この呼び出しがパニックを引き起こすことが期待されています。
これらのテストは、Go言語のtime
パッケージのAPIが、負の期間というエッジケースに対して明確で予測可能な挙動を持つことを保証し、ライブラリの堅牢性を高める上で重要な役割を果たしています。
関連リンク
- Go言語
time
パッケージのドキュメント: https://pkg.go.dev/time - Go言語のテストに関するドキュメント: https://go.dev/blog/testing
- Go言語の
defer
,panic
,recover
に関するドキュメント: https://go.dev/blog/defer-panic-and-recover
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/pkg/time/tick.go
およびsrc/pkg/time/tick_test.go
) - Go言語のブログ記事 (特に
defer
,panic
,recover
に関するもの) - GitHubのコミットページ: https://github.com/golang/go/commit/16134060dea378618b7fa4f2accd6cd4831541f0
- Go CL 37660044: https://golang.org/cl/37660044 (これは古いGerritのリンクであり、現在はGitHubのコミットページにリダイレクトされるか、アーカイブされている可能性がありますが、当時の変更履歴を追う上で参照された可能性があります。)