blob: e4986907b766a2047a847ad525ebf61c8e217178 [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 "base/bind.h"
#include "base/test/scoped_task_environment.h"
#include "media/audio/audio_system_test_util.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "services/audio/in_process_audio_manager_accessor.h"
#include "services/audio/public/cpp/audio_system_to_service_adapter.h"
#include "services/audio/public/cpp/fake_system_info.h"
#include "services/audio/public/cpp/manifest.h"
#include "services/audio/public/mojom/constants.mojom.h"
#include "services/audio/service.h"
#include "services/audio/test/service_lifetime_test_template.h"
#include "services/service_manager/public/cpp/manifest_builder.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/cpp/service_binding.h"
#include "services/service_manager/public/cpp/test/test_service_manager.h"
#include "services/service_manager/public/mojom/constants.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Exactly;
using testing::Invoke;
namespace audio {
class ServiceTestHelper : public service_manager::Service {
public:
class AudioThreadContext
: public base::RefCountedThreadSafe<AudioThreadContext> {
public:
AudioThreadContext(media::AudioManager* audio_manager,
base::TimeDelta service_quit_timeout)
: audio_manager_(audio_manager),
service_quit_timeout_(service_quit_timeout) {}
void CreateServiceOnAudioThread(
service_manager::mojom::ServiceRequest request) {
if (!audio_manager_->GetTaskRunner()->BelongsToCurrentThread()) {
audio_manager_->GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&AudioThreadContext::CreateServiceOnAudioThread,
this, std::move(request)));
return;
}
DCHECK(!service_);
service_ = std::make_unique<audio::Service>(
std::make_unique<InProcessAudioManagerAccessor>(audio_manager_),
service_quit_timeout_, false /* device_notifications_enabled */,
std::make_unique<service_manager::BinderMap>(), std::move(request));
service_->set_termination_closure(base::BindOnce(
&AudioThreadContext::QuitOnAudioThread, base::Unretained(this)));
}
void QuitOnAudioThread() {
DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
service_.reset();
}
private:
friend class base::RefCountedThreadSafe<AudioThreadContext>;
virtual ~AudioThreadContext() = default;
media::AudioManager* const audio_manager_;
const base::TimeDelta service_quit_timeout_;
std::unique_ptr<Service> service_;
DISALLOW_COPY_AND_ASSIGN(AudioThreadContext);
};
ServiceTestHelper(media::AudioManager* audio_manager,
base::TimeDelta service_quit_timeout,
service_manager::mojom::ServiceRequest request)
: service_binding_(this, std::move(request)),
audio_manager_(audio_manager),
audio_thread_context_(
new AudioThreadContext(audio_manager, service_quit_timeout)) {}
~ServiceTestHelper() override {
// Ensure that the AudioThreadContext is destroyed on the correct thread by
// passing our only reference into a task posted there.
audio_manager_->GetTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&AudioThreadContext::QuitOnAudioThread,
std::move(audio_thread_context_)));
}
service_manager::Connector* connector() {
return service_binding_.GetConnector();
}
protected:
// service_manager::Service:
void CreatePackagedServiceInstance(
const std::string& service_name,
mojo::PendingReceiver<service_manager::mojom::Service> receiver,
CreatePackagedServiceInstanceCallback callback) override {
if (service_name == mojom::kServiceName) {
audio_thread_context_->CreateServiceOnAudioThread(std::move(receiver));
std::move(callback).Run(base::GetCurrentProcId());
} else {
std::move(callback).Run(base::nullopt);
}
}
private:
service_manager::ServiceBinding service_binding_;
media::AudioManager* const audio_manager_;
scoped_refptr<AudioThreadContext> audio_thread_context_;
DISALLOW_COPY_AND_ASSIGN(ServiceTestHelper);
};
const char kTestServiceName[] = "audio_unittests";
// if |use_audio_thread| is true, AudioManager has a dedicated audio thread and
// Audio service lives on it; otherwise audio thread is the main thread of the
// test fixture, and that's where Service lives. So in the former case the
// service is accessed from another thread, and in the latter case - from the
// thread it lives on (which imitates access to Audio service from UI thread on
// Mac).
template <bool use_audio_thread>
class InProcessServiceTest : public testing::Test {
public:
explicit InProcessServiceTest(base::TimeDelta service_quit_timeout)
: test_service_manager_(
{service_manager::ManifestBuilder()
.WithServiceName(kTestServiceName)
.RequireCapability(mojom::kServiceName, "info")
.RequireCapability(service_manager::mojom::kServiceName,
"service_manager:service_manager")
.PackageService(
GetManifest(service_manager::Manifest::ExecutionMode ::
kInProcessBuiltin))
.Build()}),
audio_manager_(
std::make_unique<media::TestAudioThread>(use_audio_thread)),
helper_(std::make_unique<ServiceTestHelper>(
&audio_manager_,
service_quit_timeout,
test_service_manager_.RegisterTestInstance(kTestServiceName))),
audio_system_(std::make_unique<AudioSystemToServiceAdapter>(
connector()->Clone())) {}
InProcessServiceTest()
: InProcessServiceTest(base::TimeDelta() /* not timeout */) {}
~InProcessServiceTest() override {}
protected:
service_manager::Connector* connector() {
DCHECK(helper_);
return helper_->connector();
}
void TearDown() override {
audio_system_.reset();
// Deletes ServiceTestHelper, which will result in posting
// AuioThreadContext::QuitOnAudioThread() to AudioManager thread, so that
// Service is delete there.
helper_.reset();
// Joins AudioManager thread if it is used.
audio_manager_.Shutdown();
}
protected:
media::MockAudioManager* audio_manager() { return &audio_manager_; }
media::AudioSystem* audio_system() { return audio_system_.get(); }
private:
base::test::ScopedTaskEnvironment task_environment_;
service_manager::TestServiceManager test_service_manager_;
media::MockAudioManager audio_manager_;
std::unique_ptr<ServiceTestHelper> helper_;
std::unique_ptr<media::AudioSystem> audio_system_;
DISALLOW_COPY_AND_ASSIGN(InProcessServiceTest);
};
// Tests for FakeSystemInfo overriding the global binder.
class FakeSystemInfoTest : public InProcessServiceTest<false>,
public FakeSystemInfo {
public:
FakeSystemInfoTest() {}
~FakeSystemInfoTest() override {}
protected:
MOCK_METHOD0(MethodCalled, void());
private:
void HasInputDevices(HasInputDevicesCallback callback) override {
std::move(callback).Run(true);
MethodCalled();
}
DISALLOW_COPY_AND_ASSIGN(FakeSystemInfoTest);
};
TEST_F(FakeSystemInfoTest, HasInputDevicesCalledOnGlobalBinderOverride) {
FakeSystemInfo::OverrideGlobalBinderForAudioService(this);
base::RunLoop wait_loop;
EXPECT_CALL(*this, MethodCalled())
.WillOnce(testing::Invoke(&wait_loop, &base::RunLoop::Quit));
audio_system()->HasInputDevices(base::BindOnce([](bool) {}));
wait_loop.Run();
FakeSystemInfo::ClearGlobalBinderForAudioService();
}
// Service lifetime tests.
class InProcessServiceLifetimeTestBase : public InProcessServiceTest<false> {
public:
using TestBase = InProcessServiceTest<false>;
InProcessServiceLifetimeTestBase()
: TestBase(base::TimeDelta::FromMilliseconds(1)) {}
~InProcessServiceLifetimeTestBase() override {}
private:
DISALLOW_COPY_AND_ASSIGN(InProcessServiceLifetimeTestBase);
};
INSTANTIATE_TYPED_TEST_SUITE_P(InProcessAudioService,
ServiceLifetimeTestTemplate,
InProcessServiceLifetimeTestBase);
} // namespace audio
// AudioSystem interface conformance tests.
// AudioSystemTestTemplate is defined in media, so should be its instantiations.
namespace media {
using AudioSystemTestVariations =
testing::Types<audio::InProcessServiceTest<false>,
audio::InProcessServiceTest<true>>;
INSTANTIATE_TYPED_TEST_SUITE_P(InProcessAudioService,
AudioSystemTestTemplate,
AudioSystemTestVariations);
} // namespace media