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

[インデックス 18149] ファイルの概要

このコミットは、Go言語の標準ライブラリであるbufioパッケージのテストカバレッジを向上させることを目的としています。具体的には、NewReaderSize関数のバッファサイズに関するテスト、Peek関数のエラーハンドリング、およびUnreadByte関数の動作検証に関するテストが追加・改善されています。

コミット

commit 4d239bcea270a56f1554524598e49d87e5c48e4f
Author: Shawn Smith <shawn.p.smith@gmail.com>
Date:   Wed Jan 1 22:26:22 2014 +1100

    bufio: improve NewReaderSize, Peek, and UnreadByte test coverage
    
    R=golang-codereviews, dave
    CC=golang-codereviews
    https://golang.org/cl/42990045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4d239bcea270a56f1554524598e49d87e5c48e4f

元コミット内容

bufio: improve NewReaderSize, Peek, and UnreadByte test coverage

このコミットは、bufioパッケージ内のNewReaderSizePeek、およびUnreadByte関数のテストカバレッジを改善することを目的としています。

変更の背景

ソフトウェア開発において、テストカバレッジの向上はコードの品質と信頼性を確保するために不可欠です。特に標準ライブラリのような基盤となるコンポーネントでは、様々なエッジケースや異常な入力に対する堅牢性が求められます。

このコミットの背景には、bufioパッケージの特定の機能、特にNewReaderSizePeekUnreadByteについて、既存のテストが不十分であるという認識があったと考えられます。

  • NewReaderSizeは、指定されたバッファサイズでReaderを作成しますが、バッファサイズが0の場合の挙動など、特定のサイズに対するテストが不足していた可能性があります。
  • Peekは、次のバイトを読み込まずにバッファからデータを覗き見する機能ですが、負のカウントが指定された場合や、バッファが満杯の場合のエラーハンドリングに関するテストが不足していた可能性があります。
  • UnreadByteは、最後に読み込んだバイトをバッファに戻す機能ですが、その正確な動作、特に連続したReadByteUnreadByteの組み合わせにおける挙動を検証するテストが不足していた可能性があります。

これらのテストを追加・改善することで、これらの関数の予期せぬ動作を防ぎ、将来の変更に対する回帰テストとしての役割を強化することが目的です。

前提知識の解説

bufioパッケージ

bufioパッケージは、Go言語の標準ライブラリの一部であり、I/O操作をバッファリングすることで効率を向上させる機能を提供します。これにより、ディスクI/OやネットワークI/Oのような低速な操作の回数を減らし、アプリケーションのパフォーマンスを向上させることができます。

主要な型には以下のものがあります。

  • Reader: io.Readerインターフェースをラップし、バッファリングされた読み取り機能を提供します。
    • ReadByte(): 1バイトを読み込みます。
    • UnreadByte(): 最後に読み込んだバイトをバッファに戻します。
    • Peek(n int): 次のnバイトを読み込まずに覗き見します。
    • NewReaderSize(rd io.Reader, size int): 指定されたバッファサイズで新しいReaderを作成します。
  • Writer: io.Writerインターフェースをラップし、バッファリングされた書き込み機能を提供します。

Go言語のテスト

Go言語には、標準でテストフレームワークが組み込まれています。テストファイルは通常、テスト対象のソースファイルと同じディレクトリに配置され、ファイル名の末尾に_test.goが付きます。テスト関数はTestで始まり、*testing.T型の引数を取ります。

  • t.Fatal() / t.Fatalf(): テストを失敗として終了させます。
  • t.Error() / t.Errorf(): テストを失敗としてマークしますが、実行は継続します。
  • t.Log() / t.Logf(): テスト中に情報を出力します。

io.EOF

io.EOFは、入力の終わりに達したことを示すエラーです。Read操作がこれ以上データを読み込めない場合に返されます。

ErrNegativeCountErrBufferFull

これらはbufioパッケージ内で定義されているエラーで、それぞれ以下のような状況で返されます。

  • ErrNegativeCount: Peek関数に負のカウントが渡された場合に返されるエラーです。
  • ErrBufferFull: Peek関数が要求されたバイト数をバッファに格納できない場合に返されるエラーです。これは、バッファサイズを超えるデータをPeekしようとした場合などに発生します。

技術的詳細

このコミットは、src/pkg/bufio/bufio_test.goファイルに対して行われています。主な変更点は以下の3つです。

  1. NewReaderSizeのテストカバレッジ改善: bufsizesというテスト用のバッファサイズのスライスに0が追加されました。これにより、NewReaderSize関数にバッファサイズとして0が渡された場合の挙動がテストされるようになります。通常、バッファサイズが0の場合、bufioはデフォルトの最小バッファサイズ(minReadBufferSize)を使用するようにフォールバックします。この変更は、そのフォールバックメカニズムが正しく機能するかを確認するためのものです。

  2. UnreadByteのテスト追加: TestUnreadByteという新しいテスト関数が追加されました。このテストは、ReadByteUnreadByteの組み合わせが正しく機能するかを検証します。

    • StringReaderというカスタムのio.Reader実装を使用して、テストデータを供給します。
    • ループ内で1バイトずつ読み込み(r.ReadByte())、そのバイトをUnreadByte()でバッファに戻し、再度ReadByte()で読み込みます。
    • 読み込んだバイトがUnreadByteの前と後で一致するかを確認することで、UnreadByteの正確な動作を検証しています。
    • io.EOFに達するまでこの操作を繰り返し、最終的に読み込んだ文字列が期待通りであるかを確認します。
  3. Peekのエラーハンドリングテスト追加: TestPeek関数に2つの新しいテストケースが追加されました。

    • buf.Peek(-1): Peek関数に負の引数(-1)を渡した場合に、ErrNegativeCountエラーが正しく返されることを検証します。これは、不正な入力に対する関数の堅牢性を確認するものです。
    • buf.Peek(32): Peek関数がバッファサイズを超えるデータを要求した場合に、ErrBufferFullエラーが正しく返されることを検証します。このテストは、バッファの容量制限に関するPeekの挙動を検証します。

これらのテストの追加により、bufioパッケージのこれらの関数の堅牢性と信頼性が向上し、将来の変更に対する安全性が高まります。

コアとなるコードの変更箇所

--- a/src/pkg/bufio/bufio_test.go
+++ b/src/pkg/bufio/bufio_test.go
@@ -139,7 +139,7 @@ var bufreaders = []bufReader{
 const minReadBufferSize = 16
 
 var bufsizes = []int{
-	minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096,
+	0, minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096,
 }
 
 func TestReader(t *testing.T) {
@@ -259,6 +259,38 @@ func TestUnreadRune(t *testing.T) {
 	}
 }
 
+func TestUnreadByte(t *testing.T) {
+	want := "Hello, world"
+	got := ""
+	segments := []string{"Hello, ", "world"}
+	r := NewReader(&StringReader{data: segments})
+	// Normal execution.
+	for {
+		b1, err := r.ReadByte()
+		if err != nil {
+			if err != io.EOF {
+				t.Fatal("unexpected EOF")
+			}
+			break
+		}
+		got += string(b1)
+		// Put it back and read it again
+		if err = r.UnreadByte(); err != nil {
+			t.Fatalf("unexpected error on UnreadByte: %v", err)
+		}
+		b2, err := r.ReadByte()
+		if err != nil {
+			t.Fatalf("unexpected error reading after unreading: %v", err)
+		}
+		if b1 != b2 {
+			t.Fatalf("incorrect byte after unread: got %c wanted %c", b1, b2)
+		}
+	}
+	if got != want {
+		t.Errorf("got=%q want=%q", got, want)
+	}
+}
+
 // Test that UnreadRune fails if the preceding operation was not a ReadRune.
 func TestUnreadRuneError(t *testing.T) {
 	buf := make([]byte, 3) // All runes in this test are 3 bytes long
@@ -516,6 +548,9 @@ func TestPeek(t *testing.T) {\n 	if s, err := buf.Peek(4); string(s) != "abcd" || err != nil {\n 		t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err)\n 	}\n+\tif _, err := buf.Peek(-1); err != ErrNegativeCount {\n+\t\tt.Fatalf("want ErrNegativeCount got %v", err)\n+\t}\n \tif _, err := buf.Peek(32); err != ErrBufferFull {\n \t\tt.Fatalf("want ErrBufFull got %v", err)\n \t}\n```

## コアとなるコードの解説

### `bufsizes`スライスへの`0`の追加

```diff
-	minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096,
+	0, minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096,

bufsizesは、TestReader関数内でNewReaderSizeに渡されるバッファサイズを定義しているスライスです。ここに0が追加されたことで、NewReaderSize(rd, 0)という呼び出しがテストされるようになります。bufioパッケージの内部実装では、NewReaderSizeに0以下のサイズが渡された場合、デフォルトの最小バッファサイズ(minReadBufferSize、通常は16バイト)が使用されます。この変更は、この挙動が期待通りであることを確認するためのテストケースを追加しています。

TestUnreadByte関数の追加

func TestUnreadByte(t *testing.T) {
	want := "Hello, world"
	got := ""
	segments := []string{"Hello, ", "world"}
	r := NewReader(&StringReader{data: segments})
	// Normal execution.
	for {
		b1, err := r.ReadByte()
		if err != nil {
			if err != io.EOF {
				t.Fatal("unexpected EOF")
			}
			break
		}
		got += string(b1)
		// Put it back and read it again
		if err = r.UnreadByte(); err != nil {
			t.Fatalf("unexpected error on UnreadByte: %v", err)
		}
		b2, err := r.ReadByte()
		if err != nil {
			t.Fatalf("unexpected error reading after unreading: %v", err)
		}
		if b1 != b2 {
			t.Fatalf("incorrect byte after unread: got %c wanted %c", b1, b2)
		}
	}
	if got != want {
		t.Errorf("got=%q want=%q", got, want)
	}
}

この新しいテスト関数は、UnreadByteの基本的な機能と、ReadByteとの組み合わせにおける正確性を検証します。

  • StringReaderは、文字列スライスからデータを読み込むカスタムリーダーです。これにより、テストデータを簡単に制御できます。
  • ループ内でReadByteを呼び出して1バイト読み込み、その直後にUnreadByteを呼び出してそのバイトをバッファに戻します。
  • その後、再度ReadByteを呼び出して、戻したバイトが正しく読み取れることを確認します。
  • b1 != b2のチェックは、UnreadByteが正しく機能し、同じバイトが再度読み取られることを保証します。
  • 最終的に、すべてのバイトを読み終えた後、構築された文字列gotが期待されるwantと一致するかを確認します。これにより、UnreadByteが全体の読み取りシーケンスに悪影響を与えないことを検証しています。

TestPeek関数へのエラーハンドリングテストの追加

+\tif _, err := buf.Peek(-1); err != ErrNegativeCount {\n+\t\tt.Fatalf("want ErrNegativeCount got %v", err)\n+\t}\n \tif _, err := buf.Peek(32); err != ErrBufferFull {\n \t\tt.Fatalf("want ErrBufFull got %v", err)\n \t}\n```
`TestPeek`関数に2つの新しい`if`文が追加されました。
-   `buf.Peek(-1)`のテスト: `Peek`関数に負の引数を渡すという不正な操作をシミュレートし、その結果として`bufio.ErrNegativeCount`エラーが返されることを検証します。これは、関数の入力検証とエラー報告が正しく行われていることを確認します。
-   `buf.Peek(32)`のテスト: このテストは、バッファサイズが32バイト未満の`buf`に対して`Peek(32)`を呼び出すことを想定しています。これにより、バッファに存在しない、またはバッファの容量を超えるデータを`Peek`しようとした場合に、`bufio.ErrBufferFull`エラーが正しく返されることを検証します。これは、`Peek`がバッファの限界を適切に処理することを確認します。

これらの変更は、`bufio`パッケージの堅牢性を高め、様々な使用シナリオにおける予期せぬ挙動を防ぐための重要なテストカバレッジの改善です。

## 関連リンク

-   Go言語 `bufio` パッケージのドキュメント: [https://pkg.go.dev/bufio](https://pkg.go.dev/bufio)
-   Go言語のテストに関するドキュメント: [https://go.dev/doc/code#testing](https://go.dev/doc/code#testing)

## 参考にした情報源リンク

-   Go言語の公式ドキュメント
-   Go言語のソースコード(`src/pkg/bufio/bufio.go` および `src/pkg/bufio/bufio_test.go`)
-   Go言語のコードレビューシステム (Gerrit): [https://golang.org/cl/42990045](https://golang.org/cl/42990045)
    -   このリンクはコミットメッセージに記載されているもので、この変更がGoのコードレビュープロセスを経て取り込まれたことを示しています。
-   Go言語の`bufio`パッケージに関する一般的な情報源(ブログ記事、チュートリアルなど)