114 lines
3.4 KiB
Go
114 lines
3.4 KiB
Go
package builtin
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGrepGlobalExcludesFile(t *testing.T) {
|
|
dir := mkRepo(t)
|
|
writeFileT(t, filepath.Join(dir, "keep.txt"), "NEEDLE\n")
|
|
writeFileT(t, filepath.Join(dir, "notes.tmp"), "NEEDLE\n")
|
|
|
|
cfgDir := t.TempDir()
|
|
excludes := filepath.Join(cfgDir, "global_ignore")
|
|
writeFileT(t, excludes, "*.tmp\n")
|
|
gitconfig := filepath.Join(cfgDir, "gitconfig")
|
|
writeFileT(t, gitconfig, "[core]\n\texcludesFile = "+filepath.ToSlash(excludes)+"\n")
|
|
t.Setenv("GIT_CONFIG_GLOBAL", gitconfig)
|
|
|
|
out := runTool(t, grepTool{}, map[string]any{"pattern": "NEEDLE", "path": dir})
|
|
if !strings.Contains(out, "keep.txt") {
|
|
t.Fatalf("kept file must be found: %q", out)
|
|
}
|
|
if strings.Contains(out, "notes.tmp") {
|
|
t.Fatalf("a file matched by core.excludesFile must be skipped: %q", out)
|
|
}
|
|
}
|
|
|
|
// TestGrepWorkspaceE2E drives grep through the real per-workspace tool assembly
|
|
// (builtin.Workspace, what the desktop and ACP frontends use) against a repo that
|
|
// exercises every ignore rule at once: root + nested + /anchored .gitignore,
|
|
// nested negation, hidden dirs, and vendor dirs.
|
|
func TestGrepWorkspaceE2E(t *testing.T) {
|
|
// Neutralize any machine-global git ignore so the fixture is hermetic.
|
|
t.Setenv("GIT_CONFIG_GLOBAL", filepath.Join(t.TempDir(), "none"))
|
|
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
|
|
|
repo := mkRepo(t)
|
|
writeFileT(t, filepath.Join(repo, ".gitignore"), "*.log\nbuild/\n/dist/\n")
|
|
writeFileT(t, filepath.Join(repo, "src", ".gitignore"), "generated.go\n")
|
|
writeFileT(t, filepath.Join(repo, "pkg", ".gitignore"), "*.log\n!keep.log\n")
|
|
|
|
keep := map[string]string{
|
|
"src/app.go": "NEEDLE app",
|
|
"pkg/keep.log": "NEEDLE re-included",
|
|
"README.md": "NEEDLE readme",
|
|
}
|
|
drop := map[string]string{
|
|
"src/generated.go": "NEEDLE nested-ignored",
|
|
"app.log": "NEEDLE root-log",
|
|
"build/out.txt": "NEEDLE ignored-dir",
|
|
"dist/bundle.js": "NEEDLE anchored-dir",
|
|
"pkg/drop.log": "NEEDLE nested-log",
|
|
".github/ci.yml": "NEEDLE hidden",
|
|
"node_modules/x/index.js": "NEEDLE vendor",
|
|
}
|
|
for rel, body := range keep {
|
|
writeFileT(t, filepath.Join(repo, filepath.FromSlash(rel)), body+"\n")
|
|
}
|
|
for rel, body := range drop {
|
|
writeFileT(t, filepath.Join(repo, filepath.FromSlash(rel)), body+"\n")
|
|
}
|
|
|
|
grep := byName(Workspace{Dir: repo}.Tools())["grep"]
|
|
out, err := grep.Execute(context.Background(), argsJSON(t, map[string]any{"pattern": "NEEDLE", "path": "."}))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
found := matchedRelFiles(repo, out)
|
|
for rel := range keep {
|
|
if !found[rel] {
|
|
t.Errorf("expected %s in results, got %v", rel, sortedKeys(found))
|
|
}
|
|
}
|
|
for rel := range drop {
|
|
if found[rel] {
|
|
t.Errorf("did not expect %s in results (got %v)", rel, sortedKeys(found))
|
|
}
|
|
}
|
|
}
|
|
|
|
// matchedRelFiles parses grep's "path:line:text" output into the set of matched
|
|
// files, relative to repo with forward slashes.
|
|
func matchedRelFiles(repo, out string) map[string]bool {
|
|
found := map[string]bool{}
|
|
for _, line := range strings.Split(out, "\n") {
|
|
i := strings.LastIndex(line, ":")
|
|
if i < 0 {
|
|
continue
|
|
}
|
|
j := strings.LastIndex(line[:i], ":")
|
|
if j < 0 {
|
|
continue
|
|
}
|
|
if rel, err := filepath.Rel(repo, line[:j]); err == nil {
|
|
found[filepath.ToSlash(rel)] = true
|
|
}
|
|
}
|
|
return found
|
|
}
|
|
|
|
func sortedKeys(m map[string]bool) []string {
|
|
var ks []string
|
|
for k := range m {
|
|
ks = append(ks, k)
|
|
}
|
|
sort.Strings(ks)
|
|
return ks
|
|
}
|