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

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

このコミットでは、Go言語の標準ライブラリbytesパッケージ内のテストカバレッジを向上させるための変更が行われました。具体的には、src/pkg/bytes/bytes_test.gosrc/pkg/bytes/reader_test.goの2つのテストファイルが修正され、合計85行の追加が行われています。

コミット

commit 3ee0744c06503eece696a615b1f8b37b4a0ed8a8
Author: Dave Cheney <dave@cheney.net>
Date:   Wed Sep 11 21:20:15 2013 +1000

    bytes: additional test coverage
    
    Add coverage for some uncovered bytes methods. The increase in actual coverage is disapointing small.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/13651044

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

https://github.com/golang/go/commit/3ee0744c06503eece696a615b1f8b37b4a0ed8a8

元コミット内容

bytes: additional test coverage

Add coverage for some uncovered bytes methods. The increase in actual coverage is disapointing small.

変更の背景

このコミットの主な目的は、Go言語のbytesパッケージにおけるテストカバレッジを向上させることです。コミットメッセージにもあるように、既存のテストではカバーされていなかったbytesパッケージ内のいくつかのメソッドに対して、新たなテストケースが追加されました。これにより、これらのメソッドが意図した通りに動作することを確認し、将来的なリファクタリングや変更に対する安全性を高めることが期待されます。ただし、コミットメッセージには「実際のカバレッジの増加は期待外れに小さい」とあり、これは既存のコードベースが既に高いカバレッジを持っていたか、あるいは追加されたテストがカバーする範囲が限定的であったことを示唆しています。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とbytesパッケージの基本的な知識が必要です。

  • bytesパッケージ: Go言語の標準ライブラリの一部で、バイトスライス([]byte)を操作するためのユーティリティ関数や型を提供します。文字列操作に似た機能(検索、置換、比較など)や、バイトスライスを効率的に扱うためのBuffer型やReader型などが含まれます。
  • bytes.Buffer: 可変長のバイトバッファを実装する型です。バイトスライスを効率的に追加したり、読み出したりする際に使用されます。WriteReadGrowTruncateなどのメソッドを持ちます。
    • Grow(n int): バッファの容量を少なくともnバイト増やすことを保証します。負の値を指定するとパニック(実行時エラー)を引き起こす可能性があります。
    • Truncate(n int): バッファの長さをnバイトに切り詰めます。nがバッファの現在の長さより大きい場合や負の値の場合、パニックを引き起こす可能性があります。
  • bytes.Reader: バイトスライスをio.Readerio.Seekerio.ByteReaderio.RuneReaderインターフェースとして扱うための型です。バイトスライスからデータを読み出す際に便利です。
    • Len() int: 読み出し可能な残りのバイト数を返します。
    • ReadRune() (r rune, size int, err error): 次のUTF-8エンコードされたUnicodeコードポイント(ルーン)を読み出し、そのルーンとバイトサイズを返します。
    • UnreadByte() error: 最後に読み出されたバイトを読み出しストリームに戻します。連続して複数回呼び出すとエラーになることがあります。
  • テストカバレッジ: ソフトウェアテストにおいて、テストケースが実行された際に、ソースコードのどの部分が実行されたかを示す指標です。高いテストカバレッジは、コードが十分にテストされていることを示唆しますが、それ自体がバグがないことを保証するものではありません。
  • パニック (Panic): Go言語における回復不可能な実行時エラーの一種です。通常、プログラムの異常な状態(例: 配列の範囲外アクセス、nilポインタ参照)を示すために使用され、パニックが発生すると、通常のプログラムフローは停止し、defer関数が実行された後、プログラムが終了します。

技術的詳細

このコミットでは、bytesパッケージのいくつかの機能に対するテストケースが追加されています。

src/pkg/bytes/bytes_test.goへの変更点:

  1. indexTestsの追加:

    • {"foo", "baz", -1}という新しいテストケースがindexTestsスライスに追加されました。これは、bytes.Index(または類似の検索関数)が、検索対象のバイトスライス("foo")内に指定されたサブスライス("baz")が見つからない場合に-1を返すことを確認するためのものです。これにより、存在しないサブスライスの検索に対する関数の挙動が明示的にテストされます。
  2. ToTitle関数のテスト:

    • ToTitleTestsという新しいTitleTest型のスライスが定義され、bytes.ToTitle関数のための様々な入力と期待される出力のペアが含まれています。これには、空文字列、単一の文字、スペースを含む文字列、数字を含む文字列、ハイフンで連結された文字列、そしてUTF-8エンコードされた非ASCII文字(ÿøû)が含まれます。
    • TestToTitleという新しいテスト関数が追加され、ToTitleTestsの各ケースを反復処理し、bytes.ToTitleの実際の出力が期待される出力と一致するかどうかを検証します。これにより、bytes.ToTitleが様々な種類の入力に対して正しく動作することが保証されます。
  3. Bufferのパニックテスト:

    • TestBufferGrowNegative: bytes.BufferGrowメソッドに負の値を渡した場合にパニックが発生することを確認するテストです。deferrecoverを使用してパニックを捕捉し、パニックが発生しなかった場合にテストを失敗させます。これは、不正な入力に対する関数の堅牢性を保証します。
    • TestBufferTruncateNegative: bytes.BufferTruncateメソッドに負の値を渡した場合にパニックが発生することを確認するテストです。GrowNegativeと同様にdeferrecoverを使用します。
    • TestBufferTruncateOutOfRange: bytes.BufferTruncateメソッドに、バッファの現在の長さよりも大きい値を渡した場合にパニックが発生することを確認するテストです。これもdeferrecoverを使用してパニックを捕捉します。これらのテストは、Bufferのメソッドが不正な引数に対して適切にパニックを引き起こすことを保証し、開発者がこれらのメソッドを誤用した場合に早期に問題を検出できるようにします。

src/pkg/bytes/reader_test.goへの変更点:

  1. TestReaderLen:

    • bytes.ReaderLen()メソッドの動作をテストします。初期状態でのLen()の値、一部を読み出した後のLen()の値、そして全てを読み出した後のLen()の値を検証します。これにより、Len()メソッドが読み出しの進行状況に応じて正しく残りのバイト数を報告することを確認します。
  2. TestReaderDoubleUnreadRune:

    • bytes.Reader(実際にはbytes.BufferReaderとして使用)のUnreadByte()メソッドの特定の挙動をテストします。ReadRune()でルーンを読み出した後、UnreadByte()を一度呼び出し、さらに続けてUnreadByte()を呼び出した場合にエラーが発生することを確認します。これは、UnreadByte()が通常、最後に読み出された1バイトのみを戻すことを意図しており、それ以上の操作は不正であることを示します。

これらの追加されたテストは、bytesパッケージの様々なエッジケースやエラー条件に対するカバレッジを向上させ、ライブラリの信頼性と堅牢性を高めることに貢献しています。

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

--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -143,6 +143,7 @@ var indexTests = []BinOpTest{\n 	{\"\", \"a\", -1},\n 	{\"\", \"foo\", -1},\n 	{\"fo\", \"foo\", -1},\n+\t{\"foo\", \"baz\", -1},\n \t{\"foo\", \"foo\", 0},\n \t{\"oofofoofooo\", \"f\", 2},\n \t{\"oofofoofooo\", \"foo\", 4},\
@@ -1082,6 +1083,24 @@ func TestTitle(t *testing.T) {\n 	}\n }\n \n+var ToTitleTests = []TitleTest{\n+\t{\"\", \"\"},\n+\t{\"a\", \"A\"},\n+\t{\" aaa aaa aaa \", \" AAA AAA AAA \"},\n+\t{\" Aaa Aaa Aaa \", \" AAA AAA AAA \"},\n+\t{\"123a456\", \"123A456\"},\n+\t{\"double-blind\", \"DOUBLE-BLIND\"},\n+\t{\"ÿøû\", \"ŸØÛ\"},\n+}\n+\n+func TestToTitle(t *testing.T) {\n+\tfor _, tt := range ToTitleTests {\n+\t\tif s := string(ToTitle([]byte(tt.in))); s != tt.out {\n+\t\t\tt.Errorf(\"ToTitle(%q) = %q, want %q\", tt.in, s, tt.out)\n+\t\t}\n+\t}\n+}\n+\n var EqualFoldTests = []struct {\n \ts, t string\n \tout  bool\n@@ -1110,6 +1129,37 @@ func TestEqualFold(t *testing.T) {\n \t}\n }\n \n+func TestBufferGrowNegative(t *testing.T) {\n+\tdefer func() {\n+\t\tif err := recover(); err == nil {\n+\t\t\tt.Fatal(\"Grow(-1) should have paniced\")\n+\t\t}\n+\t}()\n+\tvar b Buffer\n+\tb.Grow(-1)\n+}\n+\n+func TestBufferTruncateNegative(t *testing.T) {\n+\tdefer func() {\n+\t\tif err := recover(); err == nil {\n+\t\t\tt.Fatal(\"Truncate(-1) should have paniced\")\n+\t\t}\n+\t}()\n+\tvar b Buffer\n+\tb.Truncate(-1)\n+}\n+\n+func TestBufferTruncateOutOfRange(t *testing.T) {\n+\tdefer func() {\n+\t\tif err := recover(); err == nil {\n+\t\t\tt.Fatal(\"Truncate(20) should have paniced\")\n+\t\t}\n+\t}()\n+\tvar b Buffer\n+\tb.Write(make([]byte, 10))\n+\tb.Truncate(20)\n+}\n+\n var makeFieldsInput = func() []byte {\n \tx := make([]byte, 1<<20)\n \t// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.\
--- a/src/pkg/bytes/reader_test.go
+++ b/src/pkg/bytes/reader_test.go
@@ -113,6 +113,41 @@ func TestReaderWriteTo(t *testing.T) {\n \t}\n }\n \n+func TestReaderLen(t *testing.T) {\n+\tconst data = \"hello world\"\n+\tr := NewReader([]byte(data))\n+\tif got, want := r.Len(), 11; got != want {\n+\t\tt.Errorf(\"r.Len(): got %d, want %d\", got, want)\n+\t}\n+\tif n, err := r.Read(make([]byte, 10)); err != nil || n != 10 {\n+\t\tt.Errorf(\"Read failed: read %d %v\", n, err)\n+\t}\n+\tif got, want := r.Len(), 1; got != want {\n+\t\tt.Errorf(\"r.Len(): got %d, want %d\", got, want)\n+\t}\n+\tif n, err := r.Read(make([]byte, 1)); err != nil || n != 1 {\n+\t\tt.Errorf(\"Read failed: read %d %v\", n, err)\n+\t}\n+\tif got, want := r.Len(), 0; got != want {\n+\t\tt.Errorf(\"r.Len(): got %d, want %d\", got, want)\n+\t}\n+}\n+\n+func TestReaderDoubleUnreadRune(t *testing.T) {\n+\tbuf := NewBuffer([]byte(\"groucho\"))\n+\tif _, _, err := buf.ReadRune(); err != nil {\n+\t\t// should not happen\n+\t\tt.Fatal(err)\n+\t}\n+\tif err := buf.UnreadByte(); err != nil {\n+\t\t// should not happen\n+\t\tt.Fatal(err)\n+\t}\n+\tif err := buf.UnreadByte(); err == nil {\n+\t\t\tt.Fatal(\"UnreadByte: expected error, got nil\")\n+\t}\n+}\n+\n // verify that copying from an empty reader always has the same results,\n // regardless of the presence of a WriteTo method.\n func TestReaderCopyNothing(t *testing.T) {\

コアとなるコードの解説

上記のコード差分は、bytesパッケージのテストファイルに追加された具体的なテストケースを示しています。

src/pkg/bytes/bytes_test.go:

  • indexTestsの追加行:

    +	{"foo", "baz", -1},
    

    これは、bytes.Indexのような関数が、検索対象の文字列("foo")に部分文字列("baz")が含まれていない場合に-1を返すことを確認するテストケースです。これにより、検索失敗時の挙動が正しく処理されることを保証します。

  • ToTitleTestsTestToTitleの追加:

    +var ToTitleTests = []TitleTest{
    +	{"", ""},
    +	{"a", "A"},
    +	{" aaa aaa aaa ", " AAA AAA AAA "},
    +	{" Aaa Aaa Aaa ", " AAA AAA AAA "},
    +	{"123a456", "123A456"},
    +	{"double-blind", "DOUBLE-BLIND"},
    +	{"ÿøû", "ŸØÛ"},
    +}
    +
    +func TestToTitle(t *testing.T) {
    +	for _, tt := range ToTitleTests {
    +		if s := string(ToTitle([]byte(tt.in))); s != tt.out {
    +			t.Errorf("ToTitle(%q) = %q, want %q", tt.in, s, tt.out)
    +		}
    +	}
    +}
    

    bytes.ToTitle関数は、バイトスライス内の各Unicode文字をタイトルケースに変換します。これらのテストケースは、空文字列、単一文字、スペースを含む文字列、数字を含む文字列、ハイフンで連結された文字列、そして多バイト文字(UTF-8)を含む文字列など、様々な入力に対するToTitleの正確な変換を検証します。

  • Bufferのパニックテスト関数群:

    +func TestBufferGrowNegative(t *testing.T) {
    +	defer func() {
    +		if err := recover(); err == nil {
    +			t.Fatal("Grow(-1) should have paniced")
    +		}
    +	}()
    +	var b Buffer
    +	b.Grow(-1)
    +}
    // ... (TestBufferTruncateNegative, TestBufferTruncateOutOfRangeも同様の構造)
    

    これらのテストは、bytes.BufferGrowおよびTruncateメソッドに不正な引数(負の値や範囲外の値)を渡した場合に、Goのランタイムがパニックを発生させることを確認します。deferrecoverのメカニズムを利用してパニックを捕捉し、期待通りにパニックが発生したかどうかを検証しています。これにより、これらのメソッドの契約違反に対する堅牢性が保証されます。

src/pkg/bytes/reader_test.go:

  • TestReaderLenの追加:

    +func TestReaderLen(t *testing.T) {
    +	const data = "hello world"
    +	r := NewReader([]byte(data))
    +	if got, want := r.Len(), 11; got != want {
    +		t.Errorf("r.Len(): got %d, want %d", got, want)
    +	}
    +	if n, err := r.Read(make([]byte, 10)); err != nil || n != 10 {
    +		t.Errorf("Read failed: read %d %v", n, err)
    +	}
    +	if got, want := r.Len(), 1; got != want {
    +		t.Errorf("r.Len(): got %d, want %d", got, want)
    +	}
    +	if n, err := r.Read(make([]byte, 1)); err != nil || n != 1 {
    +		t.Errorf("Read failed: read %d %v", n, err)
    +	}
    +	if got, want := r.Len(), 0; got != want {
    +		t.Errorf("r.Len(): got %d, want %d", got, want)
    +	}
    +}
    

    このテストは、bytes.ReaderLen()メソッドが、読み出し操作の進行に伴って残りのバイト数を正確に返すことを検証します。初期状態、部分的な読み出し後、そして全ての読み出し後のLen()の値をチェックしています。

  • TestReaderDoubleUnreadRuneの追加:

    +func TestReaderDoubleUnreadRune(t *testing.T) {
    +	buf := NewBuffer([]byte("groucho"))
    +	if _, _, err := buf.ReadRune(); err != nil {
    +		// should not happen
    +		t.Fatal(err)
    +	}
    +	if err := buf.UnreadByte(); err != nil {
    +		// should not happen
    +		t.Fatal(err)
    +	}
    +	if err := buf.UnreadByte(); err == nil {
    +		t.Fatal("UnreadByte: expected error, got nil")
    +	}
    +}
    

    このテストは、bytes.Reader(ここではbytes.BufferReaderとして利用)に対してReadRuneの後にUnreadByteを2回連続で呼び出した場合の挙動を検証します。通常、UnreadByteは直前の1バイトのみを戻すため、2回目の呼び出しはエラーとなるべきです。このテストは、そのエラーが正しく発生することを確認し、UnreadByteのセマンティクスを明確にします。

これらのテストの追加により、bytesパッケージの堅牢性と信頼性が向上し、開発者がこれらの機能を使用する際により安全な基盤が提供されます。

関連リンク

参考にした情報源リンク