File indexing completed on 2026-05-17 07:36:28
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Utilities/Any.hpp"
0012
0013 #include <any>
0014 #include <array>
0015 #include <cstddef>
0016 #include <memory>
0017 #include <type_traits>
0018 #include <utility>
0019
0020 using namespace Acts;
0021
0022 #if defined(_ACTS_ANY_ENABLE_TRACK_ALLOCATIONS)
0023 #define CHECK_ANY_ALLOCATIONS() \
0024 do { \
0025 detail::_AnyAllocationReporter::checkAllocations(); \
0026 } while (0)
0027 #else
0028 #define CHECK_ANY_ALLOCATIONS() \
0029 do { \
0030 } while (0)
0031 #endif
0032
0033 namespace ActsTests {
0034
0035 BOOST_AUTO_TEST_SUITE(UtilitiesSuite)
0036
0037 BOOST_AUTO_TEST_CASE(AnyConstructPrimitive) {
0038 {
0039
0040 Any a;
0041 BOOST_CHECK(!a);
0042
0043 int v = 5;
0044 a = Any{v};
0045 BOOST_CHECK(!!a);
0046
0047 BOOST_CHECK_EQUAL(a.as<int>(), v);
0048 BOOST_CHECK_NE(a.as<int>(), v + 1);
0049
0050 BOOST_CHECK_THROW(a.as<float>(), std::bad_any_cast);
0051 }
0052 CHECK_ANY_ALLOCATIONS();
0053
0054 {
0055
0056 Any a;
0057 BOOST_CHECK(!a);
0058
0059 std::array<int, 2> v{1, 2};
0060 a = Any{v};
0061 BOOST_CHECK(!!a);
0062
0063 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<decltype(v)>().begin(),
0064 a.as<decltype(v)>().end(), v.begin(),
0065 v.end());
0066 BOOST_CHECK_THROW(a.as<float>(), std::bad_any_cast);
0067 }
0068 CHECK_ANY_ALLOCATIONS();
0069
0070 {
0071
0072 Any a;
0073 BOOST_CHECK(!a);
0074
0075 std::array<unsigned long, 5> v{1, 2, 3, 4, 5};
0076 a = Any{v};
0077 BOOST_CHECK(!!a);
0078
0079 BOOST_CHECK_EQUAL_COLLECTIONS(a.as<decltype(v)>().begin(),
0080 a.as<decltype(v)>().end(), v.begin(),
0081 v.end());
0082 BOOST_CHECK_THROW(a.as<float>(), std::bad_any_cast);
0083 }
0084 CHECK_ANY_ALLOCATIONS();
0085 }
0086
0087 BOOST_AUTO_TEST_CASE(AnyAsPtr) {
0088 {
0089
0090 Any a{42};
0091 int* p = a.asPtr<int>();
0092 BOOST_REQUIRE_NE(p, static_cast<int*>(nullptr));
0093 BOOST_CHECK_EQUAL(*p, 42);
0094
0095
0096 BOOST_CHECK_EQUAL(a.asPtr<float>(), static_cast<float*>(nullptr));
0097 BOOST_CHECK_EQUAL(a.asPtr<double>(), static_cast<double*>(nullptr));
0098
0099
0100 *p = 99;
0101 BOOST_CHECK_EQUAL(a.as<int>(), 99);
0102 }
0103 CHECK_ANY_ALLOCATIONS();
0104
0105 {
0106
0107 std::array<unsigned long, 5> v{10, 20, 30, 40, 50};
0108 Any a{v};
0109 auto* p = a.asPtr<std::array<unsigned long, 5>>();
0110 BOOST_REQUIRE_NE(p, static_cast<decltype(p)>(nullptr));
0111 BOOST_CHECK_EQUAL_COLLECTIONS(p->begin(), p->end(), v.begin(), v.end());
0112
0113
0114 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<int*>(nullptr));
0115 }
0116 CHECK_ANY_ALLOCATIONS();
0117
0118 {
0119
0120 Any a;
0121 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<int*>(nullptr));
0122 BOOST_CHECK_EQUAL(a.asPtr<float>(), static_cast<float*>(nullptr));
0123 }
0124 CHECK_ANY_ALLOCATIONS();
0125
0126 {
0127
0128 const Any a{3.14f};
0129 const float* p = a.asPtr<float>();
0130 BOOST_REQUIRE_NE(p, static_cast<const float*>(nullptr));
0131 BOOST_CHECK_EQUAL(*p, 3.14f);
0132
0133
0134 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<const int*>(nullptr));
0135 }
0136 CHECK_ANY_ALLOCATIONS();
0137
0138 {
0139
0140 const Any a;
0141 BOOST_CHECK_EQUAL(a.asPtr<int>(), static_cast<const int*>(nullptr));
0142 }
0143 CHECK_ANY_ALLOCATIONS();
0144
0145 {
0146
0147 std::array<int, 64> v{};
0148 v.fill(7);
0149 const Any a{v};
0150 const auto* p = a.asPtr<std::array<int, 64>>();
0151 BOOST_REQUIRE_NE(p, static_cast<decltype(p)>(nullptr));
0152 for (const auto& elem : *p) {
0153 BOOST_CHECK_EQUAL(elem, 7);
0154 }
0155 BOOST_CHECK_EQUAL(a.asPtr<float>(), static_cast<const float*>(nullptr));
0156 }
0157 CHECK_ANY_ALLOCATIONS();
0158 }
0159
0160 BOOST_AUTO_TEST_CASE(AnyAssignConstructEmpty) {
0161 Any a;
0162 Any b;
0163 a = b;
0164 Any c{a};
0165 a = std::move(b);
0166 Any d{std::move(a)};
0167
0168 BOOST_CHECK(!a);
0169 BOOST_CHECK(!b);
0170 BOOST_CHECK(!c);
0171 BOOST_CHECK(!d);
0172
0173 CHECK_ANY_ALLOCATIONS();
0174 }
0175
0176 BOOST_AUTO_TEST_CASE(AnyConstructCustom) {
0177 struct A {
0178 int value;
0179 A() { value = 76; }
0180 };
0181
0182 Any a;
0183 BOOST_CHECK(!a);
0184 a = Any{A{}};
0185
0186 BOOST_CHECK(!!a);
0187
0188 BOOST_CHECK_EQUAL(a.as<A>().value, 76);
0189
0190 CHECK_ANY_ALLOCATIONS();
0191 }
0192
0193 BOOST_AUTO_TEST_CASE(AnyConstructCustomInPlace) {
0194 struct A {
0195 int value;
0196 explicit A(int v) { value = v; }
0197 };
0198
0199 Any a{std::in_place_type<A>, 42};
0200 BOOST_CHECK(!!a);
0201 BOOST_CHECK_EQUAL(a.as<A>().value, 42);
0202
0203 CHECK_ANY_ALLOCATIONS();
0204 }
0205
0206 BOOST_AUTO_TEST_CASE(AnyMove) {
0207 {
0208
0209 Any a;
0210 BOOST_CHECK(!a);
0211
0212 int v = 5;
0213 a = Any{v};
0214 BOOST_CHECK(!!a);
0215
0216 Any b = std::move(a);
0217 BOOST_CHECK(!!b);
0218 BOOST_CHECK_EQUAL(b.as<int>(), 5);
0219
0220 Any c;
0221 c = std::move(b);
0222 BOOST_CHECK(!!c);
0223 BOOST_CHECK_EQUAL(c.as<int>(), 5);
0224 }
0225
0226 CHECK_ANY_ALLOCATIONS();
0227 }
0228
0229 BOOST_AUTO_TEST_CASE(AnyCopy) {
0230 {
0231
0232 Any a;
0233 BOOST_CHECK(!a);
0234
0235 int v = 5;
0236 a = Any{v};
0237 BOOST_CHECK(!!a);
0238
0239 Any b = a;
0240 BOOST_CHECK(!!b);
0241 BOOST_CHECK_EQUAL(b.as<int>(), 5);
0242
0243 Any c;
0244 c = a;
0245 BOOST_CHECK(!!c);
0246 BOOST_CHECK_EQUAL(c.as<int>(), 5);
0247 }
0248 CHECK_ANY_ALLOCATIONS();
0249 }
0250
0251 struct D {
0252 bool* destroyed;
0253 explicit D(bool* d) : destroyed{d} {}
0254 ~D() { *destroyed = true; }
0255 };
0256
0257 struct D2 {
0258 bool* destroyed{nullptr};
0259 std::array<char, 512> blob{};
0260
0261 explicit D2(bool* d) : destroyed{d} {}
0262
0263 ~D2() { *destroyed = true; }
0264 };
0265
0266 BOOST_AUTO_TEST_CASE(AnyEmplace) {
0267 {
0268 Any a;
0269 auto& value = a.emplace<int>(42);
0270 BOOST_CHECK_EQUAL(value, 42);
0271 BOOST_CHECK_EQUAL(a.as<int>(), 42);
0272 value = 84;
0273 BOOST_CHECK_EQUAL(a.as<int>(), 84);
0274 }
0275 CHECK_ANY_ALLOCATIONS();
0276
0277 {
0278 bool destroyed = false;
0279 Any a{std::in_place_type<D>, &destroyed};
0280 BOOST_CHECK(!destroyed);
0281 a.emplace<int>(7);
0282 BOOST_CHECK(destroyed);
0283 BOOST_CHECK_EQUAL(a.as<int>(), 7);
0284 }
0285 CHECK_ANY_ALLOCATIONS();
0286
0287 {
0288 bool destroyed = false;
0289 Any a{std::in_place_type<D2>, &destroyed};
0290 BOOST_CHECK(!destroyed);
0291 bool destroyed2 = false;
0292 auto& ref = a.emplace<D2>(&destroyed2);
0293 BOOST_CHECK(destroyed);
0294 BOOST_CHECK(!destroyed2);
0295 BOOST_CHECK_EQUAL(ref.destroyed, &destroyed2);
0296 BOOST_CHECK_EQUAL(a.as<D2>().destroyed, &destroyed2);
0297 }
0298 CHECK_ANY_ALLOCATIONS();
0299 }
0300
0301 BOOST_AUTO_TEST_CASE(AnyMoveTypeChange) {
0302 BOOST_TEST_CONTEXT("Small type") {
0303 bool destroyed = false;
0304 D d{&destroyed};
0305 Any a{std::move(d)};
0306 BOOST_CHECK(!destroyed);
0307
0308 int value = 5;
0309 Any b{value};
0310 a = std::move(b);
0311 BOOST_CHECK(destroyed);
0312 BOOST_CHECK_EQUAL(a.as<int>(), value);
0313 }
0314
0315 bool destroyed = false;
0316 BOOST_TEST_CONTEXT("Large type") {
0317 D2 d{&destroyed};
0318 Any a{std::move(d)};
0319 BOOST_CHECK(!destroyed);
0320
0321 int value = 5;
0322 Any b{value};
0323 a = std::move(b);
0324 BOOST_CHECK(destroyed);
0325 BOOST_CHECK_EQUAL(a.as<int>(), value);
0326 }
0327 }
0328
0329 BOOST_AUTO_TEST_CASE(AnyCopyTypeChange) {
0330 BOOST_TEST_CONTEXT("Small type") {
0331 bool destroyed = false;
0332 D d{&destroyed};
0333 Any a{std::move(d)};
0334 BOOST_CHECK(!destroyed);
0335
0336 int value = 5;
0337 Any b{value};
0338 a = b;
0339 BOOST_CHECK(destroyed);
0340 BOOST_CHECK_EQUAL(a.as<int>(), value);
0341 }
0342
0343 bool destroyed = false;
0344 BOOST_TEST_CONTEXT("Large type") {
0345 D2 d{&destroyed};
0346 Any a{std::move(d)};
0347 BOOST_CHECK(!destroyed);
0348
0349 int value = 5;
0350 Any b{value};
0351 a = b;
0352 BOOST_CHECK(destroyed);
0353 BOOST_CHECK_EQUAL(a.as<int>(), value);
0354 }
0355 }
0356
0357 BOOST_AUTO_TEST_CASE(AnyDestroy) {
0358 {
0359 bool destroyed = false;
0360 D d{&destroyed};
0361 BOOST_CHECK(!destroyed);
0362 {
0363 Any a{std::move(d)};
0364 BOOST_CHECK(!destroyed);
0365 }
0366 BOOST_CHECK(destroyed);
0367 }
0368 CHECK_ANY_ALLOCATIONS();
0369
0370 {
0371 bool destroyed = false;
0372 D2 d{&destroyed};
0373 BOOST_CHECK(!destroyed);
0374 {
0375 Any a{std::move(d)};
0376 BOOST_CHECK(!destroyed);
0377 }
0378 BOOST_CHECK(destroyed);
0379 }
0380 CHECK_ANY_ALLOCATIONS();
0381 }
0382
0383 BOOST_AUTO_TEST_CASE(AnyDestroyCopy) {
0384 {
0385 bool destroyed = false;
0386
0387 {
0388 Any b;
0389 {
0390 Any a{std::in_place_type<D>, &destroyed};
0391 BOOST_CHECK(!destroyed);
0392 b = a;
0393 BOOST_CHECK(!destroyed);
0394 }
0395 BOOST_CHECK(destroyed);
0396 destroyed = false;
0397 BOOST_CHECK(!destroyed);
0398 }
0399 BOOST_CHECK(destroyed);
0400 }
0401 CHECK_ANY_ALLOCATIONS();
0402
0403 {
0404 bool destroyed = false;
0405
0406 {
0407 Any b;
0408 {
0409 Any a{std::in_place_type<D2>, &destroyed};
0410 BOOST_CHECK(!destroyed);
0411 b = a;
0412 BOOST_CHECK(!destroyed);
0413 }
0414 BOOST_CHECK(destroyed);
0415 destroyed = false;
0416 BOOST_CHECK(!destroyed);
0417 }
0418 BOOST_CHECK(destroyed);
0419 }
0420 CHECK_ANY_ALLOCATIONS();
0421 }
0422
0423 BOOST_AUTO_TEST_CASE(AnyDestroyInPlace) {
0424 {
0425 bool destroyed = false;
0426 BOOST_CHECK(!destroyed);
0427 {
0428 Any a{std::in_place_type<D>, &destroyed};
0429 BOOST_CHECK(!destroyed);
0430 }
0431 BOOST_CHECK(destroyed);
0432 }
0433 CHECK_ANY_ALLOCATIONS();
0434
0435 {
0436 bool destroyed = false;
0437 BOOST_CHECK(!destroyed);
0438 {
0439 Any a{std::in_place_type<D2>, &destroyed};
0440 BOOST_CHECK(!destroyed);
0441 }
0442 BOOST_CHECK(destroyed);
0443 }
0444 CHECK_ANY_ALLOCATIONS();
0445 }
0446
0447 struct D3 {
0448 std::size_t* destroyed{nullptr};
0449 std::array<char, 512> blob{};
0450
0451 explicit D3(std::size_t* d) : destroyed{d} {}
0452
0453 ~D3() { (*destroyed)++; }
0454 };
0455
0456 BOOST_AUTO_TEST_CASE(LeakCheck) {
0457 std::size_t destroyed = 0;
0458 for (std::size_t i = 0; i < 10000; i++) {
0459 {
0460 BOOST_CHECK_EQUAL(destroyed, i);
0461 Any a;
0462 BOOST_CHECK_EQUAL(destroyed, i);
0463 a = Any{std::in_place_type<D3>, &destroyed};
0464 BOOST_CHECK_EQUAL(destroyed, i);
0465 }
0466 BOOST_CHECK_EQUAL(destroyed, i + 1);
0467 }
0468 CHECK_ANY_ALLOCATIONS();
0469 }
0470
0471 struct LifecycleCounters {
0472 std::size_t nDestroy = 0;
0473 std::size_t nCopyConstruct = 0;
0474 std::size_t nCopy = 0;
0475 std::size_t nMoveConstruct = 0;
0476 std::size_t nMove = 0;
0477 };
0478
0479 template <std::size_t PADDING>
0480 struct Lifecycle;
0481
0482 template <>
0483 struct Lifecycle<0> {
0484 LifecycleCounters* counters;
0485
0486 explicit Lifecycle(LifecycleCounters* _counters) : counters{_counters} {}
0487
0488 Lifecycle(Lifecycle&& o) {
0489 counters = o.counters;
0490 counters->nMoveConstruct++;
0491 }
0492
0493 Lifecycle& operator=(Lifecycle&& o) {
0494 counters = o.counters;
0495 counters->nMove++;
0496 return *this;
0497 }
0498
0499 Lifecycle(const Lifecycle& o) {
0500 counters = o.counters;
0501 counters->nCopyConstruct++;
0502 }
0503
0504 Lifecycle& operator=(const Lifecycle& o) {
0505 counters = o.counters;
0506 counters->nCopy++;
0507 return *this;
0508 }
0509
0510 ~Lifecycle() { counters->nDestroy++; }
0511 };
0512
0513 template <std::size_t PADDING>
0514 struct Lifecycle : public Lifecycle<0> {
0515 std::array<char, PADDING> m_padding{};
0516
0517 explicit Lifecycle(LifecycleCounters* _counters) : Lifecycle<0>(_counters) {}
0518 };
0519
0520 template <std::size_t PADDING>
0521 struct LifecycleHandle {
0522 LifecycleCounters counters;
0523 Lifecycle<PADDING> inner;
0524
0525 LifecycleHandle() : counters{}, inner{&counters} {}
0526 };
0527
0528 #define checkCounters() \
0529 do { \
0530 BOOST_REQUIRE_EQUAL(l.counters.nCopy, counters.nCopy); \
0531 BOOST_REQUIRE_EQUAL(l.counters.nCopyConstruct, counters.nCopyConstruct); \
0532 BOOST_REQUIRE_EQUAL(l.counters.nMove, counters.nMove); \
0533 BOOST_REQUIRE_EQUAL(l.counters.nMoveConstruct, counters.nMoveConstruct); \
0534 BOOST_REQUIRE_EQUAL(l.counters.nDestroy, counters.nDestroy); \
0535 } while (0)
0536
0537 #define makeCounter(counter, n) \
0538 do { \
0539 counter += n; \
0540 checkCounters(); \
0541 } while (0)
0542
0543 #define incCopyConstruct(n) makeCounter(counters.nCopyConstruct, n)
0544 #define incCopy(n) makeCounter(counters.nCopy, n)
0545 #define incMoveConstruct(n) makeCounter(counters.nMoveConstruct, n)
0546 #define incMove(n) makeCounter(counters.nMove, n)
0547 #define incDestroy(n) makeCounter(counters.nDestroy, n)
0548
0549 BOOST_AUTO_TEST_CASE(LifeCycleSmall) {
0550 LifecycleCounters counters;
0551 LifecycleHandle<0> l;
0552
0553 checkCounters();
0554
0555 {
0556 const auto& o = l.inner;
0557 Any a{o};
0558 incCopyConstruct(1);
0559
0560 const auto& _a = a;
0561 {
0562 Any b{_a};
0563 incCopyConstruct(1);
0564 }
0565 incDestroy(1);
0566
0567 {
0568 Any b;
0569 b = _a;
0570 incCopyConstruct(1);
0571 b = _a;
0572 incCopy(1);
0573 }
0574 incDestroy(1);
0575
0576 {
0577 Any b{a};
0578 incCopyConstruct(1);
0579 b = a;
0580 incCopy(1);
0581 }
0582 incDestroy(1);
0583
0584 {
0585 auto _a2 = a;
0586 incCopyConstruct(1);
0587 Any b;
0588 b = std::move(_a2);
0589 incMoveConstruct(1);
0590 auto _a3 = a;
0591 incCopyConstruct(1);
0592 b = std::move(_a3);
0593 incMove(1);
0594 }
0595 incDestroy(3);
0596 }
0597 incDestroy(1);
0598
0599 checkCounters();
0600
0601 CHECK_ANY_ALLOCATIONS();
0602 }
0603
0604 BOOST_AUTO_TEST_CASE(LifeCycleHeap) {
0605 LifecycleCounters counters;
0606 LifecycleHandle<512> l;
0607
0608 checkCounters();
0609
0610 {
0611 const auto& o = l.inner;
0612 Any a{o};
0613 incCopyConstruct(1);
0614
0615 const auto& _a = a;
0616 {
0617 Any b{_a};
0618 incCopyConstruct(1);
0619 }
0620 incDestroy(1);
0621
0622 {
0623 Any b;
0624 b = _a;
0625 incCopyConstruct(1);
0626 b = _a;
0627 incCopy(1);
0628 }
0629 incDestroy(1);
0630
0631 {
0632 Any b{a};
0633 incCopyConstruct(1);
0634 b = a;
0635 incCopy(1);
0636 }
0637 incDestroy(1);
0638
0639 {
0640 Any _a2 = a;
0641 incCopyConstruct(1);
0642 Any b;
0643 b = std::move(_a2);
0644
0645
0646 Any _a3 = a;
0647 incCopyConstruct(1);
0648 b = std::move(_a3);
0649
0650 incDestroy(1);
0651 }
0652 incDestroy(1);
0653 }
0654 incDestroy(1);
0655
0656 checkCounters();
0657
0658 CHECK_ANY_ALLOCATIONS();
0659 }
0660
0661 BOOST_AUTO_TEST_CASE(AnyMoveOnlyMoveOnlyTypes) {
0662 using MoveOnlyAny = Acts::AnyMoveOnly;
0663
0664 using Ptr = std::unique_ptr<int>;
0665
0666
0667 {
0668 auto ptr = std::make_unique<int>(42);
0669 MoveOnlyAny a{std::move(ptr)};
0670 BOOST_CHECK(!!a);
0671 Ptr const* storedPtr = a.asPtr<Ptr>();
0672 BOOST_REQUIRE_NE(storedPtr, nullptr);
0673 BOOST_CHECK_NE(storedPtr->get(), nullptr);
0674 BOOST_CHECK_EQUAL(**storedPtr, 42);
0675 }
0676
0677
0678 {
0679 auto ptr = std::make_unique<int>(7);
0680 MoveOnlyAny a{std::move(ptr)};
0681 MoveOnlyAny b = std::move(a);
0682
0683 BOOST_CHECK(!!b);
0684 Ptr const* bPtr = b.asPtr<Ptr>();
0685 BOOST_REQUIRE_NE(bPtr, nullptr);
0686 int val = **bPtr;
0687 BOOST_CHECK_EQUAL(val, 7);
0688 }
0689
0690
0691 static_assert(!std::is_copy_constructible_v<MoveOnlyAny>);
0692 static_assert(!std::is_copy_assignable_v<MoveOnlyAny>);
0693 }
0694
0695 BOOST_AUTO_TEST_CASE(AnyTake) {
0696
0697 {
0698 Any a{42};
0699 BOOST_CHECK(!!a);
0700 int val = a.take<int>();
0701 BOOST_CHECK_EQUAL(val, 42);
0702 BOOST_CHECK(!a);
0703 }
0704
0705
0706 {
0707 auto ptr = std::make_unique<int>(99);
0708 Acts::AnyMoveOnly a{std::move(ptr)};
0709 BOOST_CHECK(!!a);
0710 auto taken = a.take<std::unique_ptr<int>>();
0711 BOOST_REQUIRE_NE(taken.get(), nullptr);
0712 BOOST_CHECK_EQUAL(*taken, 99);
0713 BOOST_CHECK(!a);
0714 }
0715
0716
0717 {
0718 Any a{42};
0719 BOOST_CHECK_THROW(a.take<float>(), std::bad_any_cast);
0720 BOOST_CHECK(!!a);
0721 BOOST_CHECK_EQUAL(a.as<int>(), 42);
0722 }
0723
0724
0725 {
0726 Any a;
0727 BOOST_CHECK_THROW(a.take<int>(), std::bad_any_cast);
0728 }
0729 }
0730
0731 BOOST_AUTO_TEST_SUITE_END()
0732
0733 }