| // 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 "net/base/network_cost_change_notifier_win.h" |
| |
| #include "base/run_loop.h" |
| #include "base/sequence_checker.h" |
| #include "base/test/scoped_os_info_override_win.h" |
| #include "base/win/windows_version.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/test/test_connection_cost_observer.h" |
| #include "net/test/test_with_task_environment.h" |
| #include "net/test/win/fake_network_cost_manager.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| class NetworkCostChangeNotifierWinTest : public TestWithTaskEnvironment { |
| public: |
| void SetUp() override { |
| if (base::win::GetVersion() < |
| NetworkCostChangeNotifierWin::kSupportedOsVersion) { |
| GTEST_SKIP(); |
| } |
| } |
| |
| protected: |
| FakeNetworkCostManagerEnvironment fake_network_cost_manager_environment_; |
| }; |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, InitialCostUnknown) { |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN); |
| |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = |
| base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| // Wait for `NetworkCostChangeNotifierWin` to finish initializing. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| // `NetworkCostChangeNotifierWin` must report an unknown cost after |
| // initializing. |
| EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN); |
| } |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, InitialCostKnown) { |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = |
| base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| // Initializing changes the cost from unknown to unmetered. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| ASSERT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| } |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, MultipleCostChangedEvents) { |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = |
| base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| // Initializing changes the cost from unknown to unmetered. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| ASSERT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED); |
| |
| // The simulated event changes the cost from unmetered to metered. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| ASSERT_EQ(cost_change_observer.cost_changed_calls(), 2u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED); |
| |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN); |
| |
| // The simulated event changes the cost from metered to unknown. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| ASSERT_EQ(cost_change_observer.cost_changed_calls(), 3u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN); |
| } |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, DuplicateEvents) { |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = |
| base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| // Initializing changes the cost from unknown to unmetered. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| ASSERT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| // Changing from unmetered to unmetered must dispatch a cost changed event. |
| ASSERT_EQ(cost_change_observer.cost_changed_calls(), 2u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| } |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, ShutdownImmediately) { |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = |
| base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| // Shutting down immediately must not crash. |
| cost_change_notifier.Reset(); |
| |
| // Wait for `NetworkCostChangeNotifierWin` to finish initializing and shutting |
| // down. |
| RunUntilIdle(); |
| |
| // `NetworkCostChangeNotifierWin` reports a connection change after |
| // initializing. |
| EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED); |
| |
| // Wait for `NetworkCostChangeNotifierWin` to handle the cost changed event. |
| RunUntilIdle(); |
| |
| // After shutdown, cost changed events must have no effect. |
| EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| } |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, ErrorHandling) { |
| // Simulate the failure of each OS API while initializing |
| // `NetworkCostChangeNotifierWin`. |
| constexpr const NetworkCostManagerStatus kErrorList[] = { |
| NetworkCostManagerStatus::kErrorCoCreateInstanceFailed, |
| NetworkCostManagerStatus::kErrorQueryInterfaceFailed, |
| NetworkCostManagerStatus::kErrorFindConnectionPointFailed, |
| NetworkCostManagerStatus::kErrorAdviseFailed, |
| NetworkCostManagerStatus::kErrorGetCostFailed, |
| }; |
| for (auto error : kErrorList) { |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| fake_network_cost_manager_environment_.SimulateError(error); |
| |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = base::BindRepeating( |
| &TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| if (error == NetworkCostManagerStatus::kErrorGetCostFailed) { |
| // `NetworkCostChangeNotifierWin` must report an unknown cost after |
| // `INetworkCostManager::GetCost()` fails. |
| cost_change_observer.WaitForConnectionCostChanged(); |
| |
| EXPECT_EQ(cost_change_observer.cost_changed_calls(), 1u); |
| EXPECT_EQ(cost_change_observer.last_cost_changed_input(), |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN); |
| } else { |
| // Wait for `NetworkCostChangeNotifierWin` to finish initializing. |
| RunUntilIdle(); |
| |
| // `NetworkCostChangeNotifierWin` must NOT report a changed cost after |
| // failing to initialize. |
| EXPECT_EQ(cost_change_observer.cost_changed_calls(), 0u); |
| } |
| } |
| } |
| |
| TEST_F(NetworkCostChangeNotifierWinTest, UnsupportedOS) { |
| base::test::ScopedOSInfoOverride os_override( |
| base::test::ScopedOSInfoOverride::Type::kWinServer2016); |
| |
| fake_network_cost_manager_environment_.SetCost( |
| NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED); |
| |
| TestConnectionCostObserver cost_change_observer; |
| auto cost_change_callback = |
| base::BindRepeating(&TestConnectionCostObserver::OnConnectionCostChanged, |
| base::Unretained(&cost_change_observer)); |
| |
| base::SequenceBound<NetworkCostChangeNotifierWin> cost_change_notifier = |
| NetworkCostChangeNotifierWin::CreateInstance(cost_change_callback); |
| |
| // Wait for `NetworkCostChangeNotifierWin` to finish initializing. |
| RunUntilIdle(); |
| |
| // `NetworkCostChangeNotifierWin` must NOT report a changed cost for |
| // unsupported OSes. |
| EXPECT_EQ(cost_change_observer.cost_changed_calls(), 0u); |
| } |
| |
| } // namespace net |