chromium/chromeos/ash/components/network/traffic_counters_handler_unittest.cc

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

#include "chromeos/ash/components/network/traffic_counters_handler.h"

#include <optional>
#include <string>

#include "base/functional/bind.h"
#include "base/test/task_environment.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_configuration_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_metadata_store.h"
#include "chromeos/ash/components/network/network_profile_handler.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash {
namespace traffic_counters {

namespace {

const char kDecLastResetTime[] = "Fri, 15 December 2023 10:00:00 UTC";

class TrafficCountersHandlerTest : public ::testing::Test {
 public:
  TrafficCountersHandlerTest()
      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
    //  TODO(b/278643115) Remove LoginState dependency.
    LoginState::Initialize();

    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        std::make_unique<user_manager::FakeUserManager>());

    feature_list_.InitWithFeatures(
      /*enabled_features=*/{features::kTrafficCountersEnabled,
      features::kTrafficCountersForWiFiTesting}, /*disabled_features=*/{});

    helper_ = std::make_unique<NetworkHandlerTestHelper>();
    helper_->AddDefaultProfiles();
    helper_->ResetDevicesAndServices();
    helper_->RegisterPrefs(user_prefs_.registry(), local_state_.registry());

    PrefProxyConfigTrackerImpl::RegisterProfilePrefs(user_prefs_.registry());
    PrefProxyConfigTrackerImpl::RegisterPrefs(local_state_.registry());
    helper_->InitializePrefs(&user_prefs_, &local_state_);

    NetworkHandler::Get()->managed_network_configuration_handler()->SetPolicy(
        ::onc::ONC_SOURCE_DEVICE_POLICY,
        /*userhash=*/std::string(),
        /*network_configs_onc=*/base::Value::List(),
        /*global_network_config=*/base::Value::Dict());

    task_environment_.RunUntilIdle();

    helper_->service_test()->SetTimeGetterForTest(base::BindRepeating(
        [](base::test::TaskEnvironment* env) {
          return env->GetMockClock()->Now();
        },
        &task_environment_));

    TrafficCountersHandler::InitializeForTesting();
    TrafficCountersHandler::Get()->SetTimeGetterForTesting(
      base::BindRepeating(
        [](base::test::TaskEnvironment* env) {
          return env->GetMockClock()->Now();
        },
        &task_environment_));

    SetUpWiFi();
  }

  ~TrafficCountersHandlerTest() override {
    TrafficCountersHandler::Shutdown();
    helper_.reset();
    scoped_user_manager_.reset();
    LoginState::Shutdown();
  }

  base::Time GetTime() { return task_environment_.GetMockClock()->Now(); }

 protected:
  base::Time SetLastResetTimeAndRun(const std::string& time_str) {
    base::Time reset_time = GetTimeFromString(time_str);
    double last_reset_time_ms =
        reset_time.ToDeltaSinceWindowsEpoch().InMilliseconds();
    SetServiceProperty(wifi_path_, shill::kTrafficCounterResetTimeProperty,
                       base::Value(last_reset_time_ms));
    task_environment_.RunUntilIdle();
    RunTrafficCountersHandler();
    return reset_time;
  }

  base::Time GetLastResetTime() {
    double traffic_counter_reset_time_ms =
        helper_
            ->GetServiceDoubleProperty(wifi_path_,
                                       shill::kTrafficCounterResetTimeProperty)
            .value();
    return base::Time::FromDeltaSinceWindowsEpoch(
        base::Milliseconds(traffic_counter_reset_time_ms));
  }

  void SetResetDay(int user_specified_day) {
    NetworkHandler::Get()
        ->network_metadata_store()
        ->SetDayOfTrafficCountersAutoReset(wifi_guid_, user_specified_day);
    task_environment_.RunUntilIdle();
  }

  base::Time AdvanceClockTo(const std::string& time_str) {
    base::Time time = GetTimeFromString(time_str);
    task_environment_.AdvanceClock(
        time.ToDeltaSinceWindowsEpoch() -
        task_environment_.GetMockClock()->Now().ToDeltaSinceWindowsEpoch());
    task_environment_.RunUntilIdle();
    return task_environment_.GetMockClock()->Now();
  }

  void RunTrafficCountersHandler() {
    TrafficCountersHandler::Get()->RunForTesting();
    task_environment_.RunUntilIdle();
  }

  void FastForwardBy(const base::TimeDelta& days) {
    task_environment_.FastForwardBy(days);
  }

  void RunUntilIdle() { task_environment_.RunUntilIdle(); }

  TrafficCountersHandler* traffic_counters_handler() {
    return TrafficCountersHandler::Get();
  }

  const std::string& wifi_guid() const { return wifi_guid_; }
  const std::string& wifi_path() const { return wifi_path_; }

 private:
  void SetUpWiFi() {
    ASSERT_TRUE(wifi_path_.empty());
    // By default, NetworkStateTestHelper already adds a WiFi device, so, we
    // do not need to add one here. All that remains to be done is configuring
    // the WiFi service.
    wifi_guid_ = "wifi_guid";
    wifi_path_ = helper_->ConfigureService(
        R"({"GUID": "wifi_guid", "Type": "wifi", "State": "idle",
            "SSID": "wifi", "Strength": 100, "AutoConnect": true,
            "WiFi.HiddenSSID": false, "TrafficCounterResetTime": 0})");
    SetServiceProperty(wifi_path_, shill::kStateProperty,
                       base::Value(shill::kStateOnline));
    helper_->profile_test()->AddService(
        NetworkProfileHandler::GetSharedProfilePath(), wifi_path_);
    task_environment_.RunUntilIdle();
  }

  base::Time GetTimeFromString(const std::string& time_str) {
    base::Time time;
    EXPECT_TRUE(base::Time::FromString(time_str.c_str(), &time));
    return time;
  }

  void SetServiceProperty(const std::string& service_path,
                          const std::string& key,
                          const base::Value& value) {
    helper_->SetServiceProperty(service_path, key, value);
  }

  // Member order declaration done in a way so that members outlive those that
  // are dependent on them.
  base::test::TaskEnvironment task_environment_;
  base::test::ScopedFeatureList feature_list_;
  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
  std::unique_ptr<NetworkHandlerTestHelper> helper_;
  sync_preferences::TestingPrefServiceSyncable user_prefs_;
  TestingPrefServiceSimple local_state_;
  std::string wifi_path_;
  std::string wifi_guid_;
};

}  // namespace

TEST_F(TrafficCountersHandlerTest, GetLastResetTime) {
  // Set the task_environment time to |kDecLastResetTime, ensuring that
  // the base::Now() (simulated "current" time) returns this value.
  AdvanceClockTo(kDecLastResetTime);

  base::Time reset_time =
      SetLastResetTimeAndRun(kDecLastResetTime);
  EXPECT_EQ(GetLastResetTime(), reset_time);
}

TEST_F(TrafficCountersHandlerTest, SetUserSpecifiedResetDay) {
  RunTrafficCountersHandler();
  NetworkHandler::Get()
      ->network_metadata_store()
      ->SetDayOfTrafficCountersAutoReset(wifi_guid(), 13);
  RunUntilIdle();
  const base::Value* reset_day_value =
      NetworkHandler::Get()
          ->network_metadata_store()
          ->GetDayOfTrafficCountersAutoReset(wifi_guid());
  ASSERT_TRUE(reset_day_value && reset_day_value->is_int());
  EXPECT_EQ(reset_day_value->GetInt(), 13);
}

TEST_F(TrafficCountersHandlerTest, FirstOfMonth) {
  AdvanceClockTo(kDecLastResetTime);
  base::Time reset_time =
      SetLastResetTimeAndRun(kDecLastResetTime);
  EXPECT_EQ(GetLastResetTime(), reset_time);

  SetResetDay(1);

  // Advance the clock to Jan 1st, 2024. The counters should get reset.
  base::Time simulated_time1 =
      AdvanceClockTo("Mon, 1 January 2024 07:01:00 UTC");

  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time1);
}

TEST_F(TrafficCountersHandlerTest, EndOfMonth) {
  const std::string last_reset_time_string = "Fri, 1 March 2024 07:01:00 UTC";
  AdvanceClockTo(last_reset_time_string);
  SetLastResetTimeAndRun(last_reset_time_string);

  // Adjust the user specified day to the 15th and ensure that a reset occurs
  // on March 15th.
  SetResetDay(15);
  base::Time simulated_time = AdvanceClockTo("Fri, 15 March 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Adjust the user specified day to the 14th and fast forward by 1 day to
  // March 16th. Ensure that no reset occurs on March 16th.
  SetResetDay(14);
  FastForwardBy(base::Days(1));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Adjust the user specified day to the 31st and Fast forward to Mar 31st.
  // Ensure that a reset occurs.
  SetResetDay(31);
  simulated_time = AdvanceClockTo("Sun, 31 March 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Advance the clock to Apr 30th and ensure auto reset occurs on April
  // 30th since April 31st does not exist.
  simulated_time = AdvanceClockTo("Tue, 30 Apr 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Fast forward by 1 day to May 1st and ensure no auto reset occurs.
  FastForwardBy(base::Days(1));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Advance the clock to May 31st and ensure reset occurs.
  simulated_time = AdvanceClockTo("Fri, 31 May 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);
}

TEST_F(TrafficCountersHandlerTest, LeapYear) {
  // Set last reset time.
  AdvanceClockTo(kDecLastResetTime);
  base::Time reset_time =
      SetLastResetTimeAndRun(kDecLastResetTime);
  EXPECT_EQ(GetLastResetTime(), reset_time);

  // The first traffic counters reset is expected on Jan 1st.
  SetResetDay(1);
  base::Time simulated_time =
      AdvanceClockTo("Mon, 1 January 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Fast forwarding the date by 15 days to Jan 16th should not affect the
  // last reset time.
  FastForwardBy(base::Days(15));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Fast forwarding the date to Feb 1st should update the last reset time.
  simulated_time = AdvanceClockTo("Thu, 1 February 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding the date by 28 days to Feb 29th, ensure February
  // 29th on leap years does not reset traffic counters when the user
  // specified day is the 1st.
  FastForwardBy(base::Days(28));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding the date to Mar 1st, ensure that two auto
  // traffic counter resets do not occur on the same day.
  // First, ensure traffic counters are reset on March 1st.
  simulated_time = AdvanceClockTo("Fri, 1 March 2024 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Second, run TrafficCountersHandler later on Mar 1st to ensure
  // that another reset doesn't occur.
  FastForwardBy(base::Hours(12));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);
}

TEST_F(TrafficCountersHandlerTest, NoLeapYear) {
  // Set last reset time.
  const std::string last_reset_time_string =
    "Sat, 15 January 2023 10:00:00 UTC";
  AdvanceClockTo(last_reset_time_string);
  base::Time reset_time =
      SetLastResetTimeAndRun(last_reset_time_string);
  EXPECT_EQ(GetLastResetTime(), reset_time);

  // Set the reset day.
  SetResetDay(31);

  // Adjust the clock to set the "current time". Use Jan 31st, 2023
  // to test whether the functionality is correct on non-leap years.
  base::Time simulated_time =
      AdvanceClockTo("Tue, 31 January 2023 07:01:00 UTC");
  RunTrafficCountersHandler();

  // The first traffic counters reset is expected on Jan 31st.
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding the date to Feb 28th, ensure traffic
  // counters are reset on February 28th on non-leap years when the user
  // specified day is the 31st.
  simulated_time = AdvanceClockTo("Tue, 28 February 2023 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding the date by 1 day to Mar 1st, ensure traffic
  // counters are not reset again.
  FastForwardBy(base::Days(1));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding the date by 27 days to March 28th, ensure a
  // traffic counter reset has not occurred.
  FastForwardBy(base::Days(27));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding by 2 days to March 30th, ensure a traffic counter
  // reset has not occurred.
  FastForwardBy(base::Days(2));
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // After fast forwarding the date by 1 day to March 31st, ensure a traffic
  // counter reset occurs.
  simulated_time = AdvanceClockTo("Fri, 31 March 2023 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);
}

TEST_F(TrafficCountersHandlerTest, ChangeUserSpecifiedDate) {
  // Set last reset time.
  const std::string last_reset_time_string =
    "Sat, 15 January 2023 10:00:00 UTC";
  AdvanceClockTo(last_reset_time_string);
  base::Time reset_time =
      SetLastResetTimeAndRun(last_reset_time_string);
  EXPECT_EQ(GetLastResetTime(), reset_time);

  // Advancing the date to Jan 31st should reset the traffic counters.
  SetResetDay(31);
  base::Time simulated_time =
      AdvanceClockTo("Tue, 31 January 2023 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Change user specified auto reset day.
  SetResetDay(5);
  RunTrafficCountersHandler();

  // After fast forwarding the date to February 5th, ensure traffic counters
  // are reset.
  simulated_time = AdvanceClockTo("Sun, 5 February 2023 07:01:00 UTC");
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);
}

TEST_F(TrafficCountersHandlerTest, RunFromStart) {
  // Set last reset time.
  const std::string last_reset_time_string =
    "Sat, 15 January 2023 10:00:00 UTC";
  AdvanceClockTo(last_reset_time_string);
  base::Time reset_time =
      SetLastResetTimeAndRun(last_reset_time_string);
  EXPECT_EQ(GetLastResetTime(), reset_time);

  // Set the reset day.
  SetResetDay(1);

  // Start the traffic counters timer.
  traffic_counters_handler()->StartAutoResetForTesting();
  RunUntilIdle();

  // Advance the clock to Jan 2nd, 2024. The timer should run and the
  // counters should get reset.
  base::Time simulated_time =
      AdvanceClockTo("Tue, 2 January 2024 07:01:00 UTC");
  EXPECT_EQ(GetLastResetTime(), simulated_time);
}

TEST_F(TrafficCountersHandlerTest, FastForward40Days) {
  // Set the clock to the specified time and then run traffic counters.
  base::Time simulated_time =
      AdvanceClockTo("Sun, 5 February 2023 07:01:00 UTC");
  RunTrafficCountersHandler();

  // Since no last_reset_time was available in the model, a reset should
  // occur at the time specified above.
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Simulate a device shutdown for 40 days.
  simulated_time = AdvanceClockTo("2023-03-17 07:01:00.000000 UTC");

  // Once device is up and running, run TrafficCountersHandler and confirm
  // that reset date is correct.
  RunTrafficCountersHandler();
  EXPECT_EQ(GetLastResetTime(), simulated_time);
}

TEST_F(TrafficCountersHandlerTest, ChangeResetDayToDayGreaterThanCurrentDay)
{
  // Set last reset time.
  base::Time simulated_time = AdvanceClockTo(kDecLastResetTime);

  // Start the traffic counters timer.
  traffic_counters_handler()->StartAutoResetForTesting();
  RunUntilIdle();

  // Confirm the reset time.
  EXPECT_EQ(GetLastResetTime(), simulated_time);

  // Set the reset day.
  SetResetDay(20);

  // Set simulated time.
  const std::string current_time = "Wed, 20 December 2023 10:00:00 UTC";
  simulated_time = AdvanceClockTo(current_time);
  base::Time reset_time = SetLastResetTimeAndRun(current_time);
  EXPECT_EQ(GetLastResetTime(), reset_time);
}

TEST_F(TrafficCountersHandlerTest, RequestTrafficCounters) {
  base::RunLoop run_loop;
  traffic_counters_handler()->RequestTrafficCounters(
    wifi_path(),
    base::BindOnce(
    [](base::OnceClosure quit_closure,
      std::optional<base::Value> traffic_counters) {
      EXPECT_TRUE(traffic_counters.has_value() && traffic_counters->is_list() &&
                  !traffic_counters->GetList().empty());
      std::move(quit_closure).Run();
    },
    run_loop.QuitClosure()));
    run_loop.Run();
}

TEST_F(TrafficCountersHandlerTest, ResetTrafficCounters) {
  traffic_counters_handler()->ResetTrafficCounters(wifi_path());
  RunUntilIdle();

  base::RunLoop run_loop;
  traffic_counters_handler()->RequestTrafficCounters(
    wifi_path(),
    base::BindOnce(
    [](base::OnceClosure quit_closure,
      std::optional<base::Value> traffic_counters) {
      EXPECT_TRUE(traffic_counters.has_value() && traffic_counters->is_list() &&
                  traffic_counters->GetList().empty());
      std::move(quit_closure).Run();
    },
    run_loop.QuitClosure()));
    run_loop.Run();
}

}  // namespace traffic_counters

}  // namespace ash