blob: c1b6be671f7e7343618f5c0d6a151939cd3b3bfb [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 "services/audio/public/cpp/audio_system_to_service_adapter.h"
#include "base/message_loop/message_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "media/audio/audio_system_test_util.h"
#include "media/audio/test_audio_thread.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/audio/in_process_audio_manager_accessor.h"
#include "services/audio/public/mojom/constants.mojom.h"
#include "services/audio/system_info.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/mojom/connector.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Exactly;
using testing::MockFunction;
using testing::NiceMock;
namespace audio {
// Base fixture for all the tests below.
class AudioSystemToServiceAdapterTestBase : public testing::Test {
public:
AudioSystemToServiceAdapterTestBase() {}
~AudioSystemToServiceAdapterTestBase() override {}
void SetUp() override {
audio_manager_ = std::make_unique<media::MockAudioManager>(
std::make_unique<media::TestAudioThread>(
false /* we do not use separate thread here */));
system_info_impl_ =
std::make_unique<audio::SystemInfo>(audio_manager_.get());
system_info_binding_ = std::make_unique<mojo::Binding<mojom::SystemInfo>>(
system_info_impl_.get());
service_manager::mojom::ConnectorRequest ignored_request;
auto connector = service_manager::Connector::Create(&ignored_request);
service_manager::Connector::TestApi connector_test_api(connector.get());
connector_test_api.OverrideBinderForTesting(
service_manager::Identity(mojom::kServiceName),
mojom::SystemInfo::Name_,
base::BindRepeating(
&AudioSystemToServiceAdapterTestBase::BindSystemInfoRequest,
base::Unretained(this)));
audio_system_ =
std::make_unique<AudioSystemToServiceAdapter>(std::move(connector));
}
void TearDown() override {
audio_system_.reset();
system_info_binding_->Close();
audio_manager_->Shutdown();
}
protected:
// Required by AudioSystemTestTemplate:
media::MockAudioManager* audio_manager() { return audio_manager_.get(); }
media::AudioSystem* audio_system() { return audio_system_.get(); }
// Called when SystemInfo bind request is received. Nice mock because
// AudioSystem conformance tests won't set expecnations.
NiceMock<MockFunction<void(void)>> system_info_bind_requested_;
base::MessageLoop message_loop_;
std::unique_ptr<media::MockAudioManager> audio_manager_;
std::unique_ptr<mojom::SystemInfo> system_info_impl_;
std::unique_ptr<mojo::Binding<mojom::SystemInfo>> system_info_binding_;
std::unique_ptr<media::AudioSystem> audio_system_;
private:
void BindSystemInfoRequest(mojo::ScopedMessagePipeHandle handle) {
EXPECT_TRUE(system_info_binding_) << "AudioSystemToServiceAdapter should "
"not request AudioSysteInfo during "
"construction";
EXPECT_FALSE(system_info_binding_->is_bound());
system_info_bind_requested_.Call();
system_info_binding_->Bind(mojom::SystemInfoRequest(std::move(handle)));
}
DISALLOW_COPY_AND_ASSIGN(AudioSystemToServiceAdapterTestBase);
};
// Base fixture for connection loss tests.
class AudioSystemToServiceAdapterConnectionLossTest
: public AudioSystemToServiceAdapterTestBase {
public:
AudioSystemToServiceAdapterConnectionLossTest() {}
~AudioSystemToServiceAdapterConnectionLossTest() override {}
void SetUp() override {
AudioSystemToServiceAdapterTestBase::SetUp();
params_ = media::AudioParameters::UnavailableDeviceParams();
audio_manager()->SetInputStreamParameters(params_);
audio_manager()->SetOutputStreamParameters(params_);
audio_manager()->SetDefaultOutputStreamParameters(params_);
auto get_device_descriptions =
[](const media::AudioDeviceDescriptions* source,
media::AudioDeviceDescriptions* destination) {
destination->insert(destination->end(), source->begin(),
source->end());
};
device_descriptions_.emplace_back("device_name", "device_id", "group_id");
audio_manager()->SetInputDeviceDescriptionsCallback(base::BindRepeating(
get_device_descriptions, base::Unretained(&device_descriptions_)));
audio_manager()->SetOutputDeviceDescriptionsCallback(base::BindRepeating(
get_device_descriptions, base::Unretained(&device_descriptions_)));
associated_id_ = "associated_id";
audio_manager()->SetAssociatedOutputDeviceIDCallback(base::BindRepeating(
[](const std::string* result, const std::string&) -> std::string {
return *result;
},
base::Unretained(&associated_id_)));
}
protected:
void GetDeviceDescriptionsTest(bool for_input) {
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Succeeds.
base::RunLoop wait_loop;
// Should bind:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
audio_system_->GetDeviceDescriptions(
for_input,
expectations_.GetDeviceDescriptionsCallback(
FROM_HERE, wait_loop.QuitClosure(), device_descriptions_));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
{ // Fails correctly on connection loss.
base::RunLoop wait_loop;
// Should be already bound:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(0));
audio_system_->GetDeviceDescriptions(
for_input, expectations_.GetDeviceDescriptionsCallback(
FROM_HERE, wait_loop.QuitClosure(),
media::AudioDeviceDescriptions()));
system_info_binding_->Close(); // Connection loss.
base::RunLoop().RunUntilIdle();
wait_loop.Run();
}
EXPECT_FALSE(system_info_binding_->is_bound());
}
void HasDevicesTest(bool for_input) {
auto has_devices = for_input ? &media::AudioSystem::HasInputDevices
: &media::AudioSystem::HasOutputDevices;
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Succeeds.
base::RunLoop wait_loop;
// Should bind:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
(audio_system()->*has_devices)(expectations_.GetBoolCallback(
FROM_HERE, wait_loop.QuitClosure(), true));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
{ // Fails correctly on connection loss.
base::RunLoop wait_loop;
// Should be already bound:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(0));
(audio_system()->*has_devices)(expectations_.GetBoolCallback(
FROM_HERE, wait_loop.QuitClosure(), false));
system_info_binding_->Close(); // Connection loss.
base::RunLoop().RunUntilIdle();
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(system_info_binding_->is_bound());
}
void GetStreamParametersTest(bool for_input) {
auto get_stream_parameters =
for_input ? &media::AudioSystem::GetInputStreamParameters
: &media::AudioSystem::GetOutputStreamParameters;
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Succeeds.
base::RunLoop wait_loop;
// Should bind:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
(audio_system()->*get_stream_parameters)(
media::AudioDeviceDescription::kDefaultDeviceId,
expectations_.GetAudioParamsCallback(
FROM_HERE, wait_loop.QuitClosure(), params_));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
{ // Fails correctly on connection loss.
base::RunLoop wait_loop;
// Should be already bound:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(0));
(audio_system()->*get_stream_parameters)(
media::AudioDeviceDescription::kDefaultDeviceId,
expectations_.GetAudioParamsCallback(
FROM_HERE, wait_loop.QuitClosure(), base::nullopt));
system_info_binding_->Close(); // Connection loss.
base::RunLoop().RunUntilIdle();
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(system_info_binding_->is_bound());
}
std::string associated_id_;
media::AudioParameters params_;
media::AudioDeviceDescriptions device_descriptions_;
media::AudioSystemCallbackExpectations expectations_;
DISALLOW_COPY_AND_ASSIGN(AudioSystemToServiceAdapterConnectionLossTest);
};
// This test covers various scenarios of connection loss/restore, and the
// next tests only verify that we receive a correct callback if the connection
// is lost.
TEST_F(AudioSystemToServiceAdapterConnectionLossTest,
GetAssociatedOutputDeviceIDFullConnectionTest) {
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Succeeds.
base::RunLoop wait_loop;
// Should bind:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
audio_system_->GetAssociatedOutputDeviceID(
std::string(), expectations_.GetDeviceIdCallback(
FROM_HERE, wait_loop.QuitClosure(), associated_id_));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
{ // Succeeds second time.
base::RunLoop wait_loop;
// Should be already bound:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(0));
audio_system_->GetAssociatedOutputDeviceID(
std::string(), expectations_.GetDeviceIdCallback(
FROM_HERE, wait_loop.QuitClosure(), associated_id_));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
{ // Fails correctly on connection loss.
base::RunLoop wait_loop;
// Should be already bound:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(0));
audio_system_->GetAssociatedOutputDeviceID(
std::string(), expectations_.GetDeviceIdCallback(
FROM_HERE, wait_loop.QuitClosure(), base::nullopt));
system_info_binding_->Close(); // Connection loss.
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Fails correctly on connection loss if already unbound.
base::RunLoop wait_loop;
// Should re-bind after connection loss:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
audio_system_->GetAssociatedOutputDeviceID(
std::string(), expectations_.GetDeviceIdCallback(
FROM_HERE, wait_loop.QuitClosure(), base::nullopt));
system_info_binding_->Close(); // Connection loss.
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Finally succeeds again!
base::RunLoop wait_loop;
// Should bind:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
audio_system_->GetAssociatedOutputDeviceID(
std::string(), expectations_.GetDeviceIdCallback(
FROM_HERE, wait_loop.QuitClosure(), associated_id_));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest,
GetInputStreamParameters) {
GetStreamParametersTest(true);
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest,
GetOutputStreamParameters) {
GetStreamParametersTest(false);
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest, HasInputDevices) {
HasDevicesTest(true);
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest, HasOutputDevices) {
HasDevicesTest(false);
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest,
GetInputDeviceDescriptions) {
GetDeviceDescriptionsTest(true);
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest,
GetOutputDeviceDescriptions) {
GetDeviceDescriptionsTest(false);
}
TEST_F(AudioSystemToServiceAdapterConnectionLossTest, GetInputDeviceInfo) {
EXPECT_FALSE(system_info_binding_->is_bound());
{ // Succeeds.
base::RunLoop wait_loop;
// Should bind:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(1));
audio_system_->GetInputDeviceInfo(
"device-id",
expectations_.GetInputDeviceInfoCallback(
FROM_HERE, wait_loop.QuitClosure(), params_, associated_id_));
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(system_info_binding_->is_bound());
{ // Fails correctly on connection loss.
base::RunLoop wait_loop;
// Should be already bound:
EXPECT_CALL(system_info_bind_requested_, Call()).Times(Exactly(0));
audio_system_->GetInputDeviceInfo(
"device-id",
expectations_.GetInputDeviceInfoCallback(
FROM_HERE, wait_loop.QuitClosure(), base::nullopt, base::nullopt));
system_info_binding_->Close(); // Connection loss.
wait_loop.Run();
}
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(system_info_binding_->is_bound());
}
namespace {
static constexpr auto kResponseDelay = base::TimeDelta::FromMilliseconds(10);
static constexpr char kSomeDeviceId[] = "Some device";
static constexpr char kValidReplyId[] =
"If you can read it you received the reply";
} // namespace
class AudioSystemToServiceAdapterDisconnectTest : public testing::Test {
public:
AudioSystemToServiceAdapterDisconnectTest() {}
~AudioSystemToServiceAdapterDisconnectTest() override {}
protected:
class MockSystemInfo : public mojom::SystemInfo {
public:
MockSystemInfo(base::TimeDelta response_delay) {}
~MockSystemInfo() override {}
private:
// audio::mojom::SystemInfo implementation.
void GetInputStreamParameters(
const std::string& device_id,
GetInputStreamParametersCallback callback) override {
NOTIMPLEMENTED();
}
void GetOutputStreamParameters(
const std::string& device_id,
GetOutputStreamParametersCallback callback) override {
NOTIMPLEMENTED();
}
void HasInputDevices(HasInputDevicesCallback callback) override {
NOTIMPLEMENTED();
}
void HasOutputDevices(HasOutputDevicesCallback callback) override {
NOTIMPLEMENTED();
}
void GetInputDeviceDescriptions(
GetInputDeviceDescriptionsCallback callback) override {
NOTIMPLEMENTED();
}
void GetOutputDeviceDescriptions(
GetOutputDeviceDescriptionsCallback callback) override {
NOTIMPLEMENTED();
}
void GetAssociatedOutputDeviceID(
const std::string& input_device_id,
GetAssociatedOutputDeviceIDCallback callback) override {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(std::move(callback), kValidReplyId),
kResponseDelay);
}
void GetInputDeviceInfo(const std::string& input_device_id,
GetInputDeviceInfoCallback callback) override {
NOTIMPLEMENTED();
}
}; // class MockSystemInfo
std::unique_ptr<service_manager::Connector> GetConnector() {
service_manager::mojom::ConnectorRequest ignored_request;
auto connector = service_manager::Connector::Create(&ignored_request);
service_manager::Connector::TestApi connector_test_api(connector.get());
connector_test_api.OverrideBinderForTesting(
service_manager::Identity(mojom::kServiceName),
mojom::SystemInfo::Name_,
base::BindRepeating(
&AudioSystemToServiceAdapterDisconnectTest::BindSystemInfoRequest,
base::Unretained(this)));
return connector;
}
void BindSystemInfoRequest(mojo::ScopedMessagePipeHandle handle) {
ClientConnected();
system_info_binding_.Bind(mojom::SystemInfoRequest(std::move(handle)));
system_info_binding_.set_connection_error_handler(base::BindOnce(
&AudioSystemToServiceAdapterDisconnectTest::OnConnectionError,
base::Unretained(this)));
}
void OnConnectionError() {
system_info_binding_.Close();
ClientDisconnected();
}
MOCK_METHOD0(ClientConnected, void(void));
MOCK_METHOD0(ClientDisconnected, void(void));
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
const base::Optional<std::string> valid_reply_{kValidReplyId};
base::MockCallback<media::AudioSystem::OnDeviceIdCallback> response_received_;
MockSystemInfo mock_system_info_{kResponseDelay};
mojo::Binding<mojom::SystemInfo> system_info_binding_{&mock_system_info_};
};
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
ResponseDelayIsShorterThanDisconnectTimeout) {
const base::TimeDelta kDisconnectTimeout = kResponseDelay * 2;
AudioSystemToServiceAdapter audio_system(GetConnector(), kDisconnectTimeout);
{
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
EXPECT_CALL(*this, ClientDisconnected());
scoped_task_environment_.FastForwardBy(kDisconnectTimeout);
}
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
ResponseDelayIsLongerThanDisconnectTimeout) {
const base::TimeDelta kDisconnectTimeout = kResponseDelay / 2;
AudioSystemToServiceAdapter audio_system(GetConnector(), kDisconnectTimeout);
{
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
EXPECT_CALL(*this, ClientDisconnected());
scoped_task_environment_.FastForwardBy(kDisconnectTimeout);
}
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
DisconnectTimeoutIsResetOnSecondRequest) {
const base::TimeDelta kDisconnectTimeout = kResponseDelay * 1.5;
AudioSystemToServiceAdapter audio_system(GetConnector(), kDisconnectTimeout);
{
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
{
EXPECT_CALL(*this, ClientConnected()).Times(0);
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
EXPECT_CALL(*this, ClientDisconnected());
scoped_task_environment_.FastForwardBy(kDisconnectTimeout);
}
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
DoesNotDisconnectIfNoTimeout) {
AudioSystemToServiceAdapter audio_system(GetConnector(), base::TimeDelta());
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardUntilNoTasksRemain();
}
} // namespace audio
// AudioSystem interface conformance tests.
// AudioSystemTestTemplate is defined in media, so should be its instantiations.
namespace media {
using AudioSystemToServiceAdapterTestVariations =
testing::Types<audio::AudioSystemToServiceAdapterTestBase>;
INSTANTIATE_TYPED_TEST_CASE_P(AudioSystemToServiceAdapter,
AudioSystemTestTemplate,
AudioSystemToServiceAdapterTestVariations);
} // namespace media