Test against fake 'Model' implementation

This removes most of the redundant `for` loops that would appear in
hand-written unit tests, and could potentially open the door to
fuzz-testing/property-test the implementation for a more in-depth
test-suite.
This commit is contained in:
Bruno BELANYI 2024-08-23 23:38:05 +01:00
parent 73c2b21b94
commit 65e0d4991f
2 changed files with 63 additions and 16 deletions

35
tests/unit/model.hh Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <vector>
template <typename K, typename V> struct Range {
K begin;
K end;
V val;
};
// Same behaviour as interval_map, but implementation is trivally correct
template <typename K, typename V> class Model {
public:
Model(V const& init) : init_(init) {}
void assign(K const& begin, K const& end, V const& val) {
if (!(begin < end))
return;
ranges_.emplace_back(begin, end, val);
}
V const& operator[](K const& key) const {
for (auto it = ranges_.rbegin(); it != ranges_.rend(); ++it) {
if (key < it->begin)
continue;
if (it->end <= key)
continue;
return it->val;
}
return init_;
}
V init_;
std::vector<Range<K, V>> ranges_{};
};

View file

@ -4,6 +4,8 @@
#include <type_traits> #include <type_traits>
#include "model.hh"
template <typename T> class KeyInterface { template <typename T> class KeyInterface {
public: public:
explicit KeyInterface(T val) : underlying_(val) {} explicit KeyInterface(T val) : underlying_(val) {}
@ -63,17 +65,40 @@ protected:
using key_type = char; using key_type = char;
using value_type = int; using value_type = int;
using map_type = amby::interval_map<key_type, value_type>; using map_type = amby::interval_map<key_type, value_type>;
using model_type = Model<key_type, value_type>;
map_type map{0}; map_type map{0};
model_type model{0};
void SetUp() override { void SetUp() override {
map = map_type{0}; map = map_type{0};
model = model_type{0};
} }
void TearDown() override { void TearDown() override {
check();
}
void assign(key_type const& begin, key_type const& end,
value_type const& val) {
map.assign(begin, end, val);
model.assign(begin, end, val);
}
void check() const {
check_ranges();
check_canonicity(); check_canonicity();
} }
// Compare against the fake 'Model' implementation
void check_ranges() const {
auto i = std::numeric_limits<key_type>::min();
for (; i < std::numeric_limits<key_type>::max(); ++i) {
ASSERT_EQ(map[i], model[i]) << "(i: " << +i << ")";
}
ASSERT_EQ(map[i], model[i]) << "(i: " << +i << ")";
};
void check_canonicity() const { void check_canonicity() const {
// Consecutive map entries must not contain the same value // Consecutive map entries must not contain the same value
for (auto it = map.underlying_.begin(); it != map.underlying_.end(); for (auto it = map.underlying_.begin(); it != map.underlying_.end();
@ -101,25 +126,12 @@ TEST_F(IntervalMapTest, minimal_interface) {
map.assign(Key(0), Key(1), Value(1)); map.assign(Key(0), Key(1), Value(1));
} }
TEST_F(IntervalMapTest, no_insertion) { TEST_F(IntervalMapTest, no_insertion) {}
for (int i = std::numeric_limits<char>::min();
i <= std::numeric_limits<char>::max(); ++i) {
ASSERT_EQ(map[i], 0);
}
}
TEST_F(IntervalMapTest, insert_begin_equal_end) { TEST_F(IntervalMapTest, insert_begin_equal_end) {
map.assign(0, 0, 1); assign(0, 0, 1);
for (int i = std::numeric_limits<char>::min();
i <= std::numeric_limits<char>::max(); ++i) {
ASSERT_EQ(map[i], 0);
}
} }
TEST_F(IntervalMapTest, insert_begin_bigger_than_end) { TEST_F(IntervalMapTest, insert_begin_bigger_than_end) {
map.assign(1, 0, 1); assign(1, 0, 1);
for (int i = std::numeric_limits<char>::min();
i <= std::numeric_limits<char>::max(); ++i) {
ASSERT_EQ(map[i], 0);
}
} }