// 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 "base/message_loop.h" #include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier_factory.h" #include "net/base/network_change_notifier_win.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 { namespace { // Subclass of NetworkChangeNotifierWin that overrides functions so that no // Windows API networking functions are ever called. class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin { public: TestNetworkChangeNotifierWin() {} virtual ~TestNetworkChangeNotifierWin() { // 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 NetworkChangeNotifier. virtual NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() const OVERRIDE { return NetworkChangeNotifier::CONNECTION_UNKNOWN; } // From NetworkChangeNotifierWin. MOCK_METHOD0(WatchForAddressChangeInternal, bool()); private: DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifierWin); }; class TestIPAddressObserver : public net::NetworkChangeNotifier::IPAddressObserver { public: TestIPAddressObserver() { NetworkChangeNotifier::AddIPAddressObserver(this); } ~TestIPAddressObserver() { NetworkChangeNotifier::RemoveIPAddressObserver(this); } MOCK_METHOD0(OnIPAddressChanged, void()); private: DISALLOW_COPY_AND_ASSIGN(TestIPAddressObserver); }; bool ExitMessageLoopAndReturnFalse() { MessageLoop::current()->Quit(); return false; } } // namespace class NetworkChangeNotifierWinTest : public testing::Test { 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. MessageLoop::current()->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. MessageLoop::current()->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. MessageLoop::current()->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. MessageLoop::current()->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()); EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()) .Times(1) .WillOnce(Invoke(MessageLoop::current(), &MessageLoop::Quit)); EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal()) .Times(1) .WillOnce(Return(true)); MessageLoop::current()->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)); MessageLoop::current()->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. MessageLoop::current()->RunUntilIdle(); } 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(); } } // namespace net