tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestMediaEventSource.cpp (24731B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <memory>
      8 
      9 #include "MediaEventSource.h"
     10 #include "VideoUtils.h"
     11 #include "gmock/gmock-matchers.h"  // testing::ElementsAre
     12 #include "gmock/gmock.h"
     13 #include "gtest/gtest.h"
     14 #include "mozilla/SharedThreadPool.h"
     15 #include "mozilla/TaskQueue.h"
     16 #include "mozilla/UniquePtr.h"
     17 
     18 using namespace mozilla;
     19 using testing::InSequence;
     20 using testing::MockFunction;
     21 using testing::StrEq;
     22 
     23 // TODO(bug 1954634): Once all of our toolchains support c++ requires
     24 // expression, we should be validating that ineligible function signatures will
     25 // not compile. (eg; NonExclusive does not work with non-const refs or rvalue
     26 // refs)
     27 
     28 /*
     29 * Test if listeners receive the event data correctly.
     30 */
     31 TEST(MediaEventSource, SingleListener)
     32 {
     33  RefPtr<TaskQueue> queue =
     34      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
     35                        "TestMediaEventSource SingleListener");
     36 
     37  MediaEventProducer<int> source;
     38 
     39  static std::vector<int> callbackLog;
     40  callbackLog.clear();
     41 
     42  auto func = [&](int j) { callbackLog.push_back(j); };
     43  MediaEventListener listener = source.Connect(queue, func);
     44 
     45  // Call Notify 3 times. The listener should be also called 3 times.
     46  source.Notify(3);
     47  source.Notify(5);
     48  source.Notify(7);
     49 
     50  queue->BeginShutdown();
     51  queue->AwaitShutdownAndIdle();
     52 
     53  // Verify the event data is passed correctly to the listener.
     54  EXPECT_THAT(callbackLog, testing::ElementsAre(3, 5, 7));
     55 
     56  listener.Disconnect();
     57 }
     58 
     59 TEST(MediaEventSource, MultiListener)
     60 {
     61  RefPtr<TaskQueue> queue =
     62      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
     63                        "TestMediaEventSource MultiListener");
     64 
     65  MediaEventProducer<int> source;
     66 
     67  static std::vector<int> callbackLog;
     68  callbackLog.clear();
     69 
     70  auto func1 = [&](int k) { callbackLog.push_back(k * 2); };
     71  auto func2 = [&](int k) { callbackLog.push_back(k * 3); };
     72  MediaEventListener listener1 = source.Connect(queue, func1);
     73  MediaEventListener listener2 = source.Connect(queue, func2);
     74 
     75  // Both listeners should receive the event.
     76  source.Notify(11);
     77 
     78  queue->BeginShutdown();
     79  queue->AwaitShutdownAndIdle();
     80 
     81  // Verify the event data is passed correctly to the listener.
     82  EXPECT_THAT(callbackLog, testing::ElementsAre(22, 33));
     83 
     84  listener1.Disconnect();
     85  listener2.Disconnect();
     86 }
     87 
     88 /*
     89 * Test if disconnecting a listener prevents events from coming.
     90 */
     91 TEST(MediaEventSource, DisconnectAfterNotification)
     92 {
     93  RefPtr<TaskQueue> queue =
     94      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
     95                        "TestMediaEventSource DisconnectAfterNotification");
     96 
     97  MediaEventProducer<int> source;
     98 
     99  static std::vector<int> callbackLog;
    100  callbackLog.clear();
    101 
    102  MediaEventListener listener;
    103  auto func = [&](int j) {
    104    callbackLog.push_back(j);
    105    listener.Disconnect();
    106  };
    107  listener = source.Connect(queue, func);
    108 
    109  // Call Notify() twice. Since we disconnect the listener when receiving
    110  // the 1st event, the 2nd event should not reach the listener.
    111  source.Notify(11);
    112  source.Notify(11);
    113 
    114  queue->BeginShutdown();
    115  queue->AwaitShutdownAndIdle();
    116 
    117  // Check only the 1st event is received.
    118  EXPECT_THAT(callbackLog, testing::ElementsAre(11));
    119 }
    120 
    121 TEST(MediaEventSource, DisconnectBeforeNotification)
    122 {
    123  RefPtr<TaskQueue> queue =
    124      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    125                        "TestMediaEventSource DisconnectBeforeNotification");
    126 
    127  MediaEventProducer<int> source;
    128 
    129  static std::vector<int> callbackLog;
    130  callbackLog.clear();
    131 
    132  auto func1 = [&](int k) { callbackLog.push_back(k * 2); };
    133  auto func2 = [&](int k) { callbackLog.push_back(k * 3); };
    134  MediaEventListener listener1 = source.Connect(queue, func1);
    135  MediaEventListener listener2 = source.Connect(queue, func2);
    136 
    137  // Disconnect listener2 before notification. Only listener1 should receive
    138  // the event.
    139  listener2.Disconnect();
    140  source.Notify(11);
    141 
    142  queue->BeginShutdown();
    143  queue->AwaitShutdownAndIdle();
    144 
    145  EXPECT_THAT(callbackLog, testing::ElementsAre(22));
    146 
    147  listener1.Disconnect();
    148 }
    149 
    150 /*
    151 * Test we don't hit the assertion when calling Connect() and Disconnect()
    152 * repeatedly.
    153 */
    154 TEST(MediaEventSource, DisconnectAndConnect)
    155 {
    156  RefPtr<TaskQueue> queue =
    157      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    158                        "TestMediaEventSource DisconnectAndConnect");
    159 
    160  MediaEventProducerExc<int> source;
    161  MediaEventListener listener = source.Connect(queue, []() {});
    162  listener.Disconnect();
    163  listener = source.Connect(queue, []() {});
    164  listener.Disconnect();
    165 }
    166 
    167 /*
    168 * Test void event type.
    169 */
    170 TEST(MediaEventSource, VoidEventType)
    171 {
    172  RefPtr<TaskQueue> queue =
    173      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    174                        "TestMediaEventSource VoidEventType");
    175 
    176  MediaEventProducer<void> source;
    177 
    178  static std::vector<int> callbackLog;
    179  callbackLog.clear();
    180 
    181  // Test function object.
    182  auto func = [&]() { callbackLog.push_back(1); };
    183  MediaEventListener listener1 = source.Connect(queue, func);
    184 
    185  // Test member function.
    186  struct Foo {
    187    Foo() {}
    188    void OnNotify() { callbackLog.push_back(2); }
    189  } foo;
    190  MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
    191 
    192  // Call Notify 2 times. The listener should be also called 2 times.
    193  source.Notify();
    194  source.Notify();
    195 
    196  queue->BeginShutdown();
    197  queue->AwaitShutdownAndIdle();
    198 
    199  EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2, 1, 2));
    200 
    201  listener1.Disconnect();
    202  listener2.Disconnect();
    203 }
    204 
    205 /*
    206 * Test listeners can take various event types (T, const T&, and void).
    207 */
    208 TEST(MediaEventSource, ListenerType1)
    209 {
    210  RefPtr<TaskQueue> queue =
    211      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    212                        "TestMediaEventSource ListenerType1");
    213 
    214  MediaEventProducer<int> source;
    215 
    216  static std::vector<int> callbackLog;
    217  callbackLog.clear();
    218 
    219  // Test various argument types.
    220  // func(int&&) and func(int&) are ineligible because we're in NonExclusive
    221  // mode, which passes a const.
    222  auto func1 = [&](int j) { callbackLog.push_back(1); };
    223  auto func2 = [&](const int& j) { callbackLog.push_back(2); };
    224  auto func3 = [&]() { callbackLog.push_back(3); };
    225  MediaEventListener listener1 = source.Connect(queue, func1);
    226  MediaEventListener listener2 = source.Connect(queue, func2);
    227  MediaEventListener listener3 = source.Connect(queue, func3);
    228 
    229  source.Notify(1);
    230 
    231  queue->BeginShutdown();
    232  queue->AwaitShutdownAndIdle();
    233 
    234  EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2, 3));
    235 
    236  listener1.Disconnect();
    237  listener2.Disconnect();
    238  listener3.Disconnect();
    239 }
    240 
    241 TEST(MediaEventSource, ListenerType2)
    242 {
    243  RefPtr<TaskQueue> queue =
    244      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    245                        "TestMediaEventSource ListenerType2");
    246 
    247  MediaEventProducer<int> source;
    248 
    249  static std::vector<int> callbackLog;
    250  callbackLog.clear();
    251 
    252  struct Foo {
    253    void OnNotify1(const int& i) { callbackLog.push_back(1); }
    254    void OnNotify2() { callbackLog.push_back(2); }
    255    void OnNotify3(int i) const { callbackLog.push_back(3); }
    256    void OnNotify4(int i) volatile { callbackLog.push_back(4); }
    257  } foo;
    258 
    259  // Test member functions which might be CV qualified.
    260  MediaEventListener listener1 = source.Connect(queue, &foo, &Foo::OnNotify1);
    261  MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify2);
    262  MediaEventListener listener3 = source.Connect(queue, &foo, &Foo::OnNotify3);
    263  MediaEventListener listener4 = source.Connect(queue, &foo, &Foo::OnNotify4);
    264 
    265  source.Notify(1);
    266 
    267  queue->BeginShutdown();
    268  queue->AwaitShutdownAndIdle();
    269 
    270  EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2, 3, 4));
    271 
    272  listener1.Disconnect();
    273  listener2.Disconnect();
    274  listener3.Disconnect();
    275  listener4.Disconnect();
    276 }
    277 
    278 struct SomeEvent {
    279  explicit SomeEvent(int& aCount) : mCount(aCount) {}
    280  // Increment mCount when copy constructor is called to know how many times
    281  // the event data is copied.
    282  SomeEvent(const SomeEvent& aOther) : mCount(aOther.mCount) { ++mCount; }
    283  SomeEvent(SomeEvent&& aOther) : mCount(aOther.mCount) {}
    284  int& mCount;
    285 };
    286 
    287 /*
    288 * Test we don't have unnecessary copies of the event data.
    289 */
    290 TEST(MediaEventSource, ZeroCopyNonExclusiveOneTarget)
    291 {
    292  RefPtr<TaskQueue> queue =
    293      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    294                        "TestMediaEventSource ZeroCopyNonExclusiveOneTarget");
    295 
    296  MediaEventProducer<SomeEvent> source;
    297  int copies = 0;
    298 
    299  static std::vector<int> callbackLog;
    300  callbackLog.clear();
    301 
    302  auto func = []() { callbackLog.push_back(1); };
    303  struct Foo {
    304    void OnNotify() { callbackLog.push_back(2); }
    305  } foo;
    306 
    307  MediaEventListener listener1 = source.Connect(queue, func);
    308  MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
    309 
    310  // We expect i to be 0 since Notify can take ownership of the temp object,
    311  // and use it as shared state for all listeners.
    312  source.Notify(SomeEvent(copies));
    313 
    314  queue->BeginShutdown();
    315  queue->AwaitShutdownAndIdle();
    316  EXPECT_EQ(copies, 0);
    317 
    318  EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2));
    319 
    320  listener1.Disconnect();
    321  listener2.Disconnect();
    322 }
    323 
    324 TEST(MediaEventSource, ZeroCopyNonExclusiveTwoTarget)
    325 {
    326  RefPtr<TaskQueue> queue1 = TaskQueue::Create(
    327      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    328      "TestMediaEventSource ZeroCopyNonExclusiveTwoTarget(first)");
    329  RefPtr<TaskQueue> queue2 = TaskQueue::Create(
    330      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    331      "TestMediaEventSource ZeroCopyNonExclusiveTwoTarget(second)");
    332 
    333  MediaEventProducer<SomeEvent> source;
    334  int copies = 0;
    335 
    336  static std::vector<int> callbackLog1;
    337  callbackLog1.clear();
    338 
    339  static std::vector<int> callbackLog2;
    340  callbackLog2.clear();
    341 
    342  auto func1 = []() { callbackLog1.push_back(1); };
    343  struct Foo1 {
    344    void OnNotify() { callbackLog1.push_back(2); }
    345  } foo1;
    346 
    347  auto func2 = []() { callbackLog2.push_back(1); };
    348  struct Foo2 {
    349    void OnNotify() { callbackLog2.push_back(2); }
    350  } foo2;
    351 
    352  MediaEventListener listener1 = source.Connect(queue1, func1);
    353  MediaEventListener listener2 = source.Connect(queue1, &foo1, &Foo1::OnNotify);
    354  MediaEventListener listener3 = source.Connect(queue2, func2);
    355  MediaEventListener listener4 = source.Connect(queue2, &foo2, &Foo2::OnNotify);
    356 
    357  // We expect i to be 0 since Notify can take ownership of the temp object,
    358  // and use it as shared state for all listeners.
    359  source.Notify(SomeEvent(copies));
    360 
    361  queue1->BeginShutdown();
    362  queue1->AwaitShutdownAndIdle();
    363  queue2->BeginShutdown();
    364  queue2->AwaitShutdownAndIdle();
    365  EXPECT_EQ(copies, 0);
    366  EXPECT_THAT(callbackLog1, testing::ElementsAre(1, 2));
    367  EXPECT_THAT(callbackLog2, testing::ElementsAre(1, 2));
    368 
    369  listener1.Disconnect();
    370  listener2.Disconnect();
    371  listener3.Disconnect();
    372  listener4.Disconnect();
    373 }
    374 
    375 TEST(MediaEventSource, ZeroCopyOneCopyPerThreadOneTarget)
    376 {
    377  RefPtr<TaskQueue> queue = TaskQueue::Create(
    378      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    379      "TestMediaEventSource ZeroCopyOneCopyPerThreadOneTarget");
    380 
    381  MediaEventProducerOneCopyPerThread<SomeEvent> source;
    382  int copies = 0;
    383 
    384  static std::vector<int> callbackLog;
    385  callbackLog.clear();
    386 
    387  auto func = []() { callbackLog.push_back(1); };
    388  struct Foo {
    389    void OnNotify() { callbackLog.push_back(2); }
    390  } foo;
    391 
    392  MediaEventListener listener1 = source.Connect(queue, func);
    393  MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
    394 
    395  // We expect i to be 0 since Notify can take ownership of the temp object,
    396  // which is then used to notify listeners on the single target.
    397  source.Notify(SomeEvent(copies));
    398 
    399  queue->BeginShutdown();
    400  queue->AwaitShutdownAndIdle();
    401  EXPECT_EQ(copies, 0);
    402  EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2));
    403 
    404  listener1.Disconnect();
    405  listener2.Disconnect();
    406 }
    407 
    408 TEST(MediaEventSource, ZeroCopyOneCopyPerThreadNoArglessCopy)
    409 {
    410  RefPtr<TaskQueue> queue1 = TaskQueue::Create(
    411      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    412      "TestMediaEventSource ZeroCopyOneCopyPerThreadNoArglessCopy(first)");
    413  RefPtr<TaskQueue> queue2 = TaskQueue::Create(
    414      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    415      "TestMediaEventSource ZeroCopyOneCopyPerThreadNoArglessCopy(second)");
    416 
    417  MediaEventProducerOneCopyPerThread<SomeEvent> source;
    418  int copies = 0;
    419 
    420  // func(SomeEvent&&) is ineligible, because OneCopyPerThread passes an lvalue
    421  // ref.
    422  auto arglessFunc = []() {};
    423  auto func = [](SomeEvent& aEvent) {};
    424  auto func2 = [](const SomeEvent& aEvent) {};
    425  struct Foo {
    426    void OnNotify(SomeEvent& aEvent) {}
    427    void OnNotify2(const SomeEvent& aEvent) {}
    428  } foo;
    429 
    430  MediaEventListener listener1 = source.Connect(queue1, func);
    431  MediaEventListener listener2 = source.Connect(queue1, &foo, &Foo::OnNotify);
    432  MediaEventListener listener3 = source.Connect(queue1, func2);
    433  MediaEventListener listener4 = source.Connect(queue1, &foo, &Foo::OnNotify2);
    434  MediaEventListener listener5 = source.Connect(queue2, arglessFunc);
    435 
    436  // We expect i to be 0 since Notify can take ownership of the temp object,
    437  // and use it to notify the listeners on queue1, since none of the listeners
    438  // on queue2 take arguments.
    439  source.Notify(SomeEvent(copies));
    440 
    441  queue1->BeginShutdown();
    442  queue1->AwaitShutdownAndIdle();
    443  queue2->BeginShutdown();
    444  queue2->AwaitShutdownAndIdle();
    445  EXPECT_EQ(copies, 0);
    446  listener1.Disconnect();
    447  listener2.Disconnect();
    448  listener3.Disconnect();
    449  listener4.Disconnect();
    450  listener5.Disconnect();
    451 }
    452 
    453 TEST(MediaEventSource, CopyForAdditionalTargets)
    454 {
    455  RefPtr<TaskQueue> queue1 =
    456      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    457                        "TestMediaEventSource CopyForAdditionalTargets(first)");
    458  RefPtr<TaskQueue> queue2 = TaskQueue::Create(
    459      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    460      "TestMediaEventSource CopyForAdditionalTargets(second)");
    461 
    462  MediaEventProducerOneCopyPerThread<SomeEvent> source;
    463  int copies = 0;
    464 
    465  static std::vector<int> callbackLog1;
    466  callbackLog1.clear();
    467  auto func1 = [](SomeEvent& aEvent) { callbackLog1.push_back(0); };
    468  struct Foo1 {
    469    void OnNotify(SomeEvent& aEvent) { callbackLog1.push_back(1); }
    470  } foo1;
    471 
    472  static std::vector<int> callbackLog2;
    473  callbackLog2.clear();
    474  auto func2 = [](const SomeEvent& aEvent) { callbackLog2.push_back(0); };
    475  struct Foo2 {
    476    void OnNotify(const SomeEvent& aEvent) { callbackLog2.push_back(1); }
    477  } foo2;
    478 
    479  MediaEventListener listener1 = source.Connect(queue1, func1);
    480  MediaEventListener listener2 = source.Connect(queue1, &foo1, &Foo1::OnNotify);
    481  MediaEventListener listener3 = source.Connect(queue2, func2);
    482  MediaEventListener listener4 = source.Connect(queue2, &foo2, &Foo2::OnNotify);
    483 
    484  // We expect i to be 1 since Notify can take ownership of the temp object,
    485  // make a copy for the listeners on queue1, and then give the original to the
    486  // listeners on queue2.
    487  source.Notify(SomeEvent(copies));
    488 
    489  queue1->BeginShutdown();
    490  queue1->AwaitShutdownAndIdle();
    491  queue2->BeginShutdown();
    492  queue2->AwaitShutdownAndIdle();
    493  EXPECT_EQ(copies, 1);
    494  EXPECT_THAT(callbackLog1, testing::ElementsAre(0, 1));
    495  EXPECT_THAT(callbackLog2, testing::ElementsAre(0, 1));
    496 
    497  listener1.Disconnect();
    498  listener2.Disconnect();
    499  listener3.Disconnect();
    500  listener4.Disconnect();
    501 }
    502 
    503 TEST(MediaEventSource, CopyEventUnneeded)
    504 {
    505  RefPtr<TaskQueue> queue =
    506      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    507                        "TestMediaEventSource CopyEventUnneeded");
    508 
    509  MediaEventProducer<SomeEvent> source;
    510  int copies = 0;
    511 
    512  static std::vector<int> callbackLog;
    513  callbackLog.clear();
    514  auto func = []() { callbackLog.push_back(0); };
    515  struct Foo {
    516    void OnNotify() { callbackLog.push_back(1); }
    517  } foo;
    518 
    519  MediaEventListener listener1 = source.Connect(queue, func);
    520  MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify);
    521 
    522  // Non-temporary; if Notify takes the event at all, it will need to make at
    523  // least one copy. It should not need to take it at all, since all listeners
    524  // are argless.
    525  std::unique_ptr<SomeEvent> event(new SomeEvent(copies));
    526  // SomeEvent won't be copied at all since the listeners take no arguments.
    527  source.Notify(*event);
    528 
    529  queue->BeginShutdown();
    530  queue->AwaitShutdownAndIdle();
    531  EXPECT_EQ(copies, 0);
    532  EXPECT_THAT(callbackLog, testing::ElementsAre(0, 1));
    533 
    534  listener1.Disconnect();
    535  listener2.Disconnect();
    536 }
    537 
    538 /*
    539 * Test move-only types.
    540 */
    541 TEST(MediaEventSource, MoveOnly)
    542 {
    543  RefPtr<TaskQueue> queue =
    544      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    545                        "TestMediaEventSource MoveOnly");
    546 
    547  MediaEventProducerExc<UniquePtr<int>> source;
    548  static std::vector<int> callbackLog;
    549  callbackLog.clear();
    550 
    551  auto func = [](UniquePtr<int>&& aEvent) { callbackLog.push_back(*aEvent); };
    552  MediaEventListener listener = source.Connect(queue, func);
    553 
    554  // It is OK to pass an rvalue which is move-only.
    555  source.Notify(UniquePtr<int>(new int(20)));
    556  // It is an error to pass an lvalue which is move-only.
    557  // UniquePtr<int> event(new int(30));
    558  // source.Notify(event);
    559 
    560  queue->BeginShutdown();
    561  queue->AwaitShutdownAndIdle();
    562 
    563  EXPECT_THAT(callbackLog, testing::ElementsAre(20));
    564 
    565  listener.Disconnect();
    566 }
    567 
    568 TEST(MediaEventSource, ExclusiveConstLvalueRef)
    569 {
    570  RefPtr<TaskQueue> queue =
    571      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    572                        "TestMediaEventSource ExclusiveConstLvalueRef");
    573 
    574  MediaEventProducerExc<UniquePtr<int>> source;
    575  static std::vector<int> callbackLog;
    576  callbackLog.clear();
    577 
    578  auto func = [](const UniquePtr<int>& aEvent) {
    579    callbackLog.push_back(*aEvent);
    580  };
    581  MediaEventListener listener = source.Connect(queue, func);
    582 
    583  source.Notify(UniquePtr<int>(new int(20)));
    584 
    585  queue->BeginShutdown();
    586  queue->AwaitShutdownAndIdle();
    587 
    588  EXPECT_THAT(callbackLog, testing::ElementsAre(20));
    589 
    590  listener.Disconnect();
    591 }
    592 
    593 TEST(MediaEventSource, ExclusiveNoArgs)
    594 {
    595  RefPtr<TaskQueue> queue =
    596      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    597                        "TestMediaEventSource ExclusiveNoArgs");
    598 
    599  MediaEventProducerExc<UniquePtr<int>> source;
    600  static int callbackCount = 0;
    601 
    602  auto func = []() { ++callbackCount; };
    603  MediaEventListener listener = source.Connect(queue, func);
    604 
    605  source.Notify(UniquePtr<int>(new int(20)));
    606 
    607  queue->BeginShutdown();
    608  queue->AwaitShutdownAndIdle();
    609 
    610  ASSERT_EQ(callbackCount, 1);
    611 
    612  listener.Disconnect();
    613 }
    614 
    615 struct RefCounter {
    616  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCounter)
    617  explicit RefCounter(int aVal) : mVal(aVal) {}
    618  int mVal;
    619 
    620 private:
    621  ~RefCounter() = default;
    622 };
    623 
    624 /*
    625 * Test we should copy instead of move in NonExclusive mode
    626 * for each listener must get a copy.
    627 */
    628 TEST(MediaEventSource, NoMove)
    629 {
    630  RefPtr<TaskQueue> queue =
    631      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    632                        "TestMediaEventSource NoMove");
    633 
    634  MediaEventProducer<RefPtr<RefCounter>> source;
    635 
    636  auto func1 = [](const RefPtr<RefCounter>& aEvent) {
    637    EXPECT_EQ(aEvent->mVal, 20);
    638  };
    639  auto func2 = [](const RefPtr<RefCounter>& aEvent) {
    640    EXPECT_EQ(aEvent->mVal, 20);
    641  };
    642  MediaEventListener listener1 = source.Connect(queue, func1);
    643  MediaEventListener listener2 = source.Connect(queue, func2);
    644 
    645  // We should copy this rvalue instead of move it in NonExclusive mode.
    646  RefPtr<RefCounter> val = new RefCounter(20);
    647  source.Notify(std::move(val));
    648 
    649  queue->BeginShutdown();
    650  queue->AwaitShutdownAndIdle();
    651  listener1.Disconnect();
    652  listener2.Disconnect();
    653 }
    654 
    655 /*
    656 * Rvalue lambda should be moved instead of copied.
    657 */
    658 TEST(MediaEventSource, MoveLambda)
    659 {
    660  RefPtr<TaskQueue> queue =
    661      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    662                        "TestMediaEventSource MoveLambda");
    663 
    664  MediaEventProducer<void> source;
    665 
    666  int counter = 0;
    667  SomeEvent someEvent(counter);
    668 
    669  auto func = [someEvent]() {};
    670  // someEvent is copied when captured by the lambda.
    671  EXPECT_EQ(someEvent.mCount, 1);
    672 
    673  // someEvent should be copied for we pass |func| as an lvalue.
    674  MediaEventListener listener1 = source.Connect(queue, func);
    675  EXPECT_EQ(someEvent.mCount, 2);
    676 
    677  // someEvent should be moved for we pass |func| as an rvalue.
    678  MediaEventListener listener2 = source.Connect(queue, std::move(func));
    679  EXPECT_EQ(someEvent.mCount, 2);
    680 
    681  listener1.Disconnect();
    682  listener2.Disconnect();
    683 }
    684 
    685 template <typename Bool>
    686 struct DestroyChecker {
    687  explicit DestroyChecker(Bool* aIsDestroyed) : mIsDestroyed(aIsDestroyed) {
    688    EXPECT_FALSE(*mIsDestroyed);
    689  }
    690  ~DestroyChecker() {
    691    EXPECT_FALSE(*mIsDestroyed);
    692    *mIsDestroyed = true;
    693  }
    694 
    695 private:
    696  Bool* const mIsDestroyed;
    697 };
    698 
    699 class ClassForDestroyCheck final : private DestroyChecker<bool> {
    700  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ClassForDestroyCheck);
    701 
    702  explicit ClassForDestroyCheck(bool* aIsDestroyed)
    703      : DestroyChecker(aIsDestroyed) {}
    704 
    705  int32_t RefCountNums() const { return mRefCnt; }
    706 
    707 protected:
    708  ~ClassForDestroyCheck() = default;
    709 };
    710 
    711 TEST(MediaEventSource, ResetFuncReferenceAfterDisconnect)
    712 {
    713  const RefPtr<TaskQueue> queue = TaskQueue::Create(
    714      GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    715      "TestMediaEventSource ResetFuncReferenceAfterDisconnect");
    716  MediaEventProducer<void> source;
    717 
    718  // Using a class that supports refcounting to check the object destruction.
    719  bool isDestroyed = false;
    720  auto object = MakeRefPtr<ClassForDestroyCheck>(&isDestroyed);
    721  EXPECT_FALSE(isDestroyed);
    722  EXPECT_EQ(object->RefCountNums(), 1);
    723 
    724  // Function holds a strong reference to object.
    725  MediaEventListener listener = source.Connect(queue, [ptr = object] {});
    726  EXPECT_FALSE(isDestroyed);
    727  EXPECT_EQ(object->RefCountNums(), 2);
    728 
    729  // This should destroy the function and release the object reference from the
    730  // function on the task queue,
    731  listener.Disconnect();
    732  queue->BeginShutdown();
    733  queue->AwaitShutdownAndIdle();
    734  EXPECT_FALSE(isDestroyed);
    735  EXPECT_EQ(object->RefCountNums(), 1);
    736 
    737  // No one is holding reference to object, it should be destroyed
    738  // immediately.
    739  object = nullptr;
    740  EXPECT_TRUE(isDestroyed);
    741 }
    742 
    743 TEST(MediaEventSource, ResetTargetAfterDisconnect)
    744 {
    745  RefPtr<TaskQueue> queue =
    746      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
    747                        "TestMediaEventSource ResetTargetAfterDisconnect");
    748  MediaEventProducer<void> source;
    749  MediaEventListener listener = source.Connect(queue, [] {});
    750 
    751  // MediaEventListener::Disconnect eventually gives up its target
    752  listener.Disconnect();
    753  queue->AwaitIdle();
    754 
    755  // `queue` should be the last reference to the TaskQueue, meaning that this
    756  // Release destroys it.
    757  EXPECT_EQ(queue.forget().take()->Release(), 0u);
    758 }
    759 
    760 TEST(MediaEventSource, TailDispatch)
    761 {
    762  MockFunction<void(const char*)> checkpoint;
    763  {
    764    InSequence seq;
    765    EXPECT_CALL(checkpoint, Call(StrEq("normal runnable")));
    766    EXPECT_CALL(checkpoint, Call(StrEq("source1")));
    767    EXPECT_CALL(checkpoint, Call(StrEq("tail-dispatched runnable")));
    768    EXPECT_CALL(checkpoint, Call(StrEq("source2")));
    769  }
    770 
    771  MediaEventProducer<void> source1;
    772  MediaEventListener listener1 = source1.Connect(
    773      AbstractThread::MainThread(), [&] { checkpoint.Call("source1"); });
    774  MediaEventProducer<void> source2;
    775  MediaEventListener listener2 = source2.Connect(
    776      AbstractThread::MainThread(), [&] { checkpoint.Call("source2"); });
    777 
    778  AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction(__func__, [&] {
    779    // Notify, using tail-dispatch.
    780    source1.Notify();
    781    // Dispatch runnable, using tail-dispatch.
    782    AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction(
    783        __func__, [&] { checkpoint.Call("tail-dispatched runnable"); }));
    784    // Notify other event, using tail-dispatch.
    785    source2.Notify();
    786    // Dispatch runnable to the underlying event target, i.e. without
    787    // tail-dispatch. Doesn't dispatch from a direct task so should run before
    788    // tail-dispatched tasks.
    789    GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
    790        __func__, [&] { checkpoint.Call("normal runnable"); }));
    791  }));
    792 
    793  NS_ProcessPendingEvents(nullptr);
    794 
    795  listener1.Disconnect();
    796  listener2.Disconnect();
    797 }