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::PLAYBACK));
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::PLAYBACK));
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::PLAYBACK));
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::PLAYBACK));
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   MediaEventProducerExc<int> source;
137   MediaEventListener listener = source.Connect(queue, []() {});
138   listener.Disconnect();
139   listener = source.Connect(queue, []() {});
140   listener.Disconnect();
141 }
142 
143 /*
144  * Test void event type.
145  */
TEST(MediaEventSource,VoidEventType)146 TEST(MediaEventSource, VoidEventType)
147 {
148   RefPtr<TaskQueue> queue =
149       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
150 
151   MediaEventProducer<void> source;
152   int i = 0;
153 
154   // Test function object.
155   auto func = [&]() { ++i; };
156   MediaEventListener listener1 = source.Connect(queue, func);
157 
158   // Test member function.
159   struct Foo {
160     Foo() : j(1) {}
161     void OnNotify() { j *= 2; }
162     int j;
163   } foo;
164   MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
165 
166   // Call Notify 2 times. The listener should be also called 2 times.
167   source.Notify();
168   source.Notify();
169 
170   queue->BeginShutdown();
171   queue->AwaitShutdownAndIdle();
172 
173   // Verify the event data is passed correctly to the listener.
174   EXPECT_EQ(i, 2);      // ++i called twice
175   EXPECT_EQ(foo.j, 4);  // |j *= 2| called twice
176   listener1.Disconnect();
177   listener2.Disconnect();
178 }
179 
180 /*
181  * Test listeners can take various event types (T, T&&, const T& and void).
182  */
TEST(MediaEventSource,ListenerType1)183 TEST(MediaEventSource, ListenerType1)
184 {
185   RefPtr<TaskQueue> queue =
186       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
187 
188   MediaEventProducer<int> source;
189   int i = 0;
190 
191   // Test various argument types.
192   auto func1 = [&](int&& j) { i += j; };
193   auto func2 = [&](const int& j) { i += j; };
194   auto func3 = [&]() { i += 1; };
195   MediaEventListener listener1 = source.Connect(queue, func1);
196   MediaEventListener listener2 = source.Connect(queue, func2);
197   MediaEventListener listener3 = source.Connect(queue, func3);
198 
199   source.Notify(1);
200 
201   queue->BeginShutdown();
202   queue->AwaitShutdownAndIdle();
203 
204   EXPECT_EQ(i, 3);
205 
206   listener1.Disconnect();
207   listener2.Disconnect();
208   listener3.Disconnect();
209 }
210 
TEST(MediaEventSource,ListenerType2)211 TEST(MediaEventSource, ListenerType2)
212 {
213   RefPtr<TaskQueue> queue =
214       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
215 
216   MediaEventProducer<int> source;
217 
218   struct Foo {
219     Foo() : mInt(0) {}
220     void OnNotify1(int&& i) { mInt += i; }
221     void OnNotify2(const int& i) { mInt += i; }
222     void OnNotify3() { mInt += 1; }
223     void OnNotify4(int i) const { mInt += i; }
224     void OnNotify5(int i) volatile { mInt += i; }
225     mutable int mInt;
226   } foo;
227 
228   // Test member functions which might be CV qualified.
229   MediaEventListener listener1 = source.Connect(queue, &foo, &Foo::OnNotify1);
230   MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify2);
231   MediaEventListener listener3 = source.Connect(queue, &foo, &Foo::OnNotify3);
232   MediaEventListener listener4 = source.Connect(queue, &foo, &Foo::OnNotify4);
233   MediaEventListener listener5 = source.Connect(queue, &foo, &Foo::OnNotify5);
234 
235   source.Notify(1);
236 
237   queue->BeginShutdown();
238   queue->AwaitShutdownAndIdle();
239 
240   EXPECT_EQ(foo.mInt, 5);
241 
242   listener1.Disconnect();
243   listener2.Disconnect();
244   listener3.Disconnect();
245   listener4.Disconnect();
246   listener5.Disconnect();
247 }
248 
249 struct SomeEvent {
SomeEventSomeEvent250   explicit SomeEvent(int& aCount) : mCount(aCount) {}
251   // Increment mCount when copy constructor is called to know how many times
252   // the event data is copied.
SomeEventSomeEvent253   SomeEvent(const SomeEvent& aOther) : mCount(aOther.mCount) { ++mCount; }
SomeEventSomeEvent254   SomeEvent(SomeEvent&& aOther) : mCount(aOther.mCount) {}
255   int& mCount;
256 };
257 
258 /*
259  * Test we don't have unnecessary copies of the event data.
260  */
TEST(MediaEventSource,CopyEvent1)261 TEST(MediaEventSource, CopyEvent1)
262 {
263   RefPtr<TaskQueue> queue =
264       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
265 
266   MediaEventProducer<SomeEvent> source;
267   int i = 0;
268 
269   auto func = [](SomeEvent&& aEvent) {};
270   struct Foo {
271     void OnNotify(SomeEvent&& aEvent) {}
272   } foo;
273 
274   MediaEventListener listener1 = source.Connect(queue, func);
275   MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
276 
277   // We expect i to be 2 since SomeEvent should be copied only once when
278   // passing to each listener.
279   source.Notify(SomeEvent(i));
280 
281   queue->BeginShutdown();
282   queue->AwaitShutdownAndIdle();
283   EXPECT_EQ(i, 2);
284   listener1.Disconnect();
285   listener2.Disconnect();
286 }
287 
TEST(MediaEventSource,CopyEvent2)288 TEST(MediaEventSource, CopyEvent2)
289 {
290   RefPtr<TaskQueue> queue =
291       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
292 
293   MediaEventProducer<SomeEvent> source;
294   int i = 0;
295 
296   auto func = []() {};
297   struct Foo {
298     void OnNotify() {}
299   } foo;
300 
301   MediaEventListener listener1 = source.Connect(queue, func);
302   MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
303 
304   // SomeEvent won't be copied at all since the listeners take no arguments.
305   source.Notify(SomeEvent(i));
306 
307   queue->BeginShutdown();
308   queue->AwaitShutdownAndIdle();
309   EXPECT_EQ(i, 0);
310   listener1.Disconnect();
311   listener2.Disconnect();
312 }
313 
314 /*
315  * Test move-only types.
316  */
TEST(MediaEventSource,MoveOnly)317 TEST(MediaEventSource, MoveOnly)
318 {
319   RefPtr<TaskQueue> queue =
320       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
321 
322   MediaEventProducerExc<UniquePtr<int>> source;
323 
324   auto func = [](UniquePtr<int>&& aEvent) { EXPECT_EQ(*aEvent, 20); };
325   MediaEventListener listener = source.Connect(queue, func);
326 
327   // It is OK to pass an rvalue which is move-only.
328   source.Notify(UniquePtr<int>(new int(20)));
329   // It is an error to pass an lvalue which is move-only.
330   // UniquePtr<int> event(new int(30));
331   // source.Notify(event);
332 
333   queue->BeginShutdown();
334   queue->AwaitShutdownAndIdle();
335   listener.Disconnect();
336 }
337 
338 struct RefCounter {
339   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCounter)
RefCounterRefCounter340   explicit RefCounter(int aVal) : mVal(aVal) {}
341   int mVal;
342 
343  private:
344   ~RefCounter() = default;
345 };
346 
347 /*
348  * Test we should copy instead of move in NonExclusive mode
349  * for each listener must get a copy.
350  */
TEST(MediaEventSource,NoMove)351 TEST(MediaEventSource, NoMove)
352 {
353   RefPtr<TaskQueue> queue =
354       new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK));
355 
356   MediaEventProducer<RefPtr<RefCounter>> source;
357 
358   auto func1 = [](RefPtr<RefCounter>&& aEvent) { EXPECT_EQ(aEvent->mVal, 20); };
359   auto func2 = [](RefPtr<RefCounter>&& aEvent) { EXPECT_EQ(aEvent->mVal, 20); };
360   MediaEventListener listener1 = source.Connect(queue, func1);
361   MediaEventListener listener2 = source.Connect(queue, func2);
362 
363   // We should copy this rvalue instead of move it in NonExclusive mode.
364   RefPtr<RefCounter> val = new RefCounter(20);
365   source.Notify(std::move(val));
366 
367   queue->BeginShutdown();
368   queue->AwaitShutdownAndIdle();
369   listener1.Disconnect();
370   listener2.Disconnect();
371 }
372 
373 /*
374  * Rvalue lambda should be moved instead of copied.
375  */
TEST(MediaEventSource,MoveLambda)376 TEST(MediaEventSource, MoveLambda)
377 {
378   RefPtr<TaskQueue> queue;
379   MediaEventProducer<void> source;
380 
381   int counter = 0;
382   SomeEvent someEvent(counter);
383 
384   auto func = [someEvent]() {};
385   // someEvent is copied when captured by the lambda.
386   EXPECT_EQ(someEvent.mCount, 1);
387 
388   // someEvent should be copied for we pass |func| as an lvalue.
389   MediaEventListener listener1 = source.Connect(queue, func);
390   EXPECT_EQ(someEvent.mCount, 2);
391 
392   // someEvent should be moved for we pass |func| as an rvalue.
393   MediaEventListener listener2 = source.Connect(queue, std::move(func));
394   EXPECT_EQ(someEvent.mCount, 2);
395 
396   listener1.Disconnect();
397   listener2.Disconnect();
398 }
399