tor-browser

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

TestAudioCallbackDriver.cpp (29328B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CubebUtils.h"
      8 #include "MediaTrackGraphImpl.h"
      9 #include "MockCubeb.h"
     10 #include "MockGraphInterface.h"
     11 #include "gtest/gtest.h"
     12 #include "mozilla/Attributes.h"
     13 #include "mozilla/SyncRunnable.h"
     14 #include "mozilla/gtest/WaitFor.h"
     15 #include "nsTArray.h"
     16 
     17 namespace mozilla {
     18 
     19 using IterationResult = GraphInterface::IterationResult;
     20 using ::testing::_;
     21 using ::testing::AnyNumber;
     22 using ::testing::AtMost;
     23 using ::testing::Eq;
     24 using ::testing::InSequence;
     25 using ::testing::NiceMock;
     26 
     27 NS_IMPL_ISUPPORTS0(MockGraphInterface)
     28 
     29 TEST(TestAudioCallbackDriver, StartStop)
     30 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
     31  const TrackRate rate = 44100;
     32  MockCubeb* cubeb = new MockCubeb();
     33  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
     34 
     35  RefPtr<AudioCallbackDriver> driver;
     36  auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate);
     37  EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
     38 
     39  driver = MakeRefPtr<AudioCallbackDriver>(
     40      graph, nullptr, rate, 2, 0, nullptr, nullptr, AudioInputType::Unknown,
     41      Some<AudioInputProcessingParamsRequest>(
     42          {0, CUBEB_INPUT_PROCESSING_PARAM_NONE}));
     43  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
     44  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
     45 
     46  graph->SetCurrentDriver(driver);
     47  driver->Start();
     48  // Allow some time to "play" audio.
     49  std::this_thread::sleep_for(std::chrono::milliseconds(200));
     50  EXPECT_TRUE(driver->ThreadRunning()) << "Verify thread is running";
     51  EXPECT_TRUE(driver->IsStarted()) << "Verify thread is started";
     52 
     53  // This will block untill all events have been executed.
     54  MOZ_KnownLive(driver)->Shutdown();
     55  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
     56  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
     57 }
     58 
     59 void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
     60  std::cerr << "TestSlowStart with rate " << aRate << std::endl;
     61 
     62  MockCubeb* cubeb = new MockCubeb();
     63  cubeb->SetStreamStartFreezeEnabled(true);
     64  auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
     65  (void)unforcer;
     66  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
     67 
     68  RefPtr<AudioCallbackDriver> driver;
     69  auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(aRate);
     70  EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
     71 
     72  nsIThread* mainThread = NS_GetCurrentThread();
     73  Maybe<int64_t> audioStart;
     74  Maybe<uint32_t> firstAlreadyBuffered;
     75  int64_t inputFrameCount = 0;
     76  int64_t processedFrameCount = -1;
     77  ON_CALL(*graph, NotifyInputData)
     78      .WillByDefault([&](const AudioDataValue*, size_t aFrames, TrackRate,
     79                         uint32_t, uint32_t aAlreadyBuffered) {
     80        if (!audioStart) {
     81          // GraphDrivers advance state computed time only in block size
     82          // increments and AudioCallbackDriver first advances state computed
     83          // time in the same iteration as NotifyInputData() is first called,
     84          // so the frame after firstAlreadyBuffered.value() aligns with a
     85          // block boundary in state computed time.
     86          audioStart = Some(graph->StateComputedTime());
     87          firstAlreadyBuffered = Some(aAlreadyBuffered);
     88          mainThread->Dispatch(NS_NewRunnableFunction(__func__, [&] {
     89            // Start processedFrameCount now, ignoring frames processed while
     90            // waiting for the fallback driver to stop.
     91            processedFrameCount = 0;
     92          }));
     93        }
     94        EXPECT_EQ(
     95            PR_ROUNDUP(
     96                inputFrameCount + aAlreadyBuffered - *firstAlreadyBuffered,
     97                WEBAUDIO_BLOCK_SIZE),
     98            static_cast<int64_t>(graph->StateComputedTime() - *audioStart))
     99            << "Input should be behind state time, due to the delayed start. "
    100            << "inputFrameCount=" << inputFrameCount
    101            << ", firstAlreadyBuffered=" << *firstAlreadyBuffered
    102            << ", aAlreadyBuffered=" << aAlreadyBuffered
    103            << ", stateComputedTime=" << graph->StateComputedTime()
    104            << ", audioStartTime=" << *audioStart;
    105        inputFrameCount += aFrames;
    106      });
    107 
    108  driver = MakeRefPtr<AudioCallbackDriver>(
    109      graph, nullptr, aRate, 2, 2, nullptr, (void*)1, AudioInputType::Voice,
    110      Some<AudioInputProcessingParamsRequest>(
    111          {0, CUBEB_INPUT_PROCESSING_PARAM_NONE}));
    112  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    113  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    114 
    115  graph->SetCurrentDriver(driver);
    116  graph->SetEnsureNextIteration(true);
    117 
    118  auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
    119  driver->Start();
    120  auto [stream] = WaitFor(initPromise).unwrap()[0];
    121  cubeb->SetStreamStartFreezeEnabled(false);
    122 
    123  const size_t fallbackIterations = 3;
    124  WaitUntil(graph->FramesIteratedEvent(), [&](uint32_t aFrames) {
    125    const GraphTime tenMillis = aRate / 100;
    126    // An iteration is always rounded upwards to the next full block.
    127    const GraphTime tenMillisIteration =
    128        MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(tenMillis);
    129    // The iteration may be smaller because up to an extra block may have been
    130    // processed and buffered.
    131    const GraphTime tenMillisMinIteration =
    132        tenMillisIteration - WEBAUDIO_BLOCK_SIZE;
    133    // An iteration must be at least one audio block.
    134    const GraphTime minIteration =
    135        std::max<GraphTime>(WEBAUDIO_BLOCK_SIZE, tenMillisMinIteration);
    136    EXPECT_GE(aFrames, minIteration)
    137        << "Fallback driver iteration >= 10ms, modulo an audio block";
    138    EXPECT_LT(aFrames, static_cast<size_t>(aRate))
    139        << "Fallback driver iteration <1s (sanity)";
    140    return graph->IterationCount() >= fallbackIterations;
    141  });
    142 
    143  MediaEventListener processedListener =
    144      stream->FramesProcessedEvent().Connect(mainThread, [&](uint32_t aFrames) {
    145        if (processedFrameCount >= 0) {
    146          processedFrameCount += aFrames;
    147        }
    148      });
    149  stream->Thaw();
    150 
    151  SpinEventLoopUntil(
    152      "processed at least 100ms of audio data from stream callback"_ns,
    153      [&] { return processedFrameCount >= aRate / 10; });
    154 
    155  // This will block until all events have been queued.
    156  MOZ_KnownLive(driver)->Shutdown();
    157  // Process processListener events.
    158  NS_ProcessPendingEvents(mainThread);
    159  processedListener.Disconnect();
    160 
    161  EXPECT_EQ(inputFrameCount, processedFrameCount);
    162  EXPECT_EQ(
    163      graph->StateComputedTime() - *audioStart,
    164      PR_ROUNDUP(inputFrameCount - *firstAlreadyBuffered, WEBAUDIO_BLOCK_SIZE))
    165      << "Graph progresses while audio driver runs. "
    166      << "stateComputedTime=" << graph->StateComputedTime()
    167      << ", inputFrameCount=" << inputFrameCount
    168      << ", firstAlreadyBuffered=" << *firstAlreadyBuffered;
    169 }
    170 
    171 TEST(TestAudioCallbackDriver, SlowStart)
    172 {
    173  TestSlowStart(1000);   // 10ms = 10 <<< 128 samples
    174  TestSlowStart(8000);   // 10ms = 80  <  128 samples
    175  TestSlowStart(44100);  // 10ms = 441 >  128 samples
    176 }
    177 
    178 #ifdef DEBUG
    179 template <typename T>
    180 class MOZ_STACK_CLASS AutoSetter {
    181  std::atomic<T>& mVal;
    182  T mNew;
    183  T mOld;
    184 
    185 public:
    186  explicit AutoSetter(std::atomic<T>& aVal, T aNew)
    187      : mVal(aVal), mNew(aNew), mOld(mVal.exchange(aNew)) {}
    188  ~AutoSetter() {
    189    DebugOnly<T> oldNew = mVal.exchange(mOld);
    190    MOZ_RELEASE_ASSERT(oldNew == mNew);
    191  }
    192 };
    193 #endif
    194 
    195 TEST(TestAudioCallbackDriver, SlowDeviceChange)
    196 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    197  constexpr TrackRate rate = 48000;
    198  MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
    199  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    200 
    201  int generation = 99;
    202  auto graph = MakeRefPtr<MockGraphInterface>(rate);
    203  auto driver = MakeRefPtr<AudioCallbackDriver>(
    204      graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice,
    205      Some<AudioInputProcessingParamsRequest>(
    206          {generation, CUBEB_INPUT_PROCESSING_PARAM_NONE}));
    207  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    208  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    209 
    210 #ifdef DEBUG
    211  std::atomic<std::thread::id> threadInDriverIteration{std::thread::id()};
    212  EXPECT_CALL(*graph, InDriverIteration(driver.get())).WillRepeatedly([&] {
    213    return std::this_thread::get_id() == threadInDriverIteration;
    214  });
    215 #endif
    216  constexpr size_t ignoredFrameCount = 1337;
    217  EXPECT_CALL(*graph, NotifyInputData(_, 0, rate, 1, _)).Times(AnyNumber());
    218  EXPECT_CALL(*graph, NotifyInputData(_, ignoredFrameCount, _, _, _)).Times(0);
    219  EXPECT_CALL(*graph, DeviceChanged);
    220  Result<cubeb_input_processing_params, int> expected =
    221      Err(CUBEB_ERROR_NOT_SUPPORTED);
    222  EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    223                          driver.get(), generation, Eq(std::ref(expected))));
    224 
    225  graph->SetCurrentDriver(driver);
    226  graph->SetEnsureNextIteration(true);
    227  // This starts the fallback driver.
    228  auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
    229  driver->Start();
    230  auto [stream] = WaitFor(initPromise).unwrap()[0];
    231 
    232  // Wait for the audio driver to have started the stream before running data
    233  // callbacks. driver->Start() does a dispatch to the cubeb operation thread
    234  // and starts the stream there.
    235  nsCOMPtr<nsIEventTarget> cubebOpThread =
    236      CubebUtils::GetCubebOperationThread();
    237  MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
    238      cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
    239 
    240  // This makes the fallback driver stop on its next callback.
    241  EXPECT_EQ(stream->ManualDataCallback(0),
    242            MockCubebStream::KeepProcessing::Yes);
    243  {
    244 #ifdef DEBUG
    245    AutoSetter as(threadInDriverIteration, std::this_thread::get_id());
    246 #endif
    247    while (driver->OnFallback()) {
    248      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    249    }
    250  }
    251 
    252  const TimeStamp wallClockStart = TimeStamp::Now();
    253  const GraphTime graphClockStart = graph->StateComputedTime();
    254  const size_t iterationCountStart = graph->IterationCount();
    255 
    256  // Flag that the stream should force a devicechange event.
    257  stream->NotifyDeviceChangedNow();
    258 
    259  // The audio driver should now have switched on the fallback driver again.
    260  {
    261 #ifdef DEBUG
    262    AutoSetter as(threadInDriverIteration, std::this_thread::get_id());
    263 #endif
    264    EXPECT_TRUE(driver->OnFallback());
    265  }
    266 
    267  // Make sure that the audio driver can handle (and ignore) data callbacks for
    268  // a little while after the devicechange callback. Cubeb does not provide
    269  // ordering guarantees here.
    270  auto start = TimeStamp::Now();
    271  while (start + TimeDuration::FromMilliseconds(5) > TimeStamp::Now()) {
    272    EXPECT_EQ(stream->ManualDataCallback(ignoredFrameCount),
    273              MockCubebStream::KeepProcessing::Yes);
    274    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    275  }
    276 
    277  // Let the fallback driver start and spin for one second.
    278  std::this_thread::sleep_for(std::chrono::seconds(1));
    279 
    280  // Tell the fallback driver to hand over to the audio driver which has
    281  // finished changing devices.
    282  EXPECT_EQ(stream->ManualDataCallback(0),
    283            MockCubebStream::KeepProcessing::Yes);
    284 
    285  // Wait for the fallback to stop.
    286  {
    287 #ifdef DEBUG
    288    AutoSetter as(threadInDriverIteration, std::this_thread::get_id());
    289 #endif
    290    while (driver->OnFallback()) {
    291      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    292    }
    293  }
    294 
    295  TimeStamp wallClockEnd = TimeStamp::Now();
    296  GraphTime graphClockEnd = graph->StateComputedTime();
    297  size_t iterationCountEnd = graph->IterationCount();
    298 
    299  auto wallClockDuration =
    300      media::TimeUnit::FromTimeDuration(wallClockEnd - wallClockStart);
    301  auto graphClockDuration =
    302      media::TimeUnit(CheckedInt64(graphClockEnd) - graphClockStart, rate);
    303 
    304  // Check that the time while we switched devices was accounted for by the
    305  // fallback driver.
    306  EXPECT_NEAR(
    307      wallClockDuration.ToSeconds(), graphClockDuration.ToSeconds(),
    308 #ifdef XP_MACOSX
    309      // SystemClockDriver on macOS in CI is underrunning, i.e. the driver
    310      // thread when waiting for the next iteration waits too long. Therefore
    311      // the graph clock is unable to keep up with wall clock.
    312      wallClockDuration.ToSeconds() * 0.8
    313 #else
    314      0.1
    315 #endif
    316  );
    317  // Check that each fallback driver was of reasonable cadence. It's a thread
    318  // that tries to run a task every 10ms. Check that the average callback
    319  // interval i falls in 8ms ≤ i ≤ 40ms.
    320  auto fallbackCadence =
    321      graphClockDuration /
    322      static_cast<int64_t>(iterationCountEnd - iterationCountStart);
    323  EXPECT_LE(8, fallbackCadence.ToMilliseconds());
    324  EXPECT_LE(fallbackCadence.ToMilliseconds(), 40.0);
    325 
    326  // This will block until all events have been queued.
    327  MOZ_KnownLive(driver)->Shutdown();
    328  // Drain the event queue.
    329  NS_ProcessPendingEvents(nullptr);
    330 }
    331 
    332 TEST(TestAudioCallbackDriver, DeviceChangeAfterStop)
    333 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    334  constexpr TrackRate rate = 48000;
    335  MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
    336  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    337 
    338  auto graph = MakeRefPtr<MockGraphInterface>(rate);
    339  auto driver = MakeRefPtr<AudioCallbackDriver>(
    340      graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice,
    341      Some<AudioInputProcessingParamsRequest>(
    342          {99, CUBEB_INPUT_PROCESSING_PARAM_NONE}));
    343  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    344  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    345 
    346  auto newDriver = MakeRefPtr<AudioCallbackDriver>(
    347      graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice,
    348      Some<AudioInputProcessingParamsRequest>(
    349          {99, CUBEB_INPUT_PROCESSING_PARAM_NONE}));
    350  EXPECT_FALSE(newDriver->ThreadRunning()) << "Verify thread is not running";
    351  EXPECT_FALSE(newDriver->IsStarted()) << "Verify thread is not started";
    352 
    353 #ifdef DEBUG
    354  std::atomic<std::thread::id> threadInDriverIteration{
    355      std::this_thread::get_id()};
    356  EXPECT_CALL(*graph, InDriverIteration(_)).WillRepeatedly([&] {
    357    return std::this_thread::get_id() == threadInDriverIteration;
    358  });
    359 #endif
    360  EXPECT_CALL(*graph, NotifyInputData(_, 0, rate, 1, _)).Times(AnyNumber());
    361  // This only happens if the first fallback driver is stopped by the audio
    362  // driver handover rather than the driver switch. It happens when the
    363  // subsequent audio callback performs the switch.
    364  EXPECT_CALL(*graph, NotifyInputStopped()).Times(AtMost(1));
    365  Result<cubeb_input_processing_params, int> expected =
    366      Err(CUBEB_ERROR_NOT_SUPPORTED);
    367  EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    368                          driver.get(), 99, Eq(std::ref(expected))));
    369  EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    370                          newDriver.get(), 99, Eq(std::ref(expected))));
    371 
    372  graph->SetCurrentDriver(driver);
    373  graph->SetEnsureNextIteration(true);
    374  auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
    375  // This starts the fallback driver.
    376  driver->Start();
    377  RefPtr<SmartMockCubebStream> stream;
    378  std::tie(stream) = WaitFor(initPromise).unwrap()[0];
    379 
    380  // Wait for the audio driver to have started or the DeviceChanged event will
    381  // be ignored. driver->Start() does a dispatch to the cubeb operation thread
    382  // and starts the stream there.
    383  nsCOMPtr<nsIEventTarget> cubebOpThread =
    384      CubebUtils::GetCubebOperationThread();
    385  MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
    386      cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
    387 
    388  initPromise = TakeN(cubeb->StreamInitEvent(), 1);
    389  Monitor mon(__func__);
    390  bool canContinueToStartNextDriver = false;
    391  bool continued = false;
    392 
    393  // This marks the audio driver as running.
    394  EXPECT_EQ(stream->ManualDataCallback(0),
    395            MockCubebStream::KeepProcessing::Yes);
    396 
    397  // To satisfy TSAN's lock-order-inversion checking we avoid locking stream's
    398  // mMutex (by calling ManualDataCallback) under mon. The SwitchTo runnable
    399  // below already locks mon under stream's mMutex.
    400  MonitorAutoLock lock(mon);
    401 
    402  // If a fallback driver callback happens between the audio callback
    403  // above, and the SwitchTo below, the driver will enter
    404  // `FallbackDriverState::None`, relying on the audio driver to
    405  // iterate the graph, including performing the driver switch. This
    406  // test may therefore intermittently take different code paths.
    407  // Note however that the fallback driver runs every ~10ms while the
    408  // time from the manual callback above to telling the mock graph to
    409  // switch drivers below is much much shorter. The vast majority of
    410  // test runs will exercise the intended code path.
    411 
    412  // Make the fallback driver enter FallbackDriverState::Stopped by
    413  // switching audio driver in the graph.
    414  graph->SwitchTo(newDriver, NS_NewRunnableFunction(__func__, [&] {
    415                    MonitorAutoLock lock(mon);
    416                    // Block the fallback driver on its thread until
    417                    // the test on main thread has finished testing
    418                    // what it needs.
    419                    while (!canContinueToStartNextDriver) {
    420                      lock.Wait();
    421                    }
    422                    // Notify the test that it can take these
    423                    // variables off the stack now.
    424                    continued = true;
    425                    lock.Notify();
    426                  }));
    427 
    428  // Wait for the fallback driver to stop running.
    429  while (driver->OnFallback()) {
    430    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    431  }
    432 
    433  if (driver->HasFallback()) {
    434    // Driver entered FallbackDriverState::Stopped as desired.
    435    // Proceed with a DeviceChangedCallback.
    436 
    437    EXPECT_CALL(*graph, DeviceChanged);
    438 
    439    {
    440 #ifdef DEBUG
    441      AutoSetter as(threadInDriverIteration, std::thread::id());
    442 #endif
    443      // After stopping the fallback driver, but before newDriver has
    444      // stopped the old audio driver, fire a DeviceChanged event to
    445      // ensure it is handled properly.
    446      AudioCallbackDriver::DeviceChangedCallback_s(driver);
    447    }
    448 
    449    EXPECT_FALSE(driver->OnFallback())
    450        << "DeviceChangedCallback after stopping must not start the "
    451           "fallback driver again";
    452  }
    453 
    454  // Iterate the audio driver on a background thread in case the fallback
    455  // driver completed the handover to the audio driver before the switch
    456  // above. Doing the switch would deadlock as the switch runnable waits on
    457  // mon.
    458  NS_DispatchBackgroundTask(NS_NewRunnableFunction(
    459      "DeviceChangeAfterStop::postSwitchManualAudioCallback", [stream] {
    460        // An audio callback after switching must tell the stream to stop.
    461        EXPECT_EQ(stream->ManualDataCallback(0),
    462                  MockCubebStream::KeepProcessing::No);
    463      }));
    464 
    465  // Unblock the fallback driver.
    466  canContinueToStartNextDriver = true;
    467  lock.Notify();
    468 
    469  // Wait for the fallback driver to continue, so we can clear the
    470  // stack.
    471  while (!continued) {
    472    lock.Wait();
    473  }
    474 
    475  // Wait for newDriver's cubeb stream to init.
    476  std::tie(stream) = WaitFor(initPromise).unwrap()[0];
    477 
    478  graph->StopIterating();
    479  newDriver->EnsureNextIteration();
    480  while (newDriver->OnFallback()) {
    481    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    482  }
    483 
    484  {
    485 #ifdef DEBUG
    486    AutoSetter as(threadInDriverIteration, std::thread::id());
    487 #endif
    488    EXPECT_EQ(stream->ManualDataCallback(0),
    489              MockCubebStream::KeepProcessing::No);
    490  }
    491 
    492  // Drain the event queue.
    493  NS_ProcessPendingEvents(nullptr);
    494 }
    495 
    496 void TestInputProcessingOnStart(
    497    MockCubeb* aCubeb, int aGeneration,
    498    cubeb_input_processing_params aRequested,
    499    const Result<cubeb_input_processing_params, int>& aExpected)
    500    MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    501  const TrackRate rate = 44100;
    502 
    503  auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate);
    504  auto driver = MakeRefPtr<AudioCallbackDriver>(
    505      graph, nullptr, rate, 2, 1, nullptr, nullptr, AudioInputType::Voice,
    506      Some<AudioInputProcessingParamsRequest>({aGeneration, aRequested}));
    507  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    508  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    509 
    510 #ifdef DEBUG
    511  std::atomic_bool inGraphIteration{false};
    512  ON_CALL(*graph, InDriverIteration(_)).WillByDefault([&] {
    513    return inGraphIteration.load() && NS_IsMainThread();
    514  });
    515 #endif
    516  bool notified = false;
    517  EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
    518  EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    519                          driver.get(), aGeneration, Eq(std::ref(aExpected))))
    520      .WillOnce([&] { notified = true; });
    521 
    522  graph->SetCurrentDriver(driver);
    523  auto initPromise = TakeN(aCubeb->StreamInitEvent(), 1);
    524  driver->Start();
    525  auto [stream] = WaitFor(initPromise).unwrap()[0];
    526 
    527  // Wait for the audio driver to have started the stream before running data
    528  // callbacks. driver->Start() does a dispatch to the cubeb operation thread
    529  // and starts the stream there.
    530  nsCOMPtr<nsIEventTarget> cubebOpThread =
    531      CubebUtils::GetCubebOperationThread();
    532  MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
    533      cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
    534 
    535  // This makes the fallback driver stop on its next callback.
    536  {
    537 #ifdef DEBUG
    538    AutoSetter as(inGraphIteration, true);
    539 #endif
    540    while (driver->OnFallback()) {
    541      stream->ManualDataCallback(0);
    542      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    543    }
    544  }
    545 
    546  while (!notified) {
    547    NS_ProcessNextEvent();
    548  }
    549 
    550  // This will block untill all events have been executed.
    551  MOZ_KnownLive(driver)->Shutdown();
    552  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    553  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    554 }
    555 
    556 TEST(TestAudioCallbackDriver, InputProcessingOnStart)
    557 {
    558  constexpr cubeb_input_processing_params allParams =
    559      CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
    560      CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
    561      CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
    562      CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
    563 
    564  MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
    565  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    566 
    567  // Not supported by backend.
    568  cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
    569                                           CUBEB_ERROR_NOT_SUPPORTED);
    570  TestInputProcessingOnStart(cubeb, 1,
    571                             CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    572                             Err(CUBEB_ERROR_NOT_SUPPORTED));
    573 
    574  // Not supported by params.
    575  cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
    576                                           CUBEB_OK);
    577  TestInputProcessingOnStart(cubeb, 2,
    578                             CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    579                             CUBEB_INPUT_PROCESSING_PARAM_NONE);
    580 
    581  // Successful all.
    582  cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
    583  TestInputProcessingOnStart(cubeb, 3, allParams, allParams);
    584 
    585  // Successful partial.
    586  TestInputProcessingOnStart(cubeb, 4,
    587                             CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    588                             CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
    589 
    590  // Not supported by stream.
    591  cubeb->SetInputProcessingApplyRv(CUBEB_ERROR);
    592  TestInputProcessingOnStart(cubeb, 5,
    593                             CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    594                             Err(CUBEB_ERROR));
    595 }
    596 
    597 TEST(TestAudioCallbackDriver, InputProcessingWhileRunning)
    598 MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    599  constexpr TrackRate rate = 44100;
    600  constexpr cubeb_input_processing_params allParams =
    601      CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
    602      CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
    603      CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
    604      CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
    605  constexpr int applyError = 99;
    606 
    607  int currentGeneration = 0;
    608  const auto signal = [&](auto aDriver, auto aGeneration,
    609                          auto&& aResult) mutable {
    610    MOZ_ASSERT(NS_IsMainThread());
    611    currentGeneration = aGeneration;
    612  };
    613  const auto waitForSignal = [&](int aGeneration) {
    614    while (currentGeneration != aGeneration) {
    615      NS_ProcessNextEvent();
    616    }
    617  };
    618  MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
    619  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    620 
    621  auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate);
    622  auto driver = MakeRefPtr<AudioCallbackDriver>(
    623      graph, nullptr, rate, 2, 1, nullptr, nullptr, AudioInputType::Voice,
    624      Some<AudioInputProcessingParamsRequest>(
    625          {100, CUBEB_INPUT_PROCESSING_PARAM_NONE}));
    626  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    627  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    628 
    629  EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
    630  // Expectations
    631  const Result<cubeb_input_processing_params, int> noneResult =
    632      CUBEB_INPUT_PROCESSING_PARAM_NONE;
    633  const Result<cubeb_input_processing_params, int> aecResult =
    634      CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION;
    635  const Result<cubeb_input_processing_params, int> allResult = allParams;
    636  const Result<cubeb_input_processing_params, int> notSupportedResult =
    637      Err(CUBEB_ERROR_NOT_SUPPORTED);
    638  const Result<cubeb_input_processing_params, int> applyErrorResult =
    639      Err(applyError);
    640  {
    641    InSequence s;
    642 
    643    // Notified on start.
    644    EXPECT_CALL(*graph,
    645                NotifySetRequestedInputProcessingParamsResult(
    646                    driver.get(), 100, Eq(std::ref(notSupportedResult))))
    647        .WillOnce(signal);
    648    // Not supported by backend.
    649    EXPECT_CALL(*graph,
    650                NotifySetRequestedInputProcessingParamsResult(
    651                    driver.get(), 101, Eq(std::ref(notSupportedResult))))
    652        .WillOnce(signal);
    653    // Not supported by params.
    654    EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    655                            driver.get(), 102, Eq(std::ref(noneResult))))
    656        .WillOnce(signal);
    657    // Successful all.
    658    EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    659                            driver.get(), 103, Eq(std::ref(allResult))))
    660        .WillOnce(signal);
    661    // Successful partial.
    662    EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    663                            driver.get(), 104, Eq(std::ref(aecResult))))
    664        .WillOnce(signal);
    665    // Not supported by stream.
    666    EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
    667                            driver.get(), 105, Eq(std::ref(applyErrorResult))))
    668        .WillOnce(signal);
    669  }
    670 
    671 #ifdef DEBUG
    672  std::atomic_bool inGraphIteration{false};
    673  ON_CALL(*graph, InDriverIteration(_)).WillByDefault([&] {
    674    return inGraphIteration.load() && NS_IsMainThread();
    675  });
    676 #endif
    677 
    678  const auto setParams = [&](int aGen, cubeb_input_processing_params aParams) {
    679    {
    680 #ifdef DEBUG
    681      AutoSetter as(inGraphIteration, true);
    682 #endif
    683      driver->RequestInputProcessingParams({aGen, aParams});
    684    }
    685  };
    686 
    687  graph->SetCurrentDriver(driver);
    688  auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
    689  driver->Start();
    690  auto [stream] = WaitFor(initPromise).unwrap()[0];
    691 
    692  // Wait for the audio driver to have started the stream before running data
    693  // callbacks. driver->Start() does a dispatch to the cubeb operation thread
    694  // and starts the stream there.
    695  nsCOMPtr<nsIEventTarget> cubebOpThread =
    696      CubebUtils::GetCubebOperationThread();
    697  MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
    698      cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
    699 
    700  // This makes the fallback driver stop on its next callback.
    701 
    702  {
    703 #ifdef DEBUG
    704    AutoSetter as(inGraphIteration, true);
    705 #endif
    706    while (driver->OnFallback()) {
    707      stream->ManualDataCallback(0);
    708      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    709    }
    710  }
    711  waitForSignal(100);
    712 
    713  // Not supported by backend.
    714  cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
    715                                           CUBEB_ERROR_NOT_SUPPORTED);
    716  setParams(101, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
    717  waitForSignal(101);
    718 
    719  // Not supported by params.
    720  cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
    721                                           CUBEB_OK);
    722  setParams(102, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
    723  waitForSignal(102);
    724 
    725  // Successful all.
    726  cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
    727  setParams(103, allParams);
    728  waitForSignal(103);
    729 
    730  // Successful partial.
    731  setParams(104, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
    732  waitForSignal(104);
    733 
    734  // Not supported by stream.
    735  cubeb->SetInputProcessingApplyRv(applyError);
    736  setParams(105, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
    737  waitForSignal(105);
    738 
    739  // This will block untill all events have been executed.
    740  MOZ_KnownLive(driver)->Shutdown();
    741  EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
    742  EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
    743 }
    744 
    745 }  // namespace mozilla