summaryrefslogtreecommitdiffstats
path: root/tests/include
diff options
context:
space:
mode:
authorDouglas Rumbaugh <dbr4@psu.edu>2024-01-12 14:08:33 -0500
committerDouglas Rumbaugh <dbr4@psu.edu>2024-01-12 14:09:45 -0500
commitc4514c2e62a711189cf3c914297885d97fb51a09 (patch)
tree9cea57c0ce23c6fdaf627495c7bd4f4f8dc9019d /tests/include
parent3a89d7f6ea2679ff7b9bb1e3c37da9480be6c115 (diff)
downloaddynamic-extension-c4514c2e62a711189cf3c914297885d97fb51a09.tar.gz
Initial pass at unit test refactoring
Restructured unit tests to be a bit more modular. I have some further plans to expand on this, particular for the query tests (including both shard and framework level test functions that can be injected at will).
Diffstat (limited to 'tests/include')
-rw-r--r--tests/include/dynamic_extension.h341
-rw-r--r--tests/include/rangequery.h179
-rw-r--r--tests/include/shard_standard.h198
-rw-r--r--tests/include/testing.h218
4 files changed, 936 insertions, 0 deletions
diff --git a/tests/include/dynamic_extension.h b/tests/include/dynamic_extension.h
new file mode 100644
index 0000000..5a08f5a
--- /dev/null
+++ b/tests/include/dynamic_extension.h
@@ -0,0 +1,341 @@
+/*
+ * tests/include/dynamic_extension.h
+ *
+ * Standardized unit tests for DynamicExtension objects
+ *
+ * Copyright (C) 2023 Douglas Rumbaugh <drumbaugh@psu.edu>
+ *
+ * Distributed under the Modified BSD License.
+ *
+ * WARNING: This file must be included in the main unit test set
+ * after the definition of an appropriate Shard, Query, and Rec
+ * type. In particular, Rec needs to implement the key-value
+ * pair interface. For other types of record, you'll need to
+ * use a different set of unit tests.
+ */
+#pragma once
+
+/*
+ * Uncomment these lines temporarily to remove errors in this file
+ * temporarily for development purposes. They should be removed prior
+ * to building, to ensure no duplicate definitions. These includes/defines
+ * should be included in the source file that includes this one, above the
+ * include statement.
+ */
+//#include "testing.h"
+//#include "framework/DynamicExtension.h"
+//#include "framework/scheduling/SerialScheduler.h"
+//#include "shard/ISAMTree.h"
+//#include "query/rangequery.h"
+//#include <check.h>
+//using namespace de;
+//typedef DynamicExtension<Rec, ISAMTree<Rec>, rq::Query<ISAMTree<Rec>, Rec>, LayoutPolicy::TEIRING, DeletePolicy::TAGGING, SerialScheduler> DE;
+
+
+START_TEST(t_create)
+{
+ auto test_de = new DE(100, 1000, 2);
+
+ ck_assert_ptr_nonnull(test_de);
+ ck_assert_int_eq(test_de->get_record_count(), 0);
+ ck_assert_int_eq(test_de->get_height(), 0);
+
+ delete test_de;
+}
+END_TEST
+
+
+START_TEST(t_insert)
+{
+ auto test_de = new DE(100, 1000, 2);
+
+ uint64_t key = 0;
+ uint32_t val = 0;
+ for (size_t i=0; i<100; i++) {
+ Rec r = {key, val};
+ ck_assert_int_eq(test_de->insert(r), 1);
+ key++;
+ val++;
+ }
+
+ ck_assert_int_eq(test_de->get_height(), 0);
+ ck_assert_int_eq(test_de->get_record_count(), 100);
+
+ delete test_de;
+}
+END_TEST
+
+
+START_TEST(t_debug_insert)
+{
+ auto test_de = new DE(100, 1000, 2);
+
+ uint64_t key = 0;
+ uint32_t val = 0;
+ for (size_t i=0; i<1000; i++) {
+ Rec r = {key, val};
+ ck_assert_int_eq(test_de->insert(r), 1);
+ ck_assert_int_eq(test_de->get_record_count(), i+1);
+ key++;
+ val++;
+ }
+
+ delete test_de;
+}
+END_TEST
+
+
+START_TEST(t_insert_with_mem_merges)
+{
+ auto test_de = new DE(100, 1000, 2);
+
+ uint64_t key = 0;
+ uint32_t val = 0;
+ for (size_t i=0; i<300; i++) {
+ Rec r = {key, val};
+ ck_assert_int_eq(test_de->insert(r), 1);
+ key++;
+ val++;
+ }
+
+ test_de->await_next_epoch();
+
+ ck_assert_int_eq(test_de->get_record_count(), 300);
+ ck_assert_int_eq(test_de->get_height(), 1);
+
+ delete test_de;
+}
+END_TEST
+
+
+START_TEST(t_range_query)
+{
+ auto test_de = new DE(100, 1000, 2);
+ size_t n = 10000;
+
+ std::vector<uint64_t> keys;
+ for (size_t i=0; i<n; i++) {
+ keys.push_back(rand() % 25000);
+ }
+
+ std::random_device rd;
+ std::mt19937 gen{rd()};
+ std::shuffle(keys.begin(), keys.end(), gen);
+
+ for (size_t i=0; i<keys.size(); i++) {
+ Rec r = {keys[i], (uint32_t) i};
+ ck_assert_int_eq(test_de->insert(r), 1);
+ }
+
+ test_de->await_next_epoch();
+
+ std::sort(keys.begin(), keys.end());
+
+ auto idx = rand() % (keys.size() - 250);
+
+ uint64_t lower_key = keys[idx];
+ uint64_t upper_key = keys[idx + 250];
+
+ rq::Parms<Rec> p;
+ p.lower_bound = lower_key;
+ p.upper_bound = upper_key;
+
+ auto result = test_de->query(&p);
+ auto r = result.get();
+ std::sort(r.begin(), r.end());
+ ck_assert_int_eq(r.size(), 251);
+
+ for (size_t i=0; i<r.size(); i++) {
+ ck_assert_int_eq(r[i].key, keys[idx + i]);
+ }
+
+ delete test_de;
+}
+END_TEST
+
+
+START_TEST(t_tombstone_merging_01)
+{
+ size_t reccnt = 100000;
+ auto test_de = new DE(100, 1000, 2);
+
+ auto rng = gsl_rng_alloc(gsl_rng_mt19937);
+
+ std::set<std::pair<uint64_t, uint32_t>> records;
+ std::set<std::pair<uint64_t, uint32_t>> to_delete;
+ std::set<std::pair<uint64_t, uint32_t>> deleted;
+
+ while (records.size() < reccnt) {
+ uint64_t key = rand();
+ uint32_t val = rand();
+
+ if (records.find({key, val}) != records.end()) continue;
+
+ records.insert({key, val});
+ }
+
+ size_t deletes = 0;
+ size_t cnt=0;
+ for (auto rec : records) {
+ Rec r = {rec.first, rec.second};
+ ck_assert_int_eq(test_de->insert(r), 1);
+
+ if (gsl_rng_uniform(rng) < 0.05 && !to_delete.empty()) {
+ std::vector<std::pair<uint64_t, uint32_t>> del_vec;
+ std::sample(to_delete.begin(), to_delete.end(), std::back_inserter(del_vec), 3, std::mt19937{std::random_device{}()});
+
+ for (size_t i=0; i<del_vec.size(); i++) {
+ Rec dr = {del_vec[i].first, del_vec[i].second};
+ test_de->erase(dr);
+ deletes++;
+ to_delete.erase(del_vec[i]);
+ deleted.insert(del_vec[i]);
+ }
+ }
+
+ if (gsl_rng_uniform(rng) < 0.25 && deleted.find(rec) == deleted.end()) {
+ to_delete.insert(rec);
+ }
+ }
+
+ test_de->await_next_epoch();
+
+ ck_assert(test_de->validate_tombstone_proportion());
+
+ gsl_rng_free(rng);
+ delete test_de;
+}
+END_TEST
+
+DE *create_test_tree(size_t reccnt, size_t memlevel_cnt) {
+ auto rng = gsl_rng_alloc(gsl_rng_mt19937);
+
+ auto test_de = new DE(1000, 10000, 2);
+
+ std::set<Rec> records;
+ std::set<Rec> to_delete;
+ std::set<Rec> deleted;
+
+ while (records.size() < reccnt) {
+ uint64_t key = rand();
+ uint32_t val = rand();
+
+ if (records.find({key, val}) != records.end()) continue;
+
+ records.insert({key, val});
+ }
+
+ size_t deletes = 0;
+ for (auto rec : records) {
+ ck_assert_int_eq(test_de->insert(rec), 1);
+
+ if (gsl_rng_uniform(rng) < 0.05 && !to_delete.empty()) {
+ std::vector<Rec> del_vec;
+ std::sample(to_delete.begin(), to_delete.end(), std::back_inserter(del_vec), 3, std::mt19937{std::random_device{}()});
+
+ for (size_t i=0; i<del_vec.size(); i++) {
+ test_de->erase(del_vec[i]);
+ deletes++;
+ to_delete.erase(del_vec[i]);
+ deleted.insert(del_vec[i]);
+ }
+ }
+
+ if (gsl_rng_uniform(rng) < 0.25 && deleted.find(rec) == deleted.end()) {
+ to_delete.insert(rec);
+ }
+ }
+
+ gsl_rng_free(rng);
+
+ return test_de;
+}
+
+START_TEST(t_static_structure)
+{
+ auto rng = gsl_rng_alloc(gsl_rng_mt19937);
+
+ size_t reccnt = 100000;
+ auto test_de = new DE(100, 1000, 2);
+
+ std::set<Rec> records;
+ std::set<Rec> to_delete;
+ std::set<Rec> deleted;
+
+ while (records.size() < reccnt) {
+ uint64_t key = rand();
+ uint32_t val = rand();
+
+ if (records.find({key, val}) != records.end()) continue;
+
+ records.insert({key, val});
+ }
+
+ size_t deletes = 0;
+ size_t t_reccnt = 0;
+ size_t k=0;
+ for (auto rec : records) {
+ k++;
+ ck_assert_int_eq(test_de->insert(rec), 1);
+ t_reccnt++;
+
+ if (gsl_rng_uniform(rng) < 0.05 && !to_delete.empty()) {
+ std::vector<Rec> del_vec;
+ std::sample(to_delete.begin(), to_delete.end(), std::back_inserter(del_vec), 3, std::mt19937{std::random_device{}()});
+
+ for (size_t i=0; i<del_vec.size(); i++) {
+ ck_assert_int_eq(test_de->erase(del_vec[i]), 1);
+
+ deletes++;
+ to_delete.erase(del_vec[i]);
+ deleted.insert(del_vec[i]);
+ }
+ }
+
+ if (gsl_rng_uniform(rng) < 0.25 && deleted.find(rec) == deleted.end()) {
+ to_delete.insert(rec);
+ }
+ }
+
+ auto flat = test_de->create_static_structure();
+ ck_assert_int_eq(flat->get_record_count(), reccnt - deletes);
+
+ uint64_t prev_key = 0;
+ for (size_t i=0; i<flat->get_record_count(); i++) {
+ auto k = flat->get_record_at(i)->rec.key;
+ ck_assert_int_ge(k, prev_key);
+ prev_key = k;
+ }
+
+ gsl_rng_free(rng);
+ delete flat;
+ delete test_de;
+}
+END_TEST
+
+
+static void inject_dynamic_extension_tests(Suite *suite) {
+ TCase *create = tcase_create("de::DynamicExtension::constructor Testing");
+ tcase_add_test(create, t_create);
+ suite_add_tcase(suite, create);
+
+ TCase *insert = tcase_create("de::DynamicExtension::insert Testing");
+ tcase_add_test(insert, t_insert);
+ tcase_add_test(insert, t_insert_with_mem_merges);
+ tcase_add_test(insert, t_debug_insert);
+ suite_add_tcase(suite, insert);
+
+ TCase *query = tcase_create("de::DynamicExtension::range_query Testing");
+ tcase_add_test(query, t_range_query);
+ suite_add_tcase(suite, query);
+
+ TCase *ts = tcase_create("de::DynamicExtension::tombstone_compaction Testing");
+ tcase_add_test(ts, t_tombstone_merging_01);
+ tcase_set_timeout(ts, 500);
+ suite_add_tcase(suite, ts);
+
+ TCase *flat = tcase_create("de::DynamicExtension::create_static_structure Testing");
+ tcase_add_test(flat, t_static_structure);
+ tcase_set_timeout(flat, 500);
+ suite_add_tcase(suite, flat);
+}
diff --git a/tests/include/rangequery.h b/tests/include/rangequery.h
new file mode 100644
index 0000000..3c7e7e0
--- /dev/null
+++ b/tests/include/rangequery.h
@@ -0,0 +1,179 @@
+/*
+ * tests/include/rangequery.h
+ *
+ * Standardized unit tests for range queries against supporting
+ * shard types
+ *
+ * Copyright (C) 2023 Douglas Rumbaugh <drumbaugh@psu.edu>
+ *
+ * Distributed under the Modified BSD License.
+ *
+ * WARNING: This file must be included in the main unit test set
+ * after the definition of an appropriate Shard and Rec
+ * type. In particular, Rec needs to implement the key-value
+ * pair interface and Shard needs to support lower_bound.
+ * For other types of record and shard, you'll need to
+ * use a different set of unit tests.
+ */
+#pragma once
+
+/*
+ * Uncomment these lines temporarily to remove errors in this file
+ * temporarily for development purposes. They should be removed prior
+ * to building, to ensure no duplicate definitions. These includes/defines
+ * should be included in the source file that includes this one, above the
+ * include statement.
+ */
+//#include "shard/ISAMTree.h"
+//#include "query/rangequery.h"
+//#include "testing.h"
+//#include <check.h>
+//using namespace de;
+//typedef ISAMTree<Rec> Shard;
+
+
+START_TEST(t_range_query)
+{
+ auto buffer = create_sequential_mbuffer<Rec>(100, 1000);
+ auto shard = Shard(buffer->get_buffer_view());
+
+ rq::Parms<Rec> parms;
+ parms.lower_bound = 300;
+ parms.upper_bound = 500;
+
+ auto state = rq::Query<Shard, Rec>::get_query_state(&shard, &parms);
+ auto result = rq::Query<Shard, Rec>::query(&shard, state, &parms);
+ rq::Query<Shard, Rec>::delete_query_state(state);
+
+ ck_assert_int_eq(result.size(), parms.upper_bound - parms.lower_bound + 1);
+ for (size_t i=0; i<result.size(); i++) {
+ ck_assert_int_le(result[i].rec.key, parms.upper_bound);
+ ck_assert_int_ge(result[i].rec.key, parms.lower_bound);
+ }
+
+ delete buffer;
+}
+END_TEST
+
+
+START_TEST(t_buffer_range_query)
+{
+ auto buffer = create_sequential_mbuffer<Rec>(100, 1000);
+
+ rq::Parms<Rec> parms;
+ parms.lower_bound = 300;
+ parms.upper_bound = 500;
+
+ auto state = rq::Query<Shard, Rec>::get_buffer_query_state(buffer->get_buffer_view(), &parms);
+ auto result = rq::Query<Shard, Rec>::buffer_query(state, &parms);
+ rq::Query<Shard, Rec>::delete_buffer_query_state(state);
+
+ ck_assert_int_eq(result.size(), parms.upper_bound - parms.lower_bound + 1);
+ for (size_t i=0; i<result.size(); i++) {
+ ck_assert_int_le(result[i].rec.key, parms.upper_bound);
+ ck_assert_int_ge(result[i].rec.key, parms.lower_bound);
+ }
+
+ delete buffer;
+}
+END_TEST
+
+
+START_TEST(t_range_query_merge)
+{
+ auto buffer1 = create_sequential_mbuffer<Rec>(100, 200);
+ auto buffer2 = create_sequential_mbuffer<Rec>(400, 1000);
+
+ auto shard1 = Shard(buffer1->get_buffer_view());
+ auto shard2 = Shard(buffer2->get_buffer_view());
+
+ rq::Parms<Rec> parms;
+ parms.lower_bound = 150;
+ parms.upper_bound = 500;
+
+ size_t result_size = parms.upper_bound - parms.lower_bound + 1 - 200;
+
+ auto state1 = rq::Query<Shard, Rec>::get_query_state(&shard1, &parms);
+ auto state2 = rq::Query<Shard, Rec>::get_query_state(&shard2, &parms);
+
+ std::vector<std::vector<de::Wrapped<Rec>>> results(2);
+ results[0] = rq::Query<Shard, Rec>::query(&shard1, state1, &parms);
+ results[1] = rq::Query<Shard, Rec>::query(&shard2, state2, &parms);
+
+ rq::Query<Shard, Rec>::delete_query_state(state1);
+ rq::Query<Shard, Rec>::delete_query_state(state2);
+
+ ck_assert_int_eq(results[0].size() + results[1].size(), result_size);
+
+ std::vector<std::vector<Wrapped<Rec>>> proc_results;
+
+ for (size_t j=0; j<results.size(); j++) {
+ proc_results.emplace_back(std::vector<Wrapped<Rec>>());
+ for (size_t i=0; i<results[j].size(); i++) {
+ proc_results[j].emplace_back(results[j][i]);
+ }
+ }
+
+ auto result = rq::Query<Shard, Rec>::merge(proc_results, nullptr);
+ std::sort(result.begin(), result.end());
+
+ ck_assert_int_eq(result.size(), result_size);
+ auto key = parms.lower_bound;
+ for (size_t i=0; i<result.size(); i++) {
+ ck_assert_int_eq(key++, result[i].key);
+ if (key == 200) {
+ key = 400;
+ }
+ }
+
+ delete buffer1;
+ delete buffer2;
+}
+END_TEST
+
+
+START_TEST(t_lower_bound)
+{
+ auto buffer1 = create_sequential_mbuffer<Rec>(100, 200);
+ auto buffer2 = create_sequential_mbuffer<Rec>(400, 1000);
+
+ Shard *shards[2];
+
+ auto shard1 = Shard(buffer1->get_buffer_view());
+ auto shard2 = Shard(buffer2->get_buffer_view());
+
+ shards[0] = &shard1;
+ shards[1] = &shard2;
+
+ auto merged = Shard(shards, 2);
+
+ for (size_t i=100; i<1000; i++) {
+ Rec r;
+ r.key = i;
+ r.value = i;
+
+ auto idx = merged.get_lower_bound(i);
+
+ assert(idx < merged.get_record_count());
+
+ auto res = merged.get_record_at(idx);
+
+ if (i >=200 && i <400) {
+ ck_assert_int_lt(res->rec.key, i);
+ } else {
+ ck_assert_int_eq(res->rec.key, i);
+ }
+ }
+
+ delete buffer1;
+ delete buffer2;
+}
+END_TEST
+
+static void inject_rangequery_tests(Suite *suite) {
+ TCase *range_query = tcase_create("Range Query Testing");
+ tcase_add_test(range_query, t_range_query);
+ tcase_add_test(range_query, t_buffer_range_query);
+ tcase_add_test(range_query, t_range_query_merge);
+ suite_add_tcase(suite, range_query);
+}
diff --git a/tests/include/shard_standard.h b/tests/include/shard_standard.h
new file mode 100644
index 0000000..047a7b5
--- /dev/null
+++ b/tests/include/shard_standard.h
@@ -0,0 +1,198 @@
+/*
+ * tests/include/shard_standard.h
+ *
+ * Standardized unit tests for Shard objects
+ *
+ * Copyright (C) 2023 Douglas Rumbaugh <drumbaugh@psu.edu>
+ *
+ * Distributed under the Modified BSD License.
+ *
+ * WARNING: This file must be included in the main unit test set
+ * after the definition of an appropriate Shard and Rec
+ * type. In particular, Rec needs to implement the key-value
+ * pair interface. For other types of record, you'll need to
+ * use a different set of unit tests.
+ */
+#pragma once
+
+/*
+ * Uncomment these lines temporarily to remove errors in this file
+ * temporarily for development purposes. They should be removed prior
+ * to building, to ensure no duplicate definitions. These includes/defines
+ * should be included in the source file that includes this one, above the
+ * include statement.
+ */
+//#include "shard/ISAMTree.h"
+//#include "testing.h"
+//#include <check.h>
+//using namespace de;
+//typedef ISAMTree<Rec> Shard;
+
+START_TEST(t_mbuffer_init)
+{
+ auto buffer = new MutableBuffer<Rec>(512, 1024);
+ for (uint64_t i = 512; i > 0; i--) {
+ uint32_t v = i;
+ buffer->append({i,v, 1});
+ }
+
+ for (uint64_t i = 1; i <= 256; ++i) {
+ uint32_t v = i;
+ buffer->append({i, v, 1}, true);
+ }
+
+ for (uint64_t i = 257; i <= 512; ++i) {
+ uint32_t v = i + 1;
+ buffer->append({i, v, 1});
+ }
+
+ Shard* shard = new Shard(buffer->get_buffer_view());
+ ck_assert_uint_eq(shard->get_record_count(), 512);
+
+ delete buffer;
+ delete shard;
+}
+
+
+START_TEST(t_shard_init)
+{
+ size_t n = 512;
+ auto mbuffer1 = create_test_mbuffer<Rec>(n);
+ auto mbuffer2 = create_test_mbuffer<Rec>(n);
+ auto mbuffer3 = create_test_mbuffer<Rec>(n);
+
+ auto shard1 = new Shard(mbuffer1->get_buffer_view());
+ auto shard2 = new Shard(mbuffer2->get_buffer_view());
+ auto shard3 = new Shard(mbuffer3->get_buffer_view());
+
+ Shard* shards[3] = {shard1, shard2, shard3};
+ auto shard4 = new Shard(shards, 3);
+
+ ck_assert_int_eq(shard4->get_record_count(), n * 3);
+ ck_assert_int_eq(shard4->get_tombstone_count(), 0);
+
+ size_t total_cnt = 0;
+ size_t shard1_idx = 0;
+ size_t shard2_idx = 0;
+ size_t shard3_idx = 0;
+
+ for (size_t i = 0; i < shard4->get_record_count(); ++i) {
+ auto rec1 = shard1->get_record_at(shard1_idx);
+ auto rec2 = shard2->get_record_at(shard2_idx);
+ auto rec3 = shard3->get_record_at(shard3_idx);
+
+ auto cur_rec = shard4->get_record_at(i);
+
+ if (shard1_idx < n && cur_rec->rec == rec1->rec) {
+ ++shard1_idx;
+ } else if (shard2_idx < n && cur_rec->rec == rec2->rec) {
+ ++shard2_idx;
+ } else if (shard3_idx < n && cur_rec->rec == rec3->rec) {
+ ++shard3_idx;
+ } else {
+ assert(false);
+ }
+ }
+
+ delete mbuffer1;
+ delete mbuffer2;
+ delete mbuffer3;
+
+ delete shard1;
+ delete shard2;
+ delete shard3;
+ delete shard4;
+}
+
+
+START_TEST(t_full_cancelation)
+{
+ size_t n = 100;
+ auto buffer = create_double_seq_mbuffer<Rec>(n, false);
+ auto buffer_ts = create_double_seq_mbuffer<Rec>(n, true);
+
+ Shard* shard = new Shard(buffer->get_buffer_view());
+ Shard* shard_ts = new Shard(buffer_ts->get_buffer_view());
+
+ ck_assert_int_eq(shard->get_record_count(), n);
+ ck_assert_int_eq(shard->get_tombstone_count(), 0);
+ ck_assert_int_eq(shard_ts->get_record_count(), n);
+ ck_assert_int_eq(shard_ts->get_tombstone_count(), n);
+
+ Shard* shards[] = {shard, shard_ts};
+
+ Shard* merged = new Shard(shards, 2);
+
+ ck_assert_int_eq(merged->get_tombstone_count(), 0);
+ ck_assert_int_eq(merged->get_record_count(), 0);
+
+ delete buffer;
+ delete buffer_ts;
+ delete shard;
+ delete shard_ts;
+ delete merged;
+}
+END_TEST
+
+
+START_TEST(t_point_lookup)
+{
+ size_t n = 10000;
+
+ auto buffer = create_double_seq_mbuffer<Rec>(n, false);
+ auto isam = Shard(buffer->get_buffer_view());
+
+ {
+ auto view = buffer->get_buffer_view();
+
+ for (size_t i=0; i<n; i++) {
+ Rec r;
+ auto rec = view.get(i);
+ r.key = rec->rec.key;
+ r.value = rec->rec.value;
+
+ auto result = isam.point_lookup(r);
+ ck_assert_ptr_nonnull(result);
+ ck_assert_int_eq(result->rec.key, r.key);
+ ck_assert_int_eq(result->rec.value, r.value);
+ }
+ }
+
+ delete buffer;
+}
+END_TEST
+
+
+START_TEST(t_point_lookup_miss)
+{
+ size_t n = 10000;
+
+ auto buffer = create_double_seq_mbuffer<Rec>(n, false);
+ auto isam = Shard(buffer->get_buffer_view());
+
+ for (size_t i=n + 100; i<2*n; i++) {
+ Rec r;
+ r.key = i;
+ r.value = i;
+
+ auto result = isam.point_lookup(r);
+ ck_assert_ptr_null(result);
+ }
+
+ delete buffer;
+}
+
+static void inject_shard_tests(Suite *suite) {
+ TCase *create = tcase_create("Shard constructor Testing");
+ tcase_add_test(create, t_mbuffer_init);
+ tcase_add_test(create, t_shard_init);
+ tcase_set_timeout(create, 100);
+ suite_add_tcase(suite, create);
+ TCase *tombstone = tcase_create("Shard tombstone cancellation Testing");
+ tcase_add_test(tombstone, t_full_cancelation);
+ suite_add_tcase(suite, tombstone);
+ TCase *pointlookup = tcase_create("Shard point lookup Testing");
+ tcase_add_test(pointlookup, t_point_lookup);
+ tcase_add_test(pointlookup, t_point_lookup_miss);
+ suite_add_tcase(suite, pointlookup);
+}
diff --git a/tests/include/testing.h b/tests/include/testing.h
new file mode 100644
index 0000000..4e660dd
--- /dev/null
+++ b/tests/include/testing.h
@@ -0,0 +1,218 @@
+/*
+ * tests/testing.h
+ *
+ * Unit test utility functions/definitions
+ *
+ * Copyright (C) 2023 Douglas Rumbaugh <drumbaugh@psu.edu>
+ * Dong Xie <dongx@psu.edu>
+ *
+ * Distributed under the Modified BSD License.
+ *
+ */
+#pragma once
+
+#include <string>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "util/types.h"
+#include "psu-util/alignment.h"
+#include "framework/structure/MutableBuffer.h"
+#include "framework/interface/Record.h"
+
+typedef de::WeightedRecord<uint64_t, uint32_t, uint64_t> WRec;
+typedef de::Record<uint64_t, uint32_t> Rec;
+typedef de::EuclidPoint<int64_t> PRec;
+
+template <de::RecordInterface R>
+std::vector<R> strip_wrapping(std::vector<de::Wrapped<R>> vec) {
+ std::vector<R> out(vec.size());
+ for (size_t i=0; i<vec.size(); i++) {
+ out[i] = vec[i].rec;
+ }
+
+ return out;
+}
+
+static bool initialize_test_file(std::string fname, size_t page_cnt)
+{
+ auto flags = O_RDWR | O_CREAT | O_TRUNC;
+ mode_t mode = 0640;
+ char *page = nullptr;
+
+ int fd = open(fname.c_str(), flags, mode);
+ if (fd == -1) {
+ goto error;
+ }
+
+ page = (char *) aligned_alloc(psudb::SECTOR_SIZE, psudb::PAGE_SIZE);
+ if (!page) {
+ goto error_opened;
+ }
+
+ for (size_t i=0; i<=page_cnt; i++) {
+ *((int *) page) = i;
+ if (write(fd, page, psudb::PAGE_SIZE) == -1) {
+ goto error_alloced;
+ }
+ }
+
+ free(page);
+
+ return 1;
+
+error_alloced:
+ free(page);
+
+error_opened:
+ close(fd);
+
+error:
+ return 0;
+}
+
+static bool roughly_equal(int n1, int n2, size_t mag, double epsilon) {
+ return ((double) std::abs(n1 - n2) / (double) mag) < epsilon;
+}
+
+static de::MutableBuffer<PRec> *create_2d_mbuffer(size_t cnt) {
+ auto buffer = new de::MutableBuffer<PRec>(cnt/2, cnt);
+
+ for (int64_t i=0; i<cnt; i++) {
+ buffer->append({rand(), rand()});
+ }
+
+ return buffer;
+}
+
+static de::MutableBuffer<PRec> *create_2d_sequential_mbuffer(size_t cnt) {
+ auto buffer = new de::MutableBuffer<PRec>(cnt/2, cnt);
+ for (int64_t i=0; i<cnt; i++) {
+ buffer->append({i, i});
+ }
+
+ return buffer;
+}
+
+template <de::KVPInterface R>
+static de::MutableBuffer<R> *create_test_mbuffer(size_t cnt)
+{
+ auto buffer = new de::MutableBuffer<R>(cnt/2, cnt);
+
+ R rec;
+ for (size_t i = 0; i < cnt; i++) {
+ rec.key = rand();
+ rec.value = rand();
+
+ if constexpr (de::WeightedRecordInterface<R>) {
+ rec.weight = 1;
+ }
+
+ buffer->append(rec);
+ }
+
+ return buffer;
+}
+
+template <de::KVPInterface R>
+static de::MutableBuffer<R> *create_sequential_mbuffer(decltype(R::key) start, decltype(R::key) stop)
+{
+ size_t cnt = stop - start;
+ auto buffer = new de::MutableBuffer<R>(cnt/2, cnt);
+
+ for (size_t i=start; i<stop; i++) {
+ R rec;
+ rec.key = i;
+ rec.value = i;
+
+ if constexpr (de::WeightedRecordInterface<R>) {
+ rec.weight = 1;
+ }
+
+ buffer->append(rec);
+ }
+
+ return buffer;
+}
+
+template <de::KVPInterface R>
+static de::MutableBuffer<R> *create_test_mbuffer_tombstones(size_t cnt, size_t ts_cnt)
+{
+ auto buffer = new de::MutableBuffer<R>(cnt/2, cnt);
+
+ std::vector<std::pair<uint64_t, uint32_t>> tombstones;
+
+ R rec;
+ for (size_t i = 0; i < cnt; i++) {
+ rec.key = rand();
+ rec.value = rand();
+
+ if constexpr (de::WeightedRecordInterface<R>) {
+ rec.weight = 1;
+ }
+
+ if (i < ts_cnt) {
+ tombstones.push_back({rec.key, rec.value});
+ }
+
+ buffer->append(rec);
+ }
+
+ rec.set_tombstone();
+ for (size_t i=0; i<ts_cnt; i++) {
+ buffer->append(rec);
+ }
+
+ return buffer;
+}
+
+template <typename R>
+requires de::WeightedRecordInterface<R> && de::KVPInterface<R>
+static de::MutableBuffer<R> *create_weighted_mbuffer(size_t cnt)
+{
+ auto buffer = new de::MutableBuffer<R>(cnt/2, cnt);
+
+ // Put in half of the count with weight one.
+ for (uint32_t i=0; i< cnt / 2; i++) {
+ buffer->append(R {1, i, 2});
+ }
+
+ // put in a quarter of the count with weight four.
+ for (uint32_t i=0; i< cnt / 4; i++) {
+ buffer->append(R {2, i, 4});
+ }
+
+ // the remaining quarter with weight eight.
+ for (uint32_t i=0; i< cnt / 4; i++) {
+ buffer->append(R {3, i, 8});
+ }
+
+ return buffer;
+}
+
+template <de::KVPInterface R>
+static de::MutableBuffer<R> *create_double_seq_mbuffer(size_t cnt, bool ts=false)
+{
+ auto buffer = new de::MutableBuffer<R>(cnt/2, cnt);
+
+ for (size_t i = 0; i < cnt / 2; i++) {
+ R rec;
+ rec.key = i;
+ rec.value = i;
+
+ buffer->append(rec, ts);
+ }
+
+ for (size_t i = 0; i < cnt / 2; i++) {
+ R rec;
+ rec.key = i;
+ rec.value = i + 1;
+
+ buffer->append(rec, ts);
+ }
+
+ return buffer;
+}
+
+