diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-13 02:31:32 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-13 02:31:32 +0200 |
| commit | 26c94eb3db62ec6eebbf8d22c11afe691d9520c4 (patch) | |
| tree | 165e5bf69234b4f96c9b74deb4898d7143ddf120 /internal/engine/plan_bench_test.go | |
| parent | a6e442a645902011b2081c216daaec052cdc6ce6 (diff) | |
| download | krino-0.0.1.tar.gz krino-0.0.1.zip | |
krino: release 0.0.1 — man pages, install, examples, cross and release, README, changelogv0.0.1
Also: undo removes the directories its run created; a hardlink is never a
duplicate of its own other name; a flag written before "undo" is honoured;
--version prints no leading v. Duplicate conditions with different scopes
not sharing an original is documented as a known limitation.
Diffstat (limited to 'internal/engine/plan_bench_test.go')
| -rw-r--r-- | internal/engine/plan_bench_test.go | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/engine/plan_bench_test.go b/internal/engine/plan_bench_test.go new file mode 100644 index 0000000..a3d08bb --- /dev/null +++ b/internal/engine/plan_bench_test.go @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package engine + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "krino/internal/plan" +) + +// BenchmarkPlan measures krino's own cost of planning: walking a tree, +// matching rules and building action chains (Engine.Plan, which wraps +// Engine.Match and plan.Build). It does NOT measure krino's real-world +// throughput - spec §13 is explicit that there is no performance target +// for 0.0.1, because a full run's wall time is dominated by the external +// extractors (pdftotext and friends), not by krino itself. This benchmark +// therefore uses a config with no (content ...) test, so no extractor +// ever runs and the result is identical on any machine, with or without +// poppler installed. +// +// The tree is built once in b.TempDir(), before the timer starts; each +// iteration re-plans the same on-disk tree with a fresh plan.Claims, so +// iterations are independent and repeatable. +func BenchmarkPlan(b *testing.B) { + root := b.TempDir() + scanRoot := filepath.Join(root, "Filed") + buildBenchTree(b, scanRoot) + + mainFile := filepath.Join(root, "krino.conf") + if err := os.WriteFile(mainFile, []byte(`(include "dl")`), 0o644); err != nil { + b.Fatal(err) + } + dirsDir := filepath.Join(root, "dirs") + if err := os.MkdirAll(dirsDir, 0o755); err != nil { + b.Fatal(err) + } + // Type-only rules (no content test), one per group present in the + // generated tree, mirroring examples/by-type.conf; "dat" files match + // none of them and take the unmatched path through Match. + dirConf := fmt.Sprintf(` +(path %q) +(recursive yes) +(min-age 0s) +(rule "images" (when (type image)) (move "Sorted/Images") (stop)) +(rule "documents" (when (type document)) (move "Sorted/Documents") (stop)) +(rule "spreadsheets" (when (type spreadsheet)) (move "Sorted/Spreadsheets") (stop)) +(rule "archives" (when (type archive)) (move "Sorted/Archives") (stop)) +(rule "media" (when (or (type audio) (type video))) (move "Sorted/Media") (stop)) +`, scanRoot) + if err := os.WriteFile(filepath.Join(dirsDir, "dl.conf"), []byte(dirConf), 0o644); err != nil { + b.Fatal(err) + } + + e, errs := Load(mainFile, "dl") + if len(errs) > 0 { + b.Fatalf("config errors: %v", errs) + } + + ctx := context.Background() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := e.Plan(ctx, e.Dirs[0], plan.NewClaims()); err != nil { + b.Fatal(err) + } + } +} + +// benchTreeDirs * benchFilesPerDir files are generated, spread over +// nested directories so the walk itself is exercised, not just a single +// flat directory read. +const ( + benchTreeDirs = 12 + benchFilesPerDir = 150 +) + +// buildBenchTree creates a synthetic tree under root for BenchmarkPlan: +// nested "Sub" directories holding files that cycle through extensions +// spanning several of Appendix A's type groups, plus one extension +// ("dat") that matches no rule. Names are neutral (Sub, a<N>.<ext>) - +// never anything from a real folder, per the leak-check patterns. +func buildBenchTree(b *testing.B, root string) { + b.Helper() + exts := []string{"pdf", "jpg", "xlsx", "zip", "mp3", "dat"} + old := time.Now().Add(-time.Hour) + n := 0 + for d := 0; d < benchTreeDirs; d++ { + dir := filepath.Join(root, fmt.Sprintf("Sub%d", d), "Nested") + if err := os.MkdirAll(dir, 0o755); err != nil { + b.Fatal(err) + } + for f := 0; f < benchFilesPerDir; f++ { + ext := exts[n%len(exts)] + p := filepath.Join(dir, fmt.Sprintf("a%04d.%s", n, ext)) + if err := os.WriteFile(p, []byte("x"), 0o644); err != nil { + b.Fatal(err) + } + if err := os.Chtimes(p, old, old); err != nil { + b.Fatal(err) + } + n++ + } + } +} |
