Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
18c1d91
Implement a few tests for doubly linked list and make them pass by im…
mohameds-dev May 15, 2025
7b95b1c
Use unique pointer for head and next in linked list
mohameds-dev May 15, 2025
403a3de
Make parameters more expressive in Node constructor
mohameds-dev May 15, 2025
dd827d0
Fix pop back
mohameds-dev May 15, 2025
f382ab5
Add tests for pop_back, front and back and make them pass
mohameds-dev May 15, 2025
bcdeef5
Fixate the random seed in tests
mohameds-dev May 15, 2025
eceeb72
Add tests for push front, make them pass
mohameds-dev May 15, 2025
40929c0
Refactor exception types, add tests for pop front and make them pass
mohameds-dev May 15, 2025
0f33217
Make front() and back() return const refs to prevent copying and muta…
mohameds-dev May 15, 2025
dd2214e
Update readme to reflect current state of the project
mohameds-dev May 15, 2025
531a762
Add two tests for push back and front, add tests for iterator initial…
mohameds-dev May 16, 2025
f096f6b
Create tests for iterator equality, inequality, and dereferencing by …
mohameds-dev May 16, 2025
b45da57
Fix test tags
mohameds-dev May 16, 2025
a149ce6
Refactor: split tests and group relevant tests together
mohameds-dev May 16, 2025
13a515a
Refactor: remove unnecessary cmake file and rename the project in the…
mohameds-dev May 16, 2025
725fd57
Update readme.md with the current state of the project
mohameds-dev May 16, 2025
e4437cc
Rename build.sh to run_build.sh
mohameds-dev May 19, 2025
54a863f
Add tests for post and pre increment (++) on list iterators and make …
mohameds-dev May 19, 2025
55a7f84
Create tests for clear method, make them pass
mohameds-dev May 19, 2025
d285c1f
Add a test to make sure clear method invokes destructor
mohameds-dev May 20, 2025
c15e3df
Encapsulate the TestClass inside the test function
mohameds-dev May 20, 2025
d1de3aa
Add a checklist to README for future improvements
mohameds-dev May 20, 2025
566f379
Add a point to the readme checklist
mohameds-dev May 20, 2025
8f95eb3
Create tests for ordered map, make them pass, modify doubly linked list
mohameds-dev May 20, 2025
cb287ee
Create test for .at() and make it pass
mohameds-dev May 20, 2025
c648aab
Create another test for .at() with strings and make it pass
mohameds-dev May 20, 2025
8536bf0
Create test for updating an existing value and make it pass
mohameds-dev May 20, 2025
5ac8540
Remove unnecessary _size variable
mohameds-dev May 20, 2025
5fbc8c6
Add tests lists for easier tracking
mohameds-dev May 20, 2025
ff0ef1a
Refactor: fix tests tags to make them usable and expressive
mohameds-dev May 20, 2025
80e1476
Refactor: Move lookup tests to a separate file, update cmakelists
mohameds-dev May 20, 2025
bb5cf08
Remove the tests checklist as it is found redundunt
mohameds-dev May 20, 2025
d904ba4
Fix: modify list iterator to cover intended case
mohameds-dev May 20, 2025
6a64876
Create tests for insertions in map using .at() and for [] operator an…
mohameds-dev May 20, 2025
50f12c9
Add tests for updating values using lookup operator [] in ordered map…
mohameds-dev May 20, 2025
9b5db77
Add map iterator tests and implement features passing the tests
mohameds-dev May 20, 2025
bafa8e3
Add items to future improvements checklist in readme
mohameds-dev May 20, 2025
c5fc389
Add end iterator, and add equality operators for ordered_map
mohameds-dev May 20, 2025
9269a71
Add tests for size() and empty() in the list, and minor refactorings
mohameds-dev May 20, 2025
c4e05a2
Add tests for back_iterator and for-loop iteration in ordered_map
mohameds-dev May 20, 2025
45b3d86
Add a test for back_iterator in ordered_map
mohameds-dev May 20, 2025
c1f2f56
Add tests_for size and empty methods in ordered map
mohameds-dev May 20, 2025
2ff6718
Add tests for adding large number of elements to list, make the test …
mohameds-dev May 20, 2025
9348ed2
Merge branch 'main' into dev
mohameds-dev May 20, 2025
da64bac
Refactor: rename ordered_map to OrderedMap for consistency, and updat…
mohameds-dev May 20, 2025
da1314d
Fix type in github workflow
mohameds-dev May 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ jobs:

- name: Build and test
run: |
chmod +x build.sh
./build.sh
chmod +x run_build.sh
./run_build.sh
24 changes: 19 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -21,8 +21,22 @@ FetchContent_Declare(
FetchContent_MakeAvailable(catch2)

# Add test executable
add_executable(tests tests/test_doubly_linked_list.cpp)
target_link_libraries(tests PRIVATE myproject Catch2::Catch2WithMain)
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
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
tests/ordered_map/size_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)

# Register tests
include(CTest)
Expand Down
94 changes: 60 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -11,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

Expand All @@ -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
Expand All @@ -40,22 +42,30 @@ 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 interface
├── src/
│ └── doubly_linked_list.cpp # Doubly-linked list implementation
├── tests/
│ └── test_ordered_map.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)

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
{
Expand Down Expand Up @@ -85,13 +95,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)
Expand All @@ -100,7 +113,7 @@ ordered_map/
3. **Run Specific Tests**:
```bash
cd build
./tests --tags [canary]
./tests --tags [tag]
```

## GitHub Actions CI
Expand All @@ -111,22 +124,30 @@ 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:
```cpp
TEST_CASE("Insert maintains order", "[ordered_map]") {
OrderedMap<int, int> map;
map.insert(1, 10);
map.insert(2, 20);
REQUIRE(map.size() == 2);
}
```
1. **Choose the appropriate test file** in `tests/doubly_linked_list/` based on the functionality being tested:

2. **Rebuild and Test**:
- `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<int> list;
// Your test code here
REQUIRE(/* your assertion */);
}
```

3. **Rebuild and Test**:
```bash
./build.sh
```
Expand All @@ -140,27 +161,32 @@ ordered_map/
- Clear build: `rm -rf build && ./build.sh`.
- Check prerequisites.
- **Test Failures**:
- Inspect assertions in `test_ordered_map.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.
- Check logs in the "Actions" tab on GitHub.
- Ensure `build.sh` and dependencies are compatible with `ubuntu-latest`.

## Example Test File
## Project Status

`tests/test_ordered_map.cpp`:
```cpp
#include <catch2/catch_test_macros.hpp>
#include "ordered_map.hpp"
The project is currently in development, with the following components:

TEST_CASE("Canary test", "[canary]") {
REQUIRE(true);
}
1. ✅ Doubly Linked List Implementation

TEST_CASE("Insert maintains order", "[ordered_map]") {
OrderedMap<int, int> map;
map.insert(1, 10);
map.insert(2, 20);
REQUIRE(map.size() == 2);
}
```
- Basic operations (push_back, pop_back, push_front, pop_front)
- Memory management using smart pointers
- 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
- Will maintain insertion order while providing map-like functionality

### 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
169 changes: 169 additions & 0 deletions include/doubly_linked_list.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#pragma once
#include <memory>


template <typename T>
struct Node {
T value;
std::unique_ptr<Node> next;
Node* prev;

Node(const T& a_value, Node* a_prev, std::unique_ptr<Node> a_next)
: value(a_value), prev(a_prev), next(std::move(a_next)) {}
};

template <typename T>
class DoublyLinkedList {
private:
std::unique_ptr<Node<T>> head;
Node<T>* tail;
int _size;

public:
DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {}
~DoublyLinkedList() {
clear();
}

int size() const {
return _size;
}

const T& front() const {
if (!head) {
throw std::out_of_range("List is empty");
}

return head->value;
}

const T& back() const {
if (!tail) {
throw std::out_of_range("List is empty");
}
return tail->value;
}

void push_back(const T& value) {
if (!head) {
head = std::make_unique<Node<T>>(value, nullptr, nullptr);
tail = head.get();
} else {
tail->next = std::make_unique<Node<T>>(value, tail, nullptr);
tail = tail->next.get();
}
_size++;
}

T pop_back() {
if (!head) {
throw std::out_of_range("List is empty");
}

T popped_value = tail->value;

if (tail == head.get()) {
head.reset();
tail = nullptr;
} else {
Node<T>* prev_node = tail->prev;
prev_node->next.reset();
tail = prev_node;
}

_size--;
return popped_value;
}

void push_front(const T& value) {
if (!head) {
head = std::make_unique<Node<T>>(value, nullptr, nullptr);
tail = head.get();
} else {
auto new_node = std::make_unique<Node<T>>(value, nullptr, std::move(head));
new_node->next->prev = new_node.get();
head = std::move(new_node);
}
_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;
}

void clear() {
while(size() > 0) {
pop_back();
}
}

bool empty() const {
return _size == 0;
}

class Iterator {
Node<T>* current_node_ptr;

Iterator(Node<T>* a_current) : current_node_ptr(a_current) {}

friend class DoublyLinkedList<T>;

public:
Iterator() : current_node_ptr(nullptr) {}

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& 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& operator--() {
if (current_node_ptr) {
current_node_ptr = current_node_ptr->prev;
}
return *this;
}
};

Iterator begin() {
return Iterator(head.get());
}

Iterator end() {
return Iterator(nullptr);
}

Iterator back_iterator() {
return Iterator(tail);
}
};
Loading
Loading