[インデックス 13441] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージ内のテストコードのクリーンアップと改善を目的としています。具体的には、ファイルシステム関連のテスト (fs_test.go
) の整理と、govet
ツールによって検出されたフォーマット引数の不一致問題の修正が含まれています。また、serve_test.go
内のログ出力のフォーマットも修正されています。
コミット
- コミットハッシュ:
55cc1ff721aaf14b27df31efcf0295c4ff09e35a
- 作者: Brad Fitzpatrick bradfitz@golang.org
- コミット日時: 2012年7月3日 火曜日 10:17:55 -0700
- コミットメッセージ:
net/http: clean up fs tests a bit And fix some govet-caught format arg issues. R=r CC=golang-dev https://golang.org/cl/6354068
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/55cc1ff721aaf14b27df31efcf0295c4ff09e35a
元コミット内容
net/http: clean up fs tests a bit
And fix some govet-caught format arg issues.
R=r
CC=golang-dev
https://golang.org/cl/6354068
変更の背景
このコミットの主な背景は、Go言語の標準ライブラリの品質向上とテストの堅牢性強化にあります。
govet
による指摘の対応:govet
はGo言語のコードを静的に解析し、疑わしい構造や潜在的なバグを検出するツールです。このコミットでは、govet
が指摘したfmt
パッケージのフォーマット文字列と引数の不一致(例:%q
が指定されているのに対応する引数がない、または型が異なる)を修正しています。このような不一致は、実行時にパニックを引き起こしたり、意図しない出力になったりする可能性があるため、修正はコードの安定性向上に寄与します。- テストの改善とクリーンアップ:
net/http
パッケージはGo言語のウェブアプリケーション開発において非常に重要なコンポーネントです。そのテストコードは、機能の正確性を保証するために非常に重要です。このコミットでは、テストのロジックをより堅牢にし、エラーハンドリングを改善することで、テストがより多くのシナリオを適切にカバーし、問題発生時に正確な情報を提供するようにしています。特に、t.Fatalf
をt.Errorf
に変更し、continue Cases
を追加することで、一つのテストケースでエラーが発生しても、そのテスト関数内の他のテストケースの実行を継続できるように改善されています。これにより、テスト実行の効率が向上し、一度の実行でより多くの問題を発見できるようになります。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とツールに関する知識が役立ちます。
net/http
パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。ウェブアプリケーションやAPIの構築に不可欠なパッケージです。testing
パッケージ: Go言語の標準テストフレームワークです。テスト関数はTest
で始まり、*testing.T
型の引数を取ります。t.Errorf(...)
: テスト中にエラーが発生したことを報告しますが、テストの実行は継続します。t.Fatalf(...)
: テスト中に致命的なエラーが発生したことを報告し、現在のテスト関数を即座に終了させます。このコミットでは、一部のt.Fatalf
がt.Errorf
に変更されており、テストの継続性を高めています。
fmt
パッケージ: フォーマットされたI/Oを実装するためのパッケージです。Printf
系の関数でフォーマット文字列を使用します。- フォーマット動詞:
%v
(任意の値をデフォルトフォーマットで出力),%q
(文字列をダブルクォートで囲み、Go構文でエスケープして出力),%d
(整数を10進数で出力) などがあります。govet
はこれらの動詞と引数の型の不一致を検出します。
- フォーマット動詞:
govet
ツール: Go言語の静的解析ツールの一つで、コード内の疑わしい構造(例:Printf
フォーマット文字列の誤用、到達不能なコード、ロックの誤用など)を検出します。GoのビルドプロセスやCI/CDパイプラインでよく使用され、コード品質の維持に貢献します。- HTTP Range ヘッダー: クライアントがサーバーに対して、リソース全体ではなく、その一部(バイト範囲)のみを要求するために使用するHTTPヘッダーです。例えば、動画のストリーミングやダウンロードの再開などに利用されます。
multipart/byteranges
: HTTP Range ヘッダーで複数のバイト範囲が要求された場合、サーバーはContent-Type: multipart/byteranges
を使用して、各範囲のデータをマルチパート形式で返します。log
パッケージ: Go言語の標準ロギングパッケージです。log.Panicf(...)
: フォーマットされた文字列をログに出力し、その後panic
を発生させます。これは通常、回復不可能なエラーを示すために使用されます。
技術的詳細
このコミットで行われた技術的な変更は、主に以下の2つのカテゴリに分けられます。
-
govet
によるフォーマット引数不一致の修正:src/pkg/net/http/fs_test.go
およびsrc/pkg/net/http/serve_test.go
内のt.Errorf
やlog.Panicf
の呼び出しにおいて、フォーマット文字列 (%q
,%v
など) に対応する引数が不足していたり、誤っていたりする箇所が修正されました。例えば、t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r)
という行では、%q
が2つあるにもかかわらず、引数がrt.r
の1つしかなかったため、2つ目の%q
に対応するct
が追加されました。これにより、govet
の警告が解消され、実行時の出力が意図通りになります。log.Panicf
の呼び出しでも同様に、エラーオブジェクト (err
) やボディ (body
) を適切にフォーマットするために%v
や%q
が追加されました。
-
テストロジックの改善と堅牢化:
t.Fatalf
からt.Errorf
への変更とcontinue Cases
の導入:fs_test.go
内のTestServeFile
関数において、multipart.NewReader
からのパート読み込みエラーや、パートボディの読み込みエラーが発生した場合に、以前はt.Fatalf
を使用してテスト関数全体を終了させていました。しかし、この変更によりt.Errorf
に変更され、さらにcontinue Cases
というラベル付きcontinue
文が追加されました。continue Cases
は、外側のfor
ループ(Cases:
ラベルが付いているループ)の次のイテレーションに進むことを意味します。これにより、あるレンジテストケースでエラーが発生しても、そのテスト関数内の他のレンジテストケースの実行が中断されずに継続されるようになります。これは、テストの網羅性を高め、一度のテスト実行でより多くの問題を特定できるようにするための重要な改善です。
- 早期
continue
の追加:multipart/byteranges
のContent-Type
が期待通りでない場合や、boundary
が不足している場合に、エラーを報告した後にcontinue
を追加することで、その後の無意味な処理をスキップし、テストのロジックをより明確にしています。 Content-Range
ヘッダーのチェック順序の変更: マルチパートの各パートのContent-Range
ヘッダーのチェックが、ボディの比較の前に移動されました。これは論理的な順序の改善であり、ヘッダーが正しくない場合はボディの比較に進む必要がないため、テストの効率と可読性が向上します。
これらの変更は、Go言語のテストプラクティスにおけるベストプラクティスに沿ったものであり、テストの信頼性と保守性を高めるものです。
コアとなるコードの変更箇所
src/pkg/net/http/fs_test.go
--- a/src/pkg/net/http/fs_test.go
+++ b/src/pkg/net/http/fs_test.go
@@ -81,6 +81,7 @@ func TestServeFile(t *testing.T) {
}
// Range tests
+Cases:
for _, rt := range ServeFileRangeTests {
\tif rt.r != "" {
\t\treq.Header.Set("Range", rt.r)
@@ -109,7 +110,7 @@ func TestServeFile(t *testing.T) {
\t\t\tt.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
\t\t\t}
\t\tif strings.HasPrefix(ct, "multipart/byteranges") {
-\t\t\t\tt.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r)
+\t\t\t\tt.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r, ct)
\t\t}
\t}
\tif len(rt.ranges) > 1 {
@@ -119,37 +120,41 @@ func TestServeFile(t *testing.T) {
\t\t\t\tcontinue
\t\t\t}
\t\t\tif typ != "multipart/byteranges" {
-\t\t\t\t\tt.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r)
+\t\t\t\t\tt.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r, typ)
\t\t\t\tcontinue
\t\t\t}
\t\t\tif params["boundary"] == "" {
\t\t\t\tt.Errorf("range=%q content-type = %q; lacks boundary", rt.r, ct)
+\t\t\t\t\tcontinue
\t\t\t}
\t\t\tif g, w := resp.ContentLength, int64(len(body)); g != w {
\t\t\t\tt.Errorf("range=%q Content-Length = %d; want %d", rt.r, g, w)
+\t\t\t\t\tcontinue
\t\t\t}
\t\t\tmr := multipart.NewReader(bytes.NewReader(body), params["boundary"])
\t\t\tfor ri, rng := range rt.ranges {\
\t\t\t\tpart, err := mr.NextPart()
\t\t\t\tif err != nil {\
-\t\t\t\t\t\tt.Fatalf("range=%q, reading part index %d: %v", rt.r, ri, err)
+\t\t\t\t\t\tt.Errorf("range=%q, reading part index %d: %v", rt.r, ri, err)
+\t\t\t\t\t\tcontinue Cases
+\t\t\t\t\t}
+\t\t\t\t\twantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
+\t\t\t\t\tif g, w := part.Header.Get("Content-Range"), wantContentRange; g != w {
+\t\t\t\t\t\tt.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w)
\t\t\t\t}
\t\t\t\tbody, err := ioutil.ReadAll(part)
\t\t\t\tif err != nil {\
-\t\t\t\t\t\tt.Fatalf("range=%q, reading part index %d body: %v", rt.r, ri, err)
+\t\t\t\t\t\tt.Errorf("range=%q, reading part index %d body: %v", rt.r, ri, err)
+\t\t\t\t\t\tcontinue Cases
\t\t\t\t}
-\t\t\t\twantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
\t\t\t\twantBody := file[rng.start:rng.end]
\t\t\t\tif !bytes.Equal(body, wantBody) {\
\t\t\t\t\tt.Errorf("range=%q: body = %q, want %q", rt.r, body, wantBody)
\t\t\t\t}
-\t\t\t\t\tif g, w := part.Header.Get("Content-Range"), wantContentRange; g != w {\
-\t\t\t\t\t\tt.Errorf("range=%q: part Content-Range = %q; want %q", rt.r, g, w)
-\t\t\t\t\t}
\t\t\t}
\t\t\t_, err = mr.NextPart()
\t\t\tif err != io.EOF {\
-\t\t\t\t\tt.Errorf("range=%q; expected final error io.EOF; got %v", err)
+\t\t\t\t\tt.Errorf("range=%q; expected final error io.EOF; got %v", rt.r, err)
\t\t\t}
\t\t}
\t}
src/pkg/net/http/serve_test.go
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -1283,15 +1283,15 @@ func BenchmarkServer(b *testing.B) {
\t\tfor i := 0; i < n; i++ {\
\t\t\tres, err := Get(url)
\t\t\tif err != nil {\
-\t\t\t\t\tlog.Panicf("Get:", err)
+\t\t\t\t\tlog.Panicf("Get: %v", err)
\t\t\t}
\t\t\tall, err := ioutil.ReadAll(res.Body)
\t\t\tif err != nil {\
-\t\t\t\t\tlog.Panicf("ReadAll:", err)
+\t\t\t\t\tlog.Panicf("ReadAll: %v", err)
\t\t\t}
\t\t\tbody := string(all)
\t\t\tif body != "Hello world.\\n" {\
-\t\t\t\t\tlog.Panicf("Got body:", body)
+\t\t\t\t\tlog.Panicf("Got body: %q", body)
\t\t\t}
\t\t}
\t\tos.Exit(0)
コアとなるコードの解説
src/pkg/net/http/fs_test.go
の変更点
Cases:
ラベルの追加:for _, rt := range ServeFileRangeTests
ループの直前にCases:
というラベルが追加されました。これは、continue Cases
文のターゲットとして使用されます。t.Errorf
のフォーマット引数修正:t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r)
→t.Errorf("range=%q content-type = %q; unexpected multipart/byteranges", rt.r, ct)
: 2つ目の%q
に対応するct
変数が追加されました。t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r)
→t.Errorf("range=%q content-type = %q; want multipart/byteranges", rt.r, typ)
: 同様に、2つ目の%q
に対応するtyp
変数が追加されました。t.Errorf("range=%q; expected final error io.EOF; got %v", err)
→t.Errorf("range=%q; expected final error io.EOF; got %v", rt.r, err)
:%q
に対応するrt.r
が追加されました。
- 早期
continue
の追加:if params["boundary"] == "" { ... continue }
:multipart/byteranges
のContent-Type
にboundary
が含まれていない場合、エラーを報告した後にループの次のイテレーションに進むようになりました。これにより、boundary
がない状態でmultipart.NewReader
を呼び出すことを避け、テストのロジックがより堅牢になります。if g, w := resp.ContentLength, int64(len(body)); g != w { ... continue }
:Content-Length
が期待値と異なる場合も同様に、次のイテレーションに進むようになりました。
t.Fatalf
からt.Errorf
への変更とcontinue Cases
:t.Fatalf("range=%q, reading part index %d: %v", rt.r, ri, err)
→t.Errorf("range=%q, reading part index %d: %v", rt.r, ri, err)
とcontinue Cases
: マルチパートのパート読み込みエラーが発生した場合、以前はテスト関数全体が終了していましたが、t.Errorf
に変更されたことでエラーを報告しつつテストは継続されます。さらにcontinue Cases
によって、現在のレンジテストケースの残りの処理をスキップし、次のレンジテストケースの検証に進むことができます。これにより、一つのエラーでテストスイート全体が停止するのを防ぎ、より多くのテスト結果を得られるようになります。t.Fatalf("range=%q, reading part index %d body: %v", rt.r, ri, err)
→t.Errorf("range=%q, reading part index %d body: %v", rt.r, ri, err)
とcontinue Cases
: パートのボディ読み込みエラーについても同様の変更が適用されています。
wantContentRange
の計算とチェック順序の変更:wantContentRange = fmt.Sprintf("bytes %d-%d/%d", rng.start, rng.end-1, testFileLen)
の行が、part, err := mr.NextPart()
の直後に移動しました。これにより、Content-Range
ヘッダーの期待値がより早く計算され、その直後にpart.Header.Get("Content-Range")
のチェックが行われるようになりました。これは論理的な順序の改善であり、テストの可読性を向上させます。
src/pkg/net/http/serve_test.go
の変更点
log.Panicf
のフォーマット引数修正:log.Panicf("Get:", err)
→log.Panicf("Get: %v", err)
: エラーオブジェクトerr
を適切にフォーマットするために%v
が追加されました。log.Panicf("ReadAll:", err)
→log.Panicf("ReadAll: %v", err)
: 同様に、エラーオブジェクトerr
を適切にフォーマットするために%v
が追加されました。log.Panicf("Got body:", body)
→log.Panicf("Got body: %q", body)
: 文字列body
をGo構文でエスケープして出力するために%q
が追加されました。
これらの変更は、govet
の指摘に対応し、テストの出力がより正確で情報量が多くなるようにするためのものです。
関連リンク
- Go CL 6354068: https://golang.org/cl/6354068
参考にした情報源リンク
- Go言語
testing
パッケージ: https://pkg.go.dev/testing - Go言語
fmt
パッケージ: https://pkg.go.dev/fmt - Go言語
log
パッケージ: https://pkg.go.dev/log govet
ツール (Go公式ドキュメント): https://pkg.go.dev/cmd/vet- HTTP Range ヘッダー (MDN Web Docs): https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Range
- HTTP
Content-Type
(MDN Web Docs): https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type - Go言語のラベル付き
for
ループとcontinue
(例): https://go.dev/tour/flowcontrol/14 (Go Tourの関連セクション)