#include "ash/sensor_info/sensor_provider.h"
#include <array>
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include <vector>
#include "ash/accelerometer/accelerometer_constants.h"
#include "ash/sensor_info/sensor_types.h"
#include "ash/test/ash_test_helper.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "chromeos/components/sensors/ash/sensor_hal_dispatcher.h"
#include "chromeos/components/sensors/fake_sensor_device.h"
#include "chromeos/components/sensors/fake_sensor_hal_server.h"
#include "chromeos/components/sensors/mojom/sensor.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
using ::chromeos::sensors::mojom::DeviceType;
namespace {
constexpr double kFakeScaleValue = 10.0;
constexpr int kFakeLidAccelerometerId = 1;
constexpr int kFakeBaseAccelerometerId = 2;
constexpr int kFakeBaseGyroscopeId = 3;
constexpr int kFakeLidAngleId = 4;
constexpr std::array<int64_t, 3> kFakeSampleData = {1, 2, 3};
class FakeObserver : public SensorObserver {
public:
void OnSensorUpdated(const SensorUpdate& update) override {
for (int index = 0; index < static_cast<int>(SensorType::kSensorTypeCount);
++index) {
auto source = static_cast<SensorType>(index);
if (!update.has(source)) {
continue;
}
if (source != SensorType::kLidAngle) {
EXPECT_DOUBLE_EQ(update.get(source)->x,
kFakeSampleData[0] * kFakeScaleValue);
EXPECT_DOUBLE_EQ(update.get(source)->y,
kFakeSampleData[1] * kFakeScaleValue);
EXPECT_DOUBLE_EQ(update.get(source)->z,
kFakeSampleData[2] * kFakeScaleValue);
} else {
EXPECT_DOUBLE_EQ(update.get(source)->x, kFakeSampleData[0]);
}
}
update_ = update;
}
SensorUpdate update_;
};
}
class SensorProviderTest : public testing::Test {
protected:
void SetUp() override {
chromeos::sensors::SensorHalDispatcher::Initialize();
sensor_hal_server_ =
std::make_unique<chromeos::sensors::FakeSensorHalServer>();
provider_ = std::make_unique<SensorProvider>();
provider_->AddObserver(&observer_);
}
void TearDown() override {
chromeos::sensors::SensorHalDispatcher::Shutdown();
}
void AddDevice(int32_t iio_device_id,
std::set<DeviceType> types,
std::optional<std::string> scale,
std::optional<std::string> location) {
std::vector<chromeos::sensors::FakeSensorDevice::ChannelData> channels_data;
int size = 0;
if (base::Contains(types, DeviceType::ANGL)) {
channels_data.resize(1);
channels_data[0].id = "angl";
channels_data[0].sample_data = 1;
channels_data[0].attrs["raw"] = "1";
}
if (base::Contains(types, DeviceType::ACCEL)) {
size += kNumberOfAxes;
channels_data.resize(size);
for (uint32_t i = 0; i < kNumberOfAxes; ++i) {
channels_data[size - kNumberOfAxes + i].id = kAccelerometerChannels[i];
channels_data[size - kNumberOfAxes + i].sample_data =
kFakeSampleData[i];
}
}
if (base::Contains(types, DeviceType::ANGLVEL)) {
size += kNumberOfAxes;
channels_data.resize(size);
for (uint32_t i = 0; i < kNumberOfAxes; ++i) {
channels_data[size - kNumberOfAxes + i].id = kGyroscopeChannels[i];
channels_data[size - kNumberOfAxes + i].sample_data =
kFakeSampleData[i];
}
}
std::unique_ptr<chromeos::sensors::FakeSensorDevice> sensor_device(
std::make_unique<chromeos::sensors::FakeSensorDevice>(
std::move(channels_data)));
if (scale.has_value()) {
sensor_device->SetAttribute(chromeos::sensors::mojom::kScale,
scale.value());
}
if (location.has_value()) {
sensor_device->SetAttribute(chromeos::sensors::mojom::kLocation,
location.value());
}
sensor_devices_[iio_device_id] = sensor_device.get();
mojo::Remote<chromeos::sensors::mojom::SensorDevice> sensor;
sensor_device->AddReceiver(sensor.BindNewPipeAndPassReceiver());
sensor_hal_server_->GetSensorService()->SetDevice(
iio_device_id, std::move(types), std::move(sensor_device));
}
void StartConnection() {
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
}
base::test::SingleThreadTaskEnvironment task_environment;
FakeObserver observer_;
std::unique_ptr<chromeos::sensors::FakeSensorHalServer> sensor_hal_server_;
std::map<int32_t,
raw_ptr<chromeos::sensors::FakeSensorDevice, CtnExperimental>>
sensor_devices_;
std::unique_ptr<SensorProvider> provider_;
};
TEST_F(SensorProviderTest, CheckNoScale) {
AddDevice(kFakeBaseAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
std::nullopt, kLocationStrings[1]);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected{false, false, false, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected);
EXPECT_FALSE(observer_.update_.has(SensorType::kAccelerometerBase));
}
TEST_F(SensorProviderTest, CheckNoLocation) {
AddDevice(kFakeBaseAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), std::nullopt);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected{false, false, false, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected);
EXPECT_FALSE(observer_.update_.has(SensorType::kAccelerometerLid));
EXPECT_FALSE(observer_.update_.has(SensorType::kAccelerometerBase));
}
TEST_F(SensorProviderTest, GetSamplesOfLidAccel) {
AddDevice(kFakeLidAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[0]);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected{false, false, true, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected);
EXPECT_TRUE(sensor_hal_server_->GetSensorService()->HasReceivers());
EXPECT_TRUE(base::Contains(sensor_devices_, kFakeLidAccelerometerId));
EXPECT_TRUE(sensor_devices_[kFakeLidAccelerometerId]->HasReceivers());
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerLid));
}
TEST_F(SensorProviderTest, GetSamplesOfLidAngleAndLidAccel) {
AddDevice(kFakeLidAngleId, std::set<DeviceType>{DeviceType::ANGL},
base::NumberToString(kFakeScaleValue), kLocationStrings[0]);
AddDevice(kFakeLidAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[0]);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected{true, false, true, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected);
EXPECT_TRUE(observer_.update_.has(SensorType::kLidAngle));
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerLid));
}
TEST_F(SensorProviderTest, GetSamplesOfBaseGyroscope) {
AddDevice(kFakeBaseGyroscopeId, std::set<DeviceType>{DeviceType::ANGLVEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[1]);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected{false, false, false, true, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected);
EXPECT_TRUE(observer_.update_.has(SensorType::kGyroscopeBase));
}
TEST_F(SensorProviderTest, GetSamplesOfBaseGyroscopeAndBaseAccel) {
AddDevice(kFakeBaseGyroscopeId, std::set<DeviceType>{DeviceType::ANGLVEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[1]);
AddDevice(kFakeBaseAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[1]);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected1{false, true, false, true, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected1);
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerBase));
EXPECT_TRUE(observer_.update_.has(SensorType::kGyroscopeBase));
sensor_hal_server_->GetSensorService()->ClearReceivers();
sensor_hal_server_->OnServerDisconnect();
base::RunLoop().RunUntilIdle();
AddDevice(kFakeBaseAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
std::nullopt, std::nullopt);
StartConnection();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected2{false, false, false, true, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected2);
EXPECT_FALSE(observer_.update_.has(SensorType::kAccelerometerBase));
EXPECT_TRUE(observer_.update_.has(SensorType::kGyroscopeBase));
}
TEST_F(SensorProviderTest, AddSensorsWhileSampling) {
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected1{false, false, false, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected1);
AddDevice(kFakeBaseAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[1]);
base::RunLoop().RunUntilIdle();
std::vector<bool> expected2{false, true, false, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected2);
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerBase));
AddDevice(kFakeLidAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[0]);
sensor_devices_[kFakeBaseAccelerometerId]->ClearReceiversWithReason(
chromeos::sensors::mojom::SensorDeviceDisconnectReason::DEVICE_REMOVED,
"Device was removed");
AddDevice(kFakeBaseAccelerometerId, std::set<DeviceType>{DeviceType::ACCEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[1]);
base::RunLoop().RunUntilIdle();
std::vector<bool> expected3{false, true, true, false, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected3);
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerLid));
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerBase));
}
TEST_F(SensorProviderTest, GetSamplesOfAccelGyroDevices) {
AddDevice(kFakeBaseGyroscopeId,
std::set<DeviceType>{DeviceType::ACCEL, DeviceType::ANGLVEL},
base::NumberToString(kFakeScaleValue), kLocationStrings[1]);
StartConnection();
provider_->EnableSensorReading();
base::RunLoop().RunUntilIdle();
std::vector<bool> expected{false, true, false, true, false};
EXPECT_EQ(provider_->GetStateForTesting(), expected);
EXPECT_TRUE(observer_.update_.has(SensorType::kAccelerometerBase));
EXPECT_TRUE(observer_.update_.has(SensorType::kGyroscopeBase));
}
}