diff options
Diffstat (limited to 'storage/tokudb/PerconaFT/ft/tests/dmt-test.cc')
-rw-r--r-- | storage/tokudb/PerconaFT/ft/tests/dmt-test.cc | 985 |
1 files changed, 985 insertions, 0 deletions
diff --git a/storage/tokudb/PerconaFT/ft/tests/dmt-test.cc b/storage/tokudb/PerconaFT/ft/tests/dmt-test.cc new file mode 100644 index 00000000..7e31ec2f --- /dev/null +++ b/storage/tokudb/PerconaFT/ft/tests/dmt-test.cc @@ -0,0 +1,985 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: +#ident "$Id$" +/*====== +This file is part of PerconaFT. + + +Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. + + PerconaFT is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, + as published by the Free Software Foundation. + + PerconaFT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. + +---------------------------------------- + + PerconaFT is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License, version 3, + as published by the Free Software Foundation. + + PerconaFT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. +======= */ + +#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." + +#include "test.h" + +#include <util/dmt.h> + +typedef void *DMTVALUE; + +class dmtvalue_writer { +public: + size_t get_size(void) const { + return sizeof(DMTVALUE); + } + void write_to(DMTVALUE *const dest) const { + *dest = value; + } + + dmtvalue_writer(DMTVALUE _value) + : value(_value) { + } + dmtvalue_writer(const uint32_t size UU(), DMTVALUE *const src) + : value(*src) { + paranoid_invariant(size == sizeof(DMTVALUE)); + } +private: + const DMTVALUE value; +}; + +typedef toku::dmt<DMTVALUE, DMTVALUE, dmtvalue_writer> *DMT; + +static int dmt_insert_at(DMT dmt, DMTVALUE value, uint32_t index) { + dmtvalue_writer functor(value); + return dmt->insert_at(functor, index); +} + +static DMT dmt_create_from_sorted_array(DMTVALUE *values, uint32_t numvalues) { + DMT XMALLOC(dmt); + dmt->create(); + for (uint32_t i = 0; i < numvalues; i++) { + dmt_insert_at(dmt, values[i], i); + } + return dmt; +} + +struct heftor { + int (*h)(DMTVALUE, void *v); + void *v; +}; + +int call_heftor(const uint32_t size, const DMTVALUE &v, const heftor &htor); +int call_heftor(const uint32_t size, const DMTVALUE &v, const heftor &htor) { + invariant(size == sizeof(DMTVALUE)); + return htor.h(const_cast<DMTVALUE>(v), htor.v); +} + +static int dmt_insert(DMT dmt, DMTVALUE value, int(*h)(DMTVALUE, void*v), void *v, uint32_t *index) { + struct heftor htor = { .h = h, .v = v }; + dmtvalue_writer functor(value); + return dmt->insert<heftor, call_heftor>(functor, htor, index); +} + +static int dmt_find_zero(DMT V, int (*h)(DMTVALUE, void*extra), void*extra, DMTVALUE *value, uint32_t *index) { + struct heftor htor = { .h = h, .v = extra }; + uint32_t ignore; + return V->find_zero<heftor, call_heftor>(htor, &ignore, value, index); +} + +static int dmt_find(DMT V, int (*h)(DMTVALUE, void*extra), void*extra, int direction, DMTVALUE *value, uint32_t *index) { + struct heftor htor = { .h = h, .v = extra }; + uint32_t ignore; + return V->find<heftor, call_heftor>(htor, direction, &ignore, value, index); +} + +static int dmt_split_at(DMT dmt, DMT *newdmtp, uint32_t index) { + if (index > dmt->size()) { return EINVAL; } + DMT XMALLOC(newdmt); + newdmt->create(); + int r; + + for (uint32_t i = index; i < dmt->size(); i++) { + DMTVALUE v; + r = dmt->fetch(i, nullptr, &v); + invariant_zero(r); + r = dmt_insert_at(newdmt, v, i-index); + invariant_zero(r); + } + if (dmt->size() > 0) { + for (uint32_t i = dmt->size(); i > index; i--) { + r = dmt->delete_at(i - 1); + invariant_zero(r); + } + } + r = 0; + + if (r != 0) { + toku_free(newdmt); + } else { + *newdmtp = newdmt; + } + return r; +} + +static void +parse_args (int argc, const char *argv[]) { + const char *argv0=argv[0]; + while (argc>1) { + int resultcode=0; + if (strcmp(argv[1], "-v")==0) { + verbose++; + } else if (strcmp(argv[1], "-q")==0) { + verbose = 0; + } else if (strcmp(argv[1], "-h")==0) { + do_usage: + fprintf(stderr, "Usage:\n%s [-v|-h]\n", argv0); + exit(resultcode); + } else { + resultcode=1; + goto do_usage; + } + argc--; + argv++; + } +} +/* End ".h like" stuff. */ + +struct value { + uint32_t number; +}; +#define V(x) ((struct value *)(x)) + +enum rand_type { + TEST_RANDOM, + TEST_SORTED, + TEST_IDENTITY +}; +enum close_when_done { + CLOSE_WHEN_DONE, + KEEP_WHEN_DONE +}; +enum create_type { + BATCH_INSERT, + INSERT_AT, + INSERT_AT_ALMOST_RANDOM, +}; + +/* Globals */ +DMT global_dmt; +DMTVALUE* values = nullptr; +struct value* nums = nullptr; +uint32_t length; + +static void +cleanup_globals (void) { + assert(values); + toku_free(values); + values = nullptr; + assert(nums); + toku_free(nums); + nums = nullptr; +} + +const unsigned int random_seed = 0xFEADACBA; + +static void +init_init_values (unsigned int seed, uint32_t num_elements) { + srandom(seed); + + cleanup_globals(); + + MALLOC_N(num_elements, values); + assert(values); + MALLOC_N(num_elements, nums); + assert(nums); + length = num_elements; +} + +static void +init_identity_values (unsigned int seed, uint32_t num_elements) { + uint32_t i; + + init_init_values(seed, num_elements); + + for (i = 0; i < length; i++) { + nums[i].number = i; + values[i] = (DMTVALUE)&nums[i]; + } +} + +static void +init_distinct_sorted_values (unsigned int seed, uint32_t num_elements) { + uint32_t i; + + init_init_values(seed, num_elements); + + uint32_t number = 0; + + for (i = 0; i < length; i++) { + number += (uint32_t)(random() % 32) + 1; + nums[i].number = number; + values[i] = (DMTVALUE)&nums[i]; + } +} + +static void +init_distinct_random_values (unsigned int seed, uint32_t num_elements) { + init_distinct_sorted_values(seed, num_elements); + + uint32_t i; + uint32_t choice; + uint32_t choices; + struct value temp; + for (i = 0; i < length - 1; i++) { + choices = length - i; + choice = random() % choices; + if (choice != i) { + temp = nums[i]; + nums[i] = nums[choice]; + nums[choice] = temp; + } + } +} + +static void +init_globals (void) { + MALLOC_N(1, values); + assert(values); + MALLOC_N(1, nums); + assert(nums); + length = 1; +} + +static void +test_close (enum close_when_done do_close) { + if (do_close == KEEP_WHEN_DONE) return; + assert(do_close == CLOSE_WHEN_DONE); + global_dmt->destroy(); + toku_free(global_dmt); + global_dmt = nullptr; +} + +static void +test_create (enum close_when_done do_close) { + XMALLOC(global_dmt); + global_dmt->create(); + test_close(do_close); +} + +static void +test_create_size (enum close_when_done do_close) { + test_create(KEEP_WHEN_DONE); + assert(global_dmt->size() == 0); + test_close(do_close); +} + +static void +test_create_insert_at_almost_random (enum close_when_done do_close) { + uint32_t i; + int r; + uint32_t size = 0; + + test_create(KEEP_WHEN_DONE); + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+1); + CKERR2(r, EINVAL); + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+2); + CKERR2(r, EINVAL); + for (i = 0; i < length/2; i++) { + assert(size==global_dmt->size()); + r = dmt_insert_at(global_dmt, values[i], i); + CKERR(r); + assert(++size==global_dmt->size()); + r = dmt_insert_at(global_dmt, values[length-1-i], i+1); + CKERR(r); + assert(++size==global_dmt->size()); + } + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+1); + CKERR2(r, EINVAL); + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+2); + CKERR2(r, EINVAL); + assert(size==global_dmt->size()); + test_close(do_close); +} + +static void +test_create_insert_at_sequential (enum close_when_done do_close) { + uint32_t i; + int r; + uint32_t size = 0; + + test_create(KEEP_WHEN_DONE); + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+1); + CKERR2(r, EINVAL); + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+2); + CKERR2(r, EINVAL); + for (i = 0; i < length; i++) { + assert(size==global_dmt->size()); + r = dmt_insert_at(global_dmt, values[i], i); + CKERR(r); + assert(++size==global_dmt->size()); + } + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+1); + CKERR2(r, EINVAL); + r = dmt_insert_at(global_dmt, values[0], global_dmt->size()+2); + CKERR2(r, EINVAL); + assert(size==global_dmt->size()); + test_close(do_close); +} + +static void +test_create_from_sorted_array (enum create_type create_choice, enum close_when_done do_close) { + global_dmt = nullptr; + + if (create_choice == BATCH_INSERT) { + global_dmt = dmt_create_from_sorted_array(values, length); + } + else if (create_choice == INSERT_AT) { + test_create_insert_at_sequential(KEEP_WHEN_DONE); + } + else if (create_choice == INSERT_AT_ALMOST_RANDOM) { + test_create_insert_at_almost_random(KEEP_WHEN_DONE); + } + else { + assert(false); + } + + assert(global_dmt!=nullptr); + test_close(do_close); +} + +static void +test_create_from_sorted_array_size (enum create_type create_choice, enum close_when_done do_close) { + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + assert(global_dmt->size()==length); + test_close(do_close); +} + +static void +test_fetch_verify (DMT dmtree, DMTVALUE* val, uint32_t len ) { + uint32_t i; + int r; + DMTVALUE v = (DMTVALUE)&i; + DMTVALUE oldv = v; + + assert(len == dmtree->size()); + for (i = 0; i < len; i++) { + assert(oldv!=val[i]); + v = nullptr; + r = dmtree->fetch(i, nullptr, &v); + CKERR(r); + assert(v != nullptr); + assert(v != oldv); + assert(v == val[i]); + assert(V(v)->number == V(val[i])->number); + v = oldv; + } + + for (i = len; i < len*2; i++) { + v = oldv; + r = dmtree->fetch(i, nullptr, &v); + CKERR2(r, EINVAL); + assert(v == oldv); + } + +} + +static void +test_create_fetch_verify (enum create_type create_choice, enum close_when_done do_close) { + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + test_fetch_verify(global_dmt, values, length); + test_close(do_close); +} + +static int iterate_helper_error_return = 1; + +static int +iterate_helper (DMTVALUE v, uint32_t idx, void* extra) { + if (extra == nullptr) return iterate_helper_error_return; + DMTVALUE* vals = (DMTVALUE *)extra; + assert(v != nullptr); + assert(v == vals[idx]); + assert(V(v)->number == V(vals[idx])->number); + return 0; +} +struct functor { + int (*f)(DMTVALUE, uint32_t, void *); + void *v; +}; + +int call_functor(const uint32_t size, const DMTVALUE &v, uint32_t idx, functor *const ftor); +int call_functor(const uint32_t size, const DMTVALUE &v, uint32_t idx, functor *const ftor) { + invariant(size == sizeof(DMTVALUE)); + return ftor->f(const_cast<DMTVALUE>(v), idx, ftor->v); +} + +static int dmt_iterate(DMT dmt, int (*f)(DMTVALUE, uint32_t, void*), void*v) { + struct functor ftor = { .f = f, .v = v }; + return dmt->iterate<functor, call_functor>(&ftor); +} + +static void +test_iterate_verify (DMT dmtree, DMTVALUE* vals, uint32_t len) { + int r; + iterate_helper_error_return = 0; + r = dmt_iterate(dmtree, iterate_helper, (void*)vals); + CKERR(r); + iterate_helper_error_return = 0xFEEDABBA; + r = dmt_iterate(dmtree, iterate_helper, nullptr); + if (!len) { + CKERR2(r, 0); + } + else { + CKERR2(r, iterate_helper_error_return); + } +} + +static void +test_create_iterate_verify (enum create_type create_choice, enum close_when_done do_close) { + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + test_iterate_verify(global_dmt, values, length); + test_close(do_close); +} + + +static void +permute_array (uint32_t* arr, uint32_t len) { + // + // create a permutation of 0...size-1 + // + uint32_t i = 0; + for (i = 0; i < len; i++) { + arr[i] = i; + } + for (i = 0; i < len - 1; i++) { + uint32_t choices = len - i; + uint32_t choice = random() % choices; + if (choice != i) { + uint32_t temp = arr[i]; + arr[i] = arr[choice]; + arr[choice] = temp; + } + } +} + +static int +dmt_set_at (DMT dmt, DMTVALUE value, uint32_t index) { + int r = dmt->delete_at(index); + if (r!=0) return r; + return dmt_insert_at(dmt, value, index); +} + +static void +test_create_set_at (enum create_type create_choice, enum close_when_done do_close) { + uint32_t i = 0; + + struct value* old_nums = nullptr; + MALLOC_N(length, old_nums); + assert(nums); + + uint32_t* perm = nullptr; + MALLOC_N(length, perm); + assert(perm); + + DMTVALUE* old_values = nullptr; + MALLOC_N(length, old_values); + assert(old_values); + + permute_array(perm, length); + + // + // These are going to be the new values + // + for (i = 0; i < length; i++) { + old_nums[i] = nums[i]; + old_values[i] = &old_nums[i]; + values[i] = &old_nums[i]; + } + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + int r; + r = dmt_set_at (global_dmt, values[0], length); + CKERR2(r,EINVAL); + r = dmt_set_at (global_dmt, values[0], length+1); + CKERR2(r,EINVAL); + for (i = 0; i < length; i++) { + uint32_t choice = perm[i]; + values[choice] = &nums[choice]; + nums[choice].number = (uint32_t)random(); + r = dmt_set_at (global_dmt, values[choice], choice); + CKERR(r); + test_iterate_verify(global_dmt, values, length); + test_fetch_verify(global_dmt, values, length); + } + r = dmt_set_at (global_dmt, values[0], length); + CKERR2(r,EINVAL); + r = dmt_set_at (global_dmt, values[0], length+1); + CKERR2(r,EINVAL); + + toku_free(perm); + toku_free(old_values); + toku_free(old_nums); + + test_close(do_close); +} + +static int +insert_helper (DMTVALUE value, void* extra_insert) { + DMTVALUE to_insert = (DMTVALUE)extra_insert; + assert(to_insert); + + if (V(value)->number < V(to_insert)->number) return -1; + if (V(value)->number > V(to_insert)->number) return +1; + return 0; +} + +static void +test_create_insert (enum close_when_done do_close) { + uint32_t i = 0; + + uint32_t* perm = nullptr; + MALLOC_N(length, perm); + assert(perm); + + permute_array(perm, length); + + test_create(KEEP_WHEN_DONE); + int r; + uint32_t size = length; + length = 0; + while (length < size) { + uint32_t choice = perm[length]; + DMTVALUE to_insert = &nums[choice]; + uint32_t idx = UINT32_MAX; + + assert(length==global_dmt->size()); + r = dmt_insert(global_dmt, to_insert, insert_helper, to_insert, &idx); + CKERR(r); + assert(idx <= length); + if (idx > 0) { + assert(V(to_insert)->number > V(values[idx-1])->number); + } + if (idx < length) { + assert(V(to_insert)->number < V(values[idx])->number); + } + length++; + assert(length==global_dmt->size()); + /* Make room */ + for (i = length-1; i > idx; i--) { + values[i] = values[i-1]; + } + values[idx] = to_insert; + test_fetch_verify(global_dmt, values, length); + test_iterate_verify(global_dmt, values, length); + + idx = UINT32_MAX; + r = dmt_insert(global_dmt, to_insert, insert_helper, to_insert, &idx); + CKERR2(r, DB_KEYEXIST); + assert(idx < length); + assert(V(values[idx])->number == V(to_insert)->number); + assert(length==global_dmt->size()); + + test_iterate_verify(global_dmt, values, length); + test_fetch_verify(global_dmt, values, length); + } + + toku_free(perm); + + test_close(do_close); +} + +static void +test_create_delete_at (enum create_type create_choice, enum close_when_done do_close) { + uint32_t i = 0; + int r = ENOSYS; + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + + assert(length == global_dmt->size()); + r = global_dmt->delete_at(length); + CKERR2(r,EINVAL); + assert(length == global_dmt->size()); + r = global_dmt->delete_at(length+1); + CKERR2(r,EINVAL); + while (length > 0) { + assert(length == global_dmt->size()); + uint32_t index_to_delete = random()%length; + r = global_dmt->delete_at(index_to_delete); + CKERR(r); + for (i = index_to_delete+1; i < length; i++) { + values[i-1] = values[i]; + } + length--; + test_fetch_verify(global_dmt, values, length); + test_iterate_verify(global_dmt, values, length); + } + assert(length == 0); + assert(length == global_dmt->size()); + r = global_dmt->delete_at(length); + CKERR2(r, EINVAL); + assert(length == global_dmt->size()); + r = global_dmt->delete_at(length+1); + CKERR2(r, EINVAL); + test_close(do_close); +} + +static int dmt_merge(DMT leftdmt, DMT rightdmt, DMT *newdmtp) { + DMT XMALLOC(newdmt); + newdmt->create(); + int r; + for (uint32_t i = 0; i < leftdmt->size(); i++) { + DMTVALUE v; + r = leftdmt->fetch(i, nullptr, &v); + invariant_zero(r); + r = newdmt->insert_at(v, i); + invariant_zero(r); + } + uint32_t offset = leftdmt->size(); + for (uint32_t i = 0; i < rightdmt->size(); i++) { + DMTVALUE v; + r = rightdmt->fetch(i, nullptr, &v); + invariant_zero(r); + r = newdmt->insert_at(v, i+offset); + invariant_zero(r); + } + leftdmt->destroy(); + rightdmt->destroy(); + toku_free(leftdmt); + toku_free(rightdmt); + *newdmtp = newdmt; + return 0; +} + +static void +test_split_merge (enum create_type create_choice, enum close_when_done do_close) { + int r = ENOSYS; + uint32_t i = 0; + DMT left_split = nullptr; + DMT right_split = nullptr; + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + + for (i = 0; i <= length; i++) { + r = dmt_split_at(global_dmt, &right_split, length+1); + CKERR2(r,EINVAL); + r = dmt_split_at(global_dmt, &right_split, length+2); + CKERR2(r,EINVAL); + + // + // test successful split + // + r = dmt_split_at(global_dmt, &right_split, i); + CKERR(r); + left_split = global_dmt; + global_dmt = nullptr; + assert(left_split->size() == i); + assert(right_split->size() == length - i); + test_fetch_verify(left_split, values, i); + test_iterate_verify(left_split, values, i); + test_fetch_verify(right_split, &values[i], length - i); + test_iterate_verify(right_split, &values[i], length - i); + // + // verify that new global_dmt's cannot do bad splits + // + r = dmt_split_at(left_split, &global_dmt, i+1); + CKERR2(r,EINVAL); + assert(left_split->size() == i); + assert(right_split->size() == length - i); + r = dmt_split_at(left_split, &global_dmt, i+2); + CKERR2(r,EINVAL); + assert(left_split->size() == i); + assert(right_split->size() == length - i); + r = dmt_split_at(right_split, &global_dmt, length - i + 1); + CKERR2(r,EINVAL); + assert(left_split->size() == i); + assert(right_split->size() == length - i); + r = dmt_split_at(right_split, &global_dmt, length - i + 1); + CKERR2(r,EINVAL); + assert(left_split->size() == i); + assert(right_split->size() == length - i); + + // + // test merge + // + r = dmt_merge(left_split,right_split,&global_dmt); + CKERR(r); + left_split = nullptr; + right_split = nullptr; + assert(global_dmt->size() == length); + test_fetch_verify(global_dmt, values, length); + test_iterate_verify(global_dmt, values, length); + } + test_close(do_close); +} + + +static void +init_values (enum rand_type rand_choice) { + const uint32_t test_size = 100; + if (rand_choice == TEST_RANDOM) { + init_distinct_random_values(random_seed, test_size); + } + else if (rand_choice == TEST_SORTED) { + init_distinct_sorted_values(random_seed, test_size); + } + else if (rand_choice == TEST_IDENTITY) { + init_identity_values( random_seed, test_size); + } + else assert(false); +} + +static void +test_create_array (enum create_type create_choice, enum rand_type rand_choice) { + /* ********************************************************************** */ + init_values(rand_choice); + test_create_from_sorted_array( create_choice, CLOSE_WHEN_DONE); + test_create_from_sorted_array_size(create_choice, CLOSE_WHEN_DONE); + /* ********************************************************************** */ + init_values(rand_choice); + test_create_fetch_verify( create_choice, CLOSE_WHEN_DONE); + /* ********************************************************************** */ + init_values(rand_choice); + test_create_iterate_verify( create_choice, CLOSE_WHEN_DONE); + /* ********************************************************************** */ + init_values(rand_choice); + test_create_set_at( create_choice, CLOSE_WHEN_DONE); + /* ********************************************************************** */ + init_values(rand_choice); + test_create_delete_at( create_choice, CLOSE_WHEN_DONE); + /* ********************************************************************** */ + init_values(rand_choice); + test_create_insert( CLOSE_WHEN_DONE); + /* ********************************************************************** */ + init_values(rand_choice); + test_split_merge( create_choice, CLOSE_WHEN_DONE); +} + +typedef struct { + uint32_t first_zero; + uint32_t first_pos; +} h_extra; + + +static int +test_heaviside (DMTVALUE v_dmt, void* x) { + DMTVALUE v = (DMTVALUE) v_dmt; + h_extra* extra = (h_extra*)x; + assert(v && x); + assert(extra->first_zero <= extra->first_pos); + + uint32_t value = V(v)->number; + if (value < extra->first_zero) return -1; + if (value < extra->first_pos) return 0; + return 1; +} + +static void +heavy_extra (h_extra* extra, uint32_t first_zero, uint32_t first_pos) { + extra->first_zero = first_zero; + extra->first_pos = first_pos; +} + +static void +test_find_dir (int dir, void* extra, int (*h)(DMTVALUE, void*), + int r_expect, bool idx_will_change, uint32_t idx_expect, + uint32_t number_expect, bool UU(cursor_valid)) { + uint32_t idx = UINT32_MAX; + uint32_t old_idx = idx; + DMTVALUE dmt_val; + int r; + + dmt_val = nullptr; + + /* Verify we can pass nullptr value. */ + dmt_val = nullptr; + idx = old_idx; + if (dir == 0) { + r = dmt_find_zero(global_dmt, h, extra, nullptr, &idx); + } + else { + r = dmt_find( global_dmt, h, extra, dir, nullptr, &idx); + } + CKERR2(r, r_expect); + if (idx_will_change) { + assert(idx == idx_expect); + } + else { + assert(idx == old_idx); + } + assert(dmt_val == nullptr); + + /* Verify we can pass nullptr idx. */ + dmt_val = nullptr; + idx = old_idx; + if (dir == 0) { + r = dmt_find_zero(global_dmt, h, extra, &dmt_val, 0); + } + else { + r = dmt_find( global_dmt, h, extra, dir, &dmt_val, 0); + } + CKERR2(r, r_expect); + assert(idx == old_idx); + if (r == DB_NOTFOUND) { + assert(dmt_val == nullptr); + } + else { + assert(V(dmt_val)->number == number_expect); + } + + /* Verify we can pass nullptr both. */ + dmt_val = nullptr; + idx = old_idx; + if (dir == 0) { + r = dmt_find_zero(global_dmt, h, extra, nullptr, 0); + } + else { + r = dmt_find( global_dmt, h, extra, dir, nullptr, 0); + } + CKERR2(r, r_expect); + assert(idx == old_idx); + assert(dmt_val == nullptr); +} + +static void +test_find (enum create_type create_choice, enum close_when_done do_close) { + h_extra extra; + init_identity_values(random_seed, 100); + test_create_from_sorted_array(create_choice, KEEP_WHEN_DONE); + +/* + -...- + A +*/ + heavy_extra(&extra, length, length); + test_find_dir(-1, &extra, test_heaviside, 0, true, length-1, length-1, true); + test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, false, 0, 0, false); + test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, true, length, length, false); + + +/* + +...+ + B +*/ + heavy_extra(&extra, 0, 0); + test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, false, 0, 0, false); + test_find_dir(+1, &extra, test_heaviside, 0, true, 0, 0, true); + test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, true, 0, 0, false); + +/* + 0...0 + C +*/ + heavy_extra(&extra, 0, length); + test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, false, 0, 0, false); + test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, false, 0, 0, false); + test_find_dir(0, &extra, test_heaviside, 0, true, 0, 0, true); + +/* + -...-0...0 + AC +*/ + heavy_extra(&extra, length/2, length); + test_find_dir(-1, &extra, test_heaviside, 0, true, length/2-1, length/2-1, true); + test_find_dir(+1, &extra, test_heaviside, DB_NOTFOUND, false, 0, 0, false); + test_find_dir(0, &extra, test_heaviside, 0, true, length/2, length/2, true); + +/* + 0...0+...+ + C B +*/ + heavy_extra(&extra, 0, length/2); + test_find_dir(-1, &extra, test_heaviside, DB_NOTFOUND, false, 0, 0, false); + test_find_dir(+1, &extra, test_heaviside, 0, true, length/2, length/2, true); + test_find_dir(0, &extra, test_heaviside, 0, true, 0, 0, true); + +/* + -...-+...+ + AB +*/ + heavy_extra(&extra, length/2, length/2); + test_find_dir(-1, &extra, test_heaviside, 0, true, length/2-1, length/2-1, true); + test_find_dir(+1, &extra, test_heaviside, 0, true, length/2, length/2, true); + test_find_dir(0, &extra, test_heaviside, DB_NOTFOUND, true, length/2, length/2, false); + +/* + -...-0...0+...+ + AC B +*/ + heavy_extra(&extra, length/3, 2*length/3); + test_find_dir(-1, &extra, test_heaviside, 0, true, length/3-1, length/3-1, true); + test_find_dir(+1, &extra, test_heaviside, 0, true, 2*length/3, 2*length/3, true); + test_find_dir(0, &extra, test_heaviside, 0, true, length/3, length/3, true); + + /* Cleanup */ + test_close(do_close); +} + +static void +runtests_create_choice (enum create_type create_choice) { + test_create_array(create_choice, TEST_SORTED); + test_create_array(create_choice, TEST_RANDOM); + test_create_array(create_choice, TEST_IDENTITY); + test_find( create_choice, CLOSE_WHEN_DONE); +} + +static void +test_clone(uint32_t nelts) +// Test that each clone operation gives the right data back. If nelts is +// zero, also tests that you still get a valid DMT back and that the way +// to deallocate it still works. +{ + DMT src = nullptr, dest = nullptr; + int r = 0; + + XMALLOC(src); + src->create(); + for (long i = 0; i < nelts; ++i) { + r = dmt_insert_at(src, (DMTVALUE) i, i); + assert_zero(r); + } + + XMALLOC(dest); + dest->clone(*src); + assert(dest->size() == nelts); + for (long i = 0; i < nelts; ++i) { + DMTVALUE v; + long l; + r = dest->fetch(i, nullptr, &v); + assert_zero(r); + l = (long) v; + assert(l == i); + } + dest->destroy(); + toku_free(dest); + src->destroy(); + toku_free(src); +} + +int +test_main(int argc, const char *argv[]) { + parse_args(argc, argv); + init_globals(); + test_create( CLOSE_WHEN_DONE); + test_create_size( CLOSE_WHEN_DONE); + runtests_create_choice(BATCH_INSERT); + runtests_create_choice(INSERT_AT); + runtests_create_choice(INSERT_AT_ALMOST_RANDOM); + test_clone(0); + test_clone(1); + test_clone(1000); + test_clone(10000); + cleanup_globals(); + return 0; +} + |