OpenShot Audio Library | OpenShotAudio  0.6.0
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 
32 namespace juce
33 {
34 namespace
35 {
36 
37 class ConstructCounts
38 {
39  auto tie() const noexcept { return std::tie (constructions, copies, moves, calls, destructions); }
40 
41 public:
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 
58 String& 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 
68 class 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 
96 public:
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 
360 FixedSizeFunctionTest fixedSizedFunctionTest;
361 
362 }
363 } // namespace juce
364 #undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE