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