[インデックス 11019] ファイルの概要
このコミットは、Go言語の公式ツールであるgofmt
、govet
、gofix
が、Goパッケージ内のテストファイル(_test.go
で終わるファイル)を適切に処理できるようにするための変更です。具体的には、go
コマンドの内部APIであるPackage
構造体にTestGoFiles
フィールドを追加し、テストファイルがこれらのツールに渡されるファイルリストに含まれるように修正しています。これにより、テストコードも本番コードと同様にフォーマット、静的解析、自動修正の対象となり、コードベース全体の品質と一貫性が向上します。
コミット
- コミットハッシュ:
7ccd505dc47842c94898f5aed72509c54af22576
- 作者: Sanjay Menakuru balasanjay@gmail.com
- コミット日時: 2012年1月3日 火曜日 14:12:54 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7ccd505dc47842c94898f5aed72509c54af22576
元コミット内容
cmd/go: include test files in the files sent to gofmt, govet, and gofix
Also, add TestGoFiles to the go command's public api.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5505083
変更の背景
Go言語では、テストコードは通常、対象となるソースファイルと同じディレクトリに_test.go
という命名規則で配置されます。gofmt
、govet
、gofix
といったツールは、Goコードの品質を維持し、開発プロセスを効率化するために不可欠です。
gofmt
: コードのフォーマットを統一し、可読性を高めます。govet
: 潜在的なバグや疑わしいコード構造を静的に解析して検出します。gofix
: Go言語のバージョンアップに伴うAPI変更などに対して、コードを自動的に修正します。
これらのツールが本番コードだけでなくテストコードにも適用されることは、以下のような点で重要です。
- コードの一貫性: テストコードも本番コードと同じフォーマット規則に従うことで、プロジェクト全体のコードベースの一貫性が保たれます。
- テストの信頼性:
govet
によってテストコード内の潜在的なエラーが検出されれば、テスト自体の信頼性が向上し、誤ったテスト結果を防ぐことができます。 - メンテナンス性:
gofix
によってテストコードも自動的に修正されることで、Go言語のバージョンアップ時などに手動でテストコードを修正する手間が省け、メンテナンスコストが削減されます。
このコミット以前は、go
コマンドがこれらのツールに渡すファイルリストにテストファイルが明示的に含まれていなかった可能性があります。この変更は、go
コマンドの内部でパッケージ情報を扱うPackage
構造体を拡張し、テストファイルを正式な一部として認識させることで、上記ツールの処理対象にテストファイルを含めることを目的としています。また、TestGoFiles
フィールドをgo
コマンドの公開APIに追加することで、他のツールやスクリプトがパッケージのテストファイル情報をプログラム的に取得できるようになります。
なお、コミットメッセージに記載されているhttps://golang.org/cl/5505083
のリンクは、現在の公開されているGoのChange Listとは異なる内容(Go Playgroundへのリンク追加)を示しており、このコミットの直接的なレビューリンクではないようです。これは、当時の内部的なレビューシステムやリンク形式が現在とは異なっていたためと考えられます。
前提知識の解説
Go言語のパッケージとテストファイル
Go言語では、関連するソースファイルは「パッケージ」としてまとめられます。テストファイルは、通常、テスト対象のソースファイルと同じパッケージ内に配置され、ファイル名の末尾に_test.go
が付きます(例: my_package.go
に対するmy_package_test.go
)。これらのファイルは、go test
コマンドによって実行されます。
gofmt
, govet
, gofix
gofmt
: Go言語の標準的なコードフォーマッタです。GoのソースコードをGoコミュニティで推奨されるスタイルに自動的に整形します。govet
: Goのソースコードを静的に解析し、疑わしい構造や潜在的なエラー(例: フォーマット文字列の不一致、到達不能なコード、誤ったタグの使用など)を報告します。gofix
: Go言語の古いバージョンで書かれたコードを、新しいGoのバージョンやAPIの変更に合わせて自動的に修正するツールです。
go
コマンドの内部構造 (Package
構造体とbuild.Context
)
go
コマンドは、Goのソースコードをビルド、テスト、解析する際に、内部的にパッケージの情報を表現するためのデータ構造を使用します。このコミットで変更されているPackage
構造体は、Goのパッケージに関するメタデータ(ファイルリスト、依存関係など)を保持する重要な構造体です。
Package
構造体: Goのパッケージに関する詳細情報(パッケージ名、インポートパス、ソースファイルリスト、Cgo関連ファイルなど)を格納します。build.Context
: Goのビルド環境に関する情報(Goのバージョン、OS、アーキテクチャなど)を提供し、ソースファイルの検索や解析に使用されます。build.DirInfo
:go/build
パッケージがディレクトリをスキャンして得られる情報で、通常のGoファイル、Cgoファイル、テストファイルなどを区別してリストします。
技術的詳細
このコミットの主要な変更点は、go
コマンドがパッケージ情報を扱うPackage
構造体に、テストファイル専用のフィールドTestGoFiles
を追加し、これらのファイルがgofmt
、govet
、gofix
などのツールに渡されるファイルリストに確実に含まれるようにしたことです。
具体的には、以下の2つのファイルが変更されています。
src/cmd/go/list.go
:go list
コマンドがパッケージ情報を表示する際に使用するPackage
構造体の定義とコメントが更新されました。ここでは、TestGoFiles
が新しいフィールドとして追加され、GoFiles
のコメントがTestGoFiles
を含まないことを明確にするように修正されています。src/cmd/go/pkg.go
:go
コマンドがパッケージをスキャンしてPackage
構造体を構築する主要なロジックが含まれています。Package
構造体自体にTestGoFiles
フィールドが追加されました。scanPackage
関数内で、build.DirInfo
から取得したテストファイルリスト(info.TestGoFiles
)を新しく追加されたp.TestGoFiles
フィールドに代入する処理が追加されました。- 最も重要な変更は、
p.gofiles
という内部的なファイルリスト(gofmt
などのツールに渡される実ファイルパスのリスト)に、info.TestGoFiles
の内容が追加されるようになった点です。これにより、テストファイルがこれらのツールの処理対象として認識されるようになります。 p.gofiles
のコメントも更新され、GoFiles
、CgoFiles
に加えてTestGoFiles
も含まれることが明記されました。
これらの変更により、go
コマンドはパッケージのテストファイルを正確に識別し、それらを静的解析やフォーマット、修正ツールの入力として含めることができるようになります。
コアとなるコードの変更箇所
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 21ebb5e200..9a1a4ecb14 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -36,11 +36,12 @@ 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)
- 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 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"
// Dependency information
Imports []string // import paths used by this package
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index f3f79b6a7d..d66f524269 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -29,13 +29,14 @@ type Package struct {
Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
// Source files
-\tGoFiles []string // .go source files (excluding CgoFiles)
-\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 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
// Dependency information
Imports []string `json:",omitempty"` // import paths used by this package
@@ -46,7 +47,7 @@ type Package struct {
\tpkgdir string
\tinfo *build.DirInfo
\timports []*Package
-\tgofiles []string // GoFiles+CgoFiles, absolute paths
+\tgofiles []string // GoFiles+CgoFiles+TestGoFiles files, absolute paths
\ttarget string // installed file for this package (may be executable)
\tfake bool // synthesized package
}\n@@ -126,22 +127,23 @@ 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\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\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@@ -157,7 +159,12 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string\n \tfor _, f := range info.CgoFiles {\n \t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n \t}\n+\tfor _, f := range info.TestGoFiles {\n+\t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n+\t}\n+\n \tsort.Strings(p.gofiles)\n+\n \tsrcss := [][]string{\n \t\tp.GoFiles,\n \t\tp.CFiles,\n```
## コアとなるコードの解説
### `src/cmd/go/list.go` の変更
- **`Package`構造体のコメント更新と`TestGoFiles`フィールドの追加**:
```go
- GoFiles []string // .go source files (excluding CgoFiles)
+ GoFiles []string // .go source files (excluding CgoFiles and TestGoFiles)
+ TestGoFiles []string // _test.go source files
```
`GoFiles`のコメントが「CgoFilesとTestGoFilesを除く.goソースファイル」と明確化されました。そして、`_test.go`ファイル専用の新しいフィールド`TestGoFiles`が追加されました。これは、`go list`コマンドがパッケージ情報を表示する際に、テストファイルを独立したカテゴリとして扱うことを可能にします。
### `src/cmd/go/pkg.go` の変更
- **`Package`構造体への`TestGoFiles`フィールドの追加**:
```go
-\tGoFiles []string // .go source files (excluding CgoFiles)
-\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 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
```
`list.go`と同様に、`Package`構造体自体に`TestGoFiles`スライスが追加されました。`json:",omitempty"`タグは、このフィールドが空の場合にJSON出力から省略されることを意味します。
- **`scanPackage`関数での`TestGoFiles`の初期化**:
```go
-\t\tGoFiles: info.GoFiles,\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\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 ```
`scanPackage`関数は、`build.Context`と`build.Tree`を使用してパッケージの情報をスキャンし、`Package`構造体を構築します。この変更により、`build.DirInfo`から取得した`info.TestGoFiles`が、新しく追加された`p.TestGoFiles`フィールドに直接代入されるようになりました。
- **`p.gofiles`への`TestGoFiles`の追加**:
```go
-\tgofiles []string // GoFiles+CgoFiles, absolute paths
+\tgofiles []string // GoFiles+CgoFiles+TestGoFiles files, absolute paths
```
```go
\tfor _, f := range info.CgoFiles {\n \t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n \t}\n+\tfor _, f := range info.TestGoFiles {\n+\t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n+\t}\n+\n \tsort.Strings(p.gofiles)\n+\n \tsrcss := [][]string{\n \t\tp.GoFiles,\n \t\tp.CFiles,\n ```
`p.gofiles`は、`gofmt`、`govet`、`gofix`などのツールが実際に処理するGoソースファイルの絶対パスのリストです。この変更により、`info.TestGoFiles`に含まれるすべてのテストファイルのパスが`p.gofiles`に追加されるようになりました。これにより、これらのツールがテストファイルを自動的に処理できるようになります。また、`p.gofiles`のコメントも更新され、`TestGoFiles`が含まれることが明示されました。
これらの変更は、Goのビルドシステムとツールチェーンの内部的な整合性を高め、テストコードもファーストクラスの市民として扱われるようにするための重要なステップです。
## 関連リンク
- GitHubコミットページ: [https://github.com/golang/go/commit/7ccd505dc47842c94898f5aed72509c54af22576](https://github.com/golang/go/commit/7ccd505dc47842c94898f5aed72509c54af22576)
## 参考にした情報源リンク
- Go言語の公式ドキュメント(`gofmt`, `govet`などに関する情報)
- Go言語のソースコード(`src/cmd/go/`ディレクトリ内の関連ファイル)
- Web検索(`golang gofmt govet gofix include test files`)
* `golang.org/cl/5505083`のリンクは、このコミットの直接的なレビューリンクではないことを確認しました。