Add interactive snapshot comparison, CLI tests and treegen
--compare opens the TUI with growth/shrink colouring unless a report is requested. treegen builds deterministic synthetic trees (Unicode, hostile names, sparse files). MAPSIZE_DEBUG_PANIC verifies terminal restoration on UI and scanner panics.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/andydixon/mapsize/internal/scan"
|
||||
)
|
||||
|
||||
func fixture(t *testing.T) string {
|
||||
root := t.TempDir()
|
||||
os.MkdirAll(filepath.Join(root, "big"), 0o755)
|
||||
os.MkdirAll(filepath.Join(root, "small"), 0o755)
|
||||
os.WriteFile(filepath.Join(root, "big", "a.iso"), make([]byte, 300_000), 0o644)
|
||||
os.WriteFile(filepath.Join(root, "big", "b.iso"), make([]byte, 300_000), 0o644)
|
||||
os.WriteFile(filepath.Join(root, "small", "c.txt"), []byte("hello"), 0o644)
|
||||
return root
|
||||
}
|
||||
|
||||
func run(t *testing.T, cfg *Config) string {
|
||||
t.Helper()
|
||||
var out bytes.Buffer
|
||||
cfg.Stdout, cfg.Stderr = &out, &bytes.Buffer{}
|
||||
cfg.ScanOpts = scan.Options{Workers: 2}
|
||||
if err := Run(cfg); err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func TestTopAndLargest(t *testing.T) {
|
||||
root := fixture(t)
|
||||
out := run(t, &Config{Paths: []string{root}, Top: 5, LargestFiles: 2})
|
||||
if !strings.Contains(out, filepath.Join(root, "big")) || !strings.Contains(out, "a.iso") {
|
||||
t.Fatalf("report missing entries:\n%s", out)
|
||||
}
|
||||
if strings.Index(out, filepath.Join(root, "big")) > strings.Index(out, filepath.Join(root, "small")) {
|
||||
t.Fatalf("not sorted by size:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONIsValid(t *testing.T) {
|
||||
root := fixture(t)
|
||||
out := run(t, &Config{Paths: []string{root}, JSON: true, Depth: -1})
|
||||
var v struct {
|
||||
Root string
|
||||
Complete bool
|
||||
Tree struct {
|
||||
Size int64
|
||||
Children []struct{ Name string }
|
||||
}
|
||||
}
|
||||
if err := json.Unmarshal([]byte(out), &v); err != nil {
|
||||
t.Fatalf("invalid JSON: %v\n%s", err, out)
|
||||
}
|
||||
if !v.Complete || v.Tree.Size < 600_005 || len(v.Tree.Children) != 2 {
|
||||
t.Fatalf("unexpected JSON: %+v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSV(t *testing.T) {
|
||||
root := fixture(t)
|
||||
out := run(t, &Config{Paths: []string{root}, CSV: true, Depth: -1})
|
||||
rows, err := csv.NewReader(strings.NewReader(out)).ReadAll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rows) != 1+1+2+3 { // header, root, 2 dirs, 3 files
|
||||
t.Fatalf("%d rows:\n%s", len(rows), out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotSaveLoadCompare(t *testing.T) {
|
||||
root := fixture(t)
|
||||
dir := t.TempDir()
|
||||
a, b := filepath.Join(dir, "a.msz"), filepath.Join(dir, "b.msz")
|
||||
run(t, &Config{Paths: []string{root}, NoUI: true, Save: a})
|
||||
os.WriteFile(filepath.Join(root, "small", "grown.bin"), make([]byte, 500_000), 0o644)
|
||||
os.Remove(filepath.Join(root, "big", "b.iso"))
|
||||
run(t, &Config{Paths: []string{root}, NoUI: true, Save: b})
|
||||
|
||||
out := run(t, &Config{Load: a, Top: 3})
|
||||
if !strings.Contains(out, "big") {
|
||||
t.Fatalf("loaded snapshot report:\n%s", out)
|
||||
}
|
||||
out = run(t, &Config{Compare: [2]string{a, b}, NoUI: true})
|
||||
for _, want := range []string{"new", "grown.bin", "removed", "b.iso"} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Fatalf("compare output missing %q:\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDuplicatesReport(t *testing.T) {
|
||||
root := fixture(t)
|
||||
out := run(t, &Config{Paths: []string{root}, Duplicates: true})
|
||||
if !strings.Contains(out, "1 verified duplicate groups") {
|
||||
t.Fatalf("duplicates:\n%s", out)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user