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

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

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

#include <map>
#include <numeric>
#include <vector>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/memory/ptr_util.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chrome/browser/ash/power/auto_screen_brightness/fake_brightness_monitor.h"
#include "chrome/browser/ash/power/auto_screen_brightness/fake_light_provider.h"
#include "chrome/browser/ash/power/auto_screen_brightness/fake_model_config_loader.h"
#include "chrome/browser/ash/power/auto_screen_brightness/modeller.h"
#include "chrome/browser/ash/power/auto_screen_brightness/monotone_cubic_spline.h"
#include "chrome/browser/ash/power/auto_screen_brightness/utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/testing_pref_store.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {
namespace power {
namespace auto_screen_brightness {

namespace {

// Checks |actual_avg_log| is equal to the avg log calculated from
// |expected_data|. |expected_data| contains absolute lux value, not log lux.
void CheckAvgLog(const std::vector<double>& expected_data,
                 double actual_avg_log) {
  const size_t count = expected_data.size();
  CHECK_NE(count, 0u);
  const double expected_avg_log =
      std::accumulate(
          expected_data.begin(), expected_data.end(), 0.0,
          [](double sum, double lux) { return sum + ConvertToLog(lux); }) /
      count;
  EXPECT_DOUBLE_EQ(actual_avg_log, expected_avg_log);
}

// Testing modeller.
class FakeModeller : public Modeller {
 public:
  FakeModeller() = default;
  ~FakeModeller() override = default;

  void InitModellerWithModel(const Model& model) {
    DCHECK(!modeller_initialized_);
    modeller_initialized_ = true;
    model_ = model;
  }

  void ReportModelTrained(const MonotoneCubicSpline& personal_curve) {
    DCHECK(modeller_initialized_);
    model_.personal_curve = personal_curve;
    for (auto& observer : observers_)
      observer.OnModelTrained(personal_curve);
  }

  void ReportModelInitialized() {
    DCHECK(modeller_initialized_);
    for (auto& observer : observers_)
      observer.OnModelInitialized(model_);
  }

  // Modeller overrides:
  void AddObserver(Modeller::Observer* observer) override {
    DCHECK(observer);
    observers_.AddObserver(observer);
    if (modeller_initialized_)
      observer->OnModelInitialized(model_);
  }

  void RemoveObserver(Modeller::Observer* observer) override {
    DCHECK(observer);
    observers_.RemoveObserver(observer);
  }

 private:
  bool modeller_initialized_ = false;
  Model model_;

  base::ObserverList<Observer> observers_;
};

class TestObserver : public chromeos::PowerManagerClient::Observer {
 public:
  TestObserver() = default;
  ~TestObserver() override = default;

  // chromeos::PowerManagerClient::Observer overrides:
  void ScreenBrightnessChanged(
      const power_manager::BacklightBrightnessChange& change) override {
    ++num_changes_;
    change_ = change;
  }
  double GetBrightnessPercent() const { return change_.percent(); }

  int num_changes() const { return num_changes_; }

  power_manager::BacklightBrightnessChange_Cause GetCause() const {
    return change_.cause();
  }

 private:
  int num_changes_ = 0;
  power_manager::BacklightBrightnessChange change_;
};

}  // namespace

// TODO(jiameng): add more unit tests on AdapterDecision related histograms.
class AdapterTest : public testing::Test {
 public:
  AdapterTest()
      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

  AdapterTest(const AdapterTest&) = delete;
  AdapterTest& operator=(const AdapterTest&) = delete;

  ~AdapterTest() override = default;

  void SetUp() override {
    als_reader_ = std::make_unique<AlsReader>();
    fake_light_provider_ =
        std::make_unique<FakeLightProvider>(als_reader_.get());

    chromeos::PowerManagerClient::InitializeFake();
    power_manager::SetBacklightBrightnessRequest request;
    request.set_percent(1);
    chromeos::PowerManagerClient::Get()->SetScreenBrightness(request);
    task_environment_.RunUntilIdle();

    chromeos::PowerManagerClient::Get()->AddObserver(&test_observer_);

    global_curve_ = MonotoneCubicSpline::CreateMonotoneCubicSpline(
        {-4, 12, 20}, {30, 80, 100});
    personal_curve_ = MonotoneCubicSpline::CreateMonotoneCubicSpline(
        {-4, 12, 20}, {20, 60, 100});
    DCHECK(global_curve_);
    DCHECK(personal_curve_);
  }

  void TearDown() override {
    adapter_.reset();
    base::ThreadPoolInstance::Get()->FlushForTesting();
    chromeos::PowerManagerClient::Shutdown();
  }

  // Creates Adapter only, but its input may or may not be ready.
  void SetUpAdapter(const std::map<std::string, std::string>& params,
                    bool brightness_set_by_policy = false) {
    // Simulate the real clock that will not produce TimeTicks equal to 0.
    // This is because the Adapter will treat 0 TimeTicks are uninitialized
    // values.
    task_environment_.FastForwardBy(base::Seconds(1));
    sync_preferences::PrefServiceMockFactory factory;
    factory.set_user_prefs(base::WrapRefCounted(new TestingPrefStore()));
    scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
        new user_prefs::PrefRegistrySyncable);

    auto_screen_brightness::MetricsReporter::RegisterLocalStatePrefs(
        registry.get());

    sync_preferences::PrefServiceSyncable* regular_prefs =
        factory.CreateSyncable(registry.get()).release();

    RegisterUserProfilePrefs(registry.get());
    if (brightness_set_by_policy) {
      regular_prefs->SetInteger(ash::prefs::kPowerAcScreenBrightnessPercent,
                                10);
      regular_prefs->SetInteger(
          ash::prefs::kPowerBatteryScreenBrightnessPercent, 10);
    }

    TestingProfile::Builder profile_builder;
    profile_builder.SetProfileName("[email protected]");
    profile_builder.SetPrefService(base::WrapUnique(regular_prefs));

    profile_ = profile_builder.Build();

    if (!params.empty()) {
      scoped_feature_list_.InitAndEnableFeatureWithParameters(
          features::kAutoScreenBrightness, params);
    } else {
      scoped_feature_list_.InitAndDisableFeature(
          features::kAutoScreenBrightness);
    }

    adapter_ = Adapter::CreateForTesting(
        profile_.get(), als_reader_.get(), &fake_brightness_monitor_,
        &fake_modeller_, &fake_model_config_loader_,
        nullptr /* metrics_reporter */, task_environment_.GetMockTickClock());
    adapter_->Init();
    task_environment_.RunUntilIdle();
  }

  // Sets up all required input for Adapter and then creates Adapter.
  void Init(AlsReader::AlsInitStatus als_reader_status,
            BrightnessMonitor::Status brightness_monitor_status,
            const Model& model,
            const std::optional<ModelConfig>& model_config,
            const std::map<std::string, std::string>& params,
            bool brightness_set_by_policy = false) {
    fake_light_provider_->set_als_init_status(als_reader_status);
    fake_brightness_monitor_.set_status(brightness_monitor_status);
    fake_modeller_.InitModellerWithModel(model);
    if (model_config) {
      fake_model_config_loader_.set_model_config(model_config.value());
    }

    SetUpAdapter(params, brightness_set_by_policy);
  }

  void ReportSuspendDone() {
    chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
    task_environment_.RunUntilIdle();
  }

  void ReportLidEvent(chromeos::PowerManagerClient::LidState state) {
    chromeos::FakePowerManagerClient::Get()->SetLidState(
        state, task_environment_.NowTicks());
  }

  // Returns a valid ModelConfig.
  ModelConfig GetTestModelConfig(bool enabled = true) {
    ModelConfig model_config;
    model_config.auto_brightness_als_horizon_seconds = 5.0;
    model_config.enabled = enabled;
    model_config.log_lux = {
        3.69, 4.83, 6.54, 7.68, 8.25, 8.82,
    };
    model_config.brightness = {
        36.14, 47.62, 85.83, 93.27, 93.27, 100,
    };

    model_config.metrics_key = "abc";
    model_config.model_als_horizon_seconds = 3.0;
    return model_config;
  }

  void ReportAls(int als_value) {
    fake_light_provider_->ReportAmbientLightUpdate(als_value);
    task_environment_.RunUntilIdle();
  }

  void ReportUserBrightnessChangeRequest(double old_brightness_percent,
                                         double new_brightness_percent) {
    // Report a user-brightness-change-requested signal before a
    // user-brightness-changed signal to simulate the real brightness monitor.
    fake_brightness_monitor_.ReportUserBrightnessChangeRequested();
    fake_brightness_monitor_.ReportUserBrightnessChanged(
        old_brightness_percent, new_brightness_percent);
    task_environment_.RunUntilIdle();
  }

  // Forwards time first and then reports Als.
  void ForwardTimeAndReportAls(const std::vector<int>& als_values) {
    for (const int als_value : als_values) {
      // Forward 1 second to simulate the real AlsReader that samples data at
      // 1hz.
      task_environment_.FastForwardBy(base::Seconds(1));
      ReportAls(als_value);
    }
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;

  TestObserver test_observer_;

  std::unique_ptr<TestingProfile> profile_;

  std::optional<MonotoneCubicSpline> global_curve_;
  std::optional<MonotoneCubicSpline> personal_curve_;

  std::unique_ptr<FakeLightProvider> fake_light_provider_;
  std::unique_ptr<AlsReader> als_reader_;
  FakeBrightnessMonitor fake_brightness_monitor_;
  FakeModeller fake_modeller_;
  FakeModelConfigLoader fake_model_config_loader_;

  base::HistogramTester histogram_tester_;

  // |brightening_log_lux_threshold| and |darkening_log_lux_threshold| are set
  // to very small values so a slight change in ALS would trigger brightness
  // update. |stabilization_threshold| is set to a very high value so that we
  // don't have to check ALS has stablized.
  const std::map<std::string, std::string> default_params_ = {
      {"brightening_log_lux_threshold", "0.00001"},
      {"darkening_log_lux_threshold", "0.00001"},
      {"stabilization_threshold", "100000000"},
      {"user_adjustment_effect", "0"},
  };

  base::test::ScopedFeatureList scoped_feature_list_;

  std::unique_ptr<Adapter> adapter_;
};

// AlsReader is |kDisabled| when Adapter is created.
TEST_F(AdapterTest, AlsReaderDisabledOnInit) {
  Init(AlsReader::AlsInitStatus::kDisabled, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// BrightnessMonitor is |kDisabled| when Adapter is created.
TEST_F(AdapterTest, BrightnessMonitorDisabledOnInit) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kDisabled,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// Modeller is |kDisabled| when Adapter is created.
TEST_F(AdapterTest, ModellerDisabledOnInit) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(), GetTestModelConfig(), default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// ModelConfigLoader has an invalid config, hence Modeller is disabled.
TEST_F(AdapterTest, ModelConfigLoaderDisabledOnInit) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), ModelConfig(), default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// AlsReader is |kDisabled| on later notification.
TEST_F(AdapterTest, AlsReaderDisabledOnNotification) {
  Init(AlsReader::AlsInitStatus::kInProgress,
       BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_light_provider_->set_als_init_status(
      AlsReader::AlsInitStatus::kDisabled);
  fake_light_provider_->ReportReaderInitialized();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// AlsReader is |kSuccess| on later notification.
TEST_F(AdapterTest, AlsReaderEnabledOnNotification) {
  Init(AlsReader::AlsInitStatus::kInProgress,
       BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_light_provider_->set_als_init_status(AlsReader::AlsInitStatus::kSuccess);
  fake_light_provider_->ReportReaderInitialized();
  task_environment_.RunUntilIdle();

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_FALSE(adapter_->GetPersonalCurveForTesting());
}

// BrightnessMonitor is |kDisabled| on later notification.
TEST_F(AdapterTest, BrightnessMonitorDisabledOnNotification) {
  Init(AlsReader::AlsInitStatus::kSuccess,
       BrightnessMonitor::Status::kInitializing,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_brightness_monitor_.set_status(BrightnessMonitor::Status::kDisabled);
  fake_brightness_monitor_.ReportBrightnessMonitorInitialized();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// BrightnessMonitor is |kSuccess| on later notification.
TEST_F(AdapterTest, BrightnessMonitorEnabledOnNotification) {
  Init(AlsReader::AlsInitStatus::kSuccess,
       BrightnessMonitor::Status::kInitializing,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_brightness_monitor_.set_status(BrightnessMonitor::Status::kSuccess);
  fake_brightness_monitor_.ReportBrightnessMonitorInitialized();
  task_environment_.RunUntilIdle();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_FALSE(adapter_->GetPersonalCurveForTesting());
}

// Modeller is |kDisabled| on later notification.
TEST_F(AdapterTest, ModellerDisabledOnNotification) {
  fake_light_provider_->set_als_init_status(AlsReader::AlsInitStatus::kSuccess);
  fake_brightness_monitor_.set_status(BrightnessMonitor::Status::kSuccess);
  fake_model_config_loader_.set_model_config(GetTestModelConfig());
  SetUpAdapter(default_params_);
  task_environment_.RunUntilIdle();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_modeller_.InitModellerWithModel(Model());
  fake_modeller_.ReportModelInitialized();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
  EXPECT_FALSE(adapter_->GetGlobalCurveForTesting());
  EXPECT_FALSE(adapter_->GetPersonalCurveForTesting());
}

// Modeller is |kSuccess| on later notification.
TEST_F(AdapterTest, ModellerEnabledOnNotification) {
  fake_light_provider_->set_als_init_status(AlsReader::AlsInitStatus::kSuccess);
  fake_brightness_monitor_.set_status(BrightnessMonitor::Status::kSuccess);
  fake_model_config_loader_.set_model_config(GetTestModelConfig());
  SetUpAdapter(default_params_);
  task_environment_.RunUntilIdle();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_modeller_.InitModellerWithModel(
      Model(global_curve_, personal_curve_, 0));
  fake_modeller_.ReportModelInitialized();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());
  EXPECT_EQ(*adapter_->GetPersonalCurveForTesting(), *personal_curve_);
}

// ModelConfigLoader reports an invalid config on later notification.
TEST_F(AdapterTest, InvalidModelConfigOnNotification) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), std::nullopt, default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  // ModelConfig() creates an invalid config.
  DCHECK(!IsValidModelConfig(ModelConfig()));
  fake_model_config_loader_.set_model_config(ModelConfig());
  fake_model_config_loader_.ReportModelConfigLoaded();
  task_environment_.RunUntilIdle();

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);
}

// ModelConfigLoader reports a valid config on later notification.
TEST_F(AdapterTest, ValidModelConfigOnNotification) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), std::nullopt, default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kInitializing);

  fake_model_config_loader_.set_model_config(GetTestModelConfig());
  fake_model_config_loader_.ReportModelConfigLoaded();
  task_environment_.RunUntilIdle();

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_FALSE(adapter_->GetPersonalCurveForTesting());
}

// First ALS comes in 1 second after AlsReader is initialized. Hence after
// |auto_brightness_als_horizon_seconds|, brightness is changed.
TEST_F(AdapterTest, FirstAlsAfterAlsReaderInitTime) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  // |auto_brightness_als_horizon_seconds| is 5.
  ForwardTimeAndReportAls({1, 2, 3, 4});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

// First ALS comes in at the same time when AlsReader is initialized. Hence
// after |auto_brightness_als_horizon_seconds| + 1 readings, brightness is
// changed.
TEST_F(AdapterTest, FirstAlsAtAlsReaderInitTime) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  // First ALS when AlsReader is initialized.
  ReportAls(10);
  ForwardTimeAndReportAls({1, 2, 3, 4});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

TEST_F(AdapterTest, SequenceOfBrightnessUpdatesWithDefaultParams) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());
  EXPECT_EQ(*adapter_->GetPersonalCurveForTesting(), *personal_curve_);

  ForwardTimeAndReportAls({1, 2, 3, 4});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  // Brightness is changed for the first time after the 5th reading.
  ForwardTimeAndReportAls({5});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Several other ALS readings come in, but need to wait for
  // |params.auto_brightness_als_horizon_seconds| to pass before having any
  // effect
  ForwardTimeAndReportAls({20});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({30});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({40});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({50});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // The next ALS reading triggers brightness change.
  ForwardTimeAndReportAls({60});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({20, 30, 40, 50, 60},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // |params.auto_brightness_als_horizon_seconds| has elapsed since we've made
  // the change, but there's no new ALS value, hence no brightness change is
  // triggered.
  task_environment_.FastForwardBy(base::Seconds(10));
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({20, 30, 40, 50, 60},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  EXPECT_EQ(adapter_->GetAverageAmbientWithStdDevForTesting(
                task_environment_.NowTicks()),
            std::nullopt);

  // A new ALS value triggers a brightness change.
  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 3);
  CheckAvgLog({100}, adapter_->GetCurrentAvgLogAlsForTesting().value());
}

// A user brightness change comes in when ALS readings exist. This also disables
// the adapter because |user_adjustment_effect| is 0 (disabled).
TEST_F(AdapterTest, UserBrightnessChangeAlsReadingExists) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  ForwardTimeAndReportAls({1, 2, 3, 4});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  // Adapter will not be applied after a user manual adjustment.
  ReportUserBrightnessChangeRequest(20.0, 30.0);

  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", false, 1);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());
  CheckAvgLog({1, 2, 3, 4}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // An als reading comes in but will not change the brightness.
  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  CheckAvgLog({1, 2, 3, 4}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another user manual adjustment comes in.
  task_environment_.FastForwardBy(base::Seconds(1));
  ReportUserBrightnessChangeRequest(30.0, 40.0);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());
  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", false, 2);
  CheckAvgLog({2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

// Same as |UserBrightnessChangeAlsReadingExists| except that user adjustment
// effect is Continue.
TEST_F(AdapterTest, UserBrightnessChangeAlsReadingExistsContinue) {
  std::map<std::string, std::string> params = default_params_;
  // UserAdjustmentEffect::kContinueAuto = 2.
  params["user_adjustment_effect"] = "2";
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  ForwardTimeAndReportAls({2, 4, 6, 8});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  // User brightness change comes in.
  ReportUserBrightnessChangeRequest(20.0, 30.0);
  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", false, 1);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());
  EXPECT_EQ(test_observer_.num_changes(), 0);
  CheckAvgLog({2, 4, 6, 8}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Four ALS readings come in, but not enough time has passed since user
  // brightness change.
  ForwardTimeAndReportAls({4, 6, 8, 2});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  CheckAvgLog({2, 4, 6, 8}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another ALS reading is in but brightness isn't changed because there's no
  // new curve.
  ForwardTimeAndReportAls({5});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  CheckAvgLog({2, 4, 6, 8}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another model comes in.
  fake_modeller_.ReportModelTrained(*personal_curve_);
  EXPECT_EQ(test_observer_.num_changes(), 0);
  CheckAvgLog({2, 4, 6, 8}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another ALS reading is in and brightness is changed this time.
  ForwardTimeAndReportAls({15});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({6, 8, 2, 5, 15},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another user manual adjustment comes in.
  task_environment_.FastForwardBy(base::Seconds(1));
  ReportUserBrightnessChangeRequest(30.0, 40.0);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());
  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", false, 3);
  CheckAvgLog({8, 2, 5, 15}, adapter_->GetCurrentAvgLogAlsForTesting().value());
}

// Same as |UserBrightnessChangeAlsReadingExists| except that the 1st user
// brightness change comes when there is no ALS reading.
TEST_F(AdapterTest, UserBrightnessChangeAlsReadingAbsent) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  // Adapter will not be applied after a user manual adjustment.
  ReportUserBrightnessChangeRequest(20.0, 30.0);

  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", true, 1);
  EXPECT_EQ(adapter_->GetCurrentAvgLogAlsForTesting(), std::nullopt);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());
  EXPECT_FALSE(adapter_->GetCurrentAvgLogAlsForTesting());

  // ALS readings come in but will not change the brightness.
  ForwardTimeAndReportAls({100, 101, 102, 103, 104});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  EXPECT_FALSE(adapter_->GetCurrentAvgLogAlsForTesting());

  // Another user manual adjustment comes in.
  task_environment_.FastForwardBy(base::Seconds(1));
  ReportUserBrightnessChangeRequest(30.0, 40.0);
  histogram_tester_.ExpectBucketCount(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", true, 1);
  histogram_tester_.ExpectBucketCount(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", false, 1);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());
  CheckAvgLog({101, 102, 103, 104},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

// Same as |UserBrightnessChangeAlsReadingAbsent| except that user adjustment
// effect is Continue.
TEST_F(AdapterTest, UserBrightnessChangeAlsReadingAbsentContinue) {
  std::map<std::string, std::string> params = default_params_;
  // UserAdjustmentEffect::kContinueAuto = 2.
  params["user_adjustment_effect"] = "2";
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  ReportUserBrightnessChangeRequest(20.0, 30.0);

  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", true, 1);
  EXPECT_EQ(adapter_->GetCurrentAvgLogAlsForTesting(), std::nullopt);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());
  EXPECT_FALSE(adapter_->GetCurrentAvgLogAlsForTesting());

  // ALS readings come in, and will not trigger a brightness change because
  // there is no new model.
  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  ForwardTimeAndReportAls({101, 102, 103, 104});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  // Another user manual adjustment comes in.
  task_environment_.FastForwardBy(base::Seconds(1));
  ReportUserBrightnessChangeRequest(30.0, 40.0);
  histogram_tester_.ExpectBucketCount(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", true, 1);
  histogram_tester_.ExpectBucketCount(
      "AutoScreenBrightness.MissingAlsWhenBrightnessChanged", false, 1);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());
}

// Set |brightening_log_lux_threshold| to a very high value to effectively make
// brightening impossible.
TEST_F(AdapterTest, BrighteningThreshold) {
  std::map<std::string, std::string> params = default_params_;
  params["brightening_log_lux_threshold"] = "100";
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());
  EXPECT_EQ(*adapter_->GetPersonalCurveForTesting(), *personal_curve_);

  ForwardTimeAndReportAls({1, 2, 3, 4});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  ForwardTimeAndReportAls({5});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 100);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 0.00001);

  ForwardTimeAndReportAls({4, 4, 4, 4, 4});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 100);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 0.00001);

  // Darkening is still possible.
  ForwardTimeAndReportAls({1});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 100);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 0.00001);

  ForwardTimeAndReportAls({1});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({4, 4, 4, 1, 1},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 100);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 0.00001);
}

// Set |darkening_log_lux_threshold| to a very high value to effectively make
// darkening impossible.
TEST_F(AdapterTest, DarkeningThreshold) {
  std::map<std::string, std::string> params = default_params_;
  params["darkening_log_lux_threshold"] = "100";
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  ForwardTimeAndReportAls({10, 20, 30, 40});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  ForwardTimeAndReportAls({50});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({10, 20, 30, 40, 50},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 0.00001);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 100);

  ForwardTimeAndReportAls({25, 25, 25, 25, 25});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({10, 20, 30, 40, 50},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 0.00001);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 100);

  ForwardTimeAndReportAls({40});
  CheckAvgLog({25, 25, 25, 25, 40},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() + 0.00001);
  EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                   adapter_->GetCurrentAvgLogAlsForTesting().value() - 100);
}

// Set |stabilization_threshold| to a very low value so that the average really
// should have little fluctuations before we change brightness.
TEST_F(AdapterTest, StablizationThreshold) {
  std::map<std::string, std::string> params = default_params_;
  params["stabilization_threshold"] = "0.00001";
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  ForwardTimeAndReportAls({10, 20, 30, 40, 50});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({10, 20, 30, 40, 50},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // A fluctuation means brightness is not changed.
  ForwardTimeAndReportAls({29, 29, 29, 29, 20});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({10, 20, 30, 40, 50},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({20, 20, 20, 20});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({20, 20, 20, 20, 20},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

// Shorten |auto_brightness_als_horizon| to 1 second. Averaging period is
// shorter and |stabilization_threshold| is ineffective in regularizing
// stabilization.
TEST_F(AdapterTest, AlsHorizon) {
  std::map<std::string, std::string> params = default_params_;
  // Small |stabilization_threshold|.
  params["stabilization_threshold"] = "0.00001";
  ModelConfig test_config = GetTestModelConfig();
  test_config.auto_brightness_als_horizon_seconds = 1;

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), test_config, params);

  ForwardTimeAndReportAls({10});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({10}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({100}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({2});
  EXPECT_EQ(test_observer_.num_changes(), 3);
  CheckAvgLog({2}, adapter_->GetCurrentAvgLogAlsForTesting().value());
}

TEST_F(AdapterTest, UseLatestCurve) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, std::nullopt, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  ForwardTimeAndReportAls({1, 2, 3, 4, 5});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Brightness is changed according to the global curve.
  EXPECT_DOUBLE_EQ(test_observer_.GetBrightnessPercent(),
                   global_curve_->Interpolate(
                       adapter_->GetCurrentAvgLogAlsForTesting().value()));

  // A new personal curve is received but adapter still uses the global curve.
  task_environment_.FastForwardBy(base::Seconds(20));
  fake_modeller_.ReportModelTrained(*personal_curve_);
  ReportAls(20);
  EXPECT_EQ(test_observer_.num_changes(), 2);
  EXPECT_EQ(test_observer_.GetCause(),
            power_manager::BacklightBrightnessChange_Cause_MODEL);

  // Brightness is changed according to the new personal curve.
  EXPECT_DOUBLE_EQ(test_observer_.GetBrightnessPercent(),
                   personal_curve_->Interpolate(
                       adapter_->GetCurrentAvgLogAlsForTesting().value()));
}

TEST_F(AdapterTest, BrightnessSetByPolicy) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_, true /* brightness_set_by_policy */);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);

  ForwardTimeAndReportAls({1, 2, 3, 4, 5, 6, 7, 8});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  EXPECT_EQ(adapter_->GetCurrentAvgLogAlsForTesting(), std::nullopt);
}

TEST_F(AdapterTest, FeatureDisabled) {
  // An empty param map will not enable the experiment.
  std::map<std::string, std::string> empty_params;

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       empty_params);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);

  // Global and personal curves are received, but they won't be used to change
  // brightness.
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());

  // No brightness is changed.
  ForwardTimeAndReportAls({1, 2, 3, 4, 5, 6, 7, 8});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  EXPECT_EQ(adapter_->GetCurrentAvgLogAlsForTesting(), std::nullopt);
}

TEST_F(AdapterTest, FeatureEnabledConfigDisabled) {
  // Feature flag is enabled, but model config is disabled. Final effect is
  // disabled.
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0),
       GetTestModelConfig(false /* enabled */), default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kDisabled);

  // Global and personal curves are received, but they won't be used to change
  // brightness.
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());

  // No brightness is changed.
  ForwardTimeAndReportAls({1, 2, 3, 4, 5, 6, 7, 8});
  EXPECT_EQ(test_observer_.num_changes(), 0);
  EXPECT_EQ(adapter_->GetCurrentAvgLogAlsForTesting(), std::nullopt);
}

TEST_F(AdapterTest, ValidParameters) {
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  histogram_tester_.ExpectTotalCount("AutoScreenBrightness.ParameterError", 0);
}

TEST_F(AdapterTest, InvalidParameters) {
  std::map<std::string, std::string> params = default_params_;
  params["user_adjustment_effect"] = "10";

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  histogram_tester_.ExpectUniqueSample(
      "AutoScreenBrightness.ParameterError",
      static_cast<int>(ParameterError::kAdapterError), 1);
}

TEST_F(AdapterTest, UserAdjustmentEffectDisable) {
  // |default_params_| sets the effect to disable.
  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(),
       default_params_);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());
  EXPECT_EQ(*adapter_->GetPersonalCurveForTesting(), *personal_curve_);

  // Brightness is changed for the 1st time.
  ForwardTimeAndReportAls({1, 2, 3, 4, 5});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Adapter will not be applied after a user manual adjustment.
  ReportUserBrightnessChangeRequest(20.0, 30.0);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());

  fake_modeller_.ReportModelTrained(*personal_curve_);
  ForwardTimeAndReportAls({6, 7, 8, 9, 10, 11});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // SuspendDone is received, which does not enable Adapter.
  ReportSuspendDone();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());

  ForwardTimeAndReportAls({11, 12, 13, 14, 15, 16});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

TEST_F(AdapterTest, UserAdjustmentEffectPause) {
  std::map<std::string, std::string> params = default_params_;
  // UserAdjustmentEffect::kPauseAuto = 1.
  params["user_adjustment_effect"] = "1";

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());
  EXPECT_EQ(*adapter_->GetPersonalCurveForTesting(), *personal_curve_);

  // Brightness is changed for the 1st time.
  ForwardTimeAndReportAls({1, 2, 3, 4, 5});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // User manually changes brightness so that adapter will not be applied.
  ReportUserBrightnessChangeRequest(20.0, 30.0);
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());

  // New ALS data will not trigger brightness update.
  ForwardTimeAndReportAls({101, 102, 103, 104, 105, 106, 107, 108});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // SuspendDone is received, which re-enables adapter.
  ReportSuspendDone();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());

  // Another ALS comes in but brightness isn't changed because there's no new
  // curve.
  ForwardTimeAndReportAls({109});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // A new model is received.
  fake_modeller_.ReportModelTrained(*personal_curve_);
  EXPECT_EQ(test_observer_.num_changes(), 1);
  ForwardTimeAndReportAls({110});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({106, 107, 108, 109, 110},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another user brightness change.
  ReportUserBrightnessChangeRequest(40.0, 50.0);
  CheckAvgLog({106, 107, 108, 109, 110},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_FALSE(adapter_->IsAppliedForTesting());

  // New ALS data will not trigger brightness update.
  ForwardTimeAndReportAls({200});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({106, 107, 108, 109, 110},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // SuspendDone is received, which reenables adapter.
  ReportSuspendDone();
  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());

  // A new model is in.
  fake_modeller_.ReportModelTrained(*personal_curve_);

  // Als readings come in but not sufficient time since user changed brightness.
  ForwardTimeAndReportAls({201, 202, 203});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({106, 107, 108, 109, 110},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({204});
  EXPECT_EQ(test_observer_.num_changes(), 3);
  CheckAvgLog({200, 201, 202, 203, 204},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

TEST_F(AdapterTest, UserAdjustmentEffectContinue) {
  std::map<std::string, std::string> params = default_params_;
  // UserAdjustmentEffect::kContinueAuto = 2.
  params["user_adjustment_effect"] = "2";

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
  EXPECT_EQ(*adapter_->GetGlobalCurveForTesting(), *global_curve_);
  EXPECT_TRUE(adapter_->GetPersonalCurveForTesting());
  EXPECT_EQ(*adapter_->GetPersonalCurveForTesting(), *personal_curve_);

  // Brightness is changed for the 1st time.
  ForwardTimeAndReportAls({1, 2, 3, 4, 5});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 5},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({10});
  // User manual adjustment doesn't disable adapter.
  ReportUserBrightnessChangeRequest(40.0, 50.0);
  CheckAvgLog({2, 3, 4, 5, 10},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
  EXPECT_TRUE(adapter_->IsAppliedForTesting());

  ForwardTimeAndReportAls({100, 101, 102, 103});
  CheckAvgLog({2, 3, 4, 5, 10},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({104});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({2, 3, 4, 5, 10},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

TEST_F(AdapterTest, LidEvents) {
  std::map<std::string, std::string> params = default_params_;
  params["lid_open_delay_time_seconds"] = "3";
  ModelConfig test_config = GetTestModelConfig();
  test_config.auto_brightness_als_horizon_seconds = 3;

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), test_config, params);

  // |auto_brightness_als_horizon_seconds| is 3.
  ForwardTimeAndReportAls({1, 2});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 100}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ReportLidEvent(chromeos::PowerManagerClient::LidState::CLOSED);

  // All ALS values that arrive after lid is closed are ignored.
  ForwardTimeAndReportAls({0});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 100}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({200});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 100}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);

  // ALS readings that arrive in the next 2 seconds will be ignored because
  // |lid_open_delay_time_seconds| is set to 3 seconds.
  ForwardTimeAndReportAls({300});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 100}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({400});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 100}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Another ALS reading arrives 2 seconds after lid-open. Brightness is changed
  // immediately. As earlier ALS readings were cleared when lid was closed, only
  // one ALS reading is used to calculate brightness.
  ForwardTimeAndReportAls({500});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({500}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Next two ALS readings won't change brightness because we are waiting for
  // averaging period |auto_brightness_als_horizon_seconds| to pass.
  ForwardTimeAndReportAls({600});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({500}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({700});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({500}, adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Averaging period has passed, so brightness is changed.
  ForwardTimeAndReportAls({800});
  EXPECT_EQ(test_observer_.num_changes(), 3);
  CheckAvgLog({600, 700, 800},
              adapter_->GetCurrentAvgLogAlsForTesting().value());
}

TEST_F(AdapterTest, SuspendDueToLidClosed) {
  std::map<std::string, std::string> params = default_params_;
  // UserAdjustmentEffect::kPauseAuto = 1.
  params["user_adjustment_effect"] = "1";
  params["lid_open_delay_time_seconds"] = "2";

  Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
       Model(global_curve_, personal_curve_, 0), GetTestModelConfig(), params);

  // |auto_brightness_als_horizon_seconds| is 5.
  ForwardTimeAndReportAls({1, 2, 3, 4});
  EXPECT_EQ(test_observer_.num_changes(), 0);

  ForwardTimeAndReportAls({100});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  // Lid is closed and triggers a suspend (no need to report suspend here).
  ReportLidEvent(chromeos::PowerManagerClient::LidState::CLOSED);
  ForwardTimeAndReportAls({0});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({200});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
  ReportSuspendDone();

  // First ALS reading that arrives after lid-open will be ignored because
  // |lid_open_delay_time_seconds| is set to 2 seconds.
  ForwardTimeAndReportAls({300});
  EXPECT_EQ(test_observer_.num_changes(), 1);
  CheckAvgLog({1, 2, 3, 4, 100},
              adapter_->GetCurrentAvgLogAlsForTesting().value());

  ForwardTimeAndReportAls({400});
  EXPECT_EQ(test_observer_.num_changes(), 2);
  CheckAvgLog({400}, adapter_->GetCurrentAvgLogAlsForTesting().value());
}

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