summaryrefslogtreecommitdiffstats
path: root/dom/media/gtest/TestCubebInputStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/gtest/TestCubebInputStream.cpp')
-rw-r--r--dom/media/gtest/TestCubebInputStream.cpp188
1 files changed, 188 insertions, 0 deletions
diff --git a/dom/media/gtest/TestCubebInputStream.cpp b/dom/media/gtest/TestCubebInputStream.cpp
new file mode 100644
index 0000000000..0488c2be1a
--- /dev/null
+++ b/dom/media/gtest/TestCubebInputStream.cpp
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "CubebInputStream.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "MockCubeb.h"
+#include "WaitFor.h"
+
+using namespace mozilla;
+
+namespace {
+#define DispatchFunction(f) \
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
+} // namespace
+
+class MockListener : public CubebInputStream::Listener {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockListener, override);
+ MOCK_METHOD2(DataCallback, long(const void* aBuffer, long aFrames));
+ MOCK_METHOD1(StateCallback, void(cubeb_state aState));
+ MOCK_METHOD0(DeviceChangedCallback, void());
+
+ private:
+ ~MockListener() = default;
+};
+
+TEST(TestCubebInputStream, DataCallback)
+{
+ using ::testing::Ne;
+ using ::testing::NotNull;
+
+ MockCubeb* cubeb = new MockCubeb();
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ const CubebUtils::AudioDeviceID deviceId = nullptr;
+ const uint32_t channels = 2;
+
+ uint32_t rate = 0;
+ ASSERT_EQ(cubeb_get_preferred_sample_rate(cubeb->AsCubebContext(), &rate),
+ CUBEB_OK);
+
+ nsTArray<AudioDataValue> data;
+ auto listener = MakeRefPtr<MockListener>();
+ EXPECT_CALL(*listener, DataCallback(NotNull(), Ne(0)))
+ .WillRepeatedly([&](const void* aBuffer, long aFrames) {
+ const AudioDataValue* source =
+ reinterpret_cast<const AudioDataValue*>(aBuffer);
+ size_t sampleCount =
+ static_cast<size_t>(aFrames) * static_cast<size_t>(channels);
+ data.AppendElements(source, sampleCount);
+ return aFrames;
+ });
+
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STARTED));
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STOPPED)).Times(2);
+
+ EXPECT_CALL(*listener, DeviceChangedCallback).Times(0);
+
+ UniquePtr<CubebInputStream> cis;
+ DispatchFunction([&] {
+ cis = CubebInputStream::Create(deviceId, channels, rate, true,
+ listener.get());
+ ASSERT_TRUE(cis);
+ });
+ RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ EXPECT_TRUE(stream->mHasInput);
+
+ stream->SetInputRecordingEnabled(true);
+
+ DispatchFunction([&] { ASSERT_EQ(cis->Start(), CUBEB_OK); });
+ WaitFor(stream->FramesProcessedEvent());
+
+ DispatchFunction([&] { ASSERT_EQ(cis->Stop(), CUBEB_OK); });
+ WaitFor(stream->OutputVerificationEvent());
+
+ nsTArray<AudioDataValue> record = stream->TakeRecordedInput();
+
+ DispatchFunction([&] { cis = nullptr; });
+ WaitFor(cubeb->StreamDestroyEvent());
+
+ ASSERT_EQ(data, record);
+}
+
+TEST(TestCubebInputStream, ErrorCallback)
+{
+ using ::testing::Ne;
+ using ::testing::NotNull;
+ using ::testing::ReturnArg;
+
+ MockCubeb* cubeb = new MockCubeb();
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ const CubebUtils::AudioDeviceID deviceId = nullptr;
+ const uint32_t channels = 2;
+
+ uint32_t rate = 0;
+ ASSERT_EQ(cubeb_get_preferred_sample_rate(cubeb->AsCubebContext(), &rate),
+ CUBEB_OK);
+
+ auto listener = MakeRefPtr<MockListener>();
+ EXPECT_CALL(*listener, DataCallback(NotNull(), Ne(0)))
+ .WillRepeatedly(ReturnArg<1>());
+
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STARTED));
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_ERROR));
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STOPPED));
+
+ EXPECT_CALL(*listener, DeviceChangedCallback).Times(0);
+
+ UniquePtr<CubebInputStream> cis;
+ DispatchFunction([&] {
+ cis = CubebInputStream::Create(deviceId, channels, rate, true,
+ listener.get());
+ ASSERT_TRUE(cis);
+ });
+ RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ EXPECT_TRUE(stream->mHasInput);
+
+ DispatchFunction([&] { ASSERT_EQ(cis->Start(), CUBEB_OK); });
+ WaitFor(stream->FramesProcessedEvent());
+
+ DispatchFunction([&] { stream->ForceError(); });
+ WaitFor(stream->ErrorForcedEvent());
+
+ // If stream ran into an error state, then it should be stopped.
+
+ DispatchFunction([&] { cis = nullptr; });
+ WaitFor(cubeb->StreamDestroyEvent());
+}
+
+TEST(TestCubebInputStream, DeviceChangedCallback)
+{
+ using ::testing::Ne;
+ using ::testing::NotNull;
+ using ::testing::ReturnArg;
+
+ MockCubeb* cubeb = new MockCubeb();
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ const CubebUtils::AudioDeviceID deviceId = nullptr;
+ const uint32_t channels = 2;
+
+ uint32_t rate = 0;
+ ASSERT_EQ(cubeb_get_preferred_sample_rate(cubeb->AsCubebContext(), &rate),
+ CUBEB_OK);
+
+ auto listener = MakeRefPtr<MockListener>();
+ EXPECT_CALL(*listener, DataCallback(NotNull(), Ne(0)))
+ .WillRepeatedly(ReturnArg<1>());
+
+ // In real world, the stream might run into an error state when the
+ // device-changed event is fired (e.g., the last default output device is
+ // unplugged). But it's fine to not check here since we can control how
+ // MockCubeb behaves.
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STARTED));
+ EXPECT_CALL(*listener, StateCallback(CUBEB_STATE_STOPPED)).Times(2);
+
+ EXPECT_CALL(*listener, DeviceChangedCallback);
+
+ UniquePtr<CubebInputStream> cis;
+ DispatchFunction([&] {
+ cis = CubebInputStream::Create(deviceId, channels, rate, true,
+ listener.get());
+ ASSERT_TRUE(cis);
+ });
+ RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ EXPECT_TRUE(stream->mHasInput);
+
+ DispatchFunction([&] { ASSERT_EQ(cis->Start(), CUBEB_OK); });
+ WaitFor(stream->FramesProcessedEvent());
+
+ DispatchFunction([&] { stream->ForceDeviceChanged(); });
+ WaitFor(stream->DeviceChangeForcedEvent());
+
+ // The stream can keep running when its device is changed.
+ DispatchFunction([&] { ASSERT_EQ(cis->Stop(), CUBEB_OK); });
+ cubeb_state state = WaitFor(stream->StateEvent());
+ EXPECT_EQ(state, CUBEB_STATE_STOPPED);
+
+ DispatchFunction([&] { cis = nullptr; });
+ WaitFor(cubeb->StreamDestroyEvent());
+}