]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/json/test/object.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / json / test / object.cpp
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/json
8 //
9
10 // Test that header file is self-contained.
11 #include <boost/json/object.hpp>
12
13 #include <boost/json/monotonic_resource.hpp>
14 #include <boost/json/parse.hpp>
15 #include <boost/json/serialize.hpp>
16
17 #include <cmath>
18 #include <forward_list>
19 #include <map>
20 #include <string>
21 #include <type_traits>
22 #include <vector>
23
24 #include "test.hpp"
25 #include "test_suite.hpp"
26 #include "checking_resource.hpp"
27
28 BOOST_JSON_NS_BEGIN
29
30 BOOST_STATIC_ASSERT( std::is_nothrow_destructible<object>::value );
31 BOOST_STATIC_ASSERT( std::is_nothrow_move_constructible<object>::value );
32
33 class object_test
34 {
35 public:
36 static
37 constexpr
38 std::size_t
39 size0_ =
40 detail::small_object_size_ - 2;
41
42 static
43 constexpr
44 std::size_t
45 size1_ = size0_ + 4;
46
47 test_suite::log_type log;
48 string_view const str_;
49
50 using init_list =
51 std::initializer_list<
52 std::pair<string_view, value_ref>>;
53
54 #define DECLARE_INIT_LISTS \
55 init_list i0_ = { \
56 { "0", 0 }, { "1", 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 }, \
57 { "5", 5 }, { "6", 6 }, { "7", 7 }, { "8", 8 }, { "9", 9 }, \
58 { "10", 10 }, { "11", 11 }, { "12", 12 }, { "13", 13 }, { "14", 14 }, \
59 { "15", 15 } }; \
60 init_list i1_ = { \
61 { "0", 0 }, { "1", 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 }, \
62 { "5", 5 }, { "6", 6 }, { "7", 7 }, { "8", 8 }, { "9", 9 }, \
63 { "10", 10 }, { "11", 11 }, { "12", 12 }, { "13", 13 }, { "14", 14 }, \
64 { "15", 15 }, { "16", 16 }, { "17", 17 }, { "18", 18 }, { "19", 19 } }
65
66 string_view const s0_ =
67 R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
68 R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
69 R"("10":10,"11":11,"12":12,"13":13,"14":14,)"
70 R"("15":15})";
71
72 string_view const s1_ =
73 R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
74 R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
75 R"("10":10,"11":11,"12":12,"13":13,"14":14,)"
76 R"("15":15,"16":16,"17":17,"18":18,"19":19})";
77
78 object_test()
79 : str_("abcdefghijklmnopqrstuvwxyz")
80 {
81 // ensure this string does
82 // not fit in the SBO area.
83 BOOST_ASSERT(str_.size() >
84 string().capacity());
85
86 DECLARE_INIT_LISTS;
87
88 BOOST_TEST(
89 i0_.size() == size0_);
90 BOOST_TEST(
91 i1_.size() == size1_);
92 }
93
94 template<class T, class U, class = void>
95 struct is_equal_comparable : std::false_type {};
96
97 template<class T, class U>
98 struct is_equal_comparable<T, U, detail::void_t<decltype(
99 std::declval<T const&>() == std::declval<U const&>()
100 )>> : std::true_type {};
101
102 template<class T, class U, class = void>
103 struct is_unequal_comparable : std::false_type {};
104
105 template<class T, class U>
106 struct is_unequal_comparable<T, U, detail::void_t<decltype(
107 std::declval<T const&>() != std::declval<U const&>()
108 )>> : std::true_type {};
109
110 BOOST_STATIC_ASSERT( std::is_constructible<object::iterator, object::iterator>::value);
111 BOOST_STATIC_ASSERT(! std::is_constructible<object::iterator, object::const_iterator>::value);
112
113 BOOST_STATIC_ASSERT( std::is_constructible<object::const_iterator, object::iterator>::value);
114 BOOST_STATIC_ASSERT( std::is_constructible<object::const_iterator, object::const_iterator>::value);
115
116 BOOST_STATIC_ASSERT( std::is_assignable<object::iterator&, object::iterator>::value);
117 BOOST_STATIC_ASSERT(! std::is_assignable<object::iterator&, object::const_iterator>::value);
118
119 BOOST_STATIC_ASSERT( std::is_assignable<object::const_iterator&, object::iterator>::value);
120 BOOST_STATIC_ASSERT( std::is_assignable<object::const_iterator&, object::const_iterator>::value);
121
122 BOOST_STATIC_ASSERT(is_equal_comparable<object::iterator, object::iterator>::value);
123 BOOST_STATIC_ASSERT(is_equal_comparable<object::iterator, object::const_iterator>::value);
124
125 BOOST_STATIC_ASSERT(is_equal_comparable<object::const_iterator, object::iterator>::value);
126 BOOST_STATIC_ASSERT(is_equal_comparable<object::const_iterator, object::const_iterator>::value);
127
128 BOOST_STATIC_ASSERT(is_unequal_comparable<object::iterator, object::iterator>::value);
129 BOOST_STATIC_ASSERT(is_unequal_comparable<object::iterator, object::const_iterator>::value);
130
131 BOOST_STATIC_ASSERT(is_unequal_comparable<object::const_iterator, object::iterator>::value);
132 BOOST_STATIC_ASSERT(is_unequal_comparable<object::const_iterator, object::const_iterator>::value);
133
134 BOOST_STATIC_ASSERT( std::is_constructible<object::reverse_iterator, object::reverse_iterator>::value);
135 // std::reverse_iterator ctor is not SFINAEd
136 //BOOST_STATIC_ASSERT(! std::is_constructible<object::reverse_iterator, object::const_reverse_iterator>::value);
137
138 BOOST_STATIC_ASSERT( std::is_constructible<object::const_reverse_iterator, object::reverse_iterator>::value);
139 BOOST_STATIC_ASSERT( std::is_constructible<object::const_reverse_iterator, object::const_reverse_iterator>::value);
140
141 BOOST_STATIC_ASSERT( std::is_assignable<object::reverse_iterator&, object::reverse_iterator>::value);
142 // std::reverse_iterator assignment is not SFINAEd
143 //BOOST_STATIC_ASSERT(! std::is_assignable<object::reverse_iterator&, object::const_reverse_iterator>::value);
144
145 BOOST_STATIC_ASSERT( std::is_assignable<object::const_reverse_iterator&, object::reverse_iterator>::value);
146 BOOST_STATIC_ASSERT( std::is_assignable<object::const_reverse_iterator&, object::const_reverse_iterator>::value);
147
148 BOOST_STATIC_ASSERT(is_equal_comparable<object::reverse_iterator, object::reverse_iterator>::value);
149 BOOST_STATIC_ASSERT(is_equal_comparable<object::reverse_iterator, object::const_reverse_iterator>::value);
150
151 BOOST_STATIC_ASSERT(is_equal_comparable<object::const_reverse_iterator, object::reverse_iterator>::value);
152 BOOST_STATIC_ASSERT(is_equal_comparable<object::const_reverse_iterator, object::const_reverse_iterator>::value);
153
154 BOOST_STATIC_ASSERT(is_unequal_comparable<object::reverse_iterator, object::reverse_iterator>::value);
155 BOOST_STATIC_ASSERT(is_unequal_comparable<object::reverse_iterator, object::const_reverse_iterator>::value);
156
157 BOOST_STATIC_ASSERT(is_unequal_comparable<object::const_reverse_iterator, object::reverse_iterator>::value);
158 BOOST_STATIC_ASSERT(is_unequal_comparable<object::const_reverse_iterator, object::const_reverse_iterator>::value);
159
160 static
161 void
162 check(
163 object const& o,
164 string_view s,
165 std::size_t capacity = 0)
166 {
167 BOOST_TEST(
168 parse(serialize(o)).as_object() ==
169 parse(s).as_object());
170 if(capacity != 0)
171 BOOST_TEST(o.capacity() == capacity);
172 }
173
174 static
175 void
176 check(
177 object const& o,
178 std::size_t capacity)
179 {
180 BOOST_TEST(! o.empty());
181 BOOST_TEST(o.size() == 3);
182 BOOST_TEST(
183 o.capacity() == capacity);
184 BOOST_TEST(o.at("a").as_int64() == 1);
185 BOOST_TEST(o.at("b").as_bool());
186 BOOST_TEST(o.at("c").as_string() == "hello");
187 check_storage(o, o.storage());
188 }
189
190 void
191 testDtor()
192 {
193 // ~object()
194 {
195 object o;
196 }
197 }
198
199 void
200 testCtors()
201 {
202 DECLARE_INIT_LISTS;
203
204 // object(detail::unchecked_object&&)
205 {
206 // small
207 {
208 value const jv = parse(s0_);
209 check(jv.as_object(), s0_, i0_.size());
210 }
211
212 // small, duplicate
213 {
214 value const jv = parse(
215 R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
216 R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
217 R"("10":10,"11":11,"12":12,"13":13,"14":14,)"
218 R"("15":15,"10":10})");
219 check(jv.as_object(), s0_, i0_.size() + 1);
220 }
221
222 // large
223 {
224 value const jv = parse(s1_);
225 check(jv.as_object(), s1_, i1_.size());
226 }
227
228 // large, duplicate
229 {
230 value const jv = parse(
231 R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
232 R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
233 R"("10":10,"11":11,"12":12,"13":13,"14":14,)"
234 R"("15":15,"16":16,"17":17,"18":18,"19":19,"10":10})");
235 check(jv.as_object(), s1_, i1_.size() + 1);
236 }
237 }
238
239 // object()
240 {
241 object o;
242 BOOST_TEST(o.empty());
243 BOOST_TEST(o.size() == 0);
244 BOOST_TEST(o.capacity() == 0);
245 }
246
247 // object(storage_ptr)
248 fail_loop([&](storage_ptr const& sp)
249 {
250 object o(sp);
251 check_storage(o, sp);
252 BOOST_TEST(o.empty());
253 BOOST_TEST(o.size() == 0);
254 BOOST_TEST(o.capacity() == 0);
255 });
256
257 // object(std::size_t, storage_ptr)
258 {
259 // small
260 {
261 object o(size0_);
262 BOOST_TEST(o.empty());
263 BOOST_TEST(o.size() == 0);
264 BOOST_TEST(o.capacity() == size0_);
265 }
266
267 // small
268 fail_loop([&](storage_ptr const& sp)
269 {
270 object o(size0_, sp);
271 check_storage(o, sp);
272 BOOST_TEST(o.empty());
273 BOOST_TEST(o.size() == 0);
274 BOOST_TEST(o.capacity() == size0_);
275 });
276
277 // large
278 {
279 object o(size1_);
280 BOOST_TEST(o.empty());
281 BOOST_TEST(o.size() == 0);
282 BOOST_TEST(o.capacity() == size1_);
283 }
284
285 // large
286 fail_loop([&](storage_ptr const& sp)
287 {
288 object o(size1_, sp);
289 check_storage(o, sp);
290 BOOST_TEST(o.empty());
291 BOOST_TEST(o.size() == 0);
292 BOOST_TEST(o.capacity() == size1_);
293 });
294 }
295
296 // object(InputIt, InputIt, size_type, storage_ptr)
297 {
298 // empty range
299 {
300 // random-access iterator
301 std::vector<std::pair<string_view, value>> i1;
302 object o1(i1.begin(), i1.end());
303 BOOST_TEST(o1.empty());
304
305 // bidirectional iterator
306 std::map<string_view, value> i2;
307 object o2(i2.begin(), i2.end());
308 BOOST_TEST(o2.empty());
309
310 // forward iterator
311 std::forward_list<std::pair<string_view, value>> i3;
312 object o3(i3.begin(), i3.end());
313 BOOST_TEST(o3.empty());
314
315 // input iterator
316 auto const it = make_input_iterator(i3.begin());
317 object o4(it, it);
318 BOOST_TEST(o4.empty());
319 }
320
321 // small
322 {
323 object o(i0_.begin(), i0_.end());
324 check(o, s0_, i0_.size());
325 }
326
327 // small, ForwardIterator
328 fail_loop([&](storage_ptr const& sp)
329 {
330 object o(i0_.begin(), i0_.end(), size0_ + 1, sp);
331 BOOST_TEST(! o.empty());
332 check(o, s0_, size0_ + 1);
333 check_storage(o, sp);
334 });
335
336 // small, InputIterator
337 fail_loop([&](storage_ptr const& sp)
338 {
339 object o(
340 make_input_iterator(i0_.begin()),
341 make_input_iterator(i0_.end()), size0_ + 1, sp);
342 BOOST_TEST(! o.empty());
343 BOOST_TEST(o.capacity() == size0_ + 1);
344 check(o, s0_, size0_ + 1);
345 check_storage(o, sp);
346 });
347
348 // large
349 {
350 object o(i1_.begin(), i1_.end());
351 check(o, s1_, i1_.size());
352 }
353
354 // large, ForwardIterator
355 fail_loop([&](storage_ptr const& sp)
356 {
357 object o(i1_.begin(), i1_.end(), size1_ + 1, sp);
358 BOOST_TEST(! o.empty());
359 BOOST_TEST(o.capacity() == size1_ + 1);
360 check(o, s1_, size1_ + 1);
361 check_storage(o, sp);
362 });
363
364 // large, InputIterator
365 fail_loop([&](storage_ptr const& sp)
366 {
367 object o(
368 make_input_iterator(i1_.begin()),
369 make_input_iterator(i1_.end()), size1_ + 1, sp);
370 BOOST_TEST(! o.empty());
371 BOOST_TEST(o.capacity() == size1_ + 1);
372 check(o, s1_, size1_ + 1);
373 check_storage(o, sp);
374 });
375 }
376
377 // object(object&&)
378 {
379 object o1(i0_);
380 check(o1, s0_);
381 auto const sp =
382 storage_ptr{};
383 object o2(std::move(o1));
384 BOOST_TEST(o1.empty());
385 BOOST_TEST(o1.size() == 0);
386 check(o2, s0_);
387 check_storage(o1, sp);
388 check_storage(o2, sp);
389 }
390
391 // object(object&&, storage_ptr)
392 {
393 // small
394 fail_loop([&](storage_ptr const& sp)
395 {
396 object o1(i0_);
397 object o2(std::move(o1), sp);
398 BOOST_TEST(! o1.empty());
399 check(o2, s0_, i0_.size());
400 check_storage(o1,
401 storage_ptr{});
402 check_storage(o2, sp);
403 });
404
405 // large
406 fail_loop([&](storage_ptr const& sp)
407 {
408 object o1(i1_);
409 object o2(std::move(o1), sp);
410 BOOST_TEST(! o1.empty());
411 check(o2, s1_, i1_.size());
412 check_storage(o1,
413 storage_ptr{});
414 check_storage(o2, sp);
415 });
416 }
417
418 // object(pilfered<object>)
419 {
420 {
421 auto const sp =
422 make_shared_resource<unique_resource>();
423 object o1(i0_, sp);
424 object o2(pilfer(o1));
425 BOOST_TEST(
426 o1.storage() == storage_ptr());
427 BOOST_TEST(
428 *o2.storage() == *sp);
429 BOOST_TEST(o1.empty());
430 check(o2, s0_, i0_.size());
431 }
432
433 // ensure pilfered-from objects
434 // are trivially destructible
435 {
436 object o1(make_shared_resource<
437 monotonic_resource>());
438 object o2(pilfer(o1));
439 BOOST_TEST(o1.storage().get() ==
440 storage_ptr().get());
441 }
442 }
443
444 auto const sp =
445 make_shared_resource<
446 unique_resource>();
447 auto const sp0 = storage_ptr{};
448
449 // object(object const&)
450 {
451 // small
452 {
453 object o1(i0_);
454 object o2(o1);
455 BOOST_TEST(! o1.empty());
456 check(o2, s0_, i0_.size());
457 }
458
459 // large
460 {
461 object o1(i1_);
462 object o2(o1);
463 BOOST_TEST(! o1.empty());
464 check(o2, s1_, i1_.size());
465 }
466 }
467
468 // object(object const&, storage_ptr)
469 {
470 // small
471 fail_loop([&](storage_ptr const& sp)
472 {
473 object o1(i0_);
474 object o2(o1, sp);
475 BOOST_TEST(! o1.empty());
476 check(o2, s0_, i0_.size());
477 check_storage(o2, sp);
478 });
479
480 // large
481 fail_loop([&](storage_ptr const& sp)
482 {
483 object o1(i1_);
484 object o2(o1, sp);
485 BOOST_TEST(! o1.empty());
486 check(o2, s1_, i1_.size());
487 check_storage(o2, sp);
488 });
489 }
490
491 // object(initializer_list, storage_ptr)
492 {
493 // small
494 {
495 object o(i0_);
496 check(o, s0_, i0_.size());
497 }
498
499 // small
500 fail_loop([&](storage_ptr const& sp)
501 {
502 object o(i0_, sp);
503 check(o, s0_, i0_.size());
504 check_storage(o, sp);
505 });
506
507 // large
508 {
509 object o(i1_);
510 check(o, s1_, i1_.size());
511 }
512
513 // large
514 fail_loop([&](storage_ptr const& sp)
515 {
516 object o(i1_, sp);
517 check(o, s1_, i1_.size());
518 check_storage(o, sp);
519 });
520 }
521
522 // object(initializer_list, std::size_t, storage_ptr)
523 {
524 // small
525 {
526 object o(i0_, size0_ + 1);
527 check(o, s0_, size0_ + 1);
528 }
529
530 // small
531 fail_loop([&](storage_ptr const& sp)
532 {
533 object o(i0_, size0_ + 1, sp);
534 BOOST_TEST(
535 *o.storage() == *sp);
536 check(o, s0_, size0_ + 1);
537 });
538 }
539
540 // operator=(object const&)
541 {
542 {
543 object o1(i0_);
544 object o2;
545 o2 = o1;
546 check(o1, s0_, i0_.size());
547 check(o2, s0_, i0_.size());
548 check_storage(o1,
549 storage_ptr{});
550 check_storage(o2,
551 storage_ptr{});
552 }
553
554 fail_loop([&](storage_ptr const& sp)
555 {
556 object o1(i0_);
557 object o2(sp);
558 o2 = o1;
559 check(o1, s0_, i0_.size());
560 check(o2, s0_, i0_.size());
561 check_storage(o1,
562 storage_ptr{});
563 check_storage(o2, sp);
564 });
565
566 // self-assign
567 {
568 object o1(i0_);
569 object const& o2(o1);
570 o1 = o2;
571 check(o1, s0_, i0_.size());
572 }
573
574 // copy from child
575 {
576 object o({
577 {"a", 1}, {"b",
578 { {"a", 1}, {"b", true}, {"c", "hello"} }
579 }, {"c", "hello"}});
580 o = o["b"].as_object();
581 check(o, 3);
582 }
583 }
584
585 // operator=(object&&)
586 {
587 {
588 object o1({
589 {"a", 1},
590 {"b", true},
591 {"c", "hello"}});
592 object o2;
593 o2 = std::move(o1);
594 check(o2, 3);
595 BOOST_TEST(o1.empty());
596 check_storage(o1,
597 storage_ptr{});
598 check_storage(o2,
599 storage_ptr{});
600 }
601
602 fail_loop([&](storage_ptr const& sp)
603 {
604 object o1({
605 {"a", 1},
606 {"b", true},
607 {"c", "hello"}});
608 object o2(sp);
609 o2 = std::move(o1);
610 check(o1, 3);
611 check(o2, 3);
612 check_storage(o1,
613 storage_ptr{});
614 check_storage(o2, sp);
615 });
616
617 // self-move
618 {
619 object o1({
620 {"a", 1},
621 {"b", true},
622 {"c", "hello"}});
623 object const& o2(o1);
624 o1 = std::move(o2);
625 check(o1, 3);
626 }
627
628 // move from child
629 {
630 object o({
631 {"a", 1}, {"b",
632 { {"a", 1}, {"b", true}, {"c", "hello"} }
633 }, {"c", "hello"}});
634 o = std::move(o["b"].as_object());
635 check(o, 3);
636 }
637 }
638
639 // operator=(initializer_list)
640 {
641 {
642 object o;
643 o = {
644 {"a", 1},
645 {"b", true},
646 {"c", "hello"} },
647 check(o, 3);
648 check_storage(o,
649 storage_ptr{});
650 }
651
652 fail_loop([&](storage_ptr const& sp)
653 {
654 object o(sp);
655 o = {
656 {"a", 1},
657 {"b", true},
658 {"c", "hello"} },
659 BOOST_TEST(
660 *o.storage() == *sp);
661 check(o, 3);
662 check_storage(o, sp);
663 });
664
665 // assign from child
666 {
667 object o = {
668 { "k1", 1 },
669 { "k2", 2 },
670 { "k3", 3 } };
671 o = { { "k2", o["k2"] } };
672 BOOST_TEST(
673 o == object({ { "k2", 2 } }));
674 }
675 }
676 }
677
678 void
679 testIterators()
680 {
681 object o({
682 {"a", 1},
683 {"b", true},
684 {"c", "hello"}});
685 auto const& co = o;
686 object no;
687 auto const& cno = no;
688
689 // empty container
690 {
691 BOOST_TEST(no.begin() == no.end());
692 BOOST_TEST(cno.begin() == cno.end());
693 BOOST_TEST(no.cbegin() == no.cend());
694 }
695
696 // begin()
697 {
698 auto it = o.begin();
699 BOOST_TEST(it->key() == "a"); ++it;
700 BOOST_TEST(it->key() == "b"); it++;
701 BOOST_TEST(it->key() == "c"); ++it;
702 BOOST_TEST(it == o.end());
703 }
704
705 // begin() const
706 {
707 auto it = co.begin();
708 BOOST_TEST(it->key() == "a"); ++it;
709 BOOST_TEST(it->key() == "b"); it++;
710 BOOST_TEST(it->key() == "c"); ++it;
711 BOOST_TEST(it == co.end());
712 }
713
714 // cbegin()
715 {
716 auto it = o.cbegin();
717 BOOST_TEST(it->key() == "a"); ++it;
718 BOOST_TEST(it->key() == "b"); it++;
719 BOOST_TEST(it->key() == "c"); ++it;
720 BOOST_TEST(it == o.cend());
721 }
722
723 // end()
724 {
725 auto it = o.end();
726 --it; BOOST_TEST(it->key() == "c");
727 it--; BOOST_TEST(it->key() == "b");
728 --it; BOOST_TEST(it->key() == "a");
729 BOOST_TEST(it == o.begin());
730 }
731
732 // end() const
733 {
734 auto it = co.end();
735 --it; BOOST_TEST(it->key() == "c");
736 it--; BOOST_TEST(it->key() == "b");
737 --it; BOOST_TEST(it->key() == "a");
738 BOOST_TEST(it == co.begin());
739 }
740
741 // cend()
742 {
743 auto it = o.cend();
744 --it; BOOST_TEST(it->key() == "c");
745 it--; BOOST_TEST(it->key() == "b");
746 --it; BOOST_TEST(it->key() == "a");
747 BOOST_TEST(it == o.cbegin());
748 }
749
750 // rbegin()
751 {
752 auto it = o.rbegin();
753 BOOST_TEST(it->key() == "c"); ++it;
754 BOOST_TEST(it->key() == "b"); it++;
755 BOOST_TEST(it->key() == "a"); ++it;
756 BOOST_TEST(it == o.rend());
757 }
758
759 // rbegin() const
760 {
761 auto it = co.rbegin();
762 BOOST_TEST(it->key() == "c"); ++it;
763 BOOST_TEST(it->key() == "b"); it++;
764 BOOST_TEST(it->key() == "a"); ++it;
765 BOOST_TEST(it == co.rend());
766 }
767
768 // crbegin()
769 {
770 auto it = o.crbegin();
771 BOOST_TEST(it->key() == "c"); ++it;
772 BOOST_TEST(it->key() == "b"); it++;
773 BOOST_TEST(it->key() == "a"); ++it;
774 BOOST_TEST(it == o.crend());
775 }
776
777 // rend()
778 {
779 auto it = o.rend();
780 --it; BOOST_TEST(it->key() == "a");
781 it--; BOOST_TEST(it->key() == "b");
782 --it; BOOST_TEST(it->key() == "c");
783 BOOST_TEST(it == o.rbegin());
784 }
785
786 // rend() const
787 {
788 auto it = co.rend();
789 --it; BOOST_TEST(it->key() == "a");
790 it--; BOOST_TEST(it->key() == "b");
791 --it; BOOST_TEST(it->key() == "c");
792 BOOST_TEST(it == co.rbegin());
793 }
794
795 // crend()
796 {
797 auto it = o.crend();
798 --it; BOOST_TEST(it->key() == "a");
799 it--; BOOST_TEST(it->key() == "b");
800 --it; BOOST_TEST(it->key() == "c");
801 BOOST_TEST(it == o.crbegin());
802 }
803 }
804
805 //------------------------------------------------------
806
807 void
808 testCapacity()
809 {
810 BOOST_TEST(
811 object{}.size() < object{}.max_size());
812 }
813
814 //------------------------------------------------------
815
816 void
817 testModifiers()
818 {
819 DECLARE_INIT_LISTS;
820
821 // clear
822 {
823 // empty
824 {
825 object o;
826 o.clear();
827 BOOST_TEST(o.empty());
828 }
829
830 // small
831 {
832 object o(i0_);
833 BOOST_TEST(! o.empty());
834 o.clear();
835 BOOST_TEST(o.empty());
836 }
837
838 // large
839 {
840 object o(i1_);
841 BOOST_TEST(! o.empty());
842 o.clear();
843 BOOST_TEST(o.empty());
844 }
845 }
846
847 // insert(P&&)
848 {
849 fail_loop([&](storage_ptr const& sp)
850 {
851 object o(sp);
852 auto result = o.insert(
853 std::make_pair("x", 1));
854 BOOST_TEST(result.second);
855 BOOST_TEST(result.first->key() == "x");
856 BOOST_TEST(result.first->value().as_int64() == 1);
857 });
858
859 fail_loop([&](storage_ptr const& sp)
860 {
861 object o(sp);
862 auto const p = std::make_pair("x", 1);
863 auto result = o.insert(p);
864 BOOST_TEST(result.second);
865 BOOST_TEST(result.first->key() == "x");
866 BOOST_TEST(result.first->value().as_int64() == 1);
867 });
868
869 fail_loop([&](storage_ptr const& sp)
870 {
871 object o({
872 {"a", 1},
873 {"b", 2},
874 {"c", 3}}, sp);
875 auto const result = o.insert(
876 std::make_pair("b", 4));
877 BOOST_TEST(
878 result.first->value().as_int64() == 2);
879 BOOST_TEST(! result.second);
880 });
881
882 // insert child
883 {
884 object o = {
885 { "k1", 1 },
886 { "k2", 2 },
887 { "k3", 3 } };
888 o.insert(std::pair<
889 string_view, value&>(
890 "k4", o["k2"]));
891 BOOST_TEST(o == object({
892 { "k1", 1 },
893 { "k2", 2 },
894 { "k3", 3 },
895 { "k4", 2 }}));
896 }
897 }
898
899 // insert(InputIt, InputIt)
900 {
901 // small
902 {
903 // ForwardIterator
904 fail_loop([&](storage_ptr const& sp)
905 {
906 object o(sp);
907 o.insert(i0_.begin(), i0_.end());
908 check(o, s0_);
909 });
910
911 // InputIterator
912 fail_loop([&](storage_ptr const& sp)
913 {
914 object o(sp);
915 o.insert(
916 make_input_iterator(i0_.begin()),
917 make_input_iterator(i0_.end()));
918 check(o, s0_);
919 });
920
921 // existing duplicate key, ForwardIterator
922 {
923 object o({{"0",0},{"1",1},{"2",2}});
924 init_list i = {{"2",nullptr},{"3",3}};
925 o.insert(i.begin(), i.end());
926 BOOST_TEST(o.capacity() <=
927 detail::small_object_size_);
928 check(o, R"({"0":0,"1":1,"2":2,"3":3})");
929 }
930
931 // existing duplicate key, InputIterator
932 {
933 object o({{"0",0},{"1",1},{"2",2}});
934 init_list i = {{"2",nullptr},{"3",3}};
935 o.insert(
936 make_input_iterator(i.begin()),
937 make_input_iterator(i.end()));
938 BOOST_TEST(o.capacity() <=
939 detail::small_object_size_);
940 check(o, R"({"0":0,"1":1,"2":2,"3":3})");
941 }
942
943 // new duplicate key, ForwardIterator
944 {
945 object o({{"0",0},{"1",1},{"2",2}});
946 init_list i = {{"3",3},{"4",4},{"3",5}};
947 o.insert(i.begin(), i.end());
948 BOOST_TEST(o.capacity() <=
949 detail::small_object_size_);
950 check(o, R"({"0":0,"1":1,"2":2,"3":3,"4":4})");
951 }
952
953 // new duplicate key, InputIterator
954 {
955 object o({{"0",0},{"1",1},{"2",2}});
956 init_list i = {{"3",3},{"4",4},{"3",5}};
957 o.insert(
958 make_input_iterator(i.begin()),
959 make_input_iterator(i.end()));
960 BOOST_TEST(o.capacity() <=
961 detail::small_object_size_);
962 check(o, R"({"0":0,"1":1,"2":2,"3":3,"4":4})");
963 }
964 }
965
966 // large, ForwardIterator
967 fail_loop([&](storage_ptr const& sp)
968 {
969 object o(sp);
970 o.insert(i1_.begin(), i1_.end());
971 check(o, s1_);
972 });
973
974 // large, InputIterator
975 fail_loop([&](storage_ptr const& sp)
976 {
977 object o(sp);
978 o.insert(
979 make_input_iterator(i1_.begin()),
980 make_input_iterator(i1_.end()));
981 check(o, s1_);
982 });
983 }
984
985 // insert(initializer_list)
986 {
987 // small
988 fail_loop([&](storage_ptr const& sp)
989 {
990 object o(sp);
991 o.insert(i0_);
992 check(o, s0_);
993 });
994
995 // small, existing duplicate
996 fail_loop([&](storage_ptr const& sp)
997 {
998 object o({{"0",0},{"1",1},/*{"2",2},*/{"3",3},{"4",4}}, sp);
999 BOOST_TEST(o.capacity() <= detail::small_object_size_);
1000 o.insert({{"2",2},{"3",3}});
1001 BOOST_TEST(o.capacity() <= detail::small_object_size_);
1002 check(o, R"({"0":0,"1":1,"2":2,"3":3,"4":4})");
1003 });
1004
1005 // small, new duplicate
1006 fail_loop([&](storage_ptr const& sp)
1007 {
1008 object o({{"0",0},{"1",1},/*{"2",2},{"3",3},*/{"4",4}}, sp);
1009 BOOST_TEST(o.capacity() <= detail::small_object_size_);
1010 o.insert({{"2",2},{"3",3},{"2",2}});
1011 BOOST_TEST(o.capacity() <= detail::small_object_size_);
1012 check(o, R"({"0":0,"1":1,"2":2,"3":3,"4":4})");
1013 });
1014
1015 // large
1016 fail_loop([&](storage_ptr const& sp)
1017 {
1018 object o(sp);
1019 o.insert(i1_);
1020 check(o, s1_);
1021 });
1022
1023 // large, existing duplicate
1024 fail_loop([&](storage_ptr const& sp)
1025 {
1026 object o({
1027 {"0",0},{"1",1},{"2",2},{"3",3},{"4",4},
1028 {"5",5},{"6",6},{"7",7},{"8",8},{"9",9},
1029 /*{"10",10},*/{"11",11},{"12",12},{"13",13},{"14",14},
1030 {"15",15},{"16",16},{"17",17},{"18",18},{"19",19}}, sp);
1031 BOOST_TEST(o.capacity() > detail::small_object_size_);
1032 o.insert({{"10",10},{"11",11}});
1033 BOOST_TEST(o.capacity() > detail::small_object_size_);
1034 check(o, s1_);
1035 });
1036
1037 // large, new duplicate
1038 fail_loop([&](storage_ptr const& sp)
1039 {
1040 object o({
1041 {"0",0},{"1",1},{"2",2},{"3",3},{"4",4},
1042 {"5",5},{"6",6},{"7",7},{"8",8},{"9",9},
1043 /*{"10",10},{"11",11},*/{"12",12},{"13",13},{"14",14},
1044 {"15",15},{"16",16},{"17",17},{"18",18},{"19",19}},
1045 detail::small_object_size_ + 1, sp);
1046 BOOST_TEST(o.capacity() > detail::small_object_size_);
1047 o.insert({{"10",10},{"11",11},{"10",10}});
1048 BOOST_TEST(o.capacity() > detail::small_object_size_);
1049 check(o, s1_);
1050 });
1051
1052 // do rollback in ~revert_insert
1053 fail_loop([&](storage_ptr const& sp)
1054 {
1055 object o(sp);
1056 o.insert({
1057 { "a", { 1, 2, 3, 4 } } });
1058 });
1059
1060 // insert child
1061 {
1062 object o = {
1063 { "k1", 1 },
1064 { "k2", 2 },
1065 { "k3", 3 } };
1066 o.insert({
1067 { "k4", o["k2"] } });
1068 BOOST_TEST(o == object({
1069 { "k1", 1 },
1070 { "k2", 2 },
1071 { "k3", 3 },
1072 { "k4", 2 }}));
1073 }
1074 }
1075
1076 // insert_or_assign(key, o);
1077 {
1078 fail_loop([&](storage_ptr const& sp)
1079 {
1080 object o({{"a", 1}}, sp);
1081 o.insert_or_assign("a", str_);
1082 BOOST_TEST(o["a"].is_string());
1083 });
1084
1085 fail_loop([&](storage_ptr const& sp)
1086 {
1087 object o({
1088 {"a", 1},
1089 {"b", 2},
1090 {"c", 3}}, sp);
1091 o.insert_or_assign("d", str_);
1092 BOOST_TEST(o["d"].is_string());
1093 BOOST_TEST(o.size() == 4);
1094 });
1095
1096 fail_loop([&](storage_ptr const& sp)
1097 {
1098 object o({{"a", 1}}, sp);
1099 o.insert_or_assign("b", true);
1100 o.insert_or_assign("c", "hello");
1101 check(o, 3);
1102 });
1103
1104 fail_loop([&](storage_ptr const& sp)
1105 {
1106 object o({{"a", 1}}, sp);
1107 BOOST_TEST(
1108 ! o.insert_or_assign("a", 2).second);
1109 BOOST_TEST(o["a"].as_int64() == 2);
1110 });
1111
1112 // insert child
1113 {
1114 object o = {
1115 { "k1", 1 },
1116 { "k2", 2 },
1117 { "k3", 3 } };
1118 o.insert_or_assign(
1119 "k4", o["k2"]);
1120 BOOST_TEST(o == object({
1121 { "k1", 1 },
1122 { "k2", 2 },
1123 { "k3", 3 },
1124 { "k4", 2 }}));
1125 }
1126 }
1127
1128 // emplace(key, arg)
1129 {
1130 fail_loop([&](storage_ptr const& sp)
1131 {
1132 object o(sp);
1133 o.emplace("a", 1);
1134 o.emplace("b", true);
1135 o.emplace("c", "hello");
1136 check(o, 3);
1137 });
1138
1139 // emplace child
1140 {
1141 object o = {
1142 { "k1", 1 },
1143 { "k2", 2 },
1144 { "k3", 3 } };
1145 o.emplace(
1146 "k4", o["k2"]);
1147 BOOST_TEST(o == object({
1148 { "k1", 1 },
1149 { "k2", 2 },
1150 { "k3", 3 },
1151 { "k4", 2 }}));
1152 }
1153 }
1154
1155 // erase(pos)
1156 {
1157 // small
1158 {
1159 object o(i0_);
1160 auto it = o.erase(o.find("10"));
1161 BOOST_TEST(it->key() == "15");
1162 BOOST_TEST(
1163 it->value().as_int64() == 15);
1164 BOOST_TEST(serialize(o) ==
1165 R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
1166 R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
1167 R"("15":15,"11":11,"12":12,"13":13,"14":14})");
1168 }
1169
1170 // large
1171 {
1172 object o(i1_);
1173 auto it = o.erase(o.find("10"));
1174 BOOST_TEST(it->key() == "19");
1175 BOOST_TEST(
1176 it->value().as_int64() == 19);
1177 BOOST_TEST(serialize(o) ==
1178 R"({"0":0,"1":1,"2":2,"3":3,"4":4,)"
1179 R"("5":5,"6":6,"7":7,"8":8,"9":9,)"
1180 R"("19":19,"11":11,"12":12,"13":13,"14":14,)"
1181 R"("15":15,"16":16,"17":17,"18":18})");
1182 }
1183 }
1184
1185 // erase(key)
1186 {
1187 {
1188 object o({
1189 {"a", 1},
1190 {"b", true},
1191 {"c", "hello"}});
1192 BOOST_TEST(o.erase("b2") == 0);
1193 check(o, 3);
1194 }
1195
1196 {
1197 object o({
1198 {"a", 1},
1199 {"b", true},
1200 {"b2", 2},
1201 {"c", "hello"}});
1202 BOOST_TEST(o.erase("b2") == 1);
1203 check(o, 4);
1204 }
1205 }
1206
1207 // swap(object&)
1208 {
1209 {
1210 object o1 = {{"a",1}, {"b",true}, {"c", "hello"}};
1211 object o2 = {{"d",{1,2,3}}};
1212 swap(o1, o2);
1213 BOOST_TEST(o1.size() == 1);
1214 BOOST_TEST(o2.size() == 3);
1215 BOOST_TEST(o1.count("d") == 1);
1216 }
1217
1218 fail_loop([&](storage_ptr const& sp)
1219 {
1220 object o1 = {{"a",1}, {"b",true}, {"c", "hello"}};
1221 object o2({{"d",{1,2,3}}}, sp);
1222 swap(o1, o2);
1223 BOOST_TEST(o1.size() == 1);
1224 BOOST_TEST(o2.size() == 3);
1225 BOOST_TEST(o1.count("d") == 1);
1226 });
1227
1228 fail_loop([&](storage_ptr const& sp)
1229 {
1230 object o1({{"d",{1,2,3}}}, sp);
1231 object o2 = {{"a",1}, {"b",true}, {"c", "hello"}};
1232 swap(o1, o2);
1233 BOOST_TEST(o1.size() == 3);
1234 BOOST_TEST(o2.size() == 1);
1235 BOOST_TEST(o2.count("d") == 1);
1236 });
1237 }
1238 }
1239
1240 //------------------------------------------------------
1241
1242 void
1243 testLookup()
1244 {
1245 object o0;
1246 object o1({
1247 {"a", 1},
1248 {"b", true},
1249 {"c", "hello"}});
1250 auto const& co0 = o0;
1251 auto const& co1 = o1;
1252
1253 // at(key)
1254 {
1255 BOOST_TEST(
1256 o1.at("a").is_number());
1257 BOOST_TEST_THROWS((o1.at("d")),
1258 std::out_of_range);
1259 }
1260
1261 // at(key) const
1262 {
1263 BOOST_TEST(
1264 co1.at("a").is_number());
1265 BOOST_TEST_THROWS((co1.at("d")),
1266 std::out_of_range);
1267 }
1268
1269 // operator[&](key)
1270 {
1271 object o({
1272 {"a", 1},
1273 {"b", true},
1274 {"c", "hello"}});
1275 BOOST_TEST(o.count("d") == 0);;
1276 BOOST_TEST(o["a"].is_number());
1277 BOOST_TEST(o["d"].is_null());
1278 BOOST_TEST(o.count("d") == 1);
1279 }
1280
1281 // count(key)
1282 {
1283 BOOST_TEST(o1.count("a") == 1);
1284 BOOST_TEST(o1.count("d") == 0);
1285 BOOST_TEST(o1.count("e") == 0);
1286 }
1287
1288 // find(key)
1289 // find(key) const
1290 {
1291 BOOST_TEST(
1292 o0.find("") == o0.end());
1293 BOOST_TEST(
1294 o1.find("a")->key() == "a");
1295 BOOST_TEST(
1296 o1.find("e") == o1.end());
1297
1298 BOOST_TEST(
1299 co0.find("") == co0.end());
1300 BOOST_TEST(
1301 co1.find("a")->key() == "a");
1302 BOOST_TEST(
1303 co1.find("e") == o1.end());
1304 }
1305
1306 // contains(key)
1307 {
1308 BOOST_TEST(o1.contains("a"));
1309 BOOST_TEST(! o1.contains("e"));
1310 BOOST_TEST(! object().contains(""));
1311 }
1312
1313 // if_contains(key)
1314 // if_contains(key) const
1315 {
1316 BOOST_TEST(o1.if_contains("a")->is_int64());
1317 BOOST_TEST(o1.if_contains("e") == nullptr);
1318 BOOST_TEST(co1.if_contains("a")->is_int64());
1319 BOOST_TEST(co1.if_contains("e") == nullptr);
1320
1321 *o1.if_contains("a") = 2;
1322 BOOST_TEST(co1.if_contains("a")->as_int64() == 2);
1323 }
1324 }
1325
1326 void
1327 testHashPolicy()
1328 {
1329 // reserve(size_type)
1330 {
1331 {
1332 object o;
1333 for(std::size_t i = 0; i < 10; ++i)
1334 o.emplace(std::to_string(i), i);
1335 o.reserve(15);
1336 BOOST_TEST(o.capacity() >= 15);
1337 o.reserve(20);
1338 BOOST_TEST(o.capacity() >= 20);
1339 }
1340
1341 {
1342 object o;
1343 o.reserve(3);
1344 BOOST_TEST(o.capacity() == 3);
1345 o.reserve(7);
1346 BOOST_TEST(o.capacity() == 7);
1347 }
1348 }
1349 }
1350
1351 //------------------------------------------------------
1352
1353 void
1354 testImplementation()
1355 {
1356 // insert duplicate keys
1357 {
1358 object o({
1359 {"a", 1},
1360 {"b", true},
1361 {"b", {1,2,3}},
1362 {"c", "hello"}});
1363 BOOST_TEST(o.at("a").as_int64() == 1);
1364 BOOST_TEST(o.at("b").as_bool() == true);
1365 BOOST_TEST(o.at("c").as_string() == "hello");
1366 }
1367
1368 // destroy key_value_pair array with need_free=false
1369 {
1370 monotonic_resource mr;
1371 object o({
1372 {"a", 1},
1373 {"b", true},
1374 {"b", {1,2,3}},
1375 {"c", "hello"}}, &mr);
1376 }
1377 }
1378
1379 static
1380 string_view
1381 make_key(
1382 std::size_t i,
1383 char* buf)
1384 {
1385 int constexpr base = 62;
1386 char const* const alphabet =
1387 "0123456789"
1388 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1389 "abcdefghijklmnopqrstuvwxyz";
1390 char* dest = buf;
1391 do
1392 {
1393 *dest++ = alphabet[i%base];
1394 i /= base;
1395 }
1396 while(i);
1397 return { buf, static_cast<
1398 std::size_t>(dest-buf) };
1399 }
1400
1401 void
1402 testCollisions()
1403 {
1404 int constexpr buckets =
1405 detail::small_object_size_ + 1;
1406 int constexpr collisions = 3;
1407
1408 //DECLARE_INIT_LISTS;
1409
1410 // find a set of keys that collide
1411 std::vector<std::string> v;
1412 object o;
1413 o.reserve(buckets);
1414 {
1415 BOOST_TEST(
1416 o.capacity() == buckets);
1417 char buf[26];
1418 auto const match =
1419 o.t_->digest("0") % buckets;
1420 v.push_back("0");
1421 std::size_t i = 1;
1422 for(;;)
1423 {
1424 auto s = make_key(i, buf);
1425 if((o.t_->digest(s) %
1426 buckets) == match)
1427 {
1428 v.push_back(std::string(
1429 s.data(), s.size()));
1430 if(v.size() >= collisions)
1431 break;
1432 }
1433 ++i;
1434 }
1435 }
1436
1437 // ensure collisions are distinguishable
1438 {
1439 o.clear();
1440 BOOST_TEST(
1441 (o.t_->digest(v[0]) % buckets) ==
1442 (o.t_->digest(v[1]) % buckets));
1443 BOOST_TEST(
1444 (o.t_->digest(v[1]) % buckets) ==
1445 (o.t_->digest(v[2]) % buckets));
1446 o.emplace(v[0], 1);
1447 o.emplace(v[1], 2);
1448 o.emplace(v[2], 3);
1449 BOOST_TEST(o.at(v[0]).to_number<int>() == 1);
1450 BOOST_TEST(o.at(v[1]).to_number<int>() == 2);
1451 BOOST_TEST(o.at(v[2]).to_number<int>() == 3);
1452 }
1453
1454 // erase k1
1455 {
1456 o.clear();
1457 o.emplace(v[0], 1);
1458 o.emplace(v[1], 2);
1459 o.emplace(v[2], 3);
1460 o.erase(v[0]);
1461 BOOST_TEST(o.at(v[1]).to_number<int>() == 2);
1462 BOOST_TEST(o.at(v[2]).to_number<int>() == 3);
1463 }
1464
1465 // erase k2
1466 {
1467 o.clear();
1468 o.emplace(v[0], 1);
1469 o.emplace(v[1], 2);
1470 o.emplace(v[2], 3);
1471 o.erase(v[1]);
1472 BOOST_TEST(o.at(v[0]).to_number<int>() == 1);
1473 BOOST_TEST(o.at(v[2]).to_number<int>() == 3);
1474 }
1475
1476 // erase k3
1477 {
1478 o.clear();
1479 o.emplace(v[0], 1);
1480 o.emplace(v[1], 2);
1481 o.emplace(v[2], 3);
1482 o.erase(v[2]);
1483 BOOST_TEST(o.at(v[0]).to_number<int>() == 1);
1484 BOOST_TEST(o.at(v[1]).to_number<int>() == 2);
1485 }
1486 }
1487
1488 void
1489 testEquality()
1490 {
1491 BOOST_TEST(object({}) == object({}));
1492 BOOST_TEST(object({}) != object({{"1",1},{"2",2}}));
1493 BOOST_TEST(object({{"1",1},{"2",2},{"3",3}}) == object({{"1",1},{"2",2},{"3",3}}));
1494 BOOST_TEST(object({{"1",1},{"2",2},{"3",3}}) != object({{"1",1},{"2",2}}));
1495 BOOST_TEST(object({{"1",1},{"2",2},{"3",3}}) == object({{"3",3},{"2",2},{"1",1}}));
1496 }
1497
1498 void
1499 testAllocation()
1500 {
1501 {
1502 checking_resource res;
1503 object o(&res);
1504 o.reserve(1);
1505 }
1506
1507 {
1508 checking_resource res;
1509 object o(&res);
1510 o.reserve(1000);
1511 }
1512
1513 {
1514 checking_resource res;
1515 object o({std::make_pair("one", 1)}, &res);
1516 }
1517 }
1518
1519 void
1520 testHash()
1521 {
1522 BOOST_TEST(check_hash_equal(
1523 object(), object({})));
1524 BOOST_TEST(expect_hash_not_equal(
1525 object(), object({{"1",1},{"2",2}})));
1526 BOOST_TEST(check_hash_equal(
1527 object({{"a",1}, {"b",2}, {"c",3}}),
1528 object({{"b",2}, {"c",3}, {"a",1}})));
1529 BOOST_TEST(expect_hash_not_equal(
1530 object({{"a",1}, {"b",2}, {"c",3}}),
1531 object({{"b",2}, {"c",3}})));
1532 }
1533
1534 void
1535 run()
1536 {
1537 testDtor();
1538 testCtors();
1539 testIterators();
1540 testCapacity();
1541 testModifiers();
1542 testLookup();
1543 testHashPolicy();
1544 testImplementation();
1545 testCollisions();
1546 testEquality();
1547 testAllocation();
1548 testHash();
1549 }
1550 };
1551
1552 TEST_SUITE(object_test, "boost.json.object");
1553
1554 BOOST_JSON_NS_END