28class ListenerListTests final :
public UnitTest
35 explicit TestListener (std::function<
void()> cb) : callback (std::move (cb)) {}
43 int getNumCalls()
const {
return numCalls; }
47 std::function<void()> callback;
53 void addListener (std::function<
void()> cb)
55 listeners.push_back (std::make_unique<TestListener> (std::move (cb)));
56 listenerList.add (listeners.back().get());
59 void removeListener (
int i) { listenerList.remove (listeners[(
size_t) i].get()); }
64 listenerList.call ([] (
auto& l) { l.doCallback(); });
68 int getNumListeners()
const {
return (
int) listeners.size(); }
70 auto& getListener (
int i) {
return *listeners[(size_t) i]; }
72 int getCallLevel()
const
77 bool wereAllNonRemovedListenersCalled (
int numCalls)
const
79 return std::all_of (std::begin (listeners),
83 return (! listenerList.contains (listener.get())) || listener->getNumCalls() == numCalls;
88 std::vector<std::unique_ptr<TestListener>> listeners;
89 ListenerList<TestListener> listenerList;
94 ListenerListTests() : UnitTest (
"ListenerList", UnitTestCategories::containers) {}
96 void runTest()
override
99 beginTest (
"All non-removed listeners should be called - removing an already called listener");
103 for (
int i = 0; i < 20; ++i)
105 test.addListener ([i, &test]
108 test.removeListener (6);
112 test.callListeners();
113 expect (test.wereAllNonRemovedListenersCalled (1));
117 beginTest (
"All non-removed listeners should be called - removing a yet uncalled listener");
121 for (
int i = 0; i < 20; ++i)
123 test.addListener ([i, &test]
126 test.removeListener (4);
130 test.callListeners();
131 expect (test.wereAllNonRemovedListenersCalled (1));
135 beginTest (
"All non-removed listeners should be called - one callback removes multiple listeners");
139 for (
int i = 0; i < 20; ++i)
141 test.addListener ([i, &test]
145 test.removeListener (19);
146 test.removeListener (0);
151 test.callListeners();
152 expect (test.wereAllNonRemovedListenersCalled (1));
155 beginTest (
"All non-removed listeners should be called - removing listeners randomly");
157 auto random = getRandom();
159 for (
auto run = 0; run < 10; ++run)
161 const auto numListeners = random.nextInt ({ 10, 100 });
162 const auto listenersThatRemoveListeners = chooseUnique (random,
164 random.nextInt ({ 0, numListeners / 2 }));
167 std::map<int, std::set<int>> removals;
169 for (
auto i : listenersThatRemoveListeners)
172 removals[i] = chooseUnique (random,
174 random.nextInt ({ 1, std::max (2, numListeners / 10) }));
179 for (
int i = 0; i < numListeners; ++i)
181 test.addListener ([i, &removals, &test]
183 const auto iter = removals.find (i);
185 if (iter == removals.end())
188 for (
auto j : iter->second)
190 test.removeListener (j);
195 test.callListeners();
196 expect (test.wereAllNonRemovedListenersCalled (1));
201 beginTest (
"All non-removed listeners should be called - add listener during iteration");
204 const auto numStartingListeners = 20;
206 for (
int i = 0; i < numStartingListeners; ++i)
208 test.addListener ([i, &test]
210 if (i == 5 || i == 6)
211 test.addListener ([] {});
215 test.callListeners();
220 for (
int i = 0; i < numStartingListeners; ++i)
221 success = success && test.getListener (i).getNumCalls() == 1;
224 for (
int i = numStartingListeners; i < test.getNumListeners(); ++i)
225 success = success && test.getListener (i).getNumCalls() == 0;
230 beginTest (
"All non-removed listeners should be called - nested ListenerList::call()");
234 for (
int i = 0; i < 20; ++i)
236 test.addListener ([i, &test]
238 const auto callLevel = test.getCallLevel();
240 if (i == 6 && callLevel == 1)
242 test.callListeners();
248 test.removeListener (4);
249 else if (callLevel == 2)
250 test.removeListener (6);
255 test.callListeners();
256 expect (test.wereAllNonRemovedListenersCalled (2));
259 beginTest (
"All non-removed listeners should be called - random ListenerList::call()");
261 const auto numListeners = 20;
262 auto random = getRandom();
264 for (
int run = 0; run < 10; ++run)
269 auto listenersToRemove = chooseUnique (random, numListeners, numListeners / 2);
271 for (
int i = 0; i < numListeners; ++i)
274 test.addListener ([&]
276 const auto callLevel = test.getCallLevel();
278 if (callLevel < 4 && random.nextFloat() < 0.05f)
281 test.callListeners();
284 if (random.nextFloat() < 0.5f)
286 const auto listenerToRemove = random.nextInt ({ 0, numListeners });
288 if (listenersToRemove.erase (listenerToRemove) > 0)
289 test.removeListener (listenerToRemove);
294 while (listenersToRemove.size() > 0)
296 test.callListeners();
300 expect (test.wereAllNonRemovedListenersCalled (numCalls));
304 beginTest (
"Deleting the listener list from a callback");
308 std::function<void()> onCallback;
309 void notify() { onCallback(); }
312 auto listeners = std::make_unique<juce::ListenerList<Listener>>();
314 const auto callback = [&]
316 expect (listeners !=
nullptr);
320 Listener listener1 { callback };
321 Listener listener2 { callback };
323 listeners->add (&listener1);
324 listeners->add (&listener2);
326 listeners->call (&Listener::notify);
328 expect (listeners ==
nullptr);
331 beginTest (
"Using a BailOutChecker");
335 std::function<void()> onCallback;
336 void notify() { onCallback(); }
339 ListenerList<Listener> listeners;
341 bool listener1Called =
false;
342 bool listener2Called =
false;
343 bool listener3Called =
false;
345 Listener listener1 { [&]{ listener1Called =
true; } };
346 Listener listener2 { [&]{ listener2Called =
true; } };
347 Listener listener3 { [&]{ listener3Called =
true; } };
349 listeners.add (&listener1);
350 listeners.add (&listener2);
351 listeners.add (&listener3);
353 struct BailOutChecker
356 bool shouldBailOut()
const {
return bailOutBool; }
359 BailOutChecker bailOutChecker { listener2Called };
360 listeners.callChecked (bailOutChecker, &Listener::notify);
362 expect ( listener1Called);
363 expect ( listener2Called);
364 expect (! listener3Called);
367 beginTest (
"Using a critical section");
371 std::function<void()> onCallback;
372 void notify() { onCallback(); }
375 struct TestCriticalSection
377 TestCriticalSection() { isAlive() =
true; }
378 ~TestCriticalSection() { isAlive() =
false; }
380 static void enter() noexcept { numOutOfScopeCalls() += isAlive() ? 0 : 1; }
381 static void exit() noexcept { numOutOfScopeCalls() += isAlive() ? 0 : 1; }
383 static bool tryEnter() noexcept
385 numOutOfScopeCalls() += isAlive() ? 0 : 1;
389 using ScopedLockType = GenericScopedLock<TestCriticalSection>;
391 static bool& isAlive()
393 static bool inScope =
false;
397 static int& numOutOfScopeCalls()
399 static int numOutOfScopeCalls = 0;
400 return numOutOfScopeCalls;
404 auto listeners = std::make_unique<juce::ListenerList<Listener, Array<Listener*, TestCriticalSection>>>();
406 const auto callback = [&]{ listeners.reset(); };
408 Listener listener { callback };
410 listeners->add (&listener);
411 listeners->call (&Listener::notify);
413 expect (listeners ==
nullptr);
414 expect (TestCriticalSection::numOutOfScopeCalls() == 0);
417 beginTest (
"Adding a listener during a callback when one has already been removed");
421 ListenerList<Listener> listeners;
422 expect (listeners.size() == 0);
425 listeners.add (&listener);
426 expect (listeners.size() == 1);
428 bool listenerCalled =
false;
430 listeners.call ([&] (
auto& l)
432 listeners.remove (&l);
433 expect (listeners.size() == 0);
436 expect (listeners.size() == 1);
438 listenerCalled =
true;
441 expect (listenerCalled);
442 expect (listeners.size() == 1);
447 static std::set<int> chooseUnique (Random& random,
int max,
int numChosen)
449 std::set<int> result;
451 while ((
int) result.size() < numChosen)
452 result.insert (random.nextInt ({ 0, max }));
458static ListenerListTests listenerListTests;