Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / gtest / TestMediaEventSource.cpp
blob159e7bd5c936d4ca0d209a982e2b065eca4ad255
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;
26 int i = 0;
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.
32 source.Notify(3);
33 source.Notify(5);
34 source.Notify(7);
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;
51 int i = 0;
52 int j = 0;
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.
60 source.Notify(11);
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;
83 int i = 0;
85 MediaEventListener listener;
86 auto func = [&](int j) {
87 i += 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.
94 source.Notify(11);
95 source.Notify(11);
97 queue->BeginShutdown();
98 queue->AwaitShutdownAndIdle();
100 // Check only the 1st event is received.
101 EXPECT_EQ(i, 11);
104 TEST(MediaEventSource, DisconnectBeforeNotification)
106 RefPtr<TaskQueue> queue =
107 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
108 "TestMediaEventSource DisconnectBeforeNotification");
110 MediaEventProducer<int> source;
111 int i = 0;
112 int j = 0;
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
120 // the event.
121 listener2.Disconnect();
122 source.Notify(11);
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()
135 * repeatedly.
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;
160 int i = 0;
162 // Test function object.
163 auto func = [&]() { ++i; };
164 MediaEventListener listener1 = source.Connect(queue, func);
166 // Test member function.
167 struct Foo {
168 Foo() : j(1) {}
169 void OnNotify() { j *= 2; }
170 int j;
171 } foo;
172 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
174 // Call Notify 2 times. The listener should be also called 2 times.
175 source.Notify();
176 source.Notify();
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;
198 int i = 0;
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);
208 source.Notify(1);
210 queue->BeginShutdown();
211 queue->AwaitShutdownAndIdle();
213 EXPECT_EQ(i, 3);
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;
228 struct Foo {
229 Foo() : mInt(0) {}
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; }
235 mutable int mInt;
236 } foo;
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);
245 source.Notify(1);
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();
259 struct SomeEvent {
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) {}
265 int& 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;
278 int i = 0;
280 auto func = [](SomeEvent&& aEvent) {};
281 struct Foo {
282 void OnNotify(SomeEvent&& aEvent) {}
283 } foo;
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();
294 EXPECT_EQ(i, 2);
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;
306 int i = 0;
308 auto func = []() {};
309 struct Foo {
310 void OnNotify() {}
311 } foo;
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();
321 EXPECT_EQ(i, 0);
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();
351 struct RefCounter {
352 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCounter)
353 explicit RefCounter(int aVal) : mVal(aVal) {}
354 int mVal;
356 private:
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;
398 int counter = 0;
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);
422 ~DestroyChecker() {
423 EXPECT_FALSE(*mIsDestroyed);
424 *mIsDestroyed = true;
427 private:
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; }
439 protected:
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
470 // immediately.
471 object = nullptr;
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();
485 queue->AwaitIdle();
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);