// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/network_change_notifier_win.h" #include #include "base/bind.h" #include "base/run_loop.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/win/windows_version.h" #include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier_factory.h" #include "net/test/test_with_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::AtLeast; using ::testing::Invoke; using ::testing::Return; using ::testing::StrictMock; namespace net { // Subclass of NetworkChangeNotifierWin that overrides functions so that no // Windows API networking function results effect tests. class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin { public: TestNetworkChangeNotifierWin() { last_computed_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN; last_announced_offline_ = false; last_computed_connection_cost_ = ConnectionCost::CONNECTION_COST_UNKNOWN; sequence_runner_for_registration_ = base::SequencedTaskRunnerHandle::Get(); } TestNetworkChangeNotifierWin(const TestNetworkChangeNotifierWin&) = delete; TestNetworkChangeNotifierWin& operator=(const TestNetworkChangeNotifierWin&) = delete; ~TestNetworkChangeNotifierWin() override { // This is needed so we don't try to stop watching for IP address changes, // as we never actually started. set_is_watching(false); } // From NetworkChangeNotifierWin. void RecomputeCurrentConnectionTypeOnBlockingSequence( base::OnceCallback reply_callback) const override { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(reply_callback), NetworkChangeNotifier::CONNECTION_UNKNOWN)); } // From NetworkChangeNotifierWin. MOCK_METHOD0(WatchForAddressChangeInternal, bool()); }; class TestIPAddressObserver : public NetworkChangeNotifier::IPAddressObserver { public: TestIPAddressObserver() { NetworkChangeNotifier::AddIPAddressObserver(this); } TestIPAddressObserver(const TestIPAddressObserver&) = delete; TestIPAddressObserver& operator=(const TestIPAddressObserver&) = delete; ~TestIPAddressObserver() { NetworkChangeNotifier::RemoveIPAddressObserver(this); } MOCK_METHOD0(OnIPAddressChanged, void()); }; bool ExitMessageLoopAndReturnFalse() { base::RunLoop::QuitCurrentWhenIdleDeprecated(); return false; } class NetworkChangeNotifierWinTest : public TestWithTaskEnvironment { public: // Calls WatchForAddressChange, and simulates a WatchForAddressChangeInternal // success. Expects that |network_change_notifier_| has just been created, so // it's not watching anything yet, and there have been no previous // WatchForAddressChangeInternal failures. void StartWatchingAndSucceed() { EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(0); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) .Times(1) .WillOnce(Return(true)); network_change_notifier_.WatchForAddressChange(); EXPECT_TRUE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); // If a task to notify observers of the IP address change event was // incorrectly posted, make sure it gets run to trigger a failure. base::RunLoop().RunUntilIdle(); } // Calls WatchForAddressChange, and simulates a WatchForAddressChangeInternal // failure. void StartWatchingAndFail() { EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(0); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) // Due to an expected race, it's theoretically possible for more than // one call to occur, though unlikely. .Times(AtLeast(1)) .WillRepeatedly(Return(false)); network_change_notifier_.WatchForAddressChange(); EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_LT(0, network_change_notifier_.sequential_failures()); // If a task to notify observers of the IP address change event was // incorrectly posted, make sure it gets run. base::RunLoop().RunUntilIdle(); } // Simulates a network change event, resulting in a call to OnObjectSignaled. // The resulting call to WatchForAddressChangeInternal then succeeds. void SignalAndSucceed() { EXPECT_TRUE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(1); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) .Times(1) .WillOnce(Return(true)); network_change_notifier_.OnObjectSignaled(INVALID_HANDLE_VALUE); EXPECT_TRUE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); // Run the task to notify observers of the IP address change event. base::RunLoop().RunUntilIdle(); } // Simulates a network change event, resulting in a call to OnObjectSignaled. // The resulting call to WatchForAddressChangeInternal then fails. void SignalAndFail() { EXPECT_TRUE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(1); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) // Due to an expected race, it's theoretically possible for more than // one call to occur, though unlikely. .Times(AtLeast(1)) .WillRepeatedly(Return(false)); network_change_notifier_.OnObjectSignaled(INVALID_HANDLE_VALUE); EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_LT(0, network_change_notifier_.sequential_failures()); // Run the task to notify observers of the IP address change event. base::RunLoop().RunUntilIdle(); } // Runs the message loop until WatchForAddressChange is called again, as a // result of the already posted task after a WatchForAddressChangeInternal // failure. Simulates a success on the resulting call to // WatchForAddressChangeInternal. void RetryAndSucceed() { EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_LT(0, network_change_notifier_.sequential_failures()); base::RunLoop run_loop; EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()) .Times(1) .WillOnce(Invoke(&run_loop, &base::RunLoop::QuitWhenIdle)); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) .Times(1).WillOnce(Return(true)); run_loop.Run(); EXPECT_TRUE(network_change_notifier_.is_watching()); EXPECT_EQ(0, network_change_notifier_.sequential_failures()); } // Runs the message loop until WatchForAddressChange is called again, as a // result of the already posted task after a WatchForAddressChangeInternal // failure. Simulates a failure on the resulting call to // WatchForAddressChangeInternal. void RetryAndFail() { EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_LT(0, network_change_notifier_.sequential_failures()); int initial_sequential_failures = network_change_notifier_.sequential_failures(); EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(0); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) // Due to an expected race, it's theoretically possible for more than // one call to occur, though unlikely. .Times(AtLeast(1)) .WillRepeatedly(Invoke(ExitMessageLoopAndReturnFalse)); base::RunLoop().Run(); EXPECT_FALSE(network_change_notifier_.is_watching()); EXPECT_LT(initial_sequential_failures, network_change_notifier_.sequential_failures()); // If a task to notify observers of the IP address change event was // incorrectly posted, make sure it gets run. base::RunLoop().RunUntilIdle(); } bool HasNetworkCostManager() { return network_change_notifier_.network_cost_manager_.Get() != nullptr; } bool HasNetworkCostManagerEventSink() { return network_change_notifier_.network_cost_manager_event_sink_.Get() != nullptr; } NetworkChangeNotifier::ConnectionCost LastComputedConnectionCost() { return network_change_notifier_.last_computed_connection_cost_; } NetworkChangeNotifier::ConnectionCost GetCurrentConnectionCost() { return network_change_notifier_.GetCurrentConnectionCost(); } private: // Note that the order of declaration here is important. // Allows creating a new NetworkChangeNotifier. Must be created before // |network_change_notifier_| and destroyed after it to avoid DCHECK failures. NetworkChangeNotifier::DisableForTest disable_for_test_; StrictMock network_change_notifier_; // Must be created after |network_change_notifier_|, so it can add itself as // an IPAddressObserver. StrictMock test_ip_address_observer_; }; TEST_F(NetworkChangeNotifierWinTest, NetChangeWinBasic) { StartWatchingAndSucceed(); } TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailStart) { StartWatchingAndFail(); } TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailStartOnce) { StartWatchingAndFail(); RetryAndSucceed(); } TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailStartTwice) { StartWatchingAndFail(); RetryAndFail(); RetryAndSucceed(); } TEST_F(NetworkChangeNotifierWinTest, NetChangeWinSignal) { StartWatchingAndSucceed(); SignalAndSucceed(); } TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailSignalOnce) { StartWatchingAndSucceed(); SignalAndFail(); RetryAndSucceed(); } TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailSignalTwice) { StartWatchingAndSucceed(); SignalAndFail(); RetryAndFail(); RetryAndSucceed(); } class TestConnectionCostObserver : public NetworkChangeNotifier::ConnectionCostObserver { public: TestConnectionCostObserver() {} TestConnectionCostObserver(const TestConnectionCostObserver&) = delete; TestConnectionCostObserver& operator=(const TestConnectionCostObserver&) = delete; ~TestConnectionCostObserver() override { NetworkChangeNotifier::RemoveConnectionCostObserver(this); } void OnConnectionCostChanged(NetworkChangeNotifier::ConnectionCost) override { } void Register() { NetworkChangeNotifier::AddConnectionCostObserver(this); } }; TEST_F(NetworkChangeNotifierWinTest, NetworkCostManagerIntegration) { // NetworkCostManager integration only exist on Win10+. if (base::win::GetVersion() < base::win::Version::WIN10) return; // Upon creation, none of the NetworkCostManager integration should be // initialized yet. ASSERT_FALSE(HasNetworkCostManager()); ASSERT_FALSE(HasNetworkCostManagerEventSink()); ASSERT_EQ(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN, LastComputedConnectionCost()); // Asking for the current connection cost should initialize the // NetworkCostManager integration, but not the event sink. // Note that the actual ConnectionCost value return is irrelevant beyond the // fact that it shouldn't be UNKNOWN anymore if the integration is initialized // properly. NetworkChangeNotifier::ConnectionCost current_connection_cost = GetCurrentConnectionCost(); EXPECT_NE(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN, current_connection_cost); EXPECT_EQ(current_connection_cost, LastComputedConnectionCost()); EXPECT_TRUE(HasNetworkCostManager()); EXPECT_FALSE(HasNetworkCostManagerEventSink()); // Adding a ConnectionCostObserver should initialize the event sink. If the // subsequent registration for updates fails, the event sink will get // destroyed. TestConnectionCostObserver test_connection_cost_observer; test_connection_cost_observer.Register(); // The actual registration happens on a callback, so need to run until idle. base::RunLoop().RunUntilIdle(); EXPECT_TRUE(HasNetworkCostManagerEventSink()); } } // namespace net