1 /** @file
2
3 IpMap unit tests.
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include "tscore/IpMap.h"
25 #include <sstream>
26 #include <catch.hpp>
27 #include <tscore/BufferWriter.h>
28
29 std::ostream &
operator <<(std::ostream & s,IpEndpoint const & addr)30 operator<<(std::ostream &s, IpEndpoint const &addr)
31 {
32 ip_text_buffer b;
33 ats_ip_ntop(addr, b, sizeof(b));
34 s << b;
35 return s;
36 }
37
38 void
IpMapTestPrint(IpMap & map)39 IpMapTestPrint(IpMap &map)
40 {
41 printf("IpMap Dump\n");
42 for (auto &spot : map) {
43 ip_text_buffer ipb1, ipb2;
44
45 printf("%s - %s : %p\n", ats_ip_ntop(spot.min(), ipb1, sizeof ipb1), ats_ip_ntop(spot.max(), ipb2, sizeof(ipb2)), spot.data());
46 }
47 printf("\n");
48 }
49
50 // --- Test helper classes ---
51 class MapMarkedAt : public Catch::MatcherBase<IpMap>
52 {
53 IpEndpoint const &_addr;
54
55 public:
MapMarkedAt(IpEndpoint const & addr)56 MapMarkedAt(IpEndpoint const &addr) : _addr(addr) {}
57
58 bool
match(IpMap const & map) const59 match(IpMap const &map) const override
60 {
61 return map.contains(&_addr);
62 }
63
64 std::string
describe() const65 describe() const override
66 {
67 std::ostringstream ss;
68 ss << _addr << " is marked";
69 return ss.str();
70 }
71 };
72
73 // The builder function
74 inline MapMarkedAt
IsMarkedAt(IpEndpoint const & _addr)75 IsMarkedAt(IpEndpoint const &_addr)
76 {
77 return {_addr};
78 }
79
80 class MapMarkedWith : public Catch::MatcherBase<IpMap>
81 {
82 IpEndpoint const &_addr;
83 void *_mark;
84 mutable bool _found_p = false;
85
86 public:
MapMarkedWith(IpEndpoint const & addr,void * mark)87 MapMarkedWith(IpEndpoint const &addr, void *mark) : _addr(addr), _mark(mark) {}
88
89 bool
match(IpMap const & map) const90 match(IpMap const &map) const override
91 {
92 void *mark = nullptr;
93 return (_found_p = map.contains(&_addr, &mark)) && mark == _mark;
94 }
95
96 std::string
describe() const97 describe() const override
98 {
99 std::ostringstream ss;
100 if (_found_p) {
101 ss << "is marked at " << _addr << " with " << std::hex << reinterpret_cast<intptr_t>(_mark);
102 } else {
103 ss << "is not marked at " << _addr;
104 }
105 return ss.str();
106 }
107 };
108
109 inline MapMarkedWith
IsMarkedWith(IpEndpoint const & addr,void * mark)110 IsMarkedWith(IpEndpoint const &addr, void *mark)
111 {
112 return {addr, mark};
113 }
114
115 // -------------
116 // --- TESTS ---
117 // -------------
118 TEST_CASE("IpMap Basic", "[libts][ipmap]")
119 {
120 IpMap map;
121 void *const markA = reinterpret_cast<void *>(1);
122 void *const markB = reinterpret_cast<void *>(2);
123 void *const markC = reinterpret_cast<void *>(3);
124 void *mark; // for retrieval
125
126 in_addr_t ip5 = htonl(5), ip9 = htonl(9);
127 in_addr_t ip10 = htonl(10), ip15 = htonl(15), ip20 = htonl(20);
128 in_addr_t ip50 = htonl(50), ip60 = htonl(60);
129 in_addr_t ip100 = htonl(100), ip120 = htonl(120), ip140 = htonl(140);
130 in_addr_t ip150 = htonl(150), ip160 = htonl(160);
131 in_addr_t ip200 = htonl(200);
132 in_addr_t ip0 = 0;
133 in_addr_t ipmax = ~static_cast<in_addr_t>(0);
134
135 map.mark(ip10, ip20, markA);
136 map.mark(ip5, ip9, markA);
137 {
138 INFO("Coalesce failed");
139 CHECK(map.count() == 1);
140 }
141 {
142 INFO("Range max not found.");
143 CHECK(map.contains(ip9));
144 }
145 {
146 INFO("Span min not found");
147 CHECK(map.contains(ip10, &mark));
148 }
149 {
150 INFO("Mark not preserved.");
151 CHECK(mark == markA);
152 }
153
154 map.fill(ip15, ip100, markB);
155 {
156 INFO("Fill failed.");
157 CHECK(map.count() == 2);
158 }
159 {
160 INFO("fill interior missing");
161 CHECK(map.contains(ip50, &mark));
162 }
163 {
164 INFO("Fill mark not preserved.");
165 CHECK(mark == markB);
166 }
167 {
168 INFO("Span min not found.");
169 CHECK(!map.contains(ip200));
170 }
171 {
172 INFO("Old span interior not found");
173 CHECK(map.contains(ip15, &mark));
174 }
175 {
176 INFO("Fill overwrote mark.");
177 CHECK(mark == markA);
178 }
179
180 map.clear();
181 {
182 INFO("Clear failed.");
183 CHECK(map.count() == 0);
184 }
185
186 map.mark(ip20, ip50, markA);
187 map.mark(ip100, ip150, markB);
188 map.fill(ip10, ip200, markC);
189 CHECK(map.count() == 5);
190 {
191 INFO("Left span missing");
192 CHECK(map.contains(ip15, &mark));
193 }
194 {
195 INFO("Middle span missing");
196 CHECK(map.contains(ip60, &mark));
197 }
198 {
199 INFO("fill mark wrong.");
200 CHECK(mark == markC);
201 }
202 {
203 INFO("right span missing.");
204 CHECK(map.contains(ip160));
205 }
206 {
207 INFO("right span missing");
208 CHECK(map.contains(ip120, &mark));
209 }
210 {
211 INFO("wrong data on right mark span.");
212 CHECK(mark == markB);
213 }
214
215 map.unmark(ip140, ip160);
216 {
217 INFO("unmark failed");
218 CHECK(map.count() == 5);
219 }
220 {
221 INFO("unmark left edge still there.");
222 CHECK(!map.contains(ip140));
223 }
224 {
225 INFO("unmark middle still there.");
226 CHECK(!map.contains(ip150));
227 }
228 {
229 INFO("unmark right edge still there.");
230 CHECK(!map.contains(ip160));
231 }
232
233 map.clear();
234 map.mark(ip20, ip20, markA);
235 {
236 INFO("Map failed on singleton insert");
237 CHECK(map.contains(ip20));
238 }
239 map.mark(ip10, ip200, markB);
240 mark = 0;
241 map.contains(ip20, &mark);
242 {
243 INFO("Map held singleton against range.");
244 CHECK(mark == markB);
245 }
246 map.mark(ip100, ip120, markA);
247 map.mark(ip150, ip160, markB);
248 map.mark(ip0, ipmax, markC);
249 {
250 INFO("IpMap: Full range fill left extra ranges.");
251 CHECK(map.count() == 1);
252 }
253 }
254
255 TEST_CASE("IpMap Unmark", "[libts][ipmap]")
256 {
257 IpMap map;
258 // ip_text_buffer ipb1, ipb2;
259 void *const markA = reinterpret_cast<void *>(1);
260
261 IpEndpoint a_0, a_0_0_0_16, a_0_0_0_17, a_max;
262 IpEndpoint a_10_28_56_0, a_10_28_56_4, a_10_28_56_255;
263 IpEndpoint a_10_28_55_255, a_10_28_57_0;
264 IpEndpoint a_63_128_1_12;
265 IpEndpoint a_loopback, a_loopback2;
266 IpEndpoint a6_0, a6_max, a6_fe80_9d90, a6_fe80_9d9d, a6_fe80_9d95;
267
268 ats_ip_pton("0.0.0.0", &a_0);
269 ats_ip_pton("0.0.0.16", &a_0_0_0_16);
270 ats_ip_pton("0.0.0.17", &a_0_0_0_17);
271 ats_ip_pton("255.255.255.255", &a_max);
272 ats_ip_pton("10.28.55.255", &a_10_28_55_255);
273 ats_ip_pton("10.28.56.0", &a_10_28_56_0);
274 ats_ip_pton("10.28.56.4", &a_10_28_56_4);
275 ats_ip_pton("10.28.56.255", &a_10_28_56_255);
276 ats_ip_pton("10.28.57.0", &a_10_28_57_0);
277 ats_ip_pton("63.128.1.12", &a_63_128_1_12);
278 ats_ip_pton("::", &a6_0);
279 ats_ip_pton("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &a6_max);
280 ats_ip_pton("fe80::221:9bff:fe10:9d90", &a6_fe80_9d90);
281 ats_ip_pton("fe80::221:9bff:fe10:9d9d", &a6_fe80_9d9d);
282 ats_ip_pton("fe80::221:9bff:fe10:9d95", &a6_fe80_9d95);
283 ats_ip_pton("127.0.0.1", &a_loopback);
284 ats_ip_pton("127.0.0.255", &a_loopback2);
285
286 map.mark(&a_0, &a_max, markA);
287 {
288 INFO("IpMap Unmark: Full range not single.");
289 CHECK(map.count() == 1);
290 }
291 map.unmark(&a_10_28_56_0, &a_10_28_56_255);
292 {
293 INFO("IpMap Unmark: Range unmark failed.");
294 CHECK(map.count() == 2);
295 }
296 // Generic range check.
297 {
298 INFO("IpMap Unmark: Range unmark min address not removed.");
299 CHECK(!map.contains(&a_10_28_56_0));
300 }
301 {
302 INFO("IpMap Unmark: Range unmark max address not removed.");
303 CHECK(!map.contains(&a_10_28_56_255));
304 }
305 {
306 INFO("IpMap Unmark: Range unmark min-1 address removed.");
307 CHECK(map.contains(&a_10_28_55_255));
308 }
309 {
310 INFO("IpMap Unmark: Range unmark max+1 address removed.");
311 CHECK(map.contains(&a_10_28_57_0));
312 }
313 // Test min bounded range.
314 map.unmark(&a_0, &a_0_0_0_16);
315 {
316 INFO("IpMap Unmark: Range unmark zero address not removed.");
317 CHECK(!map.contains(&a_0));
318 }
319 {
320 INFO("IpMap Unmark: Range unmark zero bounded range max not removed.");
321 CHECK(!map.contains(&a_0_0_0_16));
322 }
323 {
324 INFO("IpMap Unmark: Range unmark zero bounded range max+1 removed.");
325 CHECK(map.contains(&a_0_0_0_17));
326 }
327 }
328
329 TEST_CASE("IpMap Fill", "[libts][ipmap]")
330 {
331 IpMap map;
332 void *const allow = reinterpret_cast<void *>(0);
333 void *const deny = reinterpret_cast<void *>(~0);
334 void *const markA = reinterpret_cast<void *>(1);
335 void *const markB = reinterpret_cast<void *>(2);
336 void *const markC = reinterpret_cast<void *>(3);
337
338 IpEndpoint a0, a_10_28_56_0, a_10_28_56_4, a_10_28_56_255, a3, a4;
339 IpEndpoint a_9_255_255_255, a_10_0_0_0, a_10_0_0_19, a_10_0_0_255, a_10_0_1_0;
340 IpEndpoint a_max, a_loopback, a_loopback2;
341 IpEndpoint a_10_28_55_255, a_10_28_57_0;
342 IpEndpoint a_63_128_1_12;
343 IpEndpoint a_0000_0000, a_0000_0001, a_ffff_ffff;
344 IpEndpoint a_fe80_9d8f, a_fe80_9d90, a_fe80_9d95, a_fe80_9d9d, a_fe80_9d9e;
345
346 ats_ip_pton("0.0.0.0", &a0);
347 ats_ip_pton("255.255.255.255", &a_max);
348
349 ats_ip_pton("9.255.255.255", &a_9_255_255_255);
350 ats_ip_pton("10.0.0.0", &a_10_0_0_0);
351 ats_ip_pton("10.0.0.19", &a_10_0_0_19);
352 ats_ip_pton("10.0.0.255", &a_10_0_0_255);
353 ats_ip_pton("10.0.1.0", &a_10_0_1_0);
354
355 ats_ip_pton("10.28.55.255", &a_10_28_55_255);
356 ats_ip_pton("10.28.56.0", &a_10_28_56_0);
357 ats_ip_pton("10.28.56.4", &a_10_28_56_4);
358 ats_ip_pton("10.28.56.255", &a_10_28_56_255);
359 ats_ip_pton("10.28.57.0", &a_10_28_57_0);
360
361 ats_ip_pton("192.168.1.0", &a3);
362 ats_ip_pton("192.168.1.255", &a4);
363
364 ats_ip_pton("::", &a_0000_0000);
365 ats_ip_pton("::1", &a_0000_0001);
366 ats_ip_pton("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &a_ffff_ffff);
367 ats_ip_pton("fe80::221:9bff:fe10:9d8f", &a_fe80_9d8f);
368 ats_ip_pton("fe80::221:9bff:fe10:9d90", &a_fe80_9d90);
369 ats_ip_pton("fe80::221:9bff:fe10:9d95", &a_fe80_9d95);
370 ats_ip_pton("fe80::221:9bff:fe10:9d9d", &a_fe80_9d9d);
371 ats_ip_pton("fe80::221:9bff:fe10:9d9e", &a_fe80_9d9e);
372
373 ats_ip_pton("127.0.0.0", &a_loopback);
374 ats_ip_pton("127.0.0.255", &a_loopback2);
375 ats_ip_pton("63.128.1.12", &a_63_128_1_12);
376
377 SECTION("subnet overfill")
378 {
379 map.fill(&a_10_28_56_0, &a_10_28_56_255, deny);
380 map.fill(&a0, &a_max, allow);
381 CHECK_THAT(map, IsMarkedWith(a_10_28_56_4, deny));
382 }
383
384 SECTION("singleton overfill")
385 {
386 map.fill(&a_loopback, &a_loopback, allow);
387 {
388 INFO("singleton not marked.");
389 CHECK_THAT(map, IsMarkedAt(a_loopback));
390 }
391 map.fill(&a0, &a_max, deny);
392 THEN("singleton mark")
393 {
394 CHECK_THAT(map, IsMarkedWith(a_loopback, allow));
395 THEN("not empty")
396 {
397 REQUIRE(map.begin() != map.end());
398 IpMap::iterator spot = map.begin();
399 ++spot;
400 THEN("more than one range")
401 {
402 REQUIRE(spot != map.end());
403 THEN("ranges disjoint")
404 {
405 INFO(" " << map.begin()->max() << " < " << spot->min());
406 REQUIRE(-1 == ats_ip_addr_cmp(map.begin()->max(), spot->min()));
407 }
408 }
409 }
410 }
411 }
412
413 SECTION("3")
414 {
415 map.fill(&a_loopback, &a_loopback2, markA);
416 map.fill(&a_10_28_56_0, &a_10_28_56_255, markB);
417 {
418 INFO("over extended range");
419 CHECK_THAT(map, !IsMarkedWith(a_63_128_1_12, markC));
420 }
421 map.fill(&a0, &a_max, markC);
422 {
423 INFO("IpMap[2]: Fill failed.");
424 CHECK(map.count() == 5);
425 }
426 {
427 INFO("invalid mark in range gap");
428 CHECK_THAT(map, IsMarkedWith(a_63_128_1_12, markC));
429 }
430 }
431
432 SECTION("4")
433 {
434 map.fill(&a_10_0_0_0, &a_10_0_0_255, allow);
435 map.fill(&a_loopback, &a_loopback2, allow);
436 {
437 INFO("invalid mark between ranges");
438 CHECK_THAT(map, !IsMarkedAt(a_63_128_1_12));
439 }
440 {
441 INFO("invalid mark in lower range");
442 CHECK_THAT(map, IsMarkedWith(a_10_0_0_19, allow));
443 }
444 map.fill(&a0, &a_max, deny);
445 {
446 INFO("range count incorrect");
447 CHECK(map.count() == 5);
448 }
449 {
450 INFO("mark between ranges");
451 CHECK_THAT(map, IsMarkedWith(a_63_128_1_12, deny));
452 }
453
454 map.fill(&a_fe80_9d90, &a_fe80_9d9d, markA);
455 map.fill(&a_0000_0001, &a_0000_0001, markA);
456 map.fill(&a_0000_0000, &a_ffff_ffff, markB);
457
458 {
459 INFO("IpMap Fill[v6]: Zero address has bad mark.");
460 CHECK_THAT(map, IsMarkedWith(a_0000_0000, markB));
461 }
462 {
463 INFO("IpMap Fill[v6]: Max address has bad mark.");
464 CHECK_THAT(map, IsMarkedWith(a_ffff_ffff, markB));
465 }
466 {
467 INFO("IpMap Fill[v6]: 9d90 address has bad mark.");
468 CHECK_THAT(map, IsMarkedWith(a_fe80_9d90, markA));
469 }
470 {
471 INFO("IpMap Fill[v6]: 9d8f address has bad mark.");
472 CHECK_THAT(map, IsMarkedWith(a_fe80_9d8f, markB));
473 }
474 {
475 INFO("IpMap Fill[v6]: 9d9d address has bad mark.");
476 CHECK_THAT(map, IsMarkedWith(a_fe80_9d9d, markA));
477 }
478 {
479 INFO("IpMap Fill[v6]: 9d9b address has bad mark.");
480 CHECK_THAT(map, IsMarkedWith(a_fe80_9d9e, markB));
481 }
482 {
483 INFO("IpMap Fill[v6]: ::1 has bad mark.");
484 CHECK_THAT(map, IsMarkedWith(a_0000_0001, markA));
485 }
486
487 {
488 INFO("IpMap Fill[pre-refill]: Bad range count.");
489 CHECK(map.count() == 10);
490 }
491 // These should be ignored by the map as it is completely covered for IPv6.
492 map.fill(&a_fe80_9d90, &a_fe80_9d9d, markA);
493 map.fill(&a_0000_0001, &a_0000_0001, markC);
494 map.fill(&a_0000_0000, &a_ffff_ffff, markB);
495 {
496 INFO("IpMap Fill[post-refill]: Bad range count.");
497 CHECK(map.count() == 10);
498 }
499 }
500
501 SECTION("5")
502 {
503 map.fill(&a_fe80_9d90, &a_fe80_9d9d, markA);
504 map.fill(&a_0000_0001, &a_0000_0001, markC);
505 map.fill(&a_0000_0000, &a_ffff_ffff, markB);
506 {
507 INFO("IpMap Fill[v6-2]: Zero address has bad mark.");
508 CHECK_THAT(map, IsMarkedWith(a_0000_0000, markB));
509 }
510 {
511 INFO("IpMap Fill[v6-2]: Max address has bad mark.");
512 CHECK_THAT(map, IsMarkedWith(a_ffff_ffff, markB));
513 }
514 {
515 INFO("IpMap Fill[v6-2]: 9d90 address has bad mark.");
516 CHECK_THAT(map, IsMarkedWith(a_fe80_9d90, markA));
517 }
518 {
519 INFO("IpMap Fill[v6-2]: 9d8f address has bad mark.");
520 CHECK_THAT(map, IsMarkedWith(a_fe80_9d8f, markB));
521 }
522 {
523 INFO("IpMap Fill[v6-2]: 9d9d address has bad mark.");
524 CHECK_THAT(map, IsMarkedWith(a_fe80_9d9d, markA));
525 }
526 {
527 INFO("IpMap Fill[v6-2]: 9d9b address has bad mark.");
528 CHECK_THAT(map, IsMarkedWith(a_fe80_9d9e, markB));
529 }
530 {
531 INFO("IpMap Fill[v6-2]: ::1 has bad mark.");
532 CHECK_THAT(map, IsMarkedWith(a_0000_0001, markC));
533 }
534 }
535 }
536
537 TEST_CASE("IpMap CloseIntersection", "[libts][ipmap]")
538 {
539 IpMap map;
540 void *const markA = reinterpret_cast<void *>(1);
541 void *const markB = reinterpret_cast<void *>(2);
542 void *const markC = reinterpret_cast<void *>(3);
543 void *const markD = reinterpret_cast<void *>(4);
544
545 IpEndpoint a_1_l, a_1_u, a_2_l, a_2_u, a_3_l, a_3_u, a_4_l, a_4_u, a_5_l, a_5_u, a_6_l, a_6_u, a_7_l, a_7_u;
546 IpEndpoint b_1_l, b_1_u;
547 IpEndpoint c_1_l, c_1_u, c_2_l, c_2_u, c_3_l, c_3_u;
548 IpEndpoint c_3_m;
549 IpEndpoint d_1_l, d_1_u, d_2_l, d_2_u;
550
551 IpEndpoint a_1_m;
552
553 ats_ip_pton("123.88.172.0", &a_1_l);
554 ats_ip_pton("123.88.180.93", &a_1_m);
555 ats_ip_pton("123.88.191.255", &a_1_u);
556 ats_ip_pton("123.89.132.0", &a_2_l);
557 ats_ip_pton("123.89.135.255", &a_2_u);
558 ats_ip_pton("123.89.160.0", &a_3_l);
559 ats_ip_pton("123.89.167.255", &a_3_u);
560 ats_ip_pton("123.90.108.0", &a_4_l);
561 ats_ip_pton("123.90.111.255", &a_4_u);
562 ats_ip_pton("123.90.152.0", &a_5_l);
563 ats_ip_pton("123.90.159.255", &a_5_u);
564 ats_ip_pton("123.91.0.0", &a_6_l);
565 ats_ip_pton("123.91.35.255", &a_6_u);
566 ats_ip_pton("123.91.40.0", &a_7_l);
567 ats_ip_pton("123.91.47.255", &a_7_u);
568
569 ats_ip_pton("123.78.100.0", &b_1_l);
570 ats_ip_pton("123.78.115.255", &b_1_u);
571
572 ats_ip_pton("123.88.204.0", &c_1_l);
573 ats_ip_pton("123.88.219.255", &c_1_u);
574 ats_ip_pton("123.90.112.0", &c_2_l);
575 ats_ip_pton("123.90.119.255", &c_2_u);
576 ats_ip_pton("123.90.132.0", &c_3_l);
577 ats_ip_pton("123.90.134.157", &c_3_m);
578 ats_ip_pton("123.90.135.255", &c_3_u);
579
580 ats_ip_pton("123.82.196.0", &d_1_l);
581 ats_ip_pton("123.82.199.255", &d_1_u);
582 ats_ip_pton("123.82.204.0", &d_2_l);
583 ats_ip_pton("123.82.219.255", &d_2_u);
584
585 map.mark(a_1_l, a_1_u, markA);
586 map.mark(a_2_l, a_2_u, markA);
587 map.mark(a_3_l, a_3_u, markA);
588 map.mark(a_4_l, a_4_u, markA);
589 map.mark(a_5_l, a_5_u, markA);
590 map.mark(a_6_l, a_6_u, markA);
591 map.mark(a_7_l, a_7_u, markA);
592 CHECK_THAT(map, IsMarkedAt(a_1_m));
593
594 map.mark(b_1_l, b_1_u, markB);
595 CHECK_THAT(map, IsMarkedWith(a_1_m, markA));
596
597 map.mark(c_1_l, c_1_u, markC);
598 map.mark(c_2_l, c_2_u, markC);
599 map.mark(c_3_l, c_3_u, markC);
600 CHECK_THAT(map, IsMarkedWith(a_1_m, markA));
601
602 map.mark(d_1_l, d_1_u, markD);
603 map.mark(d_2_l, d_2_u, markD);
604 CHECK_THAT(map, IsMarkedAt(a_1_m));
605 CHECK_THAT(map, IsMarkedWith(b_1_u, markB));
606 CHECK_THAT(map, IsMarkedWith(c_3_m, markC));
607 CHECK_THAT(map, IsMarkedWith(d_2_l, markD));
608
609 CHECK(map.count() == 13);
610
611 // Check move constructor.
612 IpMap m2{std::move(map)};
613 // Original map should be empty.
614 REQUIRE(map.count() == 0);
615 // Do all these again on the destination map.
616 CHECK_THAT(m2, IsMarkedWith(a_1_m, markA));
617 CHECK_THAT(m2, IsMarkedWith(a_1_m, markA));
618 CHECK_THAT(m2, IsMarkedWith(a_1_m, markA));
619 CHECK_THAT(m2, IsMarkedWith(a_1_m, markA));
620 CHECK_THAT(m2, IsMarkedWith(b_1_u, markB));
621 CHECK_THAT(m2, IsMarkedWith(c_3_m, markC));
622 CHECK_THAT(m2, IsMarkedWith(d_2_l, markD));
623 CHECK(m2.count() == 13);
624
625 #if 0
626 ts::LocalBufferWriter<1024> w;
627 std::cout << "Basic map dump" << std::endl;
628 std::cout << w.print("{}", m2).view() << std::endl;
629 w.reset();
630 std::cout << "With tree detail" << std::endl;
631 std::cout << w.print("{::x}", m2).view() << std::endl;
632 #endif
633 };
634