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 }