Out with googletest, in with doctest #202 (#231)

* introduce doctest over googletest #202
* call dtester in in targets
* doctest conversion #202
* channel.cpp -> doctest #202
* egcpool tests -> doctest #202
* input tests to doctester
* zaxis -> doctest
* drone: always define LANG
* libav to doctest #202
* panelreel tests to doctest #202
* spec that a C++17 compiler is now required for doctest #202
* enmetric tests -> doctest #202
* fade tests -> doctest #202
* notcurses test case -> doctest #202
* last conversion to doctest #202
* finish move to doctest #202
* drone: set up make test
pull/232/head
Nick Black 5 years ago committed by GitHub
parent d4caefc55d
commit ce2a390b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -37,3 +37,4 @@ steps:
- cd build
- cmake .. -DCMAKE_BUILD_TYPE=Release
- make
- env LANG="en_US.UTF-8" make test

@ -6,7 +6,7 @@ project(notcurses VERSION 0.9.3
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
include(GNUInstallDirs)
@ -212,7 +212,7 @@ endif()
# Testing
file(GLOB TESTSRCS CONFIGURE_DEPENDS tests/*.cpp)
add_executable(notcurses-tester ${TESTSRCS})
find_package(GTest 1.9 REQUIRED)
find_package(doctest REQUIRED doctest>=1.2.7)
target_include_directories(notcurses-tester
PRIVATE
include
@ -220,19 +220,24 @@ target_include_directories(notcurses-tester
src/lib
)
target_link_libraries(notcurses-tester
GTest::GTest
notcurses
PRIVATE
doctest::doctest
notcurses
)
target_compile_options(notcurses-tester PRIVATE
-Wall -Wextra -W -Wshadow
target_compile_options(notcurses-tester
PRIVATE
-Wall -Wextra -W -Wshadow
)
target_compile_definitions(notcurses-tester
PRIVATE
FORTIFY_SOURCE=2
)
gtest_discover_tests(notcurses-tester)
enable_testing()
add_test(
NAME notcurses-tester
COMMAND notcurses-tester
)
# pkg-config support
configure_file(tools/notcurses.pc.in

@ -101,7 +101,7 @@ that fine library.
## Requirements
* A C11 and a C++14 compiler
* A C11 and a C++17 compiler
* CMake 3.13.0+
* From NCURSES: terminfo 6.1+
* (OPTIONAL) From FFMpeg: libswscale 5.0+, libavformat 57.0+, libavutil 56.0+

2
debian/control vendored

@ -3,7 +3,7 @@ Priority: optional
Maintainer: Nick Black <dankamongmen@gmail.com>
Build-Depends: debhelper-compat (= 12), cmake (>= 3.13), pkg-config (>= 0.29),
libgtest-dev (>= 1.8.0), libncurses-dev (>= 6.1), libavformat-dev (>= 57.0),
libswscale-dev (>= 5.0), libavutil-dev (>= 56.0)
libswscale-dev (>= 5.0), libavutil-dev (>= 56.0), doctest-dev (>= 1.2.7)
Standards-Version: 4.4.1.1
Section: libs
Homepage: https://nick-black.com/dankwiki/index.php/notcurses

@ -10,13 +10,6 @@
#define BLOCKSIZE 512 // show this many per page
#define CHUNKSIZE 32 // show this many per line
static int
fade_block(struct ncplane* nn, const struct timespec* subdelay){
int ret = ncplane_fadein(nn, subdelay, demo_fader);
ncplane_destroy(nn);
return ret;
}
static int
draw_block(struct ncplane* nn, uint32_t blockstart){
cell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
@ -204,9 +197,9 @@ int unicodeblocks_demo(struct notcurses* nc){
if(ncplane_printf_aligned(n, 6 + BLOCKSIZE / CHUNKSIZE, NCALIGN_CENTER, "%s", description) <= 0){
return -1;
}
if(fade_block(nn, &subdelay)){ // destroys nn
return -1;
}
notcurses_render(nc);
nanosleep(&subdelay, NULL);
ncplane_destroy(nn);
// for a 32-bit wchar_t, we would want up through 24 bits of block ID. but
// really, the vast majority of space is unused.
blockstart += BLOCKSIZE;

@ -99,10 +99,6 @@ int ncplane_fadein(ncplane* n, const struct timespec* ts, fadecb fader){
int maxsteps = maxfsteps > maxbsteps ? maxfsteps : maxbsteps;
uint64_t nanosecs_total = ts->tv_sec * NANOSECS_IN_SEC + ts->tv_nsec;
uint64_t nanosecs_step = nanosecs_total / maxsteps;
while(nanosecs_step < 10000000){ // 10msec
nanosecs_step *= 10;
maxsteps = nanosecs_total / nanosecs_step;
}
struct timespec times;
clock_gettime(CLOCK_MONOTONIC, &times);
// Start time in absolute nanoseconds
@ -178,10 +174,6 @@ int ncplane_fadeout(struct ncplane* n, const struct timespec* ts, fadecb fader){
int maxsteps = maxfsteps > maxbsteps ? maxfsteps : maxbsteps;
uint64_t nanosecs_total = ts->tv_sec * NANOSECS_IN_SEC + ts->tv_nsec;
uint64_t nanosecs_step = nanosecs_total / maxsteps;
while(nanosecs_step < 10000000){ // 10msec
nanosecs_step *= 10;
maxsteps = nanosecs_total / nanosecs_step;
}
struct timespec times;
clock_gettime(CLOCK_MONOTONIC, &times);
// Start time in absolute nanoseconds

@ -2,119 +2,109 @@
#include "egcpool.h"
#include "main.h"
class CellTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
n_ = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, n_);
TEST_CASE("Cell") {
// common initialization
if(getenv("TERM") == nullptr){
return;
}
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
}
if(outfp_){
fclose(outfp_);
}
}
struct notcurses* nc_{};
struct ncplane* n_{};
FILE* outfp_{};
};
outfp_ = fopen("/dev/tty", "wb");
REQUIRE(nullptr != outfp_);
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nullptr != nc_);
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(nullptr != n_);
TEST_F(CellTest, LoadSimple) {
cell c = CELL_TRIVIAL_INITIALIZER;
ASSERT_EQ(1, cell_load(n_, &c, " "));
EXPECT_TRUE(cell_simple_p(&c));
cell_release(n_, &c);
}
SUBCASE("LoadSimple") {
cell c = CELL_TRIVIAL_INITIALIZER;
REQUIRE(1 == cell_load(n_, &c, " "));
CHECK(cell_simple_p(&c));
cell_release(n_, &c);
}
TEST_F(CellTest, SetItalic) {
SUBCASE("SetItalic") {
cell c = CELL_TRIVIAL_INITIALIZER;
int dimy, dimx;
notcurses_term_dim_yx(nc_, &dimy, &dimx);
cell_styles_set(&c, CELL_STYLE_ITALIC);
ASSERT_EQ(1, cell_load(n_, &c, "i"));
cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_default(n_, &c);
cell_release(n_, &c);
EXPECT_EQ(0, notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_ITALIC);
}
int dimy, dimx;
notcurses_term_dim_yx(nc_, &dimy, &dimx);
cell_styles_set(&c, CELL_STYLE_ITALIC);
REQUIRE(1 == cell_load(n_, &c, "i"));
cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_default(n_, &c);
cell_release(n_, &c);
CHECK(0 == notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_ITALIC);
}
TEST_F(CellTest, SetBold) {
cell c = CELL_TRIVIAL_INITIALIZER;
int dimy, dimx;
notcurses_term_dim_yx(nc_, &dimy, &dimx);
cell_styles_set(&c, CELL_STYLE_BOLD);
ASSERT_EQ(1, cell_load(n_, &c, "b"));
cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_default(n_, &c);
cell_release(n_, &c);
EXPECT_EQ(0, notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_BOLD);
}
SUBCASE("SetBold") {
cell c = CELL_TRIVIAL_INITIALIZER;
int dimy, dimx;
notcurses_term_dim_yx(nc_, &dimy, &dimx);
cell_styles_set(&c, CELL_STYLE_BOLD);
REQUIRE(1 == cell_load(n_, &c, "b"));
cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_default(n_, &c);
cell_release(n_, &c);
CHECK(0 == notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_BOLD);
}
TEST_F(CellTest, SetUnderline) {
cell c = CELL_TRIVIAL_INITIALIZER;
int dimy, dimx;
notcurses_term_dim_yx(nc_, &dimy, &dimx);
cell_styles_set(&c, CELL_STYLE_UNDERLINE);
ASSERT_EQ(1, cell_load(n_, &c, "u"));
cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_default(n_, &c);
cell_release(n_, &c);
EXPECT_EQ(0, notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_UNDERLINE);
}
SUBCASE("SetUnderline") {
cell c = CELL_TRIVIAL_INITIALIZER;
int dimy, dimx;
notcurses_term_dim_yx(nc_, &dimy, &dimx);
cell_styles_set(&c, CELL_STYLE_UNDERLINE);
REQUIRE(1 == cell_load(n_, &c, "u"));
cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_default(n_, &c);
cell_release(n_, &c);
CHECK(0 == notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_UNDERLINE);
}
/*TEST_F(CellTest, CellLoadTamil) {
/* SUBCASE("CellLoadTamil") {
const char zerodeg[] = "\u0bb8\u0bc0\u0bb0\u0bc7\u0bb3\u0b95\u0bbf\u0b95\u0bbf\u0bb0\u0bbf";
cell c = CELL_TRIVIAL_INITIALIZER;
size_t ulen = cell_load(n_, &c, zerodeg);
// First have U+0BB8 TAMIL LETTER SA U+0BC0 TAMIL VOWEL SIGN II
// // e0 ae b8 e0 af 80
ASSERT_EQ(6, ulen);
REQUIRE(6 == ulen);
ulen = cell_load(n_, &c, zerodeg + ulen);
// U+0BB0 TAMIL LETTER RA U+0BCB TAMIL VOWEL SIGN OO
// e0 ae b0 e0 af 8b
ASSERT_EQ(6, ulen);
REQUIRE(6 == ulen);
// FIXME
}*/
}*/
TEST_F(CellTest, CellSetFGAlpha){
cell c = CELL_TRIVIAL_INITIALIZER;
EXPECT_GT(0, cell_set_fg_alpha(&c, -1));
EXPECT_GT(0, cell_set_fg_alpha(&c, 4));
EXPECT_EQ(0, cell_set_fg_alpha(&c, CELL_ALPHA_OPAQUE));
EXPECT_EQ(CELL_ALPHA_OPAQUE, cell_get_fg_alpha(&c));
EXPECT_EQ(0, cell_set_fg_alpha(&c, CELL_ALPHA_HIGHCONTRAST));
EXPECT_EQ(CELL_ALPHA_HIGHCONTRAST, cell_get_fg_alpha(&c));
EXPECT_TRUE(cell_fg_default_p(&c));
EXPECT_TRUE(cell_bg_default_p(&c));
}
SUBCASE("CellSetFGAlpha"){
cell c = CELL_TRIVIAL_INITIALIZER;
CHECK(0 > cell_set_fg_alpha(&c, -1));
CHECK(0 > cell_set_fg_alpha(&c, 4));
CHECK(0 == cell_set_fg_alpha(&c, CELL_ALPHA_OPAQUE));
CHECK(CELL_ALPHA_OPAQUE == cell_get_fg_alpha(&c));
CHECK(0 == cell_set_fg_alpha(&c, CELL_ALPHA_HIGHCONTRAST));
CHECK(CELL_ALPHA_HIGHCONTRAST == cell_get_fg_alpha(&c));
CHECK(cell_fg_default_p(&c));
CHECK(cell_bg_default_p(&c));
}
TEST_F(CellTest, CellSetBGAlpha){
cell c = CELL_TRIVIAL_INITIALIZER;
EXPECT_GT(0, cell_set_bg_alpha(&c, -1));
EXPECT_GT(0, cell_set_bg_alpha(&c, 4));
EXPECT_EQ(0, cell_set_bg_alpha(&c, CELL_ALPHA_OPAQUE));
EXPECT_EQ(CELL_ALPHA_OPAQUE, cell_get_bg_alpha(&c));
EXPECT_NE(0, cell_set_bg_alpha(&c, CELL_ALPHA_HIGHCONTRAST));
EXPECT_EQ(0, cell_set_bg_alpha(&c, CELL_ALPHA_TRANSPARENT));
EXPECT_EQ(CELL_ALPHA_TRANSPARENT, cell_get_bg_alpha(&c));
EXPECT_TRUE(cell_fg_default_p(&c));
EXPECT_TRUE(cell_bg_default_p(&c));
SUBCASE("CellSetBGAlpha"){
cell c = CELL_TRIVIAL_INITIALIZER;
CHECK(0 > cell_set_bg_alpha(&c, -1));
CHECK(0 > cell_set_bg_alpha(&c, 4));
CHECK(0 == cell_set_bg_alpha(&c, CELL_ALPHA_OPAQUE));
CHECK(CELL_ALPHA_OPAQUE == cell_get_bg_alpha(&c));
CHECK(0 != cell_set_bg_alpha(&c, CELL_ALPHA_HIGHCONTRAST));
CHECK(0 == cell_set_bg_alpha(&c, CELL_ALPHA_TRANSPARENT));
CHECK(CELL_ALPHA_TRANSPARENT == cell_get_bg_alpha(&c));
CHECK(cell_fg_default_p(&c));
CHECK(cell_bg_default_p(&c));
}
// common teardown
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
}

@ -1,14 +1,7 @@
#include <notcurses.h>
#include "main.h"
class ChannelTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
}
};
TEST_F(ChannelTest, ChannelGetRGB){
TEST_CASE("ChannelGetRGB") {
const struct t {
uint32_t channel;
int r, g, b;
@ -20,14 +13,14 @@ TEST_F(ChannelTest, ChannelGetRGB){
};
for(auto i = 0u ; i < sizeof(test) / sizeof(*test) ; ++i){
unsigned r, g, b;
EXPECT_EQ(test[i].channel, channel_get_rgb(test[i].channel, &r, &g, &b));
EXPECT_EQ(test[i].r, r);
EXPECT_EQ(test[i].g, g);
EXPECT_EQ(test[i].b, b);
CHECK(test[i].channel == channel_get_rgb(test[i].channel, &r, &g, &b));
CHECK(test[i].r == r);
CHECK(test[i].g == g);
CHECK(test[i].b == b);
}
}
TEST_F(ChannelTest, ChannelGetAlpha){
TEST_CASE("ChannelGetAlpha") {
const struct t {
uint32_t channel;
int a;
@ -40,11 +33,11 @@ TEST_F(ChannelTest, ChannelGetAlpha){
{ .channel = 0xffffffff, .a = 3, },
};
for(auto i = 0u ; i < sizeof(test) / sizeof(*test) ; ++i){
EXPECT_EQ(test[i].a, channel_get_alpha(test[i].channel));
CHECK(test[i].a == channel_get_alpha(test[i].channel));
}
}
TEST_F(ChannelTest, ChannelGetDefault){
TEST_CASE("ChannelGetDefault") {
const struct t {
uint32_t channel;
bool def;
@ -57,19 +50,19 @@ TEST_F(ChannelTest, ChannelGetDefault){
{ .channel = 0xffffffff, .def = false, },
};
for(auto i = 0u ; i < sizeof(test) / sizeof(*test) ; ++i){
EXPECT_EQ(test[i].def, channel_default_p(test[i].channel));
CHECK(test[i].def == channel_default_p(test[i].channel));
}
}
TEST_F(ChannelTest, ChannelSetDefault){
TEST_CASE("ChannelSetDefault") {
const uint32_t channels[] = {
0x40000000, 0x4fffffff, 0xcfffffff,
0x40808080, 0x40080808, 0xffffffff,
};
for(auto i = 0u ; i < sizeof(channels) / sizeof(*channels) ; ++i){
uint32_t channel = channels[i];
EXPECT_FALSE(channel_default_p(channel));
CHECK(!channel_default_p(channel));
channel_set_default(&channel);
EXPECT_TRUE(channel_default_p(channel));
CHECK(channel_default_p(channel));
}
}

File diff suppressed because it is too large Load Diff

@ -1,211 +1,208 @@
#include <vector>
#include <notcurses.h>
#include "egcpool.h"
#include "main.h"
class EGCPoolTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
}
void TearDown() override {
egcpool_dump(&pool_);
}
TEST_CASE("EGCpool") {
egcpool pool_{};
};
TEST_F(EGCPoolTest, Initialized) {
EXPECT_EQ(nullptr, pool_.pool);
EXPECT_EQ(0, pool_.poolsize);
EXPECT_EQ(0, pool_.poolwrite);
EXPECT_EQ(0, pool_.poolused);
}
SUBCASE("Initialized") {
CHECK(!pool_.pool);
CHECK(!pool_.poolsize);
CHECK(!pool_.poolwrite);
CHECK(!pool_.poolused);
}
TEST_F(EGCPoolTest, UTF8EGC) {
const char* wstr = "";
int c;
auto ulen = utf8_egc_len(wstr, &c);
ASSERT_LT(0, ulen);
EXPECT_LT(0, c);
wstr = "";
ulen = utf8_egc_len(wstr, &c);
ASSERT_LT(0, ulen);
EXPECT_LT(0, c);
}
SUBCASE("UTF8EGC") {
const char* wstr = "";
int c;
auto ulen = utf8_egc_len(wstr, &c);
REQUIRE(0 < ulen);
CHECK(0 < c);
wstr = "";
ulen = utf8_egc_len(wstr, &c);
REQUIRE(0 < ulen);
CHECK(0 < c);
}
// we're gonna run both a composed latin a with grave, and then a latin a with
// a combining nonspacing grave
TEST_F(EGCPoolTest, UTF8EGCCombining) {
const char* w1 = "\u00e0"; // (utf8: c3 a0)
const char* w2 = "\u0061\u0300"; // (utf8: 61 cc 80)
const char* w3 = "\u0061"; // (utf8: 61)
int c1, c2, c3;
auto u1 = utf8_egc_len(w1, &c1);
auto u2 = utf8_egc_len(w2, &c2);
auto u3 = utf8_egc_len(w3, &c3);
ASSERT_EQ(2, u1);
ASSERT_EQ(3, u2);
ASSERT_EQ(1, u3);
ASSERT_EQ(1, c1);
ASSERT_EQ(1, c2);
ASSERT_EQ(1, c3);
}
// we're gonna run both a composed latin a with grave, and then a latin a with
// a combining nonspacing grave
SUBCASE("UTF8EGCCombining") {
const char* w1 = "\u00e0"; // (utf8: c3 a0)
const char* w2 = "\u0061\u0300"; // (utf8: 61 cc 80)
const char* w3 = "\u0061"; // (utf8: 61)
int c1, c2, c3;
auto u1 = utf8_egc_len(w1, &c1);
auto u2 = utf8_egc_len(w2, &c2);
auto u3 = utf8_egc_len(w3, &c3);
REQUIRE(2 == u1);
REQUIRE(3 == u2);
REQUIRE(1 == u3);
REQUIRE(1 == c1);
REQUIRE(1 == c2);
REQUIRE(1 == c3);
}
TEST_F(EGCPoolTest, AddAndRemove) {
const char* wstr = "\ufdfd"; // bismallih
int c;
auto ulen = utf8_egc_len(wstr, &c);
ASSERT_LE(0, egcpool_stash(&pool_, wstr, ulen));
ASSERT_EQ(1, c); // not considered wide, believe it or not
EXPECT_NE(nullptr, pool_.pool);
EXPECT_STREQ(pool_.pool, wstr);
EXPECT_LT(0, pool_.poolsize);
EXPECT_EQ(ulen + 1, pool_.poolused);
EXPECT_LT(0, pool_.poolwrite);
EXPECT_LE(pool_.poolused, pool_.poolsize);
egcpool_release(&pool_, 0);
EXPECT_EQ('\0', *pool_.pool);
EXPECT_LT(0, pool_.poolsize);
EXPECT_EQ(0, pool_.poolused);
EXPECT_LT(0, pool_.poolwrite);
}
SUBCASE("AddAndRemove") {
const char* wstr = "\ufdfd"; // bismallih
int c;
auto ulen = utf8_egc_len(wstr, &c);
REQUIRE(0 <= egcpool_stash(&pool_, wstr, ulen));
REQUIRE(1 == c); // not considered wide, believe it or not
CHECK(pool_.pool);
CHECK(!strcmp(pool_.pool, wstr));
CHECK(0 < pool_.poolsize);
CHECK(ulen + 1 == pool_.poolused);
CHECK(0 < pool_.poolwrite);
CHECK(pool_.poolused <= pool_.poolsize);
egcpool_release(&pool_, 0);
CHECK_EQ('\0', *pool_.pool);
CHECK(0 < pool_.poolsize);
CHECK(0 == pool_.poolused);
CHECK(0 < pool_.poolwrite);
}
TEST_F(EGCPoolTest, AddTwiceRemoveFirst) {
const char* wstr = "\u8840"; // cjk unified ideograph, wide
int c1, c2; // column counts
auto u1 = utf8_egc_len(wstr, &c1); // bytes consumed
auto u2 = utf8_egc_len(wstr, &c2);
int o1 = egcpool_stash(&pool_, wstr, u1);
int o2 = egcpool_stash(&pool_, wstr, u2);
ASSERT_LE(0, o1);
ASSERT_LT(o1, o2);
ASSERT_EQ(2, c1);
ASSERT_EQ(c1, c2);
EXPECT_NE(nullptr, pool_.pool);
EXPECT_STREQ(pool_.pool + o1, wstr);
EXPECT_STREQ(pool_.pool + o2, wstr);
EXPECT_LT(0, pool_.poolsize);
EXPECT_EQ(u1 + u2 + 2, pool_.poolused);
EXPECT_EQ(u1 + u2 + 2, pool_.poolwrite);
EXPECT_LE(pool_.poolused, pool_.poolsize);
egcpool_release(&pool_, o1);
EXPECT_EQ('\0', pool_.pool[o1]);
EXPECT_EQ(u2 + 1, pool_.poolused);
EXPECT_LT(0, pool_.poolwrite);
}
SUBCASE("AddTwiceRemoveFirst") {
const char* wstr = "\u8840"; // cjk unified ideograph, wide
int c1, c2; // column counts
auto u1 = utf8_egc_len(wstr, &c1); // bytes consumed
auto u2 = utf8_egc_len(wstr, &c2);
int o1 = egcpool_stash(&pool_, wstr, u1);
int o2 = egcpool_stash(&pool_, wstr, u2);
REQUIRE(0 <= o1);
REQUIRE(o1 < o2);
REQUIRE(2 == c1);
REQUIRE(c1 == c2);
CHECK(pool_.pool);
CHECK(!strcmp(pool_.pool + o1, wstr));
CHECK(!strcmp(pool_.pool + o2, wstr));
CHECK(0 < pool_.poolsize);
CHECK(u1 + u2 + 2 == pool_.poolused);
CHECK(u1 + u2 + 2 == pool_.poolwrite);
CHECK(pool_.poolused < pool_.poolsize);
egcpool_release(&pool_, o1);
CHECK('\0' == pool_.pool[o1]);
CHECK(u2 + 1 == pool_.poolused);
CHECK(0 < pool_.poolwrite);
}
TEST_F(EGCPoolTest, AddTwiceRemoveSecond) {
const char* wstr = "\u8840"; // cjk unified ideograph, wide
int c1, c2; // column counts
auto u1 = utf8_egc_len(wstr, &c1); // bytes consumed
auto u2 = utf8_egc_len(wstr, &c2);
int o1 = egcpool_stash(&pool_, wstr, u1);
int o2 = egcpool_stash(&pool_, wstr, u2);
ASSERT_LT(o1, o2);
ASSERT_EQ(2, c1);
ASSERT_EQ(c1, c2);
EXPECT_NE(nullptr, pool_.pool);
EXPECT_STREQ(pool_.pool + o1, wstr);
EXPECT_STREQ(pool_.pool + o2, wstr);
EXPECT_LT(0, pool_.poolsize);
EXPECT_EQ(u1 + u2 + 2, pool_.poolused);
EXPECT_EQ(u1 + u2 + 2, pool_.poolwrite);
EXPECT_LE(pool_.poolused, pool_.poolsize);
egcpool_release(&pool_, o2);
EXPECT_EQ('\0', pool_.pool[o2]);
EXPECT_EQ(u2 + 1, pool_.poolused);
EXPECT_LT(0, pool_.poolwrite);
}
SUBCASE("AddTwiceRemoveSecond") {
const char* wstr = "\u8840"; // cjk unified ideograph, wide
int c1, c2; // column counts
auto u1 = utf8_egc_len(wstr, &c1); // bytes consumed
auto u2 = utf8_egc_len(wstr, &c2);
int o1 = egcpool_stash(&pool_, wstr, u1);
int o2 = egcpool_stash(&pool_, wstr, u2);
REQUIRE(o1 < o2);
REQUIRE(2 == c1);
REQUIRE(c1 == c2);
CHECK(pool_.pool);
CHECK(!strcmp(pool_.pool + o1, wstr));
CHECK(!strcmp(pool_.pool + o2, wstr));
CHECK(0 < pool_.poolsize);
CHECK(u1 + u2 + 2 == pool_.poolused);
CHECK(u1 + u2 + 2 == pool_.poolwrite);
CHECK(pool_.poolused <= pool_.poolsize);
egcpool_release(&pool_, o2);
CHECK('\0' == pool_.pool[o2]);
CHECK(u2 + 1 == pool_.poolused);
CHECK(0 < pool_.poolwrite);
}
// POOL_MINIMUM_ALLOC is the minimum size of an egcpool once it goes active.
// add EGCs to it past this boundary, and verify that they're all still
// accurate.
TEST_F(EGCPoolTest, ForceReallocation) {
std::vector<int> candidates;
char* firstalloc = nullptr;
for(auto i = 0u ; i < 1u << 20u ; ++i){
char mb[MB_CUR_MAX + 1];
wchar_t wcs = i + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
candidates.push_back(-1);
continue;
}
ASSERT_GE(sizeof(mb), r);
mb[r] = '\0';
candidates.push_back(egcpool_stash(&pool_, mb, r));
ASSERT_GT(1u << 24u, candidates[i]);
if(!firstalloc){
firstalloc = pool_.pool;
// POOL_MINIMUM_ALLOC is the minimum size of an egcpool once it goes active.
// add EGCs to it past this boundary, and verify that they're all still
// accurate.
SUBCASE("ForceReallocation") {
std::vector<int> candidates;
char* firstalloc = nullptr;
for(auto i = 0u ; i < 1u << 20u ; ++i){
char mb[MB_CUR_MAX + 1];
wchar_t wcs = i + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
candidates.push_back(-1);
continue;
}
REQUIRE(sizeof(mb) >= r);
mb[r] = '\0';
candidates.push_back(egcpool_stash(&pool_, mb, r));
REQUIRE((1u << 24u) > candidates[i]);
if(!firstalloc){
firstalloc = pool_.pool;
}
}
}
// verify that we moved the pool at least once
ASSERT_NE(pool_.pool, firstalloc);
for(auto i = 0u ; i < candidates.size() ; ++i){
auto stored = pool_.pool + candidates[i];
char mb[MB_CUR_MAX + 1];
wchar_t wcs = i + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
ASSERT_EQ(-1, candidates[i]);
continue;
// verify that we moved the pool at least once
REQUIRE(pool_.pool != firstalloc);
for(auto i = 0u ; i < candidates.size() ; ++i){
auto stored = pool_.pool + candidates[i];
char mb[MB_CUR_MAX + 1];
wchar_t wcs = i + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
REQUIRE(-1 == candidates[i]);
continue;
}
REQUIRE(0 < r);
mb[r] = '\0';
CHECK(!strcmp(mb, stored));
}
ASSERT_LT(0, r);
mb[r] = '\0';
EXPECT_STREQ(mb, stored);
}
}
// POOL_MINIMUM_ALLOC is the minimum size of an egcpool once it goes active.
// add EGCs to it past this boundary, and verify that they're all still
// accurate.
TEST_F(EGCPoolTest, ForceReallocationWithRemovals) {
std::vector<int> candidates;
char* curpool = nullptr;
for(auto i = 0u ; i < 1u << 20u ; ++i){
char mb[MB_CUR_MAX + 1];
wchar_t wcs = (i % 0x1000) + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
candidates.push_back(-1);
continue;
}
ASSERT_GE(sizeof(mb), r);
mb[r] = '\0';
candidates.push_back(egcpool_stash(&pool_, mb, r));
// POOL_MINIMUM_ALLOC is the minimum size of an egcpool once it goes active.
// add EGCs to it past this boundary, and verify that they're all still
// accurate.
SUBCASE("ForceReallocationWithRemovals") {
std::vector<int> candidates;
char* curpool = nullptr;
for(auto i = 0u ; i < 1u << 20u ; ++i){
char mb[MB_CUR_MAX + 1];
wchar_t wcs = (i % 0x1000) + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
candidates.push_back(-1);
continue;
}
REQUIRE(sizeof(mb) >= r);
mb[r] = '\0';
candidates.push_back(egcpool_stash(&pool_, mb, r));
ASSERT_GT(1u << 24u, candidates[i]);
if(pool_.pool != curpool){
// cut through and release a bunch of them
if(curpool){
for(auto j = 0u ; j < i ; j += 13){
if(candidates[j] >= 0){
egcpool_release(&pool_, candidates[j]);
candidates[j] = -1;
REQUIRE((1u << 24u) > candidates[i]);
if(pool_.pool != curpool){
// cut through and release a bunch of them
if(curpool){
for(auto j = 0u ; j < i ; j += 13){
if(candidates[j] >= 0){
egcpool_release(&pool_, candidates[j]);
candidates[j] = -1;
}
}
}
curpool = pool_.pool;
}
curpool = pool_.pool;
}
}
int no = 0;
for(auto i = 0u ; i < candidates.size() ; ++i){
if(candidates[i] == -1){
++no;
continue;
}
auto stored = pool_.pool + candidates[i];
char mb[MB_CUR_MAX + 1];
wchar_t wcs = (i % 0x1000) + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
ASSERT_EQ(-1, candidates[i]);
continue;
int no = 0;
for(auto i = 0u ; i < candidates.size() ; ++i){
if(candidates[i] == -1){
++no;
continue;
}
auto stored = pool_.pool + candidates[i];
char mb[MB_CUR_MAX + 1];
wchar_t wcs = (i % 0x1000) + 0x80;
auto r = wctomb(mb, wcs);
if(r < 0){
REQUIRE(-1 == candidates[i]);
continue;
}
REQUIRE(0 < r);
mb[r] = '\0';
CHECK(!strcmp(mb, stored));
}
ASSERT_LT(0, r);
mb[r] = '\0';
EXPECT_STREQ(mb, stored);
CHECK(candidates.size() / 13 > no);
}
ASSERT_GT(candidates.size() / 13, no);
// common cleanup
egcpool_dump(&pool_);
}

@ -2,332 +2,323 @@
#include <cfenv>
#include <iostream>
class EnmetricTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
decisep_ = localeconv()->decimal_point;
ASSERT_NE(nullptr, decisep_);
ASSERT_EQ(1, strlen(decisep_));
}
void TearDown() override {
}
const char* decisep_{};
char* impericize_enmetric(uintmax_t val, unsigned decimal, char* buf,
int omitdec, unsigned mult, int uprefix);
};
// run enmetric, and then change any localized decimal separator into our
// proud imperial yankee capitalist democratic one dot under god period.
// manifest destiny, bitchhhhhhhezzzz!
char* EnmetricTest::impericize_enmetric(uintmax_t val, unsigned decimal,
char* buf, int omitdec, unsigned mult,
int uprefix) {
enmetric(val, decimal, buf, omitdec, mult, uprefix);
char* impericize_enmetric(uintmax_t val, unsigned decimal, char* buf,
int omitdec, unsigned mult, int uprefix) {
const char* decisep = localeconv()->decimal_point;
REQUIRE(decisep);
REQUIRE(1 == strlen(decisep));
REQUIRE(enmetric(val, decimal, buf, omitdec, mult, uprefix));
char* commie = buf;
while( (commie = strstr(commie, decisep_)) ){
while( (commie = strstr(commie, decisep)) ){
*commie = '.'; // https://dank.qemfd.net/images/16whcc.jpg
++commie;
}
return buf;
}
TEST_F(EnmetricTest, CornerInts) {
char buf[PREFIXSTRLEN + 1];
impericize_enmetric(0, 1, buf, 0, 1000, '\0');
EXPECT_STREQ("0.00", buf);
impericize_enmetric(0, 1, buf, 0, 1024, 'i');
EXPECT_STREQ("0.00", buf); // no suffix on < mult
impericize_enmetric(1, 1, buf, 0, 1000, '\0');
EXPECT_STREQ("1.00", buf);
impericize_enmetric(1, 1, buf, 0, 1024, 'i');
EXPECT_STREQ("1.00", buf);
impericize_enmetric(0, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("0", buf);
impericize_enmetric(0, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("0", buf); // no suffix on < mult
impericize_enmetric(1, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("1", buf);
impericize_enmetric(1, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("1", buf);
impericize_enmetric(999, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("999", buf);
impericize_enmetric(1000, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("1K", buf);
impericize_enmetric(1000, 1, buf, 1, 1000, 'i');
EXPECT_STREQ("1Ki", buf);
impericize_enmetric(1000, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("1000", buf); // FIXME should be 0.977Ki
impericize_enmetric(1023, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("1.02K", buf);
impericize_enmetric(1023, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("1023", buf);
impericize_enmetric(1024, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("1.02K", buf);
impericize_enmetric(1024, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("1Ki", buf);
impericize_enmetric(1025, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("1.02K", buf);
impericize_enmetric(1025, 1, buf, 0, 1024, 'i');
EXPECT_STREQ("1.00Ki", buf);
impericize_enmetric(1025, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("1.00Ki", buf);
impericize_enmetric(4096, 1, buf, 1, 1000, '\0');
EXPECT_STREQ("4.09K", buf);
impericize_enmetric(4096, 1, buf, 1, 1024, 'i');
EXPECT_STREQ("4Ki", buf);
}
TEST_CASE("Enmetric") {
const char* decisep = localeconv()->decimal_point;
REQUIRE(decisep);
REQUIRE(1 == strlen(decisep));
TEST_F(EnmetricTest, Maxints) {
char buf[PREFIXSTRLEN + 1];
// FIXME these will change based on the size of intmax_t and uintmax_t
impericize_enmetric(INTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
EXPECT_STREQ("9.22E", buf);
impericize_enmetric(INTMAX_MAX, 1, buf, 0, 1000, '\0');
EXPECT_STREQ("9.22E", buf);
impericize_enmetric(UINTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
EXPECT_STREQ("18.44E", buf);
impericize_enmetric(UINTMAX_MAX, 1, buf, 0, 1000, '\0');
EXPECT_STREQ("18.44E", buf);
}
SUBCASE("CornerInts") {
char buf[PREFIXSTRLEN + 1];
impericize_enmetric(0, 1, buf, 0, 1000, '\0');
CHECK(!strcmp("0.00", buf));
impericize_enmetric(0, 1, buf, 0, 1024, 'i');
CHECK(!strcmp("0.00", buf)); // no suffix on < mult
impericize_enmetric(1, 1, buf, 0, 1000, '\0');
CHECK(!strcmp("1.00", buf));
impericize_enmetric(1, 1, buf, 0, 1024, 'i');
CHECK(!strcmp("1.00", buf));
impericize_enmetric(0, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("0", buf));
impericize_enmetric(0, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("0", buf)); // no suffix on < mult
impericize_enmetric(1, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("1", buf));
impericize_enmetric(1, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("1", buf));
impericize_enmetric(999, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("999", buf));
impericize_enmetric(1000, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("1K", buf));
impericize_enmetric(1000, 1, buf, 1, 1000, 'i');
CHECK(!strcmp("1Ki", buf));
impericize_enmetric(1000, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("1000", buf)); // FIXME should be 0.977Ki
impericize_enmetric(1023, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("1.02K", buf));
impericize_enmetric(1023, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("1023", buf));
impericize_enmetric(1024, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("1.02K", buf));
impericize_enmetric(1024, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("1Ki", buf));
impericize_enmetric(1025, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("1.02K", buf));
impericize_enmetric(1025, 1, buf, 0, 1024, 'i');
CHECK(!strcmp("1.00Ki", buf));
impericize_enmetric(1025, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("1.00Ki", buf));
impericize_enmetric(4096, 1, buf, 1, 1000, '\0');
CHECK(!strcmp("4.09K", buf));
impericize_enmetric(4096, 1, buf, 1, 1024, 'i');
CHECK(!strcmp("4Ki", buf));
}
TEST_F(EnmetricTest, Maxints1024) {
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
char buf[PREFIXSTRLEN + 1], gold[PREFIXSTRLEN + 1];
// FIXME these will change based on the size of intmax_t and uintmax_t
enmetric(INTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - (1ull << 53))) / (1ull << 60));
EXPECT_STREQ(gold, buf);
enmetric(INTMAX_MAX + 1ull, 1, buf, 0, 1024, 'i');
sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX + 1ull)) / (1ull << 60));
EXPECT_STREQ(gold, buf);
impericize_enmetric(UINTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
EXPECT_STREQ("15.99Ei", buf);
impericize_enmetric(UINTMAX_MAX, 1, buf, 0, 1024, 'i');
EXPECT_STREQ("15.99Ei", buf);
enmetric(UINTMAX_MAX - (1ull << 53), 1, buf, 0, 1024, 'i');
sprintf(gold, "%.2fEi", ((double)UINTMAX_MAX - (1ull << 53)) / (1ull << 60));
EXPECT_STREQ(gold, buf);
}
SUBCASE("Maxints") {
char buf[PREFIXSTRLEN + 1];
// FIXME these will change based on the size of intmax_t and uintmax_t
impericize_enmetric(INTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
CHECK(!strcmp("9.22E", buf));
impericize_enmetric(INTMAX_MAX, 1, buf, 0, 1000, '\0');
CHECK(!strcmp("9.22E", buf));
impericize_enmetric(UINTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
CHECK(!strcmp("18.44E", buf));
impericize_enmetric(UINTMAX_MAX, 1, buf, 0, 1000, '\0');
CHECK(!strcmp("18.44E", buf));
}
const char suffixes[] = "\0KMGTPE";
SUBCASE("Maxints1024") {
REQUIRE(0 == fesetround(FE_TOWARDZERO));
char buf[PREFIXSTRLEN + 1], gold[PREFIXSTRLEN + 1];
// FIXME these will change based on the size of intmax_t and uintmax_t
enmetric(INTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - (1ull << 53))) / (1ull << 60));
CHECK(!strcmp(gold, buf));
enmetric(INTMAX_MAX + 1ull, 1, buf, 0, 1024, 'i');
sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX + 1ull)) / (1ull << 60));
CHECK(!strcmp(gold, buf));
impericize_enmetric(UINTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
CHECK(!strcmp("15.99Ei", buf));
impericize_enmetric(UINTMAX_MAX, 1, buf, 0, 1024, 'i');
CHECK(!strcmp("15.99Ei", buf));
enmetric(UINTMAX_MAX - (1ull << 53), 1, buf, 0, 1024, 'i');
sprintf(gold, "%.2fEi", ((double)UINTMAX_MAX - (1ull << 53)) / (1ull << 60));
CHECK(!strcmp(gold, buf));
}
TEST_F(EnmetricTest, PowersOfTen) {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 0, 1000, '\0');
const int sidx = i / 3;
snprintf(gold, sizeof(gold), "%ju%s00%c", goldval, decisep_, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if((goldval *= 10) == 1000){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 3, i);
}
const char suffixes[] = "\0KMGTPE";
TEST_F(EnmetricTest, PowersOfTenNoDec) {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 1, 1000, '\0');
const int sidx = i / 3;
snprintf(gold, sizeof(gold), "%ju%c", goldval, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if((goldval *= 10) == 1000){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 3, i);
}
SUBCASE("PowersOfTen") {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 0, 1000, '\0');
const int sidx = i / 3;
snprintf(gold, sizeof(gold), "%ju%s00%c", goldval, decisep, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if((goldval *= 10) == 1000){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 3 > i);
}
TEST_F(EnmetricTest, PowersOfTwo) {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 0, 1024, 'i');
const int sidx = i / 10;
snprintf(gold, sizeof(gold), "%ju%s00%ci", goldval, decisep_, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 2;
if((goldval *= 2) == 1024){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 10, i);
}
SUBCASE("PowersOfTenNoDec") {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 1, 1000, '\0');
const int sidx = i / 3;
snprintf(gold, sizeof(gold), "%ju%c", goldval, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if((goldval *= 10) == 1000){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 3 > i);
}
TEST_F(EnmetricTest, PowersOfTwoNoDec) {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 1, 1024, 'i');
const int sidx = i / 10;
snprintf(gold, sizeof(gold), "%ju%ci", goldval, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 2;
if((goldval *= 2) == 1024){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 10, i);
}
SUBCASE("PowersOfTwo") {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 0, 1024, 'i');
const int sidx = i / 10;
snprintf(gold, sizeof(gold), "%ju%s00%ci", goldval, decisep, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 2;
if((goldval *= 2) == 1024){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 10 > i);
}
TEST_F(EnmetricTest, PowersOfTwoAsTens) {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
do{
enmetric(val, 1, buf, 0, 1000, '\0');
const int sidx = i / 10;
snprintf(gold, sizeof(gold), "%.2f%c",
((double)val) / vfloor, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 2;
if(i % 10 == 9){
vfloor *= 1000;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 10, i);
}
SUBCASE("PowersOfTwoNoDec") {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t goldval = 1;
uintmax_t val = 1;
size_t i = 0;
do{
enmetric(val, 1, buf, 1, 1024, 'i');
const int sidx = i / 10;
snprintf(gold, sizeof(gold), "%ju%ci", goldval, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 2;
if((goldval *= 2) == 1024){
goldval = 1;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 10 > i);
}
TEST_F(EnmetricTest, PowersOfTenAsTwos) {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
do{
enmetric(val, 1, buf, 0, 1024, 'i');
const int sidx = i ? (i - 1) / 3 : 0;
snprintf(gold, sizeof(gold), "%.2f%ci",
((double)val) / vfloor, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i && i % 3 == 0){
vfloor *= 1024;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 10, i);
}
SUBCASE("PowersOfTwoAsTens") {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
REQUIRE(0 == fesetround(FE_TOWARDZERO));
do{
enmetric(val, 1, buf, 0, 1000, '\0');
const int sidx = i / 10;
snprintf(gold, sizeof(gold), "%.2f%c",
((double)val) / vfloor, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 2;
if(i % 10 == 9){
vfloor *= 1000;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 10 > i);
}
TEST_F(EnmetricTest, PowersOfTenMinusOne) {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
do{
enmetric(val - 1, 1, buf, 0, 1000, '\0');
const int sidx = i ? (i - 1) / 3 : 0;
snprintf(gold, sizeof(gold), "%.2f%c",
((double)(val - 1)) / vfloor, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i && i % 3 == 0){
vfloor *= 1000;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 3, i);
}
SUBCASE("PowersOfTenAsTwos") {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
REQUIRE(0 == fesetround(FE_TOWARDZERO));
do{
enmetric(val, 1, buf, 0, 1024, 'i');
const int sidx = i ? (i - 1) / 3 : 0;
snprintf(gold, sizeof(gold), "%.2f%ci",
((double)val) / vfloor, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i && i % 3 == 0){
vfloor *= 1024;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 10 > i);
}
TEST_F(EnmetricTest, PowersOfTenPlusOne) {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
do{
enmetric(val + 1, 1, buf, 0, 1000, '\0');
const int sidx = i / 3;
snprintf(gold, sizeof(gold), "%.2f%c",
((double)(val + 1)) / vfloor, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i % 3 == 2){
vfloor *= 1000;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 3, i);
}
SUBCASE("PowersOfTenMinusOne") {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
REQUIRE(0 == fesetround(FE_TOWARDZERO));
do{
enmetric(val - 1, 1, buf, 0, 1000, '\0');
const int sidx = i ? (i - 1) / 3 : 0;
snprintf(gold, sizeof(gold), "%.2f%c",
((double)(val - 1)) / vfloor, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i && i % 3 == 0){
vfloor *= 1000;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 3 > i);
}
SUBCASE("PowersOfTenPlusOne") {
char gold[PREFIXSTRLEN + 1];
char buf[PREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
REQUIRE(0 == fesetround(FE_TOWARDZERO));
do{
enmetric(val + 1, 1, buf, 0, 1000, '\0');
const int sidx = i / 3;
snprintf(gold, sizeof(gold), "%.2f%c",
((double)(val + 1)) / vfloor, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i % 3 == 2){
vfloor *= 1000;
}
}while(++i < sizeof(suffixes) * 3);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 3 > i);
}
SUBCASE("PowersOfTenMinusOneAsTwos") {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
REQUIRE(0 == fesetround(FE_TOWARDZERO));
do{
enmetric(val - 1, 1, buf, 0, 1024, 'i');
const int sidx = i ? (i - 1) / 3 : 0;
snprintf(gold, sizeof(gold), "%.2f%ci",
((double)(val - 1)) / vfloor, suffixes[sidx]);
CHECK(!strcmp(gold, buf));
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i && i % 3 == 0){
vfloor *= 1024;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
CHECK(sizeof(suffixes) * 10 > i);
}
TEST_F(EnmetricTest, PowersOfTenMinusOneAsTwos) {
char gold[BPREFIXSTRLEN + 1];
char buf[BPREFIXSTRLEN + 1];
uintmax_t vfloor = 1;
uintmax_t val = 1;
size_t i = 0;
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
do{
enmetric(val - 1, 1, buf, 0, 1024, 'i');
const int sidx = i ? (i - 1) / 3 : 0;
snprintf(gold, sizeof(gold), "%.2f%ci",
((double)(val - 1)) / vfloor, suffixes[sidx]);
EXPECT_STREQ(gold, buf);
if(UINTMAX_MAX / val < 10){
break;
}
val *= 10;
if(i && i % 3 == 0){
vfloor *= 1024;
}
}while(++i < sizeof(suffixes) * 10);
// If we ran through all our suffixes, that's a problem
EXPECT_GT(sizeof(suffixes) * 10, i);
}

@ -2,69 +2,56 @@
#include <cstdlib>
#include <iostream>
class FadeTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
if(!notcurses_canfade(nc_)){
GTEST_SKIP();
}
n_ = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, n_);
ASSERT_EQ(0, ncplane_cursor_move_yx(n_, 0, 0));
int dimy, dimx;
ncplane_dim_yx(n_, &dimy, &dimx);
cell c = CELL_TRIVIAL_INITIALIZER;
c.gcluster = '*';
cell_set_fg_rgb(&c, 0xff, 0xff, 0xff);
unsigned rgb = 0xffffffu;
for(int y = 0 ; y < dimy ; ++y){
for(int x = 0 ; x < dimx ; ++x){
rgb -= 32;
if(rgb < 32){
rgb = 0xffffffu;
}
cell_set_fg_rgb(&c, (rgb >> 16u) & 0xff, (rgb >> 8u) & 0xff, rgb & 0xff);
cell_set_bg_rgb(&c, rgb & 0xff, (rgb >> 16u) & 0xff, (rgb >> 8u) & 0xff);
EXPECT_LT(0, ncplane_putc(n_, &c));
TEST_CASE("Fade") {
if(getenv("TERM") == nullptr){
return;
}
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
if(!notcurses_canfade(nc_)){
return;
}
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
int dimy, dimx;
ncplane_dim_yx(n_, &dimy, &dimx);
cell c = CELL_TRIVIAL_INITIALIZER;
c.gcluster = '*';
cell_set_fg_rgb(&c, 0xff, 0xff, 0xff);
unsigned rgb = 0xffffffu;
for(int y = 0 ; y < dimy ; ++y){
for(int x = 0 ; x < dimx ; ++x){
rgb -= 32;
if(rgb < 32){
rgb = 0xffffffu;
}
cell_set_fg_rgb(&c, (rgb >> 16u) & 0xff, (rgb >> 8u) & 0xff, rgb & 0xff);
cell_set_bg_rgb(&c, rgb & 0xff, (rgb >> 16u) & 0xff, (rgb >> 8u) & 0xff);
CHECK(0 < ncplane_putc(n_, &c));
}
}
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
}
if(outfp_){
fclose(outfp_);
}
SUBCASE("FadeOut") {
CHECK(0 == notcurses_render(nc_));
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
REQUIRE(0 == ncplane_fadeout(n_, &ts, nullptr));
}
struct notcurses* nc_{};
struct ncplane* n_{};
FILE* outfp_{};
};
SUBCASE("FadeIn") {
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
REQUIRE(0 == ncplane_fadein(n_, &ts, nullptr));
}
TEST_F(FadeTest, FadeOut) {
EXPECT_EQ(0, notcurses_render(nc_));
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
ASSERT_EQ(0, ncplane_fadeout(n_, &ts, nullptr));
}
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
TEST_F(FadeTest, FadeIn) {
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
ASSERT_EQ(0, ncplane_fadein(n_, &ts, nullptr));
}

@ -1,38 +1,20 @@
#include "main.h"
class InputTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
TEST_CASE("NotcursesInput") {
if(getenv("TERM") == nullptr){
return;
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
}
if(outfp_){
fclose(outfp_);
}
}
struct notcurses* nc_{};
FILE* outfp_{};
};
TEST_F(InputTest, TestMouseOn){
ASSERT_EQ(0, notcurses_mouse_enable(nc_));
}
REQUIRE(0 == notcurses_mouse_enable(nc_));
CHECK(0 == notcurses_mouse_disable(nc_));
TEST_F(InputTest, TestMouseOff){
ASSERT_EQ(0, notcurses_mouse_disable(nc_));
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
}

@ -4,79 +4,72 @@
#include "internal.h"
#include "main.h"
class InternalsTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
}
};
TEST_F(InternalsTest, RGBtoANSIWhite) {
TEST_CASE("RGBtoANSIWhite") {
unsigned r, g, b;
for(r = 250 ; r < 256 ; ++r){
g = b = r;
EXPECT_EQ(15, rgb_quantize_256(r, g, b));
CHECK(15 == rgb_quantize_256(r, g, b));
}
}
TEST_F(InternalsTest, RGBtoANSIBlack) {
TEST_CASE("RGBtoANSIBlack") {
unsigned r, g, b;
for(r = 0 ; r < 10 ; ++r){
g = b = r;
EXPECT_EQ(0, rgb_quantize_256(r, g, b));
CHECK(0 == rgb_quantize_256(r, g, b));
}
}
TEST_F(InternalsTest, RGBtoANSIGrey) {
TEST_CASE("RGBtoANSIGrey") {
unsigned r, g, b;
for(r = 10 ; r < 244 ; ++r){
g = b = r;
EXPECT_EQ(231 + (r * 5) / 49, rgb_quantize_256(r, g, b));
CHECK(231 + (r * 5) / 49 == rgb_quantize_256(r, g, b));
}
}
// Pure reds are either 0 (black), or 16 plus 36 * [0..5].
TEST_F(InternalsTest, RGBtoANSIRed) {
TEST_CASE("RGBtoANSIRed") {
unsigned r, g, b;
g = b = 0x0;
for(r = 0 ; r < 256 ; ++r){
int c256 = rgb_quantize_256(r, g, b);
if(r < 8){
EXPECT_EQ(0, c256);
CHECK(0 == c256);
}else{
EXPECT_LT(15, c256);
EXPECT_EQ(16, c256 % 36);
CHECK(15 < c256);
CHECK(16 == c256 % 36);
}
}
}
// Pure greens are either 0 (black), or 16 plus 6 * [0..5].
TEST_F(InternalsTest, RGBtoANSIGreen) {
TEST_CASE("RGBtoANSIGreen") {
unsigned r, g, b;
r = b = 0x0;
for(g = 0 ; g < 256 ; ++g){
int c256 = rgb_quantize_256(r, g, b);
EXPECT_GT(48, c256);
CHECK(48 > c256);
if(g < 8){
EXPECT_EQ(0, c256);
CHECK(0 == c256);
}else{
EXPECT_LT(15, c256);
EXPECT_EQ(4, c256 % 6);
CHECK(15 < c256);
CHECK(4 == c256 % 6);
}
}
}
// Pure blues are either 0 (black), or one of the first 6 colors [16..22].
TEST_F(InternalsTest, RGBtoANSIBlue) {
TEST_CASE("RGBtoANSIBlue") {
unsigned r, g, b;
r = g = 0x0;
for(b = 0 ; b < 256 ; ++b){
int c256 = rgb_quantize_256(r, g, b);
EXPECT_GT(22, c256);
CHECK(22 > c256);
if(b < 8){
EXPECT_EQ(0, c256);
CHECK(0 == c256);
}else{
EXPECT_LT(15, c256);
CHECK(15 < c256);
}
}
}

@ -1,104 +1,92 @@
#include <notcurses.h>
#include "version.h"
#include "main.h"
#ifndef DISABLE_FFMPEG
#include <libavutil/pixdesc.h>
#include <libavutil/avconfig.h>
#include <libavcodec/avcodec.h> // ffmpeg doesn't reliably "C"-guard itself
#endif
class LibavTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
ncp_ = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, ncp_);
}
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
}
if(outfp_){
fclose(outfp_);
}
TEST_CASE("Multimedia") {
if(getenv("TERM") == nullptr){
return;
}
notcurses* nc_{};
ncplane* ncp_{};
FILE* outfp_{};
};
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
ncplane* ncp_ = notcurses_stdplane(nc_);
REQUIRE(ncp_);
#ifdef DISABLE_FFMPEG
TEST_F(LibavTest, LibavDisabled){
ASSERT_FALSE(notcurses_canopen(nc_));
}
SUBCASE("LibavDisabled"){
REQUIRE(!notcurses_canopen(nc_));
}
#else
#include <libavutil/pixdesc.h>
#include <libavutil/avconfig.h>
#include <libavcodec/avcodec.h> // ffmpeg doesn't reliably "C"-guard itself
SUBCASE("LibavEnabled"){
REQUIRE(notcurses_canopen(nc_));
}
TEST_F(LibavTest, LibavEnabled){
ASSERT_TRUE(notcurses_canopen(nc_));
}
SUBCASE("LoadImage") {
int averr;
int dimy, dimx;
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncplane_visual_open(ncp_, "../data/dsscaw-purp.png", &averr);
REQUIRE(ncv);
REQUIRE(0 == averr);
auto frame = ncvisual_decode(ncv, &averr);
REQUIRE(frame);
REQUIRE(0 == averr);
CHECK(dimy * 2 == frame->height);
CHECK(dimx == frame->width);
CHECK(0 == ncvisual_render(ncv, 0, 0, 0, 0));
CHECK(0 == notcurses_render(nc_));
frame = ncvisual_decode(ncv, &averr);
REQUIRE_EQ(nullptr, frame);
CHECK(AVERROR_EOF == averr);
ncvisual_destroy(ncv);
}
TEST_F(LibavTest, LoadImage) {
int averr;
int dimy, dimx;
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncplane_visual_open(ncp_, "../data/dsscaw-purp.png", &averr);
ASSERT_NE(nullptr, ncv);
ASSERT_EQ(0, averr);
auto frame = ncvisual_decode(ncv, &averr);
ASSERT_NE(nullptr, frame);
ASSERT_EQ(0, averr);
EXPECT_EQ(dimy * 2, frame->height);
EXPECT_EQ(dimx, frame->width);
EXPECT_EQ(0, ncvisual_render(ncv, 0, 0, 0, 0));
EXPECT_EQ(0, notcurses_render(nc_));
frame = ncvisual_decode(ncv, &averr);
ASSERT_EQ(nullptr, frame);
EXPECT_EQ(AVERROR_EOF, averr);
ncvisual_destroy(ncv);
}
// FIXME ought run through full video, not just first frame
SUBCASE("LoadVideo") {
int averr;
int dimy, dimx;
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncplane_visual_open(ncp_, "../data/fm6.mkv", &averr);
REQUIRE(ncv);
CHECK(0 == averr);
auto frame = ncvisual_decode(ncv, &averr);
REQUIRE(frame);
CHECK(0 == averr);
CHECK(dimy * 2 == frame->height);
CHECK(dimx == frame->width);
CHECK(0 == ncvisual_render(ncv, 0, 0, 0, 0));
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
}
// FIXME ought run through full video, not just first frame
TEST_F(LibavTest, LoadVideo) {
int averr;
int dimy, dimx;
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncplane_visual_open(ncp_, "../data/fm6.mkv", &averr);
ASSERT_NE(nullptr, ncv);
EXPECT_EQ(0, averr);
auto frame = ncvisual_decode(ncv, &averr);
ASSERT_NE(nullptr, frame);
EXPECT_EQ(0, averr);
EXPECT_EQ(dimy * 2, frame->height);
EXPECT_EQ(dimx, frame->width);
EXPECT_EQ(0, ncvisual_render(ncv, 0, 0, 0, 0));
EXPECT_EQ(0, notcurses_render(nc_));
ncvisual_destroy(ncv);
}
SUBCASE("LoadVideoCreatePlane") {
int averr;
int dimy, dimx;
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncvisual_open_plane(nc_, "../data/fm6.mkv", &averr, 0, 0, NCSCALE_STRETCH);
REQUIRE(ncv);
CHECK(0 == averr);
auto frame = ncvisual_decode(ncv, &averr);
REQUIRE_NE(nullptr, frame);
CHECK(0 == averr);
CHECK(dimy * 2 == frame->height);
CHECK(dimx == frame->width);
CHECK(0 == ncvisual_render(ncv, 0, 0, 0, 0));
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
}
#endif
TEST_F(LibavTest, LoadVideoCreatePlane) {
int averr;
int dimy, dimx;
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncvisual_open_plane(nc_, "../data/fm6.mkv", &averr, 0, 0, NCSCALE_STRETCH);
ASSERT_NE(nullptr, ncv);
EXPECT_EQ(0, averr);
auto frame = ncvisual_decode(ncv, &averr);
ASSERT_NE(nullptr, frame);
EXPECT_EQ(0, averr);
EXPECT_EQ(dimy * 2, frame->height);
EXPECT_EQ(dimx, frame->width);
EXPECT_EQ(0, ncvisual_render(ncv, 0, 0, 0, 0));
EXPECT_EQ(0, notcurses_render(nc_));
ncvisual_destroy(ncv);
CHECK(!notcurses_stop(nc_));
CHECK(!fclose(outfp_));
}
#endif

@ -1,3 +1,4 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include <clocale>
#include <iostream>
#include "main.h"
@ -7,6 +8,26 @@ int main(int argc, char **argv){
std::cerr << "Coudln't set locale based on user preferences!" << std::endl;
return EXIT_FAILURE;
}
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
doctest::Context context;
// defaults
context.addFilter("test-case-exclude", "*math*"); // exclude test cases with "math" in their name
context.setOption("abort-after", 5); // stop test execution after 5 failed assertions
context.setOption("order-by", "name"); // sort the test cases by their name
context.applyCommandLine(argc, argv);
// overrides
context.setOption("no-breaks", true); // don't break in the debugger when assertions fail
int res = context.run(); // run
if(context.shouldExit()){ // important - query flags (and --exit) rely on the user doing this
return res; // propagate the result of the tests
}
int client_stuff_return_code = 0;
// your program - if the testing framework is integrated in your production code
return res + client_stuff_return_code; // the result from doctest is propagated here as well
}

@ -1,14 +1,8 @@
#ifndef NOTCURSES_TEST_MAIN
#define NOTCURSES_TEST_MAIN
#include <gtest/gtest.h>
#include "doctest.h"
#include <notcurses.h>
#include <curses.h>
// GTEST_SKIP only came along in GoogleTest 1.9
#ifndef GTEST_SKIP
#define GTEST_SKIP() return;
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -1,154 +1,141 @@
#include <string>
#include <cstdlib>
#include <iostream>
#include <notcurses.h>
#include "internal.h"
#include "main.h"
class NotcursesTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
TEST_CASE("NotcursesBase") {
if(getenv("TERM") == nullptr){
return;
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
SUBCASE("NotcursesVersionString") {
const char* ver = notcurses_version();
REQUIRE(ver);
REQUIRE(0 < strlen(ver));
std::cout << "notcurses version " << ver << std::endl;
}
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
SUBCASE("TermDimensions") {
int x, y;
notcurses_term_dim_yx(nc_, &y, &x);
auto stry = getenv("LINES");
if(stry){
auto envy = std::stoi(stry, nullptr);
CHECK(envy == y);
}
if(outfp_){
fclose(outfp_);
auto strx = getenv("COLUMNS");
if(stry){
auto envx = std::stoi(strx, nullptr);
CHECK(envx == x);
}
}
struct notcurses* nc_{};
FILE* outfp_{};
};
TEST_F(NotcursesTest, NotcursesVersionString) {
const char* ver = notcurses_version();
ASSERT_NE(nullptr, ver);
ASSERT_LT(0, strlen(ver));
std::cout << "notcurses version " << ver << std::endl;
}
TEST_F(NotcursesTest, BasicLifetime) {
}
TEST_F(NotcursesTest, TermDimensions) {
int x, y;
notcurses_term_dim_yx(nc_, &y, &x);
auto stry = getenv("LINES");
if(stry){
auto envy = std::stoi(stry, nullptr);
EXPECT_EQ(envy, y);
SUBCASE("ResizeSameSize") {
int x, y;
notcurses_term_dim_yx(nc_, &y, &x);
int newx, newy;
CHECK(0 == notcurses_resize(nc_, &newy, &newx));
CHECK(newx == x);
CHECK(newy == y);
}
auto strx = getenv("COLUMNS");
if(stry){
auto envx = std::stoi(strx, nullptr);
EXPECT_EQ(envx, x);
}
}
TEST_F(NotcursesTest, ResizeSameSize) {
int x, y;
notcurses_term_dim_yx(nc_, &y, &x);
int newx, newy;
EXPECT_EQ(0, notcurses_resize(nc_, &newy, &newx));
EXPECT_EQ(newx, x);
EXPECT_EQ(newy, y);
}
// we should at least have CELL_STYLE_BOLD everywhere, i should think?
TEST_F(NotcursesTest, CursesStyles) {
unsigned attrs = notcurses_supported_styles(nc_);
EXPECT_EQ(1, !!(CELL_STYLE_BOLD & attrs));
}
// we should at least have CELL_STYLE_BOLD everywhere, i should think?
SUBCASE("CursesStyles") {
unsigned attrs = notcurses_supported_styles(nc_);
CHECK(1 == !!(CELL_STYLE_BOLD & attrs));
}
// it is an error to attempt to destroy the standard plane
TEST_F(NotcursesTest, RejectDestroyStdPlane) {
ncplane* ncp = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, ncp);
ASSERT_NE(0, ncplane_destroy(ncp));
}
// it is an error to attempt to destroy the standard plane
SUBCASE("RejectDestroyStdPlane") {
ncplane* ncp = notcurses_stdplane(nc_);
REQUIRE(ncp);
REQUIRE(0 > ncplane_destroy(ncp));
}
// it is an error to attempt to move the standard plane
TEST_F(NotcursesTest, RejectMoveStdPlane) {
ncplane* ncp = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, ncp);
ASSERT_NE(0, ncplane_move_yx(ncp, 1, 1));
}
// it is an error to attempt to move the standard plane
SUBCASE("RejectMoveStdPlane") {
ncplane* ncp = notcurses_stdplane(nc_);
REQUIRE(ncp);
REQUIRE(0 > ncplane_move_yx(ncp, 1, 1));
}
// create planes partitioning the entirety of the screen, one at each coordinate
TEST_F(NotcursesTest, TileScreenWithPlanes) {
int maxx, maxy;
notcurses_term_dim_yx(nc_, &maxy, &maxx);
auto total = maxx * maxy;
struct ncplane** planes = new struct ncplane*[total];
int* planesecrets = new int[total];
for(int y = 0 ; y < maxy ; ++y){
for(int x = 0 ; x < maxx ; ++x){
const auto idx = y * maxx + x;
planes[idx] = notcurses_newplane(nc_, 1, 1, y, x, &planesecrets[idx]);
ASSERT_NE(nullptr, planes[idx]);
// create planes partitioning the entirety of the screen, one at each coordinate
SUBCASE("TileScreenWithPlanes") {
int maxx, maxy;
notcurses_term_dim_yx(nc_, &maxy, &maxx);
auto total = maxx * maxy;
struct ncplane** planes = new struct ncplane*[total];
int* planesecrets = new int[total];
for(int y = 0 ; y < maxy ; ++y){
for(int x = 0 ; x < maxx ; ++x){
const auto idx = y * maxx + x;
planes[idx] = notcurses_newplane(nc_, 1, 1, y, x, &planesecrets[idx]);
REQUIRE(planes[idx]);
}
}
}
ASSERT_EQ(0, notcurses_render(nc_));
for(int y = 0 ; y < maxy ; ++y){
for(int x = 0 ; x < maxx ; ++x){
const auto idx = y * maxx + x;
auto userptr = ncplane_userptr(planes[idx]);
ASSERT_NE(nullptr, userptr);
EXPECT_EQ(userptr, &planesecrets[idx]);
ASSERT_EQ(0, ncplane_destroy(planes[idx]));
REQUIRE(0 == notcurses_render(nc_));
for(int y = 0 ; y < maxy ; ++y){
for(int x = 0 ; x < maxx ; ++x){
const auto idx = y * maxx + x;
auto userptr = ncplane_userptr(planes[idx]);
REQUIRE(userptr);
CHECK(userptr == &planesecrets[idx]);
REQUIRE(0 == ncplane_destroy(planes[idx]));
}
}
delete[] planesecrets;
delete[] planes;
REQUIRE(0 == notcurses_render(nc_));
}
delete[] planesecrets;
delete[] planes;
ASSERT_EQ(0, notcurses_render(nc_));
}
TEST_F(NotcursesTest, ChannelSetFGAlpha){
uint64_t channel = 0;
EXPECT_GT(0, channels_set_fg_alpha(&channel, -1));
EXPECT_GT(0, channels_set_fg_alpha(&channel, 4));
EXPECT_EQ(0, channels_set_fg_alpha(&channel, CELL_ALPHA_OPAQUE));
EXPECT_EQ(CELL_ALPHA_OPAQUE, channels_get_fg_alpha(channel));
EXPECT_EQ(0, channels_set_fg_alpha(&channel, CELL_ALPHA_HIGHCONTRAST));
EXPECT_EQ(CELL_ALPHA_HIGHCONTRAST, channels_get_fg_alpha(channel));
EXPECT_TRUE(channels_fg_default_p(channel));
EXPECT_TRUE(channels_bg_default_p(channel));
}
SUBCASE("ChannelSetFGAlpha"){
uint64_t channel = 0;
CHECK(0 > channels_set_fg_alpha(&channel, -1));
CHECK(0 > channels_set_fg_alpha(&channel, 4));
CHECK(0 == channels_set_fg_alpha(&channel, CELL_ALPHA_OPAQUE));
CHECK(CELL_ALPHA_OPAQUE == channels_get_fg_alpha(channel));
CHECK(0 == channels_set_fg_alpha(&channel, CELL_ALPHA_HIGHCONTRAST));
CHECK(CELL_ALPHA_HIGHCONTRAST == channels_get_fg_alpha(channel));
CHECK(channels_fg_default_p(channel));
CHECK(channels_bg_default_p(channel));
}
TEST_F(NotcursesTest, ChannelSetBGAlpha){
uint64_t channel = 0;
EXPECT_GT(0, channels_set_bg_alpha(&channel, -1));
EXPECT_GT(0, channels_set_bg_alpha(&channel, 4));
EXPECT_EQ(0, channels_set_bg_alpha(&channel, CELL_ALPHA_OPAQUE));
EXPECT_EQ(0, channels_get_bg_alpha(channel));
EXPECT_EQ(0, channels_set_bg_alpha(&channel, CELL_ALPHA_TRANSPARENT));
EXPECT_NE(0, channels_set_bg_alpha(&channel, CELL_ALPHA_HIGHCONTRAST));
EXPECT_EQ(CELL_ALPHA_TRANSPARENT, channels_get_bg_alpha(channel));
EXPECT_TRUE(channels_fg_default_p(channel));
EXPECT_TRUE(channels_bg_default_p(channel));
}
SUBCASE("ChannelSetBGAlpha"){
uint64_t channel = 0;
CHECK(0 > channels_set_bg_alpha(&channel, -1));
CHECK(0 > channels_set_bg_alpha(&channel, 4));
CHECK(0 == channels_set_bg_alpha(&channel, CELL_ALPHA_OPAQUE));
CHECK(CELL_ALPHA_OPAQUE == channels_get_bg_alpha(channel));
CHECK(0 == channels_set_bg_alpha(&channel, CELL_ALPHA_TRANSPARENT));
CHECK(0 > channels_set_bg_alpha(&channel, CELL_ALPHA_HIGHCONTRAST));
CHECK(CELL_ALPHA_TRANSPARENT == channels_get_bg_alpha(channel));
CHECK(channels_fg_default_p(channel));
CHECK(channels_bg_default_p(channel));
}
SUBCASE("Stats"){
struct ncstats stats;
notcurses_stats(nc_, &stats);
CHECK(0 == stats.renders);
CHECK(0 == notcurses_render(nc_));
notcurses_stats(nc_, &stats);
CHECK(1 == stats.renders);
notcurses_reset_stats(nc_);
notcurses_stats(nc_, &stats);
CHECK(0 == stats.renders);
}
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
TEST_F(NotcursesTest, Stats){
struct ncstats stats;
notcurses_stats(nc_, &stats);
EXPECT_EQ(0, stats.renders);
EXPECT_EQ(0, notcurses_render(nc_));
notcurses_stats(nc_, &stats);
EXPECT_EQ(1, stats.renders);
notcurses_reset_stats(nc_);
notcurses_stats(nc_, &stats);
EXPECT_EQ(0, stats.renders);
}

@ -1,258 +1,243 @@
#include "main.h"
#include <iostream>
class PanelReelTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
n_ = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, n_);
ASSERT_EQ(0, ncplane_cursor_move_yx(n_, 0, 0));
}
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
}
if(outfp_){
fclose(outfp_);
}
}
struct notcurses* nc_{};
struct ncplane* n_{};
FILE* outfp_{};
};
TEST_F(PanelReelTest, InitLinear) {
panelreel_options p = { };
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
int panelcb(struct tablet* t, int begx, int begy, int maxx, int maxy, bool cliptop){
CHECK(tablet_ncplane(t));
CHECK(begx < maxx);
CHECK(begy < maxy);
CHECK(!tablet_userptr(t));
CHECK(!cliptop);
// FIXME verify geometry is as expected
return 0;
}
TEST_F(PanelReelTest, InitLinearInfinite) {
panelreel_options p{};
p.infinitescroll = true;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
}
TEST_CASE("PanelReelTest") {
if(getenv("TERM") == nullptr){
return;
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
SUBCASE("InitLinear") {
panelreel_options p = { };
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
}
TEST_F(PanelReelTest, InitCircular) {
panelreel_options p{};
p.infinitescroll = true;
p.circular = true;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
ASSERT_EQ(0, panelreel_destroy(pr));
}
SUBCASE("InitLinearInfinite") {
panelreel_options p{};
p.infinitescroll = true;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
}
// circular is not allowed to be true when infinitescroll is false
TEST_F(PanelReelTest, FiniteCircleRejected) {
panelreel_options p{};
p.infinitescroll = false;
p.circular = true;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_EQ(nullptr, pr);
}
SUBCASE("InitCircular") {
panelreel_options p{};
p.infinitescroll = true;
p.circular = true;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
REQUIRE(0 == panelreel_destroy(pr));
}
// We ought be able to invoke panelreel_next() and panelreel_prev() safely,
// even if there are no tablets. They both ought return nullptr.
TEST_F(PanelReelTest, MovementWithoutTablets) {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
EXPECT_EQ(nullptr, panelreel_next(pr));
// EXPECT_EQ(0, panelreel_validate(n_, pr));
EXPECT_EQ(nullptr, panelreel_prev(pr));
// EXPECT_EQ(0, panelreel_validate(n_, pr));
}
// circular is not allowed to be true when infinitescroll is false
SUBCASE("FiniteCircleRejected") {
panelreel_options p{};
p.infinitescroll = false;
p.circular = true;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(!pr);
}
int panelcb(struct tablet* t, int begx, int begy, int maxx, int maxy, bool cliptop){
EXPECT_NE(nullptr, tablet_ncplane(t));
EXPECT_LT(begx, maxx);
EXPECT_LT(begy, maxy);
EXPECT_EQ(nullptr, tablet_userptr(t));
EXPECT_FALSE(cliptop);
// FIXME verify geometry is as expected
return 0;
}
// We ought be able to invoke panelreel_next() and panelreel_prev() safely,
// even if there are no tablets. They both ought return nullptr.
SUBCASE("MovementWithoutTablets") {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
CHECK(!panelreel_next(pr));
// CHECK_EQ(0, panelreel_validate(n_, pr));
CHECK(!panelreel_prev(pr));
// CHECK_EQ(0, panelreel_validate(n_, pr));
}
TEST_F(PanelReelTest, OneTablet) {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
ASSERT_NE(nullptr, t);
// EXPECT_EQ(0, panelreel_validate(n_, pr));
EXPECT_EQ(0, panelreel_del(pr, t));
// EXPECT_EQ(0, panelreel_validate(n_, pr));
}
SUBCASE("OneTablet") {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
REQUIRE(t);
// CHECK_EQ(0, panelreel_validate(n_, pr));
CHECK(0 == panelreel_del(pr, t));
// CHECK_EQ(0, panelreel_validate(n_, pr));
}
TEST_F(PanelReelTest, MovementWithOneTablet) {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
ASSERT_NE(nullptr, t);
// EXPECT_EQ(0, panelreel_validate(n_, pr));
EXPECT_NE(nullptr, panelreel_next(pr));
// EXPECT_EQ(0, panelreel_validate(n_, pr));
EXPECT_NE(nullptr, panelreel_prev(pr));
// EXPECT_EQ(0, panelreel_validate(n_, pr));
EXPECT_EQ(0, panelreel_del(pr, t));
// EXPECT_EQ(0, panelreel_validate(n_, pr));
}
SUBCASE("MovementWithOneTablet") {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
REQUIRE(t);
// CHECK_EQ(0, panelreel_validate(n_, pr));
CHECK(panelreel_next(pr));
// CHECK_EQ(0, panelreel_validate(n_, pr));
CHECK(panelreel_prev(pr));
// CHECK_EQ(0, panelreel_validate(n_, pr));
CHECK(0 == panelreel_del(pr, t));
// CHECK_EQ(0, panelreel_validate(n_, pr));
}
TEST_F(PanelReelTest, DeleteActiveTablet) {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
ASSERT_NE(nullptr, t);
EXPECT_EQ(0, panelreel_del_focused(pr));
}
SUBCASE("DeleteActiveTablet") {
panelreel_options p{};
p.infinitescroll = false;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
REQUIRE(t);
CHECK(0 == panelreel_del_focused(pr));
}
TEST_F(PanelReelTest, NoBorder) {
panelreel_options p{};
p.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
}
SUBCASE("NoBorder") {
panelreel_options p{};
p.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
}
TEST_F(PanelReelTest, BadBorderBitsRejected) {
panelreel_options p{};
p.bordermask = NCBOXMASK_LEFT * 2;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_EQ(nullptr, pr);
}
SUBCASE("BadBorderBitsRejected") {
panelreel_options p{};
p.bordermask = NCBOXMASK_LEFT * 2;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(!pr);
}
TEST_F(PanelReelTest, NoTabletBorder) {
panelreel_options p{};
p.tabletmask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
}
SUBCASE("NoTabletBorder") {
panelreel_options p{};
p.tabletmask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
}
TEST_F(PanelReelTest, NoTopBottomBorder) {
panelreel_options p{};
p.bordermask = NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
}
SUBCASE("NoTopBottomBorder") {
panelreel_options p{};
p.bordermask = NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
}
TEST_F(PanelReelTest, NoSideBorders) {
panelreel_options p{};
p.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
}
SUBCASE("NoSideBorders") {
panelreel_options p{};
p.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
}
TEST_F(PanelReelTest, BadTabletBorderBitsRejected) {
panelreel_options p{};
p.tabletmask = NCBOXMASK_LEFT * 2;
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_EQ(nullptr, pr);
}
SUBCASE("BadTabletBorderBitsRejected") {
panelreel_options p{};
p.tabletmask = NCBOXMASK_LEFT * 2;
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(!pr);
}
/*
// Make a target window occupying all but a containing perimeter of the
// specified WINDOW (which will usually be n_).
struct ncpanel* make_targwin(struct ncpanel* w) {
cchar_t cc;
int cpair = COLOR_GREEN;
EXPECT_EQ(OK, setcchar(&cc, L"W", 0, 0, &cpair));
int x, y, xx, yy;
getbegyx(w, y, x);
getmaxyx(w, yy, xx);
yy -= 2;
xx -= 2;
++x;
++y;
WINDOW* ww = subwin(w, yy, xx, y, x);
EXPECT_NE(nullptr, ww);
PANEL* p = new_panel(ww);
EXPECT_NE(nullptr, p);
EXPECT_EQ(OK, wbkgrnd(ww, &cc));
return p;
}
/*
// Make a target window occupying all but a containing perimeter of the
// specified WINDOW (which will usually be n_).
struct ncpanel* make_targwin(struct ncpanel* w) {
cchar_t cc;
int cpair = COLOR_GREEN;
CHECK_EQ(OK, setcchar(&cc, L"W", 0, 0, &cpair));
int x, y, xx, yy;
getbegyx(w, y, x);
getmaxyx(w, yy, xx);
yy -= 2;
xx -= 2;
++x;
++y;
WINDOW* ww = subwin(w, yy, xx, y, x);
CHECK_NE(nullptr, ww);
PANEL* p = new_panel(ww);
CHECK_NE(nullptr, p);
CHECK_EQ(OK, wbkgrnd(ww, &cc));
return p;
}
TEST_F(PanelReelTest, InitWithinSubwin) {
panelreel_options p{};
p.loff = 1;
p.roff = 1;
p.toff = 1;
p.boff = 1;
EXPECT_EQ(0, clear());
PANEL* base = make_targwin(n_);
ASSERT_NE(nullptr, base);
WINDOW* basew = panel_window(base);
ASSERT_NE(nullptr, basew);
struct panelreel* pr = panelreel_create(basew, &p, -1);
ASSERT_NE(nullptr, pr);
EXPECT_EQ(0, panelreel_validate(basew, pr));
ASSERT_EQ(0, panelreel_destroy(pr));
EXPECT_EQ(OK, del_panel(base));
EXPECT_EQ(OK, delwin(basew));
}
SUBCASE("InitWithinSubwin") {
panelreel_options p{};
p.loff = 1;
p.roff = 1;
p.toff = 1;
p.boff = 1;
CHECK_EQ(0, clear());
PANEL* base = make_targwin(n_);
REQUIRE_NE(nullptr, base);
WINDOW* basew = panel_window(base);
REQUIRE_NE(nullptr, basew);
struct panelreel* pr = panelreel_create(basew, &p, -1);
REQUIRE_NE(nullptr, pr);
CHECK_EQ(0, panelreel_validate(basew, pr));
REQUIRE_EQ(0, panelreel_destroy(pr));
CHECK_EQ(OK, del_panel(base));
CHECK_EQ(OK, delwin(basew));
}
TEST_F(PanelReelTest, SubwinNoPanelreelBorders) {
panelreel_options p{};
p.loff = 1;
p.roff = 1;
p.toff = 1;
p.boff = 1;
p.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
EXPECT_EQ(0, clear());
PANEL* base = make_targwin(n_);
ASSERT_NE(nullptr, base);
WINDOW* basew = panel_window(base);
ASSERT_NE(nullptr, basew);
struct panelreel* pr = panelreel_create(basew, &p, -1);
ASSERT_NE(nullptr, pr);
EXPECT_EQ(0, panelreel_validate(basew, pr));
ASSERT_EQ(0, panelreel_destroy(pr));
EXPECT_EQ(OK, del_panel(base));
EXPECT_EQ(OK, delwin(basew));
}
SUBCASE("SubwinNoPanelreelBorders") {
panelreel_options p{};
p.loff = 1;
p.roff = 1;
p.toff = 1;
p.boff = 1;
p.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
CHECK_EQ(0, clear());
PANEL* base = make_targwin(n_);
REQUIRE_NE(nullptr, base);
WINDOW* basew = panel_window(base);
REQUIRE_NE(nullptr, basew);
struct panelreel* pr = panelreel_create(basew, &p, -1);
REQUIRE_NE(nullptr, pr);
CHECK_EQ(0, panelreel_validate(basew, pr));
REQUIRE_EQ(0, panelreel_destroy(pr));
CHECK_EQ(OK, del_panel(base));
CHECK_EQ(OK, delwin(basew));
}
TEST_F(PanelReelTest, SubwinNoOffsetGeom) {
panelreel_options p{};
EXPECT_EQ(0, clear());
PANEL* base = make_targwin(n_);
ASSERT_NE(nullptr, base);
WINDOW* basew = panel_window(base);
ASSERT_NE(nullptr, basew);
struct panelreel* pr = panelreel_create(basew, &p, -1);
ASSERT_NE(nullptr, pr);
EXPECT_EQ(0, panelreel_validate(basew, pr));
ASSERT_EQ(0, panelreel_destroy(pr));
EXPECT_EQ(OK, del_panel(base));
EXPECT_EQ(OK, delwin(basew));
}
*/
SUBCASE("SubwinNoOffsetGeom") {
panelreel_options p{};
CHECK_EQ(0, clear());
PANEL* base = make_targwin(n_);
REQUIRE_NE(nullptr, base);
WINDOW* basew = panel_window(base);
REQUIRE_NE(nullptr, basew);
struct panelreel* pr = panelreel_create(basew, &p, -1);
REQUIRE_NE(nullptr, pr);
CHECK_EQ(0, panelreel_validate(basew, pr));
REQUIRE_EQ(0, panelreel_destroy(pr));
CHECK_EQ(OK, del_panel(base));
CHECK_EQ(OK, delwin(basew));
}
*/
SUBCASE("TransparentBackground") {
panelreel_options p{};
channels_set_bg_alpha(&p.bgchannel, 3);
struct panelreel* pr = panelreel_create(n_, &p, -1);
REQUIRE(pr);
// FIXME
}
TEST_F(PanelReelTest, TransparentBackground) {
panelreel_options p{};
channels_set_bg_alpha(&p.bgchannel, 3);
struct panelreel* pr = panelreel_create(n_, &p, -1);
ASSERT_NE(nullptr, pr);
// FIXME
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
}

@ -2,136 +2,122 @@
#include <cstdlib>
#include <iostream>
class ZAxisTest : public :: testing::Test {
protected:
void SetUp() override {
setlocale(LC_ALL, "");
if(getenv("TERM") == nullptr){
GTEST_SKIP();
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
outfp_ = fopen("/dev/tty", "wb");
ASSERT_NE(nullptr, outfp_);
nc_ = notcurses_init(&nopts, outfp_);
ASSERT_NE(nullptr, nc_);
n_ = notcurses_stdplane(nc_);
ASSERT_NE(nullptr, n_);
TEST_CASE("ZAxisTest") {
if(getenv("TERM") == nullptr){
return;
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_bannner = true;
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
void TearDown() override {
if(nc_){
EXPECT_EQ(0, notcurses_stop(nc_));
}
if(outfp_){
fclose(outfp_);
}
SUBCASE("StdPlaneOnly") {
struct ncplane* top = notcurses_top(nc_);
CHECK(n_ == top);
CHECK(!ncplane_below(top));
}
struct notcurses* nc_{};
struct ncplane* n_{};
FILE* outfp_{};
};
TEST_F(ZAxisTest, StdPlaneOnly) {
struct ncplane* top = notcurses_top(nc_);
EXPECT_EQ(n_, top);
EXPECT_EQ(nullptr, ncplane_below(top));
}
// if you want to move the plane which is already top+bottom to either, go ahead
SUBCASE("StdPlaneOnanism") {
CHECK(0 == ncplane_move_top(n_));
struct ncplane* top = notcurses_top(nc_);
CHECK(n_ == top);
CHECK(!ncplane_below(top));
CHECK(0 == ncplane_move_bottom(n_));
CHECK(!ncplane_below(n_));
}
// if you want to move the plane which is already top+bottom to either, go ahead
TEST_F(ZAxisTest, StdPlaneOnanism) {
EXPECT_EQ(0, ncplane_move_top(n_));
struct ncplane* top = notcurses_top(nc_);
EXPECT_EQ(n_, top);
EXPECT_EQ(nullptr, ncplane_below(top));
EXPECT_EQ(0, ncplane_move_bottom(n_));
EXPECT_EQ(nullptr, ncplane_below(n_));
}
// you can't place a plane above or below itself, stdplane or otherwise
SUBCASE("NoMoveSelf") {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
REQUIRE(np);
CHECK(ncplane_move_below(n_, n_));
CHECK(ncplane_move_above(n_, n_));
CHECK(ncplane_move_below(np, np));
CHECK(ncplane_move_above(np, np));
}
// you can't place a plane above or below itself, stdplane or otherwise
TEST_F(ZAxisTest, NoMoveSelf) {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
ASSERT_NE(nullptr, np);
EXPECT_NE(0, ncplane_move_below(n_, n_));
EXPECT_NE(0, ncplane_move_above(n_, n_));
EXPECT_NE(0, ncplane_move_below(np, np));
EXPECT_NE(0, ncplane_move_above(np, np));
}
// new planes ought be on the top
SUBCASE("NewPlaneOnTop") {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
REQUIRE(np);
struct ncplane* top = notcurses_top(nc_);
CHECK(np == top);
CHECK(n_ == ncplane_below(top));
CHECK(!ncplane_below(n_));
}
// new planes ought be on the top
TEST_F(ZAxisTest, NewPlaneOnTop) {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
ASSERT_NE(nullptr, np);
struct ncplane* top = notcurses_top(nc_);
EXPECT_EQ(np, top);
EXPECT_EQ(n_, ncplane_below(top));
EXPECT_EQ(nullptr, ncplane_below(n_));
}
// "move" top plane to top. everything ought remain the same.
SUBCASE("TopToTop") {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
REQUIRE(np);
struct ncplane* top = notcurses_top(nc_);
CHECK(np == top);
CHECK(n_ == ncplane_below(top));
CHECK(!ncplane_below(n_));
CHECK(!ncplane_move_top(np));
// verify it
top = notcurses_top(nc_);
CHECK(np == top);
CHECK(n_ == ncplane_below(top));
CHECK(!ncplane_below(n_));
}
// "move" top plane to top. everything ought remain the same.
TEST_F(ZAxisTest, TopToTop) {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
ASSERT_NE(nullptr, np);
struct ncplane* top = notcurses_top(nc_);
EXPECT_EQ(np, top);
EXPECT_EQ(n_, ncplane_below(top));
EXPECT_EQ(nullptr, ncplane_below(n_));
EXPECT_EQ(0, ncplane_move_top(np));
// verify it
top = notcurses_top(nc_);
EXPECT_EQ(np, top);
EXPECT_EQ(n_, ncplane_below(top));
EXPECT_EQ(nullptr, ncplane_below(n_));
}
// move top plane to bottom, and verify enumeration
SUBCASE("TopToBottom") {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
REQUIRE(np);
struct ncplane* top = notcurses_top(nc_);
CHECK(np == top);
CHECK(n_ == ncplane_below(top));
CHECK(!ncplane_below(n_));
CHECK(!ncplane_move_bottom(np));
top = notcurses_top(nc_);
CHECK(n_ == top);
CHECK(np == ncplane_below(top));
CHECK(!ncplane_below(np));
}
// move top plane to bottom, and verify enumeration
TEST_F(ZAxisTest, TopToBottom) {
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
ASSERT_NE(nullptr, np);
struct ncplane* top = notcurses_top(nc_);
EXPECT_EQ(np, top);
EXPECT_EQ(n_, ncplane_below(top));
EXPECT_EQ(nullptr, ncplane_below(n_));
EXPECT_EQ(0, ncplane_move_bottom(np));
top = notcurses_top(nc_);
EXPECT_EQ(n_, top);
EXPECT_EQ(np, ncplane_below(top));
EXPECT_EQ(nullptr, ncplane_below(np));
}
// verify that moving one above another, with no other changes, is reflected at
// render time (requires explicit damage maintenance from move functionality).
SUBCASE("ZAxisDamage") {
cell cat = CELL_TRIVIAL_INITIALIZER;
cell c = CELL_SIMPLE_INITIALIZER('x');
REQUIRE(!cell_set_fg_rgb(&c, 0xff, 0, 0));
REQUIRE(1 == ncplane_putc(n_, &c));
CHECK(!notcurses_render(nc_));
REQUIRE(!ncplane_cursor_move_yx(n_, 0, 0));
REQUIRE(1 == ncplane_at_cursor(n_, &cat));
REQUIRE(cell_simple_p(&cat));
REQUIRE('x' == cat.gcluster);
struct ncplane* n2 = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
REQUIRE(1 == cell_load(n2, &c, "y"));
REQUIRE(!cell_set_fg_rgb(&c, 0, 0xff, 0));
REQUIRE(1 == ncplane_putc(n2, &c));
CHECK_EQ(0, notcurses_render(nc_));
REQUIRE(!ncplane_cursor_move_yx(n2, 0, 0));
REQUIRE(1 == ncplane_at_cursor(n2, &cat));
REQUIRE('y' == cat.gcluster);
struct ncplane* n3 = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
REQUIRE(1 == cell_load(n3, &c, "z"));
REQUIRE(!cell_set_fg_rgb(&c, 0, 0, 0xff));
REQUIRE(1 == ncplane_putc(n3, &c));
CHECK(!notcurses_render(nc_));
REQUIRE(!ncplane_cursor_move_yx(n3, 0, 0));
REQUIRE(1 == ncplane_at_cursor(n3, &cat));
REQUIRE('z' == cat.gcluster);
// FIXME testing damage requires notcurses keeping a copy of the screen....
// FIXME move y atop z
// FIXME inspect
// FIXME move z atop y
// FIXME inspect
}
// verify that moving one above another, with no other changes, is reflected at
// render time (requires explicit damage maintenance from move functionality).
TEST_F(ZAxisTest, ZAxisDamage) {
cell cat = CELL_TRIVIAL_INITIALIZER;
cell c = CELL_SIMPLE_INITIALIZER('x');
ASSERT_EQ(0, cell_set_fg_rgb(&c, 0xff, 0, 0));
ASSERT_EQ(1, ncplane_putc(n_, &c));
EXPECT_EQ(0, notcurses_render(nc_));
ASSERT_EQ(0, ncplane_cursor_move_yx(n_, 0, 0));
ASSERT_EQ(1, ncplane_at_cursor(n_, &cat));
ASSERT_TRUE(cell_simple_p(&cat));
ASSERT_EQ('x', cat.gcluster);
struct ncplane* n2 = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
ASSERT_EQ(1, cell_load(n2, &c, "y"));
ASSERT_EQ(0, cell_set_fg_rgb(&c, 0, 0xff, 0));
ASSERT_EQ(1, ncplane_putc(n2, &c));
EXPECT_EQ(0, notcurses_render(nc_));
ASSERT_EQ(0, ncplane_cursor_move_yx(n2, 0, 0));
ASSERT_EQ(1, ncplane_at_cursor(n2, &cat));
ASSERT_EQ('y', cat.gcluster);
struct ncplane* n3 = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
ASSERT_EQ(1, cell_load(n3, &c, "z"));
ASSERT_EQ(0, cell_set_fg_rgb(&c, 0, 0, 0xff));
ASSERT_EQ(1, ncplane_putc(n3, &c));
EXPECT_EQ(0, notcurses_render(nc_));
ASSERT_EQ(0, ncplane_cursor_move_yx(n3, 0, 0));
ASSERT_EQ(1, ncplane_at_cursor(n3, &cat));
ASSERT_EQ('z', cat.gcluster);
// FIXME testing damage requires notcurses keeping a copy of the screen....
// FIXME move y atop z
// FIXME inspect
// FIXME move z atop y
// FIXME inspect
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
}

Loading…
Cancel
Save