[インデックス 12402] ファイルの概要
このコミットでは、Go言語の標準ライブラリ net
パッケージにおけるテストコードが大幅に改善されています。具体的には、以下の3つのファイルが変更されています。
src/pkg/net/file_test.go
: 102行の追加と9行の削除src/pkg/net/server_test.go
: 490行の追加と192行の削除src/pkg/net/timeout_test.go
: 39行の追加と39行の削除
これらの変更は、ネットワークサーバーとクライアントのテストの堅牢性と網羅性を向上させることを目的としています。
コミット
commit d4e138328525341c9893f51255add19276960bb9
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Tue Mar 6 09:43:45 2012 +0900
net: improve server and file tests
* Splits into three server tests.
- TestStreamConnServer for tcp, tcp4, tcp6 and unix networks
- TestSeqpacketConnServer for unixpacket networks
- TestDatagramPacketConnServer for udp, udp4, udp6 and unixgram networks
* Adds both PacketConn and Conn test clients to datagram packet conn tests.
* Fixes wildcard listen test cases on dual IP stack platform.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5701066
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d4e138328525341c9893f51255add19276960bb9
元コミット内容
net: improve server and file tests
- サーバーテストを3つに分割:
TestStreamConnServer
: TCP、TCP4、TCP6、Unixネットワーク用TestSeqpacketConnServer
: Unixpacketネットワーク用TestDatagramPacketConnServer
: UDP、UDP4、UDP6、Unixgramネットワーク用
- データグラムパケットコネクションテストに、
PacketConn
とConn
の両方のテストクライアントを追加。 - デュアルIPスタックプラットフォームでのワイルドカードリスニングテストケースを修正。
変更の背景
Go言語の net
パッケージは、ネットワークプログラミングの基盤を提供する非常に重要なコンポーネントです。このパッケージの安定性と正確性は、Goで開発されるあらゆるネットワークアプリケーションにとって不可欠です。
このコミットが行われた2012年当時、Go言語はまだ比較的新しい言語であり、標準ライブラリも活発に開発・改善されていました。ネットワーク機能は特に複雑であり、様々なOS、ネットワークプロトコル、アドレス設定に対応する必要があるため、テストの網羅性と堅牢性が非常に重要になります。
以前のテストコードは、異なるネットワークタイプ(TCP、UDP、Unixドメインソケットなど)やアドレス設定(特定のIPアドレス、ワイルドカードアドレスなど)に対して、十分に分離されていなかったり、特定のプラットフォームでの挙動を考慮していなかった可能性があります。特に、デュアルIPスタック環境(IPv4とIPv6の両方が有効な環境)でのワイルドカードリスニング(例: 0.0.0.0
や ::
でリッスンする)は、実装が複雑で、予期せぬ挙動を引き起こす可能性があるため、厳密なテストが求められます。
このコミットの目的は、これらの問題を解決し、net
パッケージのネットワークテストをより構造化され、網羅的で、信頼性の高いものにすることにありました。テストを細分化し、異なるネットワークタイプやクライアントタイプに対応することで、将来的なバグの発見や機能追加の際のデバッグが容易になります。
前提知識の解説
このコミットを理解するためには、以下のネットワークプログラミングとGo言語のテストに関する基本的な知識が必要です。
-
ネットワークプロトコル:
- TCP (Transmission Control Protocol): 信頼性の高い、コネクション指向のプロトコル。データの順序保証、再送制御、フロー制御などが行われる。ストリーム指向の通信に適している。
- UDP (User Datagram Protocol): コネクションレスなプロトコル。データの信頼性や順序は保証されないが、オーバーヘッドが少なく高速。データグラム指向の通信に適している。
- Unixドメインソケット (Unix Domain Sockets): 同じホスト上のプロセス間通信 (IPC) に使用されるソケット。ネットワークスタックを介さないため、TCP/IPよりも高速。ファイルシステム上のパス名に関連付けられることが多い。
- Unix (Stream): TCPと同様に信頼性の高いストリーム通信。
- Unixpacket (Sequenced Packet): データグラムの境界を保持しつつ、信頼性と順序を保証する通信。
- Unixgram (Datagram): UDPと同様に信頼性のないデータグラム通信。
- IPv4/IPv6: インターネットプロトコルのバージョン。IPv4は32ビットアドレス、IPv6は128ビットアドレスを使用する。
- IPv4-mapped IPv6アドレス: IPv6アドレス空間内でIPv4アドレスを表現するための形式(例:
::ffff:192.0.2.1
)。デュアルスタック環境でIPv6ソケットがIPv4通信も処理できるようにするために使用されることがある。
-
Go言語の
net
パッケージ:net.Listen(network, address string)
: 指定されたネットワークとアドレスで着信接続をリッスンするnet.Listener
を返す。TCPなどのコネクション指向プロトコルで使用。net.ListenPacket(network, address string)
: 指定されたネットワークとアドレスでパケットをリッスンするnet.PacketConn
を返す。UDPなどのデータグラム指向プロトコルで使用。net.Dial(network, address string)
: 指定されたネットワークとアドレスへの接続を確立するnet.Conn
を返す。net.Conn
インターフェース: コネクション指向のネットワーク接続を表すインターフェース。Read
,Write
,Close
,LocalAddr
,RemoteAddr
,SetDeadline
などのメソッドを持つ。net.PacketConn
インターフェース: データグラム指向のネットワーク接続を表すインターフェース。ReadFrom
,WriteTo
,Close
,LocalAddr
,SetDeadline
などのメソッドを持つ。ReadFrom
は送信元アドレスも返す。WriteTo
は指定されたアドレスにデータを送信する。- ワイルドカードリスニング: サーバーが特定のアドレスではなく、利用可能なすべてのアドレス(例: IPv4の
0.0.0.0
や IPv6の::
)で接続を受け入れる設定。
-
Go言語のテスト:
testing
パッケージ: Goの標準テストフレームワーク。go test
コマンドで実行される。func TestXxx(t *testing.T)
: テスト関数はTest
で始まり、*testing.T
型の引数を取る。t.Errorf(...)
,t.Fatalf(...)
: テスト失敗を報告するメソッド。Fatalf
はテストを即座に終了させる。go func() { ... }()
: ゴルーチン(軽量スレッド)を起動し、並行処理を行う。ネットワークテストでは、サーバーとクライアントを並行して実行するためによく使われる。chan
(チャネル): ゴルーチン間でデータを安全にやり取りするためのGoの機能。このコミットでは、サーバーがリッスンを開始したことをクライアントに通知したり、サーバーが終了したことを待機したりするために使用されている。
技術的詳細
このコミットの主要な変更点は、src/pkg/net/server_test.go
におけるテストの構造化と、src/pkg/net/file_test.go
および src/pkg/net/timeout_test.go
におけるテストケースの拡張です。
src/pkg/net/server_test.go
の変更
- テストの分割: 以前は
doTest
やdoTestPacket
といった汎用的な関数で様々なネットワークタイプを扱っていましたが、これを以下の3つの専用テスト関数に分割しました。TestStreamConnServer
: TCP (tcp, tcp4, tcp6) および Unixストリーム (unix
) ネットワークのテスト。コネクション指向の通信を検証します。TestSeqpacketConnServer
: Unixシーケンスパケット (unixpacket
) ネットワークのテスト。これはLinux固有の機能であり、信頼性のあるデータグラム通信を検証します。TestDatagramPacketConnServer
: UDP (udp, udp4, udp6) および Unixデータグラム (unixgram
) ネットワークのテスト。コネクションレスなデータグラム通信を検証します。
- テストケースの構造化: 各テスト関数は、
streamConnServerTests
,seqpacketConnServerTests
,datagramPacketConnServerTests
という構造体のスライスをイテレートして、多数のテストシナリオを実行します。これにより、異なるネットワークタイプ、アドレス、IPv6/IPv4マッピングの有無、空のデータグラムのテストなど、多様な組み合わせを網羅的にテストできるようになりました。- 各テスト構造体には、サーバー側のネットワーク (
snet
), サーバーアドレス (saddr
), クライアント側のネットワーク (cnet
), クライアントアドレス (caddr
), IPv6サポートの有無 (ipv6
), IPv4マッピングの有無 (ipv4map
), 空のデータテストの有無 (empty
), Linux固有のテスト (linux
) などのフィールドが含まれています。
- 各テスト構造体には、サーバー側のネットワーク (
skipServerTest
関数の導入: 特定のプラットフォームや設定でサポートされていないテストケースをスキップするためのヘルパー関数skipServerTest
が導入されました。これにより、テストの実行環境に依存しない柔軟なテストが可能になります。例えば、WindowsやPlan 9ではUnixソケットがサポートされていないため、関連するテストはスキップされます。また、OSXのファイアウォールダイアログポップアップを避けるためのロジックも含まれています。- クライアントテストの強化:
TestDatagramPacketConnServer
では、データグラム通信のクライアントとしてnet.Conn
インターフェースを使用するrunDatagramConnClient
と、net.PacketConn
インターフェースを使用するrunDatagramPacketConnClient
の両方をテストするようにしました。これにより、異なるAPIパスからのデータグラム送受信の挙動を検証できます。 - ワイルドカードリスニングの修正: デュアルIPスタックプラットフォームでのワイルドカードリスニング(例:
0.0.0.0
や::
)に関するテストケースが修正されました。これは、net.Listen
が返すアドレスが、クライアントが接続する際に使用するアドレスと一致しない場合に問題が発生する可能性があるためです。新しいテスト構造では、サーバーがリッスンを開始した後に実際にリッスンしているアドレスを取得し、それに基づいてクライアントが接続するようにしています。
src/pkg/net/file_test.go
の変更
testFileListener
とtestFilePacketConnListen
の改善:switch
ステートメントを使用して、tcp
,tcp4
,tcp6
およびudp
,udp4
,udp6
の各ネットワークタイプに対して、ポート番号の自動割り当て (:0
) を行うように変更されました。- テストケースの構造化:
fileListenerTests
とfilePacketConnTests
という構造体のスライスが導入され、TestFileListener
とTestFilePacketConn
がこれらのスライスをイテレートしてテストを実行するように変更されました。これにより、ファイルディスクリプタの引き渡しに関するテストがより網羅的になりました。特に、IPv6アドレスや抽象Unixドメインソケット(Linux固有)のテストケースが追加されています。 runtime.GOOS
の削除: 以前はruntime.GOOS
を直接使用して特定のOSでのみテストを実行していましたが、skipServerTest
関数を呼び出すことで、より汎用的なスキップロジックに置き換えられました。
src/pkg/net/timeout_test.go
の変更
testTimeout
関数の引数変更:network
がnet
に、addr
がaddr
に変更され、より一般的な命名になりました。- テストサーバーの変更:
TestTimeoutUDP
とTestTimeoutTCP
で、タイムアウトテストのために使用されるサーバーが、それぞれrunDatagramPacketConnServer
とrunStreamConnServer
に変更されました。これにより、より実際のサーバーの挙動に近い形でタイムアウトをテストできるようになりました。 - エラーメッセージの改善: タイムアウトエラーメッセージが、より詳細で分かりやすいものに改善されました。
これらの変更により、Goの net
パッケージのテストスイートは、よりモジュール化され、網羅的で、異なるプラットフォームやネットワーク設定に対する堅牢性が向上しました。
コアとなるコードの変更箇所
src/pkg/net/server_test.go
の主要な変更点
--- a/src/pkg/net/server_test.go
+++ b/src/pkg/net/server_test.go
@@ -9,229 +9,460 @@ import (
"io"
"os"
"runtime"
- "strings"
"testing"
"time"
)
-// Do not test empty datagrams by default.\n
-// It causes unexplained timeouts on some systems,\n
-// including Snow Leopard. I think that the kernel\n
-// doesn\'t quite expect them.\n
-var testUDP = flag.Bool(\"udp\", false, \"whether to test UDP datagrams\")\n
+func skipServerTest(net, unixsotype, addr string, ipv6, ipv4map, linuxonly bool) bool {
+ switch runtime.GOOS {
+ case "linux":
+ case "plan9", "windows":
+ // "unix" sockets are not supported on Windows and Plan 9.
+ if net == unixsotype {
+ return true
+ }
+ default:
+ if net == unixsotype && linuxonly {
+ return true
+ }
+ }
+ switch addr {
+ case "", "0.0.0.0", "[::ffff:0.0.0.0]", "[::]":
+ if avoidOSXFirewallDialogPopup() {
+ return true
+ }
+ }
+ if ipv6 && !supportsIPv6 {
+ return true
+ }
+ if ipv4map && !supportsIPv4map {
+ return true
+ }
+ return false
+}
-func runEcho(fd io.ReadWriter, done chan<- int) {\n
- var buf [1024]byte\n
+var streamConnServerTests = []struct {
+ snet string // server side
+ saddr string
+ cnet string // client side
+ caddr string
+ ipv6 bool // test with underlying AF_INET6 socket
+ ipv4map bool // test with IPv6 IPv4-mapping functionality
+ empty bool // test with empty data
+ linux bool // test with abstract unix domain socket, a Linux-ism
+}{
+ {snet: "tcp", saddr: "", cnet: "tcp", caddr: "127.0.0.1"},
+ // ... (多数のテストケースが続く) ...
+}
+
+func TestStreamConnServer(t *testing.T) {
+ for _, tt := range streamConnServerTests {
+ if skipServerTest(tt.snet, "unix", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) {
+ continue
+ }
+
+ listening := make(chan string)
+ done := make(chan int)
+ switch tt.snet {
+ case "tcp", "tcp4", "tcp6":
+ tt.saddr += ":0"
+ case "unix":
+ os.Remove(tt.saddr)
+ os.Remove(tt.caddr)
+ }
+
+ go runStreamConnServer(t, tt.snet, tt.saddr, listening, done)
+ taddr := <-listening // wait for server to start
+
+ switch tt.cnet {
+ case "tcp", "tcp4", "tcp6":
+ _, port, err := SplitHostPort(taddr)
+ if err != nil {
+ t.Errorf("SplitHostPort(%q) failed: %v", taddr, err)
+ return
+ }
+ taddr = tt.caddr + ":" + port
+ }
+
+ runStreamConnClient(t, tt.cnet, taddr, tt.empty)
+ <-done // make sure server stopped
+
+ switch tt.snet {
+ case "unix":
+ os.Remove(tt.saddr)
+ os.Remove(tt.caddr)
+ }
+ }
+}
+
+// runStreamConnServer, runStreamConnClient, TestSeqpacketConnServer, TestDatagramPacketConnServer
+// などの新しい関数とテストケース定義が追加・変更されている。
src/pkg/net/file_test.go
の主要な変更点
--- a/src/pkg/net/file_test.go
+++ b/src/pkg/net/file_test.go
@@ -7,7 +7,6 @@ package net
import (
"os"
"reflect"
- "runtime"
"testing"
)
@@ -27,7 +26,8 @@ type connFile interface {
}
func testFileListener(t *testing.T, net, laddr string) {
- if net == "tcp" {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
laddr += ":0" // any available port
}
l, err := Listen(net, laddr)
@@ -55,15 +55,46 @@ func testFileListener(t *testing.T, net, laddr string) {
}
}
+var fileListenerTests = []struct {
+ net string
+ laddr string
+ ipv6 bool // test with underlying AF_INET6 socket
+ linux bool // test with abstract unix domain socket, a Linux-ism
+}{
+ {net: "tcp", laddr: ""},
+ // ... (多数のテストケースが続く) ...
+}
+
func TestFileListener(t *testing.T) {
- if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
- return
- }
- testFileListener(t, "tcp", "127.0.0.1")
- testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
- if runtime.GOOS == "linux" {
- testFileListener(t, "unix", "@gotest/net")
- testFileListener(t, "unixpacket", "@gotest/net")
+ for _, tt := range fileListenerTests {
+ if skipServerTest(tt.net, "unix", tt.laddr, tt.ipv6, false, tt.linux) {
+ continue
+ }
+ if skipServerTest(tt.net, "unixpacket", tt.laddr, tt.ipv6, false, tt.linux) {
+ continue
+ }
+ testFileListener(t, tt.net, tt.laddr)
}
}
// ... (testFilePacketConnListen, testFilePacketConnDial, filePacketConnTests, TestFilePacketConn も同様に変更) ...
コアとなるコードの解説
skipServerTest
関数
この関数は、特定のテストケースをスキップすべきかどうかを判断します。
runtime.GOOS
をチェックし、WindowsやPlan 9などのOSでUnixソケットがサポートされていない場合にtrue
を返します。- Linux固有の抽象Unixドメインソケットのテストを、Linux以外のOSでスキップします。
- ワイルドカードアドレス(
""
,"0.0.0.0"
,"[::ffff:0.0.0.0]"
,"[::]"
)でリッスンするテストにおいて、avoidOSXFirewallDialogPopup()
がtrue
を返す場合にスキップします。これは、OSXでワイルドカードアドレスを使用するとファイアウォールのダイアログが表示されるのを避けるためのものです。 - IPv6またはIPv4マッピングが必要なテストで、システムがそれらをサポートしていない場合にスキップします。
この関数により、テストスイートは様々な環境で適切に動作し、不要なエラーやユーザーインタラクションを避けることができます。
streamConnServerTests
および TestStreamConnServer
streamConnServerTests
は、TCPおよびUnixストリームソケットのテストシナリオを定義する構造体のスライスです。各要素は、サーバーとクライアントのネットワークタイプ、アドレス、IPv6/IPv4マッピングの有無、空のデータテストの有無、Linux固有のテストの有無などを指定します。
TestStreamConnServer
関数は、この streamConnServerTests
スライスをループで処理します。
skipServerTest
を呼び出して、現在のテストケースをスキップすべきか判断します。- サーバーがリッスンを開始したことを通知するためのチャネル (
listening
) と、サーバーが終了したことを通知するためのチャネル (done
) を作成します。 - TCP系のネットワークの場合、サーバーアドレスに
:0
を追加して、利用可能な任意のポートを割り当てます。Unixソケットの場合、テスト前にソケットファイルを削除します。 runStreamConnServer
をゴルーチンとして起動し、サーバーをバックグラウンドで実行します。<-listening
でサーバーがリッスンを開始するのを待ち、実際にリッスンしているアドレス (taddr
) を取得します。- クライアントのネットワークタイプに応じて、
taddr
を調整します(例: TCPの場合、クライアントアドレスにサーバーのポート番号を追加)。 runStreamConnClient
を呼び出してクライアントを実行し、サーバーとの通信をテストします。<-done
でサーバーが終了するのを待ちます。- Unixソケットの場合、テスト後にソケットファイルを削除します。
この構造により、多数の異なるネットワーク設定とシナリオに対して、自動的かつ網羅的にテストを実行できるようになります。
fileListenerTests
および TestFileListener
fileListenerTests
は、ファイルディスクリプタの引き渡しに関するリスナーテストのシナリオを定義する構造体のスライスです。
TestFileListener
関数は、この fileListenerTests
スライスをループで処理します。
skipServerTest
を呼び出して、UnixおよびUnixpacketソケットのテストをスキップすべきか判断します。testFileListener
関数を呼び出し、各テストケースを実行します。
testFileListener
関数内では、switch net
を使用して、tcp
, tcp4
, tcp6
ネットワークに対してポート番号の自動割り当てを行うように変更されています。これにより、テストの柔軟性が向上し、特定のポートが利用可能であるかどうかに依存しなくなります。
これらの変更は、Goの net
パッケージのテストの品質と網羅性を大幅に向上させ、将来的な開発とメンテナンスを容易にするための重要なステップでした。
関連リンク
- Go CL 5701066: https://golang.org/cl/5701066
参考にした情報源リンク
- Go言語の
net
パッケージに関する公式ドキュメント: https://pkg.go.dev/net - Go言語のテストに関する公式ドキュメント: https://pkg.go.dev/testing
- Unixドメインソケットに関する一般的な情報 (例: Wikipediaなど)
- IPv4-mapped IPv6アドレスに関する一般的な情報 (例: RFC 4291など)
- Go言語のネットワークプログラミングに関するチュートリアルや記事 (一般的な知識として)
- Go言語のソースコード (特に
src/pkg/net
ディレクトリ) - Go言語のIssue TrackerやCode Reviewシステム (当時の議論を追うため)