From 38e5b116c8d37b5ec6602d7587d9916575a3bbf4 Mon Sep 17 00:00:00 2001 From: knizhnik <knizhnik@garret.ru> Date: Sun, 26 Oct 2014 17:03:43 +0400 Subject: [PATCH] Add unit tests for R-Tree --- test/unit/rtree.cc | 261 +++++++++++++++++++++++++++++++++++++ test/unit/rtree.result | 9 ++ test/unit/rtree_itr.cc | 258 ++++++++++++++++++++++++++++++++++++ test/unit/rtree_itr.result | 8 ++ 4 files changed, 536 insertions(+) create mode 100644 test/unit/rtree.cc create mode 100644 test/unit/rtree.result create mode 100644 test/unit/rtree_itr.cc create mode 100644 test/unit/rtree_itr.result diff --git a/test/unit/rtree.cc b/test/unit/rtree.cc new file mode 100644 index 0000000000..4de3666939 --- /dev/null +++ b/test/unit/rtree.cc @@ -0,0 +1,261 @@ +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "unit.h" +#include "rtree.h" + +static int page_count = 0; + +static void * +page_alloc() +{ + page_count++; + return malloc(RTREE_PAGE_SIZE); +} + +static void +page_free(void *page) +{ + page_count--; + free(page); +} + +static void +simple_check() +{ + rectangle_t r; + R_tree_iterator iterator; + const size_t rounds = 2000; + + header(); + + R_tree tree(page_alloc, page_free); + + printf("Insert 1..X, remove 1..X\n"); + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (tree.search(r, SOP_EQUALS, iterator)) { + fail("element already in tree (1)", "true"); + } + tree.insert(r, rec); + } + if (tree.number_of_records() != rounds) { + fail("Tree count mismatch (1)", "true"); + } + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (!tree.search(r, SOP_EQUALS, iterator)) { + fail("element in tree (1)", "false"); + } + if (iterator.next() != rec) { + fail("right search result (1)", "true"); + } + if (iterator.next()) { + fail("single search result (1)", "true"); + } + if (!tree.remove(r, rec)) { + fail("delete element in tree (1)", "false"); + } + if (tree.search(r, SOP_EQUALS, iterator)) { + fail("element still in tree (1)", "true"); + } + } + if (tree.number_of_records() != 0) { + fail("Tree count mismatch (1)", "true"); + } + + printf("Insert 1..X, remove X..1\n"); + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (tree.search(r, SOP_OVERLAPS, iterator)) { + fail("element already in tree (2)", "true"); + } + tree.insert(r, rec); + } + if (tree.number_of_records() != rounds) { + fail("Tree count mismatch (2)", "true"); + } + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (!tree.search(r, SOP_OVERLAPS, iterator)) { + fail("element in tree (2)", "false"); + } + if (iterator.next() != rec) { + fail("right search result (2)", "true"); + } + if (iterator.next()) { + fail("single search result (2)", "true"); + } + if (!tree.remove(r, rec)) { + fail("delete element in tree (2)", "false"); + } + if (tree.search(r, SOP_OVERLAPS, iterator)) { + fail("element still in tree (2)", "true"); + } + } + if (tree.number_of_records() != 0) { + fail("Tree count mismatch (2)", "true"); + } + + + printf("Insert X..1, remove 1..X\n"); + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (tree.search(r, SOP_BELONGS, iterator)) { + fail("element already in tree (3)", "true"); + } + tree.insert(r, rec); + } + if (tree.number_of_records() != rounds) { + fail("Tree count mismatch (3)", "true"); + } + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (!tree.search(r, SOP_BELONGS, iterator)) { + fail("element in tree (3)", "false"); + } + if (iterator.next() != rec) { + fail("right search result (3)", "true"); + } + if (iterator.next()) { + fail("single search result (3)", "true"); + } + if (!tree.remove(r, rec)) { + fail("delete element in tree (3)", "false"); + } + if (tree.search(r, SOP_BELONGS, iterator)) { + fail("element still in tree (3)", "true"); + } + } + if (tree.number_of_records() != 0) { + fail("Tree count mismatch (3)", "true"); + } + + + printf("Insert X..1, remove X..1\n"); + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (tree.search(r, SOP_CONTAINS, iterator)) { + fail("element already in tree (4)", "true"); + } + tree.insert(r, rec); + } + if (tree.number_of_records() != rounds) { + fail("Tree count mismatch (4)", "true"); + } + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + r.boundary[0] = r.boundary[1] = i; + r.boundary[2] = r.boundary[3] = i + 0.5; + + if (!tree.search(r, SOP_CONTAINS, iterator)) { + fail("element in tree (4)", "false"); + } + if (iterator.next() != rec) { + fail("right search result (4)", "true"); + } + if (iterator.next()) { + fail("single search result (4)", "true"); + } + if (!tree.remove(r, rec)) { + fail("delete element in tree (4)", "false"); + } + if (tree.search(r, SOP_CONTAINS, iterator)) { + fail("element still in tree (4)", "true"); + } + } + if (tree.number_of_records() != 0) { + fail("Tree count mismatch (4)", "true"); + } + + tree.purge(); + + footer(); +} + +static void +rtree_test_build(R_tree& tree, rectangle_t* arr, int count) +{ + for (size_t i = 0; i < count; i++) { + record_t rec = (record_t)(i + 1); + tree.insert(arr[i], rec); + } +} + +static void +neighbor_test() +{ + header(); + + const int test_count = 1000; + R_tree_iterator iterator; + rectangle_t arr[test_count]; + static rectangle_t basis; + + for (size_t i = 0; i < test_count; i++) { + arr[i].boundary[0] = arr[i].boundary[1] = i; + arr[i].boundary[2] = arr[i].boundary[3] = i+1; + } + + for (size_t i = 0; i <= test_count; i++) { + R_tree tree(page_alloc, page_free); + + rtree_test_build(tree, arr, i); + + if (!tree.search(basis, SOP_NEIGHBOR, iterator) && i != 0) { + fail("search is successful", "true"); + } + + for (size_t j = 0; j < i; j++) { + record_t rec = iterator.next(); + if (rec != record_t(j+1)) { + fail("wrong search result", "true"); + } + } + } + + footer(); +} + + +int +main(void) +{ + simple_check(); + neighbor_test(); + if (page_count != 0) { + fail("memory leak!", "true"); + } +} diff --git a/test/unit/rtree.result b/test/unit/rtree.result new file mode 100644 index 0000000000..4f56e6513d --- /dev/null +++ b/test/unit/rtree.result @@ -0,0 +1,9 @@ + *** simple_check *** +Insert 1..X, remove 1..X +Insert 1..X, remove X..1 +Insert X..1, remove 1..X +Insert X..1, remove X..1 + *** simple_check: done *** + *** neighbor_test *** + *** neighbor_test: done *** + \ No newline at end of file diff --git a/test/unit/rtree_itr.cc b/test/unit/rtree_itr.cc new file mode 100644 index 0000000000..554777cafd --- /dev/null +++ b/test/unit/rtree_itr.cc @@ -0,0 +1,258 @@ +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> + +#include "unit.h" +#include "rtree.h" + +static int page_count = 0; + +static void * +page_alloc() +{ + page_count++; + return malloc(RTREE_PAGE_SIZE); +} + +static void +page_free(void *page) +{ + page_count--; + free(page); +} + +static void +itr_check() +{ + header(); + + R_tree tree(page_alloc, page_free); + + /* Filling tree */ + const size_t count1 = 10000; + const size_t count2 = 5; + rectangle_t r; + size_t count = 0; + record_t rec; + R_tree_iterator iterator; + + for (size_t i = 0; i < count1; i++) { + r.boundary[0] = r.boundary[1] = i * 2 * count2; /* note that filled with even numbers */ + for (size_t j = 0; j < count2; j++) { + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + tree.insert(r, record_t(++count)); + } + } + printf("Test tree size: %d\n", (int)tree.number_of_records()); + + /* Test that tree filled ok */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + r.boundary[0] = r.boundary[1] = i * 2 * count2; + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + if (!tree.search(r, SOP_BELONGS, iterator)) { + fail("Integrity check failed (1)", "false"); + } + for (size_t k = 0; k <= j; k++) { + if (!iterator.next()) { + fail("Integrity check failed (2)", "false"); + } + } + if (iterator.next()) { + fail("Integrity check failed (3)", "true"); + } + r.boundary[0] = r.boundary[1] = (i * 2 + 1) * count2; + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + if (tree.search(r, SOP_BELONGS, iterator)) { + fail("Integrity check failed (4)", "true"); + } + } + } + + /* Print 7 elems closest to coordinate basis */ + { + static rectangle_t basis; + printf("--> "); + if (!tree.search(basis, SOP_NEIGHBOR, iterator)) { + fail("Integrity check failed (5)", "false"); + } + for (int i = 0; i < 7; i++) { + rec = iterator.next(); + if (rec == 0) { + fail("Integrity check failed (6)", "false"); + } + printf("%p ", rec); + } + printf("\n"); + } + /* Print 7 elems closest to the point [(count1-1)*count2*2, (count1-1)*count2*2] */ + { + printf("<-- "); + r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = (count1-1)*count2*2; + if (!tree.search(r, SOP_NEIGHBOR, iterator)) { + fail("Integrity check failed (5)", "false"); + } + for (int i = 0; i < 7; i++) { + rec = iterator.next(); + if (rec == 0) { + fail("Integrity check failed (6)", "false"); + } + printf("%p ", rec); + } + printf("\n"); + } + + /* Test strict besize_ts */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + r.boundary[0] = r.boundary[1] = i * 2 * count2; + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + if (!tree.search(r, SOP_STRICT_BELONGS, iterator) && j != 0) { + fail("Integrity check failed (7)", "false"); + } + for (size_t k = 0; k < j; k++) { + if (!iterator.next()) { + fail("Integrity check failed (8)", "false"); + } + } + if (iterator.next()) { + fail("Integrity check failed (9)", "true"); + } + r.boundary[0] = r.boundary[1] = (i * 2 + 1) * count2; + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + if (tree.search(r, SOP_STRICT_BELONGS, iterator)) { + fail("Integrity check failed (10)", "true"); + } + } + } + + /* Test strict contains */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + r.boundary[0] = r.boundary[1] = i * 2 * count2; + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + if (!tree.search(r, SOP_STRICT_CONTAINS, iterator) && j != count2-1) { + fail("Integrity check failed (11)", "false"); + } + for (size_t k = j; k < count2-1; k++) { + if (!iterator.next()) { + fail("Integrity check failed (12)", "false"); + } + } + if (iterator.next()) { + fail("Integrity check failed (13)", "true"); + } + r.boundary[0] = r.boundary[1] = (i * 2 + 1) * count2; + r.boundary[2] = r.boundary[3] = r.boundary[0] + j; + if (tree.search(r, SOP_STRICT_CONTAINS, iterator)) { + fail("Integrity check failed (14)", "true"); + } + } + } + + tree.purge(); + + footer(); +} + +static void +itr_invalidate_check() +{ + header(); + + const size_t test_size = 300; + const size_t max_delete_count = 100; + const size_t max_insert_count = 200; + const size_t attempt_count = 100; + struct R_tree_iterator iterators[test_size]; + + R_tree tree(page_alloc, page_free); + rectangle_t r; + + /* invalidation during deletion */ + srand(0); + for (size_t attempt = 0; attempt < attempt_count; attempt++) { + size_t del_pos = rand() % test_size; + size_t del_cnt = rand() % max_delete_count + 1; + if (del_pos + del_cnt > test_size) { + del_cnt = test_size - del_pos; + } + R_tree tree(page_alloc, page_free); + + for (size_t i = 0; i < test_size; i++) { + r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; + tree.insert(r, record_t(i+1)); + } + r.boundary[0] = r.boundary[1] = 0; + r.boundary[2] = r.boundary[3] = test_size; + tree.search(r, SOP_BELONGS, iterators[0]); + if (!iterators[0].next()) { + fail("Integrity check failed (15)", "false"); + } + for (size_t i = 1; i < test_size; i++) { + iterators[i] = iterators[i - 1]; + if (!iterators[i].next()) { + fail("Integrity check failed (16)", "false"); + } + } + for (size_t i = del_pos; i < del_pos + del_cnt; i++) { + r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; + if (!tree.remove(r, record_t(i+1))) { + fail("Integrity check failed (17)", "false"); + } + } + for (size_t i = 0; i < test_size; i++) { + if (iterators[i].next()) { + fail("Iterator was not invalidated (18)", "true"); + } + } + } + + /* invalidation during insertion */ + srand(0); + for (size_t attempt = 0; attempt < attempt_count; attempt++) { + size_t ins_pos = rand() % test_size; + size_t ins_cnt = rand() % max_insert_count + 1; + + R_tree tree(page_alloc, page_free); + + for (size_t i = 0; i < test_size; i++) { + r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; + tree.insert(r, record_t(i+1)); + } + r.boundary[0] = r.boundary[1] = 0; + r.boundary[2] = r.boundary[3] = test_size; + tree.search(r, SOP_BELONGS, iterators[0]); + if (!iterators[0].next()) { + fail("Integrity check failed (19)", "false"); + } + for (size_t i = 1; i < test_size; i++) { + iterators[i] = iterators[i - 1]; + if (!iterators[0].next()) { + fail("Integrity check failed (20)", "false"); + } + } + for (size_t i = ins_pos; i < ins_pos + ins_cnt; i++) { + r.boundary[0] = r.boundary[1] = r.boundary[2] = r.boundary[3] = i; + tree.insert(r, record_t(test_size + i - ins_pos + 1)); + } + for (size_t i = 0; i < test_size; i++) { + if (iterators[i].next()) { + fail("Iterator was not invalidated (22)", "true"); + } + } + } + + footer(); +} + +int +main(void) +{ + itr_check(); + itr_invalidate_check(); + if (page_count != 0) { + fail("memory leak!", "false"); + } +} diff --git a/test/unit/rtree_itr.result b/test/unit/rtree_itr.result new file mode 100644 index 0000000000..4a3762cf11 --- /dev/null +++ b/test/unit/rtree_itr.result @@ -0,0 +1,8 @@ + *** itr_check *** +Test tree size: 50000 +--> 0x5 0x4 0x3 0x2 0x1 0xa 0x9 +<-- 0xc34c 0xc34d 0xc34e 0xc34f 0xc350 0xc34b 0xc34a + *** itr_check: done *** + *** itr_invalidate_check *** + *** itr_invalidate_check: done *** + \ No newline at end of file -- GitLab