xo-ordinaltree: work on gp<String>-key unit test
This commit is contained in:
parent
d13d2f54ed
commit
a3a73c3aa3
4 changed files with 334 additions and 29 deletions
|
|
@ -152,7 +152,7 @@ namespace xo {
|
|||
template <typename T>
|
||||
class ObjectVisitor<gp<T>> {
|
||||
public:
|
||||
void forward_children(gp<T> & target,
|
||||
static void forward_children(gp<T> & target,
|
||||
IAlloc * gc)
|
||||
{
|
||||
Object::_forward_inplace(target, gc);
|
||||
|
|
|
|||
|
|
@ -24,10 +24,14 @@ namespace xo {
|
|||
|
||||
template <typename T>
|
||||
auto operator()(const T& a, const T& b) const -> std::strong_ordering {
|
||||
if constexpr (std::three_way_comparable<T>) {
|
||||
return a <=> b;
|
||||
} else {
|
||||
if (a < b) return std::strong_ordering::less;
|
||||
if (b < a) return std::strong_ordering::greater;
|
||||
return std::strong_ordering::equal;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ namespace xo {
|
|||
|
||||
} // TEST_CASE(rbtree-gc-1)
|
||||
|
||||
TEST_CASE("gp-string-key-allocate", "[gc]")
|
||||
TEST_CASE("rbnode-gc-string-key", "[gc][redblacktree]")
|
||||
{
|
||||
up<GC> gc = GC::make(
|
||||
{
|
||||
|
|
@ -333,6 +333,7 @@ namespace xo {
|
|||
|
||||
REQUIRE(gc->native_gc_statistics().n_gc() == 0);
|
||||
|
||||
{
|
||||
gp<String> s0 = String::copy(gc.get(), "hello");
|
||||
REQUIRE(s0->length() == 5);
|
||||
|
||||
|
|
@ -352,22 +353,267 @@ namespace xo {
|
|||
REQUIRE(s0 <= s1);
|
||||
REQUIRE((s0 > s1) == false);
|
||||
REQUIRE((s0 >= s1) == false);
|
||||
}
|
||||
|
||||
/* verify that we can allocate gp<Strings> through gc::allocator template.
|
||||
*/
|
||||
|
||||
{
|
||||
static_assert(std::is_constructible_v<gp<String>>);
|
||||
static_assert(std::is_constructible_v<const gp<String>>);
|
||||
static_assert(std::is_constructible_v<std::pair<const gp<String>, double>>);
|
||||
}
|
||||
|
||||
//using Allocator = xo::gc::allocator<gp<String>>;
|
||||
//using Allocator1 = xo::gc::allocator<std::pair<gp<String>, double>>;
|
||||
|
||||
using RbTree = RedBlackTree<gp<String>,
|
||||
double,
|
||||
SumReduce<double>,
|
||||
xo::tree::DefaultThreeWayCompare,
|
||||
xo::gc::allocator<std::pair<const gp<String>, double>>>;
|
||||
|
||||
//using NodeAllocator = xo::gc::allocator<std::pair<const gp<String>, int>>;
|
||||
//using RbNode = Node<gp<String>, int, SumReduce<int>, NodeAllocator>;
|
||||
xo::gc::allocator<RbTree::node_type> allocator(gc.get());
|
||||
|
||||
/* 1. verify that tree nodes can be constructed */
|
||||
{
|
||||
RbTree::RbNode test_node;
|
||||
}
|
||||
|
||||
/* 1b. verify that tree node can be created via gc allocator */
|
||||
{
|
||||
RbTree::RbNode * test_node_ptr = new (MMPtr(gc.get())) RbTree::RbNode();
|
||||
REQUIRE(test_node_ptr);
|
||||
}
|
||||
|
||||
/* 1c. verify tree node can be constructed via allocator traits.
|
||||
*
|
||||
* Reminder: {} expressions won't deduce template arguments
|
||||
*/
|
||||
{
|
||||
RbTree::RbNode test_node;
|
||||
|
||||
gp<String> s1 = String::copy(gc.get(), "hello, world!");
|
||||
|
||||
RbTree::node_allocator_traits::construct(allocator,
|
||||
&test_node,
|
||||
RbTree::RbNode::value_type(s1, 0.0),
|
||||
RbTree::RbNode::rvpair_type{0.0, 0.0});
|
||||
}
|
||||
}
|
||||
|
||||
/* test RbTree with gc allocator,
|
||||
*
|
||||
* do lots of GC-requesting, to create old->new xgen pointers.
|
||||
*/
|
||||
TEST_CASE("rbtree-gc-string-key-1", "[gc][redblacktree][string]")
|
||||
{
|
||||
using RbTree = RedBlackTree<gp<String>,
|
||||
double,
|
||||
SumReduce<double>,
|
||||
xo::tree::DefaultThreeWayCompare,
|
||||
xo::gc::allocator<std::pair<const gp<String>, double>>>;
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
const Testcase_RbTree & tc = s_testcase_v[i_tc];
|
||||
|
||||
std::uint64_t seed = 8813374093428528487ULL;
|
||||
auto rgen = xo::rng::xoshiro256ss(seed);
|
||||
|
||||
for (std::uint32_t n=0; n<=1024;) {
|
||||
bool ok_flag = false;
|
||||
|
||||
for (std::uint32_t attention = 0; !ok_flag && (attention < 2); ++attention) {
|
||||
bool debug_flag = c_debug_flag || (attention == 1);
|
||||
|
||||
scope log(XO_DEBUG2(debug_flag, "rbtree-gc-1"), xtag("i_tc", i_tc), xtag("n", n));
|
||||
|
||||
INFO(tostr(xtag("i_tc", i_tc), xtag("n", n)));
|
||||
|
||||
ok_flag = true; // unless contradicted below
|
||||
|
||||
up<GC> gc = GC::make(
|
||||
{
|
||||
.initial_nursery_z_ = tc.nursery_z_,
|
||||
.initial_tenured_z_ = tc.tenured_z_,
|
||||
.incr_gc_threshold_ = tc.incr_gc_threshold_,
|
||||
.full_gc_threshold_ = tc.full_gc_threshold_,
|
||||
.debug_flag_ = debug_flag
|
||||
}
|
||||
);
|
||||
REQUIRE(gc.get());
|
||||
gc->disable_gc();
|
||||
|
||||
REQUIRE(gc->native_gc_statistics().n_gc() == 0);
|
||||
|
||||
RbTree::key_compare compare;
|
||||
xo::gc::allocator<RbTree> allocator(gc.get());
|
||||
|
||||
gp<RbTree> rbtree = RbTree::make(compare, allocator, c_debug_flag);
|
||||
|
||||
gc->add_gc_root_dwim(&rbtree);
|
||||
|
||||
/** strings for key values.
|
||||
* (make a List of these to keep them alive ?)
|
||||
**/
|
||||
std::vector<gp<String>> keys(n);
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
char stringdata[80];
|
||||
snprintf(stringdata, sizeof(stringdata), "key %lu yek", i);
|
||||
|
||||
gp<String> key_i = String::copy(gc.get(), stringdata);
|
||||
|
||||
keys[i] = key_i;
|
||||
}
|
||||
|
||||
REQUIRE(rbtree.get() != nullptr);
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
|
||||
if (tc.do_extra_gc_) {
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
gc->request_gc(gc::generation::nursery);
|
||||
REQUIRE(gc->is_gc_pending());
|
||||
REQUIRE(gc->enable_gc_once());
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
}
|
||||
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
|
||||
{
|
||||
INFO("insert phase A - random_inserts(0, n, 2, ..)");
|
||||
|
||||
/* insert even integers in [0, n), in random order **/
|
||||
ok_flag &= TreeUtil<RbTree>::random_inserts(0, n, 2,
|
||||
debug_flag,
|
||||
&rgen,
|
||||
rbtree.get());
|
||||
|
||||
if (tc.do_extra_gc_) {
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
gc->request_gc(gc::generation::nursery);
|
||||
REQUIRE(gc->is_gc_pending());
|
||||
REQUIRE(gc->enable_gc_once());
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
}
|
||||
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
|
||||
}
|
||||
|
||||
#ifdef NOT_YET
|
||||
if (n > 0) {
|
||||
INFO("insert phase B - random_inserts(1, n+1, ..)");
|
||||
|
||||
/* insert odd integers in [1, n+1), in random order **/
|
||||
ok_flag &= TreeUtil<RbTree>::random_inserts(1, n+1, 2,
|
||||
debug_flag,
|
||||
&rgen,
|
||||
rbtree.get());
|
||||
|
||||
if (tc.do_extra_gc_) {
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
gc->request_gc(gc::generation::nursery);
|
||||
REQUIRE(gc->is_gc_pending());
|
||||
REQUIRE(gc->enable_gc_once());
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
}
|
||||
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
}
|
||||
|
||||
/* check iterator traverses [0..n-1] in both directions */
|
||||
ok_flag &= TreeUtil<RbTree>::check_ordinal_lookup(0 /*dvalue*/,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* verify end-to-end iteration */
|
||||
ok_flag &= TreeUtil<RbTree>::check_bidirectional_iterator(0,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* check reduced sums for each cut */
|
||||
ok_flag &= TreeUtil<RbTree>::check_reduced_sum(0,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* verify read-only variant of operator[] */
|
||||
ok_flag &= TreeUtil<RbTree>::random_lookups(debug_flag,
|
||||
*rbtree,
|
||||
&rgen);
|
||||
|
||||
/* re-verify lookup on (better-be-monotonic) reduced sums
|
||||
* remove doubt that operator[] changed something.
|
||||
*/
|
||||
ok_flag &= TreeUtil<RbTree>::check_ordinal_lookup(0 /*dvalue*/,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* re-verify end-to-end iteration, so we can say we did */
|
||||
ok_flag &= TreeUtil<RbTree>::check_bidirectional_iterator(0,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* make random updates, along with basic consistency checks */
|
||||
ok_flag &= TreeUtil<RbTree>::random_updates(10000,
|
||||
debug_flag,
|
||||
rbtree.get(),
|
||||
&rgen);
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
|
||||
|
||||
if (tc.do_extra_gc_) {
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
gc->request_gc(gc::generation::nursery);
|
||||
REQUIRE(gc->is_gc_pending());
|
||||
REQUIRE(gc->enable_gc_once());
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
}
|
||||
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
|
||||
/* verify that updates changed tree contents in expected way */
|
||||
ok_flag &= TreeUtil<RbTree>::check_ordinal_lookup(10000 /*dvalue*/,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* verify end-to-end iteration */
|
||||
ok_flag &= TreeUtil<RbTree>::check_bidirectional_iterator(10000,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* check reduced sums for each cut */
|
||||
ok_flag &= TreeUtil<RbTree>::check_reduced_sum(10000,
|
||||
debug_flag,
|
||||
*rbtree);
|
||||
|
||||
/* verify behavior of read/write variant of operator[] */
|
||||
ok_flag &= TreeUtil<RbTree>::random_removes(debug_flag,
|
||||
&rgen,
|
||||
rbtree.get());
|
||||
|
||||
if (tc.do_extra_gc_) {
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
gc->request_gc(gc::generation::nursery);
|
||||
REQUIRE(gc->is_gc_pending());
|
||||
REQUIRE(gc->enable_gc_once());
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
}
|
||||
|
||||
/* verify iteration one more time --
|
||||
* remove doubt w.r.t.
|
||||
*/
|
||||
REQUIRE(rbtree->verify_ok(debug_flag));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
n = 1;
|
||||
else
|
||||
n = 2*n;
|
||||
}
|
||||
}
|
||||
|
||||
} // TEST_CASE(rbtree-gc-string-key-1)
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namesapce xo*/
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,59 @@ namespace utest {
|
|||
return ok_flag;
|
||||
} /*test_clear*/
|
||||
|
||||
static bool
|
||||
random_inserts(const std::vector<typename Tree::key_type> & keys,
|
||||
bool catch_flag,
|
||||
xo::rng::xoshiro256ss * p_rgen,
|
||||
Tree * p_tree)
|
||||
{
|
||||
bool ok_flag = true;
|
||||
|
||||
xo::scope log(XO_DEBUG(catch_flag), xtag("n-keys", keys.size()));
|
||||
|
||||
REQUIRE_ORFAIL(ok_flag, catch_flag, p_tree->verify_ok(catch_flag));
|
||||
|
||||
/* n keys */
|
||||
std::size_t n = keys.size();
|
||||
/* permute keys, remembering original position */
|
||||
std::vector<std::pair<std::size_t, typename Tree::key_type>> permuted_keys(n);
|
||||
uint32_t i = 0;
|
||||
for (const auto & x : keys) {
|
||||
permuted_keys[i] = std::make_pair(i, x);
|
||||
}
|
||||
/* shuffle to get unpredictable insert order */
|
||||
std::shuffle(keys.begin(), keys.end(), *p_rgen);
|
||||
|
||||
size_t tree_z0 = p_tree->size();
|
||||
|
||||
/* insert keys in permuted order */
|
||||
uint32_t i = 1;
|
||||
for(const auto & pr_i : permuted_keys) {
|
||||
log && log(xtag("i", i), xtag("ord", pr_i.first), xtag("n", n), xtag("key", pr_i.second));
|
||||
|
||||
/* .first: iterator @ insert position
|
||||
* .second: true if insert occurred (ịẹ tree size incremented)
|
||||
*/
|
||||
auto insert_result = p_tree->insert(typename Tree::value_type(pr_i.second, 10.0 * i));
|
||||
|
||||
REQUIRE_ORFAIL(ok_flag, catch_flag, p_tree->verify_ok(catch_flag));
|
||||
|
||||
REQUIRE_ORFAIL(ok_flag, catch_flag, insert_result.second);
|
||||
|
||||
/* verify: iterator returned by Treẹinsert(), refers to inserted key,value pair */
|
||||
log && log(xtag("iter.node", insert_result.first.node()));
|
||||
|
||||
REQUIRE_ORFAIL(ok_flag, catch_flag, insert_result.first->first == pr_i.second);
|
||||
REQUIRE_ORFAIL(ok_flag, catch_flag, insert_result.first->second == 10.0 * i);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE_ORFAIL(ok_flag, catch_flag, p_tree->size() == tree_z0 + n);
|
||||
|
||||
return ok_flag;
|
||||
}
|
||||
|
||||
/* do
|
||||
* n = (hi - lo) / k
|
||||
* random inserts (taken from *p_rgen) into *p_rbtreẹ
|
||||
|
|
@ -94,6 +147,8 @@ namespace utest {
|
|||
xo::rng::xoshiro256ss * p_rgen,
|
||||
Tree * p_tree)
|
||||
{
|
||||
// TODO: rewrite in terms of 'random_inserts with explicit vector'.
|
||||
|
||||
using xo::xtag;
|
||||
|
||||
bool ok_flag = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue