1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <fbl/alloc_checker.h>
6#include <fbl/intrusive_double_list.h>
7#include <fbl/ref_counted.h>
8#include <fbl/ref_ptr.h>
9#include <fbl/slab_allocator.h>
10#include <fbl/unique_ptr.h>
11#include <unittest/unittest.h>
12
13namespace {
14
15enum class ConstructType {
16    DEFAULT,
17    LVALUE_REF,
18    RVALUE_REF,
19    L_THEN_R_REF,
20};
21
22// Test objects.
23class TestBase {
24public:
25    // Various constructor forms to exercise SlabAllocator::New
26    TestBase()                       : ctype_(ConstructType::DEFAULT)    { ++allocated_obj_count_; }
27    explicit TestBase(const size_t&) : ctype_(ConstructType::LVALUE_REF) { ++allocated_obj_count_; }
28    explicit TestBase(size_t&&)      : ctype_(ConstructType::RVALUE_REF) { ++allocated_obj_count_; }
29    explicit TestBase(const size_t&, size_t&&)
30        : ctype_(ConstructType::L_THEN_R_REF) {
31        ++allocated_obj_count_;
32    }
33
34    virtual ~TestBase() { --allocated_obj_count_; }
35
36    ConstructType ctype() const { return ctype_; }
37
38    static void Reset() { allocated_obj_count_ = 0; }
39    static size_t allocated_obj_count() { return allocated_obj_count_; }
40    const uint8_t* payload() const { return payload_; }
41
42private:
43    const ConstructType ctype_;
44    uint8_t             payload_[13];   // 13 bytes, just to make the size/alignment strange
45
46    static size_t allocated_obj_count_;
47};
48
49// Static storage.
50size_t TestBase::allocated_obj_count_;
51
52template <typename SATraits, typename = void> struct ReleaseHelper;
53
54template <typename SATraits>
55struct ReleaseHelper<SATraits, typename fbl::enable_if<
56                        (SATraits::PtrTraits::IsManaged == false) &&
57                        (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::INSTANCED)
58                    >::type> {
59    static void ReleasePtr(fbl::SlabAllocator<SATraits>& allocator,
60                           typename SATraits::PtrType& ptr) {
61        // Instanced slab allocators should static_assert if you attempt to
62        // expand their delete method.
63#if TEST_WILL_NOT_COMPILE || 0
64        allocator.Delete(ptr);
65#else
66        delete ptr;
67#endif
68    }
69};
70
71template <typename SATraits>
72struct ReleaseHelper<SATraits, typename fbl::enable_if<
73                        (SATraits::PtrTraits::IsManaged == false) &&
74                        (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::MANUAL_DELETE)
75                    >::type> {
76    static void ReleasePtr(fbl::SlabAllocator<SATraits>& allocator,
77                           typename SATraits::PtrType& ptr) {
78        // SlabAllocated<> objects which come from MANUAL_DELETE flavors of slab
79        // allocators should have their delete operator protected in order to
80        // prevent someone from calling delete on the object.
81#if TEST_WILL_NOT_COMPILE || 0
82        delete ptr;
83#else
84        allocator.Delete(ptr);
85#endif
86    }
87};
88
89template <typename SATraits>
90struct ReleaseHelper<SATraits, typename fbl::enable_if<
91                        (SATraits::PtrTraits::IsManaged == true) &&
92                        (SATraits::AllocatorFlavor != fbl::SlabAllocatorFlavor::STATIC)
93                    >::type> {
94    static void ReleasePtr(fbl::SlabAllocator<SATraits>&, typename SATraits::PtrType& ptr) {
95        ptr = nullptr;
96    }
97};
98
99template <typename SATraits>
100struct ReleaseHelper<SATraits, typename fbl::enable_if<
101                        (SATraits::PtrTraits::IsManaged == false) &&
102                        (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::STATIC)
103                    >::type> {
104    static void ReleasePtr(typename SATraits::PtrType& ptr) {
105        delete ptr;
106    }
107};
108
109template <typename SATraits>
110struct ReleaseHelper<SATraits, typename fbl::enable_if<
111                        (SATraits::PtrTraits::IsManaged == true) &&
112                        (SATraits::AllocatorFlavor == fbl::SlabAllocatorFlavor::STATIC)
113                    >::type> {
114    static void ReleasePtr(typename SATraits::PtrType& ptr) {
115        ptr = nullptr;
116    }
117};
118
119// Traits which define the various test flavors.
120template <typename LockType,
121          fbl::SlabAllocatorFlavor AllocatorFlavor = fbl::SlabAllocatorFlavor::INSTANCED,
122          bool ENABLE_OBJ_COUNT = false>
123struct UnmanagedTestTraits {
124    class ObjType;
125    using PtrType       = ObjType*;
126    using AllocTraits   = fbl::SlabAllocatorTraits
127        <PtrType, 1024, LockType, AllocatorFlavor, ENABLE_OBJ_COUNT>;
128    using AllocatorType = fbl::SlabAllocator<AllocTraits>;
129    using RefList       = fbl::DoublyLinkedList<PtrType>;
130
131    class ObjType : public TestBase,
132                    public fbl::SlabAllocated<AllocTraits>,
133                    public fbl::DoublyLinkedListable<PtrType> {
134    public:
135        ObjType()                                     : TestBase() { }
136        explicit ObjType(const size_t& val)           : TestBase(val) { }
137        explicit ObjType(size_t&& val)                : TestBase(fbl::move(val)) { }
138        explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, fbl::move(b)) { }
139    };
140
141    static constexpr size_t MaxSlabs  = 4;
142    static constexpr bool   IsManaged = false;
143    static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
144};
145
146template <typename LockType, bool ENABLE_OBJ_COUNT = false>
147struct UniquePtrTestTraits {
148    class ObjType;
149    using PtrType       = fbl::unique_ptr<ObjType>;
150    using AllocTraits   = fbl::SlabAllocatorTraits
151        <PtrType, 1024, LockType, fbl::SlabAllocatorFlavor::INSTANCED, ENABLE_OBJ_COUNT>;
152    using AllocatorType = fbl::SlabAllocator<AllocTraits>;
153    using RefList       = fbl::DoublyLinkedList<PtrType>;
154
155    class ObjType : public TestBase,
156                    public fbl::SlabAllocated<AllocTraits>,
157                    public fbl::DoublyLinkedListable<PtrType> {
158    public:
159        ObjType()                                     : TestBase() { }
160        explicit ObjType(const size_t& val)           : TestBase(val) { }
161        explicit ObjType(size_t&& val)                : TestBase(fbl::move(val)) { }
162        explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, fbl::move(b)) { }
163    };
164
165
166    static constexpr size_t MaxSlabs  = 4;
167    static constexpr bool   IsManaged = true;
168    static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
169};
170
171template <typename LockType, bool ENABLE_OBJ_COUNT = false>
172struct RefPtrTestTraits {
173    class ObjType;
174    using PtrType       = fbl::RefPtr<ObjType>;
175    using AllocTraits   = fbl::SlabAllocatorTraits
176        <PtrType, 1024,LockType, fbl::SlabAllocatorFlavor::INSTANCED, ENABLE_OBJ_COUNT>;
177    using AllocatorType = fbl::SlabAllocator<AllocTraits>;
178    using RefList       = fbl::DoublyLinkedList<PtrType>;
179
180    class ObjType : public TestBase,
181                    public fbl::RefCounted<ObjType>,
182                    public fbl::SlabAllocated<AllocTraits>,
183                    public fbl::DoublyLinkedListable<PtrType> {
184    public:
185        ObjType()                                     : TestBase() { }
186        explicit ObjType(const size_t& val)           : TestBase(val) { }
187        explicit ObjType(size_t&& val)                : TestBase(fbl::move(val)) { }
188        explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, fbl::move(b)) { }
189    };
190
191    static constexpr size_t MaxSlabs  = 4;
192    static constexpr bool   IsManaged = true;
193    static constexpr size_t MaxAllocs(size_t slabs) { return AllocatorType::AllocsPerSlab * slabs; }
194};
195
196template <typename, bool> struct ObjCounterHelper;
197
198template <typename SA> struct ObjCounterHelper<SA, true> {
199    static bool CheckObjCount(const SA& allocator, size_t expected) {
200        return (allocator.obj_count() == expected);
201    }
202    static bool CheckMaxObjCount(const SA& allocator, size_t expected) {
203        return (allocator.max_obj_count() == expected);
204    }
205    static void ResetMaxObjCount(SA* allocator) {
206        allocator->ResetMaxObjCount();
207    }
208    static bool StaticCheckObjCount(size_t expected) {
209        return (SA::obj_count() == expected);
210    }
211    static bool StaticCheckMaxObjCount(size_t expected) {
212        return (SA::max_obj_count() == expected);
213    }
214    static void StaticResetMaxObjCount() {
215        SA::ResetMaxObjCount();
216    }
217};
218
219template <typename SA> struct ObjCounterHelper<SA, false> {
220    static bool CheckObjCount(const SA&, size_t) {
221        return true;
222    }
223    static bool CheckMaxObjCount(const SA&, size_t) {
224        return true;
225    }
226    static void ResetMaxObjCount(SA*) {}
227    static bool StaticCheckObjCount(size_t) {
228        return true;
229    }
230    static bool StaticCheckMaxObjCount(size_t) {
231        return true;
232    }
233    static void StaticResetMaxObjCount() {}
234};
235
236template <typename Traits>
237bool do_slab_test(typename Traits::AllocatorType& allocator, size_t test_allocs) {
238    BEGIN_TEST;
239
240    const size_t MAX_ALLOCS = Traits::MaxAllocs(allocator.max_slabs());
241    typename Traits::RefList ref_list;
242    const bool ENB_OBJ_COUNT = Traits::AllocTraits::ENABLE_OBJ_COUNT;
243    using AllocatorType = typename Traits::AllocatorType;
244
245    ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
246    bool res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount(allocator, 0);
247    EXPECT_TRUE(res);
248    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount(allocator, 0);
249    EXPECT_TRUE(res);
250
251    // Allocate up to the test limit.
252    for (size_t i = 0; i < test_allocs; ++i) {
253        typename Traits::PtrType ptr;
254
255        EXPECT_EQ(fbl::min(i, MAX_ALLOCS), TestBase::allocated_obj_count());
256        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
257            (allocator,TestBase::allocated_obj_count());
258        EXPECT_TRUE(res);
259        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
260            (allocator, TestBase::allocated_obj_count());
261        EXPECT_TRUE(res);
262
263        // Allocate the object; exercise the various constructors
264        switch (i % 4) {
265        case 0: ptr = allocator.New(); break;
266        case 1: ptr = allocator.New(i); break;
267        case 2: ptr = allocator.New(fbl::move(i)); break;
268        case 3: ptr = allocator.New(i, fbl::move(i)); break;
269        }
270
271        if (i < MAX_ALLOCS) {
272            ASSERT_NONNULL(ptr, "Allocation failed when it should not have!");
273            ref_list.push_front(fbl::move(ptr));
274        } else {
275            ASSERT_NULL(ptr, "Allocation succeeded when it should not have!");
276        }
277
278        EXPECT_EQ(fbl::min(i + 1, MAX_ALLOCS), TestBase::allocated_obj_count());
279        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
280            (allocator, TestBase::allocated_obj_count());
281        EXPECT_TRUE(res);
282        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
283            (allocator, TestBase::allocated_obj_count());
284        EXPECT_TRUE(res);
285    }
286
287    // Now remove and de-allocate.
288    size_t max_obj_count = TestBase::allocated_obj_count();
289    size_t i;
290    for (i = 0; !ref_list.is_empty(); ++i) {
291        auto ptr = ref_list.pop_back();
292
293        ASSERT_NONNULL(ptr, "nullptr in ref list!  This should be impossible.");
294        EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS) - i, TestBase::allocated_obj_count());
295        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
296            (allocator, TestBase::allocated_obj_count());
297        EXPECT_TRUE(res);
298        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
299            (allocator, max_obj_count);
300        EXPECT_TRUE(res);
301
302        switch (i % 4) {
303        case 0:
304            EXPECT_EQ(ConstructType::DEFAULT, ptr->ctype());
305            break;
306        case 1:
307            EXPECT_EQ(ConstructType::LVALUE_REF, ptr->ctype());
308            break;
309        case 2:
310            EXPECT_EQ(ConstructType::RVALUE_REF, ptr->ctype());
311            break;
312        case 3:
313            EXPECT_EQ(ConstructType::L_THEN_R_REF, ptr->ctype());
314            break;
315        }
316
317        // Release the reference (how this gets done depends on allocator flavor and pointer type)
318        ReleaseHelper<typename Traits::AllocTraits>::ReleasePtr(allocator, ptr);
319
320        if (i % 2 == 1) {
321            ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
322            max_obj_count = TestBase::allocated_obj_count();
323        }
324        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
325            (allocator, max_obj_count);
326        EXPECT_TRUE(res);
327    }
328
329    EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS), i);
330    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckObjCount
331        (allocator, 0);
332    EXPECT_TRUE(res);
333    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
334        (allocator, i % 2);
335    EXPECT_TRUE(res);
336    ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::ResetMaxObjCount(&allocator);
337    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::CheckMaxObjCount
338        (allocator, 0);
339    EXPECT_TRUE(res);
340
341#if TEST_WILL_NOT_COMPILE || 0
342    allocator.obj_count();
343#endif
344#if TEST_WILL_NOT_COMPILE || 0
345    allocator.max_obj_count();
346#endif
347#if TEST_WILL_NOT_COMPILE || 0
348    allocator.ResetMaxObjCount();
349#endif
350    END_TEST;
351}
352
353template <typename Traits, size_t SlabCount = Traits::MaxSlabs>
354bool slab_test() {
355    BEGIN_TEST;
356    typename Traits::AllocatorType allocator(SlabCount);
357
358    TestBase::Reset();
359
360    EXPECT_TRUE(do_slab_test<Traits>(allocator, 1),
361                "Single allocator test failed");
362
363    EXPECT_TRUE(do_slab_test<Traits>(allocator, Traits::MaxAllocs(SlabCount) / 2),
364                "1/2 capacity allocator test failed");
365
366    EXPECT_TRUE(do_slab_test<Traits>(allocator, Traits::MaxAllocs(SlabCount) + 4),
367                "Over-capacity allocator test failed");
368
369    END_TEST;
370}
371
372template <typename LockType, bool ENABLE_OBJ_COUNT = false>
373struct StaticUnmanagedTestTraits {
374    class ObjType;
375    using PtrType       = ObjType*;
376    using AllocTraits   = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, ENABLE_OBJ_COUNT>;
377    using AllocatorType = fbl::SlabAllocator<AllocTraits>;
378    using RefList       = fbl::DoublyLinkedList<PtrType>;
379
380    class ObjType : public TestBase,
381                    public fbl::SlabAllocated<AllocTraits>,
382                    public fbl::DoublyLinkedListable<PtrType> {
383    public:
384        ObjType()                                     : TestBase() { }
385        explicit ObjType(const size_t& val)           : TestBase(val) { }
386        explicit ObjType(size_t&& val)                : TestBase(fbl::move(val)) { }
387        explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, fbl::move(b)) { }
388    };
389
390    static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
391
392    static constexpr size_t MaxSlabs  = 4;
393    static constexpr bool   IsManaged = false;
394};
395template <typename LockType>
396using StaticCountedUnmanagedTestTraits = StaticUnmanagedTestTraits<LockType, true>;
397
398template <typename LockType, bool ENABLE_OBJ_COUNT = false>
399struct StaticUniquePtrTestTraits {
400    class ObjType;
401    using PtrType       = fbl::unique_ptr<ObjType>;
402    using AllocTraits   = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, ENABLE_OBJ_COUNT>;
403    using AllocatorType = fbl::SlabAllocator<AllocTraits>;
404    using RefList       = fbl::DoublyLinkedList<PtrType>;
405
406    class ObjType : public TestBase,
407                    public fbl::SlabAllocated<AllocTraits>,
408                    public fbl::DoublyLinkedListable<PtrType> {
409    public:
410        ObjType()                                     : TestBase() { }
411        explicit ObjType(const size_t& val)           : TestBase(val) { }
412        explicit ObjType(size_t&& val)                : TestBase(fbl::move(val)) { }
413        explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, fbl::move(b)) { }
414    };
415
416    static size_t MaxAllocs() { return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs(); }
417
418    static constexpr size_t MaxSlabs  = 4;
419    static constexpr bool   IsManaged = false;
420};
421
422template <typename LockType>
423using StaticCountedUniquePtrTestTraits = StaticUniquePtrTestTraits<LockType, true>;
424
425template <typename LockType, bool ENABLE_OBJ_COUNT = false>
426struct StaticRefPtrTestTraits {
427    class ObjType;
428    using PtrType       = fbl::RefPtr<ObjType>;
429    using AllocTraits   = fbl::StaticSlabAllocatorTraits<PtrType, 1024, LockType, ENABLE_OBJ_COUNT>;
430    using AllocatorType = fbl::SlabAllocator<AllocTraits>;
431    using RefList       = fbl::DoublyLinkedList<PtrType>;
432
433    class ObjType : public TestBase,
434                    public fbl::SlabAllocated<AllocTraits>,
435                    public fbl::RefCounted<ObjType>,
436                    public fbl::DoublyLinkedListable<PtrType> {
437    public:
438        ObjType()                                     : TestBase() { }
439        explicit ObjType(const size_t& val)           : TestBase(val) { }
440        explicit ObjType(size_t&& val)                : TestBase(fbl::move(val)) { }
441        explicit ObjType(const size_t& a, size_t&& b) : TestBase(a, fbl::move(b)) { }
442    };
443
444    static constexpr size_t MaxSlabs  = 4;
445    static constexpr bool   IsManaged = false;
446
447    static size_t MaxAllocs() {
448        return AllocatorType::AllocsPerSlab * AllocatorType::max_slabs();
449    }
450};
451
452template <typename LockType>
453using StaticCountedRefPtrTestTraits = StaticRefPtrTestTraits<LockType, true>;
454
455template <typename Traits>
456bool do_static_slab_test(size_t test_allocs) {
457    BEGIN_TEST;
458
459    const bool ENB_OBJ_COUNT = Traits::AllocTraits::ENABLE_OBJ_COUNT;
460    using AllocatorType = typename Traits::AllocatorType;
461
462    const size_t MAX_ALLOCS = Traits::MaxAllocs();
463    typename Traits::RefList ref_list;
464    ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
465    bool res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(0);
466    EXPECT_TRUE(res);
467    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(0);
468    EXPECT_TRUE(res);
469
470    // Allocate up to the test limit.
471    for (size_t i = 0; i < test_allocs; ++i) {
472        typename Traits::PtrType ptr;
473
474        EXPECT_EQ(fbl::min(i, MAX_ALLOCS), TestBase::allocated_obj_count());
475        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount
476            (TestBase::allocated_obj_count());
477        EXPECT_TRUE(res);
478        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount
479            (TestBase::allocated_obj_count());
480        EXPECT_TRUE(res);
481
482        // Allocate the object; exercise the various constructors
483        switch (i % 4) {
484        case 0: ptr = AllocatorType::New(); break;
485        case 1: ptr = AllocatorType::New(i); break;
486        case 2: ptr = AllocatorType::New(fbl::move(i)); break;
487        case 3: ptr = AllocatorType::New(i, fbl::move(i)); break;
488        }
489
490        if (i < MAX_ALLOCS) {
491            ASSERT_NONNULL(ptr, "Allocation failed when it should not have!");
492            ref_list.push_front(fbl::move(ptr));
493        } else {
494            ASSERT_NULL(ptr, "Allocation succeeded when it should not have!");
495        }
496
497        EXPECT_EQ(fbl::min(i + 1, MAX_ALLOCS), TestBase::allocated_obj_count());
498        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount
499            (TestBase::allocated_obj_count());
500        EXPECT_TRUE(res);
501        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount
502            (TestBase::allocated_obj_count());
503        EXPECT_TRUE(res);
504    }
505
506    // Now remove and de-allocate.
507    size_t max_obj_count = TestBase::allocated_obj_count();
508    size_t i;
509    for (i = 0; !ref_list.is_empty(); ++i) {
510        auto ptr = ref_list.pop_back();
511
512        ASSERT_NONNULL(ptr, "nullptr in ref list!  This should be impossible.");
513        EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS) - i, TestBase::allocated_obj_count());
514        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount
515            (TestBase::allocated_obj_count());
516        EXPECT_TRUE(res);
517        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(max_obj_count);
518        EXPECT_TRUE(res);
519
520        switch (i % 4) {
521        case 0:
522            EXPECT_EQ(ConstructType::DEFAULT, ptr->ctype());
523            break;
524        case 1:
525            EXPECT_EQ(ConstructType::LVALUE_REF, ptr->ctype());
526            break;
527        case 2:
528            EXPECT_EQ(ConstructType::RVALUE_REF, ptr->ctype());
529            break;
530        case 3:
531            EXPECT_EQ(ConstructType::L_THEN_R_REF, ptr->ctype());
532            break;
533        }
534
535        // Release the reference (how this gets done depends on allocator flavor and pointer type)
536        ReleaseHelper<typename Traits::AllocTraits>::ReleasePtr(ptr);
537        if (i % 2 == 1) {
538            ObjCounterHelper<AllocatorType,
539                           ENB_OBJ_COUNT>::StaticResetMaxObjCount();
540            max_obj_count = TestBase::allocated_obj_count();
541        }
542        res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(max_obj_count);
543        EXPECT_TRUE(res);
544    }
545
546    EXPECT_EQ(fbl::min(test_allocs, MAX_ALLOCS), i);
547    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckObjCount(0);
548    EXPECT_TRUE(res);
549    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(i % 2);
550    EXPECT_TRUE(res);
551    ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticResetMaxObjCount();
552    res = ObjCounterHelper<AllocatorType, ENB_OBJ_COUNT>::StaticCheckMaxObjCount(0);
553    EXPECT_TRUE(res);
554#if TEST_WILL_NOT_COMPILE || 0
555    AllocatorType::obj_count();
556#endif
557#if TEST_WILL_NOT_COMPILE || 0
558    AllocatorType::max_obj_count();
559#endif
560#if TEST_WILL_NOT_COMPILE || 0
561    AllocatorType::ResetMaxObjCount();
562#endif
563
564    END_TEST;
565}
566
567
568template <typename Traits>
569bool static_slab_test() {
570    BEGIN_TEST;
571
572    TestBase::Reset();
573
574    EXPECT_TRUE(do_static_slab_test<Traits>(1),
575                "Single allocator test failed");
576
577    EXPECT_TRUE(do_static_slab_test<Traits>(Traits::MaxAllocs() / 2),
578                "1/2 capacity allocator test failed");
579
580    EXPECT_TRUE(do_static_slab_test<Traits>(Traits::MaxAllocs() + 4),
581                "Over-capacity allocator test failed");
582
583    END_TEST;
584}
585}  // anon namespace
586
587using MutexLock = ::fbl::Mutex;
588using NullLock  = ::fbl::NullLock;
589
590DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUnmanagedTestTraits<MutexLock>::AllocTraits, 1);
591DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUniquePtrTestTraits<MutexLock>::AllocTraits, 1);
592DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticRefPtrTestTraits<MutexLock>::AllocTraits, 1);
593
594DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUnmanagedTestTraits<NullLock>::AllocTraits, 1);
595DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticUniquePtrTestTraits<NullLock>::AllocTraits, 1);
596DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticRefPtrTestTraits<NullLock>::AllocTraits, 1);
597
598DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUnmanagedTestTraits<MutexLock>::AllocTraits, 1);
599DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUniquePtrTestTraits<MutexLock>::AllocTraits, 1);
600DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedRefPtrTestTraits<MutexLock>::AllocTraits, 1);
601
602DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUnmanagedTestTraits<NullLock>::AllocTraits, 1);
603DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedUniquePtrTestTraits<NullLock>::AllocTraits, 1);
604DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(StaticCountedRefPtrTestTraits<NullLock>::AllocTraits, 1);
605
606BEGIN_TEST_CASE(slab_allocator_tests)
607RUN_NAMED_TEST("Unmanaged Single Slab (mutex)", (slab_test<UnmanagedTestTraits<MutexLock>, 1>))
608RUN_NAMED_TEST("Unmanaged Multi Slab  (mutex)", (slab_test<UnmanagedTestTraits<MutexLock>>))
609RUN_NAMED_TEST("UniquePtr Single Slab (mutex)", (slab_test<UniquePtrTestTraits<MutexLock>, 1>))
610RUN_NAMED_TEST("UniquePtr Multi Slab  (mutex)", (slab_test<UniquePtrTestTraits<MutexLock>>))
611RUN_NAMED_TEST("RefPtr Single Slab    (mutex)", (slab_test<RefPtrTestTraits<MutexLock>, 1>))
612RUN_NAMED_TEST("RefPtr Multi Slab     (mutex)", (slab_test<RefPtrTestTraits<MutexLock>>))
613
614RUN_NAMED_TEST("Unmanaged Single Slab (unlock)", (slab_test<UnmanagedTestTraits<NullLock>, 1>))
615RUN_NAMED_TEST("Unmanaged Multi Slab  (unlock)", (slab_test<UnmanagedTestTraits<NullLock>>))
616RUN_NAMED_TEST("UniquePtr Single Slab (unlock)", (slab_test<UniquePtrTestTraits<NullLock>, 1>))
617RUN_NAMED_TEST("UniquePtr Multi Slab  (unlock)", (slab_test<UniquePtrTestTraits<NullLock>>))
618RUN_NAMED_TEST("RefPtr Single Slab    (unlock)", (slab_test<RefPtrTestTraits<NullLock>, 1>))
619RUN_NAMED_TEST("RefPtr Multi Slab     (unlock)", (slab_test<RefPtrTestTraits<NullLock>>))
620
621RUN_NAMED_TEST("Manual Delete Unmanaged (mutex)",
622              (slab_test<UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE>>))
623RUN_NAMED_TEST("Manual Delete Unmanaged (unlock)",
624              (slab_test<UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE>>))
625
626RUN_NAMED_TEST("Static Unmanaged (unlock)", (static_slab_test<StaticUnmanagedTestTraits<NullLock>>))
627RUN_NAMED_TEST("Static UniquePtr (unlock)", (static_slab_test<StaticUniquePtrTestTraits<NullLock>>))
628RUN_NAMED_TEST("Static RefPtr    (unlock)", (static_slab_test<StaticRefPtrTestTraits<NullLock>>))
629
630RUN_NAMED_TEST("Static Unmanaged (mutex)", (static_slab_test<StaticUnmanagedTestTraits<MutexLock>>))
631RUN_NAMED_TEST("Static UniquePtr (mutex)", (static_slab_test<StaticUniquePtrTestTraits<MutexLock>>))
632RUN_NAMED_TEST("Static RefPtr    (mutex)", (static_slab_test<StaticRefPtrTestTraits<MutexLock>>))
633
634RUN_NAMED_TEST("Static Unmanaged (unlock)", (static_slab_test<StaticUnmanagedTestTraits<NullLock>>))
635RUN_NAMED_TEST("Static UniquePtr (unlock)", (static_slab_test<StaticUniquePtrTestTraits<NullLock>>))
636RUN_NAMED_TEST("Static RefPtr    (unlock)", (static_slab_test<StaticRefPtrTestTraits<NullLock>>))
637
638RUN_NAMED_TEST("Counted Unmanaged Single Slab (mutex)",(slab_test
639    <UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::INSTANCED, true>, 1>))
640RUN_NAMED_TEST("Counted Unmanaged Multi Slab  (mutex)", (slab_test
641    <UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::INSTANCED, true>>))
642RUN_NAMED_TEST("Counted UniquePtr Single Slab (mutex)", (slab_test
643    <UniquePtrTestTraits<MutexLock, true>, 1>))
644RUN_NAMED_TEST("Counted UniquePtr Multi Slab  (mutex)", (slab_test
645    <UniquePtrTestTraits<MutexLock, true>>))
646RUN_NAMED_TEST("Counted RefPtr Single Slab    (mutex)", (slab_test
647    <RefPtrTestTraits<MutexLock, true>, 1>))
648RUN_NAMED_TEST("Counted RefPtr Multi Slab     (mutex)", (slab_test
649    <RefPtrTestTraits<MutexLock, true>>))
650
651RUN_NAMED_TEST("Counted Unmanaged Single Slab (unlock)", (slab_test
652    <UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::INSTANCED, true>, 1>))
653RUN_NAMED_TEST("Counted Unmanaged Multi Slab  (unlock)", (slab_test
654    <UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::INSTANCED, true>>))
655RUN_NAMED_TEST("Counted UniquePtr Single Slab (unlock)", (slab_test
656    <UniquePtrTestTraits<NullLock, true>, 1>))
657RUN_NAMED_TEST("Counted UniquePtr Multi Slab  (unlock)", (slab_test
658    <UniquePtrTestTraits<NullLock, true>>))
659RUN_NAMED_TEST("Counted RefPtr Single Slab    (unlock)", (slab_test
660    <RefPtrTestTraits<NullLock, true>, 1>))
661RUN_NAMED_TEST("Counted RefPtr Multi Slab     (unlock)", (slab_test
662    <RefPtrTestTraits<NullLock, true>>))
663
664RUN_NAMED_TEST("Counted Manual Delete Unmanaged (mutex)", (slab_test
665    <UnmanagedTestTraits<MutexLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE, true>>))
666RUN_NAMED_TEST("Counted Manual Delete Unmanaged (unlock)", (slab_test
667    <UnmanagedTestTraits<NullLock, fbl::SlabAllocatorFlavor::MANUAL_DELETE, true>>))
668
669RUN_NAMED_TEST("Counted Static Unmanaged (unlock)", (static_slab_test
670    <StaticUnmanagedTestTraits<NullLock, true>>))
671RUN_NAMED_TEST("Counted Static UniquePtr (unlock)", (static_slab_test
672    <StaticUniquePtrTestTraits<NullLock, true>>))
673RUN_NAMED_TEST("Counted Static RefPtr    (unlock)", (static_slab_test
674    <StaticRefPtrTestTraits<NullLock, true>>))
675
676RUN_NAMED_TEST("Counted Static Unmanaged (mutex)", (static_slab_test
677    <StaticUnmanagedTestTraits<MutexLock, true>>))
678RUN_NAMED_TEST("Counted Static UniquePtr (mutex)", (static_slab_test
679    <StaticUniquePtrTestTraits<MutexLock, true>>))
680RUN_NAMED_TEST("Counted Static RefPtr    (mutex)", (static_slab_test
681    <StaticRefPtrTestTraits<MutexLock, true>>))
682
683RUN_NAMED_TEST("Counted Static Unmanaged (unlock)", (static_slab_test
684    <StaticUnmanagedTestTraits<NullLock, true>>))
685RUN_NAMED_TEST("Counted Static UniquePtr (unlock)", (static_slab_test
686    <StaticUniquePtrTestTraits<NullLock, true>>))
687RUN_NAMED_TEST("Counted Static RefPtr    (unlock)", (static_slab_test
688    <StaticRefPtrTestTraits<NullLock, true>>))
689END_TEST_CASE(slab_allocator_tests);
690