GCC Code Coverage Report


Directory: ../
File: test/unittest/UnitBlockAllocator.cpp
Date: 2024-12-24 01:17:15
Exec Total Coverage
Lines: 59 59 100.0%
Functions: 14 14 100.0%
Branches: 118 332 35.5%

Line Branch Exec Source
1 // Copyright (c) 2021-2024 ChilliBits. All rights reserved.
2
3 #include <gmock/gmock.h>
4 #include <gtest/gtest.h>
5
6 #include <ast/ASTNodes.h>
7 #include <util/BlockAllocator.h>
8 #include <util/CodeLoc.h>
9 #include <util/Memory.h>
10
11 namespace spice::testing {
12
13 using namespace spice::compiler;
14
15 static size_t destructedDummyNodes = 0;
16
17 class DummyNode final : public ASTNode {
18 // Constructors
19 using ASTNode::ASTNode;
20
21 // Destructors
22 202016 ~DummyNode() override { destructedDummyNodes++; }
23
24 // Visitor methods
25 std::any accept(AbstractASTVisitor *visitor) override { return {}; } // LCOV_EXCL_LINE
26 std::any accept(ParallelizableASTVisitor *visitor) const override { return {}; } // LCOV_EXCL_LINE
27 };
28 static constexpr size_t DUMMY_NODE_SIZE = sizeof(DummyNode);
29 static_assert(DUMMY_NODE_SIZE == 104, "DummyNode size has changed. Update test accordingly.");
30
31 class MockMemoryManager final : public MemoryManager {
32 public:
33
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
14 MOCK_METHOD(byte *, allocate, (size_t size), (const override));
34
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
10 MOCK_METHOD(void, deallocate, (byte * ptr), (const override));
35 };
36
37 8 TEST(BlockAllocatorTest, TestBlockAllocatorLarge) {
38 2 destructedDummyNodes = 0; // Reset destruction counter
39 static constexpr size_t NODE_COUNT = 100'000; // 100.000 * 104 bytes = 10.4 MB
40
41 {
42 // Create allocator, that can hold 5 nodes per block
43 2 constexpr DefaultMemoryManager memoryManager;
44
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 BlockAllocator<ASTNode> alloc(memoryManager, DUMMY_NODE_SIZE * 5);
45
46 // Allocate nodes
47 2 std::vector<ASTNode *> nodes;
48
2/2
✓ Branch 0 taken 100000 times.
✓ Branch 1 taken 1 times.
200002 for (size_t i = 0; i < NODE_COUNT; i++) {
49
2/4
✓ Branch 1 taken 100000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 100000 times.
✗ Branch 5 not taken.
200000 auto node = alloc.allocate<DummyNode>(CodeLoc(i, 1));
50
3/12
✓ Branch 1 taken 100000 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 100000 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 19 taken 100000 times.
✗ Branch 20 not taken.
200000 ASSERT_NE(nullptr, node);
51
1/2
✓ Branch 1 taken 100000 times.
✗ Branch 2 not taken.
200000 nodes.push_back(node);
52
4/14
✓ Branch 1 taken 100000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 100000 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 100000 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 22 taken 100000 times.
✗ Branch 23 not taken.
200000 ASSERT_EQ(i, nodes.at(i)->codeLoc.line);
53
4/14
✓ Branch 1 taken 100000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 100000 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 100000 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 22 taken 100000 times.
✗ Branch 23 not taken.
200000 ASSERT_EQ(1, nodes.at(i)->codeLoc.col);
54 }
55
56 // Check if stats are correct
57
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
2 ASSERT_EQ(NODE_COUNT, alloc.getAllocationCount());
58
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
2 ASSERT_EQ(13'000'000, alloc.getTotalAllocatedSize());
59
60 // Block Allocator gets destructed here and with that, all allocated nodes should be destructed
61
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 }
62
63
3/12
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
2 ASSERT_EQ(NODE_COUNT, destructedDummyNodes);
64 }
65
66 8 TEST(BlockAllocatorTest, TestBlockAllocatorUnevenBlockSize) {
67 2 destructedDummyNodes = 0; // Reset destruction counter
68 static constexpr size_t NODE_COUNT = 1'000; // 1.000 * 104 bytes = 104 KB
69
70 {
71 // Create allocator, that can hold 4.5 nodes per block
72 2 constexpr DefaultMemoryManager memoryManager;
73
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 BlockAllocator<ASTNode> alloc(memoryManager, DUMMY_NODE_SIZE * 4.5);
74
75 // Allocate nodes
76 2 std::vector<ASTNode *> nodes;
77
2/2
✓ Branch 0 taken 1000 times.
✓ Branch 1 taken 1 times.
2002 for (size_t i = 0; i < NODE_COUNT; i++) {
78
2/4
✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1000 times.
✗ Branch 5 not taken.
2000 auto node = alloc.allocate<DummyNode>(CodeLoc(i, 1));
79
3/12
✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1000 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 19 taken 1000 times.
✗ Branch 20 not taken.
2000 ASSERT_NE(nullptr, node);
80
1/2
✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
2000 nodes.push_back(node);
81
4/14
✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1000 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1000 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 22 taken 1000 times.
✗ Branch 23 not taken.
2000 ASSERT_EQ(i, nodes.at(i)->codeLoc.line);
82
4/14
✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1000 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1000 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 22 taken 1000 times.
✗ Branch 23 not taken.
2000 ASSERT_EQ(1, nodes.at(i)->codeLoc.col);
83 }
84
85 // Check if stats are correct
86
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
2 ASSERT_EQ(NODE_COUNT, alloc.getAllocationCount());
87
3/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
2 ASSERT_EQ(117'000, alloc.getTotalAllocatedSize());
88
89 // Block Allocator gets destructed here and with that, all allocated nodes should be destructed
90
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 }
91
92
3/12
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
2 ASSERT_EQ(NODE_COUNT, destructedDummyNodes);
93 }
94
95 8 TEST(BlockAllocatorTest, TestBlockAllocatorOOM) {
96 2 destructedDummyNodes = 0; // Reset destruction counter
97 static constexpr size_t NODE_COUNT = 10; // 10 * 104 bytes = 1.04 KB
98
99 // Prepare mock methods
100
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 MockMemoryManager mockMemoryManager;
101
102 // Make sure, that the memory manager returns nullptr when trying to allocate the fifth block
103
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 ::testing::InSequence s;
104 4 auto mallocCallback = [](size_t size) { return static_cast<byte *>(malloc(size)); };
105
7/14
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
2 EXPECT_CALL(mockMemoryManager, allocate(::testing::_)).Times(4).WillRepeatedly(mallocCallback);
106
8/16
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 1 times.
✗ Branch 24 not taken.
2 EXPECT_CALL(mockMemoryManager, allocate(::testing::_)).Times(1).WillOnce(::testing::Return(nullptr));
107
7/14
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
2 EXPECT_CALL(mockMemoryManager, deallocate(::testing::_)).Times(4).WillRepeatedly(::testing::Invoke(free));
108
109 {
110 // Create allocator, that can hold 2 nodes per block
111
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 BlockAllocator<ASTNode> alloc(mockMemoryManager, DUMMY_NODE_SIZE * 2.25);
112
113 try {
114 // Allocate nodes
115 2 std::vector<ASTNode *> nodes;
116
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 for (size_t i = 0; i < NODE_COUNT; i++) {
117
3/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
18 auto node = alloc.allocate<DummyNode>(CodeLoc(i, 1));
118
3/12
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 19 taken 8 times.
✗ Branch 20 not taken.
16 ASSERT_NE(nullptr, node);
119
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 nodes.push_back(node);
120
4/14
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 22 taken 8 times.
✗ Branch 23 not taken.
16 ASSERT_EQ(i, nodes.at(i)->codeLoc.line);
121
4/14
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 22 taken 8 times.
✗ Branch 23 not taken.
16 ASSERT_EQ(1, nodes.at(i)->codeLoc.col);
122 }
123 FAIL(); // LCOV_EXCL_LINE - Should not reach this point
124
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
4 } catch (CompilerError &ce) {
125
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 std::stringstream ss;
126
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 ss << "[Error|Compiler]:\n";
127
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 ss << "An out of memory error occurred: Could not allocate memory for BlockAllocator. Already allocated 4 blocks.";
128
4/14
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 24 taken 1 times.
✗ Branch 25 not taken.
2 ASSERT_EQ(ss.str(), ce.what());
129
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
4 }
130
131 // Block Allocator gets destructed here and with that, all allocated nodes should be destructed
132
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 }
133
134
3/12
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
2 ASSERT_EQ(8, destructedDummyNodes); // Only 8 blocks were constructed until the OOM error occurred
135
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 ::testing::Mock::VerifyAndClearExpectations(&mockMemoryManager);
136
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 }
137
138 } // namespace spice::testing
139