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

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

このコミットは、Go言語の標準ライブラリnetパッケージのテストコードnet_test.goにおけるgo tool vetによって指摘された軽微な修正(nits)を適用するものです。具体的には、テスト失敗時にエラーメッセージを出力してテストを終了させるt.Fatal関数の呼び出しを、フォーマット文字列と引数を取るt.Fatalf関数に置き換えています。これにより、go tool vetの警告を解消し、テストコードの品質と一貫性を向上させています。

コミット

commit e5a7ab8550e3725a52301586e8e99ee9845de91d
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Tue Dec 10 14:30:52 2013 +0900

    net: fix nits found by go tool vet
    
    R=golang-dev, dave, adg
    CC=golang-dev
    https://golang.org/cl/27430043

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

https://github.com/golang/go/commit/e5a7ab8550e3725a52301586e8e99ee9845de91d

元コミット内容

net: fix nits found by go tool vet

R=golang-dev, dave, adg
CC=golang-dev
https://golang.org/cl/27430043

変更の背景

このコミットの背景には、Go言語の公式ツールであるgo tool vetの利用と、テストコードにおけるエラー報告のベストプラクティスがあります。

go tool vetは、Goのソースコードを静的に解析し、疑わしい構造や潜在的なバグを検出するツールです。これには、フォーマット文字列の引数と実際の引数の不一致、到達不能なコード、ロックの誤用など、様々な種類の問題が含まれます。このコミットで修正されたのは、t.Fatalt.Fatalfの使い分けに関するvetの警告です。

Goのtestingパッケージにおいて、t.Fatalは引数をそのままエラーメッセージとして出力し、テストを即座に終了させます。一方、t.Fatalffmt.Printfと同様にフォーマット文字列と可変引数を取り、フォーマットされたメッセージを出力してテストを終了させます。

元のコードでは、t.Fatal("Listen 127.0.0.1:0: %v", err)のように、t.Fatalにフォーマット文字列と追加の引数を渡していました。これはt.Fatalの正しい使い方ではありません。t.Fatalは単一の引数(または複数の引数をスペース区切りで結合したもの)を期待しており、フォーマット文字列として解釈しません。そのため、%vのようなフォーマット指示子はそのまま出力され、errの値は表示されません。go tool vetはこの誤用を検出し、t.Fatalfの使用を推奨します。

この変更は、テストコードの可読性とデバッグのしやすさを向上させるとともに、go tool vetの警告を解消し、コードベース全体の品質基準を維持することを目的としています。

前提知識の解説

Go言語のtestingパッケージ

Go言語には、標準ライブラリとしてtestingパッケージが提供されており、ユニットテストやベンチマークテストを簡単に記述できます。テストファイルは通常、テスト対象のGoファイルと同じディレクトリに_test.goというサフィックスを付けて配置されます。

testingパッケージの主要な型は*testing.Tで、テスト関数はこの型の引数を取ります。*testing.Tは、テストの失敗を報告したり、ログを出力したりするための様々なメソッドを提供します。

  • t.Error(args ...interface{}): テストを失敗としてマークしますが、テストの実行は継続します。引数はスペース区切りで結合され、エラーメッセージとして出力されます。
  • t.Errorf(format string, args ...interface{}): t.Errorと同様にテストを失敗としてマークし、実行を継続しますが、fmt.Printf形式のフォーマット文字列と引数を受け取ります。
  • t.Fail(): テストを失敗としてマークしますが、実行は継続します。メッセージは出力しません。
  • t.FailNow(): テストを失敗としてマークし、現在のテストゴルーチンを即座に終了させます。defer関数は実行されます。
  • t.Fatal(args ...interface{}): t.Errorと同様にメッセージを出力しますが、t.FailNow()を呼び出してテストを即座に終了させます。
  • t.Fatalf(format string, args ...interface{}): t.Errorfと同様にフォーマットされたメッセージを出力し、t.FailNow()を呼び出してテストを即座に終了させます。

このコミットでは、t.Fatalt.Fatalfの使い分けが焦点となっています。

go tool vet

go tool vetは、Go言語のソースコードを静的に解析し、潜在的なエラーや疑わしいコード構造を検出するコマンドラインツールです。GoのSDKに標準で含まれており、開発者がコードの品質を維持し、一般的なプログラミングミスを避けるのに役立ちます。

vetが検出する問題の例:

  • Printf系の関数(fmt.Printf, log.Printf, t.Fatalfなど)におけるフォーマット文字列と引数の不一致。例えば、%dが指定されているのに文字列が渡されている場合など。
  • t.Fatalt.Errorにフォーマット文字列と引数が渡されている場合(このコミットで修正された問題)。
  • 構造体のフィールドタグの誤り。
  • ロックの誤用(例: sync.Mutexをコピーして使用している場合)。
  • 到達不能なコード。
  • 誤ったアトミック操作。

go tool vetは、コンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを特定するのに特に有効です。CI/CDパイプラインに組み込むことで、コードレビューの前に自動的にこれらの問題を検出できます。

技術的詳細

このコミットの技術的な変更は非常にシンプルですが、その背後にある原則は重要です。

Goのtestingパッケージにおけるt.Fatalt.Fatalfの動作は以下の通りです。

  • func (t *T) Fatal(args ...interface{}): このメソッドは、可変引数argsを受け取ります。これらの引数は、fmt.Sprint(args...)を使って文字列に変換され、エラーメッセージとして出力されます。その後、t.FailNow()が呼び出され、現在のテスト関数(ゴルーチン)が即座に終了します。重要なのは、argsがフォーマット文字列として解釈されない点です。

  • func (t *T) Fatalf(format string, args ...interface{}): このメソッドは、formatというフォーマット文字列と、それに続く可変引数argsを受け取ります。これらの引数は、fmt.Sprintf(format, args...)を使ってフォーマットされ、エラーメッセージとして出力されます。その後、t.FailNow()が呼び出され、現在のテスト関数が即座に終了します。こちらはfmt.Printfと同様の動作をします。

元のコードでは、以下のようにt.Fatalが使われていました。

t.Fatal("Listen 127.0.0.1:0: %v", err)

この場合、t.Fatalは2つの引数を受け取ります。1つ目は文字列"Listen 127.0.0.1:0: %v"、2つ目は変数errです。t.Fatalはこれらをスペース区切りで結合して出力するため、実際には以下のようなメッセージが出力されます(errsome errorだった場合):

Listen 127.0.0.1:0: %v some error

%vがフォーマット指示子として機能せず、そのまま出力されてしまいます。これは意図した動作ではありません。開発者はerrの値を%vの位置に埋め込みたかったはずです。

go tool vetは、このようなPrintf系の関数ではないものにフォーマット文字列と追加の引数が渡されているパターンを検出します。そして、このコミットでは、この問題を修正するためにt.Fatalfに置き換えられました。

t.Fatalf("Listen 127.0.0.1:0: %v", err)

これにより、t.Fatalf"Listen 127.0.0.1:0: %v"をフォーマット文字列として解釈し、errの値を%vの位置に適切に埋め込んで出力します。例えば、err"address already in use"だった場合、出力は以下のようになります。

Listen 127.0.0.1:0: address already in use

この変更は、テストのエラーメッセージがより正確で、デバッグに役立つ情報を提供するようにするためのものです。

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

変更はsrc/pkg/net/net_test.goファイルのみです。

--- a/src/pkg/net/net_test.go
+++ b/src/pkg/net/net_test.go
@@ -231,12 +231,12 @@ func TestErrorNil(t *testing.T) {
 	// Make Listen fail by relistening on the same address.
 	l, err := Listen("tcp", "127.0.0.1:0")
 	if err != nil {
-		t.Fatal("Listen 127.0.0.1:0: %v", err)
+		t.Fatalf("Listen 127.0.0.1:0: %v", err)
 	}
 	defer l.Close()
 	l1, err := Listen("tcp", l.Addr().String())
 	if err == nil {
-		t.Fatal("second Listen %v: %v", l.Addr(), err)
+		t.Fatalf("second Listen %v: %v", l.Addr(), err)
 	}
 	if l1 != nil {
 		t.Fatalf("Listen returned non-nil interface %T(%v) with err != nil", l1, l1)
@@ -245,12 +245,12 @@ func TestErrorNil(t *testing.T) {
 	// Make ListenPacket fail by relistening on the same address.
 	lp, err := ListenPacket("udp", "127.0.0.1:0")
 	if err != nil {
-		t.Fatal("Listen 127.0.0.1:0: %v", err)
+		t.Fatalf("Listen 127.0.0.1:0: %v", err)
 	}
 	defer lp.Close()
 	lp1, err := ListenPacket("udp", lp.LocalAddr().String())
 	if err == nil {
-		t.Fatal("second Listen %v: %v", lp.LocalAddr(), err)
+		t.Fatalf("second Listen %v: %v", lp.LocalAddr(), err)
 	}
 	if lp1 != nil {
 		t.Fatalf("ListenPacket returned non-nil interface %T(%v) with err != nil", lp1, lp1)

コアとなるコードの解説

TestErrorNil関数は、net.Listenおよびnet.ListenPacketがエラーを適切に処理するかどうかをテストしています。具体的には、既に使われているアドレスに再度Listenしようとした場合にエラーが発生することを確認しています。

変更前は、エラーが発生した場合にt.Fatalを使用していました。

// 変更前
t.Fatal("Listen 127.0.0.1:0: %v", err)

これは、t.Fatalがフォーマット文字列を解釈しないため、%vがそのまま出力され、errの値がその後に続くという、意図しない結果をもたらしていました。

変更後は、t.Fatalfを使用しています。

// 変更後
t.Fatalf("Listen 127.0.0.1:0: %v", err)

t.Fatalffmt.Printfと同様にフォーマット文字列を解釈するため、%verrの実際の値に置き換えられ、より意味のあるエラーメッセージが出力されるようになります。これにより、テストが失敗した際に、何が問題だったのかを正確に把握しやすくなります。

この修正は、TestErrorNil関数内の4箇所すべてに適用されており、ListenListenPacketの両方のテストケースで同様の改善が行われています。

関連リンク

  • Go言語 testing パッケージのドキュメント: https://pkg.go.dev/testing
  • Go言語 fmt パッケージのドキュメント: https://pkg.go.dev/fmt
  • go tool vetに関する公式ドキュメントやブログ記事(Goのバージョンによって内容が異なる場合がありますが、基本的な機能は共通です)

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • go tool vetに関する一般的な情報源
  • Go言語のテストに関するチュートリアルやブログ記事
  • このコミットのGo Gerritレビューページ: https://golang.org/cl/27430043 (コミットメッセージに記載されているリンク)
  • t.Fatalt.Fatalfの具体的な動作に関するGoのソースコード(src/testing/testing.go
  • fmt.Sprintfmt.Sprintfの動作に関するGoのソースコード(src/fmt/print.go)I have generated the detailed technical explanation in Markdown format, following all the instructions, including the specific chapter structure and language. I have also incorporated information about go tool vet and the difference between t.Fatal and t.Fatalf as requested.

I will now output the generated Markdown to standard output.

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

このコミットは、Go言語の標準ライブラリ`net`パッケージのテストコード`net_test.go`における`go tool vet`によって指摘された軽微な修正(nits)を適用するものです。具体的には、テスト失敗時にエラーメッセージを出力してテストを終了させる`t.Fatal`関数の呼び出しを、フォーマット文字列と引数を取る`t.Fatalf`関数に置き換えています。これにより、`go tool vet`の警告を解消し、テストコードの品質と一貫性を向上させています。

## コミット

commit e5a7ab8550e3725a52301586e8e99ee9845de91d Author: Mikio Hara mikioh.mikioh@gmail.com Date: Tue Dec 10 14:30:52 2013 +0900

net: fix nits found by go tool vet

R=golang-dev, dave, adg
CC=golang-dev
https://golang.org/cl/27430043

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

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

## 元コミット内容

net: fix nits found by go tool vet

R=golang-dev, dave, adg CC=golang-dev https://golang.org/cl/27430043


## 変更の背景

このコミットの背景には、Go言語の公式ツールである`go tool vet`の利用と、テストコードにおけるエラー報告のベストプラクティスがあります。

`go tool vet`は、Goのソースコードを静的に解析し、疑わしい構造や潜在的なバグを検出するツールです。これには、フォーマット文字列の引数と実際の引数の不一致、到達不能なコード、ロックの誤用など、様々な種類の問題が含まれます。このコミットで修正されたのは、`t.Fatal`と`t.Fatalf`の使い分けに関する`vet`の警告です。

Goの`testing`パッケージにおいて、`t.Fatal`は引数をそのままエラーメッセージとして出力し、テストを即座に終了させます。一方、`t.Fatalf`は`fmt.Printf`と同様にフォーマット文字列と可変引数を取り、フォーマットされたメッセージを出力してテストを終了させます。

元のコードでは、`t.Fatal("Listen 127.0.0.1:0: %v", err)`のように、`t.Fatal`にフォーマット文字列と追加の引数を渡していました。これは`t.Fatal`の正しい使い方ではありません。`t.Fatal`は単一の引数(または複数の引数をスペース区切りで結合したもの)を期待しており、フォーマット文字列として解釈しません。そのため、`%v`のようなフォーマット指示子はそのまま出力され、`err`の値は表示されません。`go tool vet`はこの誤用を検出し、`t.Fatalf`の使用を推奨します。

この変更は、テストコードの可読性とデバッグのしやすさを向上させるとともに、`go tool vet`の警告を解消し、コードベース全体の品質基準を維持することを目的としています。

## 前提知識の解説

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

Go言語には、標準ライブラリとして`testing`パッケージが提供されており、ユニットテストやベンチマークテストを簡単に記述できます。テストファイルは通常、テスト対象のGoファイルと同じディレクトリに`_test.go`というサフィックスを付けて配置されます。

`testing`パッケージの主要な型は`*testing.T`で、テスト関数はこの型の引数を取ります。`*testing.T`は、テストの失敗を報告したり、ログを出力したりするための様々なメソッドを提供します。

*   **`t.Error(args ...interface{})`**: テストを失敗としてマークしますが、テストの実行は継続します。引数はスペース区切りで結合され、エラーメッセージとして出力されます。
*   **`t.Errorf(format string, args ...interface{})`**: `t.Error`と同様にテストを失敗としてマークし、実行を継続しますが、`fmt.Printf`形式のフォーマット文字列と引数を受け取ります。
*   **`t.Fail()`**: テストを失敗としてマークしますが、実行は継続します。メッセージは出力しません。
*   **`t.FailNow()`**: テストを失敗としてマークし、現在のテストゴルーチンを即座に終了させます。`defer`関数は実行されます。
*   **`t.Fatal(args ...interface{})`**: `t.Error`と同様にメッセージを出力しますが、`t.FailNow()`を呼び出してテストを即座に終了させます。
*   **`t.Fatalf(format string, args ...interface{})`**: `t.Errorf`と同様にフォーマットされたメッセージを出力し、`t.FailNow()`を呼び出してテストを即座に終了させます。

このコミットでは、`t.Fatal`と`t.Fatalf`の使い分けが焦点となっています。

### `go tool vet`

`go tool vet`は、Go言語のソースコードを静的に解析し、潜在的なエラーや疑わしいコード構造を検出するコマンドラインツールです。GoのSDKに標準で含まれており、開発者がコードの品質を維持し、一般的なプログラミングミスを避けるのに役立ちます。

`vet`が検出する問題の例:
*   `Printf`系の関数(`fmt.Printf`, `log.Printf`, `t.Fatalf`など)におけるフォーマット文字列と引数の不一致。例えば、`%d`が指定されているのに文字列が渡されている場合など。
*   `t.Fatal`や`t.Error`にフォーマット文字列と引数が渡されている場合(このコミットで修正された問題)。
*   構造体のフィールドタグの誤り。
*   ロックの誤用(例: `sync.Mutex`をコピーして使用している場合)。
*   到達不能なコード。
*   誤ったアトミック操作。

`go tool vet`は、コンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを特定するのに特に有効です。CI/CDパイプラインに組み込むことで、コードレビューの前に自動的にこれらの問題を検出できます。

## 技術的詳細

このコミットの技術的な変更は非常にシンプルですが、その背後にある原則は重要です。

Goの`testing`パッケージにおける`t.Fatal`と`t.Fatalf`の動作は以下の通りです。

*   `func (t *T) Fatal(args ...interface{})`:
    このメソッドは、可変引数`args`を受け取ります。これらの引数は、`fmt.Sprint(args...)`を使って文字列に変換され、エラーメッセージとして出力されます。その後、`t.FailNow()`が呼び出され、現在のテスト関数(ゴルーチン)が即座に終了します。重要なのは、`args`がフォーマット文字列として解釈されない点です。

*   `func (t *T) Fatalf(format string, args ...interface{})`:
    このメソッドは、`format`というフォーマット文字列と、それに続く可変引数`args`を受け取ります。これらの引数は、`fmt.Sprintf(format, args...)`を使ってフォーマットされ、エラーメッセージとして出力されます。その後、`t.FailNow()`が呼び出され、現在のテスト関数が即座に終了します。こちらは`fmt.Printf`と同様の動作をします。

元のコードでは、以下のように`t.Fatal`が使われていました。

```go
t.Fatal("Listen 127.0.0.1:0: %v", err)

この場合、t.Fatalは2つの引数を受け取ります。1つ目は文字列"Listen 127.0.0.1:0: %v"、2つ目は変数errです。t.Fatalはこれらをスペース区切りで結合して出力するため、実際には以下のようなメッセージが出力されます(errsome errorだった場合):

Listen 127.0.0.1:0: %v some error

%vがフォーマット指示子として機能せず、そのまま出力されてしまいます。これは意図した動作ではありません。開発者はerrの値を%vの位置に埋め込みたかったはずです。

go tool vetは、このようなPrintf系の関数ではないものにフォーマット文字列と追加の引数が渡されているパターンを検出します。そして、このコミットでは、この問題を修正するためにt.Fatalfに置き換えられました。

t.Fatalf("Listen 127.0.0.1:0: %v", err)

これにより、t.Fatalf"Listen 127.0.0.1:0: %v"をフォーマット文字列として解釈し、errの値を%vの位置に適切に埋め込んで出力します。例えば、err"address already in use"だった場合、出力は以下のようになります。

Listen 127.0.0.1:0: address already in use

この変更は、テストのエラーメッセージがより正確で、デバッグに役立つ情報を提供するようにするためのものです。

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

変更はsrc/pkg/net/net_test.goファイルのみです。

--- a/src/pkg/net/net_test.go
+++ b/src/pkg/net/net_test.go
@@ -231,12 +231,12 @@ func TestErrorNil(t *testing.T) {
 	// Make Listen fail by relistening on the same address.
 	l, err := Listen("tcp", "127.0.0.1:0")
 	if err != nil {
-		t.Fatal("Listen 127.0.0.1:0: %v", err)
+		t.Fatalf("Listen 127.0.0.1:0: %v", err)
 	}
 	defer l.Close()
 	l1, err := Listen("tcp", l.Addr().String())
 	if err == nil {
-		t.Fatal("second Listen %v: %v", l.Addr(), err)
+		t.Fatalf("second Listen %v: %v", l.Addr(), err)
 	}
 	if l1 != nil {
 		t.Fatalf("Listen returned non-nil interface %T(%v) with err != nil", l1, l1)
@@ -245,12 +245,12 @@ func TestErrorNil(t *testing.T) {
 	// Make ListenPacket fail by relistening on the same address.
 	lp, err := ListenPacket("udp", "127.0.0.1:0")
 	if err != nil {
-		t.Fatal("Listen 127.0.0.1:0: %v", err)
+		t.Fatalf("Listen 127.0.0.1:0: %v", err)
 	}
 	defer lp.Close()
 	lp1, err := ListenPacket("udp", lp.LocalAddr().String())
 	if err == nil {
-		t.Fatal("second Listen %v: %v", lp.LocalAddr(), err)
+		t.Fatalf("second Listen %v: %v", lp.LocalAddr(), err)
 	}
 	if lp1 != nil {
 		t.Fatalf("ListenPacket returned non-nil interface %T(%v) with err != nil", lp1, lp1)

コアとなるコードの解説

TestErrorNil関数は、net.Listenおよびnet.ListenPacketがエラーを適切に処理するかどうかをテストしています。具体的には、既に使われているアドレスに再度Listenしようとした場合にエラーが発生することを確認しています。

変更前は、エラーが発生した場合にt.Fatalを使用していました。

// 変更前
t.Fatal("Listen 127.0.0.1:0: %v", err)

これは、t.Fatalがフォーマット文字列を解釈しないため、%vがそのまま出力され、errの値がその後に続くという、意図しない結果をもたらしていました。

変更後は、t.Fatalfを使用しています。

// 変更後
t.Fatalf("Listen 127.0.0.1:0: %v", err)

t.Fatalffmt.Printfと同様にフォーマット文字列を解釈するため、%verrの実際の値に置き換えられ、より意味のあるエラーメッセージが出力されるようになります。これにより、テストが失敗した際に、何が問題だったのかを正確に把握しやすくなります。

この修正は、TestErrorNil関数内の4箇所すべてに適用されており、ListenListenPacketの両方のテストケースで同様の改善が行われています。

関連リンク

  • Go言語 testing パッケージのドキュメント: https://pkg.go.dev/testing
  • Go言語 fmt パッケージのドキュメント: https://pkg.go.dev/fmt
  • go tool vetに関する公式ドキュメントやブログ記事(Goのバージョンによって内容が異なる場合がありますが、基本的な機能は共通です)

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • go tool vetに関する一般的な情報源
  • Go言語のテストに関するチュートリアルやブログ記事
  • このコミットのGo Gerritレビューページ: https://golang.org/cl/27430043 (コミットメッセージに記載されているリンク)
  • t.Fatalt.Fatalfの具体的な動作に関するGoのソースコード(src/testing/testing.go
  • fmt.Sprintfmt.Sprintfの動作に関するGoのソースコード(src/fmt/print.go