[インデックス 14193] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージにおける主要なネットワークインターフェースであるnet.Conn
、net.PacketConn
、およびnet.Listener
に対する新しいテストを追加します。既存のテストが複数の要素(サーバーの振る舞い、プラットフォーム固有の挙動、プロトコル、API)を同時に検証しており複雑であったため、APIに焦点を当てた、より単一責任の原則に基づいたテストの必要性から導入されました。これにより、各インターフェースの基本的な機能とAPIの振る舞いが独立して検証され、コードの堅牢性と保守性が向上します。
コミット
commit 4545dc6a6953b2be6d0d50719ad165d46278d9bf
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Sun Oct 21 17:17:51 2012 -0400
net: add test for Conn, PacketConn and Listener
I just realized that there is no good place for adding
exposed function or method tests because server, unicast
and multicast_test.go do test complicated multiple test
objects, platform behaviros, protocol behaviors and API,
at the same time. Perhaps splitting them into per test
object might be better, so this CL provides tests focused
on API.
R=rsc
CC=gobot, golang-dev
https://golang.org/cl/6501057
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4545dc6a6953b2be6d0d50719ad165d46278d9bf
元コミット内容
このコミットは、Go言語のnet
パッケージにConn
、PacketConn
、およびListener
インターフェースのためのテストを追加します。既存のテストがサーバー、ユニキャスト、マルチキャストといった複雑なシナリオを同時にテストしており、プラットフォームの挙動、プロトコルの挙動、APIの挙動が混在しているため、APIに特化したテストを追加することで、テストの分離と明確化を図ることを目的としています。
変更の背景
Go言語のnet
パッケージは、ネットワークプログラミングの基盤を提供する非常に重要なコンポーネントです。しかし、コミットメッセージが示唆するように、当時のテストスイートは、個々のネットワークインターフェースのAPIの振る舞いを独立して検証するよりも、より広範で複雑なシナリオ(例: サーバーの動作、ユニキャスト/マルチキャスト通信、プラットフォーム固有の挙動)を統合的にテストする傾向がありました。
このような統合テストは、システム全体の機能を確認する上では有用ですが、特定のAPIメソッドが期待通りに動作するかどうか、あるいは特定のネットワークインターフェースの振る舞いが仕様に準拠しているかを詳細に検証するには不向きです。複数の要素が絡み合うため、テストが失敗した場合にどの部分に問題があるのかを特定するのが困難になるという課題がありました。
このコミットは、この課題に対処するために導入されました。net.Conn
、net.PacketConn
、net.Listener
といった基本的なインターフェースに焦点を当てた、より粒度の細かい単体テストを追加することで、以下の目的を達成しようとしています。
- APIの正確性の検証: 各インターフェースのメソッドが、期待される入力に対して正しい出力を返し、適切なエラーを処理するかを厳密に確認します。
- 回帰テストの強化: 将来の変更が既存のAPIの振る舞いを壊さないことを保証するための安全網を構築します。
- テストの分離と理解の容易さ: 複雑なシナリオからAPIテストを分離することで、テストコード自体が読みやすくなり、何がテストされているのかが明確になります。これにより、開発者がテストの意図を理解しやすくなり、デバッグや機能追加の際に役立ちます。
- プラットフォーム固有の挙動の考慮:
unix
ソケットやip:icmp
など、特定のOSや権限に依存するネットワーク機能についても、適切な条件分岐を設けてテストを記述することで、クロスプラットフォームでの堅牢性を確保します。
この変更は、Goの標準ライブラリの品質と信頼性を向上させるための重要なステップであり、テスト駆動開発(TDD)や堅牢なソフトウェアエンジニアリングの実践に沿ったものです。
前提知識の解説
このコミットの理解には、Go言語の基本的なネットワークプログラミングの概念と、テストに関する知識が必要です。
Go言語のnet
パッケージ
net
パッケージは、Go言語でネットワーク通信を行うための主要なパッケージです。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルをサポートしています。
-
net.Conn
インターフェース:- ストリーム指向のネットワーク接続(例: TCP接続)を表す汎用インターフェースです。
Read
、Write
、Close
、LocalAddr
、RemoteAddr
、SetDeadline
、SetReadDeadline
、SetWriteDeadline
などのメソッドを持ちます。net.Dial
関数によって確立された接続がこのインターフェースを実装します。
-
net.PacketConn
インターフェース:- パケット指向のネットワーク接続(例: UDP接続)を表す汎用インターフェースです。
ReadFrom
、WriteTo
、Close
、LocalAddr
、SetDeadline
、SetReadDeadline
、SetWriteDeadline
などのメソッドを持ちます。net.ListenPacket
関数によって確立された接続がこのインターフェースを実装します。
-
net.Listener
インターフェース:- 着信ネットワーク接続をリッスンするための汎用インターフェースです。
Accept
、Close
、Addr
などのメソッドを持ちます。net.Listen
関数によって作成されたリスナーがこのインターフェースを実装します。
-
net.UDPConn
: UDP接続を表す具体的な型で、net.PacketConn
を実装し、WriteToUDP
やReadFromUDP
などのUDP固有のメソッドを提供します。 -
net.IPConn
: IPプロトコル(例: ICMP)接続を表す具体的な型で、net.PacketConn
を実装し、WriteToIP
やReadFromIP
などのIP固有のメソッドを提供します。 -
net.UnixConn
: Unixドメインソケット接続を表す具体的な型で、net.Conn
やnet.PacketConn
を実装し、WriteMsgUnix
やReadMsgUnix
などのUnixドメインソケット固有のメソッドを提供します。
ネットワークプロトコル
- TCP (Transmission Control Protocol): 信頼性のある、コネクション指向のストリーム通信プロトコル。データの順序保証と再送制御を行います。
- UDP (User Datagram Protocol): 信頼性のない、コネクションレスなデータグラム通信プロトコル。高速ですが、データの到達保証や順序保証はありません。
- Unixドメインソケット (Unix Domain Sockets): 同じホスト上のプロセス間通信(IPC)に使用されるソケット。ネットワークスタックを介さないため、TCP/IPよりも高速です。ストリーム型(
unix
)とデータグラム型(unixgram
)があります。 - ICMP (Internet Control Message Protocol): IPネットワーク上でエラーメッセージや運用情報を送るために使われるプロトコル。
ping
コマンドなどで利用されます。生IPソケット(ip:icmp
)を扱うには、通常、root権限が必要です。
Go言語のテスト
Go言語には、標準でテストフレームワークが組み込まれています。
testing
パッケージ: Goのテスト機能を提供します。- テストファイルの命名規則: テストファイルは、テスト対象のソースファイルと同じディレクトリに配置され、ファイル名の末尾が
_test.go
である必要があります(例:conn_test.go
)。 - テスト関数の命名規則: テスト関数は
Test
で始まり、その後に続く名前の最初の文字が大文字である必要があります(例:func TestConnAndListener(t *testing.T)
)。 *testing.T
: テスト関数に渡される引数で、テストの失敗を報告したり、ログを出力したりするためのメソッドを提供します(例:t.Errorf
,t.Fatalf
,t.Logf
)。t.Fatalf
: テストを即座に終了させ、失敗としてマークします。t.Errorf
: テストを失敗としてマークしますが、テストの実行は続行します。t.Logf
: テスト中に情報をログに出力します。defer
: 関数がリターンする直前に実行される処理をスケジュールします。テストでは、リソースのクリーンアップ(例:Close()
の呼び出し、一時ファイルの削除)によく使用されます。os.Remove
: ファイルシステムからファイルやディレクトリを削除します。Unixドメインソケットのテストでは、ソケットファイルが残らないようにクリーンアップするために使用されます。runtime.GOOS
: 現在のオペレーティングシステムを示す文字列(例:"linux"
,"windows"
,"plan9"
)。プラットフォーム固有のテストロジックを記述する際に使用されます。os.Getuid()
: 現在のプロセスの実効ユーザーIDを返します。特権が必要な操作(例: 生ソケットの作成)のテストで、root権限があるかを確認するために使用されます。
これらの前提知識を理解することで、コミットが追加するテストコードの意図と実装の詳細を深く把握することができます。
技術的詳細
このコミットによって追加されたテストは、net
パッケージの主要なインターフェースであるnet.Conn
、net.PacketConn
、およびnet.Listener
の基本的な機能とAPIの振る舞いを検証することに特化しています。それぞれのテストファイルは、異なる種類のネットワーク接続とそれに対応するインターフェースに焦点を当てています。
src/pkg/net/conn_test.go
このファイルは、net.Conn
とnet.Listener
インターフェースのテストを含んでいます。
connTests
:tcp
、unix
、unixpacket
の3種類のネットワークタイプとアドレスの組み合わせを定義しています。tcp
: 標準的なTCP/IPソケット。unix
: Unixドメインストリームソケット。unixpacket
: Unixドメインデータグラムソケット(Linuxのみ)。
TestConnAndListener
関数:connTests
の各エントリに対してループを実行します。- プラットフォーム固有のスキップ:
unix
およびunixpacket
ソケットは、plan9
やwindows
ではサポートされていないため、これらのOSではテストをスキップします。また、unixpacket
はLinux固有の機能であるため、Linux以外ではスキップされます。 - ソケットファイルのクリーンアップ:
unix
およびunixpacket
の場合、テスト実行前にソケットファイルが存在すれば削除し、テスト終了後にも削除します。これは、ソケットファイルが残っていると次のテスト実行時にエラーになる可能性があるためです。 net.Listen
とnet.Accept
:net.Listen
でリスナーを作成し、transponder
ゴルーチン内でln.Accept()
を呼び出して接続を待ち受けます。net.Dial
: クライアント側でnet.Dial
を使用してリスナーに接続します。net.Conn
メソッドのテスト:LocalAddr()
、RemoteAddr()
: ローカルおよびリモートのアドレスが取得できることを確認します。SetDeadline()
、SetReadDeadline()
、SetWriteDeadline()
: タイムアウト設定が機能することを確認します。Write()
、Read()
: データの送受信が正しく行われることを確認します。transponder
ゴルーチンが受け取ったデータをそのままクライアントに送り返す(エコーバック)ことで、送受信の整合性を検証します。
transponder
関数:net.Listener
から接続を受け入れ、受け取ったデータをそのままクライアントに送り返すヘルパーゴルーチンです。SetDeadline
などのnet.Conn
メソッドも呼び出し、それらの基本的な動作を確認します。
src/pkg/net/packetconn_test.go
このファイルは、net.PacketConn
インターフェースのテストを含んでいます。
packetConnTests
:udp
、ip:icmp
、unixgram
の3種類のネットワークタイプとアドレスの組み合わせを定義しています。udp
: 標準的なUDPソケット。ip:icmp
: 生IPソケット(ICMPプロトコル)。unixgram
: Unixドメインデータグラムソケット。
TestPacketConn
関数:packetConnTests
の各エントリに対してループを実行します。- プラットフォーム固有のスキップと権限チェック:
ip:icmp
はplan9
ではスキップされ、またroot権限が必要なため、os.Getuid() != 0
の場合はスキップされます。unixgram
はplan9
やwindows
ではスキップされます。 - ICMPエコーリクエストの生成:
ip:icmp
の場合、newICMPEchoRequest
関数を使用してICMPエコーリクエストパケットを生成します。 - ソケットファイルのクリーンアップ:
unixgram
の場合、テスト実行前後でソケットファイルを削除します。 net.ListenPacket
: 2つのnet.PacketConn
(c1
,c2
) を作成します。net.PacketConn
メソッドのテスト:LocalAddr()
: ローカルアドレスが取得できることを確認します。SetDeadline()
、SetReadDeadline()
、SetWriteDeadline()
: タイムアウト設定が機能することを確認します。WriteTo()
、ReadFrom()
: パケットの送受信が正しく行われることを確認します。c1
からc2
へ、そしてc2
からc1
へパケットを送信し、それぞれが正しく受信できることを検証します。
TestConnAndPacketConn
関数:packetConnTests
のudp
とip:icmp
のケースに限定してテストを実行します。net.ListenPacket
でc1
(PacketConn
)を作成し、net.Dial
でc2
(Conn
)を作成します。c2.Write()
でデータを送信し、c1.ReadFrom()
で受信できることを確認します。c1.WriteTo()
でデータを送信し、c2.Read()
で受信できることを確認します。- これにより、
net.Conn
とnet.PacketConn
が相互運用できることを検証します。
src/pkg/net/protoconn_test.go
このファイルは、net.UDPConn
、net.IPConn
、net.UnixConn
といった具体的なコネクション型の固有メソッドのテストを含んでいます。
TestUDPConnSpecificMethods
関数:net.ListenUDP
でUDP接続を作成します。File()
: ファイルディスクリプタが取得できることを確認します。LocalAddr()
、RemoteAddr()
: アドレスが取得できることを確認します。SetDeadline()
、SetReadDeadline()
、SetWriteDeadline()
: タイムアウト設定が機能することを確認します。SetReadBuffer()
、SetWriteBuffer()
: ソケットバッファサイズの設定が機能することを確認します。WriteToUDP()
、ReadFromUDP()
: UDP固有の送受信メソッドが機能することを確認します。
TestIPConnSpecificMethods
関数:ip:icmp
プロトコルを使用し、net.ListenIP
でIP接続を作成します。plan9
ではスキップされ、root権限が必要なため、権限チェックが行われます。File()
、LocalAddr()
、RemoteAddr()
、SetDeadline()
、SetReadDeadline()
、SetWriteDeadline()
、SetReadBuffer()
、SetWriteBuffer()
:UDPConn
と同様の汎用メソッドのテストを行います。WriteToIP()
、ReadFromIP()
: IP固有の送受信メソッドが機能することを確認します。newICMPEchoRequest
を使用してICMPエコーリクエストパケットを生成し、送受信をテストします。
TestUnixConnSpecificMethods
関数:unixgram
プロトコルを使用し、net.DialUnix
でUnixドメインデータグラム接続を作成します。plan9
やwindows
ではスキップされます。- ソケットファイルのクリーンアップが行われます。
File()
、LocalAddr()
、RemoteAddr()
、SetDeadline()
、SetReadDeadline()
、SetWriteDeadline()
、SetReadBuffer()
、SetWriteBuffer()
: 汎用メソッドのテストを行います。WriteMsgUnix()
、ReadMsgUnix()
: Unixドメインソケット固有のメッセージ送受信メソッドが機能することを確認します。WriteToUnix()
、ReadFromUnix()
: Unixドメインソケット固有のデータグラム送受信メソッドが機能することを確認します。
newICMPEchoRequest
関数とnewICMPInfoMessage
関数:- ICMPエコーリクエストパケットを生成するためのヘルパー関数です。
- ICMPヘッダの構造(タイプ、コード、チェックサム、識別子、シーケンス番号)を理解し、それに基づいてバイトスライスを構築します。
- 特に、ICMPチェックサムの計算ロジックが含まれています。これは、ネットワークプロトコルレベルでの正確なパケット生成に不可欠な部分です。
これらのテストは、Goのnet
パッケージが提供する様々なネットワークインターフェースとプロトコルに対して、基本的なAPIの振る舞いが期待通りであることを保証するための包括的なカバレッジを提供します。プラットフォーム固有の挙動や特権の必要性も考慮されており、堅牢なテストスイートとなっています。
コアとなるコードの変更箇所
このコミットでは、以下の3つの新しいテストファイルが追加されました。
src/pkg/net/conn_test.go
: 103行の追加src/pkg/net/packetconn_test.go
: 161行の追加src/pkg/net/protoconn_test.go
: 185行の追加
合計で449行のコードが追加されています。既存のファイルへの変更はありません。
コアとなるコードの解説
追加されたテストコードは、Goのnet
パッケージが提供する主要なネットワークインターフェースの基本的な機能とAPIの振る舞いを検証することに重点を置いています。以下に、各テストファイルの主要な部分と、その設計思想について解説します。
src/pkg/net/conn_test.go
このファイルは、ストリーム指向の接続であるnet.Conn
と、接続を待ち受けるnet.Listener
のテストを行います。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net_test
import (
"net"
"os"
"runtime"
"testing"
"time"
)
var connTests = []struct {
net string
addr string
}{
{"tcp", "127.0.0.1:0"},
{"unix", "/tmp/gotest.net"},
{"unixpacket", "/tmp/gotest.net"},
}
func TestConnAndListener(t *testing.T) {
for _, tt := range connTests {
// ... (プラットフォーム固有のスキップとソケットファイルのクリーンアップ) ...
ln, err := net.Listen(tt.net, tt.addr)
if err != nil {
t.Errorf("net.Listen failed: %v", err)
return
}
ln.Addr() // アドレス取得のテスト
defer ln.Close()
done := make(chan int)
go transponder(t, ln, done) // 別ゴルーチンで接続を待ち受け、エコーバック
c, err := net.Dial(tt.net, ln.Addr().String())
if err != nil {
t.Errorf("net.Dial failed: %v", err)
return
}
c.LocalAddr() // ローカルアドレス取得のテスト
c.RemoteAddr() // リモートアドレス取得のテスト
c.SetDeadline(time.Now().Add(100 * time.Millisecond)) // デッドライン設定のテスト
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) // 読み込みデッドライン設定のテスト
c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) // 書き込みデッドライン設定のテスト
defer c.Close()
if _, err := c.Write([]byte("CONN TEST")); err != nil {
t.Errorf("net.Conn.Write failed: %v", err)
return
}
rb := make([]byte, 128)
if _, err := c.Read(rb); err != nil {
t.Errorf("net.Conn.Read failed: %v", err)
}
<-done // transponderゴルーチンの終了を待つ
// ... (ソケットファイルのクリーンアップ) ...
}
}
func transponder(t *testing.T, ln net.Listener, done chan<- int) {
defer func() { done <- 1 }() // 終了時にチャネルに通知
c, err := ln.Accept() // 接続を受け入れる
if err != nil {
t.Errorf("net.Listener.Accept failed: %v", err)
return
}
c.LocalAddr()
c.RemoteAddr()
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c.Close()
b := make([]byte, 128)
n, err := c.Read(b) // データを受信
if err != nil {
t.Errorf("net.Conn.Read failed: %v", err)
return
}
if _, err := c.Write(b[:n]); err != nil { // 受信したデータをそのまま送り返す
t.Errorf("net.Conn.Write failed: %v", err)
return
}
}
- テストの構造:
connTests
という構造体のスライスを使って、テスト対象のネットワークタイプとアドレスをパラメータ化しています。これにより、TCP、Unixドメインストリーム、Unixドメインデータグラムソケットに対して同じテストロジックを適用できます。 - 並行処理:
transponder
関数を別のゴルーチンで実行することで、クライアントとサーバーの役割を同時にシミュレートし、実際のネットワーク通信に近い形でテストを行っています。done
チャネルを使って、transponder
ゴルーチンの完了を待機しています。 - デッドラインのテスト:
SetDeadline
,SetReadDeadline
,SetWriteDeadline
メソッドを呼び出すことで、タイムアウト機能が正しく設定できることを確認しています。これは、ネットワークアプリケーションにおける応答性とリソース管理において重要な機能です。
src/pkg/net/packetconn_test.go
このファイルは、パケット指向の接続であるnet.PacketConn
のテストを行います。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net_test
import (
"net"
"os"
"runtime"
"strings"
"testing"
"time"
)
var packetConnTests = []struct {
net string
addr1 string
addr2 string
}{
{"udp", "127.0.0.1:0", "127.0.0.1:0"},
{"ip:icmp", "127.0.0.1", "127.0.0.1"},
{"unixgram", "/tmp/gotest.net1", "/tmp/gotest.net2"},
}
func TestPacketConn(t *testing.T) {
for _, tt := range packetConnTests {
// ... (プラットフォーム固有のスキップ、権限チェック、ICMPパケット生成、ソケットファイルのクリーンアップ) ...
c1, err := net.ListenPacket(tt.net, tt.addr1)
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
c1.LocalAddr()
c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c1.Close()
c2, err := net.ListenPacket(tt.net, tt.addr2)
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
c2.LocalAddr()
c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c2.Close()
if _, err := c1.WriteTo(wb, c2.LocalAddr()); err != nil { // c1からc2へ書き込み
t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
}
rb2 := make([]byte, 128)
if _, _, err := c2.ReadFrom(rb2); err != nil { // c2で読み込み
t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
}
if _, err := c2.WriteTo(wb, c1.LocalAddr()); err != nil { // c2からc1へ書き込み
t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
}
rb1 := make([]byte, 128)
if _, _, err := c1.ReadFrom(rb1); err != nil { // c1で読み込み
t.Fatalf("net.PacketConn.ReadFrom failed: %v", err)
}
// ... (ソケットファイルのクリーンアップ) ...
}
}
func TestConnAndPacketConn(t *testing.T) {
for _, tt := range packetConnTests {
// ... (プラットフォーム固有のスキップ、権限チェック、ICMPパケット生成) ...
c1, err := net.ListenPacket(tt.net, tt.addr1) // PacketConn
if err != nil {
t.Fatalf("net.ListenPacket failed: %v", err)
}
c1.LocalAddr()
c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c1.Close()
c2, err := net.Dial(tt.net, c1.LocalAddr().String()) // Conn
if err != nil {
t.Fatalf("net.Dial failed: %v", err)
}
c2.LocalAddr()
c2.RemoteAddr()
c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c2.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
defer c2.Close()
if _, err := c2.Write(wb); err != nil { // ConnからPacketConnへ書き込み
t.Fatalf("net.Conn.Write failed: %v", err)
}
rb1 := make([]byte, 128)
if _, _, err := c1.ReadFrom(rb1); err != nil { // PacketConnで読み込み
t.Fatalf("net.PacetConn.ReadFrom failed: %v", err)
}
var dst net.Addr
if netstr[0] == "ip" {
dst = &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}
} else {
dst = c2.LocalAddr()
}
if _, err := c1.WriteTo(wb, dst); err != nil { // PacketConnからConnへ書き込み
t.Fatalf("net.PacketConn.WriteTo failed: %v", err)
}
rb2 := make([]byte, 128)
if _, err := c2.Read(rb2); err != nil { // Connで読み込み
t.Fatalf("net.Conn.Read failed: %v", err)
}
}
}
WriteTo
とReadFrom
:net.PacketConn
の主要なメソッドであるWriteTo
とReadFrom
の動作を検証しています。これらのメソッドは、データグラム通信において送信先アドレスを指定し、受信元アドレスを取得するために使用されます。TestConnAndPacketConn
:net.Conn
とnet.PacketConn
が異なるタイプのソケットであっても、特定のプロトコル(UDP、IP)においては相互に通信できることを示しています。これは、GoのネットワークAPIの柔軟性を示唆しています。
src/pkg/net/protoconn_test.go
このファイルは、net.UDPConn
、net.IPConn
、net.UnixConn
といった具体的なコネクション型が持つ、より特化したメソッドのテストを行います。
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net_test
import (
"bytes"
"net"
"os"
"runtime"
"testing"
"time"
)
func TestUDPConnSpecificMethods(t *testing.T) {
la, err := net.ResolveUDPAddr("udp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("net.ResolveUDPAddr failed: %v", err)
}
c, err := net.ListenUDP("udp4", la)
if err != nil {
t.Fatalf("net.ListenUDP failed: %v", err)
}
c.File() // ファイルディスクリプタ取得のテスト
c.LocalAddr()
c.RemoteAddr()
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
c.SetReadBuffer(2048) // 読み込みバッファサイズ設定のテスト
c.SetWriteBuffer(2048) // 書き込みバッファサイズ設定のテスト
defer c.Close()
wb := []byte("UDPCONN TEST")
if _, err := c.WriteToUDP(wb, c.LocalAddr().(*net.UDPAddr)); err != nil { // UDP固有の書き込み
t.Fatalf("net.UDPConn.WriteToUDP failed: %v", err)
}
rb := make([]byte, 128)
if _, _, err := c.ReadFromUDP(rb); err != nil { // UDP固有の読み込み
t.Fatalf("net.UDPConn.ReadFromUDP failed: %v", err)
}
}
func TestIPConnSpecificMethods(t *testing.T) {
// ... (プラットフォーム固有のスキップと権限チェック) ...
la, err := net.ResolveIPAddr("ip4", "127.0.0.1")
if err != nil {
t.Fatalf("net.ResolveIPAddr failed: %v", err)
}
c, err := net.ListenIP("ip4:icmp", la)
if err != nil {
t.Fatalf("net.ListenIP failed: %v", err)
}
c.File()
c.LocalAddr()
c.RemoteAddr()
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
c.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
c.SetReadBuffer(2048)
c.SetWriteBuffer(2048)
defer c.Close()
id := os.Getpid() & 0xffff
wb := newICMPEchoRequest(id, 1, 128, []byte("IPCONN TEST ")) // ICMPエコーリクエスト生成
if _, err := c.WriteToIP(wb, c.LocalAddr().(*net.IPAddr)); err != nil { // IP固有の書き込み
t.Fatalf("net.IPConn.WriteToIP failed: %v", err)
}
rb := make([]byte, 20+128)
if _, _, err := c.ReadFromIP(rb); err != nil { // IP固有の読み込み
t.Fatalf("net.IPConn.ReadFromIP failed: %v", err)
}
}
// ... (TestUnixConnSpecificMethods関数) ...
func newICMPEchoRequest(id, seqnum, msglen int, filler []byte) []byte {
b := newICMPInfoMessage(id, seqnum, msglen, filler)
b[0] = 8 // ICMP Echo Request type
// ... (ICMPチェックサム計算ロジック) ...
return b
}
func newICMPInfoMessage(id, seqnum, msglen int, filler []byte) []byte {
b := make([]byte, msglen)
copy(b[8:], bytes.Repeat(filler, (msglen-8)/len(filler)+1))
b[0] = 0 // type
b[1] = 0 // code
b[2] = 0 // checksum (placeholder)
b[3] = 0 // checksum (placeholder)
b[4] = byte(id >> 8) // identifier
b[5] = byte(id & 0xff) // identifier
b[6] = byte(seqnum >> 8) // sequence number
b[7] = byte(seqnum & 0xff) // sequence number
return b
}
- 固有メソッドのテスト:
WriteToUDP
,ReadFromUDP
,WriteToIP
,ReadFromIP
,WriteMsgUnix
,ReadMsgUnix
,WriteToUnix
,ReadFromUnix
といった、各コネクション型に特有のメソッドの動作を検証しています。これにより、汎用インターフェースではカバーできない、より詳細なプロトコル固有の振る舞いがテストされます。 File()
メソッドのテスト:File()
メソッドは、ネットワーク接続に対応するファイルディスクリプタ(またはファイル)を取得するために使用されます。このテストは、Goのネットワーク接続が基盤となるOSのリソースとどのように関連しているかを示しています。- バッファサイズの設定:
SetReadBuffer
とSetWriteBuffer
メソッドのテストは、ネットワークI/Oのパフォーマンスチューニングに関連する機能が正しく動作することを確認します。 - ICMPパケットの生成:
newICMPEchoRequest
とnewICMPInfoMessage
関数は、ICMPエコーリクエストパケットを手動で構築するためのものです。これには、ICMPヘッダのフィールド(タイプ、コード、識別子、シーケンス番号)の設定と、特に重要なICMPチェックサムの計算が含まれます。チェックサムは、パケットの整合性を保証するために使用されるため、その正確な計算は生ソケットプログラミングにおいて不可欠です。
これらのテストは、Goのnet
パッケージの堅牢性を高めるために、基本的なAPIの振る舞いから、特定のプロトコルやプラットフォームに依存する詳細な機能まで、幅広いカバレッジを提供しています。テストコード自体も、Goのテストのベストプラクティス(パラメータ化されたテスト、ヘルパー関数、プラットフォーム固有の条件分岐)を示しています。
関連リンク
- Go言語
net
パッケージのドキュメント: https://pkg.go.dev/net - Go言語
testing
パッケージのドキュメント: https://pkg.go.dev/testing - Go言語におけるネットワークプログラミングの基本(公式ブログ記事などがあれば追加)
- Go Blog: Go Sockets: https://go.dev/blog/go-sockets
- Unixドメインソケットに関する情報(一般的な概念)
- ICMPプロトコルに関する情報(一般的な概念)
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- ICMP (Internet Control Message Protocol) - Wikipedia: https://ja.wikipedia.org/wiki/Internet_Control_Message_Protocol
- Unixドメインソケット - Wikipedia: https://ja.wikipedia.org/wiki/Unix%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88
- TCP/IP - Wikipedia: https://ja.wikipedia.org/wiki/TCP/IP
- UDP - Wikipedia: https://ja.wikipedia.org/wiki/User_Datagram_Protocol
- Go言語のテストに関する一般的な情報源(例: Effective GoのTestingセクションなど)
- Effective Go - Testing: https://go.dev/doc/effective_go#testing