Skip to content

Commit 56ef471

Browse files
committed
Upload Tests, Update README.md and Fix Typo in Hashmap Documentation
1 parent 379b5fa commit 56ef471

File tree

6 files changed

+342
-10
lines changed

6 files changed

+342
-10
lines changed

CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
cmake_minimum_required(VERSION ${CMAKE_VERSION})
2+
project(cpp_hashmap_tests)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
6+
include_directories(.)
7+
include_directories(tests)
8+
9+
remove_executable(cpp_hashmap)
10+
11+
add_executable(basic_test
12+
Hashmap.hpp
13+
tests/basic.cpp
14+
)
15+
16+
add_executable(advanced_test
17+
Hashmap.hpp
18+
tests/advanced.cpp
19+
)
20+
21+
add_executable(extreme_test
22+
Hashmap.hpp
23+
tests/extreme.cpp
24+
)

Hashmap.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@
3131
#include <cstddef>
3232
#include <functional>
3333
#include <initializer_list>
34+
#include <iostream>
35+
#include <mutex>
3436
#include <optional>
3537
#include <stdexcept>
3638
#include <vector>
37-
#include <mutex>
3839

3940
namespace LouiEriksson {
4041

4142
/**
42-
* @mainpage Version 2.0.0
43+
* @mainpage Version 2.0.1
4344
* @details Custom Hashmap implementation accepting a customisable key and value type.
4445
* This implementation requires that your "key" type is compatible with std::hash and that the stored data types are copyable.
4546
* @see Wang, Q. (Harry) (2020). Implementing Your Own HashMap (Explanation + Code). YouTube.
@@ -102,10 +103,10 @@ namespace LouiEriksson {
102103
size_t m_Size;
103104

104105
/**
105-
* @brief Calculate the hashcode of a given object using std::fnv1a.
106-
* @param[in] _item Item to calculate fnv1a of.
106+
* @brief Calculate the hashcode of a given object using std::hash.
107+
* @param[in] _item Item to calculate hash of.
107108
* @return Hashcode of _item.
108-
* @throw std::exception If the type of _item is not supported by std::fnv1a.
109+
* @throw std::exception If the type of _item is not supported by std::hash.
109110
*/
110111
static constexpr size_t GetHashcode(const Tk& _item) {
111112
return std::hash<Tk>()(_item);

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# C++ Hashmap (2.0.0)
1+
# C++ Hashmap (2.0.1)
22

33
## Table of Contents
44

@@ -29,17 +29,17 @@ Simply include it in your project and you are ready to start!
2929

3030
#include <string>
3131

32-
#include "Hashmap.h"
32+
#include "Hashmap.hpp"
3333

34-
Hashmap<std::string, float> hashmap {
34+
LouiEriksson::Hashmap<std::string, float> hashmap {
3535
{ "key1", 1.0f },
3636
{ "key2", 2.0f },
3737
{ "key3", 3.0f },
3838
}
3939

4040
int main() {
4141

42-
if (const auto item = hashmap.Get("key3", item)) {
42+
if (const auto item = hashmap.Get("key3")) {
4343
std::cout << "Value: " << item.value() << '\n';
4444
}
4545
else {
@@ -57,10 +57,11 @@ The hashmap was written in C++17 and utilises the following standard headers:
5757
#### &lt;cstddef&gt;
5858
#### &lt;functional&gt;
5959
#### &lt;initializer_list&gt;
60+
#### &lt;iostream&gt;
61+
#### &lt;mutex&gt;
6062
#### &lt;optional&gt;
6163
#### &lt;stdexcept&gt;
6264
#### &lt;vector&gt;
63-
#### &lt;mutex&gt;
6465

6566
### Why not use &lt;unordered_set&gt;?
6667

tests/advanced.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include "../Hashmap.hpp"
2+
3+
#include <iostream>
4+
#include <cassert>
5+
#include <string>
6+
#include <thread>
7+
8+
/**
9+
* @file advanced.cpp
10+
* @brief Advanced tests for the functionality of the hashmap.
11+
*/
12+
int main([[maybe_unused]] int _argc, [[maybe_unused]] char* _argv[]) {
13+
14+
LouiEriksson::Hashmap<int, std::string> hashmap;
15+
16+
std::cout << "~ ADVANCED TESTS ~\n";
17+
18+
// Test 1: Large volume of data
19+
{
20+
std::cout << "Test 1: Large volume of data..." << std::flush;
21+
22+
static constexpr int iterations = 2000000;
23+
24+
for (int i = 0; i < iterations; ++i) {
25+
hashmap.Add(i, std::to_string(i));
26+
}
27+
assert(hashmap.size() == iterations);
28+
29+
std::cout << "Done.\n";
30+
}
31+
32+
// Test 2: Duplicate keys
33+
{
34+
std::cout << "Test 2: Duplicate keys..." << std::flush;
35+
36+
const auto controlSize = hashmap.size();
37+
38+
for (int i = 0; i < controlSize; ++i) {
39+
hashmap.Add(i, "Duplicate");
40+
41+
assert((hashmap.Get(i).value() != "Duplicate") && "Duplicate found.");
42+
assert((hashmap.size() == controlSize) && "Erroneous insertion.");
43+
}
44+
45+
std::cout << "Done.\n";
46+
}
47+
48+
// Test 3: Key collision
49+
{
50+
std::cout << "Test 3: Key collision..." << std::flush;
51+
52+
static constexpr int iterations = 2000000;
53+
54+
for (int i = 1; i < iterations; ++i) {
55+
hashmap.Add(-i, "Negative");
56+
57+
assert(hashmap.Get(-i).value() == "Negative");
58+
}
59+
60+
std::cout << "Done.\n";
61+
}
62+
63+
// Test 4: High churn
64+
{
65+
std::cout << "Test 4: High churn..." << std::flush;
66+
67+
hashmap.Clear();
68+
69+
static constexpr int iterations = 2000000;
70+
71+
for (int i = iterations; i < iterations * 2; ++i) {
72+
hashmap.Add(i, std::to_string(i));
73+
hashmap.Remove(i - iterations);
74+
}
75+
assert(hashmap.size() == iterations);
76+
77+
std::cout << "Done.\n";
78+
}
79+
80+
// Test 5: Concurrency
81+
{
82+
std::cout << "Test 5: Concurrency..." << std::flush;
83+
84+
hashmap.Clear();
85+
86+
static constexpr int iterations = 2000000;
87+
88+
std::exception_ptr writerException = nullptr;
89+
std::exception_ptr readerException = nullptr;
90+
91+
std::thread writer([&hashmap, &writerException]() {
92+
93+
try {
94+
for (int i = 0; i < iterations; ++i) {
95+
hashmap.Add(i, std::to_string(i));
96+
}
97+
}
98+
catch (...) {
99+
writerException = std::current_exception();
100+
}
101+
});
102+
103+
std::thread reader([&hashmap, &readerException]() {
104+
105+
try {
106+
int last_size = 0;
107+
108+
while (last_size < iterations) {
109+
if (hashmap.size() > last_size) {
110+
for (int i = last_size; i < hashmap.size(); ++i) {
111+
assert((hashmap.Get(i).value() == std::to_string(i)));
112+
}
113+
last_size = hashmap.size();
114+
}
115+
}
116+
}
117+
catch (...) {
118+
readerException = std::current_exception();
119+
}
120+
});
121+
122+
writer.join();
123+
reader.join();
124+
125+
if (writerException) { std::rethrow_exception(writerException); }
126+
if (readerException) { std::rethrow_exception(readerException); }
127+
128+
assert(hashmap.size() == iterations);
129+
130+
std::cout << "Done.\n";
131+
}
132+
133+
std::cout << "All tests passed!" << std::endl;
134+
135+
return 0;
136+
}

tests/basic.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include "../Hashmap.hpp"
2+
3+
#include <iostream>
4+
#include <cassert>
5+
#include <string>
6+
7+
/**
8+
* @file basic.cpp
9+
* @brief Basic tests for the functionality of the hashmap.
10+
*/
11+
int main([[maybe_unused]] int _argc, [[maybe_unused]] char* _argv[]) {
12+
13+
LouiEriksson::Hashmap<int, std::string> hashmap;
14+
15+
std::cout << "~ BASIC TESTS ~\n";
16+
17+
// Test 1: Insertion
18+
{
19+
std::cout << "Test 1: Insertion..." << std::flush;
20+
21+
hashmap.Add(1, "One");
22+
hashmap.Add(2, "Two");
23+
hashmap.Add(3, "Three");
24+
25+
std::cout << "Done.\n";
26+
}
27+
28+
// Test 2: Existence check
29+
{
30+
std::cout << "Test 2: Existence check..." << std::flush;
31+
32+
assert(hashmap.ContainsKey(1) && "Failed on key 1.");
33+
assert(hashmap.ContainsKey(2) && "Failed on key 2.");
34+
assert(hashmap.ContainsKey(3) && "Failed on key 3.");
35+
36+
std::cout << "Done.\n";
37+
}
38+
39+
// Test 3: Value retrieval
40+
{
41+
std::cout << "Test 3: Value retrieval..." << std::flush;
42+
43+
auto item1 = hashmap.Get(1);
44+
auto item2 = hashmap.Get(2);
45+
auto item3 = hashmap.Get(3);
46+
47+
assert((item1.value() == "One" ) && "Failed on key 1.");
48+
assert((item2.value() == "Two" ) && "Failed on key 2.");
49+
assert((item3.value() == "Three") && "Failed on key 3.");
50+
51+
std::cout << "Done.\n";
52+
}
53+
54+
// Test 4: Overwriting
55+
{
56+
std::cout << "Test 4: Overwriting..." << std::flush;
57+
58+
hashmap.Assign(1, "New One");
59+
60+
auto item1 = hashmap.Get(1);
61+
62+
assert((item1.value() == "New One") && "Failed on key 1.");
63+
64+
std::cout << "Done.\n";
65+
}
66+
67+
// Test 5: Deletion
68+
{
69+
std::cout << "Test 5: Deletion..." << std::flush;
70+
71+
hashmap.Remove(1);
72+
73+
assert(!hashmap.ContainsKey(1) && "Failed on key 1.");
74+
75+
std::cout << "Done.\n";
76+
}
77+
78+
// Test 6: Clearing
79+
{
80+
std::cout << "Test 5: Clearing..." << std::flush;
81+
82+
hashmap.Clear();
83+
84+
assert(hashmap.size() == 0 && "Failed on key 1.");
85+
86+
std::cout << "Done.\n";
87+
}
88+
89+
std::cout << "All tests passed!\n";
90+
91+
return 0;
92+
}

0 commit comments

Comments
 (0)