From 18c1d916587cf11ccad7541d5da0d0c2cc3556d8 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 20:52:26 -0500 Subject: [PATCH 01/46] Implement a few tests for doubly linked list and make them pass by implementing: - method to return size - method to push back - method to pop back - method to get front value - method to get back value --- include/doubly_linked_list.hpp | 52 ++++++++++++++++++++++ src/doubly_linked_list.cpp | 3 ++ tests/test_doubly_linked_list.cpp | 72 ++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index e69de29..57f83b3 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -0,0 +1,52 @@ +#pragma once + +// implement node with template type +template +struct Node { + T value; + Node* prev; + Node* next; +}; + +template +class DoublyLinkedList { +private: + Node* head; + Node* tail; + int _size; + +public: + DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} + + + void push_back(const T& value){ + if (head == nullptr) { + head = new Node{value, nullptr, nullptr}; + tail = head; + } + else { + tail->next = new Node{value, tail, nullptr}; + tail = tail->next; + } + + _size++; + } + + T pop_back() { + Node* temp = tail; + tail = tail->prev; + return temp->value; + } + + int size() const { + return _size; + } + + T front() const { + return head->value; + } + + T back() const { + return tail->value; + } +}; diff --git a/src/doubly_linked_list.cpp b/src/doubly_linked_list.cpp index e69de29..0a5f75b 100644 --- a/src/doubly_linked_list.cpp +++ b/src/doubly_linked_list.cpp @@ -0,0 +1,3 @@ +#include "doubly_linked_list.hpp" + + diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index e6e2409..2e70a96 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -3,4 +3,74 @@ TEST_CASE("Canary test", "[canary]") { REQUIRE(true); -} \ No newline at end of file +} + +TEST_CASE("Test push_back one value in an empty list makes size = 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("Test push_back two values in an empty list makes size = 2", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.size() == 2); +} + +TEST_CASE("Test push_back 1, 2 and front returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.front() == 1); +} + +TEST_CASE("Test push_back 2, 1 and front returns 2", "[push_back]") { + DoublyLinkedList list; + list.push_back(2); + list.push_back(1); + REQUIRE(list.front() == 2); +} + +TEST_CASE("Test push_back 1, 2 and back returns 2", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.back() == 2); +} + +TEST_CASE("Test push_back 2, 1 and back returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(2); + list.push_back(1); + REQUIRE(list.back() == 1); +} + +TEST_CASE("Test push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_back(); + REQUIRE(list.back() == 2); +} + +TEST_CASE("Test push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_back(); + list.pop_back(); + REQUIRE(list.back() == 1); +} + +TEST_CASE("Test push_back 1, 2, 3, pop_back once, front return 1", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_back(); + REQUIRE(list.front() == 1); +} + From 7b95b1c2343015f90b4fe5120e3a4fa5f8ed2474 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 21:25:16 -0500 Subject: [PATCH 02/46] Use unique pointer for head and next in linked list --- include/doubly_linked_list.hpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 57f83b3..07bb4e5 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -1,32 +1,34 @@ #pragma once +#include + -// implement node with template type template struct Node { T value; + std::unique_ptr next; Node* prev; - Node* next; + + Node(const T& val, Node* p, std::unique_ptr n) + : value(val), prev(p), next(std::move(n)) {} }; template class DoublyLinkedList { private: - Node* head; + std::unique_ptr> head; Node* tail; int _size; public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} - - void push_back(const T& value){ - if (head == nullptr) { - head = new Node{value, nullptr, nullptr}; - tail = head; - } - else { - tail->next = new Node{value, tail, nullptr}; - tail = tail->next; + 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++; From 403a3de7f3d46f6724403904070c8e88558f5a8b Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 21:26:17 -0500 Subject: [PATCH 03/46] Make parameters more expressive in Node constructor --- include/doubly_linked_list.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 07bb4e5..2bfdd54 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -8,8 +8,8 @@ struct Node { std::unique_ptr next; Node* prev; - Node(const T& val, Node* p, std::unique_ptr n) - : value(val), prev(p), next(std::move(n)) {} + 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 From dd827d07c76c6b3ead1a319eee2f573df372dca6 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 21:38:17 -0500 Subject: [PATCH 04/46] Fix pop back - Fix memory deletion logic with unique pointer - Add size test and make it pass --- include/doubly_linked_list.hpp | 12 +++++++++--- tests/test_doubly_linked_list.cpp | 8 ++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 2bfdd54..d415801 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -35,9 +35,15 @@ class DoublyLinkedList { } T pop_back() { - Node* temp = tail; - tail = tail->prev; - return temp->value; + T popped_value = tail->value; + + Node* prev_node = tail->prev; + prev_node->next.reset(); + tail = prev_node; + + _size--; + + return popped_value; } int size() const { diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index 2e70a96..906e975 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -46,6 +46,14 @@ TEST_CASE("Test push_back 2, 1 and back returns 1", "[push_back]") { REQUIRE(list.back() == 1); } +TEST_CASE("Test push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.pop_back(); + REQUIRE(list.size() == 1); +} + TEST_CASE("Test push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { DoublyLinkedList list; list.push_back(1); From f382ab59fc0b96a0ab5b60a05d3a4c14f556e411 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 21:49:42 -0500 Subject: [PATCH 05/46] Add tests for pop_back, front and back and make them pass - Test popping on list with 1 element - Test the return value when popping - Test popping, getting front and back on an empty list --- include/doubly_linked_list.hpp | 22 ++++++++++++++++--- tests/test_doubly_linked_list.cpp | 35 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index d415801..cf6942a 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -35,11 +35,20 @@ class DoublyLinkedList { } T pop_back() { + if (!head) { + throw std::runtime_error("List is empty"); + } + T popped_value = tail->value; - Node* prev_node = tail->prev; - prev_node->next.reset(); - tail = prev_node; + if (tail == head.get()) { + head.reset(); + tail = nullptr; + } else { + Node* prev_node = tail->prev; + prev_node->next.reset(); + tail = prev_node; + } _size--; @@ -51,10 +60,17 @@ class DoublyLinkedList { } T front() const { + if (!head) { + throw std::runtime_error("List is empty"); + } + return head->value; } T back() const { + if (!tail) { + throw std::runtime_error("List is empty"); + } return tail->value; } }; diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index 906e975..7be3155 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -46,6 +46,41 @@ TEST_CASE("Test push_back 2, 1 and back returns 1", "[push_back]") { REQUIRE(list.back() == 1); } +TEST_CASE("Test push_back 1, pop_back once, size = 0", "[pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.pop_back(); + REQUIRE(list.size() == 0); +} + +TEST_CASE("Test push_back 1, pop_back returns 1", "[pop_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.pop_back() == 1); +} + +TEST_CASE("Test push_back 1, 2, pop_back returns 2", "[pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.pop_back() == 2); +} + +TEST_CASE("Test pop_back on an empty list throws an exception", "[pop_back]") { + DoublyLinkedList list; + REQUIRE_THROWS(list.pop_back()); +} + +TEST_CASE("Test front on an empty list throws an exception", "[front]") { + DoublyLinkedList list; + REQUIRE_THROWS(list.front()); +} + +TEST_CASE("Test back on an empty list throws an exception", "[back]") { + DoublyLinkedList list; + REQUIRE_THROWS(list.back()); +} + TEST_CASE("Test push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { DoublyLinkedList list; list.push_back(1); From bcdeef5ac358d25ed317b47d282b8c2794bbb615 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 21:55:19 -0500 Subject: [PATCH 06/46] Fixate the random seed in tests --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index f9d509d..6bc597f 100755 --- a/build.sh +++ b/build.sh @@ -18,4 +18,4 @@ cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON make # Run the tests -./tests \ No newline at end of file +./tests --rng-seed 0 From eceeb729aa76428dc87fa381aa00a4de2ffbdb3f Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 22:22:18 -0500 Subject: [PATCH 07/46] Add tests for push front, make them pass --- include/doubly_linked_list.hpp | 41 +++++++++++++++++++---------- tests/test_doubly_linked_list.cpp | 43 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index cf6942a..bfd3031 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -22,6 +22,25 @@ class DoublyLinkedList { public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} + int size() const { + return _size; + } + + T front() const { + if (!head) { + throw std::runtime_error("List is empty"); + } + + return head->value; + } + + T back() const { + if (!tail) { + throw std::runtime_error("List is empty"); + } + return tail->value; + } + void push_back(const T& value) { if (!head) { head = std::make_unique>(value, nullptr, nullptr); @@ -55,22 +74,18 @@ class DoublyLinkedList { return popped_value; } - int size() const { - return _size; - } - - T front() const { + void push_front(const T& value) { if (!head) { - throw std::runtime_error("List is empty"); + 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); } - return head->value; + _size++; } - T back() const { - if (!tail) { - throw std::runtime_error("List is empty"); - } - return tail->value; - } }; diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index 7be3155..c69ca56 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -117,3 +117,46 @@ TEST_CASE("Test push_back 1, 2, 3, pop_back once, front return 1", "[push_back, REQUIRE(list.front() == 1); } +TEST_CASE("Test push_front 1, size = 1", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("Test push_front 1, 2, size = 2", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + REQUIRE(list.size() == 2); +} + +TEST_CASE("Test push_front 1, front returns 1", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + REQUIRE(list.front() == 1); +} + +TEST_CASE("Test push_front 1, 2 and front returns 2", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + REQUIRE(list.front() == 2); +} + +TEST_CASE("Test push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + list.push_back(3); + REQUIRE(list.back() == 3); +} + +TEST_CASE("Test push_back 1, push_front 2, pop_back twice, size is 0", "[push_back, push_front, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_front(2); + list.pop_back(); + list.pop_back(); + REQUIRE(list.size() == 0); +} + From 40929c0e6f8847861c9f770034935788ae6fc675 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 22:41:53 -0500 Subject: [PATCH 08/46] Refactor exception types, add tests for pop front and make them pass --- include/doubly_linked_list.hpp | 20 +++++- tests/test_doubly_linked_list.cpp | 100 ++++++++++++++++++++++-------- 2 files changed, 92 insertions(+), 28 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index bfd3031..a27e710 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -28,7 +28,7 @@ class DoublyLinkedList { T front() const { if (!head) { - throw std::runtime_error("List is empty"); + throw std::out_of_range("List is empty"); } return head->value; @@ -36,7 +36,7 @@ class DoublyLinkedList { T back() const { if (!tail) { - throw std::runtime_error("List is empty"); + throw std::out_of_range("List is empty"); } return tail->value; } @@ -55,7 +55,7 @@ class DoublyLinkedList { T pop_back() { if (!head) { - throw std::runtime_error("List is empty"); + throw std::out_of_range("List is empty"); } T popped_value = tail->value; @@ -88,4 +88,18 @@ class DoublyLinkedList { _size++; } + T pop_front() { + if (!head) + throw std::out_of_range("List is empty"); + + T popped_value = head->value; + head = std::move(head->next); + + if (!head) + tail = nullptr; + + _size--; + return popped_value; + } + }; diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index c69ca56..5c1e923 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -5,83 +5,83 @@ TEST_CASE("Canary test", "[canary]") { REQUIRE(true); } -TEST_CASE("Test push_back one value in an empty list makes size = 1", "[push_back]") { +TEST_CASE("push_back one value in an empty list makes size = 1", "[push_back]") { DoublyLinkedList list; list.push_back(1); REQUIRE(list.size() == 1); } -TEST_CASE("Test push_back two values in an empty list makes size = 2", "[push_back]") { +TEST_CASE("push_back two values in an empty list makes size = 2", "[push_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); REQUIRE(list.size() == 2); } -TEST_CASE("Test push_back 1, 2 and front returns 1", "[push_back]") { +TEST_CASE("push_back 1, 2 and front returns 1", "[push_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); REQUIRE(list.front() == 1); } -TEST_CASE("Test push_back 2, 1 and front returns 2", "[push_back]") { +TEST_CASE("push_back 2, 1 and front returns 2", "[push_back]") { DoublyLinkedList list; list.push_back(2); list.push_back(1); REQUIRE(list.front() == 2); } -TEST_CASE("Test push_back 1, 2 and back returns 2", "[push_back]") { +TEST_CASE("push_back 1, 2 and back returns 2", "[push_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); REQUIRE(list.back() == 2); } -TEST_CASE("Test push_back 2, 1 and back returns 1", "[push_back]") { +TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { DoublyLinkedList list; list.push_back(2); list.push_back(1); REQUIRE(list.back() == 1); } -TEST_CASE("Test push_back 1, pop_back once, size = 0", "[pop_back]") { +TEST_CASE("push_back 1, pop_back once, size = 0", "[pop_back]") { DoublyLinkedList list; list.push_back(1); list.pop_back(); REQUIRE(list.size() == 0); } -TEST_CASE("Test push_back 1, pop_back returns 1", "[pop_back]") { +TEST_CASE("push_back 1, pop_back returns 1", "[pop_back]") { DoublyLinkedList list; list.push_back(1); REQUIRE(list.pop_back() == 1); } -TEST_CASE("Test push_back 1, 2, pop_back returns 2", "[pop_back]") { +TEST_CASE("push_back 1, 2, pop_back returns 2", "[pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); REQUIRE(list.pop_back() == 2); } -TEST_CASE("Test pop_back on an empty list throws an exception", "[pop_back]") { +TEST_CASE("pop_back on an empty list throws an exception", "[pop_back]") { DoublyLinkedList list; - REQUIRE_THROWS(list.pop_back()); + REQUIRE_THROWS_AS(list.pop_back(), std::out_of_range); } -TEST_CASE("Test front on an empty list throws an exception", "[front]") { +TEST_CASE("front on an empty list throws an exception", "[front]") { DoublyLinkedList list; - REQUIRE_THROWS(list.front()); + REQUIRE_THROWS_AS(list.front(), std::out_of_range); } -TEST_CASE("Test back on an empty list throws an exception", "[back]") { +TEST_CASE("back on an empty list throws an exception", "[back]") { DoublyLinkedList list; - REQUIRE_THROWS(list.back()); + REQUIRE_THROWS_AS(list.back(), std::out_of_range); } -TEST_CASE("Test push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -89,7 +89,7 @@ TEST_CASE("Test push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back] REQUIRE(list.size() == 1); } -TEST_CASE("Test push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -98,7 +98,7 @@ TEST_CASE("Test push_back 1, 2, 3, pop_back once, back return 2", "[push_back, p REQUIRE(list.back() == 2); } -TEST_CASE("Test push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -108,7 +108,7 @@ TEST_CASE("Test push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, REQUIRE(list.back() == 1); } -TEST_CASE("Test push_back 1, 2, 3, pop_back once, front return 1", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, 3, pop_back once, front return 1", "[push_back, pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -117,33 +117,33 @@ TEST_CASE("Test push_back 1, 2, 3, pop_back once, front return 1", "[push_back, REQUIRE(list.front() == 1); } -TEST_CASE("Test push_front 1, size = 1", "[push_front]") { +TEST_CASE("push_front 1, size = 1", "[push_front]") { DoublyLinkedList list; list.push_front(1); REQUIRE(list.size() == 1); } -TEST_CASE("Test push_front 1, 2, size = 2", "[push_front]") { +TEST_CASE("push_front 1, 2, size = 2", "[push_front]") { DoublyLinkedList list; list.push_front(1); list.push_front(2); REQUIRE(list.size() == 2); } -TEST_CASE("Test push_front 1, front returns 1", "[push_front]") { +TEST_CASE("push_front 1, front returns 1", "[push_front]") { DoublyLinkedList list; list.push_front(1); REQUIRE(list.front() == 1); } -TEST_CASE("Test push_front 1, 2 and front returns 2", "[push_front]") { +TEST_CASE("push_front 1, 2 and front returns 2", "[push_front]") { DoublyLinkedList list; list.push_front(1); list.push_front(2); REQUIRE(list.front() == 2); } -TEST_CASE("Test push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") { +TEST_CASE("push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") { DoublyLinkedList list; list.push_front(1); list.push_front(2); @@ -151,7 +151,7 @@ TEST_CASE("Test push_front 1, 2, push_back 3, back is 3", "[push_front, push_bac REQUIRE(list.back() == 3); } -TEST_CASE("Test push_back 1, push_front 2, pop_back twice, size is 0", "[push_back, push_front, pop_back]") { +TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_back, push_front, pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_front(2); @@ -160,3 +160,53 @@ TEST_CASE("Test push_back 1, push_front 2, pop_back twice, size is 0", "[push_ba REQUIRE(list.size() == 0); } +TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[push_back, pop_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.pop_front(); + REQUIRE(list.size() == 1); +} + +TEST_CASE("push_front 1, pop_front returns 1", "[push_front, pop_front]") { + DoublyLinkedList list; + list.push_front(1); + REQUIRE(list.pop_front() == 1); +} + +TEST_CASE("push_front 1, 2, pop_front returns 2", "[push_front, pop_front]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + REQUIRE(list.pop_front() == 2); +} + +TEST_CASE("push_back 1, 2, 3, pop_front once, front is 2", "[push_back, pop_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_front(); + REQUIRE(list.front() == 2); +} + +TEST_CASE("pop_front on an empty list throws an exception", "[pop_front]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.pop_front(), std::out_of_range); +} + +TEST_CASE("pop_front clears tail when list becomes empty") { + DoublyLinkedList list; + list.push_back(42); + list.pop_front(); + REQUIRE_THROWS_AS(list.back(), std::out_of_range); +} + +TEST_CASE("push_back 1, pop_back, push_front 2, pop front returns 2", "[push_back, pop_back, push_front, pop_front]") { + DoublyLinkedList list; + list.push_back(1); + list.pop_back(); + list.push_front(2); + REQUIRE(list.pop_front() == 2); +} + From 0f33217ba8bfbdaa6601b4f7ea5a0bea7003769f Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 22:45:28 -0500 Subject: [PATCH 09/46] Make front() and back() return const refs to prevent copying and mutation in list --- include/doubly_linked_list.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index a27e710..8982947 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -26,7 +26,7 @@ class DoublyLinkedList { return _size; } - T front() const { + const T& front() const { if (!head) { throw std::out_of_range("List is empty"); } @@ -34,7 +34,7 @@ class DoublyLinkedList { return head->value; } - T back() const { + const T& back() const { if (!tail) { throw std::out_of_range("List is empty"); } From dd2214e8327e2fbcf57fc7cedac79618e3128dd8 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Wed, 14 May 2025 22:56:29 -0500 Subject: [PATCH 10/46] Update readme to reflect current state of the project --- README.md | 69 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5755afe..1436a7d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ A C++ project implementing an ordered map using a doubly-linked list to maintain element insertion order, built with CMake, tested with Catch2, and formatted with Clang-Format. ## Table of Contents + - [Prerequisites](#prerequisites) - [Project Structure](#project-structure) - [Setting Up VS Code (or Cursor)](#setting-up-vs-code-or-cursor) @@ -22,6 +23,7 @@ A C++ project implementing an ordered map using a doubly-linked list to maintain - **VS Code** or **Cursor**: Code editor with C++ support Verify installations: + ```bash cmake --version git --version @@ -40,22 +42,24 @@ ordered_map/ ├── build.sh # Script to build and test ├── .clang-format # Code formatting rules ├── include/ -│ └── ordered_map.hpp # Ordered map interface +│ ├── doubly_linked_list.hpp # Doubly-linked list implementation +│ └── ordered_map.hpp # (Future) Ordered map implementation ├── src/ -│ └── doubly_linked_list.cpp # Doubly-linked list implementation ├── tests/ -│ └── test_ordered_map.cpp # Catch2 test files +│ └── test_doubly_linked_list.cpp # Catch2 test files ``` ## Setting Up VS Code (or Cursor) 1. **Install Extensions**: + - In VS Code/Cursor, go to Extensions (Ctrl+Shift+X). - Install: - **C/C++** (Microsoft): For IntelliSense and debugging. - **CMake Tools** (Microsoft): For CMake integration. 2. **Configure IntelliSense**: + - Create `.vscode/settings.json` (or `.cursor/settings.json`): ```json { @@ -85,13 +89,16 @@ ordered_map/ ## Building and Running Tests 1. **Build and Test**: + ```bash chmod +x build.sh ./build.sh ``` + - Formats code, builds with CMake/make, and runs Catch2 tests. 2. **Output**: + - Successful tests show: ``` All tests passed (X assertions in Y test cases) @@ -111,18 +118,21 @@ ordered_map/ 2. Ensure `.github/workflows/cmake.yml` exists. 3. CI runs `build.sh` on `ubuntu-latest`, formatting, building, and testing. - **View Results**: - - Check the “Actions” tab on GitHub for CI logs. + - Check the "Actions" tab on GitHub for CI logs. ## Adding a New Test -1. **Edit `tests/test_ordered_map.cpp`**: - - Add a `TEST_CASE` for the ordered map or doubly-linked list. Example: +1. **Edit `tests/test_doubly_linked_list.cpp`**: + + - Add a `TEST_CASE` for the doubly-linked list. Example: ```cpp - TEST_CASE("Insert maintains order", "[ordered_map]") { - OrderedMap map; - map.insert(1, 10); - map.insert(2, 20); - REQUIRE(map.size() == 2); + TEST_CASE("Push back maintains order", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.size() == 2); + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 2); } ``` @@ -140,27 +150,44 @@ ordered_map/ - Clear build: `rm -rf build && ./build.sh`. - Check prerequisites. - **Test Failures**: - - Inspect assertions in `test_ordered_map.cpp`. + - Inspect assertions in `test_doubly_linked_list.cpp`. - Run specific tests: `./build/tests --tags [tag]`. - **GitHub Actions Failures**: - - Check logs in the “Actions” tab on GitHub. + - Check logs in the "Actions" tab on GitHub. - Ensure `build.sh` and dependencies are compatible with `ubuntu-latest`. ## Example Test File -`tests/test_ordered_map.cpp`: +`tests/test_doubly_linked_list.cpp`: + ```cpp #include -#include "ordered_map.hpp" +#include "doubly_linked_list.hpp" TEST_CASE("Canary test", "[canary]") { REQUIRE(true); } -TEST_CASE("Insert maintains order", "[ordered_map]") { - OrderedMap map; - map.insert(1, 10); - map.insert(2, 20); - REQUIRE(map.size() == 2); +TEST_CASE("Push back maintains order", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.size() == 2); + REQUIRE(list.front() == 1); + REQUIRE(list.back() == 2); } -``` \ No newline at end of file +``` + +## Project Status + +The project is currently in development, with the following components: + +1. ✅ Doubly Linked List Implementation + + - Basic operations (push_back, pop_back, push_front, pop_front) + - Memory management using smart pointers + - Comprehensive test coverage + +2. 🔄 Ordered Map Implementation (Coming Soon) + - Will use the doubly linked list as its underlying data structure + - Will maintain insertion order while providing map-like functionality From 531a76216237e95ae723bd6b6251ac1b68814da7 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Thu, 15 May 2025 19:51:20 -0500 Subject: [PATCH 11/46] Add two tests for push back and front, add tests for iterator initial state, make them pass --- include/doubly_linked_list.hpp | 20 ++++++++++++++++++++ tests/test_doubly_linked_list.cpp | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 8982947..a019da1 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -102,4 +102,24 @@ class DoublyLinkedList { return popped_value; } + class Iterator { + Node* current_node_ptr; + + Iterator(Node* a_current) : current_node_ptr(a_current) {} + + friend class DoublyLinkedList; + + public: + bool operator==(const Iterator& other) const { + return current_node_ptr == other.current_node_ptr; + } + }; + + Iterator begin() { + return Iterator(nullptr); + } + + Iterator end() { + return Iterator(nullptr); + } }; diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index 5c1e923..eba831b 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -18,6 +18,12 @@ TEST_CASE("push_back two values in an empty list makes size = 2", "[push_back]") REQUIRE(list.size() == 2); } +TEST_CASE("push_back 1 and front returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.front() == 1); +} + TEST_CASE("push_back 1, 2 and front returns 1", "[push_back]") { DoublyLinkedList list; list.push_back(1); @@ -32,6 +38,12 @@ TEST_CASE("push_back 2, 1 and front returns 2", "[push_back]") { REQUIRE(list.front() == 2); } +TEST_CASE("push_back 1, and back returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.back() == 1); +} + TEST_CASE("push_back 1, 2 and back returns 2", "[push_back]") { DoublyLinkedList list; list.push_back(1); @@ -210,3 +222,7 @@ TEST_CASE("push_back 1, pop_back, push_front 2, pop front returns 2", "[push_bac REQUIRE(list.pop_front() == 2); } +TEST_CASE("begin iterator equals end iterator in empty list", "[begin, end]") { + DoublyLinkedList list; + REQUIRE(list.begin() == list.end()); +} From f096f6b5f6c39846cab0accff2426cdb97f8e262 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Thu, 15 May 2025 20:57:11 -0500 Subject: [PATCH 12/46] Create tests for iterator equality, inequality, and dereferencing by reference --- include/doubly_linked_list.hpp | 10 ++++++++- tests/test_doubly_linked_list.cpp | 37 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index a019da1..9516c20 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -113,10 +113,18 @@ class DoublyLinkedList { bool operator==(const Iterator& other) const { return current_node_ptr == other.current_node_ptr; } + + bool operator!=(const Iterator& other) const { + return current_node_ptr != other.current_node_ptr; + } + + T& operator*() const { + return current_node_ptr->value; + } }; Iterator begin() { - return Iterator(nullptr); + return Iterator(head.get()); } Iterator end() { diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index eba831b..2e1eb47 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -226,3 +226,40 @@ TEST_CASE("begin iterator equals end iterator in empty list", "[begin, end]") { DoublyLinkedList list; REQUIRE(list.begin() == list.end()); } + +TEST_CASE("push_back 1, begin iterator does not equal end iterator", "[begin, end]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(!(list.begin() == list.end())); +} + +TEST_CASE("begin iterator != end iterator returns false in an empty list", "[begin, end]") { + DoublyLinkedList list; + REQUIRE(!(list.begin() != list.end())); +} + +TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[begin, end]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.begin() != list.end()); +} + +TEST_CASE("push_back 1, begin iterator dereferences to 1", "[begin, end]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(*list.begin() == 1); +} + +TEST_CASE("push_back 2, begin iterator dereferences to 2", "[begin, end]") { + DoublyLinkedList list; + list.push_back(2); + REQUIRE(*list.begin() == 2); +} + +TEST_CASE("push_back hello string, modify string by dereferencing begin, front returns modified string", "[begin, end]") { + DoublyLinkedList list; + list.push_back("hello"); + auto &str = *list.begin(); + str[0] = 'H'; + REQUIRE(*list.begin() == "Hello"); +} From b45da578bcecc1a7d11b138990c141c4bf889129 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Thu, 15 May 2025 21:10:45 -0500 Subject: [PATCH 13/46] Fix test tags --- tests/test_doubly_linked_list.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp index 2e1eb47..63bd595 100644 --- a/tests/test_doubly_linked_list.cpp +++ b/tests/test_doubly_linked_list.cpp @@ -244,19 +244,19 @@ TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[begin, REQUIRE(list.begin() != list.end()); } -TEST_CASE("push_back 1, begin iterator dereferences to 1", "[begin, end]") { +TEST_CASE("push_back 1, begin iterator dereferences to 1", "[begin]") { DoublyLinkedList list; list.push_back(1); REQUIRE(*list.begin() == 1); } -TEST_CASE("push_back 2, begin iterator dereferences to 2", "[begin, end]") { +TEST_CASE("push_back 2, begin iterator dereferences to 2", "[begin]") { DoublyLinkedList list; list.push_back(2); REQUIRE(*list.begin() == 2); } -TEST_CASE("push_back hello string, modify string by dereferencing begin, front returns modified string", "[begin, end]") { +TEST_CASE("push_back hello string, modify string by dereferencing begin, front returns modified string", "[begin]") { DoublyLinkedList list; list.push_back("hello"); auto &str = *list.begin(); From a149ce61e8d0248dc21217aa97ea434e3cca8925 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Thu, 15 May 2025 21:33:31 -0500 Subject: [PATCH 14/46] Refactor: split tests and group relevant tests together --- CMakeLists.txt | 10 +- tests/CMakeLists.txt | 3 - tests/doubly_linked_list/front_and_back.cpp | 16 ++ tests/doubly_linked_list/iterator_tests.cpp | 45 +++ tests/doubly_linked_list/pop_back_tests.cpp | 65 +++++ tests/doubly_linked_list/pop_front_tests.cpp | 54 ++++ tests/doubly_linked_list/push_back_tests.cpp | 66 +++++ tests/doubly_linked_list/push_front_tests.cpp | 47 ++++ tests/test_doubly_linked_list.cpp | 265 ------------------ 9 files changed, 302 insertions(+), 269 deletions(-) create mode 100644 tests/doubly_linked_list/front_and_back.cpp create mode 100644 tests/doubly_linked_list/iterator_tests.cpp create mode 100644 tests/doubly_linked_list/pop_back_tests.cpp create mode 100644 tests/doubly_linked_list/pop_front_tests.cpp create mode 100644 tests/doubly_linked_list/push_back_tests.cpp create mode 100644 tests/doubly_linked_list/push_front_tests.cpp delete mode 100644 tests/test_doubly_linked_list.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 93f524e..5d098c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,15 @@ FetchContent_Declare( FetchContent_MakeAvailable(catch2) # Add test executable -add_executable(tests tests/test_doubly_linked_list.cpp) +add_executable( + tests + tests/doubly_linked_list/front_and_back.cpp + tests/doubly_linked_list/iterator_tests.cpp + tests/doubly_linked_list/pop_back_tests.cpp + tests/doubly_linked_list/pop_front_tests.cpp + tests/doubly_linked_list/push_back_tests.cpp + tests/doubly_linked_list/push_front_tests.cpp +) target_link_libraries(tests PRIVATE myproject Catch2::Catch2WithMain) # Register tests diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f027f02..e69de29 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +0,0 @@ -add_executable(test_math test_math.cpp) -target_link_libraries(test_math PRIVATE Catch2::Catch2WithMain) -add_test(NAME MathTests COMMAND test_math) \ No newline at end of file diff --git a/tests/doubly_linked_list/front_and_back.cpp b/tests/doubly_linked_list/front_and_back.cpp new file mode 100644 index 0000000..06b56b5 --- /dev/null +++ b/tests/doubly_linked_list/front_and_back.cpp @@ -0,0 +1,16 @@ +#include +#include "doubly_linked_list.hpp" + +TEST_CASE("Canary test", "[canary]") { + REQUIRE(true); +} + +TEST_CASE("front on an empty list throws an exception", "[front]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.front(), std::out_of_range); +} + +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 new file mode 100644 index 0000000..16fa50b --- /dev/null +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -0,0 +1,45 @@ +#include +#include "doubly_linked_list.hpp" + + +TEST_CASE("begin iterator equals end iterator in empty list", "[begin, end]") { + DoublyLinkedList list; + REQUIRE(list.begin() == list.end()); +} + +TEST_CASE("push_back 1, begin iterator does not equal end iterator", "[begin, end]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(!(list.begin() == list.end())); +} + +TEST_CASE("begin iterator != end iterator returns false in an empty list", "[begin, end]") { + DoublyLinkedList list; + REQUIRE(!(list.begin() != list.end())); +} + +TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[begin, end]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.begin() != list.end()); +} + +TEST_CASE("push_back 1, begin iterator dereferences to 1", "[begin]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(*list.begin() == 1); +} + +TEST_CASE("push_back 2, begin iterator dereferences to 2", "[begin]") { + DoublyLinkedList list; + list.push_back(2); + REQUIRE(*list.begin() == 2); +} + +TEST_CASE("push_back hello string, modify string by dereferencing begin, front returns modified string", "[begin]") { + DoublyLinkedList list; + list.push_back("hello"); + auto &str = *list.begin(); + str[0] = 'H'; + REQUIRE(*list.begin() == "Hello"); +} diff --git a/tests/doubly_linked_list/pop_back_tests.cpp b/tests/doubly_linked_list/pop_back_tests.cpp new file mode 100644 index 0000000..8e63f09 --- /dev/null +++ b/tests/doubly_linked_list/pop_back_tests.cpp @@ -0,0 +1,65 @@ +#include +#include "doubly_linked_list.hpp" + + +TEST_CASE("push_back 1, pop_back once, size = 0", "[pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.pop_back(); + REQUIRE(list.size() == 0); +} + +TEST_CASE("push_back 1, pop_back returns 1", "[pop_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.pop_back() == 1); +} + +TEST_CASE("push_back 1, 2, pop_back returns 2", "[pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.pop_back() == 2); +} + +TEST_CASE("pop_back on an empty list throws an exception", "[pop_back]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.pop_back(), std::out_of_range); +} + + +TEST_CASE("push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.pop_back(); + REQUIRE(list.size() == 1); +} + +TEST_CASE("push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_back(); + REQUIRE(list.back() == 2); +} + +TEST_CASE("push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_back(); + list.pop_back(); + REQUIRE(list.back() == 1); +} + +TEST_CASE("push_back 1, 2, 3, pop_back once, front return 1", "[push_back, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_back(); + REQUIRE(list.front() == 1); +} diff --git a/tests/doubly_linked_list/pop_front_tests.cpp b/tests/doubly_linked_list/pop_front_tests.cpp new file mode 100644 index 0000000..d64610e --- /dev/null +++ b/tests/doubly_linked_list/pop_front_tests.cpp @@ -0,0 +1,54 @@ +#include +#include "doubly_linked_list.hpp" + + +TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[push_back, pop_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.pop_front(); + REQUIRE(list.size() == 1); +} + +TEST_CASE("push_front 1, pop_front returns 1", "[push_front, pop_front]") { + DoublyLinkedList list; + list.push_front(1); + REQUIRE(list.pop_front() == 1); +} + +TEST_CASE("push_front 1, 2, pop_front returns 2", "[push_front, pop_front]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + REQUIRE(list.pop_front() == 2); +} + +TEST_CASE("push_back 1, 2, 3, pop_front once, front is 2", "[push_back, pop_front]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.pop_front(); + REQUIRE(list.front() == 2); +} + + +TEST_CASE("pop_front on an empty list throws an exception", "[pop_front]") { + DoublyLinkedList list; + REQUIRE_THROWS_AS(list.pop_front(), std::out_of_range); +} + +TEST_CASE("pop_front clears tail when list becomes empty") { + DoublyLinkedList list; + list.push_back(42); + list.pop_front(); + REQUIRE_THROWS_AS(list.back(), std::out_of_range); +} + +TEST_CASE("push_back 1, pop_back, push_front 2, pop front returns 2", "[push_back, pop_back, push_front, pop_front]") { + DoublyLinkedList list; + list.push_back(1); + list.pop_back(); + list.push_front(2); + REQUIRE(list.pop_front() == 2); +} \ No newline at end of file diff --git a/tests/doubly_linked_list/push_back_tests.cpp b/tests/doubly_linked_list/push_back_tests.cpp new file mode 100644 index 0000000..9769684 --- /dev/null +++ b/tests/doubly_linked_list/push_back_tests.cpp @@ -0,0 +1,66 @@ +#include +#include "doubly_linked_list.hpp" + + +TEST_CASE("push_back one value in an empty list makes size = 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("push_back two values in an empty list makes size = 2", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.size() == 2); +} + +TEST_CASE("push_back 1 and front returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.front() == 1); +} + +TEST_CASE("push_back 1, 2 and front returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.front() == 1); +} + + +TEST_CASE("push_back 2, 1 and front returns 2", "[push_back]") { + DoublyLinkedList list; + list.push_back(2); + list.push_back(1); + REQUIRE(list.front() == 2); +} + + +TEST_CASE("push_back 1, and back returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.back() == 1); +} + +TEST_CASE("push_back 1, 2 and back returns 2", "[push_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.back() == 2); +} + + +TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { + DoublyLinkedList list; + list.push_back(2); + list.push_back(1); + REQUIRE(list.back() == 1); +} + + + + + + + diff --git a/tests/doubly_linked_list/push_front_tests.cpp b/tests/doubly_linked_list/push_front_tests.cpp new file mode 100644 index 0000000..e54e20d --- /dev/null +++ b/tests/doubly_linked_list/push_front_tests.cpp @@ -0,0 +1,47 @@ +#include +#include "doubly_linked_list.hpp" + + + +TEST_CASE("push_front 1, size = 1", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("push_front 1, 2, size = 2", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + REQUIRE(list.size() == 2); +} + +TEST_CASE("push_front 1, front returns 1", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + REQUIRE(list.front() == 1); +} + +TEST_CASE("push_front 1, 2 and front returns 2", "[push_front]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + REQUIRE(list.front() == 2); +} + +TEST_CASE("push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + list.push_back(3); + REQUIRE(list.back() == 3); +} + +TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_back, push_front, pop_back]") { + DoublyLinkedList list; + list.push_back(1); + list.push_front(2); + list.pop_back(); + list.pop_back(); + REQUIRE(list.size() == 0); +} diff --git a/tests/test_doubly_linked_list.cpp b/tests/test_doubly_linked_list.cpp deleted file mode 100644 index 63bd595..0000000 --- a/tests/test_doubly_linked_list.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include -#include "doubly_linked_list.hpp" - -TEST_CASE("Canary test", "[canary]") { - REQUIRE(true); -} - -TEST_CASE("push_back one value in an empty list makes size = 1", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(list.size() == 1); -} - -TEST_CASE("push_back two values in an empty list makes size = 2", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - REQUIRE(list.size() == 2); -} - -TEST_CASE("push_back 1 and front returns 1", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(list.front() == 1); -} - -TEST_CASE("push_back 1, 2 and front returns 1", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - REQUIRE(list.front() == 1); -} - -TEST_CASE("push_back 2, 1 and front returns 2", "[push_back]") { - DoublyLinkedList list; - list.push_back(2); - list.push_back(1); - REQUIRE(list.front() == 2); -} - -TEST_CASE("push_back 1, and back returns 1", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(list.back() == 1); -} - -TEST_CASE("push_back 1, 2 and back returns 2", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - REQUIRE(list.back() == 2); -} - -TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { - DoublyLinkedList list; - list.push_back(2); - list.push_back(1); - REQUIRE(list.back() == 1); -} - -TEST_CASE("push_back 1, pop_back once, size = 0", "[pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.pop_back(); - REQUIRE(list.size() == 0); -} - -TEST_CASE("push_back 1, pop_back returns 1", "[pop_back]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(list.pop_back() == 1); -} - -TEST_CASE("push_back 1, 2, pop_back returns 2", "[pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - REQUIRE(list.pop_back() == 2); -} - -TEST_CASE("pop_back on an empty list throws an exception", "[pop_back]") { - DoublyLinkedList list; - REQUIRE_THROWS_AS(list.pop_back(), std::out_of_range); -} - -TEST_CASE("front on an empty list throws an exception", "[front]") { - DoublyLinkedList list; - REQUIRE_THROWS_AS(list.front(), std::out_of_range); -} - -TEST_CASE("back on an empty list throws an exception", "[back]") { - DoublyLinkedList list; - REQUIRE_THROWS_AS(list.back(), std::out_of_range); -} - -TEST_CASE("push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.pop_back(); - REQUIRE(list.size() == 1); -} - -TEST_CASE("push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.push_back(3); - list.pop_back(); - REQUIRE(list.back() == 2); -} - -TEST_CASE("push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.push_back(3); - list.pop_back(); - list.pop_back(); - REQUIRE(list.back() == 1); -} - -TEST_CASE("push_back 1, 2, 3, pop_back once, front return 1", "[push_back, pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.push_back(3); - list.pop_back(); - REQUIRE(list.front() == 1); -} - -TEST_CASE("push_front 1, size = 1", "[push_front]") { - DoublyLinkedList list; - list.push_front(1); - REQUIRE(list.size() == 1); -} - -TEST_CASE("push_front 1, 2, size = 2", "[push_front]") { - DoublyLinkedList list; - list.push_front(1); - list.push_front(2); - REQUIRE(list.size() == 2); -} - -TEST_CASE("push_front 1, front returns 1", "[push_front]") { - DoublyLinkedList list; - list.push_front(1); - REQUIRE(list.front() == 1); -} - -TEST_CASE("push_front 1, 2 and front returns 2", "[push_front]") { - DoublyLinkedList list; - list.push_front(1); - list.push_front(2); - REQUIRE(list.front() == 2); -} - -TEST_CASE("push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") { - DoublyLinkedList list; - list.push_front(1); - list.push_front(2); - list.push_back(3); - REQUIRE(list.back() == 3); -} - -TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_back, push_front, pop_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_front(2); - list.pop_back(); - list.pop_back(); - REQUIRE(list.size() == 0); -} - -TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[push_back, pop_front]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.pop_front(); - REQUIRE(list.size() == 1); -} - -TEST_CASE("push_front 1, pop_front returns 1", "[push_front, pop_front]") { - DoublyLinkedList list; - list.push_front(1); - REQUIRE(list.pop_front() == 1); -} - -TEST_CASE("push_front 1, 2, pop_front returns 2", "[push_front, pop_front]") { - DoublyLinkedList list; - list.push_front(1); - list.push_front(2); - REQUIRE(list.pop_front() == 2); -} - -TEST_CASE("push_back 1, 2, 3, pop_front once, front is 2", "[push_back, pop_front]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - list.push_back(3); - list.pop_front(); - REQUIRE(list.front() == 2); -} - -TEST_CASE("pop_front on an empty list throws an exception", "[pop_front]") { - DoublyLinkedList list; - REQUIRE_THROWS_AS(list.pop_front(), std::out_of_range); -} - -TEST_CASE("pop_front clears tail when list becomes empty") { - DoublyLinkedList list; - list.push_back(42); - list.pop_front(); - REQUIRE_THROWS_AS(list.back(), std::out_of_range); -} - -TEST_CASE("push_back 1, pop_back, push_front 2, pop front returns 2", "[push_back, pop_back, push_front, pop_front]") { - DoublyLinkedList list; - list.push_back(1); - list.pop_back(); - list.push_front(2); - REQUIRE(list.pop_front() == 2); -} - -TEST_CASE("begin iterator equals end iterator in empty list", "[begin, end]") { - DoublyLinkedList list; - REQUIRE(list.begin() == list.end()); -} - -TEST_CASE("push_back 1, begin iterator does not equal end iterator", "[begin, end]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(!(list.begin() == list.end())); -} - -TEST_CASE("begin iterator != end iterator returns false in an empty list", "[begin, end]") { - DoublyLinkedList list; - REQUIRE(!(list.begin() != list.end())); -} - -TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[begin, end]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(list.begin() != list.end()); -} - -TEST_CASE("push_back 1, begin iterator dereferences to 1", "[begin]") { - DoublyLinkedList list; - list.push_back(1); - REQUIRE(*list.begin() == 1); -} - -TEST_CASE("push_back 2, begin iterator dereferences to 2", "[begin]") { - DoublyLinkedList list; - list.push_back(2); - REQUIRE(*list.begin() == 2); -} - -TEST_CASE("push_back hello string, modify string by dereferencing begin, front returns modified string", "[begin]") { - DoublyLinkedList list; - list.push_back("hello"); - auto &str = *list.begin(); - str[0] = 'H'; - REQUIRE(*list.begin() == "Hello"); -} From 13a515a0ccce8e3ed901d4678f4d82c351e47ab2 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Thu, 15 May 2025 21:35:40 -0500 Subject: [PATCH 15/46] Refactor: remove unnecessary cmake file and rename the project in the root cmake file --- CMakeLists.txt | 8 ++++---- tests/CMakeLists.txt | 0 2 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d098c6..98a800e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ cmake_minimum_required(VERSION 3.10) -project(MyProject LANGUAGES CXX) +project(ordered_map LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Add source files -add_library(myproject src/doubly_linked_list.cpp) -target_include_directories(myproject PUBLIC include) +add_library(ordered_map src/doubly_linked_list.cpp) +target_include_directories(ordered_map PUBLIC include) # Enable testing enable_testing() @@ -30,7 +30,7 @@ add_executable( tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp ) -target_link_libraries(tests PRIVATE myproject Catch2::Catch2WithMain) +target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) # Register tests include(CTest) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 From 725fd576cd003ee6e105087b4b6ecff77454b92a Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Thu, 15 May 2025 21:41:14 -0500 Subject: [PATCH 16/46] Update readme.md with the current state of the project --- README.md | 74 ++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 1436a7d..d01adae 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A C++ project implementing an ordered map using a doubly-linked list to maintain - [GitHub Actions CI](#github-actions-ci) - [Adding a New Test](#adding-a-new-test) - [Troubleshooting](#troubleshooting) -- [Example Test File](#example-test-file) +- [Project Status](#project-status) ## Prerequisites @@ -43,10 +43,16 @@ ordered_map/ ├── .clang-format # Code formatting rules ├── include/ │ ├── doubly_linked_list.hpp # Doubly-linked list implementation -│ └── ordered_map.hpp # (Future) Ordered map implementation +│ └── ordered_map.hpp # (Future) Ordered map interface ├── src/ ├── tests/ -│ └── test_doubly_linked_list.cpp # Catch2 test files +│ └── doubly_linked_list/ # Test files for doubly linked list +│ ├── front_and_back.cpp # Tests for front/back operations +│ ├── iterator_tests.cpp # Tests for iterator functionality +│ ├── pop_back_tests.cpp # Tests for pop_back operations +│ ├── pop_front_tests.cpp # Tests for pop_front operations +│ ├── push_back_tests.cpp # Tests for push_back operations +│ └── push_front_tests.cpp # Tests for push_front operations ``` ## Setting Up VS Code (or Cursor) @@ -107,7 +113,7 @@ ordered_map/ 3. **Run Specific Tests**: ```bash cd build - ./tests --tags [canary] + ./tests --tags [tag] ``` ## GitHub Actions CI @@ -122,21 +128,26 @@ ordered_map/ ## Adding a New Test -1. **Edit `tests/test_doubly_linked_list.cpp`**: - - - Add a `TEST_CASE` for the doubly-linked list. Example: - ```cpp - TEST_CASE("Push back maintains order", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - REQUIRE(list.size() == 2); - REQUIRE(list.front() == 1); - REQUIRE(list.back() == 2); - } - ``` +1. **Choose the appropriate test file** in `tests/doubly_linked_list/` based on the functionality being tested: + + - `front_and_back.cpp` for front/back operations + - `iterator_tests.cpp` for iterator functionality + - `pop_back_tests.cpp` for pop_back operations + - `pop_front_tests.cpp` for pop_front operations + - `push_back_tests.cpp` for push_back operations + - `push_front_tests.cpp` for push_front operations + +2. **Add your test case**: + + ```cpp + TEST_CASE("Your test description", "[tag]") { + DoublyLinkedList list; + // Your test code here + REQUIRE(/* your assertion */); + } + ``` -2. **Rebuild and Test**: +3. **Rebuild and Test**: ```bash ./build.sh ``` @@ -150,34 +161,12 @@ ordered_map/ - Clear build: `rm -rf build && ./build.sh`. - Check prerequisites. - **Test Failures**: - - Inspect assertions in `test_doubly_linked_list.cpp`. + - Inspect assertions in the relevant test file. - Run specific tests: `./build/tests --tags [tag]`. - **GitHub Actions Failures**: - Check logs in the "Actions" tab on GitHub. - Ensure `build.sh` and dependencies are compatible with `ubuntu-latest`. -## Example Test File - -`tests/test_doubly_linked_list.cpp`: - -```cpp -#include -#include "doubly_linked_list.hpp" - -TEST_CASE("Canary test", "[canary]") { - REQUIRE(true); -} - -TEST_CASE("Push back maintains order", "[push_back]") { - DoublyLinkedList list; - list.push_back(1); - list.push_back(2); - REQUIRE(list.size() == 2); - REQUIRE(list.front() == 1); - REQUIRE(list.back() == 2); -} -``` - ## Project Status The project is currently in development, with the following components: @@ -186,7 +175,8 @@ The project is currently in development, with the following components: - Basic operations (push_back, pop_back, push_front, pop_front) - Memory management using smart pointers - - Comprehensive test coverage + - Comprehensive test coverage organized by functionality + - Iterator support (basic implementation) 2. 🔄 Ordered Map Implementation (Coming Soon) - Will use the doubly linked list as its underlying data structure From e4437cc31870e0e2e42df2a270a779233c789ed7 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sun, 18 May 2025 19:15:30 -0500 Subject: [PATCH 17/46] Rename build.sh to run_build.sh --- build.sh => run_build.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename build.sh => run_build.sh (100%) diff --git a/build.sh b/run_build.sh similarity index 100% rename from build.sh rename to run_build.sh From 54a863f9c7f04c333155ab01d4149831b7a88850 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sun, 18 May 2025 19:59:58 -0500 Subject: [PATCH 18/46] Add tests for post and pre increment (++) on list iterators and make them pass --- include/doubly_linked_list.hpp | 14 +++ tests/doubly_linked_list/iterator_tests.cpp | 107 +++++++++++++++++++ tests/doubly_linked_list/push_back_tests.cpp | 7 -- 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 9516c20..4ca4777 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -121,6 +121,20 @@ class DoublyLinkedList { T& operator*() const { return current_node_ptr->value; } + + Iterator& operator++() { + if (current_node_ptr) { + current_node_ptr = current_node_ptr->next.get(); + } + return *this; + } + + Iterator operator++(int) { + Iterator initial_state_copy = *this; + ++(*this); + return initial_state_copy; + } + }; Iterator begin() { diff --git a/tests/doubly_linked_list/iterator_tests.cpp b/tests/doubly_linked_list/iterator_tests.cpp index 16fa50b..941816e 100644 --- a/tests/doubly_linked_list/iterator_tests.cpp +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -21,18 +21,21 @@ TEST_CASE("begin iterator != end iterator returns false in an empty list", "[beg TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[begin, end]") { DoublyLinkedList list; list.push_back(1); + REQUIRE(list.begin() != list.end()); } TEST_CASE("push_back 1, begin iterator dereferences to 1", "[begin]") { DoublyLinkedList list; list.push_back(1); + REQUIRE(*list.begin() == 1); } TEST_CASE("push_back 2, begin iterator dereferences to 2", "[begin]") { DoublyLinkedList list; list.push_back(2); + REQUIRE(*list.begin() == 2); } @@ -41,5 +44,109 @@ TEST_CASE("push_back hello string, modify string by dereferencing begin, front r list.push_back("hello"); auto &str = *list.begin(); str[0] = 'H'; + REQUIRE(*list.begin() == "Hello"); } + +TEST_CASE("push_back 1, 2, using pre-increment (++it) on begin iterator to reach 2", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.begin(); + ++it; + + REQUIRE(*it == 2); +} + +TEST_CASE("push_back 1, 2, 3, using pre-increment (++it) twice on begin iterator to reach 2", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); + ++it; + ++it; + + REQUIRE(*it == 3); +} + +TEST_CASE("push_back 1, 2, copy the iterator after using pre-increment (++it) on begin iterator to reach 2", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.begin(); + auto copied_it = ++it; + + REQUIRE(*copied_it == 2); +} + + +TEST_CASE("push_back 1, 2, using post-increment (it++) on begin iterator to reach 2", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.begin(); + it++; + + REQUIRE(*it == 2); +} + +TEST_CASE("push_back 1, 2, 3, using post-increment (it++) on begin iterator to reach 2", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + auto it = list.begin(); + it++; + + REQUIRE(*it == 2); +} + +TEST_CASE("push_back 1, 2, copy the iterator after using post-increment (it++) on begin iterator", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + auto it = list.begin(); + auto copied_it = it++; + + REQUIRE(*copied_it == 1); +} + +TEST_CASE("push back 1, 2, 3, iterate using for-loop with post-increment (it++) on begin iterator", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + int counter = 1; + + for (auto it = list.begin(); it != list.end(); it++) { + REQUIRE(*it == counter); + counter++; + } +} + +TEST_CASE("push back 1, 2, 3, iterate using for-loop with pre-increment (++it) on begin iterator", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + int counter = 1; + + for (auto it = list.begin(); it != list.end(); ++it) { + REQUIRE(*it == counter); + counter++; + } +} + +TEST_CASE("push back 1, 2, 3, iterate using for-loop with for-each syntax", "[iterator]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + int counter = 1; + + for (auto it : list) { + REQUIRE(it == counter); + counter++; + } +} \ No newline at end of file diff --git a/tests/doubly_linked_list/push_back_tests.cpp b/tests/doubly_linked_list/push_back_tests.cpp index 9769684..5e7d3e7 100644 --- a/tests/doubly_linked_list/push_back_tests.cpp +++ b/tests/doubly_linked_list/push_back_tests.cpp @@ -57,10 +57,3 @@ TEST_CASE("push_back 2, 1 and back returns 1", "[push_back]") { list.push_back(1); REQUIRE(list.back() == 1); } - - - - - - - From 55a7f844863ae6e6165371caaadfff9e014360e0 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Sun, 18 May 2025 20:12:04 -0500 Subject: [PATCH 19/46] Create tests for clear method, make them pass --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 6 ++++++ tests/doubly_linked_list/clear_tests.cpp | 27 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/doubly_linked_list/clear_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 98a800e..570301f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ 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/clear_tests.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 4ca4777..1f7678e 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -102,6 +102,12 @@ class DoublyLinkedList { return popped_value; } + void clear() { + _size = 0; + head.reset(); + tail = nullptr; + } + class Iterator { Node* current_node_ptr; diff --git a/tests/doubly_linked_list/clear_tests.cpp b/tests/doubly_linked_list/clear_tests.cpp new file mode 100644 index 0000000..f7472a1 --- /dev/null +++ b/tests/doubly_linked_list/clear_tests.cpp @@ -0,0 +1,27 @@ +#include +#include "doubly_linked_list.hpp" + + +TEST_CASE("calling clear method on a non-empty list makes size = 0", "[clear]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.clear(); + REQUIRE(list.size() == 0); +} + +TEST_CASE("calling clear on a non-empty list then accessing the front throws an error", "[clear]") { + DoublyLinkedList list; + list.push_back(1); + list.clear(); + REQUIRE_THROWS_AS(list.front(), std::out_of_range); +} + +TEST_CASE("calling clear on a non-empty list then accessing the back throws an error", "[clear]") { + DoublyLinkedList list; + list.push_back(1); + list.clear(); + REQUIRE_THROWS_AS(list.back(), std::out_of_range); +} + From d285c1f7bf58d4c5abec693f50a4c054c16d053c Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 21:16:27 -0500 Subject: [PATCH 20/46] Add a test to make sure clear method invokes destructor --- tests/doubly_linked_list/clear_tests.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/doubly_linked_list/clear_tests.cpp b/tests/doubly_linked_list/clear_tests.cpp index f7472a1..cf64dbc 100644 --- a/tests/doubly_linked_list/clear_tests.cpp +++ b/tests/doubly_linked_list/clear_tests.cpp @@ -25,3 +25,19 @@ TEST_CASE("calling clear on a non-empty list then accessing the back throws an e REQUIRE_THROWS_AS(list.back(), std::out_of_range); } + +class TestClass { +public: + bool *ref; + + TestClass(bool *a_ref) : ref(a_ref) {} + ~TestClass() { (*ref) = true; } +}; + +TEST_CASE("calling clear on a list of custom class objects makes the destructor called", "[clear]") { + DoublyLinkedList list; + bool deletion_flag = false; + list.push_back(TestClass(&deletion_flag)); + list.clear(); + REQUIRE(deletion_flag == true); +} From c15e3dff4592f7dd6c0de32d1a891f525106431b Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 21:17:13 -0500 Subject: [PATCH 21/46] Encapsulate the TestClass inside the test function --- tests/doubly_linked_list/clear_tests.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/doubly_linked_list/clear_tests.cpp b/tests/doubly_linked_list/clear_tests.cpp index cf64dbc..e0f0a0b 100644 --- a/tests/doubly_linked_list/clear_tests.cpp +++ b/tests/doubly_linked_list/clear_tests.cpp @@ -35,6 +35,14 @@ class TestClass { }; TEST_CASE("calling clear on a list of custom class objects makes the destructor called", "[clear]") { + class TestClass { + public: + bool *ref; + + TestClass(bool *a_ref) : ref(a_ref) {} + ~TestClass() { (*ref) = true; } + }; + DoublyLinkedList list; bool deletion_flag = false; list.push_back(TestClass(&deletion_flag)); From d1de3aae2394f7f88712860f6855298e9e02b1b9 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 21:23:21 -0500 Subject: [PATCH 22/46] Add a checklist to README for future improvements --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d01adae..ffc1234 100644 --- a/README.md +++ b/README.md @@ -181,3 +181,8 @@ The project is currently in development, with the following components: 2. 🔄 Ordered Map Implementation (Coming Soon) - Will use the doubly linked list as its underlying data structure - Will maintain insertion order while providing map-like functionality + +### Checklist for future improvements + +- [ ] Test front(), back(), insertion and deletion functions for copying behavior +- [ ] Test front(), back(), insertion and deletion functions for destructor behavior From 566f3792ee47e98e147163946f8d0c3994758270 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 22:07:56 -0500 Subject: [PATCH 23/46] Add a point to the readme checklist --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffc1234..80db3d8 100644 --- a/README.md +++ b/README.md @@ -184,5 +184,6 @@ The project is currently in development, with the following components: ### Checklist for future improvements -- [ ] Test front(), back(), insertion and deletion functions for copying behavior -- [ ] Test front(), back(), insertion and deletion functions for destructor behavior +- [ ] DoublyLinkedList: Add pre and post decrement operators +- [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for copying behavior +- [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for destructor behavior From 8f95eb34408b9b05374970961e39c8ea07e26b20 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 22:43:13 -0500 Subject: [PATCH 24/46] Create tests for ordered map, make them pass, modify doubly linked list - list: create default constructor for iterator to allow usage as value in map - create back_iterator to allow referencing tail directly - create insertion tests for ordered map --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 14 ++++++++ include/ordered_map.hpp | 24 +++++++++++++ tests/doubly_linked_list/iterator_tests.cpp | 19 ++++++++++- tests/ordered_map/insertion_test.cpp | 37 +++++++++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 include/ordered_map.hpp create mode 100644 tests/ordered_map/insertion_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 570301f..ebe7523 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable( tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp tests/doubly_linked_list/clear_tests.cpp + tests/ordered_map/insertion_test.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 1f7678e..2c4e996 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -116,6 +116,8 @@ class DoublyLinkedList { friend class DoublyLinkedList; public: + Iterator() : current_node_ptr(nullptr) {} + bool operator==(const Iterator& other) const { return current_node_ptr == other.current_node_ptr; } @@ -141,6 +143,14 @@ class DoublyLinkedList { return initial_state_copy; } + Iterator& operator--() { + if (current_node_ptr) { + current_node_ptr = current_node_ptr->prev; + } + return *this; + } + + }; Iterator begin() { @@ -150,4 +160,8 @@ class DoublyLinkedList { Iterator end() { return Iterator(nullptr); } + + Iterator back_iterator() { + return Iterator(tail); + } }; diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp new file mode 100644 index 0000000..d15524c --- /dev/null +++ b/include/ordered_map.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "doubly_linked_list.hpp" +#include + +template +class ordered_map { +private: + DoublyLinkedList _list; + std::unordered_map::Iterator> _map; + int _size = 0; +public: + ordered_map() {} + void insert(const K& key, const V& value) { + if (_map.find(key) == _map.end()) { + _map[key] = _list.back_iterator(); + _size++; + } + } + + unsigned int size() const { + return _size; + } + +}; \ No newline at end of file diff --git a/tests/doubly_linked_list/iterator_tests.cpp b/tests/doubly_linked_list/iterator_tests.cpp index 941816e..f33112d 100644 --- a/tests/doubly_linked_list/iterator_tests.cpp +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -149,4 +149,21 @@ TEST_CASE("push back 1, 2, 3, iterate using for-loop with for-each syntax", "[it REQUIRE(it == counter); counter++; } -} \ No newline at end of file +} + +TEST_CASE("push_back 1, dereferencing back iterator returns 1", "[back_iterator]") { + DoublyLinkedList list; + list.push_back(1); + auto it = list.back_iterator(); + + REQUIRE(*it == 1); +} + +TEST_CASE("push_back 1, 2, dereferencing back iterator returns 2", "[back_iterator]") { + DoublyLinkedList list; + list.push_back(1); list.push_back(2); + auto it = list.back_iterator(); + + REQUIRE(*it == 2); +} + diff --git a/tests/ordered_map/insertion_test.cpp b/tests/ordered_map/insertion_test.cpp new file mode 100644 index 0000000..d041fdb --- /dev/null +++ b/tests/ordered_map/insertion_test.cpp @@ -0,0 +1,37 @@ +#include +#include "ordered_map.hpp" + +TEST_CASE("empty ordered map has size 0", "[insert]") { + ordered_map o_map; + REQUIRE(o_map.size() == 0); +} + +TEST_CASE("inserting <1, 2> into an empty ordered map", "[insert]") { + ordered_map o_map; + o_map.insert(1, 2); + REQUIRE(o_map.size() == 1); +} + +TEST_CASE("insert <1, 2> and <1, 3>, size = 1", "[insert]") { + ordered_map o_map; + o_map.insert(1, 2); + o_map.insert(1, 3); + REQUIRE(o_map.size() == 1); +} + +TEST_CASE("insert <1, 2> and <2, 3>, size = 2", "[insert]") { + ordered_map o_map; + o_map.insert(1, 2); + o_map.insert(2, 3); + REQUIRE(o_map.size() == 2); +} + + + + + + + + + + From cb287ee73eaa36f3093503b70dac6f5434f3de2b Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 22:44:15 -0500 Subject: [PATCH 25/46] Create test for .at() and make it pass --- include/ordered_map.hpp | 4 ++++ tests/ordered_map/insertion_test.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index d15524c..9aad416 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -21,4 +21,8 @@ class ordered_map { return _size; } + int at(const K& key) { + return 2; + } + }; \ No newline at end of file diff --git a/tests/ordered_map/insertion_test.cpp b/tests/ordered_map/insertion_test.cpp index d041fdb..184ffc1 100644 --- a/tests/ordered_map/insertion_test.cpp +++ b/tests/ordered_map/insertion_test.cpp @@ -26,6 +26,11 @@ TEST_CASE("insert <1, 2> and <2, 3>, size = 2", "[insert]") { REQUIRE(o_map.size() == 2); } +TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[insert]") { + ordered_map o_map; + o_map.insert(1, 2); + REQUIRE(o_map.at(1) == 2); +} From c648aab96595ca2aa18bc8fbf7d236c5c2f61264 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 22:46:31 -0500 Subject: [PATCH 26/46] Create another test for .at() with strings and make it pass --- include/ordered_map.hpp | 5 +++-- tests/ordered_map/insertion_test.cpp | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index 9aad416..0828138 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -12,6 +12,7 @@ class ordered_map { ordered_map() {} void insert(const K& key, const V& value) { if (_map.find(key) == _map.end()) { + _list.push_back(value); _map[key] = _list.back_iterator(); _size++; } @@ -21,8 +22,8 @@ class ordered_map { return _size; } - int at(const K& key) { - return 2; + V& at(const K& key) { + return *_map[key]; } }; \ No newline at end of file diff --git a/tests/ordered_map/insertion_test.cpp b/tests/ordered_map/insertion_test.cpp index 184ffc1..9ac592f 100644 --- a/tests/ordered_map/insertion_test.cpp +++ b/tests/ordered_map/insertion_test.cpp @@ -26,12 +26,18 @@ TEST_CASE("insert <1, 2> and <2, 3>, size = 2", "[insert]") { REQUIRE(o_map.size() == 2); } -TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[insert]") { +TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[insert, at]") { ordered_map o_map; o_map.insert(1, 2); REQUIRE(o_map.at(1) == 2); } +TEST_CASE("insert , key hello returns value of world", "[insert, at]") { + ordered_map o_map; + o_map.insert("hello", "world"); + REQUIRE(o_map.at("hello") == "world"); +} + From 8536bf0ac93031be2d21789859d64d1e4a186ed3 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 22:49:46 -0500 Subject: [PATCH 27/46] Create test for updating an existing value and make it pass --- include/ordered_map.hpp | 3 +++ tests/ordered_map/insertion_test.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index 0828138..faa6389 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -16,6 +16,9 @@ class ordered_map { _map[key] = _list.back_iterator(); _size++; } + else { + *_map.find(key)->second = value; + } } unsigned int size() const { diff --git a/tests/ordered_map/insertion_test.cpp b/tests/ordered_map/insertion_test.cpp index 9ac592f..4ba4141 100644 --- a/tests/ordered_map/insertion_test.cpp +++ b/tests/ordered_map/insertion_test.cpp @@ -38,6 +38,14 @@ TEST_CASE("insert , key hello returns value of world", "[insert, a REQUIRE(o_map.at("hello") == "world"); } +TEST_CASE("insert , , key hello returns value of there", "[insert, at]") { + ordered_map o_map; + o_map.insert("hello", "world"); + o_map.insert("hello", "there"); + REQUIRE(o_map.at("hello") == "there"); +} + + From 5ac854094045a05425a8df8cca86eff9fd55b1d6 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Mon, 19 May 2025 22:50:47 -0500 Subject: [PATCH 28/46] Remove unnecessary _size variable --- include/ordered_map.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index faa6389..b68d44a 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -7,14 +7,12 @@ class ordered_map { private: DoublyLinkedList _list; std::unordered_map::Iterator> _map; - int _size = 0; public: ordered_map() {} void insert(const K& key, const V& value) { if (_map.find(key) == _map.end()) { _list.push_back(value); _map[key] = _list.back_iterator(); - _size++; } else { *_map.find(key)->second = value; @@ -22,7 +20,7 @@ class ordered_map { } unsigned int size() const { - return _size; + return _list.size(); } V& at(const K& key) { From 5fbc8c6f557e850c7f62ff513a26d8293095446d Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 14:35:18 -0500 Subject: [PATCH 29/46] Add tests lists for easier tracking --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index 80db3d8..c811112 100644 --- a/README.md +++ b/README.md @@ -187,3 +187,58 @@ The project is currently in development, with the following components: - [ ] DoublyLinkedList: Add pre and post decrement operators - [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for copying behavior - [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for destructor behavior + +## Tests + +### Doubly Linked List Tests + +#### Front and Back Operations +- [x] Empty list has size 0 +- [x] Empty list front() throws exception +- [x] Empty list back() throws exception +- [x] List with one element returns correct front +- [x] List with one element returns correct back +- [x] List with multiple elements returns correct front +- [x] List with multiple elements returns correct back + +#### Iterator Operations +- [x] Empty list begin equals end +- [x] Single element list begin points to element +- [x] Single element list begin++ equals end +- [x] Multiple element list begin points to first +- [x] Multiple element list begin++ points to second +- [x] Multiple element list can iterate through all elements + +#### Push Back Operations +- [x] Push back one element increases size to 1 +- [x] Push back multiple elements increases size correctly +- [x] Push back maintains correct order of elements + +#### Push Front Operations +- [x] Push front one element increases size to 1 +- [x] Push front multiple elements increases size correctly +- [x] Push front maintains correct order of elements + +#### Pop Back Operations +- [x] Pop back on empty list throws exception +- [x] Pop back decreases size by 1 +- [x] Pop back removes last element +- [x] Pop back maintains remaining elements + +#### Pop Front Operations +- [x] Pop front on empty list throws exception +- [x] Pop front decreases size by 1 +- [x] Pop front removes first element +- [x] Pop front maintains remaining elements + +### Ordered Map Tests + +#### Insertion Operations +- [x] Empty ordered map has size 0 +- [x] Inserting <1,2> into empty map sets size to 1 +- [x] Inserting duplicate key maintains size of 1 +- [x] Inserting different keys increases size +- [x] Inserting <1,2> allows retrieving 2 with key 1 +- [x] Inserting string key-value pairs works correctly +- [x] Updating existing key updates the value + From ff0ef1a8a06f1add86315b71c386d287f666711e Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 14:45:19 -0500 Subject: [PATCH 30/46] Refactor: fix tests tags to make them usable and expressive --- tests/doubly_linked_list/iterator_tests.cpp | 8 ++++---- tests/doubly_linked_list/pop_back_tests.cpp | 8 ++++---- tests/doubly_linked_list/pop_front_tests.cpp | 12 ++++++------ tests/doubly_linked_list/push_front_tests.cpp | 4 ++-- ...insertion_test.cpp => insertion_tests.cpp} | 19 ------------------- 5 files changed, 16 insertions(+), 35 deletions(-) rename tests/ordered_map/{insertion_test.cpp => insertion_tests.cpp} (52%) diff --git a/tests/doubly_linked_list/iterator_tests.cpp b/tests/doubly_linked_list/iterator_tests.cpp index f33112d..eda3438 100644 --- a/tests/doubly_linked_list/iterator_tests.cpp +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -2,23 +2,23 @@ #include "doubly_linked_list.hpp" -TEST_CASE("begin iterator equals end iterator in empty list", "[begin, end]") { +TEST_CASE("begin iterator equals end iterator in empty list", "[iterator]") { DoublyLinkedList list; REQUIRE(list.begin() == list.end()); } -TEST_CASE("push_back 1, begin iterator does not equal end iterator", "[begin, end]") { +TEST_CASE("push_back 1, begin iterator does not equal end iterator", "[iterator]") { DoublyLinkedList list; list.push_back(1); REQUIRE(!(list.begin() == list.end())); } -TEST_CASE("begin iterator != end iterator returns false in an empty list", "[begin, end]") { +TEST_CASE("begin iterator != end iterator returns false in an empty list", "[iterator]") { DoublyLinkedList list; REQUIRE(!(list.begin() != list.end())); } -TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[begin, end]") { +TEST_CASE("push_back 1, begin iterator != end iterator returns false", "[iterator]") { DoublyLinkedList list; list.push_back(1); diff --git a/tests/doubly_linked_list/pop_back_tests.cpp b/tests/doubly_linked_list/pop_back_tests.cpp index 8e63f09..f279f59 100644 --- a/tests/doubly_linked_list/pop_back_tests.cpp +++ b/tests/doubly_linked_list/pop_back_tests.cpp @@ -28,7 +28,7 @@ TEST_CASE("pop_back on an empty list throws an exception", "[pop_back]") { } -TEST_CASE("push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, pop_back once, size = 1", "[pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -36,7 +36,7 @@ TEST_CASE("push_back 1, 2, pop_back once, size = 1", "[push_back, pop_back]") { REQUIRE(list.size() == 1); } -TEST_CASE("push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, 3, pop_back once, back return 2", "[pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -45,7 +45,7 @@ TEST_CASE("push_back 1, 2, 3, pop_back once, back return 2", "[push_back, pop_ba REQUIRE(list.back() == 2); } -TEST_CASE("push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, 3, pop_back twice, back return 1", "[pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -55,7 +55,7 @@ TEST_CASE("push_back 1, 2, 3, pop_back twice, back return 1", "[push_back, pop_b REQUIRE(list.back() == 1); } -TEST_CASE("push_back 1, 2, 3, pop_back once, front return 1", "[push_back, pop_back]") { +TEST_CASE("push_back 1, 2, 3, pop_back once, front return 1", "[pop_back]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); diff --git a/tests/doubly_linked_list/pop_front_tests.cpp b/tests/doubly_linked_list/pop_front_tests.cpp index d64610e..52e2cb1 100644 --- a/tests/doubly_linked_list/pop_front_tests.cpp +++ b/tests/doubly_linked_list/pop_front_tests.cpp @@ -2,7 +2,7 @@ #include "doubly_linked_list.hpp" -TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[push_back, pop_front]") { +TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[pop_front]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -10,20 +10,20 @@ TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[push_back, pop_front]") REQUIRE(list.size() == 1); } -TEST_CASE("push_front 1, pop_front returns 1", "[push_front, pop_front]") { +TEST_CASE("push_front 1, pop_front returns 1", "[pop_front]") { DoublyLinkedList list; list.push_front(1); REQUIRE(list.pop_front() == 1); } -TEST_CASE("push_front 1, 2, pop_front returns 2", "[push_front, pop_front]") { +TEST_CASE("push_front 1, 2, pop_front returns 2", "[pop_front]") { DoublyLinkedList list; list.push_front(1); list.push_front(2); REQUIRE(list.pop_front() == 2); } -TEST_CASE("push_back 1, 2, 3, pop_front once, front is 2", "[push_back, pop_front]") { +TEST_CASE("push_back 1, 2, 3, pop_front once, front is 2", "[pop_front]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); @@ -38,14 +38,14 @@ TEST_CASE("pop_front on an empty list throws an exception", "[pop_front]") { REQUIRE_THROWS_AS(list.pop_front(), std::out_of_range); } -TEST_CASE("pop_front clears tail when list becomes empty") { +TEST_CASE("pop_front clears tail when list becomes empty", "[pop_back]") { DoublyLinkedList list; list.push_back(42); list.pop_front(); REQUIRE_THROWS_AS(list.back(), std::out_of_range); } -TEST_CASE("push_back 1, pop_back, push_front 2, pop front returns 2", "[push_back, pop_back, push_front, pop_front]") { +TEST_CASE("push_back 1, pop_back, push_front 2, pop front returns 2", "[pop_front]") { DoublyLinkedList list; list.push_back(1); list.pop_back(); diff --git a/tests/doubly_linked_list/push_front_tests.cpp b/tests/doubly_linked_list/push_front_tests.cpp index e54e20d..9903a1c 100644 --- a/tests/doubly_linked_list/push_front_tests.cpp +++ b/tests/doubly_linked_list/push_front_tests.cpp @@ -29,7 +29,7 @@ TEST_CASE("push_front 1, 2 and front returns 2", "[push_front]") { REQUIRE(list.front() == 2); } -TEST_CASE("push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") { +TEST_CASE("push_front 1, 2, push_back 3, back is 3", "[push_front]") { DoublyLinkedList list; list.push_front(1); list.push_front(2); @@ -37,7 +37,7 @@ TEST_CASE("push_front 1, 2, push_back 3, back is 3", "[push_front, push_back]") REQUIRE(list.back() == 3); } -TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_back, push_front, pop_back]") { +TEST_CASE("push_back 1, push_front 2, pop_back twice, size is 0", "[push_front]") { DoublyLinkedList list; list.push_back(1); list.push_front(2); diff --git a/tests/ordered_map/insertion_test.cpp b/tests/ordered_map/insertion_tests.cpp similarity index 52% rename from tests/ordered_map/insertion_test.cpp rename to tests/ordered_map/insertion_tests.cpp index 4ba4141..d041fdb 100644 --- a/tests/ordered_map/insertion_test.cpp +++ b/tests/ordered_map/insertion_tests.cpp @@ -26,25 +26,6 @@ TEST_CASE("insert <1, 2> and <2, 3>, size = 2", "[insert]") { REQUIRE(o_map.size() == 2); } -TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[insert, at]") { - ordered_map o_map; - o_map.insert(1, 2); - REQUIRE(o_map.at(1) == 2); -} - -TEST_CASE("insert , key hello returns value of world", "[insert, at]") { - ordered_map o_map; - o_map.insert("hello", "world"); - REQUIRE(o_map.at("hello") == "world"); -} - -TEST_CASE("insert , , key hello returns value of there", "[insert, at]") { - ordered_map o_map; - o_map.insert("hello", "world"); - o_map.insert("hello", "there"); - REQUIRE(o_map.at("hello") == "there"); -} - From 80e1476940d636300c866b3e5eedb2ad8c6e0b91 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 14:46:01 -0500 Subject: [PATCH 31/46] Refactor: Move lookup tests to a separate file, update cmakelists --- CMakeLists.txt | 3 ++- tests/ordered_map/lookup_tests.cpp | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/ordered_map/lookup_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ebe7523..16d3c1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,8 @@ add_executable( tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp tests/doubly_linked_list/clear_tests.cpp - tests/ordered_map/insertion_test.cpp + tests/ordered_map/insertion_tests.cpp + tests/ordered_map/lookup_tests.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/tests/ordered_map/lookup_tests.cpp b/tests/ordered_map/lookup_tests.cpp new file mode 100644 index 0000000..03c0b71 --- /dev/null +++ b/tests/ordered_map/lookup_tests.cpp @@ -0,0 +1,21 @@ +#include "ordered_map.hpp" +#include + +TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[at]") { + ordered_map o_map; + o_map.insert(1, 2); + REQUIRE(o_map.at(1) == 2); +} + +TEST_CASE("insert , key hello returns value of world", "[at]") { + ordered_map o_map; + o_map.insert("hello", "world"); + REQUIRE(o_map.at("hello") == "world"); +} + +TEST_CASE("insert , , key hello returns value of there", "[at]") { + ordered_map o_map; + o_map.insert("hello", "world"); + o_map.insert("hello", "there"); + REQUIRE(o_map.at("hello") == "there"); +} \ No newline at end of file From bb5cf08347dc315c75773d256ec477312220791c Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 15:04:49 -0500 Subject: [PATCH 32/46] Remove the tests checklist as it is found redundunt --- README.md | 57 +------------------------------------------------------ 1 file changed, 1 insertion(+), 56 deletions(-) diff --git a/README.md b/README.md index c811112..bbe7491 100644 --- a/README.md +++ b/README.md @@ -186,59 +186,4 @@ The project is currently in development, with the following components: - [ ] DoublyLinkedList: Add pre and post decrement operators - [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for copying behavior -- [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for destructor behavior - -## Tests - -### Doubly Linked List Tests - -#### Front and Back Operations -- [x] Empty list has size 0 -- [x] Empty list front() throws exception -- [x] Empty list back() throws exception -- [x] List with one element returns correct front -- [x] List with one element returns correct back -- [x] List with multiple elements returns correct front -- [x] List with multiple elements returns correct back - -#### Iterator Operations -- [x] Empty list begin equals end -- [x] Single element list begin points to element -- [x] Single element list begin++ equals end -- [x] Multiple element list begin points to first -- [x] Multiple element list begin++ points to second -- [x] Multiple element list can iterate through all elements - -#### Push Back Operations -- [x] Push back one element increases size to 1 -- [x] Push back multiple elements increases size correctly -- [x] Push back maintains correct order of elements - -#### Push Front Operations -- [x] Push front one element increases size to 1 -- [x] Push front multiple elements increases size correctly -- [x] Push front maintains correct order of elements - -#### Pop Back Operations -- [x] Pop back on empty list throws exception -- [x] Pop back decreases size by 1 -- [x] Pop back removes last element -- [x] Pop back maintains remaining elements - -#### Pop Front Operations -- [x] Pop front on empty list throws exception -- [x] Pop front decreases size by 1 -- [x] Pop front removes first element -- [x] Pop front maintains remaining elements - -### Ordered Map Tests - -#### Insertion Operations -- [x] Empty ordered map has size 0 -- [x] Inserting <1,2> into empty map sets size to 1 -- [x] Inserting duplicate key maintains size of 1 -- [x] Inserting different keys increases size -- [x] Inserting <1,2> allows retrieving 2 with key 1 -- [x] Inserting string key-value pairs works correctly -- [x] Updating existing key updates the value - +- [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for destructor behavior From d904ba4f5c2bbd9e53894d50db19e6592959f2d6 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 15:07:31 -0500 Subject: [PATCH 33/46] Fix: modify list iterator to cover intended case --- tests/doubly_linked_list/iterator_tests.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/doubly_linked_list/iterator_tests.cpp b/tests/doubly_linked_list/iterator_tests.cpp index eda3438..5b3bc82 100644 --- a/tests/doubly_linked_list/iterator_tests.cpp +++ b/tests/doubly_linked_list/iterator_tests.cpp @@ -91,15 +91,17 @@ TEST_CASE("push_back 1, 2, using post-increment (it++) on begin iterator to reac REQUIRE(*it == 2); } -TEST_CASE("push_back 1, 2, 3, using post-increment (it++) on begin iterator to reach 2", "[iterator]") { +TEST_CASE("push_back 1, 2, 3, using post-increment (it++) on begin iterator twice to reach 3", "[iterator]") { DoublyLinkedList list; list.push_back(1); list.push_back(2); list.push_back(3); auto it = list.begin(); it++; + it++; - REQUIRE(*it == 2); + + REQUIRE(*it == 3); } TEST_CASE("push_back 1, 2, copy the iterator after using post-increment (it++) on begin iterator", "[iterator]") { From 6a648761d26584c7dc2b731dd2a48f6eb14d2da6 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 15:25:50 -0500 Subject: [PATCH 34/46] Create tests for insertions in map using .at() and for [] operator and make them pass --- include/ordered_map.hpp | 9 +++++++ tests/ordered_map/lookup_tests.cpp | 40 +++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index b68d44a..a8f8fdb 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -24,7 +24,16 @@ class ordered_map { } V& at(const K& key) { + if (_map.find(key) == _map.end()) { + throw std::out_of_range("Key not found in ordered map"); + } return *_map[key]; } + V& operator[](const K& key) { + if (_map.find(key) == _map.end()) { + this->insert(key, V{}); + } + return *_map[key]; + } }; \ No newline at end of file diff --git a/tests/ordered_map/lookup_tests.cpp b/tests/ordered_map/lookup_tests.cpp index 03c0b71..aff5f09 100644 --- a/tests/ordered_map/lookup_tests.cpp +++ b/tests/ordered_map/lookup_tests.cpp @@ -18,4 +18,42 @@ TEST_CASE("insert , , key hello returns value of the o_map.insert("hello", "world"); o_map.insert("hello", "there"); REQUIRE(o_map.at("hello") == "there"); -} \ No newline at end of file +} + +TEST_CASE("lookup key that does not exist in ordered map throws out_of_range exception", "[at]") { + ordered_map o_map; + o_map.insert(1, 2); + REQUIRE_THROWS_AS(o_map.at(2), std::out_of_range); +} + +TEST_CASE("insert <1, 2>, operator[] returns value of 2", "[square_brackets]") { + ordered_map o_map; + o_map.insert(1, 2); + REQUIRE(o_map[1] == 2); +} + +TEST_CASE("insert, map[hello] returns value of world", "[square_brackets]") { + ordered_map o_map; + o_map.insert("hello", "world"); + REQUIRE(o_map["hello"] == "world"); +} + +TEST_CASE("using operator[] on an empty map makes the size = 1", "[square_brackets]") { + ordered_map o_map; + o_map[1] = 2; + REQUIRE(o_map.size() == 1); +} + +TEST_CASE("using operator[1] on a key that does not exist in the map assigns default value of type int", "[square_brackets]") { + ordered_map o_map; + o_map[1]; + REQUIRE(o_map.at(1) == int()); +} + +TEST_CASE("using operator[hello] on a key that does not exist in the map assigns default value of type string", "[square_brackets]") { + ordered_map o_map; + o_map["hello"]; + REQUIRE(o_map.at("hello") == std::string()); +} + + From 50f12c99b27f5e0052a9277e47ac5ecfd5d9e3b3 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 15:47:05 -0500 Subject: [PATCH 35/46] Add tests for updating values using lookup operator [] in ordered map, make them pass --- include/ordered_map.hpp | 2 ++ tests/ordered_map/lookup_tests.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index a8f8fdb..a4d4ef5 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -36,4 +36,6 @@ class ordered_map { } return *_map[key]; } + + }; \ No newline at end of file diff --git a/tests/ordered_map/lookup_tests.cpp b/tests/ordered_map/lookup_tests.cpp index aff5f09..37868e9 100644 --- a/tests/ordered_map/lookup_tests.cpp +++ b/tests/ordered_map/lookup_tests.cpp @@ -56,4 +56,17 @@ TEST_CASE("using operator[hello] on a key that does not exist in the map assigns REQUIRE(o_map.at("hello") == std::string()); } +TEST_CASE("insert , using operator[key] allows updating the value to another value", "[square_brackets]") { + ordered_map o_map; + o_map.insert("key", "value"); + o_map["key"] = "new value"; + REQUIRE(o_map.at("key") == "new value"); +} + +TEST_CASE("updating an existing key does not affect the size of the map", "[square_brackets]") { + ordered_map o_map; + o_map.insert("key", "value"); + o_map["key"] = "new value"; + REQUIRE(o_map.size() == 1); +} From 9b5db7708352003fd18061fa011a159e26976c89 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 17:46:45 -0500 Subject: [PATCH 36/46] Add map iterator tests and implement features passing the tests - Test begin iterator after insertion - Test increment ++ operations (pre and post increment) --- CMakeLists.txt | 1 + include/ordered_map.hpp | 60 ++++++++++++++++++++++------ tests/ordered_map/iterator_tests.cpp | 38 ++++++++++++++++++ 3 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 tests/ordered_map/iterator_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 16d3c1b..8d42319 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable( tests/doubly_linked_list/clear_tests.cpp tests/ordered_map/insertion_tests.cpp tests/ordered_map/lookup_tests.cpp + tests/ordered_map/iterator_tests.cpp ) target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index a4d4ef5..1767210 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -2,20 +2,28 @@ #include "doubly_linked_list.hpp" #include -template + + +template class ordered_map { private: - DoublyLinkedList _list; - std::unordered_map::Iterator> _map; + + DoublyLinkedList> _list; + + using ListIterator = typename DoublyLinkedList>::Iterator; + + std::unordered_map _map; + + public: ordered_map() {} - void insert(const K& key, const V& value) { + void insert(const KeyType& key, const ValueType& value) { if (_map.find(key) == _map.end()) { - _list.push_back(value); + _list.push_back(std::make_pair(key, value)); _map[key] = _list.back_iterator(); } else { - *_map.find(key)->second = value; + *_map.find(key)->second = std::make_pair(key, value); } } @@ -23,19 +31,47 @@ class ordered_map { return _list.size(); } - V& at(const K& key) { + ValueType& at(const KeyType& key) { if (_map.find(key) == _map.end()) { throw std::out_of_range("Key not found in ordered map"); } - return *_map[key]; + return (*_map[key]).second; } - V& operator[](const K& key) { + ValueType& operator[](const KeyType& key) { if (_map.find(key) == _map.end()) { - this->insert(key, V{}); + this->insert(key, ValueType{}); } - return *_map[key]; + return (*_map[key]).second; + } + + class Iterator { + ListIterator _it; + + public: + Iterator(ListIterator it) : _it(it) {} + + std::pair& operator*() { + return *_it; + } + + Iterator& operator++() { + _it++; + return *this; + } + + Iterator operator++(int) { + Iterator initial_state_copy = *this; + _it++; + return initial_state_copy; + } + + + + }; + + Iterator begin() { + return Iterator(_list.begin()); } - }; \ No newline at end of file diff --git a/tests/ordered_map/iterator_tests.cpp b/tests/ordered_map/iterator_tests.cpp new file mode 100644 index 0000000..16984e2 --- /dev/null +++ b/tests/ordered_map/iterator_tests.cpp @@ -0,0 +1,38 @@ +#include +#include "../include/ordered_map.hpp" + +TEST_CASE("insert <1, 2>, begin iterator points to <1, 2>", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + + REQUIRE(*o_map.begin() == std::make_pair(1, 2)); +} + +TEST_CASE("insert <1, 2>, <3, 4>, begin iterator points to <1, 2>", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + + REQUIRE(*o_map.begin() == std::make_pair(1, 2)); +} + +TEST_CASE("insert <1, 2>, <3, 4>, using pre-increment (++it) on begin iterator makes it point to <3, 4>", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + o_map.insert(3, 4); + + auto it = o_map.begin(); + ++it; + + REQUIRE(*it == std::make_pair(3, 4)); +} + +TEST_CASE("insert <1, 2>, <3, 4>, using post-increment (it++) on begin iterator makes it point to <1, 2>", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + o_map.insert(3, 4); + + auto it = o_map.begin(); + it++; + + REQUIRE(*it == std::make_pair(3, 4)); +} \ No newline at end of file From bafa8e3bbff629f62434839512469d1a3f3bb298 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 17:47:32 -0500 Subject: [PATCH 37/46] Add items to future improvements checklist in readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index bbe7491..1f8279f 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,9 @@ The project is currently in development, with the following components: ### Checklist for future improvements +- [ ] DoublyLinkedList: Add tests for copying & moving +- [ ] 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 - [ ] DoublyLinkedList: Test front(), back(), insertion and deletion functions for destructor behavior From c5fc389280232d7183601f11d6945c60a6486baa Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 17:55:01 -0500 Subject: [PATCH 38/46] Add end iterator, and add equality operators for ordered_map --- include/ordered_map.hpp | 11 +++++++++++ tests/ordered_map/iterator_tests.cpp | 29 +++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index 1767210..d19f684 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -66,7 +66,13 @@ class ordered_map { return initial_state_copy; } + bool operator==(const Iterator& other) const { + return _it == other._it; + } + bool operator!=(const Iterator& other) const { + return !(*this == other); + } }; @@ -74,4 +80,9 @@ class ordered_map { return Iterator(_list.begin()); } + Iterator end() { + return Iterator(_list.end()); + } + + }; \ No newline at end of file diff --git a/tests/ordered_map/iterator_tests.cpp b/tests/ordered_map/iterator_tests.cpp index 16984e2..051133c 100644 --- a/tests/ordered_map/iterator_tests.cpp +++ b/tests/ordered_map/iterator_tests.cpp @@ -35,4 +35,31 @@ TEST_CASE("insert <1, 2>, <3, 4>, using post-increment (it++) on begin iterator it++; REQUIRE(*it == std::make_pair(3, 4)); -} \ No newline at end of file +} + +TEST_CASE("iterator (begin == end) is true when map is empty", "[ordered_map_iterator]") { + ordered_map o_map; + + REQUIRE(o_map.begin() == o_map.end()); +} + +TEST_CASE("iterator (begin == end) is false when map is not empty", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + + REQUIRE(!(o_map.begin() == o_map.end())); +} + +TEST_CASE("iterator (begin != end) is false when map is empty", "[ordered_map_iterator]") { + ordered_map o_map; + + REQUIRE(!(o_map.begin() != o_map.end())); +} + +TEST_CASE("iterator (begin != end) is true when map is not empty", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + + REQUIRE(o_map.begin() != o_map.end()); +} + From 9269a71d8a44bf705a686696604ba43cf4f5fca2 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:14:03 -0500 Subject: [PATCH 39/46] Add tests for size() and empty() in the list, and minor refactorings --- CMakeLists.txt | 1 + include/doubly_linked_list.hpp | 15 +++++----- tests/doubly_linked_list/size_tests.cpp | 38 +++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 tests/doubly_linked_list/size_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d42319..1257f7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ FetchContent_MakeAvailable(catch2) # Add test executable add_executable( tests + tests/doubly_linked_list/size_tests.cpp tests/doubly_linked_list/front_and_back.cpp tests/doubly_linked_list/iterator_tests.cpp tests/doubly_linked_list/pop_back_tests.cpp diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 2c4e996..cbc5997 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -49,7 +49,6 @@ class DoublyLinkedList { tail->next = std::make_unique>(value, tail, nullptr); tail = tail->next.get(); } - _size++; } @@ -70,7 +69,6 @@ class DoublyLinkedList { } _size--; - return popped_value; } @@ -78,19 +76,18 @@ class DoublyLinkedList { if (!head) { head = std::make_unique>(value, nullptr, nullptr); tail = head.get(); - } - else { + } 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++; } T pop_front() { - if (!head) + if (!head) { throw std::out_of_range("List is empty"); + } T popped_value = head->value; head = std::move(head->next); @@ -108,6 +105,10 @@ class DoublyLinkedList { tail = nullptr; } + bool empty() const { + return _size == 0; + } + class Iterator { Node* current_node_ptr; @@ -149,8 +150,6 @@ class DoublyLinkedList { } return *this; } - - }; Iterator begin() { diff --git a/tests/doubly_linked_list/size_tests.cpp b/tests/doubly_linked_list/size_tests.cpp new file mode 100644 index 0000000..766a3d7 --- /dev/null +++ b/tests/doubly_linked_list/size_tests.cpp @@ -0,0 +1,38 @@ +#include +#include "doubly_linked_list.hpp" + +TEST_CASE("size is 0 when list is empty", "[size]") { + DoublyLinkedList list; + REQUIRE(list.size() == 0); +} + +TEST_CASE("size is 1 after pushing one element", "[size]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(list.size() == 1); +} + +TEST_CASE("size is 2 after pushing two elements", "[size]") { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + REQUIRE(list.size() == 2); +} + +TEST_CASE("empty() returns true when list is empty", "[empty]") { + DoublyLinkedList list; + REQUIRE(list.empty()); +} + +TEST_CASE("empty() returns false when list is not empty", "[empty]") { + DoublyLinkedList list; + list.push_back(1); + REQUIRE(!list.empty()); +} + + + + + + + From c4e05a2c131ac8c8d560fe614f3127d83ad92857 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:16:42 -0500 Subject: [PATCH 40/46] Add tests for back_iterator and for-loop iteration in ordered_map --- include/ordered_map.hpp | 11 +++++++ tests/ordered_map/iterator_tests.cpp | 49 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index d19f684..1e7d88b 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -83,6 +83,17 @@ class ordered_map { Iterator end() { return Iterator(_list.end()); } + + bool empty() { + return _list.empty(); + } + + Iterator back_iterator() { + if (empty()) { + throw std::out_of_range("Map is empty"); + } + return Iterator(_list.back_iterator()); + } }; \ No newline at end of file diff --git a/tests/ordered_map/iterator_tests.cpp b/tests/ordered_map/iterator_tests.cpp index 051133c..6de6369 100644 --- a/tests/ordered_map/iterator_tests.cpp +++ b/tests/ordered_map/iterator_tests.cpp @@ -63,3 +63,52 @@ TEST_CASE("iterator (begin != end) is true when map is not empty", "[ordered_map REQUIRE(o_map.begin() != o_map.end()); } +TEST_CASE("using for-loop with begin and end iterators to iterate over map yields correct results in order", "[ordered_map_iterator]") { + std::vector> expected_results = { + {1, 2}, + {2, 3}, + {3, 4} + }; + ordered_map o_map; + for (const auto& pair : expected_results) { + o_map.insert(pair.first, pair.second); + } + + int i = 0; + for (auto it = o_map.begin(); it != o_map.end(); it++) { + REQUIRE(*it == expected_results[i]); + i++; + } +} + +TEST_CASE("using for-each loop yields correct results in order", "[ordered_map_iterator]") { + std::vector> expected_results = { + {1, 2}, + {2, 3}, + {3, 4} + }; + ordered_map o_map; + for (const auto& pair : expected_results) { + o_map.insert(pair.first, pair.second); + } + + int i = 0; + for (auto& pair : o_map) { + REQUIRE(pair == expected_results[i]); + i++; + } +} + +TEST_CASE("back_iterator() throws out_of_range when map is empty", "[ordered_map_iterator]") { + ordered_map o_map; + + REQUIRE_THROWS_AS(o_map.back_iterator(), std::out_of_range); +} + +TEST_CASE("insert <1, 2>, back_iterator points to <1, 2>", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + + REQUIRE(*o_map.back_iterator() == std::make_pair(1, 2)); +} + From 45b3d8645fd8aa0c3f5bf2b2653d8d6b30dc521f Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:17:04 -0500 Subject: [PATCH 41/46] Add a test for back_iterator in ordered_map --- tests/ordered_map/iterator_tests.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/ordered_map/iterator_tests.cpp b/tests/ordered_map/iterator_tests.cpp index 6de6369..5f7f71a 100644 --- a/tests/ordered_map/iterator_tests.cpp +++ b/tests/ordered_map/iterator_tests.cpp @@ -112,3 +112,11 @@ TEST_CASE("insert <1, 2>, back_iterator points to <1, 2>", "[ordered_map_iterato REQUIRE(*o_map.back_iterator() == std::make_pair(1, 2)); } +TEST_CASE("insert <1, 2>, <3, 4>, back_iterator points to <3, 4>", "[ordered_map_iterator]") { + ordered_map o_map; + o_map.insert(1, 2); + o_map.insert(3, 4); + + REQUIRE(*o_map.back_iterator() == std::make_pair(3, 4)); +} + From c1f2f56e710e45085aa5991523828a22bafee731 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:17:34 -0500 Subject: [PATCH 42/46] Add tests_for size and empty methods in ordered map --- CMakeLists.txt | 1 + tests/ordered_map/size_tests.cpp | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/ordered_map/size_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1257f7c..a57d44c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable( tests/doubly_linked_list/push_back_tests.cpp tests/doubly_linked_list/push_front_tests.cpp tests/doubly_linked_list/clear_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 diff --git a/tests/ordered_map/size_tests.cpp b/tests/ordered_map/size_tests.cpp new file mode 100644 index 0000000..f5f14ee --- /dev/null +++ b/tests/ordered_map/size_tests.cpp @@ -0,0 +1,33 @@ +#include +#include "ordered_map.hpp" + +TEST_CASE("size is 0 when map is empty", "[size]") { + ordered_map map; + REQUIRE(map.size() == 0); +} + +TEST_CASE("size is 1 after inserting one element", "[size]") { + ordered_map map; + map.insert(1, 2); + REQUIRE(map.size() == 1); +} + +TEST_CASE("size is 2 after inserting two elements", "[size]") { + ordered_map map; + map.insert(1, 2); + map.insert(3, 4); + REQUIRE(map.size() == 2); +} + +TEST_CASE("empty() returns true when map is empty", "[empty]") { + ordered_map map; + REQUIRE(map.empty()); +} + +TEST_CASE("empty() returns false when map is not empty", "[empty]") { + ordered_map map; + map.insert(1, 2); + REQUIRE(!map.empty()); +} + + From 2ff67184f215205d4aaa218565051e7a23763388 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:43:31 -0500 Subject: [PATCH 43/46] Add tests for adding large number of elements to list, make the test pass by changing clear method - Test for large numbers like 1e6 and 1e7 elements (tested then removed to avoid slow tests) - Change clear method to make removal iterative instead of recursive, preventing stack overflow - Call clear method in destructor to avoid stack overflow --- include/doubly_linked_list.hpp | 9 +++++--- tests/doubly_linked_list/clear_tests.cpp | 10 +++++++++ tests/doubly_linked_list/size_tests.cpp | 26 +++++++++++++++++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index cbc5997..33779d8 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -21,6 +21,9 @@ class DoublyLinkedList { public: DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {} + ~DoublyLinkedList() { + clear(); + } int size() const { return _size; @@ -100,9 +103,9 @@ class DoublyLinkedList { } void clear() { - _size = 0; - head.reset(); - tail = nullptr; + while(size() > 0) { + pop_back(); + } } bool empty() const { diff --git a/tests/doubly_linked_list/clear_tests.cpp b/tests/doubly_linked_list/clear_tests.cpp index e0f0a0b..b6aff44 100644 --- a/tests/doubly_linked_list/clear_tests.cpp +++ b/tests/doubly_linked_list/clear_tests.cpp @@ -49,3 +49,13 @@ TEST_CASE("calling clear on a list of custom class objects makes the destructor list.clear(); REQUIRE(deletion_flag == true); } + +TEST_CASE("calling clear after pushing 100 elements makes size = 0", "[size]") { + DoublyLinkedList list; + for (int i = 0; i < 100; i++) { + list.push_back(i); + } + REQUIRE(list.size() == 100); + list.clear(); + REQUIRE(list.size() == 0); +} diff --git a/tests/doubly_linked_list/size_tests.cpp b/tests/doubly_linked_list/size_tests.cpp index 766a3d7..5ae30d6 100644 --- a/tests/doubly_linked_list/size_tests.cpp +++ b/tests/doubly_linked_list/size_tests.cpp @@ -29,7 +29,31 @@ TEST_CASE("empty() returns false when list is not empty", "[empty]") { list.push_back(1); REQUIRE(!list.empty()); } - + +TEST_CASE("size is 100 after pushing 100 elements", "[size]") { + DoublyLinkedList list; + for (int i = 0; i < 100; i++) { + list.push_back(i); + } + REQUIRE(list.size() == 100); +} + +TEST_CASE("size is 100000 after pushing 100000 elements", "[size]") { + DoublyLinkedList list; + for (int i = 0; i < 100000; i++) { + list.push_back(i); + } + REQUIRE(list.size() == 100000); +} + +TEST_CASE("size is 1000,000 after pushing 1000,000 elements", "[size]") { + DoublyLinkedList list; + int size = int(1e6); + for (int i = 0; i < size; i++) { + list.push_back(i); + } + REQUIRE(list.size() == size); +} From da64baccd926680ebb2fc4b83ceec16e45e942a7 Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:47:42 -0500 Subject: [PATCH 44/46] Refactor: rename ordered_map to OrderedMap for consistency, and update github workflow --- .github/workflows/cmake.yml | 4 ++-- include/ordered_map.hpp | 4 ++-- tests/ordered_map/insertion_tests.cpp | 8 ++++---- tests/ordered_map/iterator_tests.cpp | 26 +++++++++++++------------- tests/ordered_map/lookup_tests.cpp | 22 +++++++++++----------- tests/ordered_map/size_tests.cpp | 10 +++++----- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index cdf1fdc..e2d48ce 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -18,5 +18,5 @@ jobs: - name: Build and test run: | - chmod +x build.sh - ./build.sh + chmod +x run_cmake.sh + ./run_cmake.sh diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index 1e7d88b..cdb0233 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -5,7 +5,7 @@ template -class ordered_map { +class OrderedMap { private: DoublyLinkedList> _list; @@ -16,7 +16,7 @@ class ordered_map { public: - ordered_map() {} + OrderedMap() {} void insert(const KeyType& key, const ValueType& value) { if (_map.find(key) == _map.end()) { _list.push_back(std::make_pair(key, value)); diff --git a/tests/ordered_map/insertion_tests.cpp b/tests/ordered_map/insertion_tests.cpp index d041fdb..3f6f95c 100644 --- a/tests/ordered_map/insertion_tests.cpp +++ b/tests/ordered_map/insertion_tests.cpp @@ -2,25 +2,25 @@ #include "ordered_map.hpp" TEST_CASE("empty ordered map has size 0", "[insert]") { - ordered_map o_map; + OrderedMap o_map; REQUIRE(o_map.size() == 0); } TEST_CASE("inserting <1, 2> into an empty ordered map", "[insert]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(o_map.size() == 1); } TEST_CASE("insert <1, 2> and <1, 3>, size = 1", "[insert]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); o_map.insert(1, 3); REQUIRE(o_map.size() == 1); } TEST_CASE("insert <1, 2> and <2, 3>, size = 2", "[insert]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); o_map.insert(2, 3); REQUIRE(o_map.size() == 2); diff --git a/tests/ordered_map/iterator_tests.cpp b/tests/ordered_map/iterator_tests.cpp index 5f7f71a..45ead1a 100644 --- a/tests/ordered_map/iterator_tests.cpp +++ b/tests/ordered_map/iterator_tests.cpp @@ -2,21 +2,21 @@ #include "../include/ordered_map.hpp" TEST_CASE("insert <1, 2>, begin iterator points to <1, 2>", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(*o_map.begin() == std::make_pair(1, 2)); } TEST_CASE("insert <1, 2>, <3, 4>, begin iterator points to <1, 2>", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(*o_map.begin() == std::make_pair(1, 2)); } TEST_CASE("insert <1, 2>, <3, 4>, using pre-increment (++it) on begin iterator makes it point to <3, 4>", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); o_map.insert(3, 4); @@ -27,7 +27,7 @@ TEST_CASE("insert <1, 2>, <3, 4>, using pre-increment (++it) on begin iterator m } TEST_CASE("insert <1, 2>, <3, 4>, using post-increment (it++) on begin iterator makes it point to <1, 2>", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); o_map.insert(3, 4); @@ -38,26 +38,26 @@ TEST_CASE("insert <1, 2>, <3, 4>, using post-increment (it++) on begin iterator } TEST_CASE("iterator (begin == end) is true when map is empty", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; REQUIRE(o_map.begin() == o_map.end()); } TEST_CASE("iterator (begin == end) is false when map is not empty", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(!(o_map.begin() == o_map.end())); } TEST_CASE("iterator (begin != end) is false when map is empty", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; REQUIRE(!(o_map.begin() != o_map.end())); } TEST_CASE("iterator (begin != end) is true when map is not empty", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(o_map.begin() != o_map.end()); @@ -69,7 +69,7 @@ TEST_CASE("using for-loop with begin and end iterators to iterate over map yield {2, 3}, {3, 4} }; - ordered_map o_map; + OrderedMap o_map; for (const auto& pair : expected_results) { o_map.insert(pair.first, pair.second); } @@ -87,7 +87,7 @@ TEST_CASE("using for-each loop yields correct results in order", "[ordered_map_i {2, 3}, {3, 4} }; - ordered_map o_map; + OrderedMap o_map; for (const auto& pair : expected_results) { o_map.insert(pair.first, pair.second); } @@ -100,20 +100,20 @@ TEST_CASE("using for-each loop yields correct results in order", "[ordered_map_i } TEST_CASE("back_iterator() throws out_of_range when map is empty", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; REQUIRE_THROWS_AS(o_map.back_iterator(), std::out_of_range); } TEST_CASE("insert <1, 2>, back_iterator points to <1, 2>", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(*o_map.back_iterator() == std::make_pair(1, 2)); } TEST_CASE("insert <1, 2>, <3, 4>, back_iterator points to <3, 4>", "[ordered_map_iterator]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); o_map.insert(3, 4); diff --git a/tests/ordered_map/lookup_tests.cpp b/tests/ordered_map/lookup_tests.cpp index 37868e9..b769fae 100644 --- a/tests/ordered_map/lookup_tests.cpp +++ b/tests/ordered_map/lookup_tests.cpp @@ -2,69 +2,69 @@ #include TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[at]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(o_map.at(1) == 2); } TEST_CASE("insert , key hello returns value of world", "[at]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert("hello", "world"); REQUIRE(o_map.at("hello") == "world"); } TEST_CASE("insert , , key hello returns value of there", "[at]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert("hello", "world"); o_map.insert("hello", "there"); REQUIRE(o_map.at("hello") == "there"); } TEST_CASE("lookup key that does not exist in ordered map throws out_of_range exception", "[at]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE_THROWS_AS(o_map.at(2), std::out_of_range); } TEST_CASE("insert <1, 2>, operator[] returns value of 2", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert(1, 2); REQUIRE(o_map[1] == 2); } TEST_CASE("insert, map[hello] returns value of world", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert("hello", "world"); REQUIRE(o_map["hello"] == "world"); } TEST_CASE("using operator[] on an empty map makes the size = 1", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map[1] = 2; REQUIRE(o_map.size() == 1); } TEST_CASE("using operator[1] on a key that does not exist in the map assigns default value of type int", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map[1]; REQUIRE(o_map.at(1) == int()); } TEST_CASE("using operator[hello] on a key that does not exist in the map assigns default value of type string", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map["hello"]; REQUIRE(o_map.at("hello") == std::string()); } TEST_CASE("insert , using operator[key] allows updating the value to another value", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert("key", "value"); o_map["key"] = "new value"; REQUIRE(o_map.at("key") == "new value"); } TEST_CASE("updating an existing key does not affect the size of the map", "[square_brackets]") { - ordered_map o_map; + OrderedMap o_map; o_map.insert("key", "value"); o_map["key"] = "new value"; REQUIRE(o_map.size() == 1); diff --git a/tests/ordered_map/size_tests.cpp b/tests/ordered_map/size_tests.cpp index f5f14ee..f9cd138 100644 --- a/tests/ordered_map/size_tests.cpp +++ b/tests/ordered_map/size_tests.cpp @@ -2,30 +2,30 @@ #include "ordered_map.hpp" TEST_CASE("size is 0 when map is empty", "[size]") { - ordered_map map; + OrderedMap map; REQUIRE(map.size() == 0); } TEST_CASE("size is 1 after inserting one element", "[size]") { - ordered_map map; + OrderedMap map; map.insert(1, 2); REQUIRE(map.size() == 1); } TEST_CASE("size is 2 after inserting two elements", "[size]") { - ordered_map map; + OrderedMap map; map.insert(1, 2); map.insert(3, 4); REQUIRE(map.size() == 2); } TEST_CASE("empty() returns true when map is empty", "[empty]") { - ordered_map map; + OrderedMap map; REQUIRE(map.empty()); } TEST_CASE("empty() returns false when map is not empty", "[empty]") { - ordered_map map; + OrderedMap map; map.insert(1, 2); REQUIRE(!map.empty()); } From da1314d42e9c421fc44808cc967d24337b1c3f0e Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:49:24 -0500 Subject: [PATCH 45/46] Fix type in github workflow --- .github/workflows/cmake.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index e2d48ce..876cae9 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -18,5 +18,5 @@ jobs: - name: Build and test run: | - chmod +x run_cmake.sh - ./run_cmake.sh + chmod +x run_build.sh + ./run_build.sh From f0eaa1a5513d4238b22b6bdc653ea4562c6071ab Mon Sep 17 00:00:00 2001 From: itsmosalah Date: Tue, 20 May 2025 18:56:07 -0500 Subject: [PATCH 46/46] Add #include to fix github workflow issue --- include/doubly_linked_list.hpp | 1 + include/ordered_map.hpp | 2 +- tests/doubly_linked_list/clear_tests.cpp | 2 +- tests/doubly_linked_list/front_and_back.cpp | 1 + tests/doubly_linked_list/pop_back_tests.cpp | 2 +- tests/doubly_linked_list/pop_front_tests.cpp | 2 +- tests/ordered_map/iterator_tests.cpp | 1 + tests/ordered_map/lookup_tests.cpp | 1 + 8 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/doubly_linked_list.hpp b/include/doubly_linked_list.hpp index 33779d8..20e4bc5 100644 --- a/include/doubly_linked_list.hpp +++ b/include/doubly_linked_list.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include template diff --git a/include/ordered_map.hpp b/include/ordered_map.hpp index cdb0233..1fe5686 100644 --- a/include/ordered_map.hpp +++ b/include/ordered_map.hpp @@ -1,7 +1,7 @@ #pragma once #include "doubly_linked_list.hpp" #include - +#include template diff --git a/tests/doubly_linked_list/clear_tests.cpp b/tests/doubly_linked_list/clear_tests.cpp index b6aff44..5dea1cf 100644 --- a/tests/doubly_linked_list/clear_tests.cpp +++ b/tests/doubly_linked_list/clear_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include TEST_CASE("calling clear method on a non-empty list makes size = 0", "[clear]") { DoublyLinkedList list; diff --git a/tests/doubly_linked_list/front_and_back.cpp b/tests/doubly_linked_list/front_and_back.cpp index 06b56b5..c245bd5 100644 --- a/tests/doubly_linked_list/front_and_back.cpp +++ b/tests/doubly_linked_list/front_and_back.cpp @@ -1,5 +1,6 @@ #include #include "doubly_linked_list.hpp" +#include TEST_CASE("Canary test", "[canary]") { REQUIRE(true); diff --git a/tests/doubly_linked_list/pop_back_tests.cpp b/tests/doubly_linked_list/pop_back_tests.cpp index f279f59..3927c7b 100644 --- a/tests/doubly_linked_list/pop_back_tests.cpp +++ b/tests/doubly_linked_list/pop_back_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include TEST_CASE("push_back 1, pop_back once, size = 0", "[pop_back]") { DoublyLinkedList list; diff --git a/tests/doubly_linked_list/pop_front_tests.cpp b/tests/doubly_linked_list/pop_front_tests.cpp index 52e2cb1..7a3c9cb 100644 --- a/tests/doubly_linked_list/pop_front_tests.cpp +++ b/tests/doubly_linked_list/pop_front_tests.cpp @@ -1,6 +1,6 @@ #include #include "doubly_linked_list.hpp" - +#include TEST_CASE("push_back 1, 2, pop_front once, size is 1", "[pop_front]") { DoublyLinkedList list; diff --git a/tests/ordered_map/iterator_tests.cpp b/tests/ordered_map/iterator_tests.cpp index 45ead1a..5886ba4 100644 --- a/tests/ordered_map/iterator_tests.cpp +++ b/tests/ordered_map/iterator_tests.cpp @@ -1,5 +1,6 @@ #include #include "../include/ordered_map.hpp" +#include TEST_CASE("insert <1, 2>, begin iterator points to <1, 2>", "[ordered_map_iterator]") { OrderedMap o_map; diff --git a/tests/ordered_map/lookup_tests.cpp b/tests/ordered_map/lookup_tests.cpp index b769fae..3e473e3 100644 --- a/tests/ordered_map/lookup_tests.cpp +++ b/tests/ordered_map/lookup_tests.cpp @@ -1,5 +1,6 @@ #include "ordered_map.hpp" #include +#include TEST_CASE("insert <1, 2>, key 1 returns value of 2", "[at]") { OrderedMap o_map;