OpenShot Audio Library | OpenShotAudio 0.4.0
Loading...
Searching...
No Matches
juce_FixedSizeFunction_test.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26#if JUCE_ENABLE_ALLOCATION_HOOKS
27#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this)
28#else
29#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
30#endif
31
32namespace juce
33{
34namespace
35{
36
37class ConstructCounts
38{
39 auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); }
40
41public:
42 int constructions = 0;
43 int copies = 0;
44 int moves = 0;
45 int calls = 0;
46 int destructions = 0;
47
48 ConstructCounts withConstructions (int i) const noexcept { auto c = *this; c.constructions = i; return c; }
49 ConstructCounts withCopies (int i) const noexcept { auto c = *this; c.copies = i; return c; }
50 ConstructCounts withMoves (int i) const noexcept { auto c = *this; c.moves = i; return c; }
51 ConstructCounts withCalls (int i) const noexcept { auto c = *this; c.calls = i; return c; }
52 ConstructCounts withDestructions (int i) const noexcept { auto c = *this; c.destructions = i; return c; }
53
54 bool operator== (const ConstructCounts& other) const noexcept { return tie() == other.tie(); }
55 bool operator!= (const ConstructCounts& other) const noexcept { return tie() != other.tie(); }
56};
57
58String& operator<< (String& str, const ConstructCounts& c)
59{
60 return str << "{ constructions: " << c.constructions
61 << ", copies: " << c.copies
62 << ", moves: " << c.moves
63 << ", calls: " << c.calls
64 << ", destructions: " << c.destructions
65 << " }";
66}
67
68class FixedSizeFunctionTest final : public UnitTest
69{
70 static void toggleBool (bool& b) { b = ! b; }
71
72 struct ConstructCounter
73 {
74 explicit ConstructCounter (ConstructCounts& countsIn)
75 : counts (countsIn) {}
76
77 ConstructCounter (const ConstructCounter& c)
78 : counts (c.counts)
79 {
80 counts.copies += 1;
81 }
82
83 ConstructCounter (ConstructCounter&& c) noexcept
84 : counts (c.counts)
85 {
86 counts.moves += 1;
87 }
88
89 ~ConstructCounter() noexcept { counts.destructions += 1; }
90
91 void operator()() const noexcept { counts.calls += 1; }
92
93 ConstructCounts& counts;
94 };
95
96public:
97 FixedSizeFunctionTest()
98 : UnitTest ("Fixed Size Function", UnitTestCategories::containers)
99 {}
100
101 void runTest() override
102 {
103 beginTest ("Can be constructed and called from a lambda");
104 {
105 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
106
107 const auto result = 5;
108 bool wasCalled = false;
109 const auto lambda = [&] { wasCalled = true; return result; };
110
111 const FixedSizeFunction<sizeof (lambda), int()> fn (lambda);
112 const auto out = fn();
113
114 expect (wasCalled);
115 expectEquals (result, out);
116 }
117
118 beginTest ("void fn can be constructed from function with return value");
119 {
120 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
121
122 bool wasCalled = false;
123 const auto lambda = [&] { wasCalled = true; return 5; };
124 const FixedSizeFunction<sizeof (lambda), void()> fn (lambda);
125
126 fn();
127 expect (wasCalled);
128 }
129
130 beginTest ("Can be constructed and called from a function pointer");
131 {
132 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
133
134 bool state = false;
135
136 const FixedSizeFunction<sizeof (void*), void (bool&)> fn (toggleBool);
137
138 fn (state);
139 expect (state);
140
141 fn (state);
142 expect (! state);
143
144 fn (state);
145 expect (state);
146 }
147
148 beginTest ("Default constructed functions throw if called");
149 {
150 const auto a = FixedSizeFunction<8, void()>();
151 expectThrowsType (a(), std::bad_function_call)
152
153 const auto b = FixedSizeFunction<8, void()> (nullptr);
154 expectThrowsType (b(), std::bad_function_call)
155 }
156
157 beginTest ("Functions can be moved");
158 {
159 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
160
161 ConstructCounts counts;
162
163 auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (ConstructCounter { counts });
164 expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed
165
166 a();
167 expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
168
169 const auto b = std::move (a);
170 expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
171
172 b();
173 expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
174
175 b();
176 expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
177 }
178
179 beginTest ("Functions are destructed properly");
180 {
181 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
182
183 ConstructCounts counts;
184 const ConstructCounter toCopy { counts };
185
186 {
187 auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (toCopy);
188 expectEquals (counts, ConstructCounts().withCopies (1));
189 }
190
191 expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1));
192 }
193
194 beginTest ("Avoid destructing functions that fail to construct");
195 {
196 struct BadConstructor
197 {
198 explicit BadConstructor (ConstructCounts& c)
199 : counts (c)
200 {
201 counts.constructions += 1;
202 throw std::runtime_error { "this was meant to happen" };
203 }
204
205 BadConstructor (const BadConstructor&) = default;
206 BadConstructor& operator= (const BadConstructor&) = delete;
207
208 ~BadConstructor() noexcept { counts.destructions += 1; }
209
210 void operator()() const noexcept { counts.calls += 1; }
211
212 ConstructCounts& counts;
213 };
214
215 ConstructCounts counts;
216
217 expectThrowsType ((FixedSizeFunction<sizeof (BadConstructor), void()> (BadConstructor { counts })),
218 std::runtime_error)
219
220 expectEquals (counts, ConstructCounts().withConstructions (1));
221 }
222
223 beginTest ("Equality checks work");
224 {
225 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
226
227 FixedSizeFunction<8, void()> a;
228 expect (! bool (a));
229 expect (a == nullptr);
230 expect (nullptr == a);
231 expect (! (a != nullptr));
232 expect (! (nullptr != a));
233
234 FixedSizeFunction<8, void()> b ([] {});
235 expect (bool (b));
236 expect (b != nullptr);
237 expect (nullptr != b);
238 expect (! (b == nullptr));
239 expect (! (nullptr == b));
240 }
241
242 beginTest ("Functions can be cleared");
243 {
244 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
245
246 FixedSizeFunction<8, void()> fn ([] {});
247 expect (bool (fn));
248
249 fn = nullptr;
250 expect (! bool (fn));
251 }
252
253 beginTest ("Functions can be assigned");
254 {
255 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
256
257 using Fn = FixedSizeFunction<8, void()>;
258
259 int numCallsA = 0;
260 int numCallsB = 0;
261
262 Fn x;
263 Fn y;
264 expect (! bool (x));
265 expect (! bool (y));
266
267 x = [&] { numCallsA += 1; };
268 y = [&] { numCallsB += 1; };
269 expect (bool (x));
270 expect (bool (y));
271
272 x();
273 expectEquals (numCallsA, 1);
274 expectEquals (numCallsB, 0);
275
276 y();
277 expectEquals (numCallsA, 1);
278 expectEquals (numCallsB, 1);
279
280 x = std::move (y);
281 expectEquals (numCallsA, 1);
282 expectEquals (numCallsB, 1);
283
284 x();
285 expectEquals (numCallsA, 1);
286 expectEquals (numCallsB, 2);
287 }
288
289 beginTest ("Functions may mutate internal state");
290 {
291 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
292
293 using Fn = FixedSizeFunction<64, void()>;
294
295 Fn x;
296 expect (! bool (x));
297
298 int numCalls = 0;
299 x = [&numCalls, counter = 0]() mutable { counter += 1; numCalls = counter; };
300 expect (bool (x));
301
302 expectEquals (numCalls, 0);
303
304 x();
305 expectEquals (numCalls, 1);
306
307 x();
308 expectEquals (numCalls, 2);
309 }
310
311 beginTest ("Functions can sink move-only parameters");
312 {
313 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
314
315 using FnA = FixedSizeFunction<64, int (std::unique_ptr<int>)>;
316
317 auto value = 5;
318 auto ptr = std::make_unique<int> (value);
319
320 FnA fnA = [] (std::unique_ptr<int> p) { return *p; };
321
322 expect (value == fnA (std::move (ptr)));
323
324 using FnB = FixedSizeFunction<64, void (std::unique_ptr<int>&&)>;
325
326 FnB fnB = [&value] (std::unique_ptr<int>&& p)
327 {
328 auto x = std::move (p);
329 value = *x;
330 };
331
332 const auto newValue = 10;
333 fnB (std::make_unique<int> (newValue));
334 expect (value == newValue);
335 }
336
337 beginTest ("Functions be converted from smaller functions");
338 {
339 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
340
341 using SmallFn = FixedSizeFunction<20, void()>;
342 using LargeFn = FixedSizeFunction<21, void()>;
343
344 bool smallCalled = false;
345 bool largeCalled = false;
346
347 SmallFn small = [&smallCalled, a = std::array<char, 8>{}] { smallCalled = true; ignoreUnused (a); };
348 LargeFn large = [&largeCalled, a = std::array<char, 8>{}] { largeCalled = true; ignoreUnused (a); };
349
350 large = std::move (small);
351
352 large();
353
354 expect (smallCalled);
355 expect (! largeCalled);
356 }
357 }
358};
359
360FixedSizeFunctionTest fixedSizedFunctionTest;
361
362}
363} // namespace juce
364#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE