Coverage Report

Created: 2026-03-05 16:18

test/zserio/AnyTest.cpp
Line
Count
Source
1
#include <memory>
2
#include <tuple>
3
#include <vector>
4
5
#include "any_test/AnyTest.h"
6
#include "gtest/gtest.h"
7
#include "zserio/Any.h"
8
#include "zserio/ppmr/PropagatingPolymorphicAllocator.h"
9
10
#include "TrackingAllocator.h"
11
12
namespace zserio
13
{
14
15
namespace
16
{
17
18
class SmallObject
19
{
20
public:
21
    SmallObject() :
22
2
            m_value(0)
23
2
    {}
24
25
    bool operator==(const SmallObject& other) const
26
38
    {
27
38
        return m_value == other.m_value;
28
38
    }
29
30
private:
31
    int m_value;
32
};
33
34
struct BigObject
35
{
36
10
    BigObject() = default;
37
38
    bool operator==(const BigObject& other) const
39
46
    {
40
46
        return std::tie(value1, value2, value3, value4) ==
41
46
                std::tie(other.value1, other.value2, other.value3, other.value4);
42
46
    }
43
44
    uint64_t value1 = 1;
45
    uint64_t value2 = 2;
46
    uint64_t value3 = 3;
47
    uint64_t value4 = 4;
48
};
49
50
} // namespace
51
52
class AnyTest : public ::testing::Test
53
{
54
protected:
55
    template <typename T, typename ALLOC>
56
    void testAny(const T& value, const ALLOC& allocator)
57
8
    {
58
8
        testEmptyConstructor<ALLOC>();
59
8
        testAllocatorConstructor(allocator);
60
8
        testLvalueConstructor(value, allocator);
61
8
        testRvalueConstructor(value, allocator);
62
8
        testCopyConstructor(value, allocator);
63
8
        testCopyConstructorWithAllocator(value, allocator);
64
8
        testCopyAssignmentOperator(value, allocator);
65
8
        testMoveConstructor(value, allocator);
66
8
        testMoveConstructorWithAllocator(value, allocator);
67
8
        testMoveAssignmentOperator(value, allocator);
68
8
        testMoveAssignmentValueOperator(value, allocator);
69
8
        testReset(value, allocator);
70
8
        testSetGet(value, allocator);
71
8
        testIsType(value, allocator);
72
8
        testHasValue(value, allocator);
73
8
        testSwap(value, allocator);
74
8
        testEmplace(value, allocator);
75
8
        testGetIf(value, allocator);
76
8
    }
77
78
private:
79
    template <typename ALLOC>
80
    void testEmptyConstructor()
81
8
    {
82
8
        BasicAny<ALLOC> any;
83
8
        ASSERT_FALSE(any.hasValue());
84
8
    }
85
86
    template <typename ALLOC>
87
    void testAllocatorConstructor(const ALLOC& allocator)
88
8
    {
89
8
        BasicAny<ALLOC> any(allocator);
90
8
        ASSERT_FALSE(any.hasValue());
91
8
        ASSERT_EQ(allocator, any.get_allocator());
92
8
    }
93
94
    template <typename T, typename ALLOC>
95
    void testLvalueConstructor(const T& value, const ALLOC& allocator)
96
8
    {
97
8
        const void* origAddress = &value;
98
8
        BasicAny<ALLOC> any(value, allocator);
99
100
8
        const T& anyValue = any.template get<T>();
101
8
        ASSERT_NE(origAddress, &anyValue);
102
8
        ASSERT_EQ(value, anyValue);
103
8
    }
104
105
    template <typename ALLOC>
106
    void testRvalueConstructor(const std::vector<int>& value, const ALLOC& allocator)
107
2
    {
108
2
        std::vector<int> origValue(value);
109
2
        const void* origAddress = origValue.data();
110
2
        BasicAny<ALLOC> any(std::move(origValue), allocator);
111
112
2
        const std::vector<int>& anyValue = any.template get<std::vector<int>>();
113
2
        ASSERT_EQ(origAddress, anyValue.data());
114
2
        ASSERT_EQ(value, anyValue);
115
2
    }
116
117
    template <typename T, typename ALLOC>
118
    void testRvalueConstructor(const T& value, const ALLOC& allocator)
119
6
    {
120
6
        T origValue(value);
121
6
        BasicAny<ALLOC> any(std::move(origValue), allocator);
122
123
6
        const T& anyValue = any.template get<T>();
124
6
        ASSERT_EQ(value, anyValue);
125
6
    }
126
127
    template <typename T, typename ALLOC>
128
    void testCopyConstructor(const T& value, const ALLOC& allocator)
129
8
    {
130
8
        BasicAny<ALLOC> any(allocator);
131
132
        // copy empty holder
133
8
        BasicAny<ALLOC> anyEmptyCopy(any);
134
8
        ASSERT_FALSE(anyEmptyCopy.hasValue());
135
136
        // copy filled holder
137
8
        any.set(value);
138
8
        BasicAny<ALLOC> anyCopy(any);
139
8
        ASSERT_EQ(value, anyCopy.template get<T>());
140
8
    }
141
142
    template <typename T, typename ALLOC>
143
    void testCopyConstructorWithAllocator(const T& value, const ALLOC& allocator)
144
8
    {
145
8
        BasicAny<ALLOC> any(allocator);
146
147
        // copy empty holder
148
8
        BasicAny<ALLOC> anyEmptyCopy(any);
149
8
        ASSERT_FALSE(anyEmptyCopy.hasValue());
150
151
        // copy filled holder
152
8
        any.set(value);
153
8
        const size_t numAllocations = allocator.numAllocs();
154
8
        const ALLOC newAllocator;
155
8
        BasicAny<ALLOC> anyCopy(any, newAllocator);
156
8
        ASSERT_EQ(value, anyCopy.template get<T>());
157
8
        ASSERT_EQ(numAllocations, allocator.numAllocs()); // no allocation and deallocations in old allocator
158
8
    }
159
160
    template <typename T, typename ALLOC>
161
    void testCopyAssignmentOperator(const T& value, const ALLOC& allocator)
162
8
    {
163
8
        BasicAny<ALLOC> any(allocator);
164
165
        // assignment empty holder
166
8
        BasicAny<ALLOC> anyEmpty;
167
8
        anyEmpty = any;
168
8
        ASSERT_FALSE(anyEmpty.hasValue());
169
170
        // assignment filled holder to itself
171
8
        any.set(value);
172
8
        BasicAny<ALLOC>& anyRef(any);
173
8
        anyRef = any;
174
8
        ASSERT_EQ(value, anyRef.template get<T>());
175
176
        // assignment filled holder
177
8
        BasicAny<ALLOC> anyCopy;
178
8
        anyCopy = any;
179
8
        ASSERT_EQ(value, anyCopy.template get<T>());
180
8
    }
181
182
    template <typename T, typename ALLOC>
183
    void testMoveConstructor(const T& value, const ALLOC& allocator)
184
8
    {
185
8
        {
186
            // move empty holder
187
8
            BasicAny<ALLOC> any(allocator);
188
8
            BasicAny<ALLOC> anyMove(std::move(any));
189
8
            ASSERT_FALSE(anyMove.hasValue());
190
8
        }
191
192
8
        {
193
            // move filled holder
194
8
            BasicAny<ALLOC> any(allocator);
195
8
            any.set(value);
196
8
            const size_t numAllocations = allocator.numAllocs();
197
8
            BasicAny<ALLOC> anyMove(std::move(any));
198
8
            ASSERT_EQ(value, anyMove.template get<T>());
199
8
            ASSERT_EQ(numAllocations, allocator.numAllocs()); // no allocations and deallocations
200
8
        }
201
8
    }
202
203
    template <typename T, typename ALLOC>
204
    void testMoveConstructorWithAllocator(const T& value, const ALLOC& allocator)
205
8
    {
206
8
        {
207
            // empty holder with the same allocator
208
8
            BasicAny<ALLOC> any(allocator);
209
8
            BasicAny<ALLOC> anyMove(std::move(any));
210
8
            ASSERT_FALSE(anyMove.hasValue());
211
8
        }
212
213
8
        {
214
            // empty holder with the different allocator
215
8
            BasicAny<ALLOC> any(allocator);
216
8
            const ALLOC newAllocator;
217
8
            BasicAny<ALLOC> anyMove(std::move(any), newAllocator);
218
8
            ASSERT_FALSE(anyMove.hasValue());
219
8
        }
220
221
8
        {
222
            // filled holder with the same allocator
223
8
            BasicAny<ALLOC> any(allocator);
224
8
            any.set(value);
225
8
            const size_t numAllocations = allocator.numAllocs();
226
8
            BasicAny<ALLOC> anyMove(std::move(any), allocator);
227
8
            ASSERT_EQ(value, anyMove.template get<T>());
228
8
            ASSERT_EQ(numAllocations, allocator.numAllocs()); // no allocations and deallocations
229
8
        }
230
231
8
        {
232
            // filled holder with the different allocator
233
8
            BasicAny<ALLOC> any(allocator);
234
8
            any.set(value);
235
8
            const size_t numAllocations = allocator.numAllocs();
236
8
            const ALLOC newAllocator;
237
8
            BasicAny<ALLOC> anyMove(std::move(any), newAllocator);
238
8
            ASSERT_EQ(value, anyMove.template get<T>());
239
8
            ASSERT_TRUE(numAllocations >= allocator.numAllocs()); // no further allocations
240
8
        }
241
8
    }
242
243
    template <typename T, typename ALLOC>
244
    void testMoveAssignmentOperator(const T& value, const ALLOC& allocator)
245
8
    {
246
8
        {
247
            // assignment empty holder
248
8
            BasicAny<ALLOC> any(allocator);
249
8
            BasicAny<ALLOC> anyMove;
250
8
            anyMove = std::move(any);
251
8
            ASSERT_FALSE(anyMove.hasValue());
252
8
        }
253
254
8
        {
255
            // assignment filled holder to itself
256
8
            BasicAny<ALLOC> any(allocator);
257
8
            any.set(value);
258
8
            const size_t numAllocations = allocator.numAllocs();
259
8
            BasicAny<ALLOC>& anyRef = any;
260
8
            anyRef = std::move(any);
261
8
            ASSERT_EQ(value, anyRef.template get<T>());
262
8
            ASSERT_EQ(numAllocations, allocator.numAllocs()); // no allocations and deallocations
263
8
        }
264
265
8
        {
266
            // assignment filled holder
267
8
            BasicAny<ALLOC> any(allocator);
268
8
            any.set(value);
269
8
            const size_t numAllocations = allocator.numAllocs();
270
8
            BasicAny<ALLOC> anyMove;
271
8
            anyMove = std::move(any);
272
8
            ASSERT_EQ(value, anyMove.template get<T>());
273
8
            ASSERT_TRUE(numAllocations >= allocator.numAllocs()); // no further allocations
274
8
        }
275
8
    }
276
277
    template <typename T, typename ALLOC>
278
    void testMoveAssignmentValueOperator(const T& value, const ALLOC& allocator)
279
8
    {
280
8
        {
281
            // assignment empty holder
282
8
            const T valueCopy(value);
283
8
            BasicAny<ALLOC> any(allocator);
284
8
            const size_t numAllocations = allocator.numAllocs();
285
8
            BasicAny<ALLOC> anyMove;
286
8
            anyMove = std::move(valueCopy);
287
8
            ASSERT_EQ(value, anyMove.template get<T>());
288
8
            ASSERT_EQ(numAllocations, allocator.numAllocs()); // no allocations and deallocations
289
8
        }
290
291
8
        {
292
            // assignment filled holder
293
8
            const T valueCopy(value);
294
8
            BasicAny<ALLOC> any(allocator);
295
8
            any.set(value);
296
8
            const size_t numAllocations = allocator.numAllocs();
297
8
            BasicAny<ALLOC> anyMove;
298
8
            anyMove = std::move(valueCopy);
299
8
            ASSERT_EQ(value, anyMove.template get<T>());
300
8
            ASSERT_EQ(numAllocations, allocator.numAllocs()); // no allocations and deallocations
301
8
        }
302
8
    }
303
304
    template <typename T, typename ALLOC>
305
    void testReset(const T& value, const ALLOC& allocator)
306
8
    {
307
8
        BasicAny<ALLOC> any(value, allocator);
308
8
        ASSERT_TRUE(any.hasValue());
309
310
8
        any.reset();
311
8
        ASSERT_FALSE(any.hasValue());
312
313
8
        any = value;
314
8
        ASSERT_TRUE(any.hasValue());
315
8
        ASSERT_EQ(value, any.template get<T>());
316
8
    }
317
318
    template <typename T, typename ALLOC>
319
    void testSetGet(const T& value, const ALLOC& allocator)
320
8
    {
321
8
        BasicAny<ALLOC> any(allocator);
322
8
        ASSERT_THROW(any.template get<int>(), zserio::CppRuntimeException);
323
324
8
        any.set(value);
325
8
        ASSERT_EQ(value, any.template get<T>());
326
327
8
        const int intValue = 0xDEAD;
328
8
        any.set(intValue);
329
8
        ASSERT_EQ(intValue, any.template get<int>());
330
8
        ASSERT_THROW(any.template get<float>(), CppRuntimeException);
331
332
8
        const float floatValue = 3.14F;
333
8
        any.set(floatValue);
334
8
        ASSERT_THROW(any.template get<int>(), CppRuntimeException);
335
8
        ASSERT_EQ(floatValue, any.template get<float>());
336
337
8
        const BigObject bigObjectValue;
338
8
        any.set(bigObjectValue);
339
8
        ASSERT_THROW(any.template get<int>(), CppRuntimeException);
340
8
        ASSERT_EQ(bigObjectValue, any.template get<BigObject>());
341
8
    }
342
343
    template <typename T, typename ALLOC>
344
    void testIsType(const T& value, const ALLOC& allocator)
345
8
    {
346
8
        BasicAny<ALLOC> any(allocator);
347
8
        ASSERT_FALSE(any.template isType<int>());
348
349
8
        any.set(value);
350
8
        ASSERT_TRUE(any.template isType<T>());
351
352
8
        const int intValue = 0xDEAD;
353
8
        any.set(intValue);
354
8
        ASSERT_TRUE(any.template isType<int>());
355
356
8
        const float floatValue = 3.14F;
357
8
        any.set(floatValue);
358
8
        ASSERT_TRUE(any.template isType<float>());
359
8
        ASSERT_FALSE(any.template isType<int>());
360
8
    }
361
362
    template <typename T, typename ALLOC>
363
    void testHasValue(const T& value, const ALLOC& allocator)
364
8
    {
365
8
        BasicAny<ALLOC> any(allocator);
366
8
        ASSERT_FALSE(any.hasValue());
367
368
8
        any.set(value);
369
8
        ASSERT_TRUE(any.hasValue());
370
371
8
        const int intValue = 0xDEAD;
372
8
        any.set(intValue);
373
8
        ASSERT_TRUE(any.hasValue());
374
375
8
        any.reset();
376
8
        ASSERT_FALSE(any.hasValue());
377
8
    }
378
379
    template <typename T, typename ALLOC>
380
    void testSwap(const T& value, const ALLOC& allocator)
381
8
    {
382
8
        BasicAny<ALLOC> empty(allocator);
383
8
        ASSERT_TRUE(!empty.hasValue());
384
8
        BasicAny<ALLOC> any(allocator);
385
8
        any.set(value);
386
8
        ASSERT_TRUE(any.template isType<T>());
387
8
        ASSERT_EQ(any.template get<T>(), value);
388
389
8
        any.swap(empty);
390
8
        ASSERT_TRUE(!any.hasValue());
391
8
        ASSERT_TRUE(empty.template isType<T>());
392
8
        ASSERT_EQ(empty.template get<T>(), value);
393
394
8
        any.set(150);
395
8
        any.swap(empty);
396
8
        ASSERT_TRUE(empty.template isType<int>());
397
8
        ASSERT_EQ(empty.template get<int>(), 150);
398
8
        ASSERT_TRUE(any.template isType<T>());
399
8
        ASSERT_EQ(any.template get<T>(), value);
400
8
    }
401
402
    template <typename T, typename ALLOC>
403
    void testEmplace(const T& value, const ALLOC& allocator)
404
8
    {
405
8
        BasicAny<ALLOC> any(allocator);
406
8
        any.template emplace<T>(value);
407
8
        ASSERT_TRUE(any.template isType<T>());
408
8
        ASSERT_EQ(any.template get<T>(), value);
409
8
    }
410
411
    template <typename T, typename ALLOC>
412
    void testGetIf(const T& value, const ALLOC& allocator)
413
8
    {
414
8
        BasicAny<ALLOC> any(allocator);
415
8
        ASSERT_TRUE(any.template get_if<T>() == nullptr);
416
8
        any.set(value);
417
8
        ASSERT_TRUE(any.template get_if<T>() != nullptr);
418
8
        ASSERT_EQ(any.template get_if<T>(), &any.template get<T>());
419
8
        ASSERT_TRUE(any.template get_if<std::string>() == nullptr);
420
8
        ASSERT_TRUE(any.template get_if<long>() == nullptr);
421
8
        any.reset();
422
8
        ASSERT_TRUE(any.template get_if<T>() == nullptr);
423
8
    }
424
};
425
426
TEST_F(AnyTest, integerPropagatingAllocator)
427
1
{
428
1
    const int value = 0xDEAD;
429
1
    const TrackingAllocator<uint8_t> allocator;
430
1
    testAny(value, allocator);
431
1
}
432
433
TEST_F(AnyTest, integerNonPropagatingAllocator)
434
1
{
435
1
    const int value = 0xDEAD;
436
1
    const TrackingAllocatorNonProp<uint8_t> allocator;
437
1
    testAny(value, allocator);
438
1
}
439
440
TEST_F(AnyTest, vectorPropagatingAllocator)
441
1
{
442
1
    const std::vector<int> value{1, 2, 3};
443
1
    const TrackingAllocator<uint8_t> allocator;
444
1
    testAny(value, allocator);
445
1
}
446
447
TEST_F(AnyTest, vectorNonPropagatingAllocator)
448
1
{
449
1
    const std::vector<int> value{1, 2, 3};
450
1
    const TrackingAllocatorNonProp<uint8_t> allocator;
451
1
    testAny(value, allocator);
452
1
}
453
454
TEST_F(AnyTest, smallObjectPropagatingAllocator)
455
1
{
456
1
    const SmallObject value;
457
1
    const TrackingAllocator<uint8_t> allocator;
458
1
    testAny(value, allocator);
459
1
}
460
461
TEST_F(AnyTest, smallObjectNonPropagatingAllocator)
462
1
{
463
1
    const SmallObject value;
464
1
    const TrackingAllocatorNonProp<uint8_t> allocator;
465
1
    testAny(value, allocator);
466
1
}
467
468
TEST_F(AnyTest, bigObjectPropagatingAllocator)
469
1
{
470
1
    const BigObject value;
471
1
    const TrackingAllocator<uint8_t> allocator;
472
1
    testAny(value, allocator);
473
1
}
474
475
TEST_F(AnyTest, bigObjectNonPropagatingAllocator)
476
1
{
477
1
    const BigObject value;
478
1
    const TrackingAllocatorNonProp<uint8_t> allocator;
479
1
    testAny(value, allocator);
480
1
}
481
482
TEST_F(AnyTest, unexceptedCallsHeapHolder)
483
1
{
484
1
    const TrackingAllocator<uint8_t> allocator;
485
1
    detail::HeapHolder<BigObject, TrackingAllocator<uint8_t>>* holder =
486
1
            detail::HeapHolder<BigObject, TrackingAllocator<uint8_t>>::create(allocator);
487
1
    ASSERT_THROW(holder->clone(nullptr), CppRuntimeException);
488
1
    ASSERT_THROW(holder->move(nullptr), CppRuntimeException);
489
1
    holder->destroy(allocator);
490
1
}
491
492
TEST_F(AnyTest, unexceptedCallsNonHeapHolder)
493
1
{
494
1
    const TrackingAllocator<uint8_t> allocator;
495
1
    using MaxInPlaceType = std::aligned_storage<3 * sizeof(void*), alignof(void*)>::type;
496
1
    MaxInPlaceType inPlace = MaxInPlaceType();
497
1
    detail::NonHeapHolder<uint8_t, TrackingAllocator<uint8_t>>* holder =
498
1
            detail::NonHeapHolder<uint8_t, TrackingAllocator<uint8_t>>::create(&inPlace);
499
1
    ASSERT_THROW(holder->clone(allocator), CppRuntimeException);
500
1
    ASSERT_THROW(holder->move(allocator), CppRuntimeException);
501
1
    holder->destroy(allocator);
502
1
}
503
504
TEST_F(AnyTest, sharedLib)
505
1
{
506
1
    auto any1 = AnyTestLib::createAnyStdInt();
507
1
    ASSERT_TRUE(any1.isType<int>());
508
1
    ASSERT_FALSE(any1.isType<float>());
509
1
    auto any2 = AnyTestLib::createAnyInt32();
510
1
    ASSERT_TRUE(any2.isType<zserio::Int32>());
511
1
    ASSERT_FALSE(any2.isType<zserio::Int64>());
512
1
    auto any3 = AnyTestLib::createAnyString();
513
1
    ASSERT_TRUE(any3.isType<zserio::String>());
514
1
    ASSERT_FALSE(any3.isType<zserio::Int32>());
515
1
}
516
517
TEST_F(AnyTest, prettyFunction)
518
1
{
519
1
    Any any;
520
1
    any.set(std::vector<int>());
521
1
    ASSERT_TRUE(any.isType<std::vector<int>>());
522
1
    ASSERT_TRUE((any.isType<std::vector<int, std::allocator<int>>>()));
523
1
    any.set(std::vector<int, std::allocator<int>>());
524
1
    ASSERT_TRUE(any.isType<std::vector<int>>());
525
1
    ASSERT_TRUE((any.isType<std::vector<int, std::allocator<int>>>()));
526
1
}
527
528
} // namespace zserio