[インデックス 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
パッケージ内のNewReaderSize
、Peek
、およびUnreadByte
関数のテストカバレッジを改善することを目的としています。
変更の背景
ソフトウェア開発において、テストカバレッジの向上はコードの品質と信頼性を確保するために不可欠です。特に標準ライブラリのような基盤となるコンポーネントでは、様々なエッジケースや異常な入力に対する堅牢性が求められます。
このコミットの背景には、bufio
パッケージの特定の機能、特にNewReaderSize
、Peek
、UnreadByte
について、既存のテストが不十分であるという認識があったと考えられます。
NewReaderSize
は、指定されたバッファサイズでReader
を作成しますが、バッファサイズが0の場合の挙動など、特定のサイズに対するテストが不足していた可能性があります。Peek
は、次のバイトを読み込まずにバッファからデータを覗き見する機能ですが、負のカウントが指定された場合や、バッファが満杯の場合のエラーハンドリングに関するテストが不足していた可能性があります。UnreadByte
は、最後に読み込んだバイトをバッファに戻す機能ですが、その正確な動作、特に連続したReadByte
とUnreadByte
の組み合わせにおける挙動を検証するテストが不足していた可能性があります。
これらのテストを追加・改善することで、これらの関数の予期せぬ動作を防ぎ、将来の変更に対する回帰テストとしての役割を強化することが目的です。
前提知識の解説
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
操作がこれ以上データを読み込めない場合に返されます。
ErrNegativeCount
とErrBufferFull
これらはbufio
パッケージ内で定義されているエラーで、それぞれ以下のような状況で返されます。
ErrNegativeCount
:Peek
関数に負のカウントが渡された場合に返されるエラーです。ErrBufferFull
:Peek
関数が要求されたバイト数をバッファに格納できない場合に返されるエラーです。これは、バッファサイズを超えるデータをPeek
しようとした場合などに発生します。
技術的詳細
このコミットは、src/pkg/bufio/bufio_test.go
ファイルに対して行われています。主な変更点は以下の3つです。
-
NewReaderSize
のテストカバレッジ改善:bufsizes
というテスト用のバッファサイズのスライスに0
が追加されました。これにより、NewReaderSize
関数にバッファサイズとして0
が渡された場合の挙動がテストされるようになります。通常、バッファサイズが0の場合、bufio
はデフォルトの最小バッファサイズ(minReadBufferSize
)を使用するようにフォールバックします。この変更は、そのフォールバックメカニズムが正しく機能するかを確認するためのものです。 -
UnreadByte
のテスト追加:TestUnreadByte
という新しいテスト関数が追加されました。このテストは、ReadByte
とUnreadByte
の組み合わせが正しく機能するかを検証します。StringReader
というカスタムのio.Reader
実装を使用して、テストデータを供給します。- ループ内で1バイトずつ読み込み(
r.ReadByte()
)、そのバイトをUnreadByte()
でバッファに戻し、再度ReadByte()
で読み込みます。 - 読み込んだバイトが
UnreadByte
の前と後で一致するかを確認することで、UnreadByte
の正確な動作を検証しています。 io.EOF
に達するまでこの操作を繰り返し、最終的に読み込んだ文字列が期待通りであるかを確認します。
-
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`パッケージに関する一般的な情報源(ブログ記事、チュートリアルなど)