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