blob: 07fff3e0854598e2bb95dc8e93427bd57a9d99b7 [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/barrier_closure.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "device/base/synchronization/one_writer_seqlock.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/device/device_service_test_base.h"
#include "services/device/generic_sensor/fake_platform_sensor_and_provider.h"
#include "services/device/generic_sensor/platform_sensor.h"
#include "services/device/generic_sensor/platform_sensor_provider.h"
#include "services/device/generic_sensor/sensor_provider_impl.h"
#include "services/device/public/cpp/device_features.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
#include "services/device/public/cpp/generic_sensor/sensor_traits.h"
#include "services/device/public/mojom/constants.mojom.h"
using ::testing::_;
using ::testing::Invoke;
namespace device {
using mojom::SensorType;
namespace {
void CheckValue(double expect, double value) {
EXPECT_DOUBLE_EQ(expect, value);
}
void CheckSuccess(base::OnceClosure quit_closure,
bool expect,
bool is_success) {
EXPECT_EQ(expect, is_success);
std::move(quit_closure).Run();
}
class TestSensorClient : public mojom::SensorClient {
public:
TestSensorClient(SensorType type) : client_binding_(this), type_(type) {}
// Implements mojom::SensorClient:
void SensorReadingChanged() override {
UpdateReadingData();
if (check_value_)
std::move(check_value_).Run(reading_data_.als.value);
if (quit_closure_)
std::move(quit_closure_).Run();
}
void RaiseError() override {}
// Sensor mojo interfaces callbacks:
void OnSensorCreated(base::OnceClosure quit_closure,
mojom::SensorCreationResult result,
mojom::SensorInitParamsPtr params) {
ASSERT_TRUE(params);
EXPECT_EQ(mojom::SensorCreationResult::SUCCESS, result);
EXPECT_TRUE(params->memory.is_valid());
const double expected_default_frequency =
std::min(30.0, GetSensorMaxAllowedFrequency(type_));
EXPECT_DOUBLE_EQ(expected_default_frequency,
params->default_configuration.frequency());
const double expected_maximum_frequency =
std::min(50.0, GetSensorMaxAllowedFrequency(type_));
EXPECT_DOUBLE_EQ(expected_maximum_frequency, params->maximum_frequency);
EXPECT_DOUBLE_EQ(1.0, params->minimum_frequency);
shared_buffer_ = params->memory->MapAtOffset(
mojom::SensorInitParams::kReadBufferSizeForTests,
params->buffer_offset);
ASSERT_TRUE(shared_buffer_);
sensor_.Bind(std::move(params->sensor));
client_binding_.Bind(std::move(params->client_request));
std::move(quit_closure).Run();
}
void OnGetDefaultConfiguration(
base::OnceClosure quit_closure,
const PlatformSensorConfiguration& configuration) {
EXPECT_DOUBLE_EQ(30.0, configuration.frequency());
std::move(quit_closure).Run();
}
void OnAddConfiguration(base::OnceCallback<void(bool)> expect_function,
bool is_success) {
std::move(expect_function).Run(is_success);
}
// For SensorReadingChanged().
void SetQuitClosure(base::OnceClosure quit_closure) {
quit_closure_ = std::move(quit_closure);
}
void SetCheckValueCallback(base::OnceCallback<void(double)> callback) {
check_value_ = std::move(callback);
}
mojom::SensorPtr& sensor() { return sensor_; }
private:
void UpdateReadingData() {
memset(&reading_data_, 0, sizeof(SensorReading));
int read_attempts = 0;
const int kMaxReadAttemptsCount = 10;
while (!TryReadFromBuffer(reading_data_)) {
if (++read_attempts == kMaxReadAttemptsCount) {
ADD_FAILURE() << "Maximum read attempts reached.";
return;
}
}
}
bool TryReadFromBuffer(SensorReading& result) {
const SensorReadingSharedBuffer* buffer =
static_cast<const SensorReadingSharedBuffer*>(shared_buffer_.get());
const OneWriterSeqLock& seqlock = buffer->seqlock.value();
auto version = seqlock.ReadBegin();
auto reading_data = buffer->reading;
if (seqlock.ReadRetry(version))
return false;
result = reading_data;
return true;
}
mojom::SensorPtr sensor_;
mojo::Binding<mojom::SensorClient> client_binding_;
mojo::ScopedSharedBufferMapping shared_buffer_;
SensorReading reading_data_;
// Test Clients set |quit_closure_| and start a RunLoop in main thread, then
// expect the |quit_closure| will quit the RunLoop in SensorReadingChanged().
// In this way we guarantee the SensorReadingChanged() does be triggered.
base::OnceClosure quit_closure_;
// |check_value_| is called to verify the data is same as we
// expected in SensorReadingChanged().
base::OnceCallback<void(double)> check_value_;
SensorType type_;
DISALLOW_COPY_AND_ASSIGN(TestSensorClient);
};
class GenericSensorServiceTest : public DeviceServiceTestBase {
public:
GenericSensorServiceTest()
: io_thread_task_runner_(io_thread_.task_runner()),
io_loop_finished_event_(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
{features::kGenericSensor, features::kGenericSensorExtraClasses}, {});
DeviceServiceTestBase::SetUp();
io_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&GenericSensorServiceTest::SetUpOnIOThread,
base::Unretained(this)));
io_loop_finished_event_.Wait();
connector()->BindInterface(mojom::kServiceName, &sensor_provider_);
}
void TearDown() override {
io_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&GenericSensorServiceTest::TearDownOnIOThread,
base::Unretained(this)));
io_loop_finished_event_.Wait();
}
void SetUpOnIOThread() {
fake_platform_sensor_provider_ = new FakePlatformSensorProvider();
PlatformSensorProvider::SetProviderForTesting(
fake_platform_sensor_provider_);
io_loop_finished_event_.Signal();
}
void TearDownOnIOThread() {
PlatformSensorProvider::SetProviderForTesting(nullptr);
DCHECK(fake_platform_sensor_provider_);
delete fake_platform_sensor_provider_;
fake_platform_sensor_provider_ = nullptr;
io_loop_finished_event_.Signal();
}
mojom::SensorProviderPtr sensor_provider_;
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
base::WaitableEvent io_loop_finished_event_;
base::test::ScopedFeatureList scoped_feature_list_;
// FakePlatformSensorProvider must be created and deleted in IO thread.
FakePlatformSensorProvider* fake_platform_sensor_provider_;
DISALLOW_COPY_AND_ASSIGN(GenericSensorServiceTest);
};
// Requests the SensorProvider to create a sensor.
TEST_F(GenericSensorServiceTest, GetSensorTest) {
auto client = std::make_unique<TestSensorClient>(SensorType::PROXIMITY);
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::PROXIMITY,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
// Tests GetDefaultConfiguration.
TEST_F(GenericSensorServiceTest, GetDefaultConfigurationTest) {
auto client = std::make_unique<TestSensorClient>(SensorType::ACCELEROMETER);
{
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::ACCELEROMETER,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
{
base::RunLoop run_loop;
client->sensor()->GetDefaultConfiguration(
base::BindOnce(&TestSensorClient::OnGetDefaultConfiguration,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
}
// Tests adding a valid configuration. Client should be notified by
// SensorClient::SensorReadingChanged().
TEST_F(GenericSensorServiceTest, ValidAddConfigurationTest) {
auto client = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
PlatformSensorConfiguration configuration(50.0);
client->sensor()->AddConfiguration(
configuration,
base::BindOnce(
&TestSensorClient::OnAddConfiguration, base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, base::DoNothing::Once<>(), true)));
{
// Expect the SensorReadingChanged() will be called after AddConfiguration.
base::RunLoop run_loop;
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 50.0));
client->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
}
// Tests adding an invalid configuation, the max allowed frequency is 50.0 in
// the mocked SensorImpl, while we add one with 60.0.
TEST_F(GenericSensorServiceTest, InvalidAddConfigurationTest) {
auto client =
std::make_unique<TestSensorClient>(SensorType::LINEAR_ACCELERATION);
{
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::LINEAR_ACCELERATION,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
{
// Invalid configuration that exceeds the max allowed frequency.
base::RunLoop run_loop;
PlatformSensorConfiguration configuration(60.0);
client->sensor()->AddConfiguration(
configuration,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, run_loop.QuitClosure(), false)));
run_loop.Run();
}
}
// Tests adding more than one clients. Sensor should send notification to all
// its clients.
TEST_F(GenericSensorServiceTest, MultipleClientsTest) {
auto client_1 = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
auto client_2 = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
auto barrier_closure = base::BarrierClosure(2, run_loop.QuitClosure());
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client_1.get()), barrier_closure));
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client_2.get()), barrier_closure));
run_loop.Run();
}
{
base::RunLoop run_loop;
PlatformSensorConfiguration configuration(48.0);
client_1->sensor()->AddConfiguration(
configuration,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client_1.get()),
base::BindOnce(&CheckSuccess, run_loop.QuitClosure(), true)));
run_loop.Run();
}
// Expect the SensorReadingChanged() will be called for both clients.
{
base::RunLoop run_loop;
auto barrier_closure = base::BarrierClosure(2, run_loop.QuitClosure());
client_1->SetCheckValueCallback(base::BindOnce(&CheckValue, 48.0));
client_2->SetCheckValueCallback(base::BindOnce(&CheckValue, 48.0));
client_1->SetQuitClosure(barrier_closure);
client_2->SetQuitClosure(barrier_closure);
run_loop.Run();
}
}
// Tests adding more than one clients. If mojo connection is broken on one
// client, other clients should not be affected.
TEST_F(GenericSensorServiceTest, ClientMojoConnectionBrokenTest) {
auto client_1 = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
auto client_2 = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
auto barrier_closure = base::BarrierClosure(2, run_loop.QuitClosure());
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client_1.get()), barrier_closure));
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client_2.get()), barrier_closure));
run_loop.Run();
}
// Breaks mojo connection of client_1.
client_1->sensor().reset();
{
base::RunLoop run_loop;
PlatformSensorConfiguration configuration(48.0);
client_2->sensor()->AddConfiguration(
configuration,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client_2.get()),
base::BindOnce(&CheckSuccess, run_loop.QuitClosure(), true)));
run_loop.Run();
}
// Expect the SensorReadingChanged() will be called on client_2.
{
base::RunLoop run_loop;
client_2->SetCheckValueCallback(base::BindOnce(&CheckValue, 48.0));
client_2->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
}
// Test add and remove configuration operations.
TEST_F(GenericSensorServiceTest, AddAndRemoveConfigurationTest) {
auto client = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
// Expect the SensorReadingChanged() will be called. The frequency value
// should be 30.0
PlatformSensorConfiguration configuration_30(30.0);
client->sensor()->AddConfiguration(
configuration_30,
base::BindOnce(
&TestSensorClient::OnAddConfiguration, base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, base::DoNothing::Once<>(), true)));
{
base::RunLoop run_loop;
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 30.0));
client->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
// Expect the SensorReadingChanged() will be called. The frequency value
// should be 30.0 instead of 20.0
{
base::RunLoop run_loop;
PlatformSensorConfiguration configuration_20(20.0);
client->sensor()->AddConfiguration(
configuration_20,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, base::DoNothing::Once<>(), true)));
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 30.0));
client->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
// After 'configuration_30' is removed, expect the SensorReadingChanged() will
// be called. The frequency value should be 20.0.
{
base::RunLoop run_loop;
client->sensor()->RemoveConfiguration(configuration_30);
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 20.0));
client->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
}
// Test suspend. After suspending, the client won't be notified by
// SensorReadingChanged() after calling AddConfiguration.
// Call the AddConfiguration() twice, if the SensorReadingChanged() was
// called, it should happen before the callback triggerred by the second
// AddConfiguration(). In this way we make sure it won't be missed by the
// early quit of main thread (when there is an unexpected notification by
// SensorReadingChanged()).
TEST_F(GenericSensorServiceTest, SuspendTest) {
auto client = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
client->sensor()->Suspend();
// Expect the SensorReadingChanged() won't be called. Pass a bad value(123.0)
// to |check_value_| to guarantee SensorReadingChanged() really doesn't be
// called. Otherwise the CheckValue() will complain on the bad value.
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 123.0));
base::RunLoop run_loop;
PlatformSensorConfiguration configuration_1(30.0);
client->sensor()->AddConfiguration(
configuration_1,
base::BindOnce(
&TestSensorClient::OnAddConfiguration, base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, base::DoNothing::Once<>(), true)));
PlatformSensorConfiguration configuration_2(31.0);
client->sensor()->AddConfiguration(
configuration_2,
base::BindOnce(
&TestSensorClient::OnAddConfiguration, base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, run_loop.QuitClosure(), true)));
run_loop.Run();
}
// Test suspend and resume. After resuming, client can add configuration and
// be notified by SensorReadingChanged() as usual.
TEST_F(GenericSensorServiceTest, SuspendThenResumeTest) {
auto client = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client.get()), run_loop.QuitClosure()));
run_loop.Run();
}
// Expect the SensorReadingChanged() will be called. The frequency should
// be 30.0 after AddConfiguration.
{
base::RunLoop run_loop;
PlatformSensorConfiguration configuration_1(30.0);
client->sensor()->AddConfiguration(
configuration_1,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, base::DoNothing::Once<>(), true)));
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 30.0));
client->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
client->sensor()->Suspend();
client->sensor()->Resume();
// Expect the SensorReadingChanged() will be called. The frequency should
// be 50.0 after new configuration is added.
{
base::RunLoop run_loop;
PlatformSensorConfiguration configuration_2(50.0);
client->sensor()->AddConfiguration(
configuration_2,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client.get()),
base::BindOnce(&CheckSuccess, base::DoNothing::Once<>(), true)));
client->SetCheckValueCallback(base::BindOnce(&CheckValue, 50.0));
client->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
}
// Test suspend when there are more than one client. The suspended client won't
// receive SensorReadingChanged() notification.
TEST_F(GenericSensorServiceTest, MultipleClientsSuspendAndResumeTest) {
auto client_1 = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
auto client_2 = std::make_unique<TestSensorClient>(SensorType::AMBIENT_LIGHT);
{
base::RunLoop run_loop;
auto barrier_closure = base::BarrierClosure(2, run_loop.QuitClosure());
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client_1.get()), barrier_closure));
sensor_provider_->GetSensor(
SensorType::AMBIENT_LIGHT,
base::BindOnce(&TestSensorClient::OnSensorCreated,
base::Unretained(client_2.get()), barrier_closure));
run_loop.Run();
}
client_1->sensor()->Suspend();
{
base::RunLoop run_loop;
PlatformSensorConfiguration configuration(46.0);
client_2->sensor()->AddConfiguration(
configuration,
base::BindOnce(
&TestSensorClient::OnAddConfiguration,
base::Unretained(client_2.get()),
base::BindOnce(&CheckSuccess, run_loop.QuitClosure(), true)));
run_loop.Run();
}
// Expect the sensor_2 will receive SensorReadingChanged() notification while
// sensor_1 won't.
{
base::RunLoop run_loop;
client_2->SetCheckValueCallback(base::BindOnce(&CheckValue, 46.0));
client_2->SetQuitClosure(run_loop.QuitClosure());
run_loop.Run();
}
}
} // namespace
} // namespace device