tor-browser

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

TestCubebInputStream.cpp (6146B)


      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 "CubebInputStream.h"
      8 #include "MockCubeb.h"
      9 #include "gmock/gmock.h"
     10 #include "gtest/gtest.h"
     11 #include "mozilla/gtest/WaitFor.h"
     12 
     13 using namespace mozilla;
     14 
     15 namespace {
     16 #define DispatchFunction(f) \
     17  NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
     18 }  // namespace
     19 
     20 class MockListener : public CubebInputStream::Listener {
     21 public:
     22  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockListener, override);
     23  MOCK_METHOD2(DataCallback, long(const void* aBuffer, long aFrames));
     24  MOCK_METHOD1(StateCallback, void(cubeb_state aState));
     25  MOCK_METHOD0(DeviceChangedCallback, void());
     26 
     27 private:
     28  ~MockListener() = default;
     29 };
     30 
     31 TEST(TestCubebInputStream, DataCallback)
     32 {
     33  using ::testing::Ne;
     34  using ::testing::NotNull;
     35 
     36  MockCubeb* cubeb = new MockCubeb();
     37  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
     38 
     39  const CubebUtils::AudioDeviceID deviceId = nullptr;
     40  const uint32_t channels = 2;
     41 
     42  uint32_t rate = 0;
     43  ASSERT_EQ(cubeb_get_preferred_sample_rate(cubeb->AsCubebContext(), &rate),
     44            CUBEB_OK);
     45 
     46  nsTArray<AudioDataValue> data;
     47  auto listener = MakeRefPtr<MockListener>();
     48  EXPECT_CALL(*listener, DataCallback(NotNull(), Ne(0)))
     49      .WillRepeatedly([&](const void* aBuffer, long aFrames) {
     50        const AudioDataValue* source =
     51            reinterpret_cast<const AudioDataValue*>(aBuffer);
     52        size_t sampleCount =
     53            static_cast<size_t>(aFrames) * static_cast<size_t>(channels);
     54        data.AppendElements(source, sampleCount);
     55        return aFrames;
     56      });
     57 
     58  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STARTED));
     59  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STOPPED)).Times(2);
     60 
     61  EXPECT_CALL(*listener, DeviceChangedCallback).Times(0);
     62 
     63  UniquePtr<CubebInputStream> cis;
     64  DispatchFunction([&] {
     65    cis = CubebInputStream::Create(deviceId, channels, rate, true,
     66                                   listener.get());
     67    ASSERT_TRUE(cis);
     68  });
     69  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
     70  EXPECT_TRUE(stream->mHasInput);
     71 
     72  stream->SetInputRecordingEnabled(true);
     73 
     74  DispatchFunction([&] { ASSERT_EQ(cis->Start(), CUBEB_OK); });
     75  WaitFor(stream->FramesProcessedEvent());
     76 
     77  DispatchFunction([&] { ASSERT_EQ(cis->Stop(), CUBEB_OK); });
     78  WaitFor(stream->OutputVerificationEvent());
     79 
     80  nsTArray<AudioDataValue> record = stream->TakeRecordedInput();
     81 
     82  DispatchFunction([&] { cis = nullptr; });
     83  WaitFor(cubeb->StreamDestroyEvent());
     84 
     85  ASSERT_EQ(data, record);
     86 }
     87 
     88 TEST(TestCubebInputStream, ErrorCallback)
     89 {
     90  using ::testing::Ne;
     91  using ::testing::NotNull;
     92  using ::testing::ReturnArg;
     93 
     94  MockCubeb* cubeb = new MockCubeb();
     95  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
     96 
     97  const CubebUtils::AudioDeviceID deviceId = nullptr;
     98  const uint32_t channels = 2;
     99 
    100  uint32_t rate = 0;
    101  ASSERT_EQ(cubeb_get_preferred_sample_rate(cubeb->AsCubebContext(), &rate),
    102            CUBEB_OK);
    103 
    104  auto listener = MakeRefPtr<MockListener>();
    105  EXPECT_CALL(*listener, DataCallback(NotNull(), Ne(0)))
    106      .WillRepeatedly(ReturnArg<1>());
    107 
    108  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STARTED));
    109  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_ERROR));
    110  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STOPPED));
    111 
    112  EXPECT_CALL(*listener, DeviceChangedCallback).Times(0);
    113 
    114  UniquePtr<CubebInputStream> cis;
    115  DispatchFunction([&] {
    116    cis = CubebInputStream::Create(deviceId, channels, rate, true,
    117                                   listener.get());
    118    ASSERT_TRUE(cis);
    119  });
    120  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    121  EXPECT_TRUE(stream->mHasInput);
    122 
    123  DispatchFunction([&] { ASSERT_EQ(cis->Start(), CUBEB_OK); });
    124  WaitFor(stream->FramesProcessedEvent());
    125 
    126  DispatchFunction([&] { stream->ForceError(); });
    127  WaitFor(stream->ErrorForcedEvent());
    128 
    129  // If stream ran into an error state, then it should be stopped.
    130 
    131  DispatchFunction([&] { cis = nullptr; });
    132  WaitFor(cubeb->StreamDestroyEvent());
    133 }
    134 
    135 TEST(TestCubebInputStream, DeviceChangedCallback)
    136 {
    137  using ::testing::Ne;
    138  using ::testing::NotNull;
    139  using ::testing::ReturnArg;
    140 
    141  MockCubeb* cubeb = new MockCubeb();
    142  CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
    143 
    144  const CubebUtils::AudioDeviceID deviceId = nullptr;
    145  const uint32_t channels = 2;
    146 
    147  uint32_t rate = 0;
    148  ASSERT_EQ(cubeb_get_preferred_sample_rate(cubeb->AsCubebContext(), &rate),
    149            CUBEB_OK);
    150 
    151  auto listener = MakeRefPtr<MockListener>();
    152  EXPECT_CALL(*listener, DataCallback(NotNull(), Ne(0)))
    153      .WillRepeatedly(ReturnArg<1>());
    154 
    155  // In real world, the stream might run into an error state when the
    156  // device-changed event is fired (e.g., the last default output device is
    157  // unplugged). But it's fine to not check here since we can control how
    158  // MockCubeb behaves.
    159  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STARTED));
    160  EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STOPPED)).Times(2);
    161 
    162  EXPECT_CALL(*listener, DeviceChangedCallback);
    163 
    164  UniquePtr<CubebInputStream> cis;
    165  DispatchFunction([&] {
    166    cis = CubebInputStream::Create(deviceId, channels, rate, true,
    167                                   listener.get());
    168    ASSERT_TRUE(cis);
    169  });
    170  RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
    171  EXPECT_TRUE(stream->mHasInput);
    172 
    173  DispatchFunction([&] { ASSERT_EQ(cis->Start(), CUBEB_OK); });
    174  WaitFor(stream->FramesProcessedEvent());
    175 
    176  DispatchFunction([&] { stream->ForceDeviceChanged(); });
    177  WaitFor(stream->DeviceChangeForcedEvent());
    178 
    179  // The stream can keep running when its device is changed.
    180  DispatchFunction([&] { ASSERT_EQ(cis->Stop(), CUBEB_OK); });
    181  cubeb_state state = WaitFor(stream->StateEvent());
    182  EXPECT_EQ(state, CUBEB_STATE_STOPPED);
    183 
    184  DispatchFunction([&] { cis = nullptr; });
    185  WaitFor(cubeb->StreamDestroyEvent());
    186 }