chromium/chromeos/ash/components/network/metrics/default_network_metrics_logger_unittest.cc

// Copyright 2024 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/metrics/default_network_metrics_logger.h"

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

const char kCellularGuid[] = "cellular_guid";
const char kCellularServicePath[] = "/service/network";
const char kCellularName[] = "cellular_name";

const char kWifiGuid[] = "wifi_guid";
const char kWifiServicePath[] = "/service/network2";
const char kWifiName[] = "wifi_name";

const char kEthernetGuid[] = "eth_guid";
const char kEthernetServicePath[] = "/service/network3";
const char kEthernetName[] = "eth_name";

const char kVpnGuid[] = "vpn_guid";
const char kVpnServicePath[] = "/service/network4";
const char kVpnName[] = "vpn_name";

}  // namespace

class DefaultNetworkMetricsLoggerTest : public testing::Test {
 public:
  DefaultNetworkMetricsLoggerTest() = default;
  DefaultNetworkMetricsLoggerTest(const DefaultNetworkMetricsLoggerTest&) =
      delete;
  DefaultNetworkMetricsLoggerTest& operator=(
      const DefaultNetworkMetricsLoggerTest&) = delete;
  ~DefaultNetworkMetricsLoggerTest() override = default;

  void SetUp() override {
    network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
    histogram_tester_ = std::make_unique<base::HistogramTester>();

    GetShillServiceClient()->ClearServices();
    base::RunLoop().RunUntilIdle();
  }

  void TearDown() override {
    GetShillServiceClient()->ClearServices();
    network_handler_test_helper_.reset();
  }

  void SetUpGenericCellularNetwork() {
    GetShillServiceClient()->AddService(kCellularServicePath, kCellularGuid,
                                        kCellularName, shill::kTypeCellular,
                                        shill::kStateIdle,
                                        /*visible=*/true);
    base::RunLoop().RunUntilIdle();
  }

  void SetUpGenericWifiNetwork() {
    GetShillServiceClient()->AddService(kWifiServicePath, kWifiGuid, kWifiName,
                                        shill::kTypeWifi, shill::kStateIdle,
                                        /*visible=*/true);
    base::RunLoop().RunUntilIdle();
  }

  void SetUpGenericEthernetNetwork() {
    GetShillServiceClient()->AddService(kEthernetServicePath, kEthernetGuid,
                                        kEthernetName, shill::kTypeEthernet,
                                        shill::kStateIdle,
                                        /*visible=*/true);
    base::RunLoop().RunUntilIdle();
  }

  void SetUpGenericVpnNetwork() {
    GetShillServiceClient()->AddService(kVpnServicePath, kVpnGuid, kVpnName,
                                        shill::kTypeVPN, shill::kStateIdle,
                                        /*visible=*/true);
    base::RunLoop().RunUntilIdle();
  }

  void SetShillState(const std::string& service_path,
                     const std::string& shill_state) {
    GetShillServiceClient()->SetServiceProperty(
        service_path, shill::kStateProperty, base::Value(shill_state));
    base::RunLoop().RunUntilIdle();
  }

  void SetShillMetered(const std::string& service_path, bool is_metered) {
    GetShillServiceClient()->SetServiceProperty(
        service_path, shill::kMeteredProperty, base::Value(is_metered));
    base::RunLoop().RunUntilIdle();
  }

 protected:
  ShillServiceClient::TestInterface* GetShillServiceClient() {
    return ShillServiceClient::Get()->GetTestInterface();
  }

  base::test::TaskEnvironment task_environment_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
};

TEST_F(DefaultNetworkMetricsLoggerTest, NetworkTechnologyMeterSubtypeChanges) {
  using NetworkTechnologyMeterSubtype =
      DefaultNetworkMetricsLogger::NetworkTechnologyMeterSubtype;

  SetUpGenericVpnNetwork();
  SetUpGenericCellularNetwork();
  SetUpGenericWifiNetwork();
  SetUpGenericEthernetNetwork();

  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 0);

  SetShillState(kEthernetServicePath, shill::kStateOnline);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 1);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kEthernet, 1);

  // Cellular becomes connected. Note that cellular is metered by default.
  // No impact on metrics because connected ethernet is higher in priority.
  SetShillState(kCellularServicePath, shill::kStateOnline);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 1);

  // Ethernet goes offline, so connected metered cellular becomes default.
  SetShillState(kEthernetServicePath, shill::kStateIdle);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 2);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kCellularMetered, 1);

  // Cellular becomes non-metered.
  SetShillMetered(kCellularServicePath, false);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 3);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kCellular, 1);

  // WiFi becomes online, which has higher priority than cellular.
  SetShillState(kWifiServicePath, shill::kStateOnline);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 4);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kWifi, 1);

  // WiFi becomes metered, which has higher priority than cellular.
  SetShillMetered(kWifiServicePath, true);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 5);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kWifiMetered, 1);

  // Ethernet becomes online, which has highest priority.
  SetShillState(kEthernetServicePath, shill::kStateOnline);
  // Connected WiFi becomes nonmetered. No effect on metrics since it's lower in
  // priority than connected Ethernet.
  SetShillMetered(kWifiServicePath, false);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 6);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kEthernet, 2);

  // Ethernet goes offline, so connected nonmetered WiFi becomes default.
  SetShillState(kEthernetServicePath, shill::kStateIdle);
  // Cellular becomes metered. No effect on metrics since it's lower in
  // priority than connected WiFi.
  SetShillMetered(kCellularServicePath, true);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 7);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kWifi, 2);

  // WiFi goes offline, so connected metered cellular becomes default.
  SetShillState(kWifiServicePath, shill::kStateIdle);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 8);
  histogram_tester_->ExpectBucketCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram,
      NetworkTechnologyMeterSubtype::kCellularMetered, 2);

  // VPN is ignored.
  SetShillState(kVpnServicePath, shill::kStateIdle);
  SetShillState(kVpnServicePath, shill::kStateOnline);
  histogram_tester_->ExpectTotalCount(
      DefaultNetworkMetricsLogger::kDefaultNetworkMeterSubtypeHistogram, 8);
}

}  // namespace ash