LMMS
Loading...
Searching...
No Matches
juce_ListenerList.cpp
Go to the documentation of this file.
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 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26#if JUCE_UNIT_TESTS
27
28class ListenerListTests : public UnitTest
29{
30public:
31 //==============================================================================
32 class TestListener
33 {
34 public:
35 explicit TestListener (std::function<void()> cb) : callback (std::move (cb)) {}
36
37 void doCallback()
38 {
39 ++numCalls;
40 callback();
41 }
42
43 int getNumCalls() const { return numCalls; }
44
45 private:
46 int numCalls = 0;
47 std::function<void()> callback;
48 };
49
50 class TestObject
51 {
52 public:
53 void addListener (std::function<void()> cb)
54 {
55 listeners.push_back (std::make_unique<TestListener> (std::move (cb)));
56 listenerList.add (listeners.back().get());
57 }
58
59 void removeListener (int i) { listenerList.remove (listeners[(size_t) i].get()); }
60
61 void callListeners()
62 {
63 ++callLevel;
64 listenerList.call ([] (auto& l) { l.doCallback(); });
65 --callLevel;
66 }
67
68 int getNumListeners() const { return (int) listeners.size(); }
69
70 auto& getListener (int i) { return *listeners[(size_t) i]; }
71
72 int getCallLevel() const
73 {
74 return callLevel;
75 }
76
77 bool wereAllNonRemovedListenersCalled (int numCalls) const
78 {
79 return std::all_of (std::begin (listeners),
80 std::end (listeners),
81 [&] (auto& listener)
82 {
83 return (! listenerList.contains (listener.get())) || listener->getNumCalls() == numCalls;
84 });
85 }
86
87 private:
88 std::vector<std::unique_ptr<TestListener>> listeners;
89 ListenerList<TestListener> listenerList;
90 int callLevel = 0;
91 };
92
93 //==============================================================================
94 ListenerListTests() : UnitTest ("ListenerList", UnitTestCategories::containers) {}
95
96 void runTest() override
97 {
98 // This is a test that the pre-iterator adjustment implementation should pass too
99 beginTest ("All non-removed listeners should be called - removing an already called listener");
100 {
101 TestObject test;
102
103 for (int i = 0; i < 20; ++i)
104 {
105 test.addListener ([i, &test]
106 {
107 if (i == 5)
108 test.removeListener (6);
109 });
110 }
111
112 test.callListeners();
113 expect (test.wereAllNonRemovedListenersCalled (1));
114 }
115
116 // Iterator adjustment is necessary for passing this
117 beginTest ("All non-removed listeners should be called - removing a yet uncalled listener");
118 {
119 TestObject test;
120
121 for (int i = 0; i < 20; ++i)
122 {
123 test.addListener ([i, &test]
124 {
125 if (i == 5)
126 test.removeListener (4);
127 });
128 }
129
130 test.callListeners();
131 expect (test.wereAllNonRemovedListenersCalled (1));
132 }
133
134 // This test case demonstrates why we have to call --it.index instead of it.next()
135 beginTest ("All non-removed listeners should be called - one callback removes multiple listeners");
136 {
137 TestObject test;
138
139 for (int i = 0; i < 20; ++i)
140 {
141 test.addListener ([i, &test]
142 {
143 if (i == 19)
144 {
145 test.removeListener (19);
146 test.removeListener (0);
147 }
148 });
149 }
150
151 test.callListeners();
152 expect (test.wereAllNonRemovedListenersCalled (1));
153 }
154
155 beginTest ("All non-removed listeners should be called - removing listeners randomly");
156 {
157 auto random = getRandom();
158
159 for (auto run = 0; run < 10; ++run)
160 {
161 const auto numListeners = random.nextInt ({ 10, 100 });
162 const auto listenersThatRemoveListeners = chooseUnique (random,
163 numListeners,
164 random.nextInt ({ 0, numListeners / 2 }));
165
166 // The listener in position [key] should remove listeners in [value]
167 std::map<int, std::set<int>> removals;
168
169 for (auto i : listenersThatRemoveListeners)
170 {
171 // Random::nextInt ({1, 1}); triggers an assertion
172 removals[i] = chooseUnique (random,
173 numListeners,
174 random.nextInt ({ 1, std::max (2, numListeners / 10) }));
175 }
176
177 TestObject test;
178
179 for (int i = 0; i < numListeners; ++i)
180 {
181 test.addListener ([i, &removals, &test]
182 {
183 const auto iter = removals.find (i);
184
185 if (iter == removals.end())
186 return;
187
188 for (auto j : iter->second)
189 {
190 test.removeListener (j);
191 }
192 });
193 }
194
195 test.callListeners();
196 expect (test.wereAllNonRemovedListenersCalled (1));
197 }
198 }
199
200 // Iterator adjustment is not necessary for passing this
201 beginTest ("All non-removed listeners should be called - add listener during iteration");
202 {
203 TestObject test;
204 const auto numStartingListeners = 20;
205
206 for (int i = 0; i < numStartingListeners; ++i)
207 {
208 test.addListener ([i, &test]
209 {
210 if (i == 5 || i == 6)
211 test.addListener ([] {});
212 });
213 }
214
215 test.callListeners();
216
217 // Only the Listeners added before the test can be expected to have been called
218 bool success = true;
219
220 for (int i = 0; i < numStartingListeners; ++i)
221 success = success && test.getListener (i).getNumCalls() == 1;
222
223 // Listeners added during the iteration must not be called in that iteration
224 for (int i = numStartingListeners; i < test.getNumListeners(); ++i)
225 success = success && test.getListener (i).getNumCalls() == 0;
226
227 expect (success);
228 }
229
230 beginTest ("All non-removed listeners should be called - nested ListenerList::call()");
231 {
232 TestObject test;
233
234 for (int i = 0; i < 20; ++i)
235 {
236 test.addListener ([i, &test]
237 {
238 const auto callLevel = test.getCallLevel();
239
240 if (i == 6 && callLevel == 1)
241 {
242 test.callListeners();
243 }
244
245 if (i == 5)
246 {
247 if (callLevel == 1)
248 test.removeListener (4);
249 else if (callLevel == 2)
250 test.removeListener (6);
251 }
252 });
253 }
254
255 test.callListeners();
256 expect (test.wereAllNonRemovedListenersCalled (2));
257 }
258
259 beginTest ("All non-removed listeners should be called - random ListenerList::call()");
260 {
261 const auto numListeners = 20;
262 auto random = getRandom();
263
264 for (int run = 0; run < 10; ++run)
265 {
266 TestObject test;
267 auto numCalls = 0;
268
269 auto listenersToRemove = chooseUnique (random, numListeners, numListeners / 2);
270
271 for (int i = 0; i < numListeners; ++i)
272 {
273 // Capturing numListeners is a warning on MacOS, not capturing it is an error on Windows
274 test.addListener ([&]
275 {
276 const auto callLevel = test.getCallLevel();
277
278 if (callLevel < 4 && random.nextFloat() < 0.05f)
279 {
280 ++numCalls;
281 test.callListeners();
282 }
283
284 if (random.nextFloat() < 0.5f)
285 {
286 const auto listenerToRemove = random.nextInt ({ 0, numListeners });
287
288 if (listenersToRemove.erase (listenerToRemove) > 0)
289 test.removeListener (listenerToRemove);
290 }
291 });
292 }
293
294 while (listenersToRemove.size() > 0)
295 {
296 test.callListeners();
297 ++numCalls;
298 }
299
300 expect (test.wereAllNonRemovedListenersCalled (numCalls));
301 }
302 }
303 }
304
305private:
306 static std::set<int> chooseUnique (Random& random, int max, int numChosen)
307 {
308 std::set<int> result;
309
310 while ((int) result.size() < numChosen)
311 result.insert (random.nextInt ({ 0, max }));
312
313 return result;
314 }
315};
316
317static ListenerListTests listenerListTests;
318
319#endif
320
321} // namespace juce
static void run(LV2_Handle instance, uint32_t n_samples)
Definition bindings_test_plugin.c:112
int * l
Definition inflate.c:1579
register unsigned j
Definition inflate.c:1576
register unsigned i
Definition inflate.c:1575
void move(void *from, void *to)
Definition juce_FixedSizeFunction.h:53
auto & get(ProcessorChain< Processors... > &chain) noexcept
Definition juce_ProcessorChain.h:133
Definition carla_juce.cpp:31
#define max(x, y)
Definition os.h:78
static int test(SerdEnv *env, bool top_level, bool pretty_numbers)
Definition sratom_test.c:79
RECT const char void(* callback)(const char *droppath))) SWELL_API_DEFINE(BOOL
Definition swell-functions.h:1004
int result
Definition process.c:1455
#define void
Definition unzip.h:396