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

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

このコミットは、Go言語の net/http パッケージにおけるプロキシ設定のテストに関するものです。具体的には、TestUseProxy テストが複数回実行された際に失敗する問題を修正し、テストの再現性を向上させています。

コミット

commit fd7ff2049512b3a900004e3e3c83e45263f93d13
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Mar 6 18:44:14 2014 +0400

    net/http: make TestUseProxy repeatable
    Currently it fails on second and subsequent runs (when using -cpu=1,2,4) as:
    --- FAIL: TestUseProxy-4 (0.00 seconds)
    proxy_test.go:109: useProxy(barbaz.net) = true, want false
    proxy_test.go:109: useProxy(foobar.com) = true, want false
    proxy_test.go:109: useProxy(www.foobar.com) = true, want false
    
    LGTM=bradfitz
    R=bradfitz
    CC=golang-codereviews
    https://golang.org/cl/71940044

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

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

元コミット内容

このコミットの目的は、net/http パッケージ内の TestUseProxy テストが、特に -cpu=1,2,4 のような並列テスト実行オプションを使用した場合に、2回目以降の実行で失敗する問題を解決することです。具体的な失敗内容は、proxy_test.go:109useProxy 関数の結果が期待値と異なるというものでした。

変更の背景

Go言語のテストフレームワークでは、テストの並列実行をサポートしています。しかし、環境変数に依存するテストは、テスト実行間で環境変数の状態が適切にリセットされないと、テストの再現性が損なわれる可能性があります。

net/http パッケージは、HTTPプロキシの設定に環境変数(HTTP_PROXY, HTTPS_PROXY, NO_PROXY など)を利用します。TestUseProxy は、これらの環境変数の設定に基づいて useProxy 関数の動作を検証するテストです。元の実装では、テストの開始時に NO_PROXY 環境変数を設定し、テスト終了時に元の値に戻す処理(defer os.Setenv("NO_PROXY", oldenv))が含まれていました。しかし、これはテストが単独で実行される場合には問題ありませんが、複数のテストが並列で実行されたり、同じテストが複数回実行されたりするシナリオでは、環境変数の状態が他のテストや後続の実行に影響を与え、予期せぬ結果を引き起こす可能性がありました。

特に、go test -cpu=N オプションを使用すると、テストはN個のCPUコアで並列に実行されます。この場合、あるテストが環境変数を変更し、その変更が他の並列実行中のテストや、同じテストの次の実行に影響を与える可能性があります。このコミットは、このような環境変数の「汚染」を防ぎ、テストの独立性と再現性を保証することを目的としています。

前提知識の解説

Go言語の net/http パッケージとプロキシ設定

Go言語の net/http パッケージは、HTTPクライアントとサーバーを実装するための標準ライブラリです。HTTPクライアントは、通常、システム環境変数を通じてプロキシ設定を自動的に検出します。主要な環境変数は以下の通りです。

  • HTTP_PROXY / http_proxy: HTTPリクエストに使用するプロキシサーバーのアドレスを指定します。
  • HTTPS_PROXY / https_proxy: HTTPSリクエストに使用するプロキシサーバーのアドレスを指定します。
  • NO_PROXY / no_proxy: プロキシを使用しないホスト名のリストをカンマ区切りで指定します。このリストに含まれるホストへのリクエストは、プロキシを介さずに直接行われます。

これらの環境変数は、大文字と小文字の両方がサポートされており、Goの net/http パッケージはこれらを適切に解釈します。

Go言語のテストと再現性

Go言語のテストは、go test コマンドで実行されます。テストの再現性(Repeatability)は、ソフトウェアテストにおいて非常に重要な特性です。再現性のあるテストとは、同じ入力に対して常に同じ結果を返すテストのことです。これにより、バグの特定、修正の検証、リグレッションの防止が容易になります。

環境変数やグローバルな状態に依存するテストは、再現性を損なう一般的な原因となります。テストが実行されるたびに環境がクリーンな状態にリセットされない場合、以前のテスト実行が残した副作用が次のテスト実行に影響を与え、テストが不安定になったり、特定の順序でしかパスしなくなったりする可能性があります。

go test -cpu=N オプションは、テストを並列で実行するためのものです。これにより、テストスイート全体の実行時間を短縮できますが、テスト間の依存関係や共有状態の管理が不適切だと、競合状態やデッドロックなどの問題が顕在化しやすくなります。

os.Setenvos.Getenv

Go言語の os パッケージは、オペレーティングシステムとのインタラクションを提供します。

  • os.Setenv(key, value): 指定されたキーの環境変数を設定します。
  • os.Getenv(key): 指定されたキーの環境変数の値を取得します。

これらの関数は、プロセス全体の環境変数を変更するため、テスト内で使用する際には注意が必要です。テストが終了しても環境変数の変更が残ってしまうと、後続のテストに影響を与える可能性があります。

技術的詳細

このコミットの主要な変更点は、テストのセットアップとティアダウンのロジックを改善し、環境変数の状態がテスト実行間で適切にリセットされるようにしたことです。

元の TestUseProxy 関数では、NO_PROXY 環境変数を設定し、defer os.Setenv("NO_PROXY", oldenv) を使ってテスト終了時に元の値に戻していました。しかし、この defer はテスト関数が終了したときにのみ実行されるため、並列テスト実行や複数回のテスト実行において、環境変数の状態が完全にリセットされない問題がありました。

この問題を解決するために、以下の変更が行われました。

  1. ResetProxyEnv() 関数の導入: ResetProxyEnv() という新しい関数が proxy_test.go に追加されました。この関数は、HTTP_PROXY, http_proxy, NO_PROXY, no_proxy のすべてのプロキシ関連環境変数を空文字列に設定することで、それらをクリアします。これにより、テスト実行前にプロキシ設定が既知のクリーンな状態にリセットされることが保証されます。 さらに、ResetCachedEnvironment() も呼び出されています。これは net/http パッケージ内部でプロキシ設定がキャッシュされている場合に、そのキャッシュをクリアするための関数であると推測されます。これにより、環境変数をクリアするだけでなく、Goランタイムが内部的に保持しているプロキシ設定のキャッシュも確実にリフレッシュされます。

  2. TestUseProxy および TestProxyFromEnvironment での ResetProxyEnv() の利用: TestUseProxy 関数と transport_test.go にある TestProxyFromEnvironment 関数(これもプロキシ環境変数に依存するテスト)の冒頭で ResetProxyEnv() が呼び出されるようになりました。これにより、各テストの実行前に環境変数が確実にリセットされ、テストの独立性と再現性が保証されます。

  3. 冗長な環境変数リセットの削除: TestUseProxy および TestProxyFromEnvironment から、個々の環境変数を手動でリセットする冗長なコード(例: os.Setenv("NO_PROXY", oldenv)os.Setenv("HTTP_PROXY", "") など)が削除されました。これにより、コードが簡潔になり、ResetProxyEnv() 関数にプロキシ環境変数のリセットロジックが一元化されました。

この変更により、TestUseProxy は、並列実行や複数回実行された場合でも、常に同じ初期環境で実行されるようになり、テストの再現性が大幅に向上しました。

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

src/pkg/net/http/proxy_test.go

--- a/src/pkg/net/http/proxy_test.go
+++ b/src/pkg/net/http/proxy_test.go
@@ -35,12 +35,8 @@ var UseProxyTests = []struct {
 }
 
 func TestUseProxy(t *testing.T) {
-	oldenv := os.Getenv("NO_PROXY")
-	defer os.Setenv("NO_PROXY", oldenv)
-
-	no_proxy := "foobar.com, .barbaz.net"
-	os.Setenv("NO_PROXY", no_proxy)
-
+	ResetProxyEnv()
+	os.Setenv("NO_PROXY", "foobar.com, .barbaz.net")
 	for _, test := range UseProxyTests {
 		if useProxy(test.host+":80") != test.match {
 			t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
@@ -76,3 +72,10 @@ func TestCacheKeys(t *testing.T) {
 		}
 	}
 }
+
+func ResetProxyEnv() {
+	for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy"} {
+		os.Setenv(v, "")
+	}
+	ResetCachedEnvironment()
+}

src/pkg/net/http/transport_test.go

--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -1694,10 +1694,7 @@ var proxyFromEnvTests = []proxyFromEnvTest{
 }
 
 func TestProxyFromEnvironment(t *testing.T) {
-	os.Setenv("HTTP_PROXY", "")
-	os.Setenv("http_proxy", "")
-	os.Setenv("NO_PROXY", "")
-	os.Setenv("no_proxy", "")
+	ResetProxyEnv()
 	for _, tt := range proxyFromEnvTests {
 		os.Setenv("HTTP_PROXY", tt.env)
 		os.Setenv("NO_PROXY", tt.noenv)

コアとなるコードの解説

src/pkg/net/http/proxy_test.go の変更点

  • TestUseProxy 関数:

    • 元のコードにあった oldenv := os.Getenv("NO_PROXY")defer os.Setenv("NO_PROXY", oldenv) が削除されました。これは、テスト実行後に環境変数を元の状態に戻すためのものでしたが、並列実行や複数回実行のシナリオでは不十分でした。
    • 代わりに、新しく追加された ResetProxyEnv() 関数がテストの冒頭で呼び出されるようになりました。これにより、テストが開始される前にすべてのプロキシ関連環境変数が確実にクリアされ、クリーンな状態からテストが実行されます。
    • os.Setenv("NO_PROXY", "foobar.com, .barbaz.net") は残されており、これはこのテストケースで検証したい特定の NO_PROXY 設定を適用するためのものです。ResetProxyEnv() が先に呼び出されることで、この設定が他のテストの影響を受けずに適用されることが保証されます。
  • ResetProxyEnv() 関数の追加:

    • この新しい関数は、プロキシ関連の環境変数(HTTP_PROXY, http_proxy, NO_PROXY, no_proxy)をループで回し、それぞれを空文字列に設定します。これにより、これらの環境変数が確実にクリアされます。
    • ResetCachedEnvironment() が呼び出されています。この関数は net/http パッケージの内部関数であり、環境変数から読み込まれたプロキシ設定の内部キャッシュをリセットする役割を担っていると考えられます。これにより、環境変数の変更がGoランタイムに即座に反映されるようになります。

src/pkg/net/http/transport_test.go の変更点

  • TestProxyFromEnvironment 関数:
    • このテストもプロキシ環境変数に依存するため、テストの冒頭で ResetProxyEnv() が呼び出されるようになりました。
    • 元のコードにあった、個々のプロキシ環境変数を空文字列に設定する冗長な行(os.Setenv("HTTP_PROXY", "") など)が削除されました。これにより、コードが簡潔になり、プロキシ環境変数のリセットロジックが ResetProxyEnv() に一元化されました。

これらの変更により、Goの net/http パッケージのプロキシ関連テストは、より堅牢で再現性の高いものになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特に net/http パッケージのテストファイル)
  • Go言語のテストにおける環境変数の扱いに関する一般的なプラクティス
  • go test -cpu オプションに関する情報
  • Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/71940044 (コミットメッセージに記載)

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

このコミットは、Go言語の net/http パッケージにおけるプロキシ設定のテストに関するものです。具体的には、TestUseProxy テストが複数回実行された際に失敗する問題を修正し、テストの再現性を向上させています。

コミット

commit fd7ff2049512b3a900004e3e3c83e45263f93d13
Author: Dmitriy Vyukov <dvyukov@google.com>
Date:   Thu Mar 6 18:44:14 2014 +0400

    net/http: make TestUseProxy repeatable
    Currently it fails on second and subsequent runs (when using -cpu=1,2,4) as:
    --- FAIL: TestUseProxy-4 (0.00 seconds)
    proxy_test.go:109: useProxy(barbaz.net) = true, want false
    proxy_test.go:109: useProxy(foobar.com) = true, want false
    proxy_test.go:109: useProxy(www.foobar.com) = true, want false
    
    LGTM=bradfitz
    R=bradfitz
    CC=golang-codereviews
    https://golang.org/cl/71940044

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

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

元コミット内容

このコミットの目的は、net/http パッケージ内の TestUseProxy テストが、特に -cpu=1,2,4 のような並列テスト実行オプションを使用した場合に、2回目以降の実行で失敗する問題を解決することです。具体的な失敗内容は、proxy_test.go:109useProxy 関数の結果が期待値と異なるというものでした。

変更の背景

Go言語のテストフレームワークでは、テストの並列実行をサポートしています。しかし、環境変数に依存するテストは、テスト実行間で環境変数の状態が適切にリセットされないと、テストの再現性が損なわれる可能性があります。

net/http パッケージは、HTTPプロキシの設定に環境変数(HTTP_PROXY, HTTPS_PROXY, NO_PROXY など)を利用します。TestUseProxy は、これらの環境変数の設定に基づいて useProxy 関数の動作を検証するテストです。元の実装では、テストの開始時に NO_PROXY 環境変数を設定し、テスト終了時に元の値に戻す処理(defer os.Setenv("NO_PROXY", oldenv))が含まれていました。しかし、これはテストが単独で実行される場合には問題ありませんが、複数のテストが並列で実行されたり、同じテストが複数回実行されたりするシナリオでは、環境変数の状態が他のテストや後続の実行に影響を与え、予期せぬ結果を引き起こす可能性がありました。

特に、go test -cpu=N オプションを使用すると、テストはN個のCPUコアで並列に実行されます。この場合、あるテストが環境変数を変更し、その変更が他の並列実行中のテストや、同じテストの次の実行に影響を与える可能性があります。このコミットは、このような環境変数の「汚染」を防ぎ、テストの独立性と再現性を保証することを目的としています。

前提知識の解説

Go言語の net/http パッケージとプロキシ設定

Go言語の net/http パッケージは、HTTPクライアントとサーバーを実装するための標準ライブラリです。HTTPクライアントは、通常、システム環境変数を通じてプロキシ設定を自動的に検出します。主要な環境変数は以下の通りです。

  • HTTP_PROXY / http_proxy: HTTPリクエストに使用するプロキシサーバーのアドレスを指定します。
  • HTTPS_PROXY / https_proxy: HTTPSリクエストに使用するプロxyサーバーのアドレスを指定します。
  • NO_PROXY / no_proxy: プロキシを使用しないホスト名のリストをカンマ区切りで指定します。このリストに含まれるホストへのリクエストは、プロキシを介さずに直接行われます。

これらの環境変数は、大文字と小文字の両方がサポートされており、Goの net/http パッケージはこれらを適切に解釈します。net/http パッケージの ProxyFromEnvironment 関数がこれらの環境変数を読み取る役割を担っています。HTTP_PROXYHTTPS_PROXY の値は、完全なURL(例: http://proxy.example.com:8080)または「ホスト[:ポート]」形式の文字列(この場合、「http」スキームが仮定される)で指定できます。

Go言語のテストと再現性

Go言語のテストは、go test コマンドで実行されます。テストの再現性(Repeatability)は、ソフトウェアテストにおいて非常に重要な特性です。再現性のあるテストとは、同じ入力に対して常に同じ結果を返すテストのことであり、これにより、バグの特定、修正の検証、リグレッションの防止が容易になります。

環境変数やグローバルな状態に依存するテストは、再現性を損なう一般的な原因となります。テストが実行されるたびに環境がクリーンな状態にリセットされない場合、以前のテスト実行が残した副作用が次のテスト実行に影響を与え、テストが不安定になったり、特定の順序でしかパスしなくなったりする可能性があります。

go test -cpu=N オプションは、テストを並列で実行するためのものです。これにより、テストスイート全体の実行時間を短縮できますが、テスト間の依存関係や共有状態の管理が不適切だと、競合状態やデッドロックなどの問題が顕在化しやすくなります。

os.Setenvos.Getenv

Go言語の os パッケージは、オペレーティングシステムとのインタラクションを提供します。

  • os.Setenv(key, value): 指定されたキーの環境変数を設定します。
  • os.Getenv(key): 指定されたキーの環境変数の値を取得します。

これらの関数は、プロセス全体の環境変数を変更するため、テスト内で使用する際には注意が必要です。テストが終了しても環境変数の変更が残ってしまうと、後続のテストに影響を与える可能性があります。

技術的詳細

このコミットの主要な変更点は、テストのセットアップとティアダウンのロジックを改善し、環境変数の状態がテスト実行間で適切にリセットされるようにしたことです。

元の TestUseProxy 関数では、NO_PROXY 環境変数を設定し、defer os.Setenv("NO_PROXY", oldenv) を使ってテスト終了時に元の値に戻していました。しかし、この defer はテスト関数が終了したときにのみ実行されるため、並列テスト実行や複数回のテスト実行において、環境変数の状態が完全にリセットされない問題がありました。

この問題を解決するために、以下の変更が行われました。

  1. ResetProxyEnv() 関数の導入: ResetProxyEnv() という新しい関数が proxy_test.go に追加されました。この関数は、HTTP_PROXY, http_proxy, NO_PROXY, no_proxy のすべてのプロキシ関連環境変数を空文字列に設定することで、それらをクリアします。これにより、テスト実行前にプロキシ設定が既知のクリーンな状態にリセットされることが保証されます。 さらに、ResetCachedEnvironment() も呼び出されています。これは net/http パッケージ内部でプロキシ設定がキャッシュされている場合に、そのキャッシュをクリアするための関数であると推測されます。これにより、環境変数をクリアするだけでなく、Goランタイムが内部的に保持しているプロキシ設定のキャッシュも確実にリフレッシュされます。

  2. TestUseProxy および TestProxyFromEnvironment での ResetProxyEnv() の利用: TestUseProxy 関数と transport_test.go にある TestProxyFromEnvironment 関数(これもプロキシ環境変数に依存するテスト)の冒頭で ResetProxyEnv() が呼び出されるようになりました。これにより、各テストの実行前に環境変数が確実にリセットされ、テストの独立性と再現性が保証されます。

  3. 冗長な環境変数リセットの削除: TestUseProxy および TestProxyFromEnvironment から、個々の環境変数を手動でリセットする冗長なコード(例: os.Setenv("NO_PROXY", oldenv)os.Setenv("HTTP_PROXY", "") など)が削除されました。これにより、コードが簡潔になり、ResetProxyEnv() 関数にプロキシ環境変数のリセットロジックが一元化されました。

この変更により、TestUseProxy は、並列実行や複数回実行された場合でも、常に同じ初期環境で実行されるようになり、テストの再現性が大幅に向上しました。

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

src/pkg/net/http/proxy_test.go

--- a/src/pkg/net/http/proxy_test.go
+++ b/src/pkg/net/http/proxy_test.go
@@ -35,12 +35,8 @@ var UseProxyTests = []struct {
 }
 
 func TestUseProxy(t *testing.T) {
-	oldenv := os.Getenv("NO_PROXY")
-	defer os.Setenv("NO_PROXY", oldenv)
-
-	no_proxy := "foobar.com, .barbaz.net"
-	os.Setenv("NO_PROXY", no_proxy)
-
+	ResetProxyEnv()
+	os.Setenv("NO_PROXY", "foobar.com, .barbaz.net")
 	for _, test := range UseProxyTests {
 		if useProxy(test.host+":80") != test.match {
 			t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
@@ -76,3 +72,10 @@ func TestCacheKeys(t *testing.T) {\n 		}\n 	}\n }\n+\n+func ResetProxyEnv() {\n+\tfor _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy"} {\n+\t\tos.Setenv(v, "")\n+\t}\n+\tResetCachedEnvironment()\n+}\n```

### `src/pkg/net/http/transport_test.go`

```diff
--- a/src/pkg/net/http/transport_test.go
+++ b/src/pkg/net/http/transport_test.go
@@ -1694,10 +1694,7 @@ var proxyFromEnvTests = []proxyFromEnvTest{
 }
 
 func TestProxyFromEnvironment(t *testing.T) {
-	os.Setenv("HTTP_PROXY", "")
-	os.Setenv("http_proxy", "")
-	os.Setenv("NO_PROXY", "")
-	os.Setenv("no_proxy", "")
+	ResetProxyEnv()
 	for _, tt := range proxyFromEnvTests {
 		os.Setenv("HTTP_PROXY", tt.env)
 		os.Setenv("NO_PROXY", tt.noenv)

コアとなるコードの解説

src/pkg/net/http/proxy_test.go の変更点

  • TestUseProxy 関数:

    • 元のコードにあった oldenv := os.Getenv("NO_PROXY")defer os.Setenv("NO_PROXY", oldenv) が削除されました。これは、テスト実行後に環境変数を元の状態に戻すためのものでしたが、並列実行や複数回実行のシナリオでは不十分でした。defer は関数スコープで実行されるため、並列テストが同じ環境変数を同時に変更しようとしたり、前のテストの変更が次のテストに影響を与えたりする可能性がありました。
    • 代わりに、新しく追加された ResetProxyEnv() 関数がテストの冒頭で呼び出されるようになりました。これにより、テストが開始される前にすべてのプロキシ関連環境変数が確実にクリアされ、クリーンな状態からテストが実行されます。
    • os.Setenv("NO_PROXY", "foobar.com, .barbaz.net") は残されており、これはこのテストケースで検証したい特定の NO_PROXY 設定を適用するためのものです。ResetProxyEnv() が先に呼び出されることで、この設定が他のテストの影響を受けずに適用されることが保証されます。
  • ResetProxyEnv() 関数の追加:

    • この新しい関数は、プロキシ関連の環境変数(HTTP_PROXY, http_proxy, NO_PROXY, no_proxy)をループで回し、それぞれを空文字列に設定します。これにより、これらの環境変数が確実にクリアされます。
    • ResetCachedEnvironment() が呼び出されています。この関数は net/http パッケージの内部関数であり、環境変数から読み込まれたプロキシ設定の内部キャッシュをリセットする役割を担っていると考えられます。Goの net/http パッケージは、環境変数を一度読み込むと、その値を内部的にキャッシュして再利用することがあります。このキャッシュがリセットされないと、環境変数を os.Setenv で変更しても、net/http パッケージが古いキャッシュされた値を使用し続ける可能性があります。ResetCachedEnvironment() を呼び出すことで、環境変数の変更がGoランタイムに即座に反映されるようになります。

src/pkg/net/http/transport_test.go の変更点

  • TestProxyFromEnvironment 関数:
    • このテストもプロキシ環境変数に依存するため、テストの冒頭で ResetProxyEnv() が呼び出されるようになりました。
    • 元のコードにあった、個々のプロキシ環境変数を空文字列に設定する冗長な行(os.Setenv("HTTP_PROXY", "") など)が削除されました。これにより、コードが簡潔になり、プロキシ環境変数のリセットロジックが ResetProxyEnv() に一元化されました。

これらの変更により、Goの net/http パッケージのプロキシ関連テストは、より堅牢で再現性の高いものになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特に net/http パッケージのテストファイル)
  • Go言語のテストにおける環境変数の扱いに関する一般的なプラクティス
  • go test -cpu オプションに関する情報
  • Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/71940044 (コミットメッセージに記載)
  • Go net/http パッケージのプロキシ環境変数に関するWeb検索結果