[インデックス 11027] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
において、外部テストファイル (XTestGoFiles
) が gofmt
、govet
、gofix
といったコード品質向上ツールによって適切に処理されるようにするための変更です。また、XTestGoFiles
を go command
の公開APIに追加し、Goパッケージの構造に関する情報がより正確に反映されるようにしています。
コミット
commit 2693232f16fb91d715349b81bbb7af3c96c5459e
Author: Sanjay Menakuru <balasanjay@gmail.com>
Date: Thu Jan 5 10:37:15 2012 +1100
cmd/go: include external test files in the files sent to gofmt, govet, and gofix
Also, add XTestGoFiles to the go command's public api.
Fixes #2649.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5502102
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2693232f16fb91d715349b81bbb7af3c96c5459e
元コミット内容
cmd/go
: gofmt
、govet
、gofix
に送られるファイルに外部テストファイルを含める。
また、XTestGoFiles
を go command
の公開APIに追加する。
Issue #2649 を修正。
変更の背景
このコミットが行われる以前は、Go言語のビルドシステムおよびツール群において、パッケージ内部のテストファイル (TestGoFiles
) とは異なる「外部テストファイル」(XTestGoFiles
) が、gofmt
(コードフォーマッタ)、govet
(静的解析ツール)、gofix
(コード自動修正ツール) といった重要な開発ツールによって適切に処理されていませんでした。
これにより、以下のような問題が発生する可能性がありました。
- コードスタイルの不統一: 外部テストファイルが
gofmt
の対象外であったため、これらのファイルのコードスタイルがプロジェクト全体の規約から逸脱する可能性がありました。手動でのフォーマットは手間がかかり、見落としも発生しやすくなります。 - 潜在的なバグの見落とし:
govet
はGoコードの一般的なバグや疑わしい構造を検出するのに役立ちますが、外部テストファイルが解析対象外であると、テストコード内に潜むバグや非効率な記述が見過ごされるリスクがありました。 - API変更への追従の遅れ:
gofix
はGo言語の進化に伴うAPIの変更や非推奨化に対応するために、古いコードを自動的に新しい形式に修正するツールです。外部テストファイルがgofix
の対象外であると、Go言語のバージョンアップ時にこれらのテストファイルがコンパイルエラーになったり、意図しない動作を引き起こしたりする可能性がありました。
このコミットは、これらの問題を解決し、Goプロジェクト全体のコード品質、一貫性、およびメンテナンス性を向上させることを目的としています。特に、テストコードも本番コードと同様に高品質であることが、ソフトウェア全体の信頼性を保証する上で不可欠であるという認識に基づいています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびGoツールチェーンに関する知識が必要です。
-
Go言語のパッケージとテスト:
- パッケージ: Go言語のコードはパッケージにまとめられます。パッケージはディレクトリ構造に対応し、関連する機能を提供します。
- 内部テスト (
TestGoFiles
): 通常、テストファイルはテスト対象のパッケージと同じディレクトリに配置され、ファイル名が_test.go
で終わります。これらのテストファイルは、テスト対象のパッケージと同じ名前空間に属し、パッケージの内部関数や変数にアクセスできます。これらはPackage.TestGoFiles
として扱われます。 - 外部テスト (
XTestGoFiles
): 大規模なパッケージや、パッケージの公開APIのみをテストしたい場合、テストファイルを別のパッケージとして定義することがあります。この場合、テストファイルは_test.go
で終わるファイル名を持ちますが、_test
サフィックスを持つ別のパッケージ(例:package foo_test
)に属します。これにより、テストコードはテスト対象のパッケージの公開された要素のみにアクセスでき、より厳密な結合テストが可能になります。これらはPackage.XTestGoFiles
として扱われます。
-
Goツールチェーンの主要コマンド:
go test
: Go言語のテストを実行するためのコマンドです。内部テストと外部テストの両方を自動的に検出して実行します。gofmt
: Goソースコードを標準的なスタイルに自動的にフォーマットするツールです。Goコミュニティでは、gofmt
によってフォーマットされたコードが標準とされています。govet
: Goソースコードの静的解析を行うツールです。潜在的なバグや疑わしいコード構造(例: unreachable code, unkeyed struct literals)を検出します。gofix
: Go言語の進化に伴い、APIの変更や非推奨化が行われた際に、古いコードを新しいAPIに自動的に修正するためのツールです。Go 1リリース前の移行期間に特に重要でした。
-
cmd/go
パッケージ:- Go言語の公式コマンドラインツール (
go
コマンド) の実装が含まれるパッケージです。このパッケージは、Goのビルド、テスト、フォーマット、静的解析など、Go開発における様々なタスクを管理します。 Package
構造体:cmd/go
パッケージ内で、Goのパッケージに関するメタデータ(ソースファイル、依存関係など)を表現するために使用される構造体です。この構造体には、GoFiles
(通常のGoソースファイル)、TestGoFiles
(内部テストファイル)、CFiles
(Cソースファイル) などのフィールドが含まれます。scanPackage
関数: ファイルシステムをスキャンし、Goのパッケージ情報を解析してPackage
構造体を生成する役割を担う関数です。
- Go言語の公式コマンドラインツール (
技術的詳細
このコミットの技術的な核心は、cmd/go
パッケージ内の Package
構造体と、パッケージをスキャンするロジックの変更にあります。
-
Package
構造体へのXTestGoFiles
フィールドの追加:src/cmd/go/list.go
およびsrc/cmd/go/pkg.go
内のPackage
構造体に、XTestGoFiles []string
という新しいフィールドが追加されました。このフィールドは、パッケージの外部テストファイル (_test.go
で終わるが、別の_test
パッケージに属するファイル) のリストを保持します。- これにより、
go command
の内部で、外部テストファイルが正式に認識され、その情報がPackage
構造体を通じて公開されるようになりました。
-
scanPackage
関数におけるXTestGoFiles
の読み込み:src/cmd/go/pkg.go
内のscanPackage
関数が修正され、build.DirInfo
からXTestGoFiles
の情報を読み込み、新しく追加されたPackage.XTestGoFiles
フィールドに割り当てるようになりました。build.DirInfo
は、Goのビルドシステムがディレクトリをスキャンして収集するファイル情報を含んでいます。この変更により、scanPackage
がパッケージのすべての関連ファイルを正確に識別できるようになります。
-
gofiles
スライスへのXTestGoFiles
の追加:Package
構造体には、gofiles
という内部フィールドがあります。これは、gofmt
、govet
、gofix
などのツールが処理すべきすべてのGoソースファイルの絶対パスを保持するスライスです。- このコミットでは、
scanPackage
関数内で、info.XTestGoFiles
の内容がp.gofiles
スライスに追加されるようになりました。 - この変更が最も重要です。これにより、
gofmt
、govet
、gofix
といったツールがPackage.gofiles
を参照して処理対象ファイルを決定する際に、外部テストファイルも自動的に含まれるようになります。結果として、これらのツールは外部テストファイルに対してもフォーマット、静的解析、自動修正を適用するようになります。
これらの変更により、Goツールチェーンは外部テストファイルを「ファーストクラスの市民」として扱い、本番コードと同様に品質管理の対象とすることで、Goプロジェクト全体の健全性を高めています。
コアとなるコードの変更箇所
このコミットは主に src/cmd/go/list.go
と src/cmd/go/pkg.go
の2つのファイルに影響を与えています。
src/cmd/go/list.go
の変更
Package
構造体の定義に XTestGoFiles
フィールドが追加され、既存の GoFiles
および TestGoFiles
のコメントが更新されました。
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -36,12 +36,13 @@ being passed to the template is:
Stale bool // would 'go install' do anything for this package?
// Source files
- GoFiles []string // .go source files (excluding CgoFiles and TestGoFiles)
- TestGoFiles []string // _test.go source files
- CFiles []string // .c source files
- HFiles []string // .h source files
- SFiles []string // .s source files
- CgoFiles []string // .go sources files that import "C"
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, and XTestGoFiles)
+ TestGoFiles []string // _test.go source files internal to the package they are testing
+ XTestGoFiles []string // _test.go source files external to the package they are testing
+ CFiles []string // .c source files
+ HFiles []string // .h source files
+ SFiles []string // .s source files
+ CgoFiles []string // .go sources files that import "C"
// Dependency information
Imports []string // import paths used by this package
src/cmd/go/pkg.go
の変更
Package
構造体の定義にXTestGoFiles
フィールドが追加され、JSONタグが設定されました。Package
構造体の内部フィールドgofiles
のコメントが更新され、XTestGoFiles
も含まれることが明記されました。scanPackage
関数内で、Package
構造体の初期化時にinfo.XTestGoFiles
が新しいXTestGoFiles
フィールドに割り当てられるようになりました。scanPackage
関数内で、info.XTestGoFiles
の各ファイルパスがp.gofiles
スライスに追加されるようになりました。
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -29,14 +29,15 @@ type Package struct {
Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
// Source files
-\tGoFiles []string // .go source files (excluding CgoFiles and TestGoFiles)
-\tTestGoFiles []string `json:",omitempty"` // _test.go source files
-\tCFiles []string `json:",omitempty"` // .c source files
-\tHFiles []string `json:",omitempty"` // .h source files
-\tSFiles []string `json:",omitempty"` // .s source files
-\tCgoFiles []string `json:",omitempty"` // .go sources files that import "C"
-\tCgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
-\tCgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
+\tGoFiles []string // .go source files (excluding CgoFiles, TestGoFiles and XTestGoFiles)
+\tTestGoFiles []string `json:",omitempty"` // _test.go source files internal to the package they are testing
+\tXTestGoFiles []string `json:",omitempty"` //_test.go source files external to the package they are testing
+\tCFiles []string `json:",omitempty"` // .c source files
+\tHFiles []string `json:",omitempty"` // .h source files
+\tSFiles []string `json:",omitempty"` // .s source files
+\tCgoFiles []string `json:",omitempty"` // .go sources files that import "C"
+\tCgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
+\tCgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
// Dependency information
Imports []string `json:",omitempty"` // import paths used by this package
@@ -47,7 +48,7 @@ type Package struct {
\tpkgdir string
\tinfo *build.DirInfo
\timports []*Package
-\tgofiles []string // GoFiles+CgoFiles+TestGoFiles files, absolute paths
+\tgofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
\ttarget string // installed file for this package (may be executable)
\tfake bool // synthesized package
}\n
@@ -127,23 +128,24 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string\n \t}\n \n \tp := &Package{\n-\t\tName: info.Package,\n-\t\tDoc: doc.CommentText(info.PackageComment),\n-\t\tImportPath: importPath,\n-\t\tDir: dir,\n-\t\tImports: info.Imports,\n-\t\tGoFiles: info.GoFiles,\n-\t\tTestGoFiles: info.TestGoFiles,\n-\t\tCFiles: info.CFiles,\n-\t\tHFiles: info.HFiles,\n-\t\tSFiles: info.SFiles,\n-\t\tCgoFiles: info.CgoFiles,\n-\t\tCgoCFLAGS: info.CgoCFLAGS,\n-\t\tCgoLDFLAGS: info.CgoLDFLAGS,\n-\t\tStandard: t.Goroot && !strings.Contains(importPath, \".\"),\n-\t\ttarget: target,\n-\t\tt: t,\n-\t\tinfo: info,\n+\t\tName: info.Package,\n+\t\tDoc: doc.CommentText(info.PackageComment),\n+\t\tImportPath: importPath,\n+\t\tDir: dir,\n+\t\tImports: info.Imports,\n+\t\tGoFiles: info.GoFiles,\n+\t\tTestGoFiles: info.TestGoFiles,\n+\t\tXTestGoFiles: info.XTestGoFiles,\n+\t\tCFiles: info.CFiles,\n+\t\tHFiles: info.HFiles,\n+\t\tSFiles: info.SFiles,\n+\t\tCgoFiles: info.CgoFiles,\n+\t\tCgoCFLAGS: info.CgoCFLAGS,\n+\t\tCgoLDFLAGS: info.CgoLDFLAGS,\n+\t\tStandard: t.Goroot && !strings.Contains(importPath, \".\"),\n+\t\ttarget: target,\n+\t\tt: t,\n+\t\tinfo: info,\n \t}\n \n \tvar built time.Time\n@@ -162,6 +164,9 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string\n \tfor _, f := range info.TestGoFiles {\n \t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n \t}\n+\tfor _, f := range info.XTestGoFiles {\n+\t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n+\t}\n \n \tsort.Strings(p.gofiles)\n \n```
## コアとなるコードの解説
### `src/cmd/go/list.go` の変更
* **`Package` 構造体への `XTestGoFiles` の追加**:
* この変更は、`go list` コマンドがパッケージ情報を表示する際に、外部テストファイルに関する情報も提供できるようにするためのものです。`go list -json` のようなコマンドでパッケージの詳細情報を取得する際に、`XTestGoFiles` のリストが含まれるようになります。
* コメントの更新は、`GoFiles` が `CgoFiles`, `TestGoFiles`, `XTestGoFiles` を除外すること、`TestGoFiles` がパッケージ内部のテストファイルであること、そして `XTestGoFiles` がパッケージ外部のテストファイルであることを明確にしています。これにより、Goのソースファイルの種類と役割がより正確に定義されます。
### `src/cmd/go/pkg.go` の変更
* **`Package` 構造体への `XTestGoFiles` の追加とJSONタグ**:
* `list.go` と同様に、`Package` 構造体に `XTestGoFiles` が追加されます。`json:",omitempty"` タグは、このフィールドが空の場合にJSON出力から省略されることを意味します。これは、`go list -json` の出力の簡潔さを保つための一般的なGoの慣習です。
* **`gofiles` コメントの更新**:
* `gofiles` フィールドのコメントに `+XTestGoFiles` が追加されたことは、このスライスが `gofmt`、`govet`、`gofix` などのツールが処理すべきすべてのGoソースファイル(通常のGoファイル、Cgoファイル、内部テストファイル、そして今回の変更で追加された外部テストファイル)の絶対パスを含むことを明示しています。これは、ツールの動作を理解する上で非常に重要な情報です。
* **`scanPackage` 関数における `Package` 構造体の初期化**:
* `scanPackage` 関数は、Goのビルドシステムがパッケージのソースファイルをスキャンして情報を収集する中心的な役割を担います。
* `p.XTestGoFiles: info.XTestGoFiles,` の行が追加されたことで、`build.DirInfo` から取得した外部テストファイルのリストが、新しく追加された `Package.XTestGoFiles` フィールドに正確にマッピングされるようになりました。これにより、`go command` は外部テストファイルの存在を認識し、その情報を内部で管理できるようになります。
* **`gofiles` スライスへの `XTestGoFiles` の追加**:
* この部分が、`gofmt`、`govet`、`gofix` が外部テストファイルを処理するようになるための直接的な変更です。
* `for _, f := range info.XTestGoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) }` のループが追加されたことで、`scanPackage` がパッケージのすべてのGoソースファイル(通常のGoファイル、Cgoファイル、内部テストファイル)に加えて、外部テストファイルも `p.gofiles` スライスに含めるようになりました。
* `p.gofiles` は、前述の通り、各種ツールが処理対象とするファイルのリストです。したがって、この変更により、これらのツールは外部テストファイルも自動的に処理するようになり、コード品質のチェックと維持の範囲が拡張されました。
これらの変更は、Goツールチェーンの堅牢性と完全性を高め、開発者がより一貫性のある方法でGoコードベース全体(テストコードを含む)を管理できるようにするために不可欠でした。
## 関連リンク
* **GitHubコミットページ**: [https://github.com/golang/go/commit/2693232f16fb91d715349b81bbb7af3c96c5459e](https://github.com/golang/go/commit/2693232f16fb91d715349b81bbb7af3c96c5459e)
* **Go CL (Change List)**: `https://golang.org/cl/5502102` (このリンクは古いGoのコードレビューシステムのものであり、現在は直接アクセスできない可能性がありますが、コミットメッセージに記載されているため参考として残します。)
* **Go Issue #2649**: コミットメッセージに `Fixes #2649` と記載されていますが、GoのIssueトラッカーがGoogle CodeからGitHubへ移行したため、古いIssue番号は直接参照できない場合があります。このIssueは、外部テストファイルがツールによって処理されないという問題に関連していたと推測されます。
## 参考にした情報源リンク
* Go言語の公式ドキュメント (特にパッケージ、テスト、`go` コマンドに関するセクション)
* `gofmt`, `govet`, `gofix` に関するGoブログやドキュメント
* Go言語のソースコード (特に `cmd/go` パッケージ)
* Go言語のテストに関する一般的な慣習とベストプラクティス
* Go言語のIssueトラッカーの歴史と移行に関する情報 (Google CodeからGitHubへ)