blob: 166e8409483f7079e19005313a7ea7250c7e90cc [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 "content/renderer/media/audio_ipc_factory.h"
#include <string>
#include <utility>
#include <vector>
#include "base/message_loop/message_loop.h"
#include "base/threading/thread.h"
#include "content/renderer/media/audio_message_filter.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace content {
namespace {
const int kRenderFrameId = 0;
std::unique_ptr<base::Thread> MakeIOThread() {
auto io_thread = base::MakeUnique<base::Thread>("test IO thread");
base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
CHECK(io_thread->StartWithOptions(thread_options));
return io_thread;
}
class FakeRemoteFactory : public mojom::RendererAudioOutputStreamFactory {
public:
FakeRemoteFactory() : binding_(this) {}
~FakeRemoteFactory() override {}
void RequestDeviceAuthorization(
media::mojom::AudioOutputStreamProviderRequest stream_provider,
int64_t session_id,
const std::string& device_id,
RequestDeviceAuthorizationCallback callback) override {
std::move(callback).Run(
media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
media::AudioParameters::UnavailableDeviceParams(), std::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(binding_.is_bound());
binding_.Bind(
mojo::InterfaceRequest<mojom::RendererAudioOutputStreamFactory>(
std::move(handle)));
}
private:
mojo::Binding<mojom::RendererAudioOutputStreamFactory> binding_;
base::OnceClosure on_called_;
};
class FakeAudioIPCDelegate : 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::SharedMemoryHandle handle,
base::SyncSocket::Handle socket_handle,
int length) override {}
void OnIPCClosed() override {}
};
} // namespace
class AudioIPCFactoryTest : public testing::Test {
public:
AudioIPCFactoryTest() {}
~AudioIPCFactoryTest() override {}
void RequestAuthorizationOnIOThread(
std::unique_ptr<media::AudioOutputIPC> output_ipc) {
output_ipc->RequestDeviceAuthorization(&fake_delegate, 0, "",
url::Origin());
output_ipc->CloseStream();
}
private:
FakeAudioIPCDelegate fake_delegate;
};
TEST_F(AudioIPCFactoryTest, CallFactoryFromIOThread) {
// This test makes sure that AudioIPCFactory correctly binds the
// RendererAudioOutputStreamFactoryPtr to the IO thread.
base::MessageLoop message_loop;
base::RunLoop run_loop;
auto io_thread = MakeIOThread();
FakeRemoteFactory remote_factory;
remote_factory.SetOnCalledCallback(run_loop.QuitWhenIdleClosure());
service_manager::InterfaceProvider interface_provider;
service_manager::InterfaceProvider::TestApi(&interface_provider)
.SetBinderForName(mojom::RendererAudioOutputStreamFactory::Name_,
base::BindRepeating(&FakeRemoteFactory::Bind,
base::Unretained(&remote_factory)));
AudioIPCFactory ipc_factory(nullptr, io_thread->task_runner());
ipc_factory.MaybeRegisterRemoteFactory(kRenderFrameId, &interface_provider);
// 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(&AudioIPCFactoryTest::RequestAuthorizationOnIOThread,
base::Unretained(this),
ipc_factory.CreateAudioOutputIPC(kRenderFrameId)));
// Wait for call to |remote_factory|:
run_loop.Run();
ipc_factory.MaybeDeregisterRemoteFactory(0);
io_thread.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(AudioIPCFactoryTest, SeveralFactories) {
// This test simulates having several frames being created and destructed.
base::MessageLoop message_loop;
auto io_thread = MakeIOThread();
const int n_factories = 5;
std::vector<service_manager::InterfaceProvider> interface_providers(
n_factories);
std::vector<FakeRemoteFactory> remote_factories(n_factories);
for (size_t i = 0; i < n_factories; i++) {
service_manager::InterfaceProvider::TestApi(&interface_providers[i])
.SetBinderForName(
mojom::RendererAudioOutputStreamFactory::Name_,
base::BindRepeating(&FakeRemoteFactory::Bind,
base::Unretained(&remote_factories[i])));
}
base::RunLoop().RunUntilIdle();
AudioIPCFactory ipc_factory(nullptr, io_thread->task_runner());
for (size_t i = 0; i < n_factories; i++) {
ipc_factory.MaybeRegisterRemoteFactory(kRenderFrameId + i,
&interface_providers[i]);
}
base::RunLoop run_loop;
remote_factories[0].SetOnCalledCallback(run_loop.QuitWhenIdleClosure());
io_thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&AudioIPCFactoryTest::RequestAuthorizationOnIOThread,
base::Unretained(this),
ipc_factory.CreateAudioOutputIPC(kRenderFrameId)));
run_loop.Run();
// Do some operation and make sure the internal state isn't messed up:
ipc_factory.MaybeDeregisterRemoteFactory(1);
base::RunLoop run_loop2;
remote_factories[2].SetOnCalledCallback(run_loop2.QuitWhenIdleClosure());
io_thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&AudioIPCFactoryTest::RequestAuthorizationOnIOThread,
base::Unretained(this),
ipc_factory.CreateAudioOutputIPC(kRenderFrameId + 2)));
run_loop2.Run();
for (size_t i = 0; i < n_factories; i++) {
if (i == 1)
continue;
ipc_factory.MaybeDeregisterRemoteFactory(i);
}
io_thread.reset();
base::RunLoop().RunUntilIdle();
}
TEST_F(AudioIPCFactoryTest, RegisterDeregisterBackToBack_Deregisters) {
// This test makes sure that calling Register... followed by Deregister...
// correctly sequences the registration before the deregistration.
base::MessageLoop message_loop;
auto io_thread = MakeIOThread();
FakeRemoteFactory remote_factory;
service_manager::InterfaceProvider interface_provider;
service_manager::InterfaceProvider::TestApi(&interface_provider)
.SetBinderForName(mojom::RendererAudioOutputStreamFactory::Name_,
base::BindRepeating(&FakeRemoteFactory::Bind,
base::Unretained(&remote_factory)));
AudioIPCFactory ipc_factory(nullptr, io_thread->task_runner());
ipc_factory.MaybeRegisterRemoteFactory(kRenderFrameId, &interface_provider);
ipc_factory.MaybeDeregisterRemoteFactory(kRenderFrameId);
// That there is no factory remaining at destruction is DCHECKed in the
// AudioIPCFactory destructor.
base::RunLoop().RunUntilIdle();
io_thread.reset();
base::RunLoop().RunUntilIdle();
}
} // namespace content