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