#include "chromeos/ash/components/network/cellular_inhibitor.h"
#include <memory>
#include <variant>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.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/dbus_thread_manager.h"
#include "chromeos/ash/components/dbus/shill/fake_shill_device_client.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/network_device_handler_impl.h"
#include "chromeos/ash/components/network/network_handler_callbacks.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/service_constants.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace ash {
namespace {
const char kDefaultCellularDevicePath[] = "stub_cellular_device";
const char kInhibitOperationResultHistogram[] =
"Network.Cellular.InhibitResult";
constexpr base::TimeDelta kScanningChangeTimeout = base::Seconds(120);
enum class GetInhibitedPropertyResult { kTrue, kFalse, kOperationFailed };
class TestObserver : public CellularInhibitor::Observer {
public:
TestObserver() = default;
~TestObserver() override = default;
size_t num_observer_events() const { return num_observer_events_; }
private:
void OnInhibitStateChanged() override { ++num_observer_events_; }
size_t num_observer_events_ = 0u;
};
}
class CellularInhibitorTest : public testing::Test {
protected:
CellularInhibitorTest()
: task_environment_(
base::test::SingleThreadTaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::TimeSource::MOCK_TIME),
helper_(false) {}
~CellularInhibitorTest() override = default;
void SetUp() override {
helper_.device_test()->SetSimulateInhibitScanning(false);
helper_.device_test()->ClearDevices();
cellular_inhibitor_.Init(helper_.network_state_handler(),
helper_.network_device_handler());
cellular_inhibitor_.AddObserver(&observer_);
}
void TearDown() override {
cellular_inhibitor_.RemoveObserver(&observer_);
helper_.device_test()->SetPropertyChangeDelay(std::nullopt);
}
void AddCellularDevice() {
helper_.device_test()->AddDevice(kDefaultCellularDevicePath,
shill::kTypeCellular, "cellular1");
base::RunLoop().RunUntilIdle();
}
std::unique_ptr<CellularInhibitor::InhibitLock> InhibitCellularScanningSync(
CellularInhibitor::InhibitReason inhibit_reason) {
base::RunLoop run_loop;
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
cellular_inhibitor_.InhibitCellularScanning(
inhibit_reason,
base::BindLambdaForTesting(
[&](std::unique_ptr<CellularInhibitor::InhibitLock> result) {
SetScanning(true);
inhibit_lock = std::move(result);
run_loop.Quit();
}));
run_loop.Run();
return inhibit_lock;
}
void InhibitCellularScanning(
CellularInhibitor::InhibitReason inhibit_reason,
std::unique_ptr<CellularInhibitor::InhibitLock>& lock) {
cellular_inhibitor_.InhibitCellularScanning(
inhibit_reason,
base::BindLambdaForTesting(
[&](std::unique_ptr<CellularInhibitor::InhibitLock> result) {
SetScanning(true);
lock = std::move(result);
}));
}
void SetScanning(bool is_scanning) {
helper_.network_device_handler()->SetDeviceProperty(
kDefaultCellularDevicePath, shill::kScanningProperty,
base::Value(is_scanning), base::DoNothing(), base::DoNothing());
base::RunLoop().RunUntilIdle();
}
GetInhibitedPropertyResult GetInhibitedProperty() {
properties_.reset();
helper_.network_device_handler()->GetDeviceProperties(
kDefaultCellularDevicePath,
base::BindOnce(&CellularInhibitorTest::GetPropertiesCallback,
base::Unretained(this)));
base::RunLoop().RunUntilIdle();
if (!properties_.has_value()) {
return GetInhibitedPropertyResult::kOperationFailed;
}
std::optional<bool> inhibited =
properties_->FindBool(shill::kInhibitedProperty);
EXPECT_TRUE(inhibited.has_value());
return inhibited.value() ? GetInhibitedPropertyResult::kTrue
: GetInhibitedPropertyResult::kFalse;
}
std::optional<CellularInhibitor::InhibitReason> GetInhibitReason() const {
return cellular_inhibitor_.GetInhibitReason();
}
void SetDevicePropertyChangeDelay() {
helper_.device_test()->SetPropertyChangeDelay(
CellularInhibitor::kInhibitPropertyChangeTimeout);
}
void FastForwardInhibitPropertyChangeTimeout() {
task_environment_.FastForwardBy(
CellularInhibitor::kInhibitPropertyChangeTimeout);
}
void FastForwardScanningChangeTimeout() {
task_environment_.FastForwardBy(kScanningChangeTimeout);
}
size_t GetNumObserverEvents() const {
return observer_.num_observer_events();
}
base::HistogramTester& histogram_tester() { return histogram_tester_; }
private:
void GetPropertiesCallback(const std::string& device_path,
std::optional<base::Value::Dict> properties) {
properties_ = std::move(properties);
}
base::test::SingleThreadTaskEnvironment task_environment_;
base::HistogramTester histogram_tester_;
NetworkStateTestHelper helper_;
CellularInhibitor cellular_inhibitor_;
TestObserver observer_;
std::optional<base::Value::Dict> properties_;
};
TEST_F(CellularInhibitorTest, SuccessSingleRequest) {
AddCellularDevice();
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock =
InhibitCellularScanningSync(
CellularInhibitor::InhibitReason::kInstallingProfile);
EXPECT_TRUE(inhibit_lock);
EXPECT_EQ(1u, GetNumObserverEvents());
EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
GetInhibitReason());
EXPECT_EQ(GetInhibitedPropertyResult::kTrue, GetInhibitedProperty());
inhibit_lock.reset();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
EXPECT_EQ(1u, GetNumObserverEvents());
EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
GetInhibitReason());
SetScanning(false);
EXPECT_EQ(2u, GetNumObserverEvents());
EXPECT_FALSE(GetInhibitReason().has_value());
histogram_tester().ExpectBucketCount(
kInhibitOperationResultHistogram,
CellularInhibitor::InhibitOperationResult::kSuccess,
1);
}
TEST_F(CellularInhibitorTest, SuccessMultipleRequests) {
AddCellularDevice();
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock2;
InhibitCellularScanning(CellularInhibitor::InhibitReason::kInstallingProfile,
inhibit_lock);
InhibitCellularScanning(CellularInhibitor::InhibitReason::kRemovingProfile,
inhibit_lock2);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(inhibit_lock);
EXPECT_FALSE(inhibit_lock2);
EXPECT_EQ(1u, GetNumObserverEvents());
EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
GetInhibitReason());
EXPECT_EQ(GetInhibitedPropertyResult::kTrue, GetInhibitedProperty());
inhibit_lock.reset();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, GetNumObserverEvents());
EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
GetInhibitReason());
EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
EXPECT_FALSE(inhibit_lock2);
SetScanning(false);
histogram_tester().ExpectBucketCount(
kInhibitOperationResultHistogram,
CellularInhibitor::InhibitOperationResult::kSuccess,
1);
EXPECT_TRUE(inhibit_lock2);
EXPECT_EQ(3u, GetNumObserverEvents());
EXPECT_EQ(CellularInhibitor::InhibitReason::kRemovingProfile,
GetInhibitReason());
EXPECT_EQ(GetInhibitedPropertyResult::kTrue, GetInhibitedProperty());
inhibit_lock2.reset();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, GetNumObserverEvents());
EXPECT_EQ(CellularInhibitor::InhibitReason::kRemovingProfile,
GetInhibitReason());
EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
SetScanning(false);
base::RunLoop().RunUntilIdle();
histogram_tester().ExpectBucketCount(
kInhibitOperationResultHistogram,
CellularInhibitor::InhibitOperationResult::kSuccess,
2);
}
TEST_F(CellularInhibitorTest, Failure) {
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock =
InhibitCellularScanningSync(
CellularInhibitor::InhibitReason::kInstallingProfile);
EXPECT_EQ(GetInhibitedPropertyResult::kOperationFailed,
GetInhibitedProperty());
EXPECT_FALSE(inhibit_lock);
histogram_tester().ExpectBucketCount(
kInhibitOperationResultHistogram,
CellularInhibitor::InhibitOperationResult::kSetInhibitNoDevice,
1);
}
TEST_F(CellularInhibitorTest, FailurePropertySetTimeout) {
AddCellularDevice();
SetDevicePropertyChangeDelay();
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
InhibitCellularScanning(CellularInhibitor::InhibitReason::kInstallingProfile,
inhibit_lock);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(inhibit_lock);
FastForwardInhibitPropertyChangeTimeout();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(inhibit_lock);
histogram_tester().ExpectBucketCount(
kInhibitOperationResultHistogram,
CellularInhibitor::InhibitOperationResult::kSetInhibitTimeout,
1);
}
TEST_F(CellularInhibitorTest, FailureScanningChangeTimeout) {
AddCellularDevice();
std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock =
InhibitCellularScanningSync(
CellularInhibitor::InhibitReason::kInstallingProfile);
EXPECT_TRUE(inhibit_lock);
EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
GetInhibitReason());
inhibit_lock.reset();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
GetInhibitReason());
FastForwardScanningChangeTimeout();
EXPECT_FALSE(GetInhibitReason().has_value());
histogram_tester().ExpectBucketCount(
kInhibitOperationResultHistogram,
CellularInhibitor::InhibitOperationResult::kUninhibitTimeout,
1);
}
}