//go:build integration package builder import ( "os" "os/exec" "path/filepath" "strings" "testing" ) func TestAGoProgramsBuildSourceIsItsImportClosure(t *testing.T) { root := aGoTree(t, aProgram) got, err := GoBuildSource(root, "cmd/prog") if err != nil { t.Fatal(err) } for _, c := range []struct { file string held bool why string }{ {"cmd/prog/main.go", true, "the program itself"}, {"cmd/prog/helper.go", true, "a file added to the program's package"}, {"lib/lib.go", true, "a package it imports"}, {"emb/emb_amd64.s", true, "assembly beside a package's Go"}, {"lib/lib_plan9.go", true, "a file one system builds"}, {"plan9only/p.go", true, "what a file one system builds imports"}, {"emb/static/index.html", true, "an embedded file"}, {"emb/static/deep/style.css", true, "an embedded file below the pattern's directory"}, {"emb/a b.txt", true, "an embed named outright, quoted"}, {"vendor/example.org/dep/dep.go", true, "a vendored package it imports"}, {"vendor/example.org/dep/sub/sub.go", true, "what a vendored package imports"}, {"vendor/example.net/local/pkg/p.go", true, "a replaced module, as vendored"}, {"third_party/local/pkg/p.go", true, "a replaced module, at its directory"}, {"third_party/local/go.mod", true, "a replaced module's requirements"}, {"go.mod", true, "the module's requirements"}, {"go.sum", true, "the module's sums"}, {"vendor/modules.txt", true, "the vendored modules"}, {"go.work", true, "a workspace, were one made"}, {"lib/lib_test.go", false, "a test is not built"}, {"testonly/t.go", false, "a package only a test imports"}, {"lib/sub/sub.go", false, "a package below an imported one, not imported"}, {"unrelated/u.go", false, "a package nothing imports"}, {"vendor/example.org/other/other.go", false, "a vendored package nothing imports"}, {"README.md", false, "a file no build reads"}, } { if SourceHolds(got, c.file) != c.held { t.Errorf("%s (%s): held %v, wanted %v\n %v", c.file, c.why, !c.held, c.held, got) } } // **Never narrower than go list -deps**: every package go names, and every file it compiles or embeds, // is held. Where no go command is at hand, said, not passed. goCmd, err := exec.LookPath("go") if err != nil { t.Skip("NOT COMPARED: no go command to list the closure with") } list := exec.Command(goCmd, "list", "-deps", "-f", `{{if not .Standard}}{{$d := .Dir}}{{range .GoFiles}}{{$d}}/{{.}} {{end}}{{range .SFiles}}{{$d}}/{{.}} {{end}}{{range .EmbedFiles}}{{$d}}/{{.}} {{end}}{{end}}`, "./cmd/prog") list.Dir = root list.Env = append(os.Environ(), "GOFLAGS=-mod=vendor", "GOPROXY=off", "GOWORK=off", "GOOS=linux", "GOARCH=amd64") out, err := list.CombinedOutput() if err != nil { t.Fatalf("go list: %v\n%s", err, out) } real, _ := filepath.EvalSymlinks(root) for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { if line == "" { continue } rel, err := filepath.Rel(real, line) if err != nil || strings.HasPrefix(rel, "..") { rel, _ = filepath.Rel(root, line) } if !SourceHolds(got, filepath.ToSlash(rel)) { t.Errorf("go list builds %s, and the build source does not hold it", rel) } } }