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

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

このコミットは、Go言語の標準ライブラリであるstringsパッケージに、Count関数のための新しいテストケースを追加するものです。具体的には、src/pkg/strings/strings_test.goファイルにCountTestsというテストデータ構造と、それを利用したTestCount関数が追加されています。これにより、strings.Count関数の様々なエッジケースや一般的な使用シナリオにおける挙動が検証されるようになります。

コミット

commit 14903f65984a113d2558195b2dd862368d1c96ef
Author: Pieter Droogendijk <pieter@binky.org.uk>
Date:   Fri Aug 9 12:51:21 2013 -0700

    strings: add test for Count
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/12541050

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

https://github.com/golang/go/commit/14903f65984a113d2558195b2dd862368d1c96ef

元コミット内容

このコミットの元の内容は、stringsパッケージのCount関数に対するテストを追加することです。これは、既存の関数の堅牢性を高め、将来の変更によるリグレッションを防ぐための標準的な開発プラクティスです。

変更の背景

ソフトウェア開発において、テストは非常に重要な要素です。特に標準ライブラリのような基盤となるコードにおいては、その機能が正確かつ期待通りに動作することを保証するために、包括的なテストスイートが不可欠です。strings.Count関数は、文字列内で特定のサブ文字列が何回出現するかを数えるという、非常に基本的ながらも頻繁に使用される機能を提供します。このコミットは、この重要な関数のテストカバレッジを向上させ、様々な入力パターン(空文字列、単一文字、複数文字、重複するパターンなど)に対する挙動を明示的に検証することを目的としています。これにより、関数の信頼性が向上し、将来のGo言語のバージョンアップや最適化の際に、意図しない副作用が発生するリスクを低減できます。

前提知識の解説

Go言語のstringsパッケージ

stringsパッケージは、Go言語の標準ライブラリの一部であり、UTF-8でエンコードされた文字列を操作するための多くの便利な関数を提供します。これには、文字列の検索、置換、分割、結合、大文字・小文字変換などが含まれます。このパッケージは、Goプログラムで文字列データを扱う際の基本的なツールセットとなります。

strings.Count関数

strings.Count(s, sep string) int関数は、文字列s内で、非オーバーラップなsepの出現回数を数えて返します。 重要な特性として、以下の点が挙げられます。

  • sepが空文字列の場合、CountはUTF-8エンコードされたsの文字数 + 1 を返します。これは、空文字列が各文字の間と文字列の最初と最後に存在すると見なされるためです。
  • sが空文字列の場合、sepが空文字列であれば1を返し、そうでなければ0を返します。

Go言語のテストフレームワーク

Go言語には、標準で軽量なテストフレームワークが組み込まれています。テストファイルは通常、テスト対象のソースファイルと同じディレクトリに配置され、ファイル名の末尾に_test.goが付きます。テスト関数はTestで始まり、*testing.T型の引数を取ります。テストの実行はgo testコマンドで行われます。*testing.Tオブジェクトは、テストの失敗を報告したり、ログを出力したりするためのメソッド(例: t.Errorf, t.Fatalf, t.Logf)を提供します。

テーブルドリブンテスト (Table-Driven Tests)

Go言語のテストでよく用いられるパターンの一つに「テーブルドリブンテスト」があります。これは、テストケースの入力と期待される出力を構造体のスライス(テーブル)として定義し、そのスライスをループで回しながら各テストケースを実行する手法です。この方法の利点は以下の通りです。

  • 可読性: テストケースが一覧で分かりやすく、どのようなシナリオをテストしているかが一目で理解できます。
  • 保守性: 新しいテストケースを追加する際に、新しい構造体要素をスライスに追加するだけで済み、テストロジック自体を変更する必要がありません。
  • 簡潔性: 繰り返し行うテストロジックを一度書くだけで済みます。

このコミットで追加されたCountTestsは、まさにこのテーブルドリブンテストの典型的な例です。

技術的詳細

このコミットは、strings.Count関数のテストカバレッジを強化するために、src/pkg/strings/strings_test.goファイルに新しいテストデータとテスト関数を追加しています。

追加されたCountTestsスライスは、以下のフィールドを持つ匿名構造体の要素で構成されています。

  • s: strings.Count関数の第一引数となる文字列。
  • sep: strings.Count関数の第二引数となるサブ文字列。
  • num: strings.Count関数が返すことが期待される結果(出現回数)。

このスライスには、strings.Countの挙動を検証するための様々なエッジケースと一般的なケースが含まれています。

  • {"", "", 1}: ssepも空文字列の場合。期待値は1。
  • {"", "notempty", 0}: sが空文字列でsepが空でない場合。期待値は0。
  • {"notempty", "", 9}: sが空でなくsepが空文字列の場合。sの文字数(8)+1で9。
  • {"smaller", "not smaller", 0}: sepsより長い場合。期待値は0。
  • {"12345678987654321", "6", 2}: sepが単一文字で複数回出現する場合。
  • {"611161116", "6", 3}: sepが単一文字で複数回出現し、文字列の最初と最後にも出現する場合。
  • {"notequal", "NotEqual", 0}: 大文字・小文字が異なるため一致しない場合。Countはケースセンシティブ。
  • {"equal", "equal", 1}: 完全一致する場合。
  • {"abc1231231123q", "123", 3}: sepが複数文字で複数回出現する場合。
  • {"11111", "11", 2}: seps内でオーバーラップして出現する可能性があるが、Countは非オーバーラップで数えるため、"11"が2回出現する("11" + "11" + "1")。

TestCount関数は、このCountTestsスライスをループで処理し、各テストケースに対してstrings.Count関数を呼び出します。そして、実際の戻り値(num)が期待される値(tt.num)と異なる場合に、t.Errorfを使用してテストの失敗を報告します。t.Errorfは、テストが失敗したことを示し、指定されたフォーマット文字列と引数でエラーメッセージを出力します。

この追加により、strings.Count関数の動作がより厳密に検証され、特に空文字列やオーバーラップするパターンといった、直感と異なる挙動をする可能性のあるケースが明示的にカバーされることになります。

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

--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -1010,6 +1010,30 @@ func TestEqualFold(t *testing.T) {
 	}\n }\n \n+var CountTests = []struct {\n+\ts, sep string\n+\tnum    int\n+}{\n+\t{\"\", \"\", 1},\n+\t{\"\", \"notempty\", 0},\n+\t{\"notempty\", \"\", 9},\n+\t{\"smaller\", \"not smaller\", 0},\n+\t{\"12345678987654321\", \"6\", 2},\n+\t{\"611161116\", \"6\", 3},\n+\t{\"notequal\", \"NotEqual\", 0},\n+\t{\"equal\", \"equal\", 1},\n+\t{\"abc123123123q\", \"123\", 3},\n+\t{\"11111\", \"11\", 2},\n+}\n+\n+func TestCount(t *testing.T) {\n+\tfor _, tt := range CountTests {\n+\t\tif num := Count(tt.s, tt.sep); num != tt.num {\n+\t\t\tt.Errorf(\"Count(\\\"%s\\\", \\\"%s\\\") = %d, want %d\", tt.s, tt.sep, num, tt.num)\n+\t\t}\n+\t}\n+}\n+\n func makeBenchInputHard() string {\n \ttokens := [...]string{\n \t\t\"<a>\", \"<p>\", \"<b>\", \"<strong>\",\n```

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

上記の差分は、`src/pkg/strings/strings_test.go`ファイルに追加された新しいテストコードを示しています。

1.  **`CountTests`変数の定義**:
    ```go
    var CountTests = []struct {
    	s, sep string
    	num    int
    }{
    	{"", "", 1},
    	{"", "notempty", 0},
    	{"notempty", "", 9},
    	{"smaller", "not smaller", 0},
    	{"12345678987654321", "6", 2},
    	{"611161116", "6", 3},
    	{"notequal", "NotEqual", 0},
    	{"equal", "equal", 1},
    	{"abc123123123q", "123", 3},
    	{"11111", "11", 2},
    }
    ```
    これは、`strings.Count`関数のテストケースを定義するテーブルです。各要素は匿名構造体であり、テスト対象の文字列`s`、検索するサブ文字列`sep`、そして期待されるカウント結果`num`を保持しています。このテーブルには、空文字列の扱い、部分文字列が長い場合、単一文字のカウント、複数文字のカウント、そして非オーバーラップカウントの挙動(例: `"11111"`と`"11"`の場合)など、様々なシナリオが含まれています。

2.  **`TestCount`関数の定義**:
    ```go
    func TestCount(t *testing.T) {
    	for _, tt := range CountTests {
    		if num := Count(tt.s, tt.sep); num != tt.num {
    			t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num)
    		}
    	}
    }
    ```
    この関数は、`strings.Count`関数のテストを実行します。
    *   `for _, tt := range CountTests`: `CountTests`テーブルの各テストケースをループで処理します。`tt`は現在のテストケース(匿名構造体)を表します。
    *   `if num := Count(tt.s, tt.sep); num != tt.num`: `strings.Count`関数を呼び出し、実際の戻り値`num`と、テストケースで定義された期待値`tt.num`を比較します。
    *   `t.Errorf(...)`: もし`num`と`tt.num`が一致しない場合、`t.Errorf`が呼び出され、テストが失敗したことを報告します。エラーメッセージには、入力文字列`s`と`sep`、実際のカウント結果`num`、そして期待されるカウント結果`tt.num`が含まれるため、どのテストケースがどのような理由で失敗したのかを明確に把握できます。

この変更により、`strings.Count`関数の堅牢性が向上し、将来のGo言語のバージョンアップにおいてもその正確な動作が保証されるようになります。

## 関連リンク

*   Go言語 `strings` パッケージのドキュメント: [https://pkg.go.dev/strings](https://pkg.go.dev/strings)
*   Go言語のテストに関する公式ドキュメント: [https://go.dev/doc/tutorial/add-a-test](https://go.dev/doc/tutorial/add-a-test)

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

*   Go言語の公式ドキュメント
*   Go言語のテストに関する一般的なプラクティス(テーブルドリブンテストなど)に関する情報
*   `strings.Count`関数の具体的な挙動に関するGo言語のソースコードおよびドキュメント
# [インデックス 17131] ファイルの概要

このコミットは、Go言語の標準ライブラリである`strings`パッケージに、`Count`関数のための新しいテストケースを追加するものです。具体的には、`src/pkg/strings/strings_test.go`ファイルに`CountTests`というテストデータ構造と、それを利用した`TestCount`関数が追加されています。これにより、`strings.Count`関数の様々なエッジケースや一般的な使用シナリオにおける挙動が検証されるようになります。

## コミット

commit 14903f65984a113d2558195b2dd862368d1c96ef Author: Pieter Droogendijk pieter@binky.org.uk Date: Fri Aug 9 12:51:21 2013 -0700

strings: add test for Count

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12541050

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

[https://github.com/golang/go/commit/14903f65984a113d2558195b2dd862368d1c96ef](https://github.com/golang/go/commit/14903f65984a113d2558195b2dd862368d1c96ef)

## 元コミット内容

このコミットの元の内容は、`strings`パッケージの`Count`関数に対するテストを追加することです。これは、既存の関数の堅牢性を高め、将来の変更によるリグレッションを防ぐための標準的な開発プラクティスです。

## 変更の背景

ソフトウェア開発において、テストは非常に重要な要素です。特に標準ライブラリのような基盤となるコードにおいては、その機能が正確かつ期待通りに動作することを保証するために、包括的なテストスイートが不可欠です。`strings.Count`関数は、文字列内で特定のサブ文字列が何回出現するかを数えるという、非常に基本的ながらも頻繁に使用される機能を提供します。このコミットは、この重要な関数のテストカバレッジを向上させ、様々な入力パターン(空文字列、単一文字、複数文字、重複するパターンなど)に対する挙動を明示的に検証することを目的としています。これにより、関数の信頼性が向上し、将来のGo言語のバージョンアップや最適化の際に、意図しない副作用が発生するリスクを低減できます。

## 前提知識の解説

### Go言語の`strings`パッケージ

`strings`パッケージは、Go言語の標準ライブラリの一部であり、UTF-8でエンコードされた文字列を操作するための多くの便利な関数を提供します。これには、文字列の検索、置換、分割、結合、大文字・小文字変換などが含まれます。このパッケージは、Goプログラムで文字列データを扱う際の基本的なツールセットとなります。

### `strings.Count`関数

`strings.Count(s, sep string) int`関数は、文字列`s`内で、非オーバーラップな`sep`の出現回数を数えて返します。
重要な特性として、以下の点が挙げられます。
*   `sep`が空文字列の場合、`Count`はUTF-8エンコードされた`s`の文字数 + 1 を返します。これは、空文字列が各文字の間と文字列の最初と最後に存在すると見なされるためです。例えば、`strings.Count("A", "")`は2を返し、`strings.Count("ABC", "")`は4を返します。これは、空文字列が挿入されうる「境界」の数を数えていると解釈できます。
*   `s`が空文字列の場合、`sep`が空文字列であれば1を返し、そうでなければ0を返します。

### Go言語のテストフレームワーク

Go言語には、標準で軽量なテストフレームワークが組み込まれています。テストファイルは通常、テスト対象のソースファイルと同じディレクトリに配置され、ファイル名の末尾に`_test.go`が付きます。テスト関数は`Test`で始まり、`*testing.T`型の引数を取ります。テストの実行は`go test`コマンドで行われます。`*testing.T`オブジェクトは、テストの失敗を報告したり、ログを出力したりするためのメソッド(例: `t.Errorf`, `t.Fatalf`, `t.Logf`)を提供します。

### テーブルドリブンテスト (Table-Driven Tests)

Go言語のテストでよく用いられるパターンの一つに「テーブルドリブンテスト」があります。これは、テストケースの入力と期待される出力を構造体のスライス(テーブル)として定義し、そのスライスをループで回しながら各テストケースを実行する手法です。この方法の利点は以下の通りです。
*   **可読性**: テストケースが一覧で分かりやすく、どのようなシナリオをテストしているかが一目で理解できます。
*   **保守性**: 新しいテストケースを追加する際に、新しい構造体要素をスライスに追加するだけで済み、テストロジック自体を変更する必要がありません。
*   **簡潔性**: 繰り返し行うテストロジックを一度書くだけで済みます。

このコミットで追加された`CountTests`は、まさにこのテーブルドリブンテストの典型的な例です。

## 技術的詳細

このコミットは、`strings.Count`関数のテストカバレッジを強化するために、`src/pkg/strings/strings_test.go`ファイルに新しいテストデータとテスト関数を追加しています。

追加された`CountTests`スライスは、以下のフィールドを持つ匿名構造体の要素で構成されています。
*   `s`: `strings.Count`関数の第一引数となる文字列。
*   `sep`: `strings.Count`関数の第二引数となるサブ文字列。
*   `num`: `strings.Count`関数が返すことが期待される結果(出現回数)。

このスライスには、`strings.Count`の挙動を検証するための様々なエッジケースと一般的なケースが含まれています。

*   `{"", "", 1}`: `s`も`sep`も空文字列の場合。期待値は1。
*   `{"", "notempty", 0}`: `s`が空文字列で`sep`が空でない場合。期待値は0。
*   `{"notempty", "", 9}`: `s`が空でなく`sep`が空文字列の場合。`s`の文字数(8)+1で9。
*   `{"smaller", "not smaller", 0}`: `sep`が`s`より長い場合。期待値は0。
*   `{"12345678987654321", "6", 2}`: `sep`が単一文字で複数回出現する場合。
*   `{"611161116", "6", 3}`: `sep`が単一文字で複数回出現し、文字列の最初と最後にも出現する場合。
*   `{"notequal", "NotEqual", 0}`: 大文字・小文字が異なるため一致しない場合。`Count`はケースセンシティブ。
*   `{"equal", "equal", 1}`: 完全一致する場合。
*   `{"abc1231231123q", "123", 3}`: `sep`が複数文字で複数回出現する場合。
*   `{"11111", "11", 2}`: `sep`が`s`内でオーバーラップして出現する可能性があるが、`Count`は非オーバーラップで数えるため、`"11"`が2回出現する(`"11"` + `"11"` + `"1"`)。

`TestCount`関数は、この`CountTests`スライスをループで処理し、各テストケースに対して`strings.Count`関数を呼び出します。そして、実際の戻り値(`num`)が期待される値(`tt.num`)と異なる場合に、`t.Errorf`を使用してテストの失敗を報告します。`t.Errorf`は、テストが失敗したことを示し、指定されたフォーマット文字列と引数でエラーメッセージを出力します。

この追加により、`strings.Count`関数の動作がより厳密に検証され、特に空文字列やオーバーラップするパターンといった、直感と異なる挙動をする可能性のあるケースが明示的にカバーされることになります。

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

```diff
--- a/src/pkg/strings/strings_test.go
+++ b/src/pkg/strings/strings_test.go
@@ -1010,6 +1010,30 @@ func TestEqualFold(t *testing.T) {
 	}\n }\n \n+var CountTests = []struct {\n+\ts, sep string\n+\tnum    int\n+}{\n+\t{\"\", \"\", 1},\n+\t{\"\", \"notempty\", 0},\n+\t{\"notempty\", \"\", 9},\n+\t{\"smaller\", \"not smaller\", 0},\n+\t{\"12345678987654321\", \"6\", 2},\n+\t{\"611161116\", \"6\", 3},\n+\t{\"notequal\", \"NotEqual\", 0},\n+\t{\"equal\", \"equal\", 1},\n+\t{\"abc123123123q\", \"123\", 3},\n+\t{\"11111\", \"11\", 2},\n+}\n+\n+func TestCount(t *testing.T) {\n+\tfor _, tt := range CountTests {\n+\t\tif num := Count(tt.s, tt.sep); num != tt.num {\n+\t\t\tt.Errorf(\"Count(\\\"%s\\\", \\\"%s\\\") = %d, want %d\", tt.s, tt.sep, num, tt.num)\n+\t\t}\n+\t}\n+}\n+\n func makeBenchInputHard() string {\n \ttokens := [...]string{\n \t\t\"<a>\", \"<p>\", \"<b>\", \"<strong>\",\n```

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

上記の差分は、`src/pkg/strings/strings_test.go`ファイルに追加された新しいテストコードを示しています。

1.  **`CountTests`変数の定義**:
    ```go
    var CountTests = []struct {
    	s, sep string
    	num    int
    }{
    	{"", "", 1},
    	{"", "notempty", 0},
    	{"notempty", "", 9},
    	{"smaller", "not smaller", 0},
    	{"12345678987654321", "6", 2},
    	{"611161116", "6", 3},
    	{"notequal", "NotEqual", 0},
    	{"equal", "equal", 1},
    	{"abc123123123q", "123", 3},
    	{"11111", "11", 2},
    }
    ```
    これは、`strings.Count`関数のテストケースを定義するテーブルです。各要素は匿名構造体であり、テスト対象の文字列`s`、検索するサブ文字列`sep`、そして期待されるカウント結果`num`を保持しています。このテーブルには、空文字列の扱い、部分文字列が長い場合、単一文字のカウント、複数文字のカウント、そして非オーバーラップカウントの挙動(例: `"11111"`と`"11"`の場合)など、様々なシナリオが含まれています。

2.  **`TestCount`関数の定義**:
    ```go
    func TestCount(t *testing.T) {
    	for _, tt := range CountTests {
    		if num := Count(tt.s, tt.sep); num != tt.num {
    			t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num)
    		}
    	}
    }
    ```
    この関数は、`strings.Count`関数のテストを実行します。
    *   `for _, tt := range CountTests`: `CountTests`テーブルの各テストケースをループで処理します。`tt`は現在のテストケース(匿名構造体)を表します。
    *   `if num := Count(tt.s, tt.sep); num != tt.num`: `strings.Count`関数を呼び出し、実際の戻り値`num`と、テストケースで定義された期待値`tt.num`を比較します。
    *   `t.Errorf(...)`: もし`num`と`tt.num`が一致しない場合、`t.Errorf`が呼び出され、テストが失敗したことを報告します。エラーメッセージには、入力文字列`s`と`sep`、実際のカウント結果`num`、そして期待されるカウント結果`tt.num`が含まれるため、どのテストケースがどのような理由で失敗したのかを明確に把握できます。

この変更により、`strings.Count`関数の堅牢性が向上し、将来のGo言語のバージョンアップにおいてもその正確な動作が保証されるようになります。

## 関連リンク

*   Go言語 `strings` パッケージのドキュメント: [https://pkg.go.dev/strings](https://pkg.go.dev/strings)
*   Go言語のテストに関する公式ドキュメント: [https://go.dev/doc/tutorial/add-a-test](https://go.dev/doc/tutorial/add-a-test)

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

*   Go言語の公式ドキュメント
*   Go言語のテストに関する一般的なプラクティス(テーブルドリブンテストなど)に関する情報
*   `strings.Count`関数の具体的な挙動に関するGo言語のソースコードおよびドキュメント
*   Go strings.Count empty string behavior: [https://www.google.com/search?q=Go+strings.Count+empty+string+behavior](https://www.google.com/search?q=Go+strings.Count+empty+string+behavior)