chromium/chrome/browser/ash/app_mode/metrics/network_connectivity_metrics_service_unittest.cc

// Copyright 2022 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/app_mode/metrics/network_connectivity_metrics_service.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/sync_wifi/network_test_helper.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash {

class NetworkConnectivityMetricsServiceTest : public testing::Test {
 public:
  NetworkConnectivityMetricsServiceTest()
      : local_state_(std::make_unique<ScopedTestingLocalState>(
            TestingBrowserProcess::GetGlobal())) {}

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

  TestingPrefServiceSimple* local_state() { return local_state_->Get(); }

  void SetUp() override {
    helper_.SetUp();
    histogram_tester_ = std::make_unique<base::HistogramTester>();
  }
  void TearDown() override {
    local_state()->RemoveUserPref(prefs::kKioskMetrics);
  }
  NetworkStateHandler* network_state_handler() {
    return NetworkHandler::Get()->network_state_handler();
  }
  base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }

  std::optional<int> GetNetworkDropsFromLocalState() {
    return local_state()
        ->GetDict(prefs::kKioskMetrics)
        .FindInt(kKioskNetworkDrops);
  }

  void SimulateConnectionFailure(const NetworkState* network,
                                 std::string error) {
    SetNetworkProperty(network->path(), shill::kStateProperty,
                       shill::kStateConfiguration);

    SetNetworkProperty(network->path(), shill::kErrorProperty, error);
    SetNetworkProperty(network->path(), shill::kStateProperty,
                       shill::kStateFailure);
  }

  const NetworkState* SimulateConnectionSuccess() {
    const NetworkState* network = CreateNetwork();

    SetNetworkProperty(network->path(), shill::kStateProperty,
                       shill::kStateOnline);
    return network;
  }

  const NetworkState* CreateNetwork() {
    std::string guid = helper_.ConfigureWiFiNetwork(
        "ssid", /*is_secured=*/true, helper_.primary_user(),
        /*has_connected=*/true,
        /*owned_by_user=*/true, /*configured_by_sync=*/true);
    return helper_.network_state_helper()
        .network_state_handler()
        ->GetNetworkStateFromGuid(guid);
  }

 private:
  void SetNetworkProperty(const std::string& service_path,
                          const std::string& key,
                          const std::string& value) {
    helper_.network_state_test_helper()->SetServiceProperty(service_path, key,
                                                            base::Value(value));
    task_environment_.RunUntilIdle();
  }

  base::test::SingleThreadTaskEnvironment task_environment_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
  sync_wifi::NetworkTestHelper helper_;
  std::unique_ptr<ScopedTestingLocalState> local_state_;
};

TEST_F(NetworkConnectivityMetricsServiceTest, StartNotInitialized) {
  auto service =
      NetworkConnectivityMetricsService::CreateForTesting(local_state());
  EXPECT_TRUE(network_state_handler()->HasObserver(service.get()));
  EXPECT_FALSE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());
}

TEST_F(NetworkConnectivityMetricsServiceTest, StartOnlineGoOnline) {
  EXPECT_TRUE(SimulateConnectionSuccess() != nullptr);
  auto service =
      NetworkConnectivityMetricsService::CreateForTesting(local_state());
  EXPECT_TRUE(network_state_handler()->HasObserver(service.get()));
  EXPECT_TRUE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());

  // Nothing changes when go online from online.
  EXPECT_TRUE(SimulateConnectionSuccess() != nullptr);
  EXPECT_TRUE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());
}

TEST_F(NetworkConnectivityMetricsServiceTest, StartOnlineGoOfflineDrop) {
  const auto* network = SimulateConnectionSuccess();
  auto service =
      NetworkConnectivityMetricsService::CreateForTesting(local_state());
  EXPECT_TRUE(network_state_handler()->HasObserver(service.get()));
  EXPECT_TRUE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());

  // Network connectivity drop.
  SimulateConnectionFailure(network, shill::kErrorUnknownFailure);
  EXPECT_FALSE(service->is_online());
  EXPECT_EQ(std::optional<int>(1), GetNetworkDropsFromLocalState());
}

TEST_F(NetworkConnectivityMetricsServiceTest, StartOfflineGoOffline) {
  const auto* network = CreateNetwork();
  SimulateConnectionFailure(network, shill::kErrorUnknownFailure);

  auto service =
      NetworkConnectivityMetricsService::CreateForTesting(local_state());
  EXPECT_TRUE(network_state_handler()->HasObserver(service.get()));
  EXPECT_FALSE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());

  // Number of drops does not change when go offline from offline.
  SimulateConnectionFailure(network, shill::kErrorUnknownFailure);
  EXPECT_FALSE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());
}

TEST_F(NetworkConnectivityMetricsServiceTest, StartOfflineGoOnline) {
  SimulateConnectionFailure(CreateNetwork(), shill::kErrorUnknownFailure);

  auto service =
      NetworkConnectivityMetricsService::CreateForTesting(local_state());
  EXPECT_TRUE(network_state_handler()->HasObserver(service.get()));
  EXPECT_FALSE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());

  // Number of drops does not change when go online from offline.
  EXPECT_TRUE(SimulateConnectionSuccess() != nullptr);
  EXPECT_TRUE(service->is_online());
  EXPECT_EQ(std::optional<int>(0), GetNetworkDropsFromLocalState());
}

TEST_F(NetworkConnectivityMetricsServiceTest, LogAndReportNetworkDrops) {
  constexpr size_t kMaxNetworkDrops = 5;

  auto service =
      NetworkConnectivityMetricsService::CreateForTesting(local_state());
  EXPECT_TRUE(network_state_handler()->HasObserver(service.get()));

  // Disconnect / connect networks kMaxNetworkDrops times.
  for (size_t network_drops = 1; network_drops <= kMaxNetworkDrops;
       network_drops++) {
    const auto* network = SimulateConnectionSuccess();
    EXPECT_TRUE(service->is_online());
    EXPECT_EQ(std::optional<int>(network_drops - 1),
              GetNetworkDropsFromLocalState());
    SimulateConnectionFailure(network, shill::kErrorUnknownFailure);
    EXPECT_FALSE(service->is_online());
    EXPECT_EQ(std::optional<int>(network_drops),
              GetNetworkDropsFromLocalState());
  }

  // Check network-drops from Local State gets reported once the next kiosk
  // session starts.
  service = NetworkConnectivityMetricsService::CreateForTesting(local_state());
  histogram_tester()->ExpectBucketCount(kKioskNetworkDropsPerSessionHistogram,
                                        kMaxNetworkDrops, 1);
}

}  // namespace ash