Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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がパニックを起こすことをテストするためにdeferrecoverが使用されています。

技術的詳細

このコミットは、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ではありません。もしパニックが発生していなければerrnilになります。
  • 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文では、実際に返されたチャネルgotnilでない場合にテストを失敗とします。
  • 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言語の公式ドキュメント
  • 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のコミットページにリダイレクトされるか、アーカイブされている可能性がありますが、当時の変更履歴を追う上で参照された可能性があります。)