Q-W1 code-review follow-ups: restore 104 trailing newlines, relocate reasampler_uid.h to core/wire, drop ext_keys namespaces shim, add slot_map_tests, golden serialize literals for 4 modules, namespaces.h/pragma-once ordering sweep. 60/60 green.

This commit is contained in:
2026-07-28 21:33:51 -04:00
parent 847936f813
commit 2d59bbe35d
131 changed files with 460 additions and 263 deletions
+33 -129
View File
@@ -42,6 +42,29 @@ static Sample sampleWith(const std::string& seed) { return sampleWith(seed, "has
// ---------------------------------------------------------------------------
// Golden byte-literal (Q-W1 follow-up): pins the EXACT serialized bytes for a
// small fixture (a freshly-seeded book: pool only, one sample), not just
// self-consistent re-serialization — a format drift that both writer and
// reader agree on would slip past the round-trip tests but not this. The
// format is frozen as-shipped; the literal below is the captured current
// output.
static void testSerializeGoldenLiteral() {
BankBook book;
CHECK(book.pool().index.add(sampleWith("g1")) == AddResult::Added);
CHECK(book.serialize() ==
"{\"version\":1,\"activeBank\":\"pool\",\"banks\":[{\"id\":\"pool\","
"\"displayName\":\"Pool\",\"ordinal\":0,\"index\":{\"version\":1,"
"\"samples\":[{\"id\":\"id-g1\",\"displayName\":\"sample g1\","
"\"relativePath\":\"bank/g1.wav\",\"sourceMode\":0,\"sourceRange\":{"
"\"startSeconds\":0,\"endSeconds\":0,\"startPpq\":0,\"endPpq\":0},"
"\"trackGuids\":[],\"wetDry\":1,\"channelCount\":2,\"sampleRate\":48000,"
"\"lengthSeconds\":0,\"lengthBeats\":0,\"captureTempo\":0,"
"\"captureTimeSigNum\":0,\"captureTimeSigDenom\":0,\"key\":null,"
"\"rootNote\":null,\"loop\":null,\"levels\":{\"peakDb\":0,\"rmsDb\":0,"
"\"lufs\":0},\"clipped\":false,\"tier\":0,\"contentHash\":\"hash-g1\","
"\"provenance\":null,\"createdTimestamp\":1753080000}]},\"slots\":[]}]}");
}
static void testPoolSeededAndDefaults() {
BankBook book;
// Pool present as bank-zero with fixed id + name + ordinal 0.
@@ -790,123 +813,13 @@ static void testUpdateSampleInPlace() {
// ===========================================================================
// L7 — SlotMap (gap-preserving display positions) + BankBook ordering/reorder/replace
// ===========================================================================
// --- SlotMap unit behaviour --------------------------------------------------
static void testSlotMapDenseAppend() {
SlotMap m;
m.append("a");
m.append("b");
m.append("c");
CHECK(m.slotOf("a") == 0);
CHECK(m.slotOf("b") == 1);
CHECK(m.slotOf("c") == 2);
CHECK(m.maxSlot() == 2);
CHECK((m.orderedIds() == std::vector<std::string>{"a", "b", "c"}));
CHECK(m.idAt(1) == "b");
CHECK(m.slotOf("nope") == -1);
}
static void testSlotMapRemoveLeavesGap() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
CHECK(m.remove("b")); // slot 1 now EMPTY (no re-pack)
CHECK(m.slotOf("a") == 0);
CHECK(m.slotOf("c") == 2); // c did NOT shift down
CHECK(m.idAt(1).empty()); // gap preserved
CHECK((m.orderedIds() == std::vector<std::string>{"a", "c"}));
CHECK(!m.remove("b")); // already gone
}
static void testSlotMapAppendAfterGapGoesToFrontier() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.remove("a"); // slot 0 empty
m.append("d"); // append goes AFTER last occupied (2) -> 3
CHECK(m.slotOf("d") == 3); // did NOT fill the slot-0 gap
CHECK(m.idAt(0).empty());
}
static void testSlotMapReorderIntoEmpty() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.remove("b"); // slot 1 empty
CHECK(m.reorder("c", 1)); // c -> empty slot 1; its slot 2 empties
CHECK(m.slotOf("c") == 1);
CHECK(m.idAt(2).empty());
CHECK(m.slotOf("a") == 0); // untouched
}
static void testSlotMapReorderOntoOccupiedInsertsAndShifts() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); m.append("d"); // 0,1,2,3
CHECK(m.reorder("d", 1)); // d onto occupied slot 1 -> insert-before, shift b,c up
CHECK(m.slotOf("a") == 0); // before the target: unchanged
CHECK(m.slotOf("d") == 1); // took the target slot
CHECK(m.slotOf("b") == 2); // shifted +1
CHECK(m.slotOf("c") == 3); // shifted +1
CHECK((m.orderedIds() == std::vector<std::string>{"a", "d", "b", "c"}));
}
static void testSlotMapReorderPreservesInteriorGapAboveTarget() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.remove("b"); // gap at 1: a@0, c@2
m.append("d"); // d@3
CHECK(m.reorder("d", 0)); // d onto occupied slot 0 -> a shifts to 1, c shifts to 3
CHECK(m.slotOf("d") == 0);
CHECK(m.slotOf("a") == 1); // shifted from 0 -> 1
CHECK(m.slotOf("c") == 3); // shifted from 2 -> 3 (gap at 2 preserved as a +1 of its own)
CHECK(m.idAt(2).empty()); // interior gap above the target survives
}
static void testSlotMapReorderUnmappedIsNoOp() {
SlotMap m;
m.append("a");
CHECK(!m.reorder("ghost", 0)); // not mapped -> false, no mutation
CHECK(m.slotOf("a") == 0);
}
static void testSlotMapNegativeTargetClampsToZero() {
SlotMap m;
m.append("a"); m.append("b"); // 0,1
CHECK(m.reorder("b", -3)); // clamp to 0 -> insert-before a
CHECK(m.slotOf("b") == 0);
CHECK(m.slotOf("a") == 1);
}
static void testSlotMapResetDenseSkipsDupesAndEmpties() {
SlotMap m;
m.resetDense({"a", "", "b", "a", "c"}); // "" and the second "a" dropped
CHECK((m.orderedIds() == std::vector<std::string>{"a", "b", "c"}));
CHECK(m.slotOf("a") == 0);
CHECK(m.slotOf("c") == 2);
}
static void testSlotMapReconcileDropsStaleAppendsNew() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.reconcile({"a", "c", "d"}); // b left the index (drop), d is new (append)
CHECK(m.slotOf("a") == 0); // kept at its slot
CHECK(m.slotOf("c") == 2); // kept at its slot (gap where b was)
CHECK(m.slotOf("b") == -1); // stale marker dropped
CHECK(m.slotOf("d") == 3); // appended after the frontier
CHECK(m.idAt(1).empty()); // b's slot stays empty
}
static void testSlotMapEqualityAndFromEntries() {
SlotMap a;
a.append("x"); a.append("y");
SlotMap b = SlotMap::fromEntries({{"x", 0}, {"y", 1}});
CHECK(a == b);
// Defensive repair: duplicate id (first wins), slot conflict (later dropped),
// empty id / negative slot dropped.
SlotMap c = SlotMap::fromEntries({{"x", 0}, {"x", 5}, {"y", 0}, {"", 9}, {"z", -1}, {"w", 2}});
CHECK(c.slotOf("x") == 0); // first x wins
CHECK(c.slotOf("y") == -1); // slot 0 already taken -> dropped
CHECK(c.slotOf("w") == 2); // valid
CHECK(c.slotOf("z") == -1); // negative slot dropped
}
//
// Pure SlotMap-only unit behaviour (add/remove/query, reorder gap-preservation,
// resetDense/reconcile, equality/fromEntries, serialize golden literal + round
// trip) now lives in test_slot_map.cpp (Q-W1 follow-up), extracted per the house
// every-pure-module-has-a-_tests rule. This file keeps the BankBook-level
// integration coverage below: reorderSample / reconcileSlots / JSON round-trip
// WITH a full book.
// --- BankBook L7: JSON round-trip WITH positions -----------------------------
@@ -1081,6 +994,7 @@ static void testReplaceSampleInPoolPassesGuard() {
}
int main() {
testSerializeGoldenLiteral();
testPoolSeededAndDefaults();
testPoolPrivileges();
testCreateRenameReorder();
@@ -1117,18 +1031,8 @@ int main() {
testRemoveAllBanksLatentScope();
testUpdateSampleInPlace();
// L7 — SlotMap + ordering/reorder/replace + slot round-trip/migration.
testSlotMapDenseAppend();
testSlotMapRemoveLeavesGap();
testSlotMapAppendAfterGapGoesToFrontier();
testSlotMapReorderIntoEmpty();
testSlotMapReorderOntoOccupiedInsertsAndShifts();
testSlotMapReorderPreservesInteriorGapAboveTarget();
testSlotMapReorderUnmappedIsNoOp();
testSlotMapNegativeTargetClampsToZero();
testSlotMapResetDenseSkipsDupesAndEmpties();
testSlotMapReconcileDropsStaleAppendsNew();
testSlotMapEqualityAndFromEntries();
// L7 — BankBook ordering/reorder/replace + slot round-trip/migration.
// (Pure SlotMap-only unit behaviour lives in slot_map_tests.)
testBankBookSlotsRoundTrip();
testMigrationDefaultsToInsertionOrderDense();
testOrderedSampleIdsReconcilesLazily();
+25
View File
@@ -104,6 +104,30 @@ static void testFullFieldRoundTrip() {
}
}
// Golden byte-literal (Q-W1 T?-05 follow-up): pins the EXACT serialized bytes for
// a small fixture, not just self-consistent re-serialization — a format drift
// that round-trips losslessly (e.g. a renamed key both writer and reader agree
// on) would slip past testFullFieldRoundTrip but not this. The format is frozen
// as-shipped; the literal below is the captured current output.
static void testSerializeGoldenLiteral() {
BankModel idx;
Sample s;
s.id = "g1";
s.relativePath = "bank/g1.wav";
s.contentHash = "hash-g1";
CHECK(idx.add(s) == AddResult::Added);
CHECK(idx.serialize() ==
"{\"version\":1,\"samples\":[{\"id\":\"g1\",\"displayName\":\"\","
"\"relativePath\":\"bank/g1.wav\",\"sourceMode\":0,\"sourceRange\":{"
"\"startSeconds\":0,\"endSeconds\":0,\"startPpq\":0,\"endPpq\":0},"
"\"trackGuids\":[],\"wetDry\":1,\"channelCount\":0,\"sampleRate\":0,"
"\"lengthSeconds\":0,\"lengthBeats\":0,\"captureTempo\":0,"
"\"captureTimeSigNum\":0,\"captureTimeSigDenom\":0,\"key\":null,"
"\"rootNote\":null,\"loop\":null,\"levels\":{\"peakDb\":0,\"rmsDb\":0,"
"\"lufs\":0},\"clipped\":false,\"tier\":0,\"contentHash\":\"hash-g1\","
"\"provenance\":null,\"createdTimestamp\":0}]}");
}
static void testDedupByHash() {
BankModel idx;
Sample a = fullSample("x");
@@ -551,6 +575,7 @@ static void testSeamFieldsAdditiveInvariant() {
int main() {
testFullFieldRoundTrip();
testSerializeGoldenLiteral();
testDedupByHash();
testTierFilterAndMove();
testRelativePathInvariant();
+13
View File
@@ -36,6 +36,18 @@ static void testEmptyManifest() {
CHECK(back->empty());
}
// Golden byte-literal (Q-W1 follow-up): pins the EXACT serialized bytes for a
// small fixture (two paths), not just self-consistent re-serialization — a
// format drift that both writer and reader agree on would slip past the
// round-trip tests but not this. The format is frozen as-shipped; the literal
// below is the captured current output.
static void testSerializeGoldenLiteral() {
OwnedFileManifest m;
m.add("reasampler_bank/a.wav");
m.add("reasampler_bank/b.wav");
CHECK(m.serialize() == "{\"owned\":[\"reasampler_bank/a.wav\",\"reasampler_bank/b.wav\"]}");
}
// --- add / contains / order --------------------------------------------------
static void testAddAndContains() {
@@ -159,6 +171,7 @@ static void testMalformedParse() {
}
int main() {
testSerializeGoldenLiteral();
testEmptyManifest();
testAddAndContains();
testDedupRepeatedAdds();
+228
View File
@@ -0,0 +1,228 @@
// Standalone tests for reasampler::model::SlotMap — no REAPER, no test framework.
// SlotMap is the L7 gap-preserving display-position carrier for one bank, extracted
// from bank_book (Q-W1, T4-05). These are the pure SlotMap-only assertions that
// previously lived inline in test_bank_book.cpp (the L7 "SlotMap unit behaviour"
// block); test_bank_book.cpp keeps its BankBook-level integration coverage
// (reorderSample / reconcileSlots / JSON round-trip WITH a full book), this file
// owns the module's own contract: add/remove/query, reorder gap-preservation,
// resetDense/reconcile, equality/fromEntries, and the serialize wire shape.
#include "../src/core/model/slot_map.h"
#include <cstdio>
#include <string>
#include <utility>
#include <vector>
#include "../src/core/json/json.h"
using namespace reasampler::model;
namespace json = reasampler::json;
static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// --- SlotMap unit behaviour --------------------------------------------------
static void testSlotMapDenseAppend() {
SlotMap m;
m.append("a");
m.append("b");
m.append("c");
CHECK(m.slotOf("a") == 0);
CHECK(m.slotOf("b") == 1);
CHECK(m.slotOf("c") == 2);
CHECK(m.maxSlot() == 2);
CHECK((m.orderedIds() == std::vector<std::string>{"a", "b", "c"}));
CHECK(m.idAt(1) == "b");
CHECK(m.slotOf("nope") == -1);
}
static void testSlotMapRemoveLeavesGap() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
CHECK(m.remove("b")); // slot 1 now EMPTY (no re-pack)
CHECK(m.slotOf("a") == 0);
CHECK(m.slotOf("c") == 2); // c did NOT shift down
CHECK(m.idAt(1).empty()); // gap preserved
CHECK((m.orderedIds() == std::vector<std::string>{"a", "c"}));
CHECK(!m.remove("b")); // already gone
}
static void testSlotMapAppendAfterGapGoesToFrontier() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.remove("a"); // slot 0 empty
m.append("d"); // append goes AFTER last occupied (2) -> 3
CHECK(m.slotOf("d") == 3); // did NOT fill the slot-0 gap
CHECK(m.idAt(0).empty());
}
static void testSlotMapReorderIntoEmpty() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.remove("b"); // slot 1 empty
CHECK(m.reorder("c", 1)); // c -> empty slot 1; its slot 2 empties
CHECK(m.slotOf("c") == 1);
CHECK(m.idAt(2).empty());
CHECK(m.slotOf("a") == 0); // untouched
}
static void testSlotMapReorderOntoOccupiedInsertsAndShifts() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); m.append("d"); // 0,1,2,3
CHECK(m.reorder("d", 1)); // d onto occupied slot 1 -> insert-before, shift b,c up
CHECK(m.slotOf("a") == 0); // before the target: unchanged
CHECK(m.slotOf("d") == 1); // took the target slot
CHECK(m.slotOf("b") == 2); // shifted +1
CHECK(m.slotOf("c") == 3); // shifted +1
CHECK((m.orderedIds() == std::vector<std::string>{"a", "d", "b", "c"}));
}
static void testSlotMapReorderPreservesInteriorGapAboveTarget() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.remove("b"); // gap at 1: a@0, c@2
m.append("d"); // d@3
CHECK(m.reorder("d", 0)); // d onto occupied slot 0 -> a shifts to 1, c shifts to 3
CHECK(m.slotOf("d") == 0);
CHECK(m.slotOf("a") == 1); // shifted from 0 -> 1
CHECK(m.slotOf("c") == 3); // shifted from 2 -> 3 (gap at 2 preserved as a +1 of its own)
CHECK(m.idAt(2).empty()); // interior gap above the target survives
}
static void testSlotMapReorderUnmappedIsNoOp() {
SlotMap m;
m.append("a");
CHECK(!m.reorder("ghost", 0)); // not mapped -> false, no mutation
CHECK(m.slotOf("a") == 0);
}
static void testSlotMapNegativeTargetClampsToZero() {
SlotMap m;
m.append("a"); m.append("b"); // 0,1
CHECK(m.reorder("b", -3)); // clamp to 0 -> insert-before a
CHECK(m.slotOf("b") == 0);
CHECK(m.slotOf("a") == 1);
}
static void testSlotMapResetDenseSkipsDupesAndEmpties() {
SlotMap m;
m.resetDense({"a", "", "b", "a", "c"}); // "" and the second "a" dropped
CHECK((m.orderedIds() == std::vector<std::string>{"a", "b", "c"}));
CHECK(m.slotOf("a") == 0);
CHECK(m.slotOf("c") == 2);
}
static void testSlotMapReconcileDropsStaleAppendsNew() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c"); // 0,1,2
m.reconcile({"a", "c", "d"}); // b left the index (drop), d is new (append)
CHECK(m.slotOf("a") == 0); // kept at its slot
CHECK(m.slotOf("c") == 2); // kept at its slot (gap where b was)
CHECK(m.slotOf("b") == -1); // stale marker dropped
CHECK(m.slotOf("d") == 3); // appended after the frontier
CHECK(m.idAt(1).empty()); // b's slot stays empty
}
static void testSlotMapEqualityAndFromEntries() {
SlotMap a;
a.append("x"); a.append("y");
SlotMap b = SlotMap::fromEntries({{"x", 0}, {"y", 1}});
CHECK(a == b);
// Defensive repair: duplicate id (first wins), slot conflict (later dropped),
// empty id / negative slot dropped.
SlotMap c = SlotMap::fromEntries({{"x", 0}, {"x", 5}, {"y", 0}, {"", 9}, {"z", -1}, {"w", 2}});
CHECK(c.slotOf("x") == 0); // first x wins
CHECK(c.slotOf("y") == -1); // slot 0 already taken -> dropped
CHECK(c.slotOf("w") == 2); // valid
CHECK(c.slotOf("z") == -1); // negative slot dropped
}
// --- serialize: golden byte-literal + round-trip -----------------------------
// Pins the exact wire shape (an array of {"id":..,"slot":..} objects, ascending
// slot, no whitespace) so a future format drift is caught here rather than only
// as a downstream bank_book diff. Mirrors the pre-extraction bank_book writer
// byte-for-byte (core/json emit helpers are shared, not reimplemented).
static void testSlotMapSerializeGoldenLiteral() {
SlotMap empty;
CHECK(empty.serialize() == "[]");
SlotMap m;
m.append("a");
m.append("b");
CHECK(m.serialize() == "[{\"id\":\"a\",\"slot\":0},{\"id\":\"b\",\"slot\":1}]");
}
// A local mirror of bank_book's private parseSlots (the "slots" array grammar):
// [{id, slot}, ...]. slot_map.cpp itself only emits — JSON parsing is a consumer
// concern (see slot_map.h) — so the round-trip proof below parses the emitted
// text back into pairs the same way bank_book does, then rebuilds via
// SlotMap::fromEntries and checks equality against the original.
static bool parseSlotsArray(json::Reader& r, std::vector<std::pair<std::string, int>>& out) {
out.clear();
if (!r.consume('[')) return false;
r.skipWs();
if (r.consume(']')) return true; // empty array
do {
if (!r.consume('{')) return false;
std::string id;
int slot = 0;
bool haveId = false, haveSlot = false;
do {
std::string k;
if (!r.parseKey(k)) return false;
if (k == "id") { if (!r.parseString(id)) return false; haveId = true; }
else if (k == "slot") { if (!r.parseInt(slot)) return false; haveSlot = true; }
else { if (!r.skipValue()) return false; }
} while (r.consume(','));
if (!r.consume('}')) return false;
if (!haveId || !haveSlot) return false;
out.emplace_back(std::move(id), slot);
} while (r.consume(','));
return r.consume(']');
}
static void testSlotMapSerializeRoundTrip() {
SlotMap m;
m.append("a"); m.append("b"); m.append("c");
m.remove("b"); // leave a gap: a@0, c@2
m.append("d"); // d@3
const std::string blob = m.serialize();
json::Reader r(blob);
std::vector<std::pair<std::string, int>> pairs;
CHECK(parseSlotsArray(r, pairs));
SlotMap round = SlotMap::fromEntries(pairs);
CHECK(round == m);
CHECK(round.slotOf("a") == 0);
CHECK(round.idAt(1).empty()); // gap survives the round trip
CHECK(round.slotOf("c") == 2);
CHECK(round.slotOf("d") == 3);
}
int main() {
testSlotMapDenseAppend();
testSlotMapRemoveLeavesGap();
testSlotMapAppendAfterGapGoesToFrontier();
testSlotMapReorderIntoEmpty();
testSlotMapReorderOntoOccupiedInsertsAndShifts();
testSlotMapReorderPreservesInteriorGapAboveTarget();
testSlotMapReorderUnmappedIsNoOp();
testSlotMapNegativeTargetClampsToZero();
testSlotMapResetDenseSkipsDupesAndEmpties();
testSlotMapReconcileDropsStaleAppendsNew();
testSlotMapEqualityAndFromEntries();
testSlotMapSerializeGoldenLiteral();
testSlotMapSerializeRoundTrip();
if (g_fail == 0) {
std::printf("slot_map_tests: all passed\n");
return 0;
}
std::printf("slot_map_tests: %d failure(s)\n", g_fail);
return 1;
}
+15
View File
@@ -56,6 +56,20 @@ static int flagValue(const TrackPlan& p, Flag f) {
return -999; // sentinel: flag absent
}
// Golden byte-literal (Q-W1 follow-up): pins the EXACT serialized bytes for the
// default-seeded model (Arrange + Design, no membership), not just self-
// consistent re-serialization — a format drift that both writer and reader
// agree on would slip past the round-trip tests but not this. The format is
// frozen as-shipped; the literal below is the captured current output.
static void testSerializeGoldenLiteral() {
ViewModeModel vm;
CHECK(vm.serialize() ==
"{\"version\":1,\"activeMode\":\"arrange\",\"modes\":[{\"id\":\"arrange\","
"\"displayName\":\"Arrange\",\"ordinal\":0},{\"id\":\"design\","
"\"displayName\":\"Design\",\"ordinal\":1}],\"membership\":[],"
"\"snapshots\":[],\"lanes\":[]}");
}
// -- 1. N-mode proven --------------------------------------------------------
static void testNModeRegistryAndMembership() {
@@ -1787,6 +1801,7 @@ static void testLaneMalformedJson() {
}
int main() {
testSerializeGoldenLiteral();
testNModeRegistryAndMembership();
testParentDerivationMultiMode();
testParentOwnMembershipVisibility();