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

[インデックス 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言語の標準ライブラリの品質向上とテストの堅牢性強化にあります。

  1. govet による指摘の対応: govet はGo言語のコードを静的に解析し、疑わしい構造や潜在的なバグを検出するツールです。このコミットでは、govet が指摘した fmt パッケージのフォーマット文字列と引数の不一致(例: %q が指定されているのに対応する引数がない、または型が異なる)を修正しています。このような不一致は、実行時にパニックを引き起こしたり、意図しない出力になったりする可能性があるため、修正はコードの安定性向上に寄与します。
  2. テストの改善とクリーンアップ: net/http パッケージはGo言語のウェブアプリケーション開発において非常に重要なコンポーネントです。そのテストコードは、機能の正確性を保証するために非常に重要です。このコミットでは、テストのロジックをより堅牢にし、エラーハンドリングを改善することで、テストがより多くのシナリオを適切にカバーし、問題発生時に正確な情報を提供するようにしています。特に、t.Fatalft.Errorf に変更し、continue Cases を追加することで、一つのテストケースでエラーが発生しても、そのテスト関数内の他のテストケースの実行を継続できるように改善されています。これにより、テスト実行の効率が向上し、一度の実行でより多くの問題を発見できるようになります。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とツールに関する知識が役立ちます。

  • net/http パッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーの実装を提供します。ウェブアプリケーションやAPIの構築に不可欠なパッケージです。
  • testing パッケージ: Go言語の標準テストフレームワークです。テスト関数は Test で始まり、*testing.T 型の引数を取ります。
    • t.Errorf(...): テスト中にエラーが発生したことを報告しますが、テストの実行は継続します。
    • t.Fatalf(...): テスト中に致命的なエラーが発生したことを報告し、現在のテスト関数を即座に終了させます。このコミットでは、一部の t.Fatalft.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つのカテゴリに分けられます。

  1. govet によるフォーマット引数不一致の修正:

    • src/pkg/net/http/fs_test.go および src/pkg/net/http/serve_test.go 内の t.Errorflog.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 が追加されました。
  2. テストロジックの改善と堅牢化:

    • 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/byterangesContent-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 の変更点

  1. Cases: ラベルの追加: for _, rt := range ServeFileRangeTests ループの直前に Cases: というラベルが追加されました。これは、continue Cases 文のターゲットとして使用されます。
  2. 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 が追加されました。
  3. 早期 continue の追加:
    • if params["boundary"] == "" { ... continue }: multipart/byterangesContent-Typeboundary が含まれていない場合、エラーを報告した後にループの次のイテレーションに進むようになりました。これにより、boundary がない状態で multipart.NewReader を呼び出すことを避け、テストのロジックがより堅牢になります。
    • if g, w := resp.ContentLength, int64(len(body)); g != w { ... continue }: Content-Length が期待値と異なる場合も同様に、次のイテレーションに進むようになりました。
  4. 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: パートのボディ読み込みエラーについても同様の変更が適用されています。
  5. 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 の変更点

  1. 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 の指摘に対応し、テストの出力がより正確で情報量が多くなるようにするためのものです。

関連リンク

参考にした情報源リンク