#include "main.h" #include auto ncreel_validate(const ncreel* n) -> bool { if(n->tablets == NULL){ return true; } const nctablet* t = n->tablets; int cury = -1; bool wentaround = false; do{ const ncplane* np = t->p; if(np){ int y, x; ncplane_yx(np, &y, &x); //fprintf(stderr, "forvart: %p (%p) @ %d\n", t, np, y); if(y < cury + 1){ if(wentaround){ return false; } wentaround = true; }else if(y == cury){ return false; } int ylen, xlen; ncplane_dim_yx(np, &ylen, &xlen); cury = y + ylen - 1; } }while((t = t->next) != n->tablets); cury = INT_MAX; wentaround = false; do{ const ncplane* np = t->p; if(np){ int y, x, ylen, xlen; ncplane_yx(np, &y, &x); ncplane_dim_yx(np, &ylen, &xlen); //fprintf(stderr, "backwards: %p (%p) @ %d\n", t, np, y); if(y + ylen - 1 > cury - 1){ if(wentaround){ return false; } wentaround = true; }else if(y == cury){ return false; } cury = y; } }while((t = t->prev) != n->tablets); return true; } auto panelcb(struct nctablet* t, bool toptobottom) -> int { CHECK(nctablet_ncplane(t)); CHECK(!nctablet_userptr(t)); CHECK(toptobottom); // FIXME verify geometry is as expected return 0; } auto cbfxn(struct nctablet* t, bool toptobottom) -> int { (void)toptobottom; int* userptr = static_cast(nctablet_userptr(t)); ++*userptr; return 4; } TEST_CASE("Reels") { auto nc_ = testing_notcurses(); if(!nc_){ return; } struct ncplane* n_ = notcurses_stdplane(nc_); REQUIRE(n_); REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0)); SUBCASE("InitLinear") { ncreel_options r = { }; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK(0 == notcurses_render(nc_)); CHECK(0 == ncreel_destroy(nr)); } SUBCASE("InitLinearInfinite") { ncreel_options r{}; r.flags = NCREEL_OPTION_INFINITESCROLL; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK(0 == notcurses_render(nc_)); CHECK(0 == ncreel_destroy(nr)); } SUBCASE("InitCircular") { ncreel_options r{}; r.flags = NCREEL_OPTION_INFINITESCROLL | NCREEL_OPTION_CIRCULAR; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK(ncreel_validate(nr)); CHECK(0 == notcurses_render(nc_)); REQUIRE(0 == ncreel_destroy(nr)); } // circular is not allowed to be true when infinitescroll is false SUBCASE("FiniteCircleRejected") { ncreel_options r{}; r.flags = NCREEL_OPTION_CIRCULAR; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(!nr); CHECK(0 == notcurses_render(nc_)); REQUIRE(0 == ncreel_destroy(nr)); } // We ought be able to invoke ncreel_next() and ncreel_prev() safely, // even if there are no tablets. They both ought return nullptr. SUBCASE("MovementWithoutTablets") { ncreel_options r{}; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK(!ncreel_next(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(!ncreel_prev(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); REQUIRE(0 == ncreel_destroy(nr)); } SUBCASE("OneTablet") { ncreel_options r{}; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); struct nctablet* t = ncreel_add(nr, nullptr, nullptr, panelcb, nullptr); REQUIRE(t); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_del(nr, t)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); REQUIRE(0 == ncreel_destroy(nr)); } SUBCASE("MovementWithOneTablet") { ncreel_options r{}; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); struct nctablet* t = ncreel_add(nr, nullptr, nullptr, panelcb, nullptr); REQUIRE(t); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(ncreel_next(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(ncreel_prev(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_del(nr, t)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); REQUIRE(0 == ncreel_destroy(nr)); } SUBCASE("DeleteActiveTablet") { ncreel_options r{}; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); struct nctablet* t = ncreel_add(nr, nullptr, nullptr, panelcb, nullptr); REQUIRE(t); CHECK(0 == ncreel_del(nr, ncreel_focused(nr))); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); REQUIRE(0 == ncreel_destroy(nr)); } SUBCASE("NoBorder") { ncreel_options r{}; r.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT | NCBOXMASK_TOP | NCBOXMASK_BOTTOM; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, ncreel_redraw(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_destroy(nr)); } SUBCASE("BadBorderBitsRejected") { ncreel_options r{}; r.bordermask = NCBOXMASK_LEFT * 2; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(!nr); } SUBCASE("NoTabletBorder") { ncreel_options r{}; r.tabletmask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT | NCBOXMASK_TOP | NCBOXMASK_BOTTOM; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, ncreel_redraw(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_destroy(nr)); } SUBCASE("NoTopBottomBorder") { ncreel_options r{}; r.bordermask = NCBOXMASK_TOP | NCBOXMASK_BOTTOM; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, ncreel_redraw(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_destroy(nr)); } SUBCASE("NoSideBorders") { ncreel_options r{}; r.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, ncreel_redraw(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_destroy(nr)); } SUBCASE("BadTabletBorderBitsRejected") { ncreel_options r{}; r.tabletmask = NCBOXMASK_LEFT * 2; struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(!nr); } SUBCASE("TransparentBackground") { ncreel_options r{}; channels_set_bg_alpha(&r.bgchannel, 3); struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, ncreel_redraw(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); CHECK(0 == ncreel_destroy(nr)); } // Layout tests. Add some tablets, move around, and verify that they all // have the expected locations/contents/geometries. SUBCASE("ThreeCycleDown") { ncreel_options r{}; channels_set_bg_alpha(&r.bgchannel, 3); struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, ncreel_redraw(nr)); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); int order[3]; nctablet* tabs[3]; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ order[n] = -1; tabs[n] = ncreel_add(nr, nullptr, nullptr, cbfxn, &order[n]); REQUIRE(tabs[n]); CHECK(tabs[0] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); } int expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(0, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 7; } ncreel_next(nr); CHECK(tabs[1] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(1, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 7; } ncreel_next(nr); CHECK(tabs[2] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(2, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 7; } ncreel_prev(nr); CHECK(tabs[1] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(3, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 7; } ncreel_prev(nr); CHECK(tabs[0] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(4, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 7; } CHECK(0 == ncreel_destroy(nr)); } // Layout tests. Add some tablets, move around, and verify that they all // have the expected locations/contents/geometries. SUBCASE("ThreeCycleDownNoTabletBorders") { ncreel_options r{}; r.tabletmask = 0xf; channels_set_bg_alpha(&r.bgchannel, 3); struct ncreel* nr = ncreel_create(n_, &r); REQUIRE(nr); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); int order[3]; nctablet* tabs[3]; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ order[n] = -1; tabs[n] = ncreel_add(nr, nullptr, nullptr, cbfxn, &order[n]); REQUIRE(tabs[n]); CHECK(tabs[0] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); } int expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(-1, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 5; } ncreel_next(nr); CHECK(tabs[1] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(1, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 5; } ncreel_next(nr); CHECK(tabs[2] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(2, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 5; } ncreel_prev(nr); CHECK(tabs[1] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(3, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 5; } ncreel_prev(nr); CHECK(tabs[0] == nr->tablets); CHECK_EQ(0, notcurses_render(nc_)); CHECK(ncreel_validate(nr)); expectedy = 1; for(size_t n = 0 ; n < sizeof(order) / sizeof(*order) ; ++n){ CHECK_LE(4, order[n]); int y; ncplane_yx(ncplane_parent(nctablet_ncplane(tabs[n])), &y, nullptr); CHECK(y == expectedy); expectedy += 5; } CHECK(0 == ncreel_destroy(nr)); } CHECK(0 == notcurses_stop(nc_)); }