notcurses/tests/egcpool.cpp
Nick Black ce2a390b52
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
2019-12-27 17:20:20 -05:00

209 lines
5.9 KiB
C++

#include <vector>
#include <notcurses.h>
#include "egcpool.h"
#include "main.h"
TEST_CASE("EGCpool") {
egcpool pool_{};
SUBCASE("Initialized") {
CHECK(!pool_.pool);
CHECK(!pool_.poolsize);
CHECK(!pool_.poolwrite);
CHECK(!pool_.poolused);
}
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
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);
}
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);
}
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);
}
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.
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
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));
}
}
// 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));
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;
}
}
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));
}
CHECK(candidates.size() / 13 > no);
}
// common cleanup
egcpool_dump(&pool_);
}