package gitignored import ( "os" "path/filepath" "strings" "testing" ) // The kind of catalogue review F1: a bare pattern hides the command directory it shares a name with; an anchored one // hides the binary alone; a glob that ignores source is refused too; a negation keeps a path. func TestFindNamesEachPatternThatHidesSource(t *testing.T) { for name, c := range map[string]struct { ignore string hidden []string }{ "a bare name hides cmd/": {"nfs-server\n", []string{"modules/m/cmd/nfs-server/", "modules/m/cmd/nfs-server/" + Probe, "modules/m/cmd/nfs-server/main.go"}}, "anchored to the binary": {"/nfs-server\n/cmd/nfs-server/nfs-server\n", nil}, "a glob ignoring Go source": {"*.go\n!main.go\n", []string{"modules/m/cmd/" + Probe, "modules/m/cmd/nfs-server/" + Probe, "modules/m/internal/" + Probe, "modules/m/internal/x/" + Probe, "modules/m/internal/x/x.go"}}, "a pattern over internal/": {"internal/\n", []string{"modules/m/internal/", "modules/m/internal/" + Probe, "modules/m/internal/x/", "modules/m/internal/x/" + Probe, "modules/m/internal/x/x.go"}}, "nothing but generated JavaScript": {"*.js\n", nil}, } { root := t.TempDir() for f, b := range map[string]string{ "modules/m/.gitignore": c.ignore, "modules/m/cmd/nfs-server/main.go": "package main\n", "modules/m/cmd/nfs-server/run.js": "", "modules/m/internal/x/x.go": "package x\n", "modules/m/node_modules/y/index.go": "", "modules/other/cmd/other/other.go": "package main\n", } { p := filepath.Join(root, f) if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(p, []byte(b), 0o644); err != nil { t.Fatal(err) } } dirs, err := Sources(root) if err != nil { t.Fatal(err) } got, err := Find(root, dirs) if err != nil { t.Fatal(err) } var paths []string for _, h := range got { paths = append(paths, h.Path) if !strings.HasPrefix(h.Pattern, "modules/m/.gitignore:") { t.Errorf("%s: %s is named hidden by %q, want the module's .gitignore", name, h.Path, h.Pattern) } } if strings.Join(paths, ",") != strings.Join(c.hidden, ",") { t.Errorf("%s: hidden %v, want %v", name, paths, c.hidden) } } } // The catalogue's own tree: no ignore pattern hides a path under a module's cmd/ or internal/. func TestNoIgnorePatternHidesAModulesSource(t *testing.T) { root := filepath.Join("..", "..") dirs, err := Sources(root) if err != nil { t.Fatal(err) } if len(dirs) == 0 { t.Fatal("no module source directory found: the check reads nothing") } hidden, err := Find(root, dirs) if err != nil { t.Fatal(err) } for _, h := range hidden { t.Errorf("%s is hidden by %s: a module's source is never ignored; anchor the pattern to the binary (/, /cmd//)", h.Path, h.Pattern) } } // The kind of issue 1577, met twice in one milestone: a module's program `go build` left in its folder was committed. // Every program a main package builds, in its own directory and at its module's root, is found, and one no ignore file // names is open; a directory of the same name is no program's place. func TestEveryProgramABuildLeavesIsIgnored(t *testing.T) { files := map[string]string{ "modules/m/go.mod": "module example.com/m/v2\n", "modules/m/main.go": "// a program\npackage main\n", "modules/m/cmd/m-tools/main.go": "package main\n", "modules/m/cmd/m-tools/main_test.go": "package main\n", "modules/m/internal/x/x.go": "package x\n", "modules/m/internal/x/x_test.go": "package main\n", "modules/m/node_modules/y/y.go": "package main\n", "modules/m/tool/go.mod": "module tool\n", "modules/m/tool/cmd/tool/main.go": "package main\n", "modules/m/tool/tool/source.go": "package tool\n", "checks/c/go.mod": "module example.com/checks/c\n", "checks/c/main.go": "package main\n", } for name, c := range map[string]struct { ignore string open []string }{ "nothing ignored": {"", []string{"checks/c/c", "modules/m/cmd/m-tools/m-tools", "modules/m/m", "modules/m/m-tools", "modules/m/tool/cmd/tool/tool"}}, "each anchored": {"/checks/c/c\n/modules/m/cmd/m-tools/m-tools\n/modules/m/m\n/modules/m/m-tools\n/modules/m/tool/cmd/tool/tool\n", nil}, "one kept by a negation": {"/checks/c/c\n/modules/m/*\n!/modules/m/m\n/modules/m/cmd/m-tools/m-tools\n/modules/m/tool/cmd/tool/tool\n", []string{"modules/m/m"}}, } { root := t.TempDir() for f, b := range files { p := filepath.Join(root, f) if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(p, []byte(b), 0o644); err != nil { t.Fatal(err) } } if err := os.WriteFile(filepath.Join(root, ".gitignore"), []byte(c.ignore), 0o644); err != nil { t.Fatal(err) } programs, err := Programs(root) if err != nil { t.Fatal(err) } want := "checks/c/c,modules/m/cmd/m-tools/m-tools,modules/m/m,modules/m/m-tools,modules/m/tool/cmd/tool/tool" if got := strings.Join(programs, ","); got != want { t.Fatalf("%s: programs %s, want %s", name, got, want) } open, err := Unignored(root, programs) if err != nil { t.Fatal(err) } if strings.Join(open, ",") != strings.Join(c.open, ",") { t.Errorf("%s: open %v, want %v", name, open, c.open) } } } // The catalogue's own tree: every program a local build leaves is ignored, so `git add -A` never takes one. func TestNoProgramABuildLeavesIsCommitted(t *testing.T) { root := filepath.Join("..", "..") programs, err := Programs(root) if err != nil { t.Fatal(err) } if len(programs) == 0 { t.Fatal("no main package found: the check reads nothing") } open, err := Unignored(root, programs) if err != nil { t.Fatal(err) } for _, p := range open { t.Errorf("%s is the program a local go build leaves, and no ignore file names it: add /%s to the catalogue's .gitignore", p, p) } }