Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibTest/TestCase.h>
8
9#include <AK/DeprecatedString.h>
10#include <AK/HashMap.h>
11#include <AK/OwnPtr.h>
12#include <AK/String.h>
13
14TEST_CASE(construct)
15{
16 using IntIntMap = HashMap<int, int>;
17 EXPECT(IntIntMap().is_empty());
18 EXPECT_EQ(IntIntMap().size(), 0u);
19}
20
21TEST_CASE(construct_from_initializer_list)
22{
23 HashMap<int, DeprecatedString> number_to_string {
24 { 1, "One" },
25 { 2, "Two" },
26 { 3, "Three" },
27 };
28 EXPECT_EQ(number_to_string.is_empty(), false);
29 EXPECT_EQ(number_to_string.size(), 3u);
30}
31
32TEST_CASE(populate)
33{
34 HashMap<int, DeprecatedString> number_to_string;
35 number_to_string.set(1, "One");
36 number_to_string.set(2, "Two");
37 number_to_string.set(3, "Three");
38
39 EXPECT_EQ(number_to_string.is_empty(), false);
40 EXPECT_EQ(number_to_string.size(), 3u);
41}
42
43TEST_CASE(range_loop)
44{
45 HashMap<int, DeprecatedString> number_to_string;
46 EXPECT_EQ(number_to_string.set(1, "One"), AK::HashSetResult::InsertedNewEntry);
47 EXPECT_EQ(number_to_string.set(2, "Two"), AK::HashSetResult::InsertedNewEntry);
48 EXPECT_EQ(number_to_string.set(3, "Three"), AK::HashSetResult::InsertedNewEntry);
49
50 int loop_counter = 0;
51 for (auto& it : number_to_string) {
52 EXPECT_EQ(it.value.is_null(), false);
53 ++loop_counter;
54 }
55 EXPECT_EQ(loop_counter, 3);
56}
57
58TEST_CASE(map_remove)
59{
60 HashMap<int, DeprecatedString> number_to_string;
61 EXPECT_EQ(number_to_string.set(1, "One"), AK::HashSetResult::InsertedNewEntry);
62 EXPECT_EQ(number_to_string.set(2, "Two"), AK::HashSetResult::InsertedNewEntry);
63 EXPECT_EQ(number_to_string.set(3, "Three"), AK::HashSetResult::InsertedNewEntry);
64
65 EXPECT_EQ(number_to_string.remove(1), true);
66 EXPECT_EQ(number_to_string.size(), 2u);
67 EXPECT(number_to_string.find(1) == number_to_string.end());
68
69 EXPECT_EQ(number_to_string.remove(3), true);
70 EXPECT_EQ(number_to_string.size(), 1u);
71 EXPECT(number_to_string.find(3) == number_to_string.end());
72 EXPECT(number_to_string.find(2) != number_to_string.end());
73}
74
75TEST_CASE(remove_all_matching)
76{
77 HashMap<int, DeprecatedString> map;
78
79 map.set(1, "One");
80 map.set(2, "Two");
81 map.set(3, "Three");
82 map.set(4, "Four");
83
84 EXPECT_EQ(map.size(), 4u);
85
86 EXPECT_EQ(map.remove_all_matching([&](int key, DeprecatedString const& value) { return key == 1 || value == "Two"; }), true);
87 EXPECT_EQ(map.size(), 2u);
88
89 EXPECT_EQ(map.remove_all_matching([&](int, DeprecatedString const&) { return false; }), false);
90 EXPECT_EQ(map.size(), 2u);
91
92 EXPECT(map.contains(3));
93 EXPECT(map.contains(4));
94
95 EXPECT_EQ(map.remove_all_matching([&](int, DeprecatedString const&) { return true; }), true);
96 EXPECT_EQ(map.remove_all_matching([&](int, DeprecatedString const&) { return false; }), false);
97
98 EXPECT(map.is_empty());
99
100 EXPECT_EQ(map.remove_all_matching([&](int, DeprecatedString const&) { return true; }), false);
101}
102
103TEST_CASE(case_insensitive)
104{
105 HashMap<DeprecatedString, int, CaseInsensitiveStringTraits> casemap;
106 EXPECT_EQ(DeprecatedString("nickserv").to_lowercase(), DeprecatedString("NickServ").to_lowercase());
107 EXPECT_EQ(casemap.set("nickserv", 3), AK::HashSetResult::InsertedNewEntry);
108 EXPECT_EQ(casemap.set("NickServ", 3), AK::HashSetResult::ReplacedExistingEntry);
109 EXPECT_EQ(casemap.size(), 1u);
110}
111
112TEST_CASE(case_insensitive_stringview)
113{
114 HashMap<StringView, int, CaseInsensitiveStringViewTraits> casemap;
115 EXPECT_EQ(casemap.set("nickserv"sv, 3), AK::HashSetResult::InsertedNewEntry);
116 EXPECT_EQ(casemap.set("NickServ"sv, 3), AK::HashSetResult::ReplacedExistingEntry);
117 EXPECT_EQ(casemap.size(), 1u);
118}
119
120TEST_CASE(hashmap_of_nonnullownptr_get)
121{
122 struct Object {
123 Object(DeprecatedString const& s)
124 : string(s)
125 {
126 }
127 DeprecatedString string;
128 };
129
130 HashMap<int, NonnullOwnPtr<Object>> objects;
131 objects.set(1, make<Object>("One"));
132 objects.set(2, make<Object>("Two"));
133 objects.set(3, make<Object>("Three"));
134
135 {
136 auto x = objects.get(2);
137 EXPECT_EQ(x.has_value(), true);
138 EXPECT_EQ(x.value()->string, "Two");
139 }
140
141 {
142 // Do it again to make sure that peeking into the map above didn't
143 // remove the value from the map.
144 auto x = objects.get(2);
145 EXPECT_EQ(x.has_value(), true);
146 EXPECT_EQ(x.value()->string, "Two");
147 }
148
149 EXPECT_EQ(objects.size(), 3u);
150}
151
152TEST_CASE(many_strings)
153{
154 HashMap<DeprecatedString, int> strings;
155 for (int i = 0; i < 999; ++i) {
156 EXPECT_EQ(strings.set(DeprecatedString::number(i), i), AK::HashSetResult::InsertedNewEntry);
157 }
158 EXPECT_EQ(strings.size(), 999u);
159 for (auto& it : strings) {
160 EXPECT_EQ(it.key.to_int().value(), it.value);
161 }
162 for (int i = 0; i < 999; ++i) {
163 EXPECT_EQ(strings.remove(DeprecatedString::number(i)), true);
164 }
165 EXPECT_EQ(strings.is_empty(), true);
166}
167
168TEST_CASE(basic_remove)
169{
170 HashMap<int, int> map;
171 map.set(1, 10);
172 map.set(2, 20);
173 map.set(3, 30);
174
175 EXPECT_EQ(map.remove(3), true);
176 EXPECT_EQ(map.remove(3), false);
177 EXPECT_EQ(map.size(), 2u);
178
179 EXPECT_EQ(map.remove(1), true);
180 EXPECT_EQ(map.remove(1), false);
181 EXPECT_EQ(map.size(), 1u);
182
183 EXPECT_EQ(map.remove(2), true);
184 EXPECT_EQ(map.remove(2), false);
185 EXPECT_EQ(map.size(), 0u);
186}
187
188TEST_CASE(basic_contains)
189{
190 HashMap<int, int> map;
191 map.set(1, 10);
192 map.set(2, 20);
193 map.set(3, 30);
194
195 EXPECT_EQ(map.contains(1), true);
196 EXPECT_EQ(map.contains(2), true);
197 EXPECT_EQ(map.contains(3), true);
198 EXPECT_EQ(map.contains(4), false);
199
200 EXPECT_EQ(map.remove(3), true);
201 EXPECT_EQ(map.contains(3), false);
202 EXPECT_EQ(map.contains(1), true);
203 EXPECT_EQ(map.contains(2), true);
204
205 EXPECT_EQ(map.remove(2), true);
206 EXPECT_EQ(map.contains(2), false);
207 EXPECT_EQ(map.contains(3), false);
208 EXPECT_EQ(map.contains(1), true);
209
210 EXPECT_EQ(map.remove(1), true);
211 EXPECT_EQ(map.contains(1), false);
212}
213
214TEST_CASE(in_place_rehashing_ordered_loop_bug)
215{
216 OrderedHashMap<DeprecatedString, DeprecatedString> map;
217 map.set("yt.innertube::nextId", "");
218 map.set("yt.innertube::requests", "");
219 map.remove("yt.innertube::nextId");
220 map.set("yt.innertube::nextId", "");
221 VERIFY(map.keys().size() == 2);
222}
223
224TEST_CASE(take)
225{
226 HashMap<String, int> map;
227
228 EXPECT(!map.take("foo"sv).has_value());
229 EXPECT(!map.take("bar"sv).has_value());
230 EXPECT(!map.take("baz"_short_string).has_value());
231
232 map.set("foo"_short_string, 1);
233 map.set("bar"_short_string, 2);
234 map.set("baz"_short_string, 3);
235
236 auto foo = map.take("foo"sv);
237 EXPECT_EQ(foo, 1);
238
239 foo = map.take("foo"sv);
240 EXPECT(!foo.has_value());
241
242 auto bar = map.take("bar"sv);
243 EXPECT_EQ(bar, 2);
244
245 bar = map.take("bar"sv);
246 EXPECT(!bar.has_value());
247
248 auto baz = map.take("baz"_short_string);
249 EXPECT_EQ(baz, 3);
250
251 baz = map.take("baz"_short_string);
252 EXPECT(!baz.has_value());
253}