220 lines
10 KiB
C++
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 a representative two-row deck height (216px — the ceiling test_deck_groups.cpp bounds
|
|
// the wrapped deck to), 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;
|
|
}
|