blob: c0d48eac0db46d5ed31461908533259305af0b29 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/threading/thread.h"
#include "media/audio/audio_output_ipc.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom-blink.h"
#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
using ::testing::_;
namespace blink {
namespace {
const int kRenderFrameId = 0;
blink::LocalFrameToken TokenFromInt(int i) {
static base::UnguessableToken base_token = base::UnguessableToken::Create();
return blink::LocalFrameToken(base::UnguessableToken::Deserialize(
base_token.GetHighForSerialization() + i,
base_token.GetLowForSerialization() + i));
}
std::unique_ptr<base::Thread> MakeIOThread() {
auto io_thread = std::make_unique<base::Thread>("test IO thread");
base::Thread::Options thread_options(base::MessagePumpType::IO, 0);
CHECK(io_thread->StartWithOptions(thread_options));
return io_thread;
}
class FakeRemoteFactory
: public mojom::blink::RendererAudioOutputStreamFactory {
public:
FakeRemoteFactory() = default;
~FakeRemoteFactory() override {}
void RequestDeviceAuthorization(
mojo::PendingReceiver<media::mojom::blink::AudioOutputStreamProvider>
stream_provider,
const base::Optional<base::UnguessableToken>& session_id,
const String& device_id,
RequestDeviceAuthorizationCallback callback) override {
std::move(callback).Run(
static_cast<media::mojom::blink::OutputDeviceStatus>(
media::OutputDeviceStatus::
OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED),
media::AudioParameters::UnavailableDeviceParams(), WTF::g_empty_string);
EXPECT_FALSE(on_called_.is_null());
std::move(on_called_).Run();
}
void SetOnCalledCallback(base::OnceClosure on_called) {
on_called_ = std::move(on_called);
}
void Bind(mojo::ScopedMessagePipeHandle handle) {
EXPECT_FALSE(receiver_.is_bound());
receiver_.Bind(
mojo::PendingReceiver<mojom::blink::RendererAudioOutputStreamFactory>(
std::move(handle)));
}
private:
mojo::Receiver<mojom::blink::RendererAudioOutputStreamFactory> receiver_{
this};
base::OnceClosure on_called_;
};
class FakeAudioOutputIPCDelegate : public media::AudioOutputIPCDelegate {
void OnError() override {}
void OnDeviceAuthorized(media::OutputDeviceStatus device_status,
const media::AudioParameters& output_params,
const std::string& matched_device_id) override {}
void OnStreamCreated(base::UnsafeSharedMemoryRegion region,
base::SyncSocket::ScopedHandle socket_handle,
bool playing_automatically) override {}
void OnIPCClosed() override {}
};
} // namespace
class WebAudioOutputIPCFactoryTest : public testing::Test {
public:
WebAudioOutputIPCFactoryTest() = default;
~WebAudioOutputIPCFactoryTest() override = default;
void RequestAuthorizationOnIOThread(
std::unique_ptr<media::AudioOutputIPC> output_ipc) {
output_ipc->RequestDeviceAuthorization(&fake_delegate,
base::UnguessableToken(), "");
output_ipc->CloseStream();
}
private:
FakeAudioOutputIPCDelegate fake_delegate;
};
TEST_F(WebAudioOutputIPCFactoryTest, CallFactoryFromIOThread) {
// This test makes sure that WebAudioOutputIPCFactory correctly binds the
// RendererAudioOutputStreamFactory to the IO thread.
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
base::RunLoop run_loop;
auto io_thread = MakeIOThread();
FakeRemoteFactory remote_factory;
remote_factory.SetOnCalledCallback(run_loop.QuitWhenIdleClosure());
auto& interface_broker = blink::GetEmptyBrowserInterfaceBroker();
interface_broker.SetBinderForTesting(
mojom::blink::RendererAudioOutputStreamFactory::Name_,
base::BindRepeating(&FakeRemoteFactory::Bind,
base::Unretained(&remote_factory)));
WebAudioOutputIPCFactory ipc_factory(io_thread->task_runner());
ipc_factory.RegisterRemoteFactory(TokenFromInt(kRenderFrameId),
&interface_broker);
// To make sure that the pointer stored in |ipc_factory| is connected to
// |remote_factory|, and also that it's bound to |io_thread|, we create an
// AudioOutputIPC object and request device authorization on the IO thread.
// This is supposed to call |remote_factory| on the main thread.
io_thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
&WebAudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
base::Unretained(this),
ipc_factory.CreateAudioOutputIPC(TokenFromInt(kRenderFrameId))));
// Wait for call to |remote_factory|:
run_loop.Run();
ipc_factory.MaybeDeregisterRemoteFactory(TokenFromInt(0));
interface_broker.SetBinderForTesting(
mojom::blink::RendererAudioOutputStreamFactory::Name_, {});
io_thread.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(WebAudioOutputIPCFactoryTest, SeveralFactories) {
// This test simulates having several frames being created and destructed.
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
auto io_thread = MakeIOThread();
const int n_factories = 5;
std::vector<FakeRemoteFactory> remote_factories(n_factories);
auto& interface_broker = blink::GetEmptyBrowserInterfaceBroker();
interface_broker.SetBinderForTesting(
mojom::blink::RendererAudioOutputStreamFactory::Name_,
base::BindLambdaForTesting([&](mojo::ScopedMessagePipeHandle handle) {
static int factory_index = 0;
DCHECK_LT(factory_index, n_factories);
remote_factories[factory_index++].Bind(std::move(handle));
}));
base::RunLoop().RunUntilIdle();
WebAudioOutputIPCFactory ipc_factory(io_thread->task_runner());
for (size_t i = 0; i < n_factories; i++) {
ipc_factory.RegisterRemoteFactory(TokenFromInt(kRenderFrameId + i),
&interface_broker);
}
base::RunLoop run_loop;
remote_factories[0].SetOnCalledCallback(run_loop.QuitWhenIdleClosure());
io_thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
&WebAudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
base::Unretained(this),
ipc_factory.CreateAudioOutputIPC(TokenFromInt(kRenderFrameId))));
run_loop.Run();
// Do some operation and make sure the internal state isn't messed up:
ipc_factory.MaybeDeregisterRemoteFactory(TokenFromInt(1));
base::RunLoop run_loop2;
remote_factories[2].SetOnCalledCallback(run_loop2.QuitWhenIdleClosure());
io_thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
&WebAudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
base::Unretained(this),
ipc_factory.CreateAudioOutputIPC(TokenFromInt(kRenderFrameId + 2))));
run_loop2.Run();
for (size_t i = 0; i < n_factories; i++) {
if (i == 1)
continue;
ipc_factory.MaybeDeregisterRemoteFactory(TokenFromInt(i));
}
interface_broker.SetBinderForTesting(
mojom::blink::RendererAudioOutputStreamFactory::Name_, {});
io_thread.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(WebAudioOutputIPCFactoryTest, RegisterDeregisterBackToBack_Deregisters) {
// This test makes sure that calling Register... followed by Deregister...
// correctly sequences the registration before the deregistration.
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
auto io_thread = MakeIOThread();
FakeRemoteFactory remote_factory;
auto& interface_broker = blink::GetEmptyBrowserInterfaceBroker();
interface_broker.SetBinderForTesting(
mojom::blink::RendererAudioOutputStreamFactory::Name_,
base::BindRepeating(&FakeRemoteFactory::Bind,
base::Unretained(&remote_factory)));
WebAudioOutputIPCFactory ipc_factory(io_thread->task_runner());
ipc_factory.RegisterRemoteFactory(TokenFromInt(kRenderFrameId),
&interface_broker);
ipc_factory.MaybeDeregisterRemoteFactory(TokenFromInt(kRenderFrameId));
// That there is no factory remaining at destruction is DCHECKed in the
// WebAudioOutputIPCFactory destructor.
base::RunLoop().RunUntilIdle();
interface_broker.SetBinderForTesting(
mojom::blink::RendererAudioOutputStreamFactory::Name_, {});
io_thread.reset();
base::RunLoop().RunUntilIdle();
}
} // namespace blink