[インデックス 13098] ファイルの概要
このドキュメントは、Go言語の標準ライブラリnet
パッケージにおけるテストコードの修正に関するコミット(インデックス13098)について、その詳細な技術解説を提供します。具体的には、テストにおけるリソースリークの修正と、変数名の変更に焦点を当てています。
コミット
net: fix leak in test
このコミットは、Go言語のnet
パッケージのテストコードにおけるリソースリークを修正し、同時にListener
変数の名前をl
からln
に変更しています。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ba57c8800318d3378b438f5bf79ac276960c03e7
元コミット内容
commit ba57c8800318d3378b438f5bf79ac276960c03e7
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Sat May 19 10:42:54 2012 +0900
net: fix leak in test
Also change the Listner variable name from l to ln.
R=golang-dev, rsc, dave
CC=golang-dev
https://golang.org/cl/5918046
変更の背景
このコミットの主な背景は、net
パッケージのテストコードnet_test.go
において、テスト実行後にネットワークリソース(特にListener
によって開かれたポート)が適切にクローズされず、リークが発生していた問題の修正です。リソースリークは、テストの信頼性を低下させ、特にCI/CD環境のような連続的なテスト実行において、ポートの枯渇やテストの不安定化を引き起こす可能性があります。
また、変数名l
はListener
の略として使われていましたが、より明確なln
(Listenerの略)に変更することで、コードの可読性と保守性を向上させる意図があります。これはGo言語のコーディング規約において、短い変数名を使用する場合でも、その意味が明確であることが推奨される原則に沿ったものです。
前提知識の解説
Go言語のnet
パッケージ
Go言語のnet
パッケージは、ネットワークI/Oのプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うための機能が含まれています。
net.Listen(network, address string)
: 指定されたネットワーク(例: "tcp", "tcp4", "tcp6", "udp", "unix")とアドレスでネットワークリスナーを宣言します。成功するとnet.Listener
インターフェースを実装したオブジェクトを返します。net.Dial(network, address string)
: 指定されたネットワークとアドレスに接続します。成功するとnet.Conn
インターフェースを実装したオブジェクトを返します。net.Listener
インターフェース:Accept() (Conn, error)
: 新しい接続を待ち受け、確立された接続をnet.Conn
として返します。Close() error
: リスナーを閉じ、関連するネットワークリソースを解放します。Addr() Addr
: リスナーのネットワークアドレスを返します。
net.Conn
インターフェース:Close() error
: 接続を閉じます。
net.ListenPacket(network, address string)
: パケット指向のネットワーク接続(UDPなど)をリッスンします。成功するとnet.PacketConn
インターフェースを実装したオブジェクトを返します。net.PacketConn
インターフェース:ReadFrom(b []byte) (n int, addr Addr, err error)
: パケットを読み込みます。Close() error
: 接続を閉じます。
Go言語のテスト
Go言語のテストは、testing
パッケージを使用して記述されます。テスト関数はTestXxx(*testing.T)
というシグネチャを持ち、go test
コマンドで実行されます。
t.Fatalf(format string, args ...interface{})
: テストを失敗としてマークし、実行を停止します。t.Errorf(format string, args ...interface{})
: テストを失敗としてマークしますが、実行は継続します。t.Logf(format string, args ...interface{})
: テストのログに出力します。defer
ステートメント:defer
に続く関数呼び出しは、その関数がリターンする直前に実行されます。これはリソースのクリーンアップ(ファイルやネットワーク接続のクローズなど)に非常に便利です。
リソースリーク
プログラムが確保したメモリやファイルハンドル、ネットワークソケットなどのリソースを、使用後に適切に解放しない場合に発生する問題です。特にネットワークプログラミングでは、開いたソケットやリスナーを閉じ忘れると、ポートが占有され続け、新しい接続を受け付けられなくなったり、システムリソースを枯渇させたりする可能性があります。
技術的詳細
このコミットは、net_test.go
内のTestShutdown
、TestTCPListenClose
、TestUDPListenClose
という3つのテスト関数に影響を与えています。これらのテストは、ネットワークリスナーのライフサイクル(作成、接続の受け入れ、クローズ)を検証するものです。
リソースリークの修正
TestTCPListenClose
関数において、l.Accept()
がエラーを返した場合(リスナーがクローズされたためなど)、c.Close()
が呼び出されないパスが存在していました。具体的には、Accept
がエラーを返した場合、if err == nil
の条件が偽となり、c.Close()
がスキップされていました。しかし、Accept
が成功してc
が有効なnet.Conn
を返した場合、そのc
がクローズされないままテスト関数が終了する可能性がありました。
修正前:
_, err = l.Accept()
if err == nil {
t.Error("Accept succeeded")
} else {
t.Logf("Accept timeout error: %s (any error is fine)", err)
}
修正後:
c, err := ln.Accept()
if err == nil {
c.Close() // Acceptが成功した場合、接続をクローズする
t.Error("Accept succeeded")
} else {
t.Logf("Accept timeout error: %s (any error is fine)", err)
}
この変更により、Accept
が成功して接続が確立された場合でも、その接続が確実にクローズされるようになりました。これにより、テスト実行後のリソースリークが防止されます。
変数名の変更
net.Listener
およびnet.PacketConn
のインスタンスを指す変数名が、一貫してl
からln
に変更されました。これは、Go言語の慣習に従い、より記述的で明確な変数名を使用するためのリファクタリングです。l
は"listener"の略として一般的ですが、ln
はより明確に"listener"を指し示します。この変更は機能的な影響はなく、コードの可読性向上のみを目的としています。
コアとなるコードの変更箇所
変更はsrc/pkg/net/net_test.go
ファイルに集中しています。
--- a/src/pkg/net/net_test.go
+++ b/src/pkg/net/net_test.go
@@ -16,15 +16,15 @@ func TestShutdown(t *testing.T) {
t.Logf("skipping test on %q", runtime.GOOS)
return
}
- l, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- if l, err = Listen("tcp6", "[::1]:0"); err != nil {
+ if ln, err = Listen("tcp6", "[::1]:0"); err != nil {
t.Fatalf("ListenTCP on :0: %v", err)
}
}
go func() {
- c, err := l.Accept()
+ c, err := ln.Accept()
if err != nil {
t.Fatalf("Accept: %v", err)
}
@@ -37,7 +37,7 @@ func TestShutdown(t *testing.T) {
c.Close()
}()
- c, err := Dial("tcp", l.Addr().String())
+ c, err := Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -59,7 +59,7 @@ func TestShutdown(t *testing.T) {
}
func TestTCPListenClose(t *testing.T) {
- l, err := Listen("tcp", "127.0.0.1:0")
+ ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
@@ -67,11 +67,12 @@ func TestTCPListenClose(t *testing.T) {
done := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
- l.Close()
+ ln.Close()
}()
go func() {
- _, err = l.Accept()
+ c, err := ln.Accept()
if err == nil {
+ c.Close() // 追加された行
t.Error("Accept succeeded")
} else {
t.Logf("Accept timeout error: %s (any error is fine)", err)
@@ -86,7 +87,7 @@ func TestTCPListenClose(t *testing.T) {
}
func TestUDPListenClose(t *testing.T) {
- l, err := ListenPacket("udp", "127.0.0.1:0")
+ ln, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
@@ -95,10 +96,10 @@ func TestUDPListenClose(t *testing.T) {
done := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
- l.Close()
+ ln.Close()
}()
go func() {
- _, _, err = l.ReadFrom(buf)
+ _, _, err = ln.ReadFrom(buf)
if err == nil {
t.Error("ReadFrom succeeded")
} else {
コアとなるコードの解説
TestShutdown
関数
この関数では、net.Listen
で作成されたリスナー変数l
がln
に変更されています。機能的な変更はありませんが、変数名の一貫性が保たれています。
TestTCPListenClose
関数
この関数は、TCPリスナーがクローズされた後にAccept
がどのように振る舞うかをテストします。
最も重要な変更は以下の部分です。
c, err := ln.Accept()
if err == nil {
+ c.Close() // Acceptが成功した場合、接続をクローズする
t.Error("Accept succeeded")
} else {
t.Logf("Accept timeout error: %s (any error is fine)", err)
}
以前はAccept
が成功した場合(これはテストの意図に反するが、起こりうる)、取得したnet.Conn
オブジェクトc
がクローズされずに残る可能性がありました。この修正により、Accept
が成功した場合にはc.Close()
が明示的に呼び出され、リソースリークが防止されます。
TestUDPListenClose
関数
この関数は、UDPパケットリスナーがクローズされた後にReadFrom
がどのように振る舞うかをテストします。
ここでも、リスナー変数l
がln
に変更されています。機能的な変更はありません。
全体として、このコミットはGoのnet
パッケージのテストの堅牢性を高め、リソース管理のベストプラクティスを適用しています。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
net
パッケージのドキュメント: https://pkg.go.dev/nettesting
パッケージのドキュメント: https://pkg.go.dev/testing- Go CL (Change List) 5918046: https://golang.org/cl/5918046
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/pkg/net/net_test.go
) - Gitコミット履歴
- Go言語のコーディング規約に関する一般的な情報