Skip to content

Commit a97347e

Browse files
committed
More tests.
1 parent e52c583 commit a97347e

File tree

5 files changed

+222
-18
lines changed

5 files changed

+222
-18
lines changed

interval_tree.hpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ namespace lib_interval_tree
352352
*/
353353
rb_color color() const
354354
{
355-
return node_->color;
355+
return node_->color();
356356
}
357357

358358
typename tree_type::interval_type interval() const
@@ -546,6 +546,18 @@ namespace lib_interval_tree
546546
return cpy;
547547
}
548548

549+
/**
550+
* Returns an iterator to the parent of this node.
551+
* will equal std::end(tree) if there is no parent node.
552+
*/
553+
interval_tree_iterator parent()
554+
{
555+
if (node_)
556+
return {node_->parent_, owner_};
557+
else
558+
throw std::out_of_range("interval_tree_iterator out of bounds");
559+
}
560+
549561
/**
550562
* Continues down the left side of this node.
551563
* will equal std::end(tree) if there is no left node.

tests/erase_tests.hpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#pragma once
2+
3+
#include <ctime>
4+
#include <random>
5+
#include <cmath>
6+
7+
class EraseTests
8+
: public ::testing::Test
9+
{
10+
public:
11+
using types = IntervalTypes <int>;
12+
13+
protected:
14+
lib_interval_tree::interval_tree <int> tree;
15+
std::default_random_engine gen;
16+
std::uniform_int_distribution <int> distSmall{-500, 500};
17+
std::uniform_int_distribution <int> distLarge{-50000, 50000};
18+
};
19+
20+
TEST_F(EraseTests, EraseSingleElement)
21+
{
22+
auto inserted_interval = types::interval_type{0, 16};
23+
24+
tree.insert(inserted_interval);
25+
26+
tree.erase(tree.begin());
27+
28+
EXPECT_EQ(tree.empty(), true);
29+
EXPECT_EQ(tree.size(), 0);
30+
}
31+
32+
TEST_F(EraseTests, ManualClearTest)
33+
{
34+
constexpr int amount = 10'000;
35+
36+
for (int i = 0; i != amount; ++i)
37+
tree.insert(lib_interval_tree::make_safe_interval(distSmall(gen), distSmall(gen)));
38+
39+
for (auto i = std::begin(tree); i != std::end(tree);)
40+
i = tree.erase(i);
41+
42+
EXPECT_EQ(tree.empty(), true);
43+
}
44+
45+
TEST_F(EraseTests, ClearTest)
46+
{
47+
constexpr int amount = 10'000;
48+
49+
for (int i = 0; i != amount; ++i)
50+
tree.insert(lib_interval_tree::make_safe_interval(distSmall(gen), distSmall(gen)));
51+
52+
tree.clear();
53+
54+
EXPECT_EQ(tree.empty(), true);
55+
}
56+
57+
TEST_F(EraseTests, RandomEraseTest)
58+
{
59+
constexpr int amount = 10'000;
60+
constexpr int deleteAmount = 20;
61+
62+
for (int i = 0; i != amount; ++i)
63+
tree.insert(lib_interval_tree::make_safe_interval(distSmall(gen), distSmall(gen)));
64+
65+
for (int i = 0; i != deleteAmount; ++i)
66+
{
67+
std::uniform_int_distribution <int> dist{0, amount - i - 1};
68+
auto end = dist(gen);
69+
auto iter = tree.begin();
70+
for (int j = 0; j != end; ++j)
71+
++iter;
72+
}
73+
74+
testMaxProperty(tree);
75+
testRedBlackPropertyViolation(tree);
76+
testTreeHeightHealth(tree);
77+
}

tests/insert_tests.hpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
#pragma once
2+
3+
#include "test_utility.hpp"
4+
15
#include <ctime>
26
#include <random>
37
#include <cmath>
@@ -54,11 +58,7 @@ TEST_F(InsertTests, TreeHeightHealthynessTest)
5458
for (int i = 0; i != amount; ++i)
5559
tree.insert(lib_interval_tree::make_safe_interval(distSmall(gen), distSmall(gen)));
5660

57-
auto maxHeight{0};
58-
for (auto i = std::begin(tree); i != std::end(tree); ++i)
59-
maxHeight = std::max(maxHeight, i->height());
60-
61-
EXPECT_LE(maxHeight, 2 * std::log2(amount + 1));
61+
testTreeHeightHealth(tree);
6262
}
6363

6464
TEST_F(InsertTests, MaxValueTest1)
@@ -68,15 +68,15 @@ TEST_F(InsertTests, MaxValueTest1)
6868
for (int i = 0; i != amount; ++i)
6969
tree.insert(lib_interval_tree::make_safe_interval(distSmall(gen), distSmall(gen)));
7070

71-
for (auto i = std::begin(tree); i != std::end(tree); ++i)
72-
{
73-
if (i->left())
74-
{
75-
EXPECT_LE(i->left()->max(), i->max());
76-
}
77-
if (i->right())
78-
{
79-
EXPECT_LE(i->right()->max(), i->max());
80-
}
81-
}
71+
testMaxProperty(tree);
72+
}
73+
74+
TEST_F(InsertTests, RBPropertyInsertTest)
75+
{
76+
constexpr int amount = 1000;
77+
78+
for (int i = 0; i != amount; ++i)
79+
tree.insert(lib_interval_tree::make_safe_interval(distSmall(gen), distSmall(gen)));
80+
81+
testRedBlackPropertyViolation(tree);
8282
}

tests/test_utility.hpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <list>
5+
#include <cmath>
6+
7+
/**
8+
* Warning this function is very expensive.
9+
*/
10+
template <typename TreeT>
11+
void testRedBlackPropertyViolation(TreeT const& tree)
12+
{
13+
using namespace lib_interval_tree;
14+
15+
// root is always black.
16+
EXPECT_EQ(tree.root().color(), rb_color::black);
17+
18+
// check that all nodes have red or black coloring. (seems obvious, but is not on bug)
19+
for (auto i = std::begin(tree); i != std::end(tree); ++i)
20+
{
21+
EXPECT_EQ(true, i.color() == rb_color::black || i.color() == rb_color::red);
22+
}
23+
24+
// check for (red children = black) property:
25+
for (auto i = std::begin(tree); i != std::end(tree); ++i)
26+
{
27+
auto nodeColor = i.color();
28+
if (nodeColor == rb_color::red)
29+
{
30+
if (i.left() != std::end(tree))
31+
{
32+
EXPECT_EQ(i.left().color(), rb_color::black);
33+
}
34+
if (i.right() != std::end(tree))
35+
{
36+
EXPECT_EQ(i.right().color(), rb_color::black);
37+
}
38+
}
39+
}
40+
41+
auto leafCollector = [&](typename TreeT::const_iterator root)
42+
{
43+
std::list <typename TreeT::const_iterator> leaves{};
44+
std::function <void(typename std::list <typename TreeT::const_iterator>::iterator)> recursiveLeafFinder;
45+
recursiveLeafFinder = [&](typename std::list <typename TreeT::const_iterator>::iterator self)
46+
{
47+
if (self->left() != std::end(tree))
48+
{
49+
recursiveLeafFinder(leaves.insert(self, self->left()));
50+
}
51+
if (self->right() != std::end(tree))
52+
{
53+
*self = self->right();
54+
recursiveLeafFinder(self);
55+
}
56+
};
57+
leaves.push_back(root);
58+
recursiveLeafFinder(leaves.begin());
59+
return leaves;
60+
};
61+
62+
// Test that for every node, on the path to its leaves, has the same number of black nodes.
63+
for (auto i = std::cbegin(tree); i != std::cend(tree); ++i)
64+
{
65+
auto leaves = leafCollector(i);
66+
int comparisonCounter{0};
67+
for (auto const& leaf : leaves)
68+
{
69+
auto p = leaf;
70+
int counter{0};
71+
do
72+
{
73+
if (p.color() == rb_color::black)
74+
++counter;
75+
p = p.parent();
76+
} while (p != i && p != std::end(tree));
77+
if (comparisonCounter == 0)
78+
comparisonCounter = counter;
79+
else
80+
{
81+
EXPECT_EQ(comparisonCounter, counter);
82+
}
83+
}
84+
}
85+
}
86+
87+
template <typename TreeT>
88+
void testMaxProperty(TreeT const& tree)
89+
{
90+
for (auto i = std::begin(tree); i != std::end(tree); ++i)
91+
{
92+
if (i->left())
93+
{
94+
EXPECT_LE(i->left()->max(), i->max());
95+
}
96+
if (i->right())
97+
{
98+
EXPECT_LE(i->right()->max(), i->max());
99+
}
100+
EXPECT_GE(i->max(), i->interval().high());
101+
}
102+
}
103+
104+
template <typename TreeT>
105+
void testTreeHeightHealth(TreeT const& tree)
106+
{
107+
int treeSize = tree.size();
108+
109+
auto maxHeight{0};
110+
for (auto i = std::begin(tree); i != std::end(tree); ++i)
111+
maxHeight = std::max(maxHeight, i->height());
112+
113+
EXPECT_LE(maxHeight, 2 * std::log2(treeSize + 1));
114+
}

tests/tests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
// following headers expect to be included after gtest headers and interval_tree
88
#include "interval_tests.hpp"
9-
#include "insert_tests.hpp"
9+
#include "insert_tests.hpp"
10+
#include "erase_tests.hpp"
1011

1112
int main(int argc, char** argv)
1213
{

0 commit comments

Comments
 (0)