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

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

このコミットは、Go言語のsyscallパッケージにおけるUnix系システム向けのテストケースの整理と統合を目的としています。具体的には、既存の複数のテストファイルを一つのファイルに集約し、テストコードの管理と保守を効率化しています。

コミット

commit 2dcf8593acbacc36b970be859711c97e4f266b35
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Mon Feb 24 14:41:10 2014 +0900

    syscall: consolidate test cases for Unix-like systems
    
    As per request from minux in CL 61520049, this CL consolidates
    existing test cases for Unix-like systems into one file except
    Linux-specific credential test.
    
    LGTM=bradfitz
    R=iant, minux.ma, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/67800044

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

https://github.com/golang/go/commit/2dcf8593acbacc36b970be859711c97e4f266b35

元コミット内容

このコミットの元々の内容は、syscallパッケージにおけるUnix系システム向けのテストケースを統合することです。具体的には、src/pkg/syscall/consistency_unix_test.gosrc/pkg/syscall/rlimit_unix_test.goのテストコードを、src/pkg/syscall/passfd_test.goをリネームしたsrc/pkg/syscall/syscall_unix_test.goに移動・統合しています。ただし、Linux固有のクレデンシャルテストは統合の対象外とされています。

変更の背景

この変更の背景には、minux氏からのリクエスト(CL 61520049)があります。Goの標準ライブラリ、特にsyscallパッケージのようにOS固有の機能と密接に関わる部分では、様々なUnix系システム(FreeBSD, Dragonfly BSD, Darwin (macOS), Linux, NetBSD, OpenBSDなど)に対応するためのテストが多数存在します。これらのテストが複数のファイルに分散していると、管理が煩雑になり、新しいテストの追加や既存テストの修正、あるいは全体的なテストカバレッジの把握が困難になる可能性があります。

テストケースを一つのファイルに集約することで、以下のようなメリットが期待されます。

  • 保守性の向上: 関連するテストが同じ場所にあるため、変更が必要な際に探しやすくなります。
  • 可読性の向上: テストの全体像を把握しやすくなり、コードレビューも効率的になります。
  • テスト実行の効率化: 複数のテストファイルを個別にコンパイル・実行するオーバーヘッドが削減される可能性があります。
  • 一貫性の確保: テストの記述スタイルやプラットフォームごとのビルドタグ(+buildディレクティブ)の一貫性を保ちやすくなります。

このコミットは、これらの課題を解決し、syscallパッケージのテスト基盤をより堅牢で管理しやすいものにすることを目的としています。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

Go言語のsyscallパッケージ

syscallパッケージは、Goプログラムからオペレーティングシステム(OS)のシステムコールを直接呼び出すための低レベルなインターフェースを提供します。システムコールは、ファイルI/O、ネットワーク通信、プロセス管理、メモリ管理など、OSカーネルが提供する基本的なサービスを利用するために使用されます。Goの標準ライブラリの多くの高レベルな機能(例: osパッケージ、netパッケージ)は、内部的にこのsyscallパッケージを利用しています。

syscallパッケージはOSに強く依存するため、Unix系システム(Linux, macOS, BSDなど)、Windows、Plan 9など、各OSプラットフォーム向けに異なる実装を持っています。これにより、Goプログラムはクロスプラットフォームで動作しつつも、必要に応じてOS固有の機能を利用できます。

Unix系システム

Unix系システムとは、Unixオペレーティングシステムの設計思想や機能セットを継承したOSの総称です。Linux、macOS (Darwin)、FreeBSD、OpenBSD、NetBSD、Solarisなどが含まれます。これらのシステムは、ファイルシステム構造、プロセス管理、システムコールインターフェースなどにおいて共通の概念やAPIを多く持っています。

Goのsyscallパッケージでは、これらのUnix系システム間で共通して利用できるシステムコールやデータ構造(例: Flock_tRlimit)に対するテストが重要になります。

Goのテストフレームワーク

Goには標準で強力なテストフレームワークが組み込まれています。

  • testingパッケージ: Goのテストは、_test.goで終わるファイルに記述され、testingパッケージを利用します。テスト関数はTestXxxという命名規則に従い、*testing.T型の引数を取ります。
  • ビルドタグ (+buildディレクティブ): Goのソースファイルには、ファイルの先頭に// +build tagのようなコメントを記述することで、特定のビルド条件(OS、アーキテクチャ、カスタムタグなど)が満たされた場合にのみそのファイルをコンパイル対象とするように指定できます。このコミットでは、+build freebsd dragonfly darwin linux netbsd openbsdというタグが使用されており、これは指定されたUnix系OSでのみテストが実行されることを意味します。
  • go testコマンド: go testコマンドは、プロジェクト内のテストファイルを自動的に発見し、実行します。

RLIMIT_NOFILEGetrlimit/Setrlimit

RLIMIT_NOFILEは、プロセスがオープンできるファイルディスクリプタの最大数を制限するリソースリミットの一つです。Unix系システムでは、getrlimitおよびsetrlimitシステムコールを使用して、プロセスのリソース制限(ソフトリミットとハードリミット)を取得および設定できます。

  • ソフトリミット (soft limit): 現在適用されている制限で、プロセスはこの値を超えてリソースを使用できません。
  • ハードリミット (hard limit): ソフトリミットの最大値で、特権プロセス(rootなど)のみがハードリミットを増やすことができます。非特権プロセスはソフトリミットをハードリミット以下に減らすことしかできません。

TestRlimit関数は、これらのシステムコールが正しく機能するかどうかをテストしています。

passfd (File Descriptor Passing)

passfdは、Unixドメインソケットを介してプロセス間でファイルディスクリプタ(FD)を渡すメカニズムです。これにより、異なるプロセスが同じファイルやソケットにアクセスできるようになります。これは、特権分離や効率的なリソース共有に利用されます。TestPassFD関数は、このFDの受け渡し機能が正しく動作するかを検証しています。

技術的詳細

このコミットの主要な技術的変更は、複数のテストファイルを一つに統合することです。

  1. ファイルの削除:

    • src/pkg/syscall/consistency_unix_test.go: このファイルは、Unix系システム間で共通のsyscallの定数や関数(Setpriority, Getpriority, PRIO_USER, PRIO_PROCESS, PRIO_PGRP, TCIFLUSH, TCIOFLUSH, TCOFLUSH, Flock_t構造体など)の存在と型の一貫性をテストしていました。
    • src/pkg/syscall/rlimit_unix_test.go: このファイルは、RLIMIT_NOFILEに対するGetrlimitSetrlimitシステムコールの動作をテストしていました。
  2. ファイルのリネームと統合:

    • src/pkg/syscall/passfd_test.gosrc/pkg/syscall/syscall_unix_test.goにリネームされました。
    • 削除されたconsistency_unix_test.gorlimit_unix_test.goの内容が、新しくリネームされたsyscall_unix_test.goに移動・追加されました。
  3. ビルドタグの調整:

    • passfd_test.goのビルドタグは+build linux dragonfly darwin freebsd netbsd openbsdでしたが、統合後のsyscall_unix_test.goでは+build freebsd dragonfly darwin linux netbsd openbsdに変更されています。これは、consistency_unix_test.goのタグと一致させることで、統合されたテストが意図したすべてのUnix系システムで実行されるようにするためです。
    • 新しいsyscall_unix_test.goには、consistency_unix_test.goにあったコメント// This file tests that some basic syscalls are consistent across // all Unixes.が追加され、ファイルの目的が明確化されています。
  4. テストロジックの移動:

    • consistency_unix_test.go内の_()関数(Goでは、_に代入することで、変数が使用されていないというコンパイラエラーを回避しつつ、型や定数の存在を確認するイディオムとして使われることがあります)がsyscall_unix_test.goに移動されました。
    • rlimit_unix_test.go内のTestRlimit関数がsyscall_unix_test.goに移動されました。
  5. Linux固有のテストの除外: コミットメッセージにある通り、Linux固有のクレデンシャルテストは統合の対象外とされています。これは、特定のOSに強く依存するテストは、そのOS専用のテストファイルに残しておく方が管理しやすいという判断があったためと考えられます。

この統合により、Unix系システム向けの一般的なsyscallテストはsyscall_unix_test.goという単一のエントリポイントからアクセスできるようになり、テストコードの構造がより整理されました。

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

このコミットにおけるコアとなるコードの変更は、主に以下の3つのファイルにわたります。

  1. src/pkg/syscall/consistency_unix_test.go: このファイルが削除されました。

    • 削除された内容:
      • Setpriority, Getpriority, PRIO_USER, PRIO_PROCESS, PRIO_PGRPなどの優先度関連の関数と定数の型チェック。
      • TCIFLUSH, TCIOFLUSH, TCOFLUSHなどのtermios関連の定数の型チェック。
      • Flock_t構造体の型チェック。
  2. src/pkg/syscall/rlimit_unix_test.go: このファイルが削除されました。

    • 削除された内容:
      • TestRlimit関数: RLIMIT_NOFILEに対するGetrlimitSetrlimitの動作をテストする関数。
  3. src/pkg/syscall/passfd_test.go: このファイルがsrc/pkg/syscall/syscall_unix_test.goにリネームされ、上記2つのファイルの内容が追加されました。

    • リネーム前の内容(TestPassFD, TestUnixRightsRoundtripなど)はそのまま維持。
    • 追加された内容:
      • consistency_unix_test.goから移動された、優先度、termios、Flock_tに関する型チェックの_()関数群。
      • rlimit_unix_test.goから移動されたTestRlimit関数。
      • ファイルの先頭に// This file tests that some basic syscalls are consistent across // all Unixes.というコメントが追加。
      • ビルドタグが+build freebsd dragonfly darwin linux netbsd openbsdに更新。
      • TestPassFD内のt.Skip("Skipping test on dragonfly")t.Skip("skipping test on dragonfly")に修正(メッセージの小文字化)。

コアとなるコードの解説

統合されたsyscall_unix_test.goファイルは、Unix系システムにおけるsyscallパッケージの基本的な機能と一貫性を検証するための包括的なテストスイートとなります。

_()関数群 (旧 consistency_unix_test.go からの移動)

// {Set,Get}priority and needed constants for them
func _() {
	var (
		_ func(int, int, int) error   = syscall.Setpriority
		_ func(int, int) (int, error) = syscall.Getpriority
	)
	const (
		_ int = syscall.PRIO_USER
		_ int = syscall.PRIO_PROCESS
		_ int = syscall.PRIO_PGRP
	)
}

// termios functions and constants
func _() {
	const (
		_ int = syscall.TCIFLUSH
		_ int = syscall.TCIOFLUSH
		_ int = syscall.TCOFLUSH
	)
}

func _() {
	_ = syscall.Flock_t{
		Type:   int16(0),
		Whence: int16(0),
		Start:  int64(0),
		Len:    int64(0),
		Pid:    int32(0),
	}
}

これらの_()関数は、Goのコンパイラの型チェックを利用して、特定のシステムコール関数、定数、および構造体が、Goのsyscallパッケージ内で期待されるシグネチャと型で定義されていることを検証します。これは、異なるUnix系システム間でsyscallパッケージの実装に一貫性があることを保証するための基本的なテストです。例えば、syscall.Setpriorityfunc(int, int, int) errorというシグネチャを持つことを確認しています。もし、いずれかのOSでこれらの定義が異なると、コンパイルエラーが発生し、互換性の問題が早期に発見されます。

TestRlimit関数 (旧 rlimit_unix_test.go からの移動)

func TestRlimit(t *testing.T) {
	var rlimit, zero syscall.Rlimit
	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
	if err != nil {
		t.Fatalf("Getrlimit: save failed: %v", err)
	}
	if zero == rlimit {
		t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit)
	}
	set := rlimit
	set.Cur = set.Max - 1
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &set)
	if err != nil {
		t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
	}
	var get syscall.Rlimit
	err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &get)
	if err != nil {
		t.Fatalf("Getrlimit: get failed: %v", err)
	}
	set = rlimit
	set.Cur = set.Max - 1
	if set != get {
		// Seems like Darwin requires some privilege to
		// increase the soft limit of rlimit sandbox, though
		// Setrlimit never reports an error.
		switch runtime.GOOS {
		case "darwin":
		default:
			t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get)
		}
	}
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit)
	if err != nil {
		t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
	}
}

このテスト関数は、Unix系システムにおけるファイルディスクリプタのリソース制限(RLIMIT_NOFILE)の操作が正しく行われるかを検証します。

  1. 現在のリミットの取得と保存: syscall.Getrlimitを呼び出して、現在のRLIMIT_NOFILEのソフトリミットとハードリミットを取得し、rlimit変数に保存します。取得が失敗したり、ゼロ値が返されたりしないことを確認します。
  2. ソフトリミットの変更: 取得したrlimitのコピー(set)を作成し、ソフトリミット(set.Cur)をハードリミット(set.Max)より1つ小さい値に設定します。そして、syscall.Setrlimitを呼び出してこの新しい制限を設定します。
  3. 変更の検証: 再度syscall.Getrlimitを呼び出して、設定した制限が正しく反映されているかを確認します。
  4. Darwin特有の挙動の考慮: Darwin(macOS)では、Setrlimitがエラーを報告しないにもかかわらず、ソフトリミットの増加に特権が必要な場合があるため、このOSではリミットの変更が期待通りに行われなくてもテストを失敗させないように特別な処理が記述されています。
  5. 元のリミットへの復元: テストの最後に、最初に保存したrlimitの値を使って元のリソース制限に戻します。これは、テストがシステムの状態を汚染しないようにするための重要なステップです。

このテストは、syscallパッケージが提供するリソースリミット管理機能が、異なるUnix系システムで期待通りに動作することを保証します。

関連リンク

参考にした情報源リンク