Files
reasampler/tests/test_sample_bands.cpp
T
daniel 0627398bbb Reflow the deck into two categorical rows plus a double-height MASTER bus deck
Row membership is now the group's own property, not a wrap outcome. FILTER's Band|Notch moves to its caption slack, which is what makes the sound row fit. MASTER gains the limiter toggle, the output meter and the GR lamp.
2026-08-02 13:52:33 -04:00

220 lines
10 KiB
C++

// Standalone tests for reasampler::instrument::ui::sample_bands — no VST3, no REAPER, no
// test framework. Same fast assert loop as the sibling pure tests.
//
// Covers: the shared Rect vocabulary (contains() half-open + degenerate rects); the
// three-band vertical inventory (chrome over waveform over decks, no overlap, no
// inversion) asserted as pure geometry with no paint call; the waveform band's two-lane
// floor and the bands-clip-rather-than-squeeze rule on a short window; the deck band's
// bottom anchor and its exact requested height; the same stack/anchor/degrade properties
// re-anchored AT the allocator's own kEditorMinWidth/kEditorMinHeight floor rather than only
// below it; and the lane split (mono = one full-band lane, stereo = two lanes with the seam
// gap between them).
#include "../src/core/instrument/ui/sample_bands.h"
#include <cstdio>
using namespace reasampler;
using namespace reasampler::instrument::ui;
static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// --- the shared Rect vocabulary ----------------------------------------------
static void testContainsHalfOpen() {
Rect r = Rect::ltrb(10, 20, 50, 40); // [10,50) x [20,40)
CHECK(contains(r, 10, 20)); // top-left inclusive
CHECK(contains(r, 49, 39)); // bottom-right exclusive edge, inside
CHECK(!contains(r, 50, 30)); // right edge excluded
CHECK(!contains(r, 30, 40)); // bottom edge excluded
CHECK(!contains(r, 9, 30)); // left of rect
CHECK(!contains(r, 30, 19)); // above rect
}
static void testContainsDegenerate() {
CHECK(!contains(Rect::ltrb(10, 10, 10, 20), 10, 15)); // zero width
CHECK(!contains(Rect::ltrb(10, 10, 20, 10), 15, 10)); // zero height
CHECK(!contains(Rect::ltrb(20, 10, 10, 20), 15, 15)); // inverted (right < left)
}
// --- the vertical inventory ---------------------------------------------------
static void testBandsStackTopToBottomWithoutOverlap() {
const SampleBands b = computeSampleBands(840, 620, 120);
CHECK(b.chrome.y == 0);
CHECK(b.chrome.height == kTitleHeight + kChromeRowHeight);
// Strictly ordered, no overlap: each band starts at or after the previous one's bottom.
CHECK(b.waveform.y >= b.chrome.bottom());
CHECK(b.decks.y >= b.waveform.bottom());
// No inversion anywhere.
CHECK(b.chrome.height > 0 && b.waveform.height > 0 && b.decks.height > 0);
CHECK(b.chrome.width > 0 && b.waveform.width > 0 && b.decks.width > 0);
}
static void testChromeSpansFullWidthAndLowerBandsAreInset() {
const SampleBands b = computeSampleBands(840, 620, 120);
CHECK(b.chrome.x == 0 && b.chrome.right() == 840);
CHECK(b.waveform.x == kPad && b.waveform.right() == 840 - kPad);
CHECK(b.decks.x == kPad && b.decks.right() == 840 - kPad);
}
static void testDeckBandIsBottomAnchoredAtItsRequestedHeight() {
const int deckH = 96;
const SampleBands b = computeSampleBands(840, 620, deckH);
CHECK(b.decks.height == deckH);
CHECK(b.decks.bottom() == 620 - kPad); // bottom-anchored inside the pad
}
static void testWaveformAbsorbsSlackAsTheWindowGrows() {
const SampleBands small = computeSampleBands(840, 620, 120);
const SampleBands big = computeSampleBands(840, 900, 120);
CHECK(big.waveform.height == small.waveform.height + 280);
// The fixed bands do not grow with the window.
CHECK(big.chrome.height == small.chrome.height);
CHECK(big.decks.height == small.decks.height);
}
static void testWaveformNeverShrinksBelowTheTwoLaneFloor() {
// A window far too short for chrome + two lanes + deck: the floor wins and the deck band
// is pushed past the bottom (clipped) rather than squeezing the waveform.
const SampleBands b = computeSampleBands(840, 160, 120);
CHECK(b.waveform.height == kWaveformMinHeight);
CHECK(b.decks.y >= b.waveform.bottom());
CHECK(b.decks.bottom() > 160); // deliberately clipped below the window
}
static void testTwoLaneFloorHoldsTwoUsableLanes() {
// The floor is exactly what two minimum lanes plus their seam need — not an arbitrary
// number, so a lane can never be allocated below its own minimum.
CHECK(kWaveformMinHeight == 2 * kLaneMinHeight + kLaneGap);
const SampleBands b = computeSampleBands(840, 160, 120);
const WaveformLanes lanes = waveformLanes(b.waveform, LaneSplit::Stereo);
CHECK(lanes.upper.height >= kLaneMinHeight);
CHECK(lanes.lower.height >= kLaneMinHeight);
}
static void testDegenerateWindowYieldsNoInvertedRects() {
const SampleBands z = computeSampleBands(0, 0, 0);
CHECK(z.chrome.width == 0 && z.chrome.height == 0);
CHECK(z.waveform.width <= 0 || z.waveform.height >= 0);
CHECK(z.waveform.right() >= z.waveform.x);
CHECK(z.decks.right() >= z.decks.x);
const SampleBands tiny = computeSampleBands(20, 20, 4);
CHECK(tiny.waveform.right() >= tiny.waveform.x);
CHECK(tiny.decks.right() >= tiny.decks.x);
}
// --- the allocator at its own floor (kEditorMinWidth x kEditorMinHeight) -------
// The allocator's own client-area floor is a fact IT owns (kEditorMinWidth/kEditorMinHeight
// above); the fixtures above validate the general shape entirely below that floor (840x620,
// 840x160). These anchor the same properties AT the floor itself, so a floor move that broke
// the stack there would have nothing else in this file to catch it.
static void testBandsStackWithoutOverlapAtTheEditorFloor() {
const SampleBands b = computeSampleBands(kEditorMinWidth, kEditorMinHeight, 120);
CHECK(b.chrome.y == 0);
CHECK(b.chrome.width == kEditorMinWidth);
CHECK(b.chrome.height == kTitleHeight + kChromeRowHeight);
CHECK(b.waveform.y >= b.chrome.bottom());
CHECK(b.decks.y >= b.waveform.bottom());
CHECK(b.waveform.x == kPad && b.waveform.right() == kEditorMinWidth - kPad);
CHECK(b.decks.x == kPad && b.decks.right() == kEditorMinWidth - kPad);
}
static void testDeckBandIsBottomAnchoredAtTheEditorFloor() {
constexpr int deckH = 120;
const SampleBands b = computeSampleBands(kEditorMinWidth, kEditorMinHeight, deckH);
CHECK(b.decks.height == deckH);
CHECK(b.decks.bottom() == kEditorMinHeight - kPad);
}
// At the shipped two-row deck height (216px — what test_deck_groups.cpp pins the deck to by
// construction, at and above the floor width), the waveform gets exactly what the floor's own
// height leaves it: an equality, not a bound, so a floor-height change that quietly ate into
// the waveform's slack would fail here rather than only widen/narrow a `>=`.
static void testWaveformGetsExactlyTheFloorsRemainingHeightAtATwoRowDeck() {
constexpr int twoRowDeckH = 216;
const SampleBands b = computeSampleBands(kEditorMinWidth, kEditorMinHeight, twoRowDeckH);
const int expected = kEditorMinHeight - kPad - twoRowDeckH - 2 * kBandGap -
(kTitleHeight + kChromeRowHeight);
CHECK(b.waveform.height == expected);
CHECK(b.waveform.height == 358);
CHECK(b.waveform.height >= kWaveformMinHeight);
}
// The floor is not immune to the degrade path: a deck grown too tall for the floor's OWN
// height still hits the floor-wins rule instead of squeezing the waveform below its usable
// minimum — the sub-floor fixtures above cover the general rule; this is the same rule
// exercised at the allocator's own minimum width.
static void testAnOversizedDeckAtTheFloorStillDegradesGracefully() {
const SampleBands b = computeSampleBands(kEditorMinWidth, kEditorMinHeight, 500);
CHECK(b.waveform.height == kWaveformMinHeight);
CHECK(b.decks.y >= b.waveform.bottom());
CHECK(b.decks.bottom() > kEditorMinHeight); // clipped below the floor's own window
}
// --- the waveform band's lanes ------------------------------------------------
static void testMonoUsesOneFullBandLane() {
const Rect band = Rect::ltrb(8, 100, 832, 300);
const WaveformLanes lanes = waveformLanes(band, LaneSplit::Single);
CHECK(lanes.upper == band);
CHECK(lanes.lower.empty()); // no redundant duplicate lane in mono
}
static void testStereoSplitsIntoTwoLanesWithTheSeamGap() {
const Rect band = Rect::ltrb(8, 100, 832, 300); // height 200
const WaveformLanes lanes = waveformLanes(band, LaneSplit::Stereo);
CHECK(lanes.upper.y == band.y);
CHECK(lanes.lower.bottom() == band.bottom());
// Full width each, seam exactly kLaneGap, no overlap.
CHECK(lanes.upper.x == band.x && lanes.upper.right() == band.right());
CHECK(lanes.lower.x == band.x && lanes.lower.right() == band.right());
CHECK(lanes.lower.y - lanes.upper.bottom() == kLaneGap);
CHECK(lanes.upper.height + lanes.lower.height + kLaneGap == band.height);
}
static void testStereoOddRemainderGoesToTheUpperLane() {
const Rect band = Rect::ltrb(0, 0, 100, 201); // usable 199 -> 100 / 99
const WaveformLanes lanes = waveformLanes(band, LaneSplit::Stereo);
CHECK(lanes.upper.height == 100);
CHECK(lanes.lower.height == 99);
CHECK(lanes.lower.bottom() == band.bottom());
}
static void testEmptyBandYieldsEmptyLanes() {
const WaveformLanes lanes = waveformLanes(Rect{}, LaneSplit::Stereo);
CHECK(lanes.upper.empty());
CHECK(lanes.lower.empty());
}
int main() {
testContainsHalfOpen();
testContainsDegenerate();
testBandsStackTopToBottomWithoutOverlap();
testChromeSpansFullWidthAndLowerBandsAreInset();
testDeckBandIsBottomAnchoredAtItsRequestedHeight();
testWaveformAbsorbsSlackAsTheWindowGrows();
testWaveformNeverShrinksBelowTheTwoLaneFloor();
testTwoLaneFloorHoldsTwoUsableLanes();
testDegenerateWindowYieldsNoInvertedRects();
testBandsStackWithoutOverlapAtTheEditorFloor();
testDeckBandIsBottomAnchoredAtTheEditorFloor();
testWaveformGetsExactlyTheFloorsRemainingHeightAtATwoRowDeck();
testAnOversizedDeckAtTheFloorStillDegradesGracefully();
testMonoUsesOneFullBandLane();
testStereoSplitsIntoTwoLanesWithTheSeamGap();
testStereoOddRemainderGoesToTheUpperLane();
testEmptyBandYieldsEmptyLanes();
if (g_fail == 0) {
std::printf("sample_bands: all tests passed\n");
return 0;
}
std::printf("sample_bands: %d failure(s)\n", g_fail);
return 1;
}