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

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

このコミットは、Go言語の標準ライブラリ内の複数のテストファイルにおいて、メモリ割り当て(malloc)のカウントを行うテストが、go test -short フラグが指定された場合にスキップされるように変更を加えるものです。これにより、ビルドダッシュボード上でのテストの不安定性(flakiness)を軽減し、より安定したCI/CDパイプラインの運用を目指しています。

コミット

commit f578726de1d75b5816904972b0a29c1a5fdbda24
Author: Rob Pike <r@golang.org>
Date:   Wed Aug 21 14:00:45 2013 +1000

    all: protect alloc count tests by -testing.short
    
    Update #5000
    Should reduce the flakiness a little. Malloc counting is important
    to general testing but not to the build dashboard, which uses -short.
    
    R=golang-dev, dsymonds
    CC=golang-dev
    https://golang.org/cl/12866047

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

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

元コミット内容

all: protect alloc count tests by -testing.short

Update #5000
Should reduce the flakiness a little. Malloc counting is important
to general testing but not to the build dashboard, which uses -short.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/12866047

変更の背景

このコミットの主な背景は、Go言語のビルドダッシュボード(継続的インテグレーション/デリバリーシステム)におけるテストの「不安定性(flakiness)」の軽減です。

Goのテストフレームワークには、testing.AllocsPerRun のような関数を用いて、特定の操作がどれだけのメモリ割り当て(mallocs)を引き起こすかを計測する機能があります。これは、パフォーマンス最適化やメモリ効率の維持において非常に重要なテストです。しかし、メモリ割り当ての正確なカウントは、実行環境の細かな違い(OSのスケジューリング、他のプロセスの影響、ガベージコレクションのタイミングなど)によって変動しやすく、結果としてテストが時折失敗する「不安定な」状態を引き起こすことがあります。

ビルドダッシュボードは、Goプロジェクトの健全性を継続的に監視するために使用されます。不安定なテストは、実際のバグがないにもかかわらずビルドを失敗させ、開発者の生産性を低下させ、アラート疲れを引き起こす可能性があります。

Goのテストコマンド go test には -short というフラグがあり、これはテストの実行時間を短縮するために、時間のかかるテストやリソースを多く消費するテストをスキップするために使用されます。コミットメッセージにあるように、メモリ割り当てカウントテストは「一般的なテストには重要だが、-short を使用するビルドダッシュボードには重要ではない」と判断されました。これは、ビルドダッシュボードの主な目的が、基本的な機能の回帰テストと迅速なフィードバックであるため、厳密なメモリ割り当ての数値が常に必要ではないという判断に基づいています。

したがって、この変更は、ビルドダッシュボードの安定性を向上させ、開発者がより信頼性の高いフィードバックを得られるようにすることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語のテストに関する概念と一般的なソフトウェア開発の用語を理解しておく必要があります。

  1. go test -short フラグ: go test コマンドはGo言語のテストを実行するための主要なツールです。-short フラグは、テストの実行時間を短縮するために使用されます。テストコード内で testing.Short() 関数を呼び出すことで、このフラグが設定されているかどうかをプログラム的にチェックできます。通常、時間のかかるベンチマークテストや、外部リソースに依存するテスト、あるいは今回のケースのように環境に敏感なテストなどで、このフラグが設定されている場合にスキップするロジックが実装されます。これにより、開発中のローカル環境での迅速なテスト実行や、CI/CD環境での高速なビルドチェックが可能になります。

  2. メモリ割り当て(Mallocs)のカウント: Go言語では、testing パッケージが提供する testing.AllocsPerRun 関数を使用して、特定の関数やコードブロックが実行される際に発生するメモリ割り当ての回数を計測できます。これは、Goプログラムのパフォーマンス最適化、特にガベージコレクションの負荷を軽減するために、ヒープ割り当てを最小限に抑える必要がある場合に非常に有用です。割り当て回数が少ないほど、通常はパフォーマンスが向上し、ガベージコレクションの頻度が減少します。

  3. テストの不安定性(Flakiness): 「不安定なテスト」または「flaky test」とは、コードの変更がないにもかかわらず、実行するたびに成功したり失敗したりするテストのことです。これは、テストが外部要因(ネットワークの遅延、データベースの状態、並行処理のタイミング、OSのスケジューリング、環境変数など)に依存している場合に発生しやすい問題です。不安定なテストは、開発者が実際のバグとテストの不安定性を区別することを困難にし、CI/CDパイプラインの信頼性を損ないます。結果として、開発者はテストの失敗を無視するようになり、実際のバグが見過ごされるリスクが高まります。

  4. ビルドダッシュボード / CI/CD: ビルドダッシュボードは、継続的インテグレーション(CI)および継続的デリバリー(CD)システムの一部として、プロジェクトのビルドとテストのステータスを視覚的に表示するツールです。開発者がコードをリポジトリにプッシュするたびに、自動的にビルドとテストが実行され、その結果がダッシュボードに表示されます。これにより、コードベースの健全性が常に監視され、問題が早期に発見されます。不安定なテストは、このダッシュボードを常に「赤」の状態にし、開発チームに誤ったアラートを送り続けるため、その解決はCI/CDの効率化において重要です。

技術的詳細

このコミットの技術的な核心は、Goの testing パッケージが提供する testing.Short() 関数と t.Skip() メソッドの組み合わせです。

Goのテスト関数(func TestXxx(t *testing.T))内で、testing.Short() を呼び出すと、go test コマンドが -short フラグ付きで実行された場合に true を返します。この真偽値を利用して、特定のテストロジックを条件付きで実行またはスキップすることができます。

このコミットでは、メモリ割り当てをカウントするテストの冒頭に以下のパターンが追加されています。

if testing.Short() {
    t.Skip("skipping malloc count in short mode")
}

このコードスニペットの動作は以下の通りです。

  1. testing.Short(): 現在のテスト実行が -short フラグ付きで行われているかをチェックします。
  2. t.Skip(...): もし testing.Short()true を返した場合(つまり、-short フラグが指定されている場合)、t.Skip() が呼び出されます。t.Skip() は、現在のテスト関数を直ちに終了させ、そのテストをスキップ済みとしてマークします。引数として渡された文字列は、テスト結果の出力にスキップ理由として表示されます。

このメカニズムにより、ビルドダッシュボードのように -short フラグを使用してテストを実行する環境では、メモリ割り当てカウントテストが実行されなくなり、その結果として発生する可能性のある不安定な挙動が回避されます。一方で、開発者がローカルで -short フラグなしでテストを実行する際には、これらの重要なメモリ割り当てテストが通常通り実行され、パフォーマンス特性の監視が継続されます。

また、一部のテストでは runtime.GOMAXPROCS(0) > 1 のチェックも行われています。これは、Goの並行処理の最大CPU数(GOMAXPROCS)が1より大きい場合にテストをスキップするものです。メモリ割り当ての正確なカウントは、並行処理の状況下ではさらに複雑になり、結果が不安定になる可能性があるため、このような条件も追加されています。このコミットでは、既存の GOMAXPROCS チェックに加えて testing.Short() チェックが追加されています。

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

このコミットでは、以下の11個のファイルに共通のパターンで変更が加えられています。

  • src/pkg/encoding/gob/timing_test.go
  • src/pkg/fmt/fmt_test.go
  • src/pkg/net/http/header_test.go
  • src/pkg/net/rpc/server_test.go
  • src/pkg/net/tcp_test.go
  • src/pkg/path/filepath/path_test.go
  • src/pkg/path/path_test.go
  • src/pkg/reflect/all_test.go
  • src/pkg/sort/search_test.go
  • src/pkg/strconv/strconv_test.go
  • src/pkg/time/time_test.go

これらのファイルでは、主にメモリ割り当てを計測するテスト関数(例: TestCountEncodeMallocs, TestCountMallocs, TestHeaderWriteSubsetMallocs, TestAllocsInterfaceBig など)の冒頭に、if testing.Short() { t.Skip("skipping malloc count in short mode") } という行が追加されています。

例: src/pkg/encoding/gob/timing_test.go の変更

--- a/src/pkg/encoding/gob/timing_test.go
+++ b/src/pkg/encoding/gob/timing_test.go
@@ -50,6 +49,9 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) {
 }
 
 func TestCountEncodeMallocs(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping malloc count in short mode")
+	}
 	if runtime.GOMAXPROCS(0) > 1 {
 		t.Skip("skipping; GOMAXPROCS>1")
 	}
@@ -66,10 +68,15 @@ func TestCountEncodeMallocs(t *testing.T) {
 		t.Fatal("encode:", err)
 	})
 	fmt.Printf("mallocs per encode of type Bench: %v\n", allocs)
+	if allocs != 0 {
+		t.Fatalf("mallocs per encode of type Bench: %v; wanted 0\n", allocs)
+	}
 }
 
 func TestCountDecodeMallocs(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping malloc count in short mode")
+	}
 	if runtime.GOMAXPROCS(0) > 1 {
 		t.Skip("skipping; GOMAXPROCS>1")
 	}
@@ -96,5 +103,7 @@ func TestCountDecodeMallocs(t *testing.T) {
 		t.Fatal("decode:", err)
 	})
 	fmt.Printf("mallocs per decode of type Bench: %v\n", allocs)
+	if allocs != 3 {
+		t.Fatalf("mallocs per decode of type Bench: %v; wanted 3\n", allocs)
+	}
 }

また、src/pkg/path/path_test.go では、既存の TestClean 関数とは別に、新たに TestCleanMallocs というテスト関数が追加され、同様のスキップロジックが適用されています。

コアとなるコードの解説

このコミットで追加された主要なコードは、Goの標準ライブラリ testing パッケージの機能を利用したものです。

  1. testing.Short() 関数: この関数は、go test コマンドが -short フラグ付きで実行された場合に true を返します。このフラグは、テストスイート全体の実行時間を短縮するために、時間のかかるテストやリソースを多く消費するテストをスキップする目的で使用されます。開発者は、テストコード内でこの関数を呼び出すことで、テストの実行モードに応じて異なる動作をさせることができます。

  2. *testing.T 型の Skip() メソッド: testing.T は、個々のテストの実行を制御するためのメソッドを提供する型です。t.Skip(args ...interface{}) メソッドは、呼び出されたテスト関数を直ちに終了させ、そのテストを「スキップ済み」としてマークします。args に渡された値は、テスト結果の出力にスキップ理由として表示されます。これにより、テストが失敗したわけではなく、意図的に実行されなかったことを明確に伝えることができます。

これらの機能を組み合わせることで、メモリ割り当てカウントテストのように、環境に敏感で不安定になりがちなテストを、ビルドダッシュボードのような迅速なフィードバックが求められる環境ではスキップし、より詳細なテストが必要な場合には実行するという柔軟なテスト戦略が可能になります。

この変更は、Goのテストフレームワークが提供する組み込みの機能を使って、テストの信頼性と効率性を向上させる典型的な例と言えます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • GitHubのGoリポジトリのコミット履歴
  • Go言語のテストに関する一般的な知識
  • 継続的インテグレーション/デリバリー(CI/CD)に関する一般的な知識
  • テストの不安定性(Flakiness)に関する一般的な情報