// Standalone tests for reasampler::vst::editor_geometry — no VST3, no REAPER, no test // framework. Same fast assert loop as the sibling pure tests (mode_switch et al.): // assert the IPlugView LICE editor's layout math + hit-testing directly. // // Covers: contains() half-open convention + degenerate rects; layoutEditor regions on a // normal view (title band + button + canvas), a tiny view (button clamped to canvas, // never overhanging), and a zero view (all rects empty, no inversion); hitTest hitting // the button, missing on the title/canvas, missing outside the surface, and boundary // pixels; layout<->hit-test agreement (a click on the drawn button rect hits it). #include "../src/vst/editor_geometry.h" #include using namespace reasampler::vst; static int g_fail = 0; #define CHECK(cond) do { if(!(cond)) { \ std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) // --- contains() --------------------------------------------------------------- static void testContainsHalfOpen() { Rect r{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{10, 10, 10, 20}, 10, 15)); // zero width CHECK(!contains(Rect{10, 10, 20, 10}, 15, 10)); // zero height CHECK(!contains(Rect{20, 10, 10, 20}, 15, 15)); // inverted (right < left) } // --- layoutEditor: normal view ------------------------------------------------ static void testLayoutNormalView() { // A comfortable 400x260 view: title band spans the top full width; canvas is the // rest; button sits inside the canvas, inset by the margin. const EditorLayout L = layoutEditor(400, 260); CHECK(L.titleBar.left == 0 && L.titleBar.top == 0); CHECK(L.titleBar.right == 400); CHECK(L.titleBar.height() > 0 && L.titleBar.height() <= 260); // Canvas begins right below the title bar and reaches the bottom-right. CHECK(L.canvas.top == L.titleBar.bottom); CHECK(L.canvas.right == 400 && L.canvas.bottom == 260); // Button is inside the canvas (does not overhang any edge). CHECK(L.button.left >= L.canvas.left); CHECK(L.button.top >= L.canvas.top); CHECK(L.button.right <= L.canvas.right); CHECK(L.button.bottom <= L.canvas.bottom); CHECK(L.button.width() > 0 && L.button.height() > 0); } // --- layoutEditor: tiny view (clamping) --------------------------------------- static void testLayoutTinyViewClampsButton() { // A view narrower/shorter than the button's natural size: the button must clamp to // the canvas and never produce an inverted or overhanging rect. const EditorLayout L = layoutEditor(40, 40); CHECK(L.button.right <= L.canvas.right); CHECK(L.button.bottom <= L.canvas.bottom); CHECK(L.button.right >= L.button.left); // never inverted CHECK(L.button.bottom >= L.button.top); // Title bar clamps to the client height when the view is shorter than its height. CHECK(L.titleBar.bottom <= 40); } // --- layoutEditor: zero view (all empty, no inversion) ------------------------ static void testLayoutZeroView() { const EditorLayout L = layoutEditor(0, 0); CHECK(L.titleBar.width() <= 0 || L.titleBar.height() <= 0); CHECK(L.canvas.width() <= 0 || L.canvas.height() <= 0); // No rect is inverted. CHECK(L.button.right >= L.button.left); CHECK(L.button.bottom >= L.button.top); CHECK(L.canvas.right >= L.canvas.left); CHECK(L.canvas.bottom >= L.canvas.top); // A click anywhere on an empty layout hits nothing. CHECK(hitTest(L, 0, 0) == HitTarget::kNone); CHECK(hitTest(L, 5, 5) == HitTarget::kNone); } // --- hitTest ------------------------------------------------------------------ static void testHitTestButton() { const EditorLayout L = layoutEditor(400, 260); // Center of the button hits it. const int cx = (L.button.left + L.button.right) / 2; const int cy = (L.button.top + L.button.bottom) / 2; CHECK(hitTest(L, cx, cy) == HitTarget::kButton); } static void testHitTestMissesNonButton() { const EditorLayout L = layoutEditor(400, 260); // Title bar is inert in the spike. CHECK(hitTest(L, 200, L.titleBar.top + 1) == HitTarget::kNone); // Empty canvas away from the button. CHECK(hitTest(L, 380, 240) == HitTarget::kNone); // Outside the surface entirely. CHECK(hitTest(L, -5, -5) == HitTarget::kNone); CHECK(hitTest(L, 500, 500) == HitTarget::kNone); } static void testHitTestButtonBoundary() { const EditorLayout L = layoutEditor(400, 260); // Top-left corner of the button is inclusive; the right/bottom edges are excluded. CHECK(hitTest(L, L.button.left, L.button.top) == HitTarget::kButton); CHECK(hitTest(L, L.button.right, L.button.top) == HitTarget::kNone); CHECK(hitTest(L, L.button.left, L.button.bottom) == HitTarget::kNone); } // --- layout<->hit-test agreement ---------------------------------------------- // Every pixel inside the drawn button rect must hit the button; this is the // load-bearing consistency invariant between what the shell draws and what it routes. static void testHitTestMatchesDrawnButton() { const EditorLayout L = layoutEditor(320, 200); for (int y = L.button.top; y < L.button.bottom; ++y) { for (int x = L.button.left; x < L.button.right; ++x) { CHECK(hitTest(L, x, y) == HitTarget::kButton); } } } int main() { testContainsHalfOpen(); testContainsDegenerate(); testLayoutNormalView(); testLayoutTinyViewClampsButton(); testLayoutZeroView(); testHitTestButton(); testHitTestMissesNonButton(); testHitTestButtonBoundary(); testHitTestMatchesDrawnButton(); if (g_fail == 0) std::printf("editor_geometry: all tests passed\n"); return g_fail != 0; }