Files
reasampler/tests/test_card_meta.cpp
T
daniel fdb9e38ad6 L7: capture-order slot model, card metadata overlay, tertiary-border selection
Add gap-preserving per-bank SlotMap (reorder + Alt-replace mutators, JSON
round-trip, insertion-order migration) to bank_book; stamp captureTimeSig on
Sample; pure card_meta formatters + card_drag gesture/slot module; card
metadata overlay + purple selection border in the panel. Shell drop/cursor
wiring deferred. Fixes: removeSample syncs SlotMap; card_drag gap-probe coordinate.
2026-07-27 01:49:07 -04:00

133 lines
4.6 KiB
C++

// Standalone tests for reasampler::card_meta — no REAPER, no test framework. Asserts the
// L7 decorative overlay formatters: bars.beats.subdivisions (F1 tempo+meter stamp) and
// seconds.milliseconds.
//
// Covers: bar-1 origin (zero length), sub-bar, exact bar boundary rollover, multi-bar,
// long capture, non-4/4 meters (3/4 and 6/8), unstamped meter -> blank, unknown tempo ->
// blank (s.ms still derivable); s.ms zero / sub-second / multi-second / ms carry / negative.
#include "../src/card_meta.h"
#include <cstdio>
#include <string>
using namespace reasampler;
static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// Beat length in 4/4 at 120 BPM: (60/120)*(4/4) = 0.5 s/beat, so a 4/4 bar = 2.0 s.
// --- bars.beats.subdivisions -------------------------------------------------
static void testZeroLengthIsBarOneOrigin() {
// Zero length -> the musical origin, "1.1.00".
CHECK(formatBarsBeats(MusicalLength{0.0, 120.0, 4, 4}) == "1.1.00");
}
static void testSubBeat() {
// 0.25 s at 120 BPM 4/4 = 0.5 beats -> bar 1, beat 1, .50.
CHECK(formatBarsBeats(MusicalLength{0.25, 120.0, 4, 4}) == "1.1.50");
}
static void testWholeBeatWithinBar() {
// 0.5 s = exactly 1 beat -> bar 1, beat 2, .00.
CHECK(formatBarsBeats(MusicalLength{0.5, 120.0, 4, 4}) == "1.2.00");
}
static void testExactBarBoundaryRollsOver() {
// 2.0 s = exactly 4 beats = 1 bar in 4/4 -> rolls to bar 2, beat 1 (NOT "1.5.00").
CHECK(formatBarsBeats(MusicalLength{2.0, 120.0, 4, 4}) == "2.1.00");
}
static void testMultiBar() {
// 5.0 s at 120 4/4 = 10 beats = 2 bars + 2 beats -> "3.3.00".
CHECK(formatBarsBeats(MusicalLength{5.0, 120.0, 4, 4}) == "3.3.00");
}
static void testLongCaptureNoBarCap() {
// 256 s at 120 4/4 = 512 beats = 128 bars exactly -> bar 129, beat 1.
CHECK(formatBarsBeats(MusicalLength{256.0, 120.0, 4, 4}) == "129.1.00");
}
static void testThreeFourMeter() {
// 3/4 at 120 BPM: beat = (60/120)*(4/4)=0.5 s, bar = 3 beats = 1.5 s. 1.5 s -> bar 2,1.
CHECK(formatBarsBeats(MusicalLength{1.5, 120.0, 3, 4}) == "2.1.00");
// 1.0 s = 2 beats -> bar 1, beat 3.
CHECK(formatBarsBeats(MusicalLength{1.0, 120.0, 3, 4}) == "1.3.00");
}
static void testSixEightMeter() {
// 6/8 at 120 BPM: an eighth-beat = (60/120)*(4/8) = 0.25 s; bar = 6 beats = 1.5 s.
// 1.5 s -> bar 2, beat 1.
CHECK(formatBarsBeats(MusicalLength{1.5, 120.0, 6, 8}) == "2.1.00");
// 0.25 s = exactly 1 eighth-beat -> bar 1, beat 2.
CHECK(formatBarsBeats(MusicalLength{0.25, 120.0, 6, 8}) == "1.2.00");
}
static void testUnstampedMeterIsBlank() {
// 0/0 (pre-L7 sample) -> no musical read-out.
CHECK(formatBarsBeats(MusicalLength{3.0, 120.0, 0, 0}).empty());
CHECK(formatBarsBeats(MusicalLength{3.0, 120.0, 4, 0}).empty()); // partial stamp also blank
CHECK(formatBarsBeats(MusicalLength{3.0, 120.0, 0, 4}).empty());
}
static void testUnknownTempoIsBlank() {
// Tempo 0 -> no musical read-out even with a meter (cannot derive beats).
CHECK(formatBarsBeats(MusicalLength{3.0, 0.0, 4, 4}).empty());
}
static void testNegativeLengthClampsToOrigin() {
// Defensive: a negative length reads as the origin, not garbage.
CHECK(formatBarsBeats(MusicalLength{-5.0, 120.0, 4, 4}) == "1.1.00");
}
// --- seconds.milliseconds ----------------------------------------------------
static void testSecondsMsZero() {
CHECK(formatSecondsMs(0.0) == "0.000");
}
static void testSecondsMsSubSecond() {
CHECK(formatSecondsMs(0.5) == "0.500");
}
static void testSecondsMsMultiSecond() {
CHECK(formatSecondsMs(1.5) == "1.500");
CHECK(formatSecondsMs(62.037) == "62.037");
}
static void testSecondsMsNegativeClamps() {
CHECK(formatSecondsMs(-1.0) == "0.000");
}
static void testSecondsMsNearWholeSecondCarry() {
// 0.9999 s: floor to ms could hit 999 or 1000; the carry keeps it well-formed.
const std::string r = formatSecondsMs(0.9999);
CHECK(r == "0.999" || r == "1.000");
}
int main() {
testZeroLengthIsBarOneOrigin();
testSubBeat();
testWholeBeatWithinBar();
testExactBarBoundaryRollsOver();
testMultiBar();
testLongCaptureNoBarCap();
testThreeFourMeter();
testSixEightMeter();
testUnstampedMeterIsBlank();
testUnknownTempoIsBlank();
testNegativeLengthClampsToOrigin();
testSecondsMsZero();
testSecondsMsSubSecond();
testSecondsMsMultiSecond();
testSecondsMsNegativeClamps();
testSecondsMsNearWholeSecondCarry();
if (g_fail == 0) std::printf("card_meta: all tests passed\n");
else std::printf("card_meta: %d CHECK(s) FAILED\n", g_fail);
return g_fail == 0 ? 0 : 1;
}