tor-browser

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

TestDeviceInputTrack.cpp (20326B)


      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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "AudioGenerator.h"
      8 #include "DeviceInputTrack.h"
      9 #include "MediaTrackGraphImpl.h"
     10 #include "MockCubeb.h"
     11 #include "gmock/gmock.h"
     12 #include "gtest/gtest.h"
     13 #include "mozilla/StaticPrefs_media.h"
     14 #include "mozilla/gtest/WaitFor.h"
     15 #include "nsContentUtils.h"
     16 
     17 using namespace mozilla;
     18 using testing::NiceMock;
     19 using testing::Return;
     20 
     21 namespace {
     22 #define DispatchFunction(f) \
     23  NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
     24 }  // namespace
     25 
     26 class MockGraphImpl : public MediaTrackGraphImpl {
     27 public:
     28  explicit MockGraphImpl(TrackRate aRate)
     29      : MediaTrackGraphImpl(0, aRate, nullptr, NS_GetCurrentThread()) {
     30    ON_CALL(*this, OnGraphThread).WillByDefault(Return(true));
     31  }
     32 
     33  void Init(uint32_t aChannels) {
     34    MediaTrackGraphImpl::Init(OFFLINE_THREAD_DRIVER, DIRECT_DRIVER, aChannels);
     35    // We have to call `Destroy()` manually in order to break the reference.
     36    // The reason we don't assign a null driver is because we would add a track
     37    // to the graph, then it would trigger graph's `EnsureNextIteration()` that
     38    // requires a non-null driver.
     39    SetCurrentDriver(new NiceMock<MockDriver>());
     40  }
     41 
     42  MOCK_CONST_METHOD0(OnGraphThread, bool());
     43  MOCK_METHOD1(AppendMessage, void(UniquePtr<ControlMessageInterface>));
     44 
     45 protected:
     46  ~MockGraphImpl() = default;
     47 
     48  class MockDriver : public GraphDriver {
     49    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockDriver, override);
     50 
     51    MockDriver() : GraphDriver(nullptr, nullptr, 0) {
     52      ON_CALL(*this, OnThread).WillByDefault(Return(true));
     53      ON_CALL(*this, ThreadRunning).WillByDefault(Return(true));
     54    }
     55 
     56    MOCK_METHOD0(Start, void());
     57    MOCK_METHOD0(Shutdown, void());
     58    MOCK_METHOD0(IterationDuration, TimeDuration());
     59    MOCK_METHOD0(EnsureNextIteration, void());
     60    MOCK_CONST_METHOD0(OnThread, bool());
     61    MOCK_CONST_METHOD0(ThreadRunning, bool());
     62 
     63   protected:
     64    ~MockDriver() = default;
     65  };
     66 };
     67 
     68 class TestDeviceInputTrack : public testing::Test {
     69 protected:
     70  TestDeviceInputTrack() : mChannels(2), mRate(44100) {}
     71 
     72  void SetUp() override {
     73    mGraph = MakeRefPtr<NiceMock<MockGraphImpl>>(mRate);
     74    mGraph->Init(mChannels);
     75  }
     76 
     77  void TearDown() override { mGraph->Destroy(); }
     78 
     79  const uint32_t mChannels;
     80  const TrackRate mRate;
     81  RefPtr<MockGraphImpl> mGraph;
     82 };
     83 
     84 TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
     85  class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
     86   public:
     87    static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
     88      MOZ_RELEASE_ASSERT(NS_IsMainThread());
     89      TestDeviceInputConsumerTrack* track =
     90          new TestDeviceInputConsumerTrack(aGraph->GraphRate());
     91      aGraph->AddTrack(track);
     92      return track;
     93    }
     94 
     95    void Destroy() {
     96      MOZ_RELEASE_ASSERT(NS_IsMainThread());
     97      DisconnectDeviceInput();
     98      DeviceInputConsumerTrack::Destroy();
     99    }
    100 
    101    void ProcessInput(GraphTime aFrom, GraphTime aTo,
    102                      uint32_t aFlags) override { /* Ignored */ };
    103 
    104    uint32_t NumberOfChannels() const override {
    105      if (mInputs.IsEmpty()) {
    106        return 0;
    107      }
    108      DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
    109      MOZ_RELEASE_ASSERT(t);
    110      return t->NumberOfChannels();
    111    }
    112 
    113   private:
    114    explicit TestDeviceInputConsumerTrack(TrackRate aSampleRate)
    115        : DeviceInputConsumerTrack(aSampleRate) {}
    116  };
    117 
    118  class TestAudioDataListener : public AudioDataListener {
    119   public:
    120    TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
    121        : mChannelCount(aChannelCount), mIsVoice(aIsVoice) {}
    122    // Graph thread APIs: AudioDataListenerInterface implementations.
    123    uint32_t RequestedInputChannelCount(
    124        MediaTrackGraph* aGraph) const override {
    125      aGraph->AssertOnGraphThread();
    126      return mChannelCount;
    127    }
    128    cubeb_input_processing_params RequestedInputProcessingParams(
    129        MediaTrackGraph*) const override {
    130      return CUBEB_INPUT_PROCESSING_PARAM_NONE;
    131    }
    132    bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
    133      return mIsVoice;
    134    };
    135    void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */ }
    136    void Disconnect(MediaTrackGraph* aGraph) override { /* Ignored */ };
    137    void NotifySetRequestedInputProcessingParams(
    138        MediaTrackGraph* aGraph, int aGeneration,
    139        cubeb_input_processing_params aRequestedParams) override {
    140      /* Ignored */
    141    }
    142    void NotifySetRequestedInputProcessingParamsResult(
    143        MediaTrackGraph* aGraph, int aGeneration,
    144        const Result<cubeb_input_processing_params, int>& aResult) override {
    145      /* Ignored */
    146    }
    147 
    148   private:
    149    ~TestAudioDataListener() = default;
    150 
    151    // Graph thread-only.
    152    uint32_t mChannelCount;
    153    // Any thread.
    154    const bool mIsVoice;
    155  };
    156 
    157  const PrincipalHandle testPrincipal =
    158      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    159 
    160  const CubebUtils::AudioDeviceID device1 = (void*)1;
    161  RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
    162  RefPtr<TestDeviceInputConsumerTrack> track1 =
    163      TestDeviceInputConsumerTrack::Create(mGraph);
    164  track1->ConnectDeviceInput(device1, listener1.get(), testPrincipal);
    165  EXPECT_TRUE(track1->ConnectedToNativeDevice());
    166  EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
    167 
    168  const CubebUtils::AudioDeviceID device2 = (void*)2;
    169  RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false);
    170  RefPtr<TestDeviceInputConsumerTrack> track2 =
    171      TestDeviceInputConsumerTrack::Create(mGraph);
    172  track2->ConnectDeviceInput(device2, listener2.get(), testPrincipal);
    173  EXPECT_FALSE(track2->ConnectedToNativeDevice());
    174  EXPECT_TRUE(track2->ConnectedToNonNativeDevice());
    175 
    176  track2->Destroy();
    177  mGraph->RemoveTrackGraphThread(track2);
    178 
    179  track1->Destroy();
    180  mGraph->RemoveTrackGraphThread(track1);
    181 }
    182 
    183 TEST_F(TestDeviceInputTrack, NativeInputTrackData) {
    184  const uint32_t flags = 0;
    185  const CubebUtils::AudioDeviceID deviceId = (void*)1;
    186 
    187  AudioGenerator<AudioDataValue> generator(mChannels, mRate);
    188  const size_t nrFrames = 10;
    189  const size_t bufferSize = nrFrames * mChannels;
    190  nsTArray<AudioDataValue> buffer(bufferSize);
    191  buffer.AppendElements(bufferSize);
    192 
    193  const PrincipalHandle testPrincipal =
    194      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    195 
    196  // Setup: Create a NativeInputTrack and add it to mGraph
    197  RefPtr<NativeInputTrack> track =
    198      new NativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
    199  mGraph->AddTrack(track);
    200 
    201  // Main test below:
    202 
    203  generator.GenerateInterleaved(buffer.Elements(), nrFrames);
    204  track->NotifyInputData(mGraph.get(), buffer.Elements(), nrFrames, mRate,
    205                         mChannels, 0);
    206 
    207  track->ProcessInput(0, WEBAUDIO_BLOCK_SIZE + nrFrames, flags);
    208  EXPECT_EQ(static_cast<size_t>(track->GetEnd()),
    209            static_cast<size_t>(WEBAUDIO_BLOCK_SIZE) + nrFrames);
    210 
    211  // Check pre-buffering: null data with PRINCIPAL_HANDLE_NONE principal
    212  AudioSegment preBuffering;
    213  preBuffering.AppendSlice(*track->GetData(), 0, WEBAUDIO_BLOCK_SIZE);
    214  EXPECT_TRUE(preBuffering.IsNull());
    215  for (AudioSegment::ConstChunkIterator iter(preBuffering); !iter.IsEnded();
    216       iter.Next()) {
    217    const AudioChunk& chunk = *iter;
    218    EXPECT_EQ(chunk.mPrincipalHandle, PRINCIPAL_HANDLE_NONE);
    219  }
    220 
    221  // Check rest of the data
    222  AudioSegment data;
    223  data.AppendSlice(*track->GetData(), WEBAUDIO_BLOCK_SIZE,
    224                   WEBAUDIO_BLOCK_SIZE + nrFrames);
    225  nsTArray<AudioDataValue> interleaved;
    226  size_t sampleCount = data.WriteToInterleavedBuffer(interleaved, mChannels);
    227  EXPECT_EQ(sampleCount, bufferSize);
    228  EXPECT_EQ(interleaved, buffer);
    229 
    230  // Check principal in data
    231  for (AudioSegment::ConstChunkIterator iter(data); !iter.IsEnded();
    232       iter.Next()) {
    233    const AudioChunk& chunk = *iter;
    234    EXPECT_EQ(chunk.mPrincipalHandle, testPrincipal);
    235  }
    236 
    237  // Tear down: Destroy the NativeInputTrack and remove it from mGraph.
    238  track->Destroy();
    239  mGraph->RemoveTrackGraphThread(track);
    240 }
    241 
    242 class MockEventListener : public AudioInputSource::EventListener {
    243 public:
    244  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockEventListener, override);
    245  MOCK_METHOD1(AudioDeviceChanged, void(AudioInputSource::Id));
    246  MOCK_METHOD2(AudioStateCallback,
    247               void(AudioInputSource::Id,
    248                    AudioInputSource::EventListener::State));
    249 
    250 private:
    251  ~MockEventListener() = default;
    252 };
    253 
    254 TEST_F(TestDeviceInputTrack, StartAndStop) {
    255  MockCubeb* cubeb = new MockCubeb();
    256  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    257 
    258  // Non native input settings
    259  const AudioInputSource::Id sourceId = 1;
    260  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    261  const uint32_t channels = 2;
    262  const PrincipalHandle testPrincipal =
    263      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    264  const TrackRate rate = 48000;
    265 
    266  // Setup: Create a NonNativeInputTrack and add it to mGraph.
    267  RefPtr<NonNativeInputTrack> track =
    268      new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
    269  mGraph->AddTrack(track);
    270 
    271  // Main test below:
    272 
    273  // Make sure the NonNativeInputTrack can start and stop its audio correctly.
    274  {
    275    auto listener = MakeRefPtr<MockEventListener>();
    276    EXPECT_CALL(*listener,
    277                AudioStateCallback(
    278                    sourceId, AudioInputSource::EventListener::State::Started));
    279    EXPECT_CALL(*listener,
    280                AudioStateCallback(
    281                    sourceId, AudioInputSource::EventListener::State::Stopped))
    282        .Times(2);
    283 
    284    // No input channels and device preference before start.
    285    EXPECT_EQ(track->NumberOfChannels(), 0U);
    286    EXPECT_EQ(track->DevicePreference(), AudioInputType::Unknown);
    287 
    288    DispatchFunction([&] {
    289      track->StartAudio(MakeRefPtr<AudioInputSource>(
    290          std::move(listener), sourceId, deviceId, channels, true /* voice */,
    291          testPrincipal, rate, mGraph->GraphRate()));
    292    });
    293 
    294    // Wait for stream creation.
    295    RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    296 
    297    // Make sure the audio stream and the track's settings are correct.
    298    EXPECT_TRUE(stream->mHasInput);
    299    EXPECT_FALSE(stream->mHasOutput);
    300    EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
    301    EXPECT_EQ(stream->InputChannels(), channels);
    302    EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
    303    EXPECT_EQ(track->NumberOfChannels(), channels);
    304    EXPECT_EQ(track->DevicePreference(), AudioInputType::Voice);
    305 
    306    // Wait for stream callbacks.
    307    (void)WaitFor(stream->FramesProcessedEvent());
    308 
    309    DispatchFunction([&] { track->StopAudio(); });
    310 
    311    // Wait for stream destroy.
    312    (void)WaitFor(cubeb->StreamDestroyEvent());
    313 
    314    // No input channels and device preference after stop.
    315    EXPECT_EQ(track->NumberOfChannels(), 0U);
    316    EXPECT_EQ(track->DevicePreference(), AudioInputType::Unknown);
    317  }
    318 
    319  // Make sure the NonNativeInputTrack can restart its audio correctly.
    320  {
    321    auto listener = MakeRefPtr<MockEventListener>();
    322    EXPECT_CALL(*listener,
    323                AudioStateCallback(
    324                    sourceId, AudioInputSource::EventListener::State::Started));
    325    EXPECT_CALL(*listener,
    326                AudioStateCallback(
    327                    sourceId, AudioInputSource::EventListener::State::Stopped))
    328        .Times(2);
    329 
    330    DispatchFunction([&] {
    331      track->StartAudio(MakeRefPtr<AudioInputSource>(
    332          std::move(listener), sourceId, deviceId, channels, true,
    333          testPrincipal, rate, mGraph->GraphRate()));
    334    });
    335 
    336    // Wait for stream creation.
    337    RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    338    EXPECT_TRUE(stream->mHasInput);
    339    EXPECT_FALSE(stream->mHasOutput);
    340    EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
    341    EXPECT_EQ(stream->InputChannels(), channels);
    342    EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
    343 
    344    // Wait for stream callbacks.
    345    (void)WaitFor(stream->FramesProcessedEvent());
    346 
    347    DispatchFunction([&] { track->StopAudio(); });
    348 
    349    // Wait for stream destroy.
    350    (void)WaitFor(cubeb->StreamDestroyEvent());
    351  }
    352 
    353  // Tear down: Destroy the NativeInputTrack and remove it from mGraph.
    354  track->Destroy();
    355  mGraph->RemoveTrackGraphThread(track);
    356 }
    357 
    358 TEST_F(TestDeviceInputTrack, NonNativeInputTrackData) {
    359  MockCubeb* cubeb = new MockCubeb();
    360  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    361 
    362  // Graph settings
    363  const uint32_t flags = 0;
    364  const GraphTime frames = 440;
    365 
    366  // Non native input settings
    367  const AudioInputSource::Id sourceId = 1;
    368  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    369  const uint32_t channels = 2;
    370  const PrincipalHandle testPrincipal =
    371      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    372  const TrackRate rate = 48000;
    373 
    374  // Setup: Create a NonNativeInputTrack and add it to mGraph.
    375  RefPtr<NonNativeInputTrack> track =
    376      new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
    377  mGraph->AddTrack(track);
    378 
    379  // Main test below:
    380 
    381  // Make sure we get null data if the track is not started yet.
    382  GraphTime current = 0;
    383  GraphTime next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(frames);
    384  ASSERT_NE(current, next);  // Make sure we have data produced in ProcessInput.
    385 
    386  track->ProcessInput(current, next, flags);
    387  {
    388    AudioSegment data;
    389    data.AppendSegment(track->GetData<AudioSegment>());
    390    EXPECT_TRUE(data.IsNull());
    391  }
    392 
    393  // Make sure we get the AudioInputSource's data once we start the track.
    394 
    395  current = next;
    396  next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(2 * frames);
    397  ASSERT_NE(current, next);  // Make sure we have data produced in ProcessInput.
    398 
    399  auto listener = MakeRefPtr<MockEventListener>();
    400  EXPECT_CALL(*listener,
    401              AudioStateCallback(
    402                  sourceId, AudioInputSource::EventListener::State::Started));
    403  EXPECT_CALL(*listener,
    404              AudioStateCallback(
    405                  sourceId, AudioInputSource::EventListener::State::Stopped))
    406      .Times(2);
    407 
    408  DispatchFunction([&] {
    409    track->StartAudio(MakeRefPtr<AudioInputSource>(
    410        std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    411        rate, mGraph->GraphRate()));
    412  });
    413  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    414  EXPECT_TRUE(stream->mHasInput);
    415  EXPECT_FALSE(stream->mHasOutput);
    416  EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
    417  EXPECT_EQ(stream->InputChannels(), channels);
    418  EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
    419 
    420  // Check audio data.
    421  (void)WaitFor(stream->FramesProcessedEvent());
    422  track->ProcessInput(current, next, flags);
    423  {
    424    AudioSegment data;
    425    data.AppendSlice(*track->GetData<AudioSegment>(), current, next);
    426    EXPECT_FALSE(data.IsNull());
    427    for (AudioSegment::ConstChunkIterator iter(data); !iter.IsEnded();
    428         iter.Next()) {
    429      EXPECT_EQ(iter->mChannelData.Length(), channels);
    430      EXPECT_EQ(iter->mPrincipalHandle, testPrincipal);
    431    }
    432  }
    433 
    434  // Stop the track and make sure it produces null data again.
    435  current = next;
    436  next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(3 * frames);
    437  ASSERT_NE(current, next);  // Make sure we have data produced in ProcessInput.
    438 
    439  DispatchFunction([&] { track->StopAudio(); });
    440  (void)WaitFor(cubeb->StreamDestroyEvent());
    441 
    442  track->ProcessInput(current, next, flags);
    443  {
    444    AudioSegment data;
    445    data.AppendSlice(*track->GetData<AudioSegment>(), current, next);
    446    EXPECT_TRUE(data.IsNull());
    447  }
    448 
    449  // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
    450  track->Destroy();
    451  mGraph->RemoveTrackGraphThread(track);
    452 }
    453 
    454 TEST_F(TestDeviceInputTrack, NonNativeDeviceChangedCallback) {
    455  MockCubeb* cubeb = new MockCubeb();
    456  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    457 
    458  // Non native input settings
    459  const AudioInputSource::Id sourceId = 1;
    460  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    461  const uint32_t channels = 2;
    462  const PrincipalHandle testPrincipal =
    463      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    464  const TrackRate rate = 48000;
    465 
    466  // Setup: Create a NonNativeInputTrack and add it to mGraph.
    467  RefPtr<NonNativeInputTrack> track =
    468      new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
    469  mGraph->AddTrack(track);
    470 
    471  // Main test below:
    472 
    473  auto listener = MakeRefPtr<MockEventListener>();
    474  EXPECT_CALL(*listener, AudioDeviceChanged(sourceId));
    475  EXPECT_CALL(*listener,
    476              AudioStateCallback(
    477                  sourceId, AudioInputSource::EventListener::State::Started));
    478  EXPECT_CALL(*listener,
    479              AudioStateCallback(
    480                  sourceId, AudioInputSource::EventListener::State::Stopped))
    481      .Times(2);
    482 
    483  // Launch and start an audio stream.
    484  DispatchFunction([&] {
    485    track->StartAudio(MakeRefPtr<AudioInputSource>(
    486        std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    487        rate, mGraph->GraphRate()));
    488  });
    489  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    490  EXPECT_TRUE(stream->mHasInput);
    491  EXPECT_FALSE(stream->mHasOutput);
    492  EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
    493  EXPECT_EQ(stream->InputChannels(), channels);
    494  EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
    495 
    496  // Make sure the stream is running.
    497  (void)WaitFor(stream->FramesProcessedEvent());
    498 
    499  // Fire a device-changed callback.
    500  DispatchFunction([&] { stream->ForceDeviceChanged(); });
    501  WaitFor(stream->DeviceChangeForcedEvent());
    502 
    503  // Stop and destroy the stream.
    504  DispatchFunction([&] { track->StopAudio(); });
    505  (void)WaitFor(cubeb->StreamDestroyEvent());
    506 
    507  // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
    508  track->Destroy();
    509  mGraph->RemoveTrackGraphThread(track);
    510 }
    511 
    512 TEST_F(TestDeviceInputTrack, NonNativeErrorCallback) {
    513  MockCubeb* cubeb = new MockCubeb();
    514  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    515 
    516  // Non native input settings
    517  const AudioInputSource::Id sourceId = 1;
    518  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    519  const uint32_t channels = 2;
    520  const PrincipalHandle testPrincipal =
    521      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    522  const TrackRate rate = 48000;
    523 
    524  // Setup: Create a NonNativeInputTrack and add it to mGraph.
    525  RefPtr<NonNativeInputTrack> track =
    526      new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
    527  mGraph->AddTrack(track);
    528 
    529  // Main test below:
    530 
    531  auto listener = MakeRefPtr<MockEventListener>();
    532  EXPECT_CALL(*listener,
    533              AudioStateCallback(
    534                  sourceId, AudioInputSource::EventListener::State::Started));
    535  EXPECT_CALL(*listener,
    536              AudioStateCallback(
    537                  sourceId, AudioInputSource::EventListener::State::Error));
    538  EXPECT_CALL(*listener,
    539              AudioStateCallback(
    540                  sourceId, AudioInputSource::EventListener::State::Stopped))
    541      .Times(2);
    542 
    543  // Launch and start an audio stream.
    544  DispatchFunction([&] {
    545    track->StartAudio(MakeRefPtr<AudioInputSource>(
    546        std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    547        rate, mGraph->GraphRate()));
    548  });
    549  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    550  EXPECT_TRUE(stream->mHasInput);
    551  EXPECT_FALSE(stream->mHasOutput);
    552  EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
    553  EXPECT_EQ(stream->InputChannels(), channels);
    554  EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
    555 
    556  // Make sure the stream is running.
    557  (void)WaitFor(stream->FramesProcessedEvent());
    558 
    559  // Force an error in the MockCubeb.
    560  DispatchFunction([&] { stream->ForceError(); });
    561  WaitFor(stream->ErrorForcedEvent());
    562 
    563  // Stop and destroy the stream.
    564  DispatchFunction([&] { track->StopAudio(); });
    565  (void)WaitFor(cubeb->StreamDestroyEvent());
    566 
    567  // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
    568  track->Destroy();
    569  mGraph->RemoveTrackGraphThread(track);
    570 }