blob: e96d70d8ad0aa32e8980967a1544b9f9482d52db [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 Laskowski54494bd2022-08-02 13:37:14 -070018#include <ftl/unit.h>
Dominik Laskowskic4b91462020-11-02 13:37:25 -080019#include <gtest/gtest.h>
20
21#include <cctype>
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080022#include <string>
Dominik Laskowski54494bd2022-08-02 13:37:14 -070023#include <string_view>
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080024
25using namespace std::string_literals;
Dominik Laskowski54494bd2022-08-02 13:37:14 -070026using namespace std::string_view_literals;
Dominik Laskowskic4b91462020-11-02 13:37:25 -080027
28namespace android::test {
29
30using ftl::SmallMap;
31
32// Keep in sync with example usage in header file.
33TEST(SmallMap, Example) {
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080034 ftl::SmallMap<int, std::string, 3> map;
35 EXPECT_TRUE(map.empty());
36 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080037
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080038 map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
39 EXPECT_EQ(map.size(), 3u);
40 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080041
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080042 EXPECT_TRUE(map.contains(123));
Dominik Laskowskic4b91462020-11-02 13:37:25 -080043
Dominik Laskowski54494bd2022-08-02 13:37:14 -070044 EXPECT_EQ(map.get(42).transform([](const std::string& s) { return s.size(); }), 3u);
Dominik Laskowskic4b91462020-11-02 13:37:25 -080045
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080046 const auto opt = map.get(-1);
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080047 ASSERT_TRUE(opt);
Dominik Laskowskic4b91462020-11-02 13:37:25 -080048
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080049 std::string& ref = *opt;
50 EXPECT_TRUE(ref.empty());
51 ref = "xyz";
Dominik Laskowskic4b91462020-11-02 13:37:25 -080052
Dominik Laskowskidda9bba2021-02-03 18:56:00 -080053 map.emplace_or_replace(0, "vanilla", 2u, 3u);
54 EXPECT_TRUE(map.dynamic());
55
Dominik Laskowski54494bd2022-08-02 13:37:14 -070056 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv)));
Dominik Laskowskic4b91462020-11-02 13:37:25 -080057}
58
59TEST(SmallMap, Construct) {
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080060 {
61 // Default constructor.
62 SmallMap<int, std::string, 2> map;
Dominik Laskowskic4b91462020-11-02 13:37:25 -080063
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080064 EXPECT_TRUE(map.empty());
65 EXPECT_FALSE(map.dynamic());
66 }
67 {
68 // In-place constructor with same types.
69 SmallMap<int, std::string, 5> map =
70 ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
Dominik Laskowskic4b91462020-11-02 13:37:25 -080071
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080072 EXPECT_EQ(map.size(), 3u);
73 EXPECT_EQ(map.max_size(), 5u);
74 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080075
Dominik Laskowski54494bd2022-08-02 13:37:14 -070076 EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc"sv)(456, "def"sv)(789, "ghi"sv)));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080077 }
78 {
79 // In-place constructor with different types.
80 SmallMap<int, std::string, 5> map =
81 ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
Dominik Laskowskic4b91462020-11-02 13:37:25 -080082
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080083 EXPECT_EQ(map.size(), 3u);
84 EXPECT_EQ(map.max_size(), 5u);
85 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080086
Dominik Laskowski54494bd2022-08-02 13:37:14 -070087 EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???"sv)(123, "abc"sv)(-1, ""sv)));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080088 }
89 {
90 // In-place constructor with implicit size.
91 SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
Dominik Laskowskic4b91462020-11-02 13:37:25 -080092
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080093 static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
94 EXPECT_EQ(map.size(), 3u);
95 EXPECT_EQ(map.max_size(), 3u);
96 EXPECT_FALSE(map.dynamic());
Dominik Laskowskic4b91462020-11-02 13:37:25 -080097
Dominik Laskowski54494bd2022-08-02 13:37:14 -070098 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""sv)(42, "???"sv)(123, "abc"sv)));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -080099 }
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800100}
101
Dominik Laskowski206996f2022-01-20 13:21:57 -0800102TEST(SmallMap, Assign) {
103 {
104 // Same types; smaller capacity.
105 SmallMap map1 = ftl::init::map<char, std::string>('k', "kilo")('M', "mega")('G', "giga");
106 const SmallMap map2 = ftl::init::map('T', "tera"s)('P', "peta"s);
107
108 map1 = map2;
109 EXPECT_EQ(map1, map2);
110 }
111 {
112 // Convertible types; same capacity.
113 SmallMap map1 = ftl::init::map<char, std::string>('M', "mega")('G', "giga");
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700114 const SmallMap map2 = ftl::init::map('T', "tera"sv)('P', "peta"sv);
Dominik Laskowski206996f2022-01-20 13:21:57 -0800115
116 map1 = map2;
117 EXPECT_EQ(map1, map2);
118 }
119 {
120 // Convertible types; zero capacity.
121 SmallMap<char, std::string, 0> map1 = ftl::init::map('M', "mega")('G', "giga");
122 const SmallMap<char, std::string, 0> map2 = ftl::init::map('T', "tera")('P', "peta");
123
124 map1 = map2;
125 EXPECT_EQ(map1, map2);
126 }
127}
128
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800129TEST(SmallMap, UniqueKeys) {
130 {
131 // Duplicate mappings are discarded.
132 const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1);
133
134 EXPECT_EQ(map.size(), 3u);
135 EXPECT_EQ(map.max_size(), 9u);
136
137 using Map = decltype(map);
138 EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f)));
139 }
140 {
141 // Duplicate mappings may be reordered.
142 const SmallMap map = ftl::init::map('a', 'A')(
143 'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F');
144
145 EXPECT_EQ(map.size(), 6u);
146 EXPECT_EQ(map.max_size(), 12u);
147
148 using Map = decltype(map);
149 EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F')));
150 }
151}
152
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700153TEST(SmallMap, Get) {
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800154 {
155 // Constant reference.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800156 const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800157
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800158 const auto opt = map.get('b');
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800159 EXPECT_EQ(opt, 'B');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800160
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800161 const char d = 'D';
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800162 const auto ref = map.get('d').value_or(std::cref(d));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800163 EXPECT_EQ(ref.get(), 'D');
164 }
165 {
166 // Mutable reference.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800167 SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800168
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800169 const auto opt = map.get('c');
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800170 EXPECT_EQ(opt, 'C');
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800171
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800172 char d = 'd';
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800173 const auto ref = map.get('d').value_or(std::ref(d));
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800174 ref.get() = 'D';
175 EXPECT_EQ(d, 'D');
176 }
177 {
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700178 // Immutable transform operation.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800179 const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700180 EXPECT_EQ(map.get('c').transform([](char c) { return std::toupper(c); }), 'Z');
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800181 }
182 {
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700183 // Mutable transform operation.
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800184 SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700185 EXPECT_EQ(map.get('c').transform(ftl::unit_fn([](char& c) { c = std::toupper(c); })),
186 ftl::unit);
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800187
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800188 EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
189 }
Dominik Laskowskic4b91462020-11-02 13:37:25 -0800190}
191
Dominik Laskowski70e45912022-04-18 08:12:19 -0700192template <typename Capacity>
193struct SmallMapTest : testing::Test {
194 static constexpr std::size_t kCapacity = Capacity{}();
195};
196
197template <std::size_t N>
198using Capacity = std::integral_constant<std::size_t, N>;
199
200using Capacities = testing::Types<Capacity<3>, Capacity<0>>;
201TYPED_TEST_SUITE(SmallMapTest, Capacities, );
202
203TYPED_TEST(SmallMapTest, TryEmplace) {
204 SmallMap<int, std::string, TestFixture::kCapacity> map;
205 using Pair = typename decltype(map)::value_type;
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800206
207 {
208 const auto [it, ok] = map.try_emplace(123, "abc");
209 ASSERT_TRUE(ok);
210 EXPECT_EQ(*it, Pair(123, "abc"s));
211 }
212 {
213 const auto [it, ok] = map.try_emplace(42, 3u, '?');
214 ASSERT_TRUE(ok);
215 EXPECT_EQ(*it, Pair(42, "???"s));
216 }
217 {
218 const auto [it, ok] = map.try_emplace(-1);
219 ASSERT_TRUE(ok);
220 EXPECT_EQ(*it, Pair(-1, std::string()));
Dominik Laskowski70e45912022-04-18 08:12:19 -0700221 if constexpr (map.static_capacity() > 0) {
222 EXPECT_FALSE(map.dynamic());
223 } else {
224 EXPECT_TRUE(map.dynamic());
225 }
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800226 }
227 {
228 // Insertion fails if mapping exists.
229 const auto [it, ok] = map.try_emplace(42, "!!!");
230 EXPECT_FALSE(ok);
231 EXPECT_EQ(*it, Pair(42, "???"));
Dominik Laskowski70e45912022-04-18 08:12:19 -0700232 if constexpr (map.static_capacity() > 0) {
233 EXPECT_FALSE(map.dynamic());
234 } else {
235 EXPECT_TRUE(map.dynamic());
236 }
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800237 }
238 {
239 // Insertion at capacity promotes the map.
240 const auto [it, ok] = map.try_emplace(999, "xyz");
241 ASSERT_TRUE(ok);
242 EXPECT_EQ(*it, Pair(999, "xyz"));
243 EXPECT_TRUE(map.dynamic());
244 }
245
246 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
247}
248
249namespace {
250
251// The mapped type does not require a copy/move assignment operator.
252struct String {
253 template <typename... Args>
254 String(Args... args) : str(args...) {}
255 const std::string str;
256
257 bool operator==(const String& other) const { return other.str == str; }
258};
259
260} // namespace
261
Dominik Laskowski70e45912022-04-18 08:12:19 -0700262TYPED_TEST(SmallMapTest, TryReplace) {
263 SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B");
264 using Pair = typename decltype(map)::value_type;
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800265
266 {
267 // Replacing fails unless mapping exists.
268 const auto it = map.try_replace(3, "c");
269 EXPECT_EQ(it, map.end());
270 }
271 {
272 // Replacement arguments can refer to the replaced mapping.
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700273 const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; });
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800274 ASSERT_TRUE(ref);
275
276 // Construct std::string from one character.
277 const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
278 ASSERT_NE(it, map.end());
279 EXPECT_EQ(*it, Pair(2, "b"));
280 }
281
Dominik Laskowski70e45912022-04-18 08:12:19 -0700282 if constexpr (map.static_capacity() > 0) {
283 EXPECT_FALSE(map.dynamic());
284 } else {
285 EXPECT_TRUE(map.dynamic());
286 }
287
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800288 EXPECT_TRUE(map.try_emplace(3, "abc").second);
289 EXPECT_TRUE(map.try_emplace(4, "d").second);
290 EXPECT_TRUE(map.dynamic());
291
292 {
293 // Replacing fails unless mapping exists.
294 const auto it = map.try_replace(5, "e");
295 EXPECT_EQ(it, map.end());
296 }
297 {
298 // Replacement arguments can refer to the replaced mapping.
299 const auto ref = map.get(3);
300 ASSERT_TRUE(ref);
301
302 // Construct std::string from substring.
303 const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
304 ASSERT_NE(it, map.end());
305 EXPECT_EQ(*it, Pair(3, "c"));
306 }
307
308 EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
309}
310
Dominik Laskowski70e45912022-04-18 08:12:19 -0700311TYPED_TEST(SmallMapTest, EmplaceOrReplace) {
312 SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B");
313 using Pair = typename decltype(map)::value_type;
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800314
315 {
316 // New mapping is emplaced.
317 const auto [it, emplace] = map.emplace_or_replace(3, "c");
318 EXPECT_TRUE(emplace);
319 EXPECT_EQ(*it, Pair(3, "c"));
320 }
321 {
322 // Replacement arguments can refer to the replaced mapping.
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700323 const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; });
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800324 ASSERT_TRUE(ref);
325
326 // Construct std::string from one character.
327 const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
328 EXPECT_FALSE(emplace);
329 EXPECT_EQ(*it, Pair(2, "b"));
330 }
331
Dominik Laskowski70e45912022-04-18 08:12:19 -0700332 if constexpr (map.static_capacity() > 0) {
333 EXPECT_FALSE(map.dynamic());
334 } else {
335 EXPECT_TRUE(map.dynamic());
336 }
337
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800338 EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace.
339 EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace.
340 EXPECT_TRUE(map.dynamic());
341
342 {
343 // New mapping is emplaced.
344 const auto [it, emplace] = map.emplace_or_replace(5, "e");
345 EXPECT_TRUE(emplace);
346 EXPECT_EQ(*it, Pair(5, "e"));
347 }
348 {
349 // Replacement arguments can refer to the replaced mapping.
350 const auto ref = map.get(3);
351 ASSERT_TRUE(ref);
352
353 // Construct std::string from substring.
354 const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
355 EXPECT_FALSE(emplace);
356 EXPECT_EQ(*it, Pair(3, "c"));
357 }
358
359 EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
360}
361
362TEST(SmallMap, Erase) {
363 {
364 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
365 EXPECT_FALSE(map.dynamic());
366
367 EXPECT_FALSE(map.erase(0)); // Key not found.
368
369 EXPECT_TRUE(map.erase(2));
370 EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
371
372 EXPECT_TRUE(map.erase(1));
373 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
374
375 EXPECT_TRUE(map.erase(4));
376 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
377
378 EXPECT_TRUE(map.erase(3));
379 EXPECT_FALSE(map.erase(3)); // Key not found.
380
381 EXPECT_TRUE(map.empty());
382 EXPECT_FALSE(map.dynamic());
383 }
384 {
385 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
386 map.try_emplace(4, '4');
387 EXPECT_TRUE(map.dynamic());
388
389 EXPECT_FALSE(map.erase(0)); // Key not found.
390
391 EXPECT_TRUE(map.erase(2));
392 EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
393
394 EXPECT_TRUE(map.erase(1));
395 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
396
397 EXPECT_TRUE(map.erase(4));
398 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
399
400 EXPECT_TRUE(map.erase(3));
401 EXPECT_FALSE(map.erase(3)); // Key not found.
402
403 EXPECT_TRUE(map.empty());
404 EXPECT_TRUE(map.dynamic());
405 }
406}
407
Dominik Laskowski44828ce2021-09-13 11:00:22 -0700408TEST(SmallMap, Clear) {
409 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
410
411 map.clear();
412
413 EXPECT_TRUE(map.empty());
414 EXPECT_FALSE(map.dynamic());
415
416 map = ftl::init::map(1, '1')(2, '2')(3, '3');
417 map.try_emplace(4, '4');
418
419 map.clear();
420
421 EXPECT_TRUE(map.empty());
422 EXPECT_TRUE(map.dynamic());
423}
424
Dominik Laskowski18748962021-09-26 18:47:58 -0700425TEST(SmallMap, KeyEqual) {
426 struct KeyEqual {
427 bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
428 };
429
430 SmallMap<int, char, 1, KeyEqual> map;
431
432 EXPECT_TRUE(map.try_emplace(3, '3').second);
433 EXPECT_FALSE(map.try_emplace(13, '3').second);
434
435 EXPECT_TRUE(map.try_emplace(22, '2').second);
436 EXPECT_TRUE(map.contains(42));
437
438 EXPECT_TRUE(map.try_emplace(111, '1').second);
439 EXPECT_EQ(map.get(321), '1');
440
441 map.erase(123);
442 EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
443}
444
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800445} // namespace android::test