1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "gtest/gtest.h"
7
8 #include "mozilla/SharedThreadPool.h"
9 #include "mozilla/TaskQueue.h"
10 #include "mozilla/UniquePtr.h"
11 #include "MediaEventSource.h"
12 #include "VideoUtils.h"
13
14 using namespace mozilla;
15
16 /*
17 * Test if listeners receive the event data correctly.
18 */
TEST(MediaEventSource,SingleListener)19 TEST(MediaEventSource, SingleListener)
20 {
21 RefPtr<TaskQueue> queue =
22 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
23
24 MediaEventProducer<int> source;
25 int i = 0;
26
27 auto func = [&](int j) { i += j; };
28 MediaEventListener listener = source.Connect(queue, func);
29
30 // Call Notify 3 times. The listener should be also called 3 times.
31 source.Notify(3);
32 source.Notify(5);
33 source.Notify(7);
34
35 queue->BeginShutdown();
36 queue->AwaitShutdownAndIdle();
37
38 // Verify the event data is passed correctly to the listener.
39 EXPECT_EQ(i, 15); // 3 + 5 + 7
40 listener.Disconnect();
41 }
42
TEST(MediaEventSource,MultiListener)43 TEST(MediaEventSource, MultiListener)
44 {
45 RefPtr<TaskQueue> queue =
46 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
47
48 MediaEventProducer<int> source;
49 int i = 0;
50 int j = 0;
51
52 auto func1 = [&](int k) { i = k * 2; };
53 auto func2 = [&](int k) { j = k * 3; };
54 MediaEventListener listener1 = source.Connect(queue, func1);
55 MediaEventListener listener2 = source.Connect(queue, func2);
56
57 // Both listeners should receive the event.
58 source.Notify(11);
59
60 queue->BeginShutdown();
61 queue->AwaitShutdownAndIdle();
62
63 // Verify the event data is passed correctly to the listener.
64 EXPECT_EQ(i, 22); // 11 * 2
65 EXPECT_EQ(j, 33); // 11 * 3
66
67 listener1.Disconnect();
68 listener2.Disconnect();
69 }
70
71 /*
72 * Test if disconnecting a listener prevents events from coming.
73 */
TEST(MediaEventSource,DisconnectAfterNotification)74 TEST(MediaEventSource, DisconnectAfterNotification)
75 {
76 RefPtr<TaskQueue> queue =
77 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
78
79 MediaEventProducer<int> source;
80 int i = 0;
81
82 MediaEventListener listener;
83 auto func = [&](int j) {
84 i += j;
85 listener.Disconnect();
86 };
87 listener = source.Connect(queue, func);
88
89 // Call Notify() twice. Since we disconnect the listener when receiving
90 // the 1st event, the 2nd event should not reach the listener.
91 source.Notify(11);
92 source.Notify(11);
93
94 queue->BeginShutdown();
95 queue->AwaitShutdownAndIdle();
96
97 // Check only the 1st event is received.
98 EXPECT_EQ(i, 11);
99 }
100
TEST(MediaEventSource,DisconnectBeforeNotification)101 TEST(MediaEventSource, DisconnectBeforeNotification)
102 {
103 RefPtr<TaskQueue> queue =
104 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
105
106 MediaEventProducer<int> source;
107 int i = 0;
108 int j = 0;
109
110 auto func1 = [&](int k) { i = k * 2; };
111 auto func2 = [&](int k) { j = k * 3; };
112 MediaEventListener listener1 = source.Connect(queue, func1);
113 MediaEventListener listener2 = source.Connect(queue, func2);
114
115 // Disconnect listener2 before notification. Only listener1 should receive
116 // the event.
117 listener2.Disconnect();
118 source.Notify(11);
119
120 queue->BeginShutdown();
121 queue->AwaitShutdownAndIdle();
122
123 EXPECT_EQ(i, 22); // 11 * 2
124 EXPECT_EQ(j, 0); // event not received
125
126 listener1.Disconnect();
127 }
128
129 /*
130 * Test we don't hit the assertion when calling Connect() and Disconnect()
131 * repeatedly.
132 */
TEST(MediaEventSource,DisconnectAndConnect)133 TEST(MediaEventSource, DisconnectAndConnect)
134 {
135 RefPtr<TaskQueue> queue =
136 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
137
138 MediaEventProducerExc<int> source;
139 MediaEventListener listener = source.Connect(queue, []() {});
140 listener.Disconnect();
141 listener = source.Connect(queue, []() {});
142 listener.Disconnect();
143 }
144
145 /*
146 * Test void event type.
147 */
TEST(MediaEventSource,VoidEventType)148 TEST(MediaEventSource, VoidEventType)
149 {
150 RefPtr<TaskQueue> queue =
151 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
152
153 MediaEventProducer<void> source;
154 int i = 0;
155
156 // Test function object.
157 auto func = [&]() { ++i; };
158 MediaEventListener listener1 = source.Connect(queue, func);
159
160 // Test member function.
161 struct Foo {
162 Foo() : j(1) {}
163 void OnNotify() { j *= 2; }
164 int j;
165 } foo;
166 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
167
168 // Call Notify 2 times. The listener should be also called 2 times.
169 source.Notify();
170 source.Notify();
171
172 queue->BeginShutdown();
173 queue->AwaitShutdownAndIdle();
174
175 // Verify the event data is passed correctly to the listener.
176 EXPECT_EQ(i, 2); // ++i called twice
177 EXPECT_EQ(foo.j, 4); // |j *= 2| called twice
178 listener1.Disconnect();
179 listener2.Disconnect();
180 }
181
182 /*
183 * Test listeners can take various event types (T, T&&, const T& and void).
184 */
TEST(MediaEventSource,ListenerType1)185 TEST(MediaEventSource, ListenerType1)
186 {
187 RefPtr<TaskQueue> queue =
188 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
189
190 MediaEventProducer<int> source;
191 int i = 0;
192
193 // Test various argument types.
194 auto func1 = [&](int&& j) { i += j; };
195 auto func2 = [&](const int& j) { i += j; };
196 auto func3 = [&]() { i += 1; };
197 MediaEventListener listener1 = source.Connect(queue, func1);
198 MediaEventListener listener2 = source.Connect(queue, func2);
199 MediaEventListener listener3 = source.Connect(queue, func3);
200
201 source.Notify(1);
202
203 queue->BeginShutdown();
204 queue->AwaitShutdownAndIdle();
205
206 EXPECT_EQ(i, 3);
207
208 listener1.Disconnect();
209 listener2.Disconnect();
210 listener3.Disconnect();
211 }
212
TEST(MediaEventSource,ListenerType2)213 TEST(MediaEventSource, ListenerType2)
214 {
215 RefPtr<TaskQueue> queue =
216 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
217
218 MediaEventProducer<int> source;
219
220 struct Foo {
221 Foo() : mInt(0) {}
222 void OnNotify1(int&& i) { mInt += i; }
223 void OnNotify2(const int& i) { mInt += i; }
224 void OnNotify3() { mInt += 1; }
225 void OnNotify4(int i) const { mInt += i; }
226 void OnNotify5(int i) volatile { mInt += i; }
227 mutable int mInt;
228 } foo;
229
230 // Test member functions which might be CV qualified.
231 MediaEventListener listener1 = source.Connect(queue, &foo, &Foo::OnNotify1);
232 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify2);
233 MediaEventListener listener3 = source.Connect(queue, &foo, &Foo::OnNotify3);
234 MediaEventListener listener4 = source.Connect(queue, &foo, &Foo::OnNotify4);
235 MediaEventListener listener5 = source.Connect(queue, &foo, &Foo::OnNotify5);
236
237 source.Notify(1);
238
239 queue->BeginShutdown();
240 queue->AwaitShutdownAndIdle();
241
242 EXPECT_EQ(foo.mInt, 5);
243
244 listener1.Disconnect();
245 listener2.Disconnect();
246 listener3.Disconnect();
247 listener4.Disconnect();
248 listener5.Disconnect();
249 }
250
251 struct SomeEvent {
SomeEventSomeEvent252 explicit SomeEvent(int& aCount) : mCount(aCount) {}
253 // Increment mCount when copy constructor is called to know how many times
254 // the event data is copied.
SomeEventSomeEvent255 SomeEvent(const SomeEvent& aOther) : mCount(aOther.mCount) { ++mCount; }
SomeEventSomeEvent256 SomeEvent(SomeEvent&& aOther) : mCount(aOther.mCount) {}
257 int& mCount;
258 };
259
260 /*
261 * Test we don't have unnecessary copies of the event data.
262 */
TEST(MediaEventSource,CopyEvent1)263 TEST(MediaEventSource, CopyEvent1)
264 {
265 RefPtr<TaskQueue> queue =
266 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
267
268 MediaEventProducer<SomeEvent> source;
269 int i = 0;
270
271 auto func = [](SomeEvent&& aEvent) {};
272 struct Foo {
273 void OnNotify(SomeEvent&& aEvent) {}
274 } foo;
275
276 MediaEventListener listener1 = source.Connect(queue, func);
277 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
278
279 // We expect i to be 2 since SomeEvent should be copied only once when
280 // passing to each listener.
281 source.Notify(SomeEvent(i));
282
283 queue->BeginShutdown();
284 queue->AwaitShutdownAndIdle();
285 EXPECT_EQ(i, 2);
286 listener1.Disconnect();
287 listener2.Disconnect();
288 }
289
TEST(MediaEventSource,CopyEvent2)290 TEST(MediaEventSource, CopyEvent2)
291 {
292 RefPtr<TaskQueue> queue =
293 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
294
295 MediaEventProducer<SomeEvent> source;
296 int i = 0;
297
298 auto func = []() {};
299 struct Foo {
300 void OnNotify() {}
301 } foo;
302
303 MediaEventListener listener1 = source.Connect(queue, func);
304 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
305
306 // SomeEvent won't be copied at all since the listeners take no arguments.
307 source.Notify(SomeEvent(i));
308
309 queue->BeginShutdown();
310 queue->AwaitShutdownAndIdle();
311 EXPECT_EQ(i, 0);
312 listener1.Disconnect();
313 listener2.Disconnect();
314 }
315
316 /*
317 * Test move-only types.
318 */
TEST(MediaEventSource,MoveOnly)319 TEST(MediaEventSource, MoveOnly)
320 {
321 RefPtr<TaskQueue> queue =
322 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
323
324 MediaEventProducerExc<UniquePtr<int>> source;
325
326 auto func = [](UniquePtr<int>&& aEvent) { EXPECT_EQ(*aEvent, 20); };
327 MediaEventListener listener = source.Connect(queue, func);
328
329 // It is OK to pass an rvalue which is move-only.
330 source.Notify(UniquePtr<int>(new int(20)));
331 // It is an error to pass an lvalue which is move-only.
332 // UniquePtr<int> event(new int(30));
333 // source.Notify(event);
334
335 queue->BeginShutdown();
336 queue->AwaitShutdownAndIdle();
337 listener.Disconnect();
338 }
339
340 struct RefCounter {
341 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCounter)
RefCounterRefCounter342 explicit RefCounter(int aVal) : mVal(aVal) {}
343 int mVal;
344
345 private:
346 ~RefCounter() = default;
347 };
348
349 /*
350 * Test we should copy instead of move in NonExclusive mode
351 * for each listener must get a copy.
352 */
TEST(MediaEventSource,NoMove)353 TEST(MediaEventSource, NoMove)
354 {
355 RefPtr<TaskQueue> queue =
356 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
357
358 MediaEventProducer<RefPtr<RefCounter>> source;
359
360 auto func1 = [](RefPtr<RefCounter>&& aEvent) { EXPECT_EQ(aEvent->mVal, 20); };
361 auto func2 = [](RefPtr<RefCounter>&& aEvent) { EXPECT_EQ(aEvent->mVal, 20); };
362 MediaEventListener listener1 = source.Connect(queue, func1);
363 MediaEventListener listener2 = source.Connect(queue, func2);
364
365 // We should copy this rvalue instead of move it in NonExclusive mode.
366 RefPtr<RefCounter> val = new RefCounter(20);
367 source.Notify(std::move(val));
368
369 queue->BeginShutdown();
370 queue->AwaitShutdownAndIdle();
371 listener1.Disconnect();
372 listener2.Disconnect();
373 }
374
375 /*
376 * Rvalue lambda should be moved instead of copied.
377 */
TEST(MediaEventSource,MoveLambda)378 TEST(MediaEventSource, MoveLambda)
379 {
380 RefPtr<TaskQueue> queue =
381 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
382
383 MediaEventProducer<void> source;
384
385 int counter = 0;
386 SomeEvent someEvent(counter);
387
388 auto func = [someEvent]() {};
389 // someEvent is copied when captured by the lambda.
390 EXPECT_EQ(someEvent.mCount, 1);
391
392 // someEvent should be copied for we pass |func| as an lvalue.
393 MediaEventListener listener1 = source.Connect(queue, func);
394 EXPECT_EQ(someEvent.mCount, 2);
395
396 // someEvent should be moved for we pass |func| as an rvalue.
397 MediaEventListener listener2 = source.Connect(queue, std::move(func));
398 EXPECT_EQ(someEvent.mCount, 2);
399
400 listener1.Disconnect();
401 listener2.Disconnect();
402 }
403
404 class ClassForDestroyCheck final {
405 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ClassForDestroyCheck);
406
ClassForDestroyCheck(bool * aIsDestroyed)407 explicit ClassForDestroyCheck(bool* aIsDestroyed)
408 : mIsDestroyed(aIsDestroyed) {
409 EXPECT_FALSE(*aIsDestroyed);
410 }
411
RefCountNums() const412 int32_t RefCountNums() const { return mRefCnt; }
413
414 private:
~ClassForDestroyCheck()415 ~ClassForDestroyCheck() {
416 EXPECT_FALSE(*mIsDestroyed);
417 *mIsDestroyed = true;
418 }
419 bool* const mIsDestroyed;
420 };
421
TEST(MediaEventSource,ResetFuncReferenceAfterDisconnect)422 TEST(MediaEventSource, ResetFuncReferenceAfterDisconnect)
423 {
424 const RefPtr<TaskQueue> queue =
425 new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR));
426 MediaEventProducer<void> source;
427
428 // Using a class that supports refcounting to check the object destruction.
429 bool isDestroyed = false;
430 auto object = MakeRefPtr<ClassForDestroyCheck>(&isDestroyed);
431 EXPECT_FALSE(isDestroyed);
432 EXPECT_EQ(object->RefCountNums(), 1);
433
434 // Function holds a strong reference to object.
435 MediaEventListener listener = source.Connect(queue, [ptr = object] {});
436 EXPECT_FALSE(isDestroyed);
437 EXPECT_EQ(object->RefCountNums(), 2);
438
439 // This should destroy the function and release the object reference from the
440 // function on the task queue,
441 listener.Disconnect();
442 queue->BeginShutdown();
443 queue->AwaitShutdownAndIdle();
444 EXPECT_FALSE(isDestroyed);
445 EXPECT_EQ(object->RefCountNums(), 1);
446
447 // No one is holding reference to object, it should be destroyed
448 // immediately.
449 object = nullptr;
450 EXPECT_TRUE(isDestroyed);
451 }
452