walk_test.go raw
1 package walk
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "git.mleku.dev/mleku/dendrite/pkg/ratio"
9 )
10
11 func setupTestRepo(t *testing.T) string {
12 t.Helper()
13 dir := t.TempDir()
14
15 // Create a mini repo structure.
16 dirs := []string{
17 "pkg/a",
18 "pkg/b",
19 "cmd/main",
20 "_output",
21 "node_modules",
22 }
23 for _, d := range dirs {
24 os.MkdirAll(filepath.Join(dir, d), 0o755)
25 }
26
27 // Create source files.
28 files := map[string]string{
29 "main.go": "package main\n",
30 "pkg/a/a.go": "package a\n",
31 "pkg/a/a_test.go": "package a\n",
32 "pkg/b/b.go": "package b\n",
33 "cmd/main/main.go": "package main\n",
34 "README.md": "# readme\n",
35 "_output/gen.go": "package gen\n",
36 "node_modules/x.js": "export default {};\n",
37 }
38 for name, content := range files {
39 os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644)
40 }
41
42 return dir
43 }
44
45 func TestBuildManifest(t *testing.T) {
46 dir := setupTestRepo(t)
47 m, err := Build(dir, 42, DefaultExclude)
48 if err != nil {
49 t.Fatalf("Build: %v", err)
50 }
51
52 // Should include everything: main.go, pkg/a/a.go, pkg/a/a_test.go, pkg/b/b.go,
53 // cmd/main/main.go, README.md, _output/gen.go
54 // Should exclude only: node_modules/x.js (excluded directory)
55 if len(m.Files) != 7 {
56 t.Fatalf("expected 7 files, got %d: %v", len(m.Files), m.Files)
57 }
58
59 for _, f := range m.Files {
60 if f == filepath.Join("node_modules", "x.js") {
61 t.Fatalf("unexpected file in manifest: %s", f)
62 }
63 }
64 }
65
66 func TestDeterministicPermutation(t *testing.T) {
67 dir := setupTestRepo(t)
68 m1, _ := Build(dir, 42, DefaultExclude)
69 m2, _ := Build(dir, 42, DefaultExclude)
70
71 if len(m1.Files) != len(m2.Files) {
72 t.Fatalf("different file counts: %d vs %d", len(m1.Files), len(m2.Files))
73 }
74 for i := range m1.Files {
75 if m1.Files[i] != m2.Files[i] {
76 t.Fatalf("order differs at %d: %s vs %s", i, m1.Files[i], m2.Files[i])
77 }
78 }
79 }
80
81 func TestDifferentSeedDifferentOrder(t *testing.T) {
82 dir := setupTestRepo(t)
83 m1, _ := Build(dir, 42, DefaultExclude)
84 m2, _ := Build(dir, 99, DefaultExclude)
85
86 if len(m1.Files) != len(m2.Files) {
87 t.Fatalf("different file counts")
88 }
89
90 same := true
91 for i := range m1.Files {
92 if m1.Files[i] != m2.Files[i] {
93 same = false
94 break
95 }
96 }
97 if same {
98 t.Fatal("different seeds produced identical order")
99 }
100 }
101
102 func TestErgodicCoverage(t *testing.T) {
103 dir := setupTestRepo(t)
104 m, _ := Build(dir, 42, DefaultExclude)
105 w := NewWalker(m)
106
107 visited := make(map[string]bool)
108 for {
109 path, ok := w.Next()
110 if !ok {
111 break
112 }
113 if visited[path] {
114 t.Fatalf("file visited twice: %s", path)
115 }
116 visited[path] = true
117 }
118
119 if len(visited) != len(m.Files) {
120 t.Fatalf("visited %d files, expected %d", len(visited), len(m.Files))
121 }
122 if !w.Done() {
123 t.Fatal("walker should be done")
124 }
125 }
126
127 func TestResume(t *testing.T) {
128 dir := setupTestRepo(t)
129 m, _ := Build(dir, 42, DefaultExclude)
130
131 w1 := NewWalker(m)
132 w1.Next()
133 w1.Next()
134
135 w2 := Resume(m, w1.Position)
136 if w2.Remaining() != w1.Remaining() {
137 t.Fatalf("remaining mismatch: %d vs %d", w2.Remaining(), w1.Remaining())
138 }
139
140 path1, ok1 := w1.Next()
141 path2, ok2 := w2.Next()
142 if ok1 != ok2 || path1 != path2 {
143 t.Fatalf("resumed walker diverged: %s/%v vs %s/%v", path1, ok1, path2, ok2)
144 }
145 }
146
147 func TestProgress(t *testing.T) {
148 dir := setupTestRepo(t)
149 m, _ := Build(dir, 42, DefaultExclude)
150 w := NewWalker(m)
151
152 if !w.Progress().IsZero() {
153 t.Fatalf("progress should be 0 at start, got %s", w.Progress())
154 }
155
156 for !w.Done() {
157 w.Next()
158 }
159
160 if !w.Progress().Equal(ratio.One) {
161 t.Fatalf("progress should be 1 at end, got %s", w.Progress())
162 }
163 }
164