blob: ee650e5627c87227a7ea4378e17ab32bc41f39dd [file] [log] [blame]
Dominik Laskowskic4b91462020-11-02 13:37:25 -08001/*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Dominik Laskowski5444fc82020-11-24 13:41:10 -080017#include <ftl/small_map.h>
Dominik Laskowskic4b91462020-11-02 13:37:25 -080018#include <gtest/gtest.h>
19
20#include <cctype>
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080021#include <string>
22
23using namespace std::string_literals;
Dominik Laskowskic4b91462020-11-02 13:37:25 -080024
25namespace android::test {
26
27using ftl::SmallMap;
28
29// Keep in sync with example usage in header file.
30TEST(SmallMap, Example) {
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080031 ftl::SmallMap<int, std::string, 3> map;
32 EXPECT_TRUE(map.empty());
33 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080034
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080035 map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
36 EXPECT_EQ(map.size(), 3u);
37 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080038
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080039 EXPECT_TRUE(map.contains(123));
Dominik Laskowskic4b91462020-11-02 13:37:25 -080040
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080041 EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u);
Dominik Laskowskic4b91462020-11-02 13:37:25 -080042
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080043 const auto opt = map.get(-1);
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080044 ASSERT_TRUE(opt);
Dominik Laskowskic4b91462020-11-02 13:37:25 -080045
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080046 std::string& ref = *opt;
47 EXPECT_TRUE(ref.empty());
48 ref = "xyz";
Dominik Laskowskic4b91462020-11-02 13:37:25 -080049
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080050 map.emplace_or_replace(0, "vanilla", 2u, 3u);
51 EXPECT_TRUE(map.dynamic());
52
53 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
Dominik Laskowskic4b91462020-11-02 13:37:25 -080054}
55
56TEST(SmallMap, Construct) {
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080057 {
58 // Default constructor.
59 SmallMap<int, std::string, 2> map;
Dominik Laskowskic4b91462020-11-02 13:37:25 -080060
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080061 EXPECT_TRUE(map.empty());
62 EXPECT_FALSE(map.dynamic());
63 }
64 {
65 // In-place constructor with same types.
66 SmallMap<int, std::string, 5> map =
67 ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
Dominik Laskowskic4b91462020-11-02 13:37:25 -080068
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080069 EXPECT_EQ(map.size(), 3u);
70 EXPECT_EQ(map.max_size(), 5u);
71 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080072
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080073 EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
74 }
75 {
76 // In-place constructor with different types.
77 SmallMap<int, std::string, 5> map =
78 ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
Dominik Laskowskic4b91462020-11-02 13:37:25 -080079
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080080 EXPECT_EQ(map.size(), 3u);
81 EXPECT_EQ(map.max_size(), 5u);
82 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080083
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080084 EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
85 }
86 {
87 // In-place constructor with implicit size.
88 SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
Dominik Laskowskic4b91462020-11-02 13:37:25 -080089
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080090 static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
91 EXPECT_EQ(map.size(), 3u);
92 EXPECT_EQ(map.max_size(), 3u);
93 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080094
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080095 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
96 }
Dominik Laskowskic4b91462020-11-02 13:37:25 -080097}
98
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080099TEST(SmallMap, UniqueKeys) {
100 {
101 // Duplicate mappings are discarded.
102 const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1);
103
104 EXPECT_EQ(map.size(), 3u);
105 EXPECT_EQ(map.max_size(), 9u);
106
107 using Map = decltype(map);
108 EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f)));
109 }
110 {
111 // Duplicate mappings may be reordered.
112 const SmallMap map = ftl::init::map('a', 'A')(
113 'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F');
114
115 EXPECT_EQ(map.size(), 6u);
116 EXPECT_EQ(map.max_size(), 12u);
117
118 using Map = decltype(map);
119 EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F')));
120 }
121}
122
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800123TEST(SmallMap, Find) {
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800124 {
125 // Constant reference.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800126 const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800127
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800128 const auto opt = map.get('b');
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800129 EXPECT_EQ(opt, 'B');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800130
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800131 const char d = 'D';
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800132 const auto ref = map.get('d').value_or(std::cref(d));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800133 EXPECT_EQ(ref.get(), 'D');
134 }
135 {
136 // Mutable reference.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800137 SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800138
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800139 const auto opt = map.get('c');
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800140 EXPECT_EQ(opt, 'C');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800141
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800142 char d = 'd';
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800143 const auto ref = map.get('d').value_or(std::ref(d));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800144 ref.get() = 'D';
145 EXPECT_EQ(d, 'D');
146 }
147 {
148 // Constant unary operation.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800149 const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
150 EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800151 }
152 {
153 // Mutable unary operation.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800154 SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
155 EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); }));
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800156
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800157 EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
158 }
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800159}
160
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800161TEST(SmallMap, TryEmplace) {
162 SmallMap<int, std::string, 3> map;
163 using Pair = decltype(map)::value_type;
164
165 {
166 const auto [it, ok] = map.try_emplace(123, "abc");
167 ASSERT_TRUE(ok);
168 EXPECT_EQ(*it, Pair(123, "abc"s));
169 }
170 {
171 const auto [it, ok] = map.try_emplace(42, 3u, '?');
172 ASSERT_TRUE(ok);
173 EXPECT_EQ(*it, Pair(42, "???"s));
174 }
175 {
176 const auto [it, ok] = map.try_emplace(-1);
177 ASSERT_TRUE(ok);
178 EXPECT_EQ(*it, Pair(-1, std::string()));
179 EXPECT_FALSE(map.dynamic());
180 }
181 {
182 // Insertion fails if mapping exists.
183 const auto [it, ok] = map.try_emplace(42, "!!!");
184 EXPECT_FALSE(ok);
185 EXPECT_EQ(*it, Pair(42, "???"));
186 EXPECT_FALSE(map.dynamic());
187 }
188 {
189 // Insertion at capacity promotes the map.
190 const auto [it, ok] = map.try_emplace(999, "xyz");
191 ASSERT_TRUE(ok);
192 EXPECT_EQ(*it, Pair(999, "xyz"));
193 EXPECT_TRUE(map.dynamic());
194 }
195
196 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
197}
198
199namespace {
200
201// The mapped type does not require a copy/move assignment operator.
202struct String {
203 template <typename... Args>
204 String(Args... args) : str(args...) {}
205 const std::string str;
206
207 bool operator==(const String& other) const { return other.str == str; }
208};
209
210} // namespace
211
212TEST(SmallMap, TryReplace) {
213 SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
214 using Pair = decltype(map)::value_type;
215
216 {
217 // Replacing fails unless mapping exists.
218 const auto it = map.try_replace(3, "c");
219 EXPECT_EQ(it, map.end());
220 }
221 {
222 // Replacement arguments can refer to the replaced mapping.
223 const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
224 ASSERT_TRUE(ref);
225
226 // Construct std::string from one character.
227 const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
228 ASSERT_NE(it, map.end());
229 EXPECT_EQ(*it, Pair(2, "b"));
230 }
231
232 EXPECT_FALSE(map.dynamic());
233 EXPECT_TRUE(map.try_emplace(3, "abc").second);
234 EXPECT_TRUE(map.try_emplace(4, "d").second);
235 EXPECT_TRUE(map.dynamic());
236
237 {
238 // Replacing fails unless mapping exists.
239 const auto it = map.try_replace(5, "e");
240 EXPECT_EQ(it, map.end());
241 }
242 {
243 // Replacement arguments can refer to the replaced mapping.
244 const auto ref = map.get(3);
245 ASSERT_TRUE(ref);
246
247 // Construct std::string from substring.
248 const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
249 ASSERT_NE(it, map.end());
250 EXPECT_EQ(*it, Pair(3, "c"));
251 }
252
253 EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
254}
255
256TEST(SmallMap, EmplaceOrReplace) {
257 SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
258 using Pair = decltype(map)::value_type;
259
260 {
261 // New mapping is emplaced.
262 const auto [it, emplace] = map.emplace_or_replace(3, "c");
263 EXPECT_TRUE(emplace);
264 EXPECT_EQ(*it, Pair(3, "c"));
265 }
266 {
267 // Replacement arguments can refer to the replaced mapping.
268 const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
269 ASSERT_TRUE(ref);
270
271 // Construct std::string from one character.
272 const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
273 EXPECT_FALSE(emplace);
274 EXPECT_EQ(*it, Pair(2, "b"));
275 }
276
277 EXPECT_FALSE(map.dynamic());
278 EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace.
279 EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace.
280 EXPECT_TRUE(map.dynamic());
281
282 {
283 // New mapping is emplaced.
284 const auto [it, emplace] = map.emplace_or_replace(5, "e");
285 EXPECT_TRUE(emplace);
286 EXPECT_EQ(*it, Pair(5, "e"));
287 }
288 {
289 // Replacement arguments can refer to the replaced mapping.
290 const auto ref = map.get(3);
291 ASSERT_TRUE(ref);
292
293 // Construct std::string from substring.
294 const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
295 EXPECT_FALSE(emplace);
296 EXPECT_EQ(*it, Pair(3, "c"));
297 }
298
299 EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
300}
301
302TEST(SmallMap, Erase) {
303 {
304 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
305 EXPECT_FALSE(map.dynamic());
306
307 EXPECT_FALSE(map.erase(0)); // Key not found.
308
309 EXPECT_TRUE(map.erase(2));
310 EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
311
312 EXPECT_TRUE(map.erase(1));
313 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
314
315 EXPECT_TRUE(map.erase(4));
316 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
317
318 EXPECT_TRUE(map.erase(3));
319 EXPECT_FALSE(map.erase(3)); // Key not found.
320
321 EXPECT_TRUE(map.empty());
322 EXPECT_FALSE(map.dynamic());
323 }
324 {
325 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
326 map.try_emplace(4, '4');
327 EXPECT_TRUE(map.dynamic());
328
329 EXPECT_FALSE(map.erase(0)); // Key not found.
330
331 EXPECT_TRUE(map.erase(2));
332 EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
333
334 EXPECT_TRUE(map.erase(1));
335 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
336
337 EXPECT_TRUE(map.erase(4));
338 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
339
340 EXPECT_TRUE(map.erase(3));
341 EXPECT_FALSE(map.erase(3)); // Key not found.
342
343 EXPECT_TRUE(map.empty());
344 EXPECT_TRUE(map.dynamic());
345 }
346}
347
Dominik Laskowski44828ce2021-09-13 11:00:22 -0700348TEST(SmallMap, Clear) {
349 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
350
351 map.clear();
352
353 EXPECT_TRUE(map.empty());
354 EXPECT_FALSE(map.dynamic());
355
356 map = ftl::init::map(1, '1')(2, '2')(3, '3');
357 map.try_emplace(4, '4');
358
359 map.clear();
360
361 EXPECT_TRUE(map.empty());
362 EXPECT_TRUE(map.dynamic());
363}
364
Dominik Laskowski18748962021-09-26 18:47:58 -0700365TEST(SmallMap, KeyEqual) {
366 struct KeyEqual {
367 bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
368 };
369
370 SmallMap<int, char, 1, KeyEqual> map;
371
372 EXPECT_TRUE(map.try_emplace(3, '3').second);
373 EXPECT_FALSE(map.try_emplace(13, '3').second);
374
375 EXPECT_TRUE(map.try_emplace(22, '2').second);
376 EXPECT_TRUE(map.contains(42));
377
378 EXPECT_TRUE(map.try_emplace(111, '1').second);
379 EXPECT_EQ(map.get(321), '1');
380
381 map.erase(123);
382 EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
383}
384
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800385} // namespace android::test