chromium/chrome/browser/ash/power/auto_screen_brightness/light_samples_observer_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/ash/power/auto_screen_brightness/light_samples_observer.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "chrome/browser/ash/power/auto_screen_brightness/fake_observer.h"
#include "chrome/browser/ash/power/auto_screen_brightness/utils.h"
#include "chromeos/components/sensors/fake_sensor_device.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {
namespace power {
namespace auto_screen_brightness {

namespace {

constexpr int64_t kFakeSampleData = 50;
constexpr int64_t kFakeColorSampleData = 60;
constexpr char kIlluminanceColorChannels[][18] = {
    "illuminance_red", "illuminance_green", "illuminance_blue"};

}  // namespace

class LightSamplesObserverTest : public testing::Test {
 protected:
  void SetChannels(bool is_color_sensor) {
    std::vector<chromeos::sensors::FakeSensorDevice::ChannelData> channels_data;
    chromeos::sensors::FakeSensorDevice::ChannelData illuminance_data;

    illuminance_data.id = chromeos::sensors::mojom::kLightChannel;
    illuminance_data.sample_data = kFakeSampleData;
    channels_data.push_back(std::move(illuminance_data));

    if (is_color_sensor) {
      for (size_t i = 0; i < std::size(kIlluminanceColorChannels); ++i) {
        illuminance_data.id = kIlluminanceColorChannels[i];
        illuminance_data.sample_data = kFakeColorSampleData;
        channels_data.push_back(std::move(illuminance_data));
      }
    }

    sensor_device_ = std::make_unique<chromeos::sensors::FakeSensorDevice>(
        std::move(channels_data));
  }

  void SetProvider(mojo::Remote<chromeos::sensors::mojom::SensorDevice> light) {
    als_reader_ = std::make_unique<AlsReader>();
    observer_ = std::make_unique<LightSamplesObserver>(als_reader_.get(),
                                                       std::move(light));
    als_reader_->AddObserver(&fake_observer_);
  }

  void DisableIlluminanceChannel(mojo::ReceiverId id) {
    sensor_device_->SetChannelsEnabledWithId(id, {0}, false);
  }

  std::unique_ptr<chromeos::sensors::FakeSensorDevice> sensor_device_;
  FakeObserver fake_observer_;

  std::unique_ptr<LightSamplesObserver> observer_;
  std::unique_ptr<AlsReader> als_reader_;

  base::HistogramTester histogram_tester_;
  base::test::SingleThreadTaskEnvironment task_environment_;
};

TEST_F(LightSamplesObserverTest, MissingChannels) {
  sensor_device_ = std::make_unique<chromeos::sensors::FakeSensorDevice>(
      std::vector<chromeos::sensors::FakeSensorDevice::ChannelData>{});

  mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
  sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
  SetProvider(std::move(light));

  // Wait until the mojo connection is reset.
  base::RunLoop().RunUntilIdle();

  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.DataError",
      static_cast<int>(DataError::kMojoSamplesObserver), 1);
  EXPECT_FALSE(sensor_device_->HasReceivers());
  EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 0);
}

TEST_F(LightSamplesObserverTest, StartReadingTwiceError) {
  SetChannels(false);

  mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
  sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());

  mojo::PendingRemote<chromeos::sensors::mojom::SensorDeviceSamplesObserver>
      pending_remote;
  auto null_receiver = pending_remote.InitWithNewPipeAndPassReceiver();
  light->StartReadingSamples(std::move(pending_remote));

  SetProvider(std::move(light));

  // Wait until the mojo connection is reset.
  base::RunLoop().RunUntilIdle();

  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.DataError",
      static_cast<int>(DataError::kMojoSamplesObserver), 1);
  EXPECT_FALSE(sensor_device_->HasReceivers());
  EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 0);
}

TEST_F(LightSamplesObserverTest, GetSamplesWithoutColorChannels) {
  SetChannels(false);

  mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
  auto id = sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
  SetProvider(std::move(light));

  // Wait until a sample is received.
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(sensor_device_->HasReceivers());
  EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
  EXPECT_EQ(fake_observer_.ambient_light(), kFakeSampleData);

  DisableIlluminanceChannel(id);

  // Wait until a sample is received.
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(sensor_device_->HasReceivers());
  // The updated sample is not sent to |OnAmbientLightUpdated(lux)|.
  EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);

  // Simulate a disconnection of the observer's mojo channel in IIO Service.
  sensor_device_->ResetObserverRemote(id);

  // Wait until the disconnection is done.
  base::RunLoop().RunUntilIdle();
  // OnObserverDisconnect shouldn't reset SensorDevice's mojo endpoint so that
  // LightProviderMojo can get the disconnection.
  EXPECT_TRUE(sensor_device_->HasReceivers());
}

TEST_F(LightSamplesObserverTest, GetSamplesWithColorChannels) {
  SetChannels(true);

  mojo::Remote<chromeos::sensors::mojom::SensorDevice> light;
  sensor_device_->AddReceiver(light.BindNewPipeAndPassReceiver());
  SetProvider(std::move(light));

  // Wait until a sample is received.
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(sensor_device_->HasReceivers());
  EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
  EXPECT_EQ(fake_observer_.ambient_light(), kFakeSampleData);

  base::flat_map<int32_t, int64_t> sample;
  sample[2] = kFakeColorSampleData;

  // Should ignore a sample without the target channel: illuminance.
  observer_->OnSampleUpdated(sample);

  EXPECT_EQ(fake_observer_.num_received_ambient_lights(), 1);
}

}  // namespace auto_screen_brightness
}  // namespace power
}  // namespace ash