#include #include #include "../../../implementations/caching/QueueMap.h" #include #include using ::testing::Test; using std::unique_ptr; using std::make_unique; using namespace blockstore::caching; // This is a not-default-constructible Key type class MinimalKeyType { public: static int instances; static MinimalKeyType create(int value) { return MinimalKeyType(value); } bool operator==(const MinimalKeyType &rhs) const { return _value == rhs._value; } int value() const { return _value; } MinimalKeyType(const MinimalKeyType &rhs): MinimalKeyType(rhs.value()) { } ~MinimalKeyType() { --instances; } private: MinimalKeyType(int value): _value(value) { ++instances; } int _value; }; int MinimalKeyType::instances = 0; namespace std { template <> struct hash { size_t operator()(const MinimalKeyType &obj) const { return obj.value(); } }; } // This is a not-default-constructible non-copyable but moveable Value type class MinimalValueType { public: static int instances; static MinimalValueType create(int value) { return MinimalValueType(value); } MinimalValueType(MinimalValueType &&rhs): MinimalValueType(rhs.value()) { rhs._isMoved = true; } ~MinimalValueType() { assert(!_isDestructed); --instances; _isDestructed = true; } int value() const { assert(!_isMoved && !_isDestructed); return _value; } private: MinimalValueType(int value): _value(value), _isMoved(false), _isDestructed(false) { ++instances; } int _value; bool _isMoved; bool _isDestructed; DISALLOW_COPY_AND_ASSIGN(MinimalValueType); }; int MinimalValueType::instances = 0; class QueueMapTest: public Test { public: QueueMapTest() { MinimalKeyType::instances = 0; MinimalValueType::instances = 0; _map = make_unique>(); } ~QueueMapTest() { _map.reset(); EXPECT_EQ(0, MinimalKeyType::instances); EXPECT_EQ(0, MinimalValueType::instances); } void push(int key, int value) { _map->push(MinimalKeyType::create(key), MinimalValueType::create(value)); } boost::optional pop() { auto elem = _map->pop(); if (!elem) { return boost::none; } return elem.value().value(); } boost::optional pop(int key) { auto elem = _map->pop(MinimalKeyType::create(key)); if (!elem) { return boost::none; } return elem.value().value(); } boost::optional peek() { auto elem = _map->peek(); if (!elem) { return boost::none; } return elem.value().value(); } int size() { return _map->size(); } private: unique_ptr> _map; }; class QueueMapSizeTest: public QueueMapTest {}; TEST_F(QueueMapSizeTest, Empty) { EXPECT_EQ(0, size()); } TEST_F(QueueMapSizeTest, AfterPushingOne) { push(2, 3); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingTwo) { push(2, 3); push(3, 4); EXPECT_EQ(2, size()); } TEST_F(QueueMapSizeTest, AfterPushingTwoAndPoppingOldest) { push(2, 3); push(3, 4); pop(); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingTwoAndPoppingFirst) { push(2, 3); push(3, 4); pop(2); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingTwoAndPoppingLast) { push(2, 3); push(3, 4); pop(3); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingOnePoppingOne) { push(2, 3); pop(); EXPECT_EQ(0, size()); } TEST_F(QueueMapSizeTest, AfterPushingOnePoppingOnePerKey) { push(2, 3); pop(2); EXPECT_EQ(0, size()); } TEST_F(QueueMapSizeTest, AfterPushingOnePoppingOnePushingOne) { push(2, 3); pop(); push(3, 4); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingOnePoppingOnePerKeyPushingOne) { push(2, 3); pop(2); push(3, 4); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingOnePoppingOnePushingSame) { push(2, 3); pop(); push(2, 3); EXPECT_EQ(1, size()); } TEST_F(QueueMapSizeTest, AfterPushingOnePoppingOnePerKeyPushingSame) { push(2, 3); pop(2); push(2, 3); EXPECT_EQ(1, size()); } class QueueMapMemoryLeakTest: public QueueMapTest { public: void EXPECT_NUM_INSTANCES(int num) { EXPECT_EQ(num, MinimalKeyType::instances); EXPECT_EQ(num, MinimalValueType::instances); } }; TEST_F(QueueMapMemoryLeakTest, Empty) { EXPECT_NUM_INSTANCES(0); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOne) { push(2, 3); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingTwo) { push(2, 3); push(3, 4); EXPECT_NUM_INSTANCES(2); } TEST_F(QueueMapMemoryLeakTest, AfterPushingTwoAndPoppingOldest) { push(2, 3); push(3, 4); pop(); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingTwoAndPoppingFirst) { push(2, 3); push(3, 4); pop(2); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingTwoAndPoppingLast) { push(2, 3); push(3, 4); pop(3); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOnePoppingOne) { push(2, 3); pop(); EXPECT_NUM_INSTANCES(0); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOnePoppingOnePerKey) { push(2, 3); pop(2); EXPECT_NUM_INSTANCES(0); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOnePoppingOnePushingOne) { push(2, 3); pop(); push(3, 4); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOnePoppingOnePerKeyPushingOne) { push(2, 3); pop(2); push(3, 4); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOnePoppingOnePushingSame) { push(2, 3); pop(); push(2, 3); EXPECT_NUM_INSTANCES(1); } TEST_F(QueueMapMemoryLeakTest, AfterPushingOnePoppingOnePerKeyPushingSame) { push(2, 3); pop(2); push(2, 3); EXPECT_NUM_INSTANCES(1); } class QueueMapValueTest: public QueueMapTest {}; TEST_F(QueueMapValueTest, PoppingFromEmpty) { EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, PoppingFromEmptyPerKey) { EXPECT_EQ(boost::none, pop(2)); } TEST_F(QueueMapValueTest, PoppingNonexistingPerKey) { push(3, 2); EXPECT_EQ(boost::none, pop(2)); } TEST_F(QueueMapValueTest, PushingOne) { push(3, 2); EXPECT_EQ(2, pop(3).value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, PushingTwo) { push(2, 3); push(3, 4); EXPECT_EQ(3, pop().value()); EXPECT_EQ(4, pop().value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, AfterPushingTwoAndPoppingFirst) { push(2, 3); push(3, 4); pop(2); EXPECT_EQ(boost::none, pop(2)); EXPECT_EQ(4, pop(3).value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, AfterPushingTwoAndPoppingLast) { push(2, 3); push(3, 4); pop(3); EXPECT_EQ(boost::none, pop(3)); EXPECT_EQ(3, pop(2).value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, AfterPushingOnePoppingOne) { push(2, 3); pop(); EXPECT_EQ(boost::none, pop()); EXPECT_EQ(boost::none, pop(2)); } TEST_F(QueueMapValueTest, AfterPushingOnePoppingOnePerKey) { push(2, 3); pop(2); EXPECT_EQ(boost::none, pop()); EXPECT_EQ(boost::none, pop(2)); } TEST_F(QueueMapValueTest, AfterPushingOnePoppingOnePushingOne) { push(2, 3); pop(); push(3, 4); EXPECT_EQ(boost::none, pop(2)); EXPECT_EQ(4, pop(3).value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, AfterPushingOnePoppingOnePerKeyPushingOne) { push(2, 3); pop(2); push(3, 4); EXPECT_EQ(boost::none, pop(2)); EXPECT_EQ(4, pop(3).value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, PushingSomePoppingMiddlePerKey) { push(1, 2); push(2, 3); push(3, 4); push(4, 5); push(5, 6); EXPECT_EQ(3, pop(2).value()); EXPECT_EQ(5, pop(4).value()); EXPECT_EQ(2, pop().value()); EXPECT_EQ(4, pop().value()); EXPECT_EQ(6, pop().value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, PushingSomePoppingFirstPerKey) { push(1, 2); push(2, 3); push(3, 4); push(4, 5); push(5, 6); EXPECT_EQ(2, pop(1).value()); EXPECT_EQ(3, pop(2).value()); EXPECT_EQ(4, pop().value()); EXPECT_EQ(5, pop().value()); EXPECT_EQ(6, pop().value()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapValueTest, PushingSomePoppingLastPerKey) { push(1, 2); push(2, 3); push(3, 4); push(4, 5); push(5, 6); EXPECT_EQ(6, pop(5).value()); EXPECT_EQ(5, pop(4).value()); EXPECT_EQ(2, pop().value()); EXPECT_EQ(3, pop().value()); EXPECT_EQ(4, pop().value()); EXPECT_EQ(boost::none, pop()); } //This test forces the underlying datastructure (std::map or std::unordered_map) to grow and reallocate memory. //So it tests, that QueueMap still works after reallocating memory. TEST_F(QueueMapValueTest, ManyValues) { //Push 1 million entries for (int i = 0; i < 1000000; ++i) { push(i, 2*i); } //pop every other one by key for (int i = 0; i < 1000000; i += 2) { EXPECT_EQ(2*i, pop(i).value()); } //pop the rest in queue order for (int i = 1; i < 1000000; i += 2) { EXPECT_EQ(2*i, peek().value()); EXPECT_EQ(2*i, pop().value()); } EXPECT_EQ(0, size()); EXPECT_EQ(boost::none, pop()); EXPECT_EQ(boost::none, peek()); } TEST_F(QueueMapValueTest, PushAlreadyExistingValue) { push(2, 3); EXPECT_ANY_THROW( push(2, 4); ); } class QueueMapPeekTest: public QueueMapTest {}; TEST_F(QueueMapPeekTest, PoppingFromEmpty) { EXPECT_EQ(boost::none, peek()); } TEST_F(QueueMapPeekTest, PushingOne) { push(3, 2); EXPECT_EQ(2, peek().value()); } TEST_F(QueueMapPeekTest, PushingTwo) { push(2, 3); push(3, 4); EXPECT_EQ(3, peek().value()); EXPECT_EQ(3, peek().value()); EXPECT_EQ(3, pop().value()); EXPECT_EQ(4, peek().value()); EXPECT_EQ(4, peek().value()); EXPECT_EQ(4, pop().value()); EXPECT_EQ(boost::none, peek()); EXPECT_EQ(boost::none, pop()); } TEST_F(QueueMapPeekTest, AfterPushingTwoAndPoppingFirst) { push(2, 3); push(3, 4); pop(2); EXPECT_EQ(boost::none, pop(2)); EXPECT_EQ(4, peek().value()); } class CopyableValueType { public: static int numCopyConstructorCalled; CopyableValueType(int value): _value(value) {} CopyableValueType(const CopyableValueType &rhs): CopyableValueType(rhs._value) { ++numCopyConstructorCalled; } CopyableValueType(CopyableValueType &&rhs): CopyableValueType(rhs._value) { //Don't increase numCopyConstructorCalled } int value() const { return _value; } private: int _value; }; int CopyableValueType::numCopyConstructorCalled = 0; //Test that QueueMap uses a move constructor for Value if possible class QueueMapMoveConstructorTest: public Test { public: QueueMapMoveConstructorTest() { CopyableValueType::numCopyConstructorCalled = 0; map = make_unique>(); } unique_ptr> map; }; TEST_F(QueueMapMoveConstructorTest, PushingAndPopping_MoveIntoMap) { map->push(MinimalKeyType::create(0), CopyableValueType(2)); CopyableValueType val = map->pop().value(); EXPECT_EQ(0, CopyableValueType::numCopyConstructorCalled); } TEST_F(QueueMapMoveConstructorTest, PushingAndPoppingPerKey_MoveIntoMap) { map->push(MinimalKeyType::create(0), CopyableValueType(2)); CopyableValueType val = map->pop(MinimalKeyType::create(0)).value(); EXPECT_EQ(0, CopyableValueType::numCopyConstructorCalled); } TEST_F(QueueMapMoveConstructorTest, PushingAndPopping_CopyIntoMap) { CopyableValueType value(2); map->push(MinimalKeyType::create(0), value); CopyableValueType val = map->pop().value(); EXPECT_EQ(1, CopyableValueType::numCopyConstructorCalled); } TEST_F(QueueMapMoveConstructorTest, PushingAndPoppingPerKey_CopyIntoMap) { CopyableValueType value(2); map->push(MinimalKeyType::create(0), value); CopyableValueType val = map->pop(MinimalKeyType::create(0)).value(); EXPECT_EQ(1, CopyableValueType::numCopyConstructorCalled); }