174 lines
7.4 KiB
C++
174 lines
7.4 KiB
C++
// Standalone tests for reasampler::mode_switch — no REAPER, no test framework.
|
|
// Same fast loop as the sibling pure tests (bank_grid et al.): assert the segmented
|
|
// mode-switch layout math and hit-testing directly.
|
|
//
|
|
// Covers (D5 brief §unit-test): N=2 and N=3 segment layout; exact tiling (adjacent
|
|
// segments abut with no gap/overlap, half-open boundaries — no pixel claimed twice,
|
|
// last segment reaches the header's right edge even when width doesn't divide
|
|
// evenly); hit-test hits per segment and misses (outside the band above/below/
|
|
// left/right, boundary pixels); degenerate widths and counts.
|
|
|
|
#include "../src/core/view/mode_switch.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
|
|
using namespace reasampler;
|
|
using namespace reasampler::view;
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(cond) do { if(!(cond)) { \
|
|
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
|
|
|
// --- Layout: N=2 -------------------------------------------------------------
|
|
|
|
// A header 100 wide at origin (10, 4), height 24, split in two: segments
|
|
// [10,60) and [60,110), each 50 wide, full header y/height.
|
|
static void testTwoSegmentsEvenSplit() {
|
|
HeaderRect h{10, 4, 100, 24};
|
|
auto rects = computeSegmentRects(h, 2);
|
|
CHECK(rects.size() == 2);
|
|
CHECK((rects[0] == SegmentRect{10, 4, 50, 24}));
|
|
CHECK((rects[1] == SegmentRect{60, 4, 50, 24}));
|
|
}
|
|
|
|
// --- Layout: N=3, uneven width absorbed at boundaries ------------------------
|
|
|
|
// 100 wide / 3 doesn't divide evenly: boundaries at floor(i*100/3) =
|
|
// 0, 33, 66, 100 -> widths 33, 33, 34. The rounding lands in the LAST segment;
|
|
// segments still abut exactly and the last reaches header.x + width (0+100).
|
|
static void testThreeSegmentsUnevenTilesExactly() {
|
|
HeaderRect h{0, 0, 100, 20};
|
|
auto rects = computeSegmentRects(h, 3);
|
|
CHECK(rects.size() == 3);
|
|
CHECK((rects[0] == SegmentRect{0, 0, 33, 20}));
|
|
CHECK((rects[1] == SegmentRect{33, 0, 33, 20}));
|
|
CHECK((rects[2] == SegmentRect{66, 0, 34, 20}));
|
|
// Adjacent segments abut (no gap, no overlap): each left edge == prior right.
|
|
CHECK(rects[1].x == rects[0].x + rects[0].width);
|
|
CHECK(rects[2].x == rects[1].x + rects[1].width);
|
|
// Last segment's right edge reaches the header's right edge exactly.
|
|
CHECK(rects[2].x + rects[2].width == h.x + h.width);
|
|
}
|
|
|
|
// --- Layout: full coverage invariant for a range of widths -------------------
|
|
|
|
// For any width and count, the segments must tile [x, x+width) with no gap/overlap:
|
|
// first left == x, last right == x+width, every mid boundary shared.
|
|
static void testTilingCoversHeaderForVariousWidths() {
|
|
for (int width = 1; width <= 200; ++width) {
|
|
for (int count = 1; count <= 5; ++count) {
|
|
HeaderRect h{7, 3, width, 16};
|
|
auto rects = computeSegmentRects(h, count);
|
|
CHECK(static_cast<int>(rects.size()) == count);
|
|
CHECK(rects.front().x == h.x);
|
|
CHECK(rects.back().x + rects.back().width == h.x + h.width);
|
|
for (std::size_t i = 1; i < rects.size(); ++i)
|
|
CHECK(rects[i].x == rects[i - 1].x + rects[i - 1].width);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Layout: degenerate --------------------------------------------------------
|
|
|
|
// segmentCount <= 0 or non-positive width yields no rects (the panel draws nothing
|
|
// / falls back to full-client grid).
|
|
static void testLayoutDegenerate() {
|
|
CHECK(computeSegmentRects(HeaderRect{0, 0, 100, 20}, 0).empty());
|
|
CHECK(computeSegmentRects(HeaderRect{0, 0, 100, 20}, -3).empty());
|
|
CHECK(computeSegmentRects(HeaderRect{0, 0, 0, 20}, 2).empty());
|
|
CHECK(computeSegmentRects(HeaderRect{0, 0, -5, 20}, 2).empty());
|
|
}
|
|
|
|
// A single segment fills the whole header.
|
|
static void testSingleSegmentFillsHeader() {
|
|
HeaderRect h{5, 5, 80, 24};
|
|
auto rects = computeSegmentRects(h, 1);
|
|
CHECK(rects.size() == 1);
|
|
CHECK((rects[0] == SegmentRect{5, 5, 80, 24}));
|
|
}
|
|
|
|
// --- Hit-test: hits -----------------------------------------------------------
|
|
|
|
// A point inside each segment maps to that segment's index. Header 100 wide at
|
|
// x=10, two segments [10,60) and [60,110).
|
|
static void testHitTestHitsEachSegment() {
|
|
HeaderRect h{10, 4, 100, 24};
|
|
CHECK(hitTestSegment(10, 4, h, 2) == 0); // top-left corner of segment 0
|
|
CHECK(hitTestSegment(35, 15, h, 2) == 0); // middle of segment 0
|
|
CHECK(hitTestSegment(59, 27, h, 2) == 0); // last pixel of segment 0 (band)
|
|
CHECK(hitTestSegment(60, 15, h, 2) == 1); // first pixel of segment 1
|
|
CHECK(hitTestSegment(109, 4, h, 2) == 1); // last pixel column of segment 1
|
|
}
|
|
|
|
// The boundary pixel belongs to exactly ONE segment (half-open): px==60 is the
|
|
// start of segment 1, never the end of segment 0 — so no pixel is double-claimed.
|
|
static void testHitTestBoundaryHalfOpen() {
|
|
HeaderRect h{10, 4, 100, 24};
|
|
CHECK(hitTestSegment(59, 10, h, 2) == 0);
|
|
CHECK(hitTestSegment(60, 10, h, 2) == 1);
|
|
// Three-way boundaries at floor(i*100/2)+x are consistent for N=3 too.
|
|
HeaderRect h3{0, 0, 99, 20}; // 99/3 = 33 exactly -> edges 0,33,66,99
|
|
CHECK(hitTestSegment(32, 5, h3, 3) == 0);
|
|
CHECK(hitTestSegment(33, 5, h3, 3) == 1);
|
|
CHECK(hitTestSegment(65, 5, h3, 3) == 1);
|
|
CHECK(hitTestSegment(66, 5, h3, 3) == 2);
|
|
CHECK(hitTestSegment(98, 5, h3, 3) == 2);
|
|
}
|
|
|
|
// --- Hit-test: misses ---------------------------------------------------------
|
|
|
|
// Points outside the header band on any side miss (-1). Below the band is where
|
|
// the grid lives; the panel treats -1 as "fall through to grid handling".
|
|
static void testHitTestMissesOutsideBand() {
|
|
HeaderRect h{10, 4, 100, 24};
|
|
CHECK(hitTestSegment(9, 10, h, 2) == -1); // left of header
|
|
CHECK(hitTestSegment(110, 10, h, 2) == -1); // right edge (px == x+width, excluded)
|
|
CHECK(hitTestSegment(50, 3, h, 2) == -1); // above the band
|
|
CHECK(hitTestSegment(50, 28, h, 2) == -1); // below the band (into the grid)
|
|
CHECK(hitTestSegment(200, 200, h, 2) == -1); // far away
|
|
}
|
|
|
|
// Degenerate hit-test inputs miss safely.
|
|
static void testHitTestDegenerate() {
|
|
HeaderRect h{10, 4, 100, 24};
|
|
CHECK(hitTestSegment(50, 15, h, 0) == -1); // no segments
|
|
CHECK(hitTestSegment(50, 15, h, -2) == -1);
|
|
CHECK(hitTestSegment(50, 15, HeaderRect{10, 4, 0, 24}, 2) == -1); // zero width
|
|
CHECK(hitTestSegment(50, 15, HeaderRect{10, 4, 100, 0}, 2) == -1); // zero height
|
|
}
|
|
|
|
// --- Cross-check: every point in the header hit-tests to the segment that DREW
|
|
// it (hit-test and layout agree, the load-bearing consistency invariant). ------
|
|
|
|
static void testHitTestMatchesLayout() {
|
|
HeaderRect h{4, 2, 87, 18}; // deliberately awkward width/origin
|
|
const int count = 3;
|
|
auto rects = computeSegmentRects(h, count);
|
|
for (int px = h.x; px < h.x + h.width; ++px) {
|
|
const int seg = hitTestSegment(px, h.y + 1, h, count);
|
|
CHECK(seg >= 0);
|
|
// The returned segment's rect must contain px (half-open).
|
|
const SegmentRect& r = rects[static_cast<std::size_t>(seg)];
|
|
CHECK(px >= r.x && px < r.x + r.width);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
testTwoSegmentsEvenSplit();
|
|
testThreeSegmentsUnevenTilesExactly();
|
|
testTilingCoversHeaderForVariousWidths();
|
|
testLayoutDegenerate();
|
|
testSingleSegmentFillsHeader();
|
|
|
|
testHitTestHitsEachSegment();
|
|
testHitTestBoundaryHalfOpen();
|
|
testHitTestMissesOutsideBand();
|
|
testHitTestDegenerate();
|
|
testHitTestMatchesLayout();
|
|
|
|
if (g_fail == 0) std::printf("All tests passed.\n");
|
|
return g_fail ? 1 : 0;
|
|
}
|