diff --git a/CMakeLists.txt b/CMakeLists.txt index 60396bc..735dfac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,12 +30,18 @@ add_executable( tests/doubly_linked_list/pop_front_tests.cpp tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp + tests/doubly_linked_list/constructor_tests.cpp tests/doubly_linked_list/clear_tests.cpp + tests/doubly_linked_list/erase_tests.cpp + tests/doubly_linked_list/move_to_begin_tests.cpp + tests/doubly_linked_list/emplace_back_tests.cpp tests/ordered_map/size_tests.cpp tests/ordered_map/insertion_tests.cpp tests/ordered_map/lookup_tests.cpp tests/ordered_map/iterator_tests.cpp tests/ordered_map/constructor_tests.cpp + tests/ordered_map/find_tests.cpp + tests/ordered_map/move_to_front_tests.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/README.md b/README.md index 91de09d..8fd82fc 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,9 @@ ordered_map/ ./build.sh ``` -## Future Improvements +## Future Features + +- [ ] Add `move_to_front` and `move_to_back` operations in ordered_map to re-order entries (without affecting or copying the entry value) - [ ] Add const iterators - [ ] Add reverse iterators @@ -206,8 +208,7 @@ ordered_map/ - [ ] **DoublyLinkedList:** Add `erase` method to erase elements given their iterator - [ ] **DoublyLinkedList:** Add pre and post decrement operators - [ ] **DoublyLinkedList:** Test front(), back(), insertion and deletion functions for copying behavior -- [ ] **OrderedMap:** Add support for initializing map with +- [ ] **OrderedMap:** Add support for initializing map with - [ ] **OrderedMap:** Add support for custom hash functions - [ ] **OrderedMap:** Add `erase` method to erase elements given their iterator or key - [ ] Test for memory leaks - diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 20e4bc5..44fb04d 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -9,8 +9,14 @@ struct Node { std::unique_ptr next; Node* prev; - Node(const T& a_value, Node* a_prev, std::unique_ptr a_next) - : value(a_value), prev(a_prev), next(std::move(a_next)) {} + template + Node(U&& a_value, Node* a_prev, std::unique_ptr a_next) + : value(std::forward(a_value)), prev(a_prev), next(std::move(a_next)) {} + + template + Node(Node* p, std::unique_ptr n, Args&&... args) + : value(std::forward(args)...), prev(p), next(std::move(n)) {} + }; template @@ -20,8 +26,98 @@ class DoublyLinkedList { Node* tail; int _size; + void link_new_back_node(std::unique_ptr> new_node) { + if (!head) { + head = std::move(new_node); + tail = head.get(); + } else { + tail->next = std::move(new_node); + tail->next->prev = tail; + tail = tail->next.get(); + } + ++_size; + } + + template + void push_back_internal(U&& value) { + auto new_node = std::make_unique>(std::forward(value), tail, nullptr); + link_new_back_node(std::move(new_node)); + } + + template + void push_front_internal(U&& value) { + auto new_node = std::make_unique>(std::forward(value), nullptr, std::move(head)); + if (new_node->next) { + new_node->next->prev = new_node.get(); + } + else { + tail = new_node.get(); + } + head = std::move(new_node); + _size++; + } + + T erase_middle_node(Node* node) { + T value = std::move(node->value); + node->next->prev = node->prev; + node->prev->next = std::move(node->next); + _size--; + return value; + } + + std::unique_ptr> extract_node_and_link_prev_with_next(Node* node) { + std::unique_ptr> extracted; + + if (node->next) { + node->next->prev = node->prev; + } else { + tail = node->prev; + } + + if (node->prev) { + extracted = std::move(node->prev->next); + node->prev->next = std::move(node->next); + } else { + extracted = std::move(head); + head = std::move(node->next); + } + + return extracted; + } + + void emplace_node_before(std::unique_ptr> node, Node* position) { + if (position == head.get()) { + node->next = std::move(head); + if (node->next) node->next->prev = node.get(); + head = std::move(node); + } + else if (position == nullptr) { + node->prev = tail; + tail = node.get(); + node->prev->next = std::move(node); + } + else { + node->prev = position->prev; + node->next = std::move(position->prev->next); + position->prev = node.get(); + node->prev->next = std::move(node); + } + } + public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} + + DoublyLinkedList(std::initializer_list init_list) : DoublyLinkedList() { + for (const auto& value : init_list) push_back(value); + } + + template + DoublyLinkedList(ParamIterator begin, ParamIterator end) : DoublyLinkedList() { + for (auto it = begin; it != end; it++) { + push_back(*it); + } + } + ~DoublyLinkedList() { clear(); } @@ -45,15 +141,14 @@ class DoublyLinkedList { return tail->value; } + + + void push_back(T&& value) { + push_back_internal(std::move(value)); + } + void push_back(const T& value) { - if (!head) { - head = std::make_unique>(value, nullptr, nullptr); - tail = head.get(); - } else { - tail->next = std::make_unique>(value, tail, nullptr); - tail = tail->next.get(); - } - _size++; + push_back_internal(value); } T pop_back() { @@ -61,7 +156,7 @@ class DoublyLinkedList { throw std::out_of_range("List is empty"); } - T popped_value = tail->value; + T popped_value = std::move(tail->value); if (tail == head.get()) { head.reset(); @@ -76,16 +171,12 @@ class DoublyLinkedList { return popped_value; } + void push_front(T&& value) { + push_front_internal(std::move(value)); + } + void push_front(const T& value) { - if (!head) { - head = std::make_unique>(value, nullptr, nullptr); - tail = head.get(); - } else { - auto new_node = std::make_unique>(value, nullptr, std::move(head)); - new_node->next->prev = new_node.get(); - head = std::move(new_node); - } - _size++; + push_front_internal(value); } T pop_front() { @@ -93,7 +184,7 @@ class DoublyLinkedList { throw std::out_of_range("List is empty"); } - T popped_value = head->value; + T popped_value = std::move(head->value); head = std::move(head->next); if (!head) @@ -104,7 +195,7 @@ class DoublyLinkedList { } void clear() { - while(size() > 0) { + while(head) { pop_back(); } } @@ -115,13 +206,14 @@ class DoublyLinkedList { class Iterator { Node* current_node_ptr; + DoublyLinkedList* list_ptr; - Iterator(Node* a_current) : current_node_ptr(a_current) {} + Iterator(Node* a_current, DoublyLinkedList* a_list_ptr) : current_node_ptr(a_current), list_ptr(a_list_ptr) {} friend class DoublyLinkedList; public: - Iterator() : current_node_ptr(nullptr) {} + Iterator() : current_node_ptr(nullptr), list_ptr(nullptr) {} bool operator==(const Iterator& other) const { return current_node_ptr == other.current_node_ptr; @@ -149,7 +241,10 @@ class DoublyLinkedList { } Iterator& operator--() { - if (current_node_ptr) { + if (current_node_ptr == nullptr) { + current_node_ptr = list_ptr->tail; + } + else if (current_node_ptr) { current_node_ptr = current_node_ptr->prev; } return *this; @@ -157,14 +252,45 @@ class DoublyLinkedList { }; Iterator begin() { - return Iterator(head.get()); + return Iterator(head.get(), this); } Iterator end() { - return Iterator(nullptr); + return Iterator(nullptr, this); } Iterator back_iterator() { - return Iterator(tail); + return Iterator(tail, this); + } + + + Iterator erase(Iterator it) { + if (empty()) throw std::out_of_range("List is empty"); + if (it == end()) throw std::out_of_range("Invalid iterator"); + + Iterator target_it = it++; + + target_it == begin() ? pop_front() : + target_it == back_iterator() ? pop_back() : + erase_middle_node(target_it.current_node_ptr); + + return it; + } + + void move_to_begin(Iterator it) { + if (empty()) throw std::out_of_range("List is empty"); + if (it == end()) throw std::out_of_range("Invalid iterator"); + if (it == begin()) return; + + auto extracted = extract_node_and_link_prev_with_next(it.current_node_ptr); + emplace_node_before(std::move(extracted), head.get()); } + + template + void emplace_back(Args&&... args) { + auto new_node = std::make_unique>(tail, nullptr, std::forward(args)...); + link_new_back_node(std::move(new_node)); + } + + }; diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index 5765020..cb6927f 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -33,11 +33,21 @@ class OrderedMap { void insert(const KeyType& key, const ValueType& value) { if (_map.find(key) == _map.end()) { - _list.push_back(std::make_pair(key, value)); + _list.emplace_back(key, value); _map[key] = _list.back_iterator(); } else { - *_map.find(key)->second = std::make_pair(key, value); + (*_map[key]).second = value; + } + } + + void insert(const KeyType& key, ValueType&& value) { + if (_map.find(key) == _map.end()) { + _list.emplace_back(key, std::move(value)); + _map[key] = _list.back_iterator(); + } + else { + (*_map[key]).second = std::move(value); } } @@ -113,6 +123,15 @@ class OrderedMap { } return Iterator(_list.back_iterator()); } - + Iterator find(const KeyType& key) { + return _map.find(key) == _map.end() ? end() : Iterator(_map[key]); + } + + void move_to_front(const KeyType& key) { + if (_map.find(key) == _map.end()) { + throw std::out_of_range("Key not found in ordered map"); + } + _list.move_to_begin(_map[key]); + } }; \ No newline at end of file diff --git a/tests/doubly_linked_list/constructor_tests.cpp b/tests/doubly_linked_list/constructor_tests.cpp new file mode 100644 index 0000000..aa49fa2 --- /dev/null +++ b/tests/doubly_linked_list/constructor_tests.cpp @@ -0,0 +1,59 @@ +#include +#include "doubly_linked_list.hpp" +#include "../tests_utils.hpp" +#include +TEST_CASE("Default constructor creates an empty list", "[constructor]") { + DoublyLinkedList list; + REQUIRE(list.empty()); +} + +TEST_CASE("Constructor with initializer list {1, 2} creates a list with the elements in the initializer list", "[constructor]") { + DoublyLinkedList list = {1, 2}; + REQUIRE(list.size() == 2); + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 2); +} + +TEST_CASE("Constructor with initializer list {1, 2, 3, 4, 5} creates a list with the elements in the initializer list", "[constructor]") { + DoublyLinkedList list = {1, 2, 3, 4, 5}; + int value = 1; + for (auto it = list.begin(); it != list.end(); ++it) { + REQUIRE(*it == value++); + } +} + +TEST_CASE("Constructor with vector of size 10 creates a list with 10 elements", "[constructor]") { + std::vector vec(10); + DoublyLinkedList list(vec.begin(), vec.end()); + REQUIRE(list.size() == 10); +} + +TEST_CASE("Constructor with vector {1, 2, 3, 4, 5} creates a list with the elements in the vector", "[constructor]") { + std::vector vec = {1, 2, 3, 4, 5}; + DoublyLinkedList list(vec.begin(), vec.end()); + + auto vec_it = vec.begin(); + auto list_it = list.begin(); + + while (vec_it != vec.end()) { + REQUIRE(*list_it == *vec_it); + ++vec_it; ++list_it; + } + REQUIRE(list_it == list.end()); + REQUIRE(vec_it == vec.end()); +} + +TEST_CASE("Constructor with set of strings creates a list with the elements in the set", "[constructor]") { + std::set set = {"hello", "world", "test"}; + DoublyLinkedList list(set.begin(), set.end()); + + auto set_it = set.begin(); + auto list_it = list.begin(); + + while (set_it != set.end()) { + REQUIRE(*list_it == *set_it); + ++set_it; ++list_it; + } + REQUIRE(list_it == list.end()); + REQUIRE(set_it == set.end()); +} diff --git a/tests/doubly_linked_list/emplace_back_tests.cpp b/tests/doubly_linked_list/emplace_back_tests.cpp new file mode 100644 index 0000000..0e40ffb --- /dev/null +++ b/tests/doubly_linked_list/emplace_back_tests.cpp @@ -0,0 +1,15 @@ +#include +#include "doubly_linked_list.hpp" +#include "../tests_utils.hpp" + +TEST_CASE("emplace_back one value in an empty list makes size = 1", "[emplace_back]") { + DoublyLinkedList list; + list.emplace_back(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("emplace_back one pair in an empty list makes size = 1", "[emplace_back]") { + DoublyLinkedList> list; + list.emplace_back(1, 2); + REQUIRE(list.size() == 1); +} diff --git a/tests/doubly_linked_list/erase_tests.cpp b/tests/doubly_linked_list/erase_tests.cpp new file mode 100644 index 0000000..dd0900b --- /dev/null +++ b/tests/doubly_linked_list/erase_tests.cpp @@ -0,0 +1,103 @@ +#include "doubly_linked_list.hpp" +#include + +TEST_CASE("calling erase on an empty list throws an exception", "[erase]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.erase(list.begin()), std::out_of_range); +} + +TEST_CASE("push_back 1, call erase on end throws an exception", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE_THROWS_AS(list.erase(list.end()), std::out_of_range); +} + +TEST_CASE("push_back 1, call erase on begin returns end iterator", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.erase(list.begin()) == list.end()); +} + +TEST_CASE("push_back 1, call erase on begin makes the list empty", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.erase(list.begin()); + REQUIRE(list.empty()); +} + +TEST_CASE("push_back 1, 2, call erase on begin makes the front equal 2", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.erase(list.begin()); + REQUIRE(list.front() == 2); +} + +TEST_CASE("push_back 1, 2, call erase on back_iterator makes back equal 1", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.erase(list.back_iterator()); + REQUIRE(list.back() == 1); +} + +TEST_CASE("push_back 1, 2, call erase on begin returns iterator to 2", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.erase(list.begin()); + REQUIRE(*it == 2); +} + +TEST_CASE("push_back 1, 2, 3, call erase on second element makes list size = 2", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); list.erase(++it); + + REQUIRE(list.size() == 2); +} + +TEST_CASE("push_back 1, 2, 3, call erase on second element front = 1, back = 3", "[erase]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); list.erase(++it); + + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 3); +} + +TEST_CASE("push_back 10,000 elements, erase all elements", "[erase]") { + DoublyLinkedList list; + for (int i = 0; i < 10000; i++) { + list.push_back(i); + } + + auto it = list.begin(); + while (it != list.end()) { + it = list.erase(it); + } + + REQUIRE(list.empty()); +} + +TEST_CASE("push_back 10,000 elements, erase every other element", "[erase]") { + DoublyLinkedList list; + for (int i = 0; i < 10000; i++) { + list.push_back(i); + } + + auto it = list.begin(); + while (it != list.end()) { + it = list.erase(it); + if (it != list.end()) { + it++; + } + } + + REQUIRE(list.size() == 5000); +} + diff --git a/tests/doubly_linked_list/front_and_back.cpp b/tests/doubly_linked_list/front_and_back.cpp index c245bd5..3c9b6f4 100644 --- a/tests/doubly_linked_list/front_and_back.cpp +++ b/tests/doubly_linked_list/front_and_back.cpp @@ -1,6 +1,7 @@ #include #include "doubly_linked_list.hpp" #include +#include "../tests_utils.hpp" TEST_CASE("Canary test", "[canary]") { REQUIRE(true); @@ -11,6 +12,27 @@ TEST_CASE("front on an empty list throws an exception", "[front]") { REQUIRE_THROWS_AS(list.front(), std::out_of_range); } +TEST_CASE("front on a list with one element returns the element", "[front]") { + DoublyLinkedList list = {1}; + REQUIRE(list.front() == 1); +} + +TEST_CASE("front on a list with {1, 2} returns 1", "[front]") { + DoublyLinkedList list = {1, 2}; + REQUIRE(list.front() == 1); +} + +TEST_CASE("front on a list with a MoveCopyFlag element returns the element without copying it", "[front]") { + DoublyLinkedList list; + list.push_back(MoveCopyFlag()); + REQUIRE(list.front().state == "default, moved"); +} + +TEST_CASE("front on a list constructed with initializer list of MoveCopyFlag returns the element copied once", "[front]") { + DoublyLinkedList list = {MoveCopyFlag()}; + REQUIRE(list.front().state == "default, copied"); +} + TEST_CASE("back on an empty list throws an exception", "[back]") { DoublyLinkedList list; REQUIRE_THROWS_AS(list.back(), std::out_of_range); diff --git a/tests/doubly_linked_list/iterator_tests.cpp b/tests/doubly_linked_list/iterator_tests.cpp index 5b3bc82..1606831 100644 --- a/tests/doubly_linked_list/iterator_tests.cpp +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -169,3 +169,11 @@ TEST_CASE("push_back 1, 2, dereferencing back iterator returns 2", "[back_iterat REQUIRE(*it == 2); } +TEST_CASE("push_back 1, 2, 3, using decerement (--) on end iterator returns 3", "[back_iterator]") { + DoublyLinkedList list; + list.push_back(1); list.push_back(2); list.push_back(3); + auto it = list.end(); + --it; + + REQUIRE(*it == 3); +} \ No newline at end of file diff --git a/tests/doubly_linked_list/move_to_begin_tests.cpp b/tests/doubly_linked_list/move_to_begin_tests.cpp new file mode 100644 index 0000000..e5e8778 --- /dev/null +++ b/tests/doubly_linked_list/move_to_begin_tests.cpp @@ -0,0 +1,88 @@ +#include +#include "doubly_linked_list.hpp" +#include "../tests_utils.hpp" + +TEST_CASE("move_to_front on an empty list throws an exception", "[move_to_front]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.move_to_begin(list.begin()), std::out_of_range); +} + +TEST_CASE("list with 1, 2, move_to_front with end iterator throws an exception", "[move_to_front]") { + DoublyLinkedList list = {1, 2}; + REQUIRE_THROWS_AS(list.move_to_begin(list.end()), std::out_of_range); +} + +TEST_CASE("list with 1, 2, move_to_front with begin iterator does nothing", "[move_to_front]") { + DoublyLinkedList list = {1, 2}; + list.move_to_begin(list.begin()); + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 2); +} + +TEST_CASE("list with 1, 2, move_to_front with iterator to 2 moves 2 to front", "[move_to_front]") { + DoublyLinkedList list = {1, 2}; + auto it = list.end(); + list.move_to_begin(--it); + REQUIRE(list.front() == 2); + REQUIRE(list.back() == 1); +} + +TEST_CASE("list with 1, 2, 3, move_to_begin with iterator to 2 moves 2 to front", "[move_to_begin]") { + DoublyLinkedList list = {1, 2, 3}; + auto it = list.begin(); + list.move_to_begin(++it); + + REQUIRE(list.front() == 2); + REQUIRE(*++it == 1); + REQUIRE(list.back() == 3); +} + +TEST_CASE("list with 2 MoveCopyFlag elements, move_to_begin with iterator to 1st does nothing, no additional move or copy", "[move_to_begin]") { + DoublyLinkedList list; + list.push_back(MoveCopyFlag()); + list.push_back(MoveCopyFlag()); + auto it = list.begin(); + list.move_to_begin(it); + REQUIRE(list.front().state == "default, moved"); + REQUIRE(list.back().state == "default, moved"); +} + +TEST_CASE("list with 2 MoveCopyFlag elements constructed with move, move_to_begin with iterator to 2nd moves it to front with no additional move or copy", "[move_to_begin]") { + DoublyLinkedList list; + list.push_back(MoveCopyFlag()); + list.push_back(MoveCopyFlag()); + auto it = list.begin(); + auto it_to_second_element = ++it; + list.move_to_begin(it_to_second_element); + + REQUIRE(list.front().state == "default, moved"); +} + +TEST_CASE("list with 5 MoveCopyFlag elements copied via initializer list, move_to_begin with iterator to 3rd moves it to front with no additional move or copy", "[move_to_begin]") { + DoublyLinkedList list = { + MoveCopyFlag(), + MoveCopyFlag(), + MoveCopyFlag(), + MoveCopyFlag(), + MoveCopyFlag() + }; + auto it = list.begin(); ++it; ++it; + list.move_to_begin(it); + REQUIRE(list.front().state == "default, copied"); +} + +TEST_CASE("list with 5 strings, reversed using move_to_begin", "[move_to_begin]") { + DoublyLinkedList list = {"a", "b", "c", "d", "e"}; + auto it = list.begin(); + while(it != list.end()) { + list.move_to_begin(it++); + } + + it = list.begin(); + REQUIRE(*it++ == "e"); + REQUIRE(*it++ == "d"); + REQUIRE(*it++ == "c"); + REQUIRE(*it++ == "b"); + REQUIRE(*it++ == "a"); +} + diff --git a/tests/doubly_linked_list/push_back_tests.cpp b/tests/doubly_linked_list/push_back_tests.cpp index 5e7d3e7..686d55b 100644 --- a/tests/doubly_linked_list/push_back_tests.cpp +++ b/tests/doubly_linked_list/push_back_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include "../tests_utils.hpp" TEST_CASE("push_back one value in an empty list makes size = 1", "[push_back]") { DoublyLinkedList list; @@ -57,3 +57,19 @@ TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { list.push_back(1); REQUIRE(list.back() == 1); } + +TEST_CASE("using move with push_back does not copy the value", "[push_back]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_back(std::move(object)); + + REQUIRE(list.back().state == "default, moved"); +} + +TEST_CASE("passing variable by value to push_back copies the value", "[push_back]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_back(object); + + REQUIRE(list.back().state == "default, copied"); +} \ No newline at end of file diff --git a/tests/doubly_linked_list/push_front_tests.cpp b/tests/doubly_linked_list/push_front_tests.cpp index 9903a1c..641d278 100644 --- a/tests/doubly_linked_list/push_front_tests.cpp +++ b/tests/doubly_linked_list/push_front_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include "../tests_utils.hpp" TEST_CASE("push_front 1, size = 1", "[push_front]") { @@ -45,3 +45,18 @@ TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_front]" list.pop_back(); REQUIRE(list.size() == 0); } + +TEST_CASE("using move with push_front does not copy the value", "[push_front]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_front(std::move(object)); + + REQUIRE(list.front().state == "default, moved"); +} + +TEST_CASE("passing variable by value to push_front copies the value", "[push_front]") { + DoublyLinkedList list; + auto object = MoveCopyFlag(); + list.push_front(object); + REQUIRE(list.front().state == "default, copied"); +} \ No newline at end of file diff --git a/tests/ordered_map/find_tests.cpp b/tests/ordered_map/find_tests.cpp new file mode 100644 index 0000000..690fbce --- /dev/null +++ b/tests/ordered_map/find_tests.cpp @@ -0,0 +1,24 @@ +#include "ordered_map.hpp" +#include + +TEST_CASE("calling find on an empty map returns end()", "[find]") { + OrderedMap o_map; + REQUIRE(o_map.find(1) == o_map.end()); +} + +TEST_CASE("calling find with a key that does not exist returns end()", "[find]") { + OrderedMap o_map; + o_map.insert(1, 2); + REQUIRE(o_map.find(2) == o_map.end()); +} + +TEST_CASE("calling find with a key that exists returns an iterator to the key", "[find]") { + OrderedMap o_map; + o_map.insert(1, 2); + auto it = o_map.find(1); + + REQUIRE(*it == std::make_pair(1, 2)); +} + + + diff --git a/tests/ordered_map/move_to_front_tests.cpp b/tests/ordered_map/move_to_front_tests.cpp new file mode 100644 index 0000000..e363c10 --- /dev/null +++ b/tests/ordered_map/move_to_front_tests.cpp @@ -0,0 +1,57 @@ +#include +#include "ordered_map.hpp" +#include "../tests_utils.hpp" + +TEST_CASE("move_to_front on an empty map throws an exception", "[move_to_front]") { + OrderedMap o_map; + REQUIRE_THROWS_AS(o_map.move_to_front(1), std::out_of_range); +} + +TEST_CASE("move_to_front on a map with one element has the same element at the front", "[move_to_front]") { + OrderedMap o_map; + o_map.insert(1, 0); + o_map.move_to_front(1); + REQUIRE(o_map.begin()->first == 1); +} + +TEST_CASE("move_to_front on a map with two elements moves the element to the front", "[move_to_front]") { + OrderedMap o_map; + o_map.insert(1, 0); + o_map.insert(2, 0); + o_map.move_to_front(2); + + REQUIRE(o_map.begin()->first == 2); +} + +TEST_CASE("move_to_front does not copy the value", "[move_to_front]") { + OrderedMap o_map; + o_map.insert(1, MoveCopyFlag()); + o_map.insert(2, MoveCopyFlag()); + o_map.move_to_front(2); + + REQUIRE(o_map.begin()->second.state == "default, moved"); +} + +TEST_CASE("map with 5 strings, move_to_front reverses the order of the elements") { + OrderedMap o_map = { + {"a", 0}, + {"b", 0}, + {"c", 0}, + {"d", 0}, + {"e", 0} + }; + + auto it = o_map.begin(); + while (it != o_map.end()) { + o_map.move_to_front((it++)->first); + } + + it = o_map.begin(); + REQUIRE((it++)->first == "e"); + REQUIRE((it++)->first == "d"); + REQUIRE((it++)->first == "c"); + REQUIRE((it++)->first == "b"); + REQUIRE((it++)->first == "a"); +} + + diff --git a/tests/tests_utils.hpp b/tests/tests_utils.hpp new file mode 100644 index 0000000..7146f6e --- /dev/null +++ b/tests/tests_utils.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + +class MoveCopyFlag { + public: + std::string state; + MoveCopyFlag() : state("default") {} + MoveCopyFlag(const MoveCopyFlag& other) : state(other.state + ", copied") {} + MoveCopyFlag(MoveCopyFlag&& other) : state(other.state + ", moved") {} + + MoveCopyFlag& operator=(const MoveCopyFlag& other) { + state = other.state + ", copied"; + return *this; + } + + MoveCopyFlag& operator=(MoveCopyFlag&& other) { + state = other.state + ", moved"; + return *this; + } +}; \ No newline at end of file