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

// Copyright 2023 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/technology_state_controller.h"

#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/values.h"
#include "chromeos/ash/components/dbus/shill/shill_clients.h"
#include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
#include "chromeos/ash/components/network/enterprise_managed_metadata_store.h"
#include "chromeos/ash/components/network/hotspot_allowed_flag_handler.h"
#include "chromeos/ash/components/network/hotspot_capabilities_provider.h"
#include "chromeos/ash/components/network/hotspot_controller.h"
#include "chromeos/ash/components/network/hotspot_state_handler.h"
#include "chromeos/ash/components/network/metrics/connection_results.h"
#include "chromeos/ash/components/network/metrics/hotspot_feature_usage_metrics.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash {

namespace {

const char kEnableWifiResultHistogram[] =
    "Network.Ash.WiFi.EnabledState.Enable.Result";
const char kEnableWifiResultCodeHistogram[] =
    "Network.Ash.WiFi.EnabledState.Enable.ResultCode";

}  // namespace

class TechnologyStateControllerTest : public ::testing::Test {
 public:
  void SetUp() override {
    enterprise_managed_metadata_store_ =
        std::make_unique<EnterpriseManagedMetadataStore>();
    hotspot_capabilities_provider_ =
        std::make_unique<HotspotCapabilitiesProvider>();
    hotspot_allowed_flag_handler_ =
        std::make_unique<HotspotAllowedFlagHandler>();
    hotspot_capabilities_provider_->Init(
        network_state_test_helper_.network_state_handler(),
        hotspot_allowed_flag_handler_.get());
    hotspot_feature_usage_metrics_ =
        std::make_unique<HotspotFeatureUsageMetrics>();
    hotspot_feature_usage_metrics_->Init(
        enterprise_managed_metadata_store_.get(),
        hotspot_capabilities_provider_.get());
    technology_state_controller_ =
        std::make_unique<TechnologyStateController>();
    technology_state_controller_->Init(
        network_state_test_helper_.network_state_handler());
    hotspot_state_handler_ = std::make_unique<HotspotStateHandler>();
    hotspot_state_handler_->Init();
    hotspot_controller_ = std::make_unique<HotspotController>();
    hotspot_controller_->Init(hotspot_capabilities_provider_.get(),
                              hotspot_feature_usage_metrics_.get(),
                              hotspot_state_handler_.get(),
                              technology_state_controller_.get());
  }

  void TearDown() override {
    network_state_test_helper_.ClearDevices();
    network_state_test_helper_.ClearServices();
    hotspot_controller_.reset();
    hotspot_feature_usage_metrics_.reset();
    hotspot_capabilities_provider_.reset();
    hotspot_allowed_flag_handler_.reset();
    hotspot_state_handler_.reset();
    enterprise_managed_metadata_store_.reset();
    technology_state_controller_.reset();
  }

  // Returns the pair of prepare_success and wifi_turned_off result of the
  // TechnologStateHandler::PrepareEnableHotspot method.
  std::pair<bool, bool> PrepareEnableHotspot() {
    base::RunLoop run_loop;
    bool prepare_success, wifi_turned_off;
    technology_state_controller_->PrepareEnableHotspot(
        base::BindLambdaForTesting([&](bool success, bool wifi_off) {
          prepare_success = success;
          wifi_turned_off = wifi_off;
          run_loop.Quit();
        }));
    run_loop.Run();
    return std::make_pair(prepare_success, wifi_turned_off);
  }

 protected:
  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  base::HistogramTester histogram_tester_;
  std::unique_ptr<HotspotController> hotspot_controller_;
  std::unique_ptr<EnterpriseManagedMetadataStore>
      enterprise_managed_metadata_store_;
  std::unique_ptr<HotspotCapabilitiesProvider> hotspot_capabilities_provider_;
  std::unique_ptr<HotspotAllowedFlagHandler> hotspot_allowed_flag_handler_;
  std::unique_ptr<HotspotFeatureUsageMetrics> hotspot_feature_usage_metrics_;
  std::unique_ptr<HotspotStateHandler> hotspot_state_handler_;
  std::unique_ptr<TechnologyStateController> technology_state_controller_;
  NetworkStateTestHelper network_state_test_helper_{
      /*use_default_devices_and_services=*/false};
};

TEST_F(TechnologyStateControllerTest, ChangeWifiTechnology) {
  // Disable Wifi technology. Will immediately set the state to DISABLING.
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::WiFi(), /*enabled=*/false,
      network_handler::ErrorCallback());
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_DISABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));

  // Run the message loop. When Shill updates the enabled technologies since
  // the state should transition to AVAILABLE.
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));

  // Enable Wifi technology. Will immediately set the state to ENABLING.
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::WiFi(), /*enabled=*/true,
      network_handler::ErrorCallback());
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));

  // Run the message loop. State should change to ENABLED.
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLED,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
}

TEST_F(TechnologyStateControllerTest, ChangePhysicalTechnologies) {
  // Disable Physical technologies which includes WiFi(), Cellular() and
  // Ethernet()
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::Physical(), /*enabled=*/false,
      network_handler::ErrorCallback());
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_DISABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_DISABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Cellular()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_DISABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Ethernet()));

  // Run the message loop. When Shill updates the enabled technologies since
  // the state should transition to AVAILABLE.
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Cellular()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Ethernet()));

  // Enable Physical technologies. Will immediately set the state to ENABLING.
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::Physical(), /*enabled=*/true,
      network_handler::ErrorCallback());
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Cellular()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLING,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Ethernet()));

  // Run the message loop. State should change to ENABLED.
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLED,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLED,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Cellular()));
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLED,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::Ethernet()));
}

TEST_F(TechnologyStateControllerTest, EnableWifiWhenHotspotOn) {
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::WiFi(), /*enabled=*/false,
      network_handler::ErrorCallback());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));

  // Simulate that there's an active hotspot
  auto status_dict = base::Value::Dict().Set(
      shill::kTetheringStatusStateProperty, shill::kTetheringStateActive);
  network_state_test_helper_.manager_test()->SetManagerProperty(
      shill::kTetheringStatusProperty, base::Value(std::move(status_dict)));
  base::RunLoop().RunUntilIdle();

  // Simulate disable hotspot will fail.
  network_state_test_helper_.manager_test()->SetSimulateTetheringEnableResult(
      FakeShillSimulatedResult::kSuccess, "network_failure");

  std::string error;
  base::RunLoop run_loop;
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::WiFi(), /*enabled=*/true,
      base::BindLambdaForTesting([&](const std::string& error_name) {
        error = error_name;
        run_loop.Quit();
      }));
  run_loop.Run();
  EXPECT_EQ(TechnologyStateController::kErrorDisableHotspot, error);
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
  histogram_tester_.ExpectTotalCount(kEnableWifiResultCodeHistogram, 1);
  histogram_tester_.ExpectBucketCount(
      kEnableWifiResultCodeHistogram,
      ShillConnectResult::kErrorDisableHotspotFailed, 1);
  histogram_tester_.ExpectTotalCount(kEnableWifiResultHistogram, 1);
  histogram_tester_.ExpectBucketCount(kEnableWifiResultHistogram, false, 1);

  // Simulate disable hotspot will succeed.
  network_state_test_helper_.manager_test()->SetSimulateTetheringEnableResult(
      FakeShillSimulatedResult::kSuccess, shill::kTetheringEnableResultSuccess);
  technology_state_controller_->SetTechnologiesEnabled(
      NetworkTypePattern::WiFi(), /*enabled=*/true,
      network_handler::ErrorCallback());
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLED,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
  histogram_tester_.ExpectTotalCount(kEnableWifiResultCodeHistogram, 2);
  histogram_tester_.ExpectBucketCount(kEnableWifiResultCodeHistogram,
                                      ShillConnectResult::kSuccess, 1);
  histogram_tester_.ExpectTotalCount(kEnableWifiResultHistogram, 2);
  histogram_tester_.ExpectBucketCount(kEnableWifiResultHistogram, true, 1);
}

TEST_F(TechnologyStateControllerTest, PrepareEnableHotspot) {
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_ENABLED,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));

  std::pair<bool, bool> result = PrepareEnableHotspot();
  // Verifies that |prepare_success| will return true.
  EXPECT_TRUE(result.first);
  // Verifies that |wifi_turned_off| will return true since Wifi was on.
  EXPECT_TRUE(result.second);
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));

  result = PrepareEnableHotspot();
  // Verifies that |prepare_success| will return true.
  EXPECT_TRUE(result.first);
  // Verifies that |wifi_turned_off| will return false since Wifi was off.
  EXPECT_FALSE(result.second);
  EXPECT_EQ(
      NetworkStateHandler::TECHNOLOGY_AVAILABLE,
      network_state_test_helper_.network_state_handler()->GetTechnologyState(
          NetworkTypePattern::WiFi()));
}

}  // namespace ash