// Standalone tests for reasampler::vst::capture_browser — no VST3, no REAPER, no framework. // Same fast assert loop as the sibling pure tests (embed_strip / editor_geometry): assert // the capture-first browser's card-grid + bank-filter-tab layout and hit-testing directly. // // Covers: layoutBrowser splitting an area into the tab strip + card grid and deriving the // column count; a tiny/zero area (no inversion, columns >= 1); cardCellRect / cardContentRect // / cardThumbnailRect / cardLabelRect tiling row-major across columns with the gutter inset // and the thumbnail band above the label; cardHitTest landing on the card content (and MISSING // in the inter-card gutter, past the last card, and on the tab strip); filterTabRect dividing // the strip into equal segments with the last tab absorbing the remainder; filterTabHitTest // hitting each tab and missing off-strip. #include "../src/vst/capture_browser.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) // --- layoutBrowser ------------------------------------------------------------ static void testLayoutNormalArea() { // Wide enough for several columns of the fixed-width card. const BrowserLayout L = layoutBrowser(560, 300); CHECK(L.tabStrip.left == 0 && L.tabStrip.top == 0 && L.tabStrip.right == 560); CHECK(L.tabStrip.height() == kBrowserTabHeight); // The grid starts right below the tab strip and fills the rest, contiguous. CHECK(L.grid.top == L.tabStrip.bottom); CHECK(L.grid.bottom == 300 && L.grid.right == 560); // columns = grid.width() / cardWidth (>= 1). CHECK(L.columns == 560 / kBrowserCardWidth); CHECK(L.columns >= 1); } static void testLayoutNarrowAreaSingleColumn() { // Narrower than one card: still a single column, no inversion. const BrowserLayout L = layoutBrowser(kBrowserCardWidth - 10, 200); CHECK(L.columns == 1); CHECK(L.grid.width() >= 0); CHECK(L.tabStrip.height() == kBrowserTabHeight); } static void testLayoutZeroArea() { const BrowserLayout L = layoutBrowser(0, 0); CHECK(L.tabStrip.width() == 0 && L.tabStrip.height() == 0); CHECK(L.grid.width() == 0); CHECK(L.columns == 1); // never zero (avoids a divide-by-zero in card layout) } static void testLayoutTinyHeightClampsTabStrip() { // A height below the tab band: the tab strip clamps to the area, the grid is empty. const BrowserLayout L = layoutBrowser(560, kBrowserTabHeight - 6); CHECK(L.tabStrip.height() == kBrowserTabHeight - 6); CHECK(L.grid.height() <= 0); // no room left for cards } // --- card rects --------------------------------------------------------------- static void testCardCellsTileRowMajor() { const BrowserLayout L = layoutBrowser(560, 300); const int cols = L.columns; // Card 0 is top-left of the grid. const Rect c0 = cardCellRect(L, 0); CHECK(c0.left == L.grid.left && c0.top == L.grid.top); CHECK(c0.width() == kBrowserCardWidth && c0.height() == kBrowserCardHeight); // Card 1 is one card-width to the right, same row. const Rect c1 = cardCellRect(L, 1); CHECK(c1.left == L.grid.left + kBrowserCardWidth); CHECK(c1.top == c0.top); // The first card of the SECOND row wraps back to the left, one card-height down. const Rect wrap = cardCellRect(L, cols); CHECK(wrap.left == L.grid.left); CHECK(wrap.top == L.grid.top + kBrowserCardHeight); } static void testCardCellNegativeIndex() { const BrowserLayout L = layoutBrowser(560, 300); const Rect r = cardCellRect(L, -1); CHECK(r.left == 0 && r.top == 0 && r.right == 0 && r.bottom == 0); } static void testCardContentInsetByGutter() { const BrowserLayout L = layoutBrowser(560, 300); const Rect cell = cardCellRect(L, 0); const Rect content = cardContentRect(L, 0); CHECK(content.left == cell.left + kBrowserCardGutter); CHECK(content.top == cell.top + kBrowserCardGutter); CHECK(content.right == cell.right - kBrowserCardGutter); CHECK(content.bottom == cell.bottom - kBrowserCardGutter); } static void testThumbnailAboveLabel() { const BrowserLayout L = layoutBrowser(560, 300); const Rect content = cardContentRect(L, 0); const Rect thumb = cardThumbnailRect(L, 0); const Rect label = cardLabelRect(L, 0); // Thumbnail is the top band of the content; the label is the remainder below it, contiguous. CHECK(thumb.left == content.left && thumb.right == content.right); CHECK(thumb.top == content.top); CHECK(thumb.height() == kBrowserThumbHeight); CHECK(label.top == thumb.bottom); CHECK(label.bottom == content.bottom); CHECK(label.left == content.left && label.right == content.right); } // --- cardHitTest -------------------------------------------------------------- static void testCardHitCenterOfCard() { const BrowserLayout L = layoutBrowser(560, 300); const Rect content = cardContentRect(L, 3); const int cx = content.left + content.width() / 2; const int cy = content.top + content.height() / 2; CHECK(cardHitTest(L, 12, cx, cy) == 3); } static void testCardHitMissesGutter() { const BrowserLayout L = layoutBrowser(560, 300); // A point in the gutter between the content and the cell edge (top-left corner of cell 0) // is a miss — only the card CONTENT counts. const Rect cell = cardCellRect(L, 0); CHECK(cardHitTest(L, 12, cell.left, cell.top) == -1); } static void testCardHitMissesPastLastCard() { const BrowserLayout L = layoutBrowser(560, 300); // Only 2 cards exist; a point on where card 5 WOULD be is a miss. const Rect content = cardContentRect(L, 5); const int cx = content.left + content.width() / 2; const int cy = content.top + content.height() / 2; CHECK(cardHitTest(L, 2, cx, cy) == -1); } static void testCardHitMissesTabStrip() { const BrowserLayout L = layoutBrowser(560, 300); CHECK(cardHitTest(L, 12, 10, L.tabStrip.top + 2) == -1); } static void testCardHitZeroCards() { const BrowserLayout L = layoutBrowser(560, 300); CHECK(cardHitTest(L, 0, 20, 40) == -1); } // --- filter tabs -------------------------------------------------------------- static void testFilterTabsTileStrip() { const BrowserLayout L = layoutBrowser(560, 300); const int n = 4; // "All" + 3 banks const Rect t0 = filterTabRect(L, n, 0); const Rect tLast = filterTabRect(L, n, n - 1); CHECK(t0.left == L.tabStrip.left); // Adjacent tabs share an exact edge (no gap). CHECK(filterTabRect(L, n, 0).right == filterTabRect(L, n, 1).left); CHECK(filterTabRect(L, n, 1).right == filterTabRect(L, n, 2).left); // The last tab reaches the strip's right edge exactly (absorbs the remainder). CHECK(tLast.right == L.tabStrip.right); // All tabs share the strip's height. CHECK(t0.top == L.tabStrip.top && t0.bottom == L.tabStrip.bottom); } static void testFilterTabOutOfRange() { const BrowserLayout L = layoutBrowser(560, 300); CHECK(filterTabRect(L, 3, -1).width() == 0); CHECK(filterTabRect(L, 3, 3).width() == 0); CHECK(filterTabRect(L, 0, 0).width() == 0); } static void testFilterTabHit() { const BrowserLayout L = layoutBrowser(560, 300); const int n = 3; for (int i = 0; i < n; ++i) { const Rect t = filterTabRect(L, n, i); const int cx = t.left + t.width() / 2; const int cy = t.top + t.height() / 2; CHECK(filterTabHitTest(L, n, cx, cy) == i); } // Below the strip (in the grid) -> no tab. CHECK(filterTabHitTest(L, n, 20, L.grid.top + 4) == -1); } int main() { testLayoutNormalArea(); testLayoutNarrowAreaSingleColumn(); testLayoutZeroArea(); testLayoutTinyHeightClampsTabStrip(); testCardCellsTileRowMajor(); testCardCellNegativeIndex(); testCardContentInsetByGutter(); testThumbnailAboveLabel(); testCardHitCenterOfCard(); testCardHitMissesGutter(); testCardHitMissesPastLastCard(); testCardHitMissesTabStrip(); testCardHitZeroCards(); testFilterTabsTileStrip(); testFilterTabOutOfRange(); testFilterTabHit(); if (g_fail == 0) std::printf("capture_browser: all tests passed\n"); return g_fail != 0; }