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