// Standalone tests for reasampler::overflow_menu — no REAPER, no test framework. Same fast // loop as the sibling pure tests (prune_button / mode_switch): assert the top-toolbar More // ("⋯") button placement, the action-bar reserve, and hit-testing directly. // // Covers (L5 refinement 1 pure module): right-anchored layout in a wide band; the reserve the // action_bar must leave (width + 2*rightInset); vertical inset; thin-band height clamp; // SUPPRESSION (empty rect) when the band is too narrow to clear the left inset or is degenerate; // a degenerate band reserves nothing; hit-test in/out/edge (half-open bounds); an empty button // claims no point; draw and hit-test agree over the whole rect. #include "../src/overflow_menu.h" #include 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) // --- Layout: wide band, right-anchored --------------------------------------- // Band 400 wide at origin (0, 0), height 40. Default spec: buttonWidth 28, rightInset 6, // verticalInset 3, minLeftInset 40. Right edge = 0+400-6 = 394, left = 394-28 = 366 // (>= 0+40, placed). Top = 0+3 = 3, height = 40-6 = 34. static void testWideBandRightAnchored() { MenuBarRect bar{0, 0, 400, 40}; const MenuButtonRect b = computeMenuButton(bar, MenuButtonSpec{}); CHECK(!b.empty()); CHECK((b == MenuButtonRect{366, 3, 28, 34})); CHECK(b.x + b.width == bar.x + bar.width - 6); // right edge at rightInset CHECK(b.x >= bar.x + 40); // clears the left inset } // Origin offset honoured (button anchors to THIS band's right). static void testOffsetBandAnchors() { MenuBarRect bar{10, 5, 400, 40}; const MenuButtonRect b = computeMenuButton(bar, MenuButtonSpec{}); CHECK(!b.empty()); CHECK(b.x + b.width == bar.x + bar.width - 6); // = 10+400-6 = 404 CHECK(b.y == 8); // 5 + 3 } // --- Reserve ----------------------------------------------------------------- // The reserve = buttonWidth + 2*rightInset so the frequent buttons get a right gap AND a left // breathing gap before the menu button. Default: 28 + 2*6 = 40. static void testReserve() { MenuBarRect bar{0, 0, 400, 40}; CHECK(menuButtonReserve(bar, MenuButtonSpec{}) == 40); } // A degenerate band reserves nothing (no button to reserve for). static void testDegenerateBandReservesNothing() { CHECK(menuButtonReserve(MenuBarRect{0, 0, 0, 40}, MenuButtonSpec{}) == 0); CHECK(menuButtonReserve(MenuBarRect{0, 0, 400, 0}, MenuButtonSpec{}) == 0); MenuButtonSpec zero{}; zero.buttonWidth = 0; CHECK(menuButtonReserve(MenuBarRect{0, 0, 400, 40}, zero) == 0); } // --- Suppression: too narrow / degenerate ------------------------------------ // The button is suppressed when its left edge would fall past minLeftInset. left = x + width - // rightInset - buttonWidth. Need left < x + minLeftInset -> // width < rightInset + buttonWidth + minLeftInset = 6 + 28 + 40 = 74. Width 73 suppresses; 74 // places (boundary). static void testNarrowBandSuppressed() { CHECK(computeMenuButton(MenuBarRect{0, 0, 73, 40}, MenuButtonSpec{}).empty()); CHECK(!computeMenuButton(MenuBarRect{0, 0, 74, 40}, MenuButtonSpec{}).empty()); } static void testDegenerateBandSuppressed() { CHECK(computeMenuButton(MenuBarRect{0, 0, 0, 40}, MenuButtonSpec{}).empty()); CHECK(computeMenuButton(MenuBarRect{0, 0, 400, 0}, MenuButtonSpec{}).empty()); MenuButtonSpec zero{}; zero.buttonWidth = 0; CHECK(computeMenuButton(MenuBarRect{0, 0, 400, 40}, zero).empty()); } // A thin band (height <= 2*verticalInset) still places a button, clamped to the band's height. static void testThinBandClampsHeight() { MenuBarRect bar{0, 0, 400, 4}; // 4 <= 2*3, so height would be negative -> clamp const MenuButtonRect b = computeMenuButton(bar, MenuButtonSpec{}); CHECK(!b.empty()); CHECK(b.y == bar.y); CHECK(b.height == bar.height); } // --- Hit-test ---------------------------------------------------------------- static void testHitTestInside() { MenuBarRect bar{0, 0, 400, 40}; const MenuButtonRect b = computeMenuButton(bar, MenuButtonSpec{}); // {366,3,28,34} CHECK(hitTestMenuButton(b.x, b.y, b)); // top-left inclusive CHECK(hitTestMenuButton(b.x + b.width - 1, b.y + b.height - 1, b)); // bottom-right inclusive CHECK(hitTestMenuButton(b.x + b.width / 2, b.y + b.height / 2, b)); // centre } static void testHitTestEdgesExcluded() { MenuBarRect bar{0, 0, 400, 40}; const MenuButtonRect b = computeMenuButton(bar, MenuButtonSpec{}); CHECK(!hitTestMenuButton(b.x - 1, b.y, b)); // just left CHECK(!hitTestMenuButton(b.x + b.width, b.y, b)); // right edge excluded CHECK(!hitTestMenuButton(b.x, b.y - 1, b)); // just above CHECK(!hitTestMenuButton(b.x, b.y + b.height, b)); // bottom edge excluded } // An empty/suppressed button claims no point — a click where it would be falls through. static void testEmptyButtonClaimsNothing() { MenuButtonRect empty{}; CHECK(!hitTestMenuButton(0, 0, empty)); const MenuButtonRect suppressed = computeMenuButton(MenuBarRect{0, 0, 50, 40}, MenuButtonSpec{}); CHECK(suppressed.empty()); CHECK(!hitTestMenuButton(30, 20, suppressed)); } // Draw/hit-test agreement over the whole rect + the four immediate outside neighbours. static void testHitTestMatchesLayout() { MenuBarRect bar{3, 7, 377, 38}; // awkward origin/size const MenuButtonRect b = computeMenuButton(bar, MenuButtonSpec{}); CHECK(!b.empty()); for (int py = b.y; py < b.y + b.height; ++py) for (int px = b.x; px < b.x + b.width; ++px) CHECK(hitTestMenuButton(px, py, b)); CHECK(!hitTestMenuButton(b.x - 1, b.y, b)); CHECK(!hitTestMenuButton(b.x + b.width, b.y, b)); } int main() { testWideBandRightAnchored(); testOffsetBandAnchors(); testReserve(); testDegenerateBandReservesNothing(); testNarrowBandSuppressed(); testDegenerateBandSuppressed(); testThinBandClampsHeight(); testHitTestInside(); testHitTestEdgesExcluded(); testEmptyButtonClaimsNothing(); testHitTestMatchesLayout(); if (g_fail == 0) std::printf("overflow_menu: all tests passed\n"); else std::printf("overflow_menu: %d CHECK(s) FAILED\n", g_fail); return g_fail == 0 ? 0 : 1; }