summaryrefslogtreecommitdiffstats
path: root/tests/include
diff options
context:
space:
mode:
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;
+}
+
+