blob: 81253b5ac30417a53cdf62774e3baba1cac299a1 [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/command_line.h"
#include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "media/base/media_switches.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "services/video_capture/public/cpp/mock_receiver.h"
#include "services/video_capture/public/mojom/constants.mojom.h"
#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
#include "services/video_capture/service_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace video_capture {
using testing::Exactly;
using testing::_;
using testing::Invoke;
using testing::InvokeWithoutArgs;
// Test fixture that creates a video_capture::ServiceImpl and sets up a
// local service_manager::Connector through which client code can connect to
// it.
template <class DeviceFactoryProviderConnectorTestTraits>
class DeviceFactoryProviderConnectorTest : public ::testing::Test {
public:
DeviceFactoryProviderConnectorTest() {}
~DeviceFactoryProviderConnectorTest() override {}
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kUseFakeDeviceForMediaStream);
service_impl_ = std::make_unique<ServiceImpl>(
connector_factory_.RegisterInstance(video_capture::mojom::kServiceName),
scoped_task_environment_.GetMainThreadTaskRunner(),
DeviceFactoryProviderConnectorTestTraits::shutdown_delay());
service_impl_->set_termination_closure(
base::BindOnce(&DeviceFactoryProviderConnectorTest::OnServiceQuit,
base::Unretained(this)));
connector_ = connector_factory_.CreateConnector();
base::RunLoop wait_loop;
service_impl_->SetFactoryProviderClientConnectedObserver(
wait_loop.QuitClosure());
connector_->BindInterface(mojom::kServiceName, &factory_provider_);
wait_loop.Run();
}
void TearDown() override {
if (factory_provider_.is_bound() &&
DeviceFactoryProviderConnectorTestTraits::shutdown_delay()
.has_value()) {
factory_provider_.reset();
service_destroyed_wait_loop_.Run();
}
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<ServiceImpl> service_impl_;
mojom::DeviceFactoryProviderPtr factory_provider_;
base::MockCallback<mojom::DeviceFactory::GetDeviceInfosCallback>
device_info_receiver_;
std::unique_ptr<service_manager::Connector> connector_;
base::RunLoop service_destroyed_wait_loop_;
base::OnceClosure service_quit_callback_;
private:
void OnServiceQuit() {
service_impl_.reset();
service_destroyed_wait_loop_.Quit();
if (service_quit_callback_)
std::move(service_quit_callback_).Run();
}
service_manager::TestConnectorFactory connector_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceFactoryProviderConnectorTest);
};
// We need to set the shutdown delay to at least some epsilon > 0 in order
// to avoid the service shutting down synchronously which would prevent
// test case ServiceIncreasesRefCountOnNewConnectionAfterDisconnect from
// being able to reconnect before the timer expires.
struct ShortShutdownDelayDeviceFactoryProviderConnectorTestTraits {
static base::Optional<base::TimeDelta> shutdown_delay() {
return base::TimeDelta::FromMicroseconds(100);
}
};
using ShortShutdownDelayDeviceFactoryProviderConnectorTest =
DeviceFactoryProviderConnectorTest<
ShortShutdownDelayDeviceFactoryProviderConnectorTestTraits>;
// Tests that the service does not quit when a client connects
// while a second client stays connected.
TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
ServiceDoesNotQuitWhenOneOfTwoClientsDisconnects) {
// Establish second connection
mojom::DeviceFactoryProviderPtr second_connection;
{
base::RunLoop wait_loop;
service_impl_->SetFactoryProviderClientConnectedObserver(
wait_loop.QuitClosure());
connector_->BindInterface(mojom::kServiceName, &second_connection);
wait_loop.Run();
}
// Release first connection
{
base::RunLoop wait_loop;
service_impl_->SetFactoryProviderClientDisconnectedObserver(
wait_loop.QuitClosure());
factory_provider_.reset();
wait_loop.Run();
}
EXPECT_FALSE(service_impl_->HasNoContextRefs());
EXPECT_FALSE(factory_provider_.is_bound());
EXPECT_TRUE(second_connection.is_bound());
}
// Tests that the service quits when the only client disconnects after not
// having done anything other than obtaining a connection to the fake device
// factory.
TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
ServiceQuitsWhenSingleClientDisconnected) {
mojom::DeviceFactoryPtr factory;
factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
factory.reset();
factory_provider_.reset();
service_destroyed_wait_loop_.Run();
}
// Tests that the service quits when both of two clients disconnect.
TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
ServiceQuitsWhenAllClientsDisconnected) {
// Bind another client to the DeviceFactoryProvider interface.
mojom::DeviceFactoryProviderPtr second_connection;
connector_->BindInterface(mojom::kServiceName, &second_connection);
mojom::DeviceFactoryPtr factory;
factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
factory.reset();
factory_provider_.reset();
second_connection.reset();
service_destroyed_wait_loop_.Run();
}
// Tests that the service increase the context ref count when a new connection
// comes in after all previous connections have been released.
TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
ServiceIncreasesRefCountOnNewConnectionAfterDisconnect) {
base::RunLoop wait_loop;
service_impl_->SetShutdownTimeoutCancelledObserver(base::BindRepeating(
[](base::RunLoop* wait_loop) { wait_loop->Quit(); }, &wait_loop));
// Disconnect and reconnect
factory_provider_.reset();
connector_->BindInterface(mojom::kServiceName, &factory_provider_);
wait_loop.Run();
EXPECT_FALSE(service_impl_->HasNoContextRefs());
}
// Tests that the service quits when the last client disconnects while using a
// device.
TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
ServiceQuitsWhenClientDisconnectsWhileUsingDevice) {
mojom::DeviceFactoryPtr factory;
factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
// Connect to and start first device (in this case a fake camera).
base::MockCallback<mojom::DeviceFactory::GetDeviceInfosCallback>
device_info_receiver_;
media::VideoCaptureDeviceInfo fake_device_info;
{
base::RunLoop wait_loop;
EXPECT_CALL(device_info_receiver_, Run(_))
.WillOnce(Invoke(
[&fake_device_info, &wait_loop](
const std::vector<media::VideoCaptureDeviceInfo>& infos) {
fake_device_info = infos[0];
wait_loop.Quit();
}));
factory->GetDeviceInfos(device_info_receiver_.Get());
wait_loop.Run();
}
mojom::DevicePtr fake_device;
factory->CreateDevice(
std::move(fake_device_info.descriptor.device_id),
mojo::MakeRequest(&fake_device),
base::BindOnce([](mojom::DeviceAccessResultCode result_code) {
ASSERT_EQ(mojom::DeviceAccessResultCode::SUCCESS, result_code);
}));
media::VideoCaptureParams requestable_settings;
requestable_settings.requested_format = fake_device_info.supported_formats[0];
mojom::ReceiverPtr receiver_proxy;
MockReceiver mock_receiver(mojo::MakeRequest(&receiver_proxy));
fake_device->Start(requestable_settings, std::move(receiver_proxy));
{
base::RunLoop wait_loop;
EXPECT_CALL(mock_receiver, OnStarted()).WillOnce([&wait_loop]() {
wait_loop.Quit();
});
wait_loop.Run();
}
// Disconnect
fake_device.reset();
factory.reset();
factory_provider_.reset();
service_destroyed_wait_loop_.Run();
}
// Tests that the service does not quit when the only client discards the
// DeviceFactoryProvider but holds on to a DeviceFactory.
TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
DeviceFactoryCanStillBeUsedAfterReleaseingDeviceFactoryProvider) {
mojom::DeviceFactoryPtr factory;
factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
// Exercise: Disconnect DeviceFactoryProvider
{
base::RunLoop wait_loop;
service_impl_->SetFactoryProviderClientDisconnectedObserver(
wait_loop.QuitClosure());
factory_provider_.reset();
wait_loop.Run();
}
EXPECT_FALSE(service_impl_->HasNoContextRefs());
// Verify that |factory| is still functional by calling GetDeviceInfos().
{
base::RunLoop wait_loop;
EXPECT_CALL(device_info_receiver_, Run(_))
.WillOnce(Invoke(
[&wait_loop](
const std::vector<media::VideoCaptureDeviceInfo>& infos) {
wait_loop.Quit();
}));
factory->GetDeviceInfos(device_info_receiver_.Get());
wait_loop.Run();
}
}
struct NoAutomaticShutdownDeviceFactoryProviderConnectorTestTraits {
static base::Optional<base::TimeDelta> shutdown_delay() {
return base::Optional<base::TimeDelta>();
}
};
using NoAutomaticShutdownDeviceFactoryProviderConnectorTest =
DeviceFactoryProviderConnectorTest<
NoAutomaticShutdownDeviceFactoryProviderConnectorTestTraits>;
// Tests that the service does not shut down after disconnecting.
TEST_F(NoAutomaticShutdownDeviceFactoryProviderConnectorTest,
ServiceDoesNotShutDownOnDisconnect) {
{
base::RunLoop wait_loop;
service_impl_->SetFactoryProviderClientDisconnectedObserver(
wait_loop.QuitClosure());
factory_provider_.reset();
wait_loop.Run();
}
base::MockCallback<base::OnceClosure> service_impl_destructor_cb;
service_quit_callback_ = service_impl_destructor_cb.Get();
EXPECT_CALL(service_impl_destructor_cb, Run()).Times(0);
// Wait for an arbitrary short extra time after which we are convinced that
// there is enough evidence that service is not going to shut down.
{
base::RunLoop wait_loop;
base::OneShotTimer wait_timer;
wait_timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
wait_loop.QuitClosure());
wait_loop.Run();
}
service_quit_callback_.Reset();
}
} // namespace video_capture