tor-browser

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

TestAudioInputSource.cpp (12951B)


      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 "AudioInputSource.h"
      8 #include "MockCubeb.h"
      9 #include "gmock/gmock.h"
     10 #include "gtest/gtest.h"
     11 #include "mozilla/Result.h"
     12 #include "mozilla/gtest/WaitFor.h"
     13 #include "nsContentUtils.h"
     14 
     15 using namespace mozilla;
     16 using testing::ContainerEq;
     17 
     18 // Short-hand for DispatchToCurrentThread with a function.
     19 #define DispatchFunction(f) \
     20  NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
     21 
     22 // Short-hand for draining the current threads event queue, i.e. processing
     23 // those runnables dispatched per above.
     24 #define ProcessEventQueue()                     \
     25  while (NS_ProcessNextEvent(nullptr, false)) { \
     26  }
     27 
     28 class MockEventListener : public AudioInputSource::EventListener {
     29 public:
     30  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockEventListener, override);
     31  MOCK_METHOD1(AudioDeviceChanged, void(AudioInputSource::Id));
     32  MOCK_METHOD2(AudioStateCallback,
     33               void(AudioInputSource::Id,
     34                    AudioInputSource::EventListener::State));
     35 
     36 private:
     37  ~MockEventListener() = default;
     38 };
     39 
     40 TEST(TestAudioInputSource, StartAndStop)
     41 {
     42  MockCubeb* cubeb = new MockCubeb();
     43  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
     44 
     45  const AudioInputSource::Id sourceId = 1;
     46  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
     47  const uint32_t channels = 2;
     48  const PrincipalHandle testPrincipal =
     49      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
     50  const TrackRate sourceRate = 44100;
     51  const TrackRate targetRate = 48000;
     52 
     53  auto listener = MakeRefPtr<MockEventListener>();
     54  EXPECT_CALL(*listener,
     55              AudioStateCallback(
     56                  sourceId, AudioInputSource::EventListener::State::Started))
     57      .Times(2);
     58  EXPECT_CALL(*listener,
     59              AudioStateCallback(
     60                  sourceId, AudioInputSource::EventListener::State::Stopped))
     61      .Times(4);
     62 
     63  RefPtr<AudioInputSource> ais = MakeRefPtr<AudioInputSource>(
     64      std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
     65      sourceRate, targetRate);
     66  ASSERT_TRUE(ais);
     67 
     68  // Make sure start and stop works.
     69  {
     70    DispatchFunction([&] {
     71      ais->Init();
     72      ais->Start();
     73    });
     74    RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
     75    EXPECT_TRUE(stream->mHasInput);
     76    EXPECT_FALSE(stream->mHasOutput);
     77    EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
     78    EXPECT_EQ(stream->InputChannels(), channels);
     79    EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(sourceRate));
     80 
     81    (void)WaitFor(stream->FramesProcessedEvent());
     82 
     83    DispatchFunction([&] { ais->Stop(); });
     84    (void)WaitFor(cubeb->StreamDestroyEvent());
     85  }
     86 
     87  // Make sure restart is ok.
     88  {
     89    DispatchFunction([&] {
     90      ais->Init();
     91      ais->Start();
     92    });
     93    RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
     94    EXPECT_TRUE(stream->mHasInput);
     95    EXPECT_FALSE(stream->mHasOutput);
     96    EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
     97    EXPECT_EQ(stream->InputChannels(), channels);
     98    EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(sourceRate));
     99 
    100    (void)WaitFor(stream->FramesProcessedEvent());
    101 
    102    DispatchFunction([&] { ais->Stop(); });
    103    (void)WaitFor(cubeb->StreamDestroyEvent());
    104  }
    105 
    106  ais = nullptr;  // Drop the SharedThreadPool here.
    107 }
    108 
    109 TEST(TestAudioInputSource, DataOutputBeforeStartAndAfterStop)
    110 {
    111  MockCubeb* cubeb = new MockCubeb();
    112  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    113 
    114  const AudioInputSource::Id sourceId = 1;
    115  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    116  const uint32_t channels = 2;
    117  const PrincipalHandle testPrincipal =
    118      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    119  const TrackRate sourceRate = 44100;
    120  const TrackRate targetRate = 48000;
    121 
    122  const TrackTime requestFrames = 2 * WEBAUDIO_BLOCK_SIZE;
    123 
    124  auto listener = MakeRefPtr<MockEventListener>();
    125  EXPECT_CALL(*listener,
    126              AudioStateCallback(
    127                  sourceId, AudioInputSource::EventListener::State::Started));
    128  EXPECT_CALL(*listener,
    129              AudioStateCallback(
    130                  sourceId, AudioInputSource::EventListener::State::Stopped))
    131      .Times(2);
    132 
    133  RefPtr<AudioInputSource> ais = MakeRefPtr<AudioInputSource>(
    134      std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    135      sourceRate, targetRate);
    136  ASSERT_TRUE(ais);
    137 
    138  // It's ok to call GetAudioSegment before starting
    139  {
    140    AudioSegment data =
    141        ais->GetAudioSegment(requestFrames, AudioInputSource::Consumer::Same);
    142    EXPECT_EQ(data.GetDuration(), requestFrames);
    143    EXPECT_TRUE(data.IsNull());
    144  }
    145 
    146  DispatchFunction([&] {
    147    ais->Init();
    148    ais->Start();
    149  });
    150  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    151  EXPECT_TRUE(stream->mHasInput);
    152  EXPECT_FALSE(stream->mHasOutput);
    153  EXPECT_EQ(stream->InputChannels(), channels);
    154 
    155  stream->SetInputRecordingEnabled(true);
    156 
    157  (void)WaitFor(stream->FramesProcessedEvent());
    158 
    159  DispatchFunction([&] { ais->Stop(); });
    160  (void)WaitFor(cubeb->StreamDestroyEvent());
    161 
    162  // Check the data output
    163  {
    164    nsTArray<AudioDataValue> record = stream->TakeRecordedInput();
    165    size_t frames = record.Length() / channels;
    166    AudioSegment deinterleaved;
    167    deinterleaved.AppendFromInterleavedBuffer(record.Elements(), frames,
    168                                              channels, testPrincipal);
    169    AudioDriftCorrection driftCorrector(sourceRate, targetRate, testPrincipal);
    170    AudioSegment expectedSegment = driftCorrector.RequestFrames(
    171        deinterleaved, static_cast<uint32_t>(requestFrames));
    172 
    173    CopyableTArray<AudioDataValue> expected;
    174    size_t expectedSamples =
    175        expectedSegment.WriteToInterleavedBuffer(expected, channels);
    176 
    177    AudioSegment actualSegment =
    178        ais->GetAudioSegment(requestFrames, AudioInputSource::Consumer::Same);
    179    EXPECT_EQ(actualSegment.GetDuration(), requestFrames);
    180    CopyableTArray<AudioDataValue> actual;
    181    size_t actualSamples =
    182        actualSegment.WriteToInterleavedBuffer(actual, channels);
    183 
    184    EXPECT_EQ(actualSamples, expectedSamples);
    185    EXPECT_EQ(actualSamples / channels, static_cast<size_t>(requestFrames));
    186    EXPECT_THAT(actual, ContainerEq(expected));
    187  }
    188 
    189  ais = nullptr;  // Drop the SharedThreadPool here.
    190 }
    191 
    192 TEST(TestAudioInputSource, ErrorCallback)
    193 {
    194  MockCubeb* cubeb = new MockCubeb();
    195  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    196 
    197  const AudioInputSource::Id sourceId = 1;
    198  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    199  const uint32_t channels = 2;
    200  const PrincipalHandle testPrincipal =
    201      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    202  const TrackRate sourceRate = 44100;
    203  const TrackRate targetRate = 48000;
    204 
    205  auto listener = MakeRefPtr<MockEventListener>();
    206  EXPECT_CALL(*listener,
    207              AudioStateCallback(
    208                  sourceId, AudioInputSource::EventListener::State::Started));
    209  EXPECT_CALL(*listener,
    210              AudioStateCallback(
    211                  sourceId, AudioInputSource::EventListener::State::Error));
    212  EXPECT_CALL(*listener,
    213              AudioStateCallback(
    214                  sourceId, AudioInputSource::EventListener::State::Stopped))
    215      .Times(2);
    216 
    217  RefPtr<AudioInputSource> ais = MakeRefPtr<AudioInputSource>(
    218      std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    219      sourceRate, targetRate);
    220  ASSERT_TRUE(ais);
    221 
    222  DispatchFunction([&] {
    223    ais->Init();
    224    ais->Start();
    225  });
    226  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    227  EXPECT_TRUE(stream->mHasInput);
    228  EXPECT_FALSE(stream->mHasOutput);
    229  EXPECT_EQ(stream->InputChannels(), channels);
    230 
    231  (void)WaitFor(stream->FramesProcessedEvent());
    232 
    233  DispatchFunction([&] { stream->ForceError(); });
    234  WaitFor(stream->ErrorForcedEvent());
    235 
    236  DispatchFunction([&] { ais->Stop(); });
    237  (void)WaitFor(cubeb->StreamDestroyEvent());
    238 
    239  ais = nullptr;  // Drop the SharedThreadPool here.
    240 }
    241 
    242 TEST(TestAudioInputSource, DeviceChangedCallback)
    243 {
    244  MockCubeb* cubeb = new MockCubeb();
    245  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    246 
    247  const AudioInputSource::Id sourceId = 1;
    248  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    249  const uint32_t channels = 2;
    250  const PrincipalHandle testPrincipal =
    251      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    252  const TrackRate sourceRate = 44100;
    253  const TrackRate targetRate = 48000;
    254 
    255  auto listener = MakeRefPtr<MockEventListener>();
    256  EXPECT_CALL(*listener, AudioDeviceChanged(sourceId));
    257  EXPECT_CALL(*listener,
    258              AudioStateCallback(
    259                  sourceId, AudioInputSource::EventListener::State::Started));
    260  EXPECT_CALL(*listener,
    261              AudioStateCallback(
    262                  sourceId, AudioInputSource::EventListener::State::Stopped))
    263      .Times(2);
    264 
    265  RefPtr<AudioInputSource> ais = MakeRefPtr<AudioInputSource>(
    266      std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    267      sourceRate, targetRate);
    268  ASSERT_TRUE(ais);
    269 
    270  DispatchFunction([&] {
    271    ais->Init();
    272    ais->Start();
    273  });
    274  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    275  EXPECT_TRUE(stream->mHasInput);
    276  EXPECT_FALSE(stream->mHasOutput);
    277  EXPECT_EQ(stream->InputChannels(), channels);
    278 
    279  (void)WaitFor(stream->FramesProcessedEvent());
    280 
    281  DispatchFunction([&] { stream->ForceDeviceChanged(); });
    282  WaitFor(stream->DeviceChangeForcedEvent());
    283 
    284  DispatchFunction([&] { ais->Stop(); });
    285  (void)WaitFor(cubeb->StreamDestroyEvent());
    286 
    287  ais = nullptr;  // Drop the SharedThreadPool here.
    288 }
    289 
    290 TEST(TestAudioInputSource, InputProcessing)
    291 {
    292  MockCubeb* cubeb = new MockCubeb();
    293  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    294 
    295  const AudioInputSource::Id sourceId = 1;
    296  const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
    297  const uint32_t channels = 2;
    298  const PrincipalHandle testPrincipal =
    299      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    300  const TrackRate sourceRate = 44100;
    301  const TrackRate targetRate = 48000;
    302  using ProcessingPromise =
    303      AudioInputSource::SetRequestedProcessingParamsPromise;
    304 
    305  auto listener = MakeRefPtr<MockEventListener>();
    306  EXPECT_CALL(*listener,
    307              AudioStateCallback(
    308                  sourceId, AudioInputSource::EventListener::State::Started))
    309      .Times(0);
    310  EXPECT_CALL(*listener,
    311              AudioStateCallback(
    312                  sourceId, AudioInputSource::EventListener::State::Stopped))
    313      .Times(10);
    314 
    315  RefPtr<AudioInputSource> ais = MakeRefPtr<AudioInputSource>(
    316      std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
    317      sourceRate, targetRate);
    318 
    319  const auto test =
    320      [&](cubeb_input_processing_params aRequested,
    321          const Result<cubeb_input_processing_params, int>& aExpected) {
    322        RefPtr<ProcessingPromise> p;
    323        DispatchFunction([&] {
    324          ais->Init();
    325          p = ais->SetRequestedProcessingParams(aRequested);
    326        });
    327        ProcessEventQueue();
    328        EXPECT_EQ(WaitFor(p), aExpected);
    329 
    330        DispatchFunction([&] { ais->Stop(); });
    331        (void)WaitFor(cubeb->StreamDestroyEvent());
    332      };
    333 
    334  // Not supported by backend.
    335  cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
    336                                           CUBEB_ERROR_NOT_SUPPORTED);
    337  test(CUBEB_INPUT_PROCESSING_PARAM_NONE, Err(CUBEB_ERROR_NOT_SUPPORTED));
    338 
    339  // Not supported by params.
    340  cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
    341                                           CUBEB_OK);
    342  test(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    343       CUBEB_INPUT_PROCESSING_PARAM_NONE);
    344 
    345  constexpr cubeb_input_processing_params allParams =
    346      CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
    347      CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
    348      CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
    349      CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
    350 
    351  // Successful all.
    352  cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
    353  test(allParams, allParams);
    354 
    355  // Successful partial.
    356  test(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
    357       CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
    358 
    359  // Not supported by stream.
    360  // Note this also tests that AudioInputSource resets its configured params
    361  // state from the previous successful test.
    362  constexpr int propagatedError = 99;
    363  cubeb->SetInputProcessingApplyRv(propagatedError);
    364  test(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, Err(propagatedError));
    365 
    366  ais = nullptr;  // Drop the SharedThreadPool here.
    367 }