feat(prune): R2 dry-run prune shell + persist wiring, report-only
Add ReaSamplerSession::pruneDryRun enumerating the resolved current bank folder, feeding the R1 core, and returning a PruneReport (count/bytes/list). New pure bankRelativeForName + buildPruneReport keep spelling and tally testable. Register forever-stable BANK_PRUNE_FOLDER action, report-only. No deletion.
This commit is contained in:
@@ -19,8 +19,10 @@
|
||||
#include "../src/prune_reconcile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace reasampler;
|
||||
@@ -259,6 +261,69 @@ static void testReferencedPathsSkipsEmptyPath() {
|
||||
CHECK(refs[0] == "bank/real.wav");
|
||||
}
|
||||
|
||||
// --- buildPruneReport: count / byte-sum / truncation (Phase R, R2) ----------
|
||||
|
||||
static void testReportCountSizeAndFullList() {
|
||||
// A known orphan set with known sizes -> exact count, exact byte sum, full list
|
||||
// (under the cap). Order follows the orphan input order (deterministic).
|
||||
const std::vector<std::string> orphans = {"bank/a.wav", "bank/b.wav", "bank/c.wav"};
|
||||
const std::unordered_map<std::string, std::uint64_t> sizes = {
|
||||
{"bank/a.wav", 100}, {"bank/b.wav", 250}, {"bank/c.wav", 50}};
|
||||
const PruneReport r = buildPruneReport(orphans, sizes, /*displayCap=*/64);
|
||||
CHECK(r.count == 3);
|
||||
CHECK(r.totalBytes == 400); // 100 + 250 + 50 — would fail if sizes mis-summed
|
||||
CHECK(r.orphans.size() == 3);
|
||||
CHECK(!r.truncated);
|
||||
CHECK(r.orphans[0] == "bank/a.wav"); // input order preserved
|
||||
CHECK(r.orphans[1] == "bank/b.wav");
|
||||
CHECK(r.orphans[2] == "bank/c.wav");
|
||||
}
|
||||
|
||||
static void testReportMissingSizeCountsZeroNotDropped() {
|
||||
// An orphan with no stat'd size contributes 0 to the sum but is STILL listed/counted.
|
||||
const std::vector<std::string> orphans = {"bank/a.wav", "bank/nosize.wav"};
|
||||
const std::unordered_map<std::string, std::uint64_t> sizes = {{"bank/a.wav", 10}};
|
||||
const PruneReport r = buildPruneReport(orphans, sizes, 64);
|
||||
CHECK(r.count == 2); // both counted (missing size must not drop the orphan)
|
||||
CHECK(r.totalBytes == 10); // nosize contributes 0
|
||||
CHECK(r.orphans.size() == 2);
|
||||
}
|
||||
|
||||
static void testReportTruncatesListButKeepsExactCountAndSize() {
|
||||
// More orphans than the display cap: list clips to the cap, but count and byte sum
|
||||
// stay EXACT over the whole set, and truncated flags the clip.
|
||||
std::vector<std::string> orphans;
|
||||
std::unordered_map<std::string, std::uint64_t> sizes;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
const std::string p = "bank/f" + std::to_string(i) + ".wav";
|
||||
orphans.push_back(p);
|
||||
sizes[p] = 5; // 10 files * 5 bytes = 50 total
|
||||
}
|
||||
const PruneReport r = buildPruneReport(orphans, sizes, /*displayCap=*/3);
|
||||
CHECK(r.count == 10); // exact, not clipped
|
||||
CHECK(r.totalBytes == 50); // summed over ALL 10, not just the shown 3
|
||||
CHECK(r.orphans.size() == 3); // list clipped to the cap
|
||||
CHECK(r.truncated);
|
||||
CHECK(r.orphans[0] == "bank/f0.wav"); // first-N in input order
|
||||
}
|
||||
|
||||
static void testReportUncappedWhenCapZero() {
|
||||
// displayCap == 0 means "no cap": the whole list is emitted, truncated stays false.
|
||||
const std::vector<std::string> orphans = {"bank/a.wav", "bank/b.wav"};
|
||||
const PruneReport r = buildPruneReport(orphans, /*sizes*/ {}, /*displayCap=*/0);
|
||||
CHECK(r.count == 2);
|
||||
CHECK(r.orphans.size() == 2);
|
||||
CHECK(!r.truncated);
|
||||
}
|
||||
|
||||
static void testReportEmptyOrphanSet() {
|
||||
const PruneReport r = buildPruneReport({}, {}, 64);
|
||||
CHECK(r.count == 0);
|
||||
CHECK(r.totalBytes == 0);
|
||||
CHECK(r.orphans.empty());
|
||||
CHECK(!r.truncated);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testNullTestAllReferenced();
|
||||
testFormulaMixedPopulations();
|
||||
@@ -276,6 +341,11 @@ int main() {
|
||||
testReferencedPathsUnionAcrossBanksAndPool();
|
||||
testReferencedPathsEmptyBook();
|
||||
testReferencedPathsSkipsEmptyPath();
|
||||
testReportCountSizeAndFullList();
|
||||
testReportMissingSizeCountsZeroNotDropped();
|
||||
testReportTruncatesListButKeepsExactCountAndSize();
|
||||
testReportUncappedWhenCapZero();
|
||||
testReportEmptyOrphanSet();
|
||||
|
||||
if (g_fail == 0) std::printf("prune_reconcile_tests: ALL PASS\n");
|
||||
else std::printf("prune_reconcile_tests: %d FAILURE(S)\n", g_fail);
|
||||
|
||||
Reference in New Issue
Block a user