blob: 634877f67207a4a1da60f4aed058b3d763cc363a [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 Laskowskidda9bba2021-02-03 18:56:00 -0800192TEST(SmallMap, TryEmplace) {
193 SmallMap<int, std::string, 3> map;
194 using Pair = decltype(map)::value_type;
195
196 {
197 const auto [it, ok] = map.try_emplace(123, "abc");
198 ASSERT_TRUE(ok);
199 EXPECT_EQ(*it, Pair(123, "abc"s));
200 }
201 {
202 const auto [it, ok] = map.try_emplace(42, 3u, '?');
203 ASSERT_TRUE(ok);
204 EXPECT_EQ(*it, Pair(42, "???"s));
205 }
206 {
207 const auto [it, ok] = map.try_emplace(-1);
208 ASSERT_TRUE(ok);
209 EXPECT_EQ(*it, Pair(-1, std::string()));
210 EXPECT_FALSE(map.dynamic());
211 }
212 {
213 // Insertion fails if mapping exists.
214 const auto [it, ok] = map.try_emplace(42, "!!!");
215 EXPECT_FALSE(ok);
216 EXPECT_EQ(*it, Pair(42, "???"));
217 EXPECT_FALSE(map.dynamic());
218 }
219 {
220 // Insertion at capacity promotes the map.
221 const auto [it, ok] = map.try_emplace(999, "xyz");
222 ASSERT_TRUE(ok);
223 EXPECT_EQ(*it, Pair(999, "xyz"));
224 EXPECT_TRUE(map.dynamic());
225 }
226
227 EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
228}
229
230namespace {
231
232// The mapped type does not require a copy/move assignment operator.
233struct String {
234 template <typename... Args>
235 String(Args... args) : str(args...) {}
236 const std::string str;
237
238 bool operator==(const String& other) const { return other.str == str; }
239};
240
241} // namespace
242
243TEST(SmallMap, TryReplace) {
244 SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
245 using Pair = decltype(map)::value_type;
246
247 {
248 // Replacing fails unless mapping exists.
249 const auto it = map.try_replace(3, "c");
250 EXPECT_EQ(it, map.end());
251 }
252 {
253 // Replacement arguments can refer to the replaced mapping.
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700254 const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; });
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800255 ASSERT_TRUE(ref);
256
257 // Construct std::string from one character.
258 const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
259 ASSERT_NE(it, map.end());
260 EXPECT_EQ(*it, Pair(2, "b"));
261 }
262
263 EXPECT_FALSE(map.dynamic());
264 EXPECT_TRUE(map.try_emplace(3, "abc").second);
265 EXPECT_TRUE(map.try_emplace(4, "d").second);
266 EXPECT_TRUE(map.dynamic());
267
268 {
269 // Replacing fails unless mapping exists.
270 const auto it = map.try_replace(5, "e");
271 EXPECT_EQ(it, map.end());
272 }
273 {
274 // Replacement arguments can refer to the replaced mapping.
275 const auto ref = map.get(3);
276 ASSERT_TRUE(ref);
277
278 // Construct std::string from substring.
279 const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
280 ASSERT_NE(it, map.end());
281 EXPECT_EQ(*it, Pair(3, "c"));
282 }
283
284 EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
285}
286
287TEST(SmallMap, EmplaceOrReplace) {
288 SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
289 using Pair = decltype(map)::value_type;
290
291 {
292 // New mapping is emplaced.
293 const auto [it, emplace] = map.emplace_or_replace(3, "c");
294 EXPECT_TRUE(emplace);
295 EXPECT_EQ(*it, Pair(3, "c"));
296 }
297 {
298 // Replacement arguments can refer to the replaced mapping.
Dominik Laskowski54494bd2022-08-02 13:37:14 -0700299 const auto ref = map.get(2).transform([](const String& s) { return s.str[0]; });
Dominik Laskowskidda9bba2021-02-03 18:56:00 -0800300 ASSERT_TRUE(ref);
301
302 // Construct std::string from one character.
303 const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
304 EXPECT_FALSE(emplace);
305 EXPECT_EQ(*it, Pair(2, "b"));
306 }
307
308 EXPECT_FALSE(map.dynamic());
309 EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace.
310 EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace.
311 EXPECT_TRUE(map.dynamic());
312
313 {
314 // New mapping is emplaced.
315 const auto [it, emplace] = map.emplace_or_replace(5, "e");
316 EXPECT_TRUE(emplace);
317 EXPECT_EQ(*it, Pair(5, "e"));
318 }
319 {
320 // Replacement arguments can refer to the replaced mapping.
321 const auto ref = map.get(3);
322 ASSERT_TRUE(ref);
323
324 // Construct std::string from substring.
325 const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
326 EXPECT_FALSE(emplace);
327 EXPECT_EQ(*it, Pair(3, "c"));
328 }
329
330 EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
331}
332
333TEST(SmallMap, Erase) {
334 {
335 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
336 EXPECT_FALSE(map.dynamic());
337
338 EXPECT_FALSE(map.erase(0)); // Key not found.
339
340 EXPECT_TRUE(map.erase(2));
341 EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
342
343 EXPECT_TRUE(map.erase(1));
344 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
345
346 EXPECT_TRUE(map.erase(4));
347 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
348
349 EXPECT_TRUE(map.erase(3));
350 EXPECT_FALSE(map.erase(3)); // Key not found.
351
352 EXPECT_TRUE(map.empty());
353 EXPECT_FALSE(map.dynamic());
354 }
355 {
356 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
357 map.try_emplace(4, '4');
358 EXPECT_TRUE(map.dynamic());
359
360 EXPECT_FALSE(map.erase(0)); // Key not found.
361
362 EXPECT_TRUE(map.erase(2));
363 EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
364
365 EXPECT_TRUE(map.erase(1));
366 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
367
368 EXPECT_TRUE(map.erase(4));
369 EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
370
371 EXPECT_TRUE(map.erase(3));
372 EXPECT_FALSE(map.erase(3)); // Key not found.
373
374 EXPECT_TRUE(map.empty());
375 EXPECT_TRUE(map.dynamic());
376 }
377}
378
Dominik Laskowski44828ce2021-09-13 11:00:22 -0700379TEST(SmallMap, Clear) {
380 SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
381
382 map.clear();
383
384 EXPECT_TRUE(map.empty());
385 EXPECT_FALSE(map.dynamic());
386
387 map = ftl::init::map(1, '1')(2, '2')(3, '3');
388 map.try_emplace(4, '4');
389
390 map.clear();
391
392 EXPECT_TRUE(map.empty());
393 EXPECT_TRUE(map.dynamic());
394}
395
Dominik Laskowski18748962021-09-26 18:47:58 -0700396TEST(SmallMap, KeyEqual) {
397 struct KeyEqual {
398 bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
399 };
400
401 SmallMap<int, char, 1, KeyEqual> map;
402
403 EXPECT_TRUE(map.try_emplace(3, '3').second);
404 EXPECT_FALSE(map.try_emplace(13, '3').second);
405
406 EXPECT_TRUE(map.try_emplace(22, '2').second);
407 EXPECT_TRUE(map.contains(42));
408
409 EXPECT_TRUE(map.try_emplace(111, '1').second);
410 EXPECT_EQ(map.get(321), '1');
411
412 map.erase(123);
413 EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
414}
415
Dominik Laskowskie21dbed2020-12-04 20:51:43 -0800416} // namespace android::test