[インデックス 12429] ファイルの概要
このコミットは、Go言語の標準ライブラリ path/filepath
パッケージ内の path_test.go
ファイルに対する変更です。具体的には、filepath.Abs
関数のテストケースである TestAbs
の修正と有効化が行われています。path/filepath
パッケージは、ファイルパスの操作(結合、クリーンアップ、絶対パスへの変換など)を行うためのユーティリティを提供します。path_test.go
は、これらのパス操作関数の正確性を検証するためのテストコードを含んでいます。
コミット
- コミットハッシュ:
2184137cf3569bd665f256d03aeee659244e2552
- Author: Rob Pike r@golang.org
- Date: Wed Mar 7 07:54:56 2012 +1100
- コミットメッセージ:
path/filepath/path_test.go: repair and enable TestAbs R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5759051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2184137cf3569bd665f256d03aeee659244e2552
元コミット内容
path/filepath/path_test.go: repair and enable TestAbs
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5759051
変更の背景
このコミットの背景には、filepath.Abs
関数のテスト TestAbs
が以前は無効化されていたか、正しく機能していなかったという問題があります。元のコードでは t.Logf("test needs to be rewritten; disabled")
とコメントアウトされており、テストがスキップされていました。これは、テストが特定の環境変数($GOROOT
)に依存しており、テストの再現性や独立性に問題があったためと考えられます。
filepath.Abs
は与えられたパスを絶対パスに変換する重要な関数であり、その正確な動作を保証するための堅牢なテストは不可欠です。このコミットは、テストを環境に依存しない形に修正し、再度有効化することで、filepath.Abs
の信頼性を向上させることを目的としています。
前提知識の解説
1. ファイルパスと絶対パス・相対パス
- 絶対パス (Absolute Path): ファイルシステムにおけるファイルやディレクトリの完全な位置を示すパスです。ルートディレクトリから始まり、一意にリソースを特定できます。例えば、Unix系システムでは
/home/user/document.txt
、WindowsではC:\Users\user\document.txt
のようになります。 - 相対パス (Relative Path): 現在の作業ディレクトリ(カレントディレクトリ)を基準としてファイルやディレクトリの位置を示すパスです。例えば、カレントディレクトリが
/home/user
の場合、document.txt
は/home/user/document.txt
を指し、../another_user/image.jpg
は/home/another_user/image.jpg
を指します。
2. filepath.Abs
関数
Go言語の path/filepath
パッケージが提供する Abs(path string) (string, error)
関数は、与えられた path
を絶対パスに変換します。この関数は、シンボリックリンクの解決や、カレントディレクトリの考慮など、OS固有のルールに従ってパスを正規化します。
3. Go言語のテストフレームワーク
Go言語には標準でテストフレームワークが組み込まれています。
testing
パッケージ: テストコードを記述するための基本的な機能を提供します。func TestXxx(t *testing.T)
:Test
で始まる関数はテスト関数として認識されます。*testing.T
はテストの状態やエラー報告のためのメソッドを提供します。t.Fatal()
/t.Errorf()
: テスト中にエラーが発生した場合に呼び出すメソッドです。Fatal
はテストを即座に終了させ、Errorf
はテストを続行しながらエラーを報告します。
4. os
パッケージとファイルシステム操作
os.Getwd() (dir string, err error)
: 現在の作業ディレクトリの絶対パスを返します。os.Chdir(dir string) error
: 現在の作業ディレクトリを指定されたディレクトリに変更します。テストにおいて、特定のディレクトリを基準としたパス操作をシミュレートする際によく使用されます。os.Mkdir(name string, perm os.FileMode) error
: 指定された名前とパーミッションで新しいディレクトリを作成します。os.Stat(name string) (fi os.FileInfo, err error)
: 指定されたファイルまたはディレクトリの情報を返します。ファイルが存在しない場合はエラーを返します。
5. io/ioutil
パッケージ
ioutil.TempDir(dir, pattern string) (name string, err error)
: 一時的なディレクトリを作成します。テストにおいて、クリーンな環境でファイルシステム操作をテストする際に非常に便利です。テスト終了後にos.RemoveAll
で削除することが一般的です。
6. strings
パッケージ
strings.Replace(s, old, new string, n int) string
: 文字列s
内のold
のすべての非オーバーラップインスタンスをnew
に置き換えます。n
が負の場合、すべてのインスタンスが置き換えられます。このコミットでは、テストパス内のプレースホルダー($
や$GOROOT
)を実際のパスに置き換えるために使用されています。
技術的詳細
このコミットの主要な技術的変更点は、TestAbs
関数のテスト環境のセットアップ方法とテストケースの定義方法の根本的な見直しです。
-
テスト環境の独立化:
- 変更前:
TestAbs
は$GOROOT/src
をカレントディレクトリとしてテストを実行していました。これは、テストが特定の環境変数とファイルシステム構造に依存することを意味し、異なる環境でのテストの再現性や信頼性を損なう可能性がありました。 - 変更後:
ioutil.TempDir
を使用して一時的なルートディレクトリを作成し、その中でテストを実行するように変更されました。これにより、テストは完全に独立したクリーンな環境で行われ、環境変数や既存のファイルシステム構造に影響されることなく、予測可能な結果が得られるようになりました。テスト終了時にはdefer os.RemoveAll(root)
を使って一時ディレクトリが確実に削除されます。
- 変更前:
-
テストディレクトリの動的作成:
absTestDirs
という新しいスライスが導入され、"a"
,"a/b"
,"a/b/c"
といったテスト用のディレクトリ構造が定義されました。- テストの実行時に、これらのディレクトリが
os.Mkdir
を使って一時ルートディレクトリ内に動的に作成されます。これにより、テストケースが依存するディレクトリ構造が明示的になり、テストのセットアップがより堅牢になりました。
-
テストケースの変更とプレースホルダーの利用:
- 変更前:
abstests
スライスには、$GOROOT
を含むパスや、../AUTHORS
のような相対パスが直接記述されていました。これらのパスはstrings.Replace
で$GOROOT
を実際の値に置き換えていました。 - 変更後:
absTests
スライスには、一時ルートディレクトリを基準とした相対パスや、新しいプレースホルダー$
を含むパスが定義されました。この$
は、テスト実行時にstrings.Replace
を使ってioutil.TempDir
で作成された一時ルートディレクトリのパスに置き換えられます。これにより、テストケースが一時ディレクトリの構造に適合し、より柔軟なテストが可能になりました。
- 変更前:
-
エラーハンドリングの改善:
t.Fatal("Getwd failed: " + err.Error())
のような文字列結合によるエラーメッセージから、t.Fatal("Getwd failed: ", err)
のように可変引数でエラーオブジェクトを渡す形式に変更されました。これはGoのエラーハンドリングの慣習に沿ったもので、より詳細なエラー情報がログに出力されるようになります。
これらの変更により、TestAbs
はより堅牢で、独立性が高く、再現性の高いテストになりました。
コアとなるコードの変更箇所
--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -642,33 +642,61 @@ func TestEvalSymlinks(t *testing.T) {
}\n
}\n
\n-// Test paths relative to $GOROOT/src
-var abstests = []string{\n-\t\"../AUTHORS\",\n-\t\"pkg/../../AUTHORS\",\n-\t\"Make.inc\",\n-\t\"pkg/math\",\n+// Test directories relative to temporary directory.\n+// The tests are run in absTestDirs[0].\n+var absTestDirs = []string{\n+\t\"a\",\n+\t\"a/b\",\n+\t\"a/b/c\",\n+}\n+\n+// Test paths relative to temporary directory. $ expands to the directory.\n+// The tests are run in absTestDirs[0].\n+// We create absTestDirs first.\n+var absTests = []string{\n \t\".\",\n-\t\"$GOROOT/src/Make.inc\",\n-\t\"$GOROOT/src/../src/Make.inc\",\n-\t\"$GOROOT/misc/cgo\",\n-\t\"$GOROOT\",\n+\t\"b\",\n+\t\"../a\",\n+\t\"../a/b\",\n+\t\"../a/b/./c/../../.././a\",\n+\t\"$\",\n+\t\"$/.\",\n+\t\"$/a/../a/b\",\n+\t\"$/a/b/c/../../.././a\",\n }\n \n func TestAbs(t *testing.T) {\n-\tt.Logf(\"test needs to be rewritten; disabled\")\n-\treturn\n-\n \toldwd, err := os.Getwd()\n \tif err != nil {\n-\t\tt.Fatal(\"Getwd failed: \" + err.Error())\n+\t\tt.Fatal(\"Getwd failed: \", err)\n \t}\n \tdefer os.Chdir(oldwd)\n-\tgoroot := os.Getenv(\"GOROOT\")\n-\tcwd := filepath.Join(goroot, \"src\")\n-\tos.Chdir(cwd)\n-\tfor _, path := range abstests {\n-\t\tpath = strings.Replace(path, \"$GOROOT\", goroot, -1)\n+\n+\troot, err := ioutil.TempDir(\"\", \"TestAbs\")\n+\tif err != nil {\n+\t\tt.Fatal(\"TempDir failed: \", err)\n+\t}\n+\tdefer os.RemoveAll(root)\n+\n+\terr = os.Chdir(root)\n+\tif err != nil {\n+\t\tt.Fatal(\"chdir failed: \", err)\n+\t}\n+\n+\tfor _, dir := range absTestDirs {\n+\t\terr = os.Mkdir(dir, 0777)\n+\t\tif err != nil {\n+\t\t\tt.Fatal(\"Mkdir failed: \", err)\n+\t\t}\n+\t}\n+\n+\terr = os.Chdir(absTestDirs[0])\n+\tif err != nil {\n+\t\tt.Fatal(\"chdir failed: \", err)\n+\t}\n+\n+\tfor _, path := range absTests {\n+\t\tpath = strings.Replace(path, \"$\", root, -1)\n \t\tinfo, err := os.Stat(path)\n \t\tif err != nil {\n \t\t\tt.Errorf(\"%s: %s\", path, err)\n```
## コアとなるコードの解説
1. **テストの無効化解除**:
```diff
- t.Logf("test needs to be rewritten; disabled")
- return
```
以前のテストをスキップしていた行が削除され、`TestAbs` が実行されるようになりました。
2. **一時ディレクトリの導入**:
```diff
+ root, err := ioutil.TempDir("", "TestAbs")
+ if err != nil {
+ t.Fatal("TempDir failed: ", err)
+ }
+ defer os.RemoveAll(root)
```
`ioutil.TempDir` を使用して、テスト専用の一時的なルートディレクトリが作成されます。`defer os.RemoveAll(root)` により、テスト関数が終了する際にこの一時ディレクトリとその内容が確実に削除され、テスト環境がクリーンに保たれます。
3. **カレントディレクトリの変更(一時ルートへ)**:
```diff
- goroot := os.Getenv("GOROOT")
- cwd := filepath.Join(goroot, "src")
- os.Chdir(cwd)
+ err = os.Chdir(root)
+ if err != nil {
+ t.Fatal("chdir failed: ", err)
+ }
```
テストのカレントディレクトリが、以前の `$GOROOT/src` から、新しく作成された一時ルートディレクトリ (`root`) に変更されます。これにより、テストが環境変数に依存しなくなります。
4. **テスト用ディレクトリ構造の作成**:
```diff
+ for _, dir := range absTestDirs {
+ err = os.Mkdir(dir, 0777)
+ if err != nil {
+ t.Fatal("Mkdir failed: ", err)
+ }
+ }
```
`absTestDirs` スライスに定義された `"a"`, `"a/b"`, `"a/b/c"` などのディレクトリが、一時ルートディレクトリ内に順次作成されます。これにより、テストケースが期待するファイルシステム構造が動的に構築されます。
5. **カレントディレクトリの変更(テスト実行の基準ディレクトリへ)**:
```diff
+ err = os.Chdir(absTestDirs[0])
+ if err != nil {
+ t.Fatal("chdir failed: ", err)
+ }
```
テストの実行基準となるカレントディレクトリが、一時ルートディレクトリ内の `absTestDirs[0]` (つまり `"a"`) に変更されます。これにより、`absTests` で定義された相対パスが正しく評価されるようになります。
6. **テストパスのプレースホルダー置換**:
```diff
- path = strings.Replace(path, "$GOROOT", goroot, -1)
+ path = strings.Replace(path, "$", root, -1)
```
テストケースのパスに含まれるプレースホルダーの置換ロジックが変更されました。以前は `$GOROOT` を実際の `GOROOT` パスに置き換えていましたが、新しいコードでは `$` を一時ルートディレクトリのパス (`root`) に置き換えます。これにより、テストケースが一時ディレクトリの構造に適合します。
これらの変更により、`TestAbs` はより独立性が高く、再現性があり、堅牢なテストとして機能するようになりました。
## 関連リンク
* Go Change-Id: `I2184137cf3569bd665f256d03aeee659244e2552` (Goの内部変更管理システムにおけるID)
* Go CL (Change List) 5759051: [https://golang.org/cl/5759051](https://golang.org/cl/5759051) (このコミットに対応するGoの変更リストページ)
## 参考にした情報源リンク
* Go言語の公式ドキュメント: `path/filepath` パッケージ
* Go言語の公式ドキュメント: `os` パッケージ
* Go言語の公式ドキュメント: `io/ioutil` パッケージ
* Go言語の公式ドキュメント: `strings` パッケージ
* Go言語の公式ドキュメント: `testing` パッケージ
* Goの変更リスト (CL) 5759051: [https://golang.org/cl/5759051](https://golang.org/cl/5759051)
* GitHub: golang/go リポジトリ
* 一般的なファイルシステムパスの概念(絶対パス、相対パス)に関する情報