diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/media/gtest/TestCubebInputStream.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/gtest/TestCubebInputStream.cpp')
-rw-r--r-- | dom/media/gtest/TestCubebInputStream.cpp | 188 |
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()); +} |