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/. */
6 #include "gtest/gtest.h"
8 #include "mozilla/SharedThreadPool.h"
9 #include "mozilla/TaskQueue.h"
10 #include "mozilla/UniquePtr.h"
11 #include "MediaEventSource.h"
12 #include "VideoUtils.h"
14 using namespace mozilla
;
17 * Test if listeners receive the event data correctly.
19 TEST(MediaEventSource
, SingleListener
)
21 RefPtr
<TaskQueue
> queue
=
22 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
23 "TestMediaEventSource SingleListener");
25 MediaEventProducer
<int> source
;
28 auto func
= [&](int j
) { i
+= j
; };
29 MediaEventListener listener
= source
.Connect(queue
, func
);
31 // Call Notify 3 times. The listener should be also called 3 times.
36 queue
->BeginShutdown();
37 queue
->AwaitShutdownAndIdle();
39 // Verify the event data is passed correctly to the listener.
40 EXPECT_EQ(i
, 15); // 3 + 5 + 7
41 listener
.Disconnect();
44 TEST(MediaEventSource
, MultiListener
)
46 RefPtr
<TaskQueue
> queue
=
47 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
48 "TestMediaEventSource MultiListener");
50 MediaEventProducer
<int> source
;
54 auto func1
= [&](int k
) { i
= k
* 2; };
55 auto func2
= [&](int k
) { j
= k
* 3; };
56 MediaEventListener listener1
= source
.Connect(queue
, func1
);
57 MediaEventListener listener2
= source
.Connect(queue
, func2
);
59 // Both listeners should receive the event.
62 queue
->BeginShutdown();
63 queue
->AwaitShutdownAndIdle();
65 // Verify the event data is passed correctly to the listener.
66 EXPECT_EQ(i
, 22); // 11 * 2
67 EXPECT_EQ(j
, 33); // 11 * 3
69 listener1
.Disconnect();
70 listener2
.Disconnect();
74 * Test if disconnecting a listener prevents events from coming.
76 TEST(MediaEventSource
, DisconnectAfterNotification
)
78 RefPtr
<TaskQueue
> queue
=
79 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
80 "TestMediaEventSource DisconnectAfterNotification");
82 MediaEventProducer
<int> source
;
85 MediaEventListener listener
;
86 auto func
= [&](int j
) {
88 listener
.Disconnect();
90 listener
= source
.Connect(queue
, func
);
92 // Call Notify() twice. Since we disconnect the listener when receiving
93 // the 1st event, the 2nd event should not reach the listener.
97 queue
->BeginShutdown();
98 queue
->AwaitShutdownAndIdle();
100 // Check only the 1st event is received.
104 TEST(MediaEventSource
, DisconnectBeforeNotification
)
106 RefPtr
<TaskQueue
> queue
=
107 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
108 "TestMediaEventSource DisconnectBeforeNotification");
110 MediaEventProducer
<int> source
;
114 auto func1
= [&](int k
) { i
= k
* 2; };
115 auto func2
= [&](int k
) { j
= k
* 3; };
116 MediaEventListener listener1
= source
.Connect(queue
, func1
);
117 MediaEventListener listener2
= source
.Connect(queue
, func2
);
119 // Disconnect listener2 before notification. Only listener1 should receive
121 listener2
.Disconnect();
124 queue
->BeginShutdown();
125 queue
->AwaitShutdownAndIdle();
127 EXPECT_EQ(i
, 22); // 11 * 2
128 EXPECT_EQ(j
, 0); // event not received
130 listener1
.Disconnect();
134 * Test we don't hit the assertion when calling Connect() and Disconnect()
137 TEST(MediaEventSource
, DisconnectAndConnect
)
139 RefPtr
<TaskQueue
> queue
=
140 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
141 "TestMediaEventSource DisconnectAndConnect");
143 MediaEventProducerExc
<int> source
;
144 MediaEventListener listener
= source
.Connect(queue
, []() {});
145 listener
.Disconnect();
146 listener
= source
.Connect(queue
, []() {});
147 listener
.Disconnect();
151 * Test void event type.
153 TEST(MediaEventSource
, VoidEventType
)
155 RefPtr
<TaskQueue
> queue
=
156 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
157 "TestMediaEventSource VoidEventType");
159 MediaEventProducer
<void> source
;
162 // Test function object.
163 auto func
= [&]() { ++i
; };
164 MediaEventListener listener1
= source
.Connect(queue
, func
);
166 // Test member function.
169 void OnNotify() { j
*= 2; }
172 MediaEventListener listener2
= source
.Connect(queue
, &foo
, &Foo::OnNotify
);
174 // Call Notify 2 times. The listener should be also called 2 times.
178 queue
->BeginShutdown();
179 queue
->AwaitShutdownAndIdle();
181 // Verify the event data is passed correctly to the listener.
182 EXPECT_EQ(i
, 2); // ++i called twice
183 EXPECT_EQ(foo
.j
, 4); // |j *= 2| called twice
184 listener1
.Disconnect();
185 listener2
.Disconnect();
189 * Test listeners can take various event types (T, T&&, const T& and void).
191 TEST(MediaEventSource
, ListenerType1
)
193 RefPtr
<TaskQueue
> queue
=
194 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
195 "TestMediaEventSource ListenerType1");
197 MediaEventProducer
<int> source
;
200 // Test various argument types.
201 auto func1
= [&](int&& j
) { i
+= j
; };
202 auto func2
= [&](const int& j
) { i
+= j
; };
203 auto func3
= [&]() { i
+= 1; };
204 MediaEventListener listener1
= source
.Connect(queue
, func1
);
205 MediaEventListener listener2
= source
.Connect(queue
, func2
);
206 MediaEventListener listener3
= source
.Connect(queue
, func3
);
210 queue
->BeginShutdown();
211 queue
->AwaitShutdownAndIdle();
215 listener1
.Disconnect();
216 listener2
.Disconnect();
217 listener3
.Disconnect();
220 TEST(MediaEventSource
, ListenerType2
)
222 RefPtr
<TaskQueue
> queue
=
223 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
224 "TestMediaEventSource ListenerType2");
226 MediaEventProducer
<int> source
;
230 void OnNotify1(int&& i
) { mInt
+= i
; }
231 void OnNotify2(const int& i
) { mInt
+= i
; }
232 void OnNotify3() { mInt
+= 1; }
233 void OnNotify4(int i
) const { mInt
+= i
; }
234 void OnNotify5(int i
) volatile { mInt
+= i
; }
238 // Test member functions which might be CV qualified.
239 MediaEventListener listener1
= source
.Connect(queue
, &foo
, &Foo::OnNotify1
);
240 MediaEventListener listener2
= source
.Connect(queue
, &foo
, &Foo::OnNotify2
);
241 MediaEventListener listener3
= source
.Connect(queue
, &foo
, &Foo::OnNotify3
);
242 MediaEventListener listener4
= source
.Connect(queue
, &foo
, &Foo::OnNotify4
);
243 MediaEventListener listener5
= source
.Connect(queue
, &foo
, &Foo::OnNotify5
);
247 queue
->BeginShutdown();
248 queue
->AwaitShutdownAndIdle();
250 EXPECT_EQ(foo
.mInt
, 5);
252 listener1
.Disconnect();
253 listener2
.Disconnect();
254 listener3
.Disconnect();
255 listener4
.Disconnect();
256 listener5
.Disconnect();
260 explicit SomeEvent(int& aCount
) : mCount(aCount
) {}
261 // Increment mCount when copy constructor is called to know how many times
262 // the event data is copied.
263 SomeEvent(const SomeEvent
& aOther
) : mCount(aOther
.mCount
) { ++mCount
; }
264 SomeEvent(SomeEvent
&& aOther
) : mCount(aOther
.mCount
) {}
269 * Test we don't have unnecessary copies of the event data.
271 TEST(MediaEventSource
, CopyEvent1
)
273 RefPtr
<TaskQueue
> queue
=
274 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
275 "TestMediaEventSource CopyEvent1");
277 MediaEventProducer
<SomeEvent
> source
;
280 auto func
= [](SomeEvent
&& aEvent
) {};
282 void OnNotify(SomeEvent
&& aEvent
) {}
285 MediaEventListener listener1
= source
.Connect(queue
, func
);
286 MediaEventListener listener2
= source
.Connect(queue
, &foo
, &Foo::OnNotify
);
288 // We expect i to be 2 since SomeEvent should be copied only once when
289 // passing to each listener.
290 source
.Notify(SomeEvent(i
));
292 queue
->BeginShutdown();
293 queue
->AwaitShutdownAndIdle();
295 listener1
.Disconnect();
296 listener2
.Disconnect();
299 TEST(MediaEventSource
, CopyEvent2
)
301 RefPtr
<TaskQueue
> queue
=
302 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
303 "TestMediaEventSource CopyEvent2");
305 MediaEventProducer
<SomeEvent
> source
;
313 MediaEventListener listener1
= source
.Connect(queue
, func
);
314 MediaEventListener listener2
= source
.Connect(queue
, &foo
, &Foo::OnNotify
);
316 // SomeEvent won't be copied at all since the listeners take no arguments.
317 source
.Notify(SomeEvent(i
));
319 queue
->BeginShutdown();
320 queue
->AwaitShutdownAndIdle();
322 listener1
.Disconnect();
323 listener2
.Disconnect();
327 * Test move-only types.
329 TEST(MediaEventSource
, MoveOnly
)
331 RefPtr
<TaskQueue
> queue
=
332 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
333 "TestMediaEventSource MoveOnly");
335 MediaEventProducerExc
<UniquePtr
<int>> source
;
337 auto func
= [](UniquePtr
<int>&& aEvent
) { EXPECT_EQ(*aEvent
, 20); };
338 MediaEventListener listener
= source
.Connect(queue
, func
);
340 // It is OK to pass an rvalue which is move-only.
341 source
.Notify(UniquePtr
<int>(new int(20)));
342 // It is an error to pass an lvalue which is move-only.
343 // UniquePtr<int> event(new int(30));
344 // source.Notify(event);
346 queue
->BeginShutdown();
347 queue
->AwaitShutdownAndIdle();
348 listener
.Disconnect();
352 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCounter
)
353 explicit RefCounter(int aVal
) : mVal(aVal
) {}
357 ~RefCounter() = default;
361 * Test we should copy instead of move in NonExclusive mode
362 * for each listener must get a copy.
364 TEST(MediaEventSource
, NoMove
)
366 RefPtr
<TaskQueue
> queue
=
367 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
368 "TestMediaEventSource NoMove");
370 MediaEventProducer
<RefPtr
<RefCounter
>> source
;
372 auto func1
= [](RefPtr
<RefCounter
>&& aEvent
) { EXPECT_EQ(aEvent
->mVal
, 20); };
373 auto func2
= [](RefPtr
<RefCounter
>&& aEvent
) { EXPECT_EQ(aEvent
->mVal
, 20); };
374 MediaEventListener listener1
= source
.Connect(queue
, func1
);
375 MediaEventListener listener2
= source
.Connect(queue
, func2
);
377 // We should copy this rvalue instead of move it in NonExclusive mode.
378 RefPtr
<RefCounter
> val
= new RefCounter(20);
379 source
.Notify(std::move(val
));
381 queue
->BeginShutdown();
382 queue
->AwaitShutdownAndIdle();
383 listener1
.Disconnect();
384 listener2
.Disconnect();
388 * Rvalue lambda should be moved instead of copied.
390 TEST(MediaEventSource
, MoveLambda
)
392 RefPtr
<TaskQueue
> queue
=
393 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
394 "TestMediaEventSource MoveLambda");
396 MediaEventProducer
<void> source
;
399 SomeEvent
someEvent(counter
);
401 auto func
= [someEvent
]() {};
402 // someEvent is copied when captured by the lambda.
403 EXPECT_EQ(someEvent
.mCount
, 1);
405 // someEvent should be copied for we pass |func| as an lvalue.
406 MediaEventListener listener1
= source
.Connect(queue
, func
);
407 EXPECT_EQ(someEvent
.mCount
, 2);
409 // someEvent should be moved for we pass |func| as an rvalue.
410 MediaEventListener listener2
= source
.Connect(queue
, std::move(func
));
411 EXPECT_EQ(someEvent
.mCount
, 2);
413 listener1
.Disconnect();
414 listener2
.Disconnect();
417 template <typename Bool
>
418 struct DestroyChecker
{
419 explicit DestroyChecker(Bool
* aIsDestroyed
) : mIsDestroyed(aIsDestroyed
) {
420 EXPECT_FALSE(*mIsDestroyed
);
423 EXPECT_FALSE(*mIsDestroyed
);
424 *mIsDestroyed
= true;
428 Bool
* const mIsDestroyed
;
431 class ClassForDestroyCheck final
: private DestroyChecker
<bool> {
432 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ClassForDestroyCheck
);
434 explicit ClassForDestroyCheck(bool* aIsDestroyed
)
435 : DestroyChecker(aIsDestroyed
) {}
437 int32_t RefCountNums() const { return mRefCnt
; }
440 ~ClassForDestroyCheck() = default;
443 TEST(MediaEventSource
, ResetFuncReferenceAfterDisconnect
)
445 const RefPtr
<TaskQueue
> queue
= TaskQueue::Create(
446 GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
447 "TestMediaEventSource ResetFuncReferenceAfterDisconnect");
448 MediaEventProducer
<void> source
;
450 // Using a class that supports refcounting to check the object destruction.
451 bool isDestroyed
= false;
452 auto object
= MakeRefPtr
<ClassForDestroyCheck
>(&isDestroyed
);
453 EXPECT_FALSE(isDestroyed
);
454 EXPECT_EQ(object
->RefCountNums(), 1);
456 // Function holds a strong reference to object.
457 MediaEventListener listener
= source
.Connect(queue
, [ptr
= object
] {});
458 EXPECT_FALSE(isDestroyed
);
459 EXPECT_EQ(object
->RefCountNums(), 2);
461 // This should destroy the function and release the object reference from the
462 // function on the task queue,
463 listener
.Disconnect();
464 queue
->BeginShutdown();
465 queue
->AwaitShutdownAndIdle();
466 EXPECT_FALSE(isDestroyed
);
467 EXPECT_EQ(object
->RefCountNums(), 1);
469 // No one is holding reference to object, it should be destroyed
472 EXPECT_TRUE(isDestroyed
);
475 TEST(MediaEventSource
, ResetTargetAfterDisconnect
)
477 RefPtr
<TaskQueue
> queue
=
478 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
479 "TestMediaEventSource ResetTargetAfterDisconnect");
480 MediaEventProducer
<void> source
;
481 MediaEventListener listener
= source
.Connect(queue
, [] {});
483 // MediaEventListener::Disconnect eventually gives up its target
484 listener
.Disconnect();
487 // `queue` should be the last reference to the TaskQueue, meaning that this
488 // Release destroys it.
489 EXPECT_EQ(queue
.forget().take()->Release(), 0u);