blob: 196148f0efeb18d392ba5196b118a2875ccb4e64 [file] [log] [blame]
// 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 "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_change_notifier_factory.h"
#include "net/test/test_with_scoped_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 {
namespace {
// Subclass of NetworkChangeNotifierWin that overrides functions so that no
// Windows API networking functions are ever called.
class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin {
public:
TestNetworkChangeNotifierWin() {}
~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.
NetworkChangeNotifier::ConnectionType RecomputeCurrentConnectionType()
const override {
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
// From NetworkChangeNotifierWin.
void RecomputeCurrentConnectionTypeOnDnsThread(
base::Callback<void(ConnectionType)> reply_callback) const override {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(reply_callback, NetworkChangeNotifier::CONNECTION_UNKNOWN));
}
// From NetworkChangeNotifierWin.
MOCK_METHOD0(WatchForAddressChangeInternal, bool());
private:
DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifierWin);
};
class TestIPAddressObserver : public NetworkChangeNotifier::IPAddressObserver {
public:
TestIPAddressObserver() {
NetworkChangeNotifier::AddIPAddressObserver(this);
}
~TestIPAddressObserver() {
NetworkChangeNotifier::RemoveIPAddressObserver(this);
}
MOCK_METHOD0(OnIPAddressChanged, void());
private:
DISALLOW_COPY_AND_ASSIGN(TestIPAddressObserver);
};
bool ExitMessageLoopAndReturnFalse() {
base::RunLoop::QuitCurrentWhenIdleDeprecated();
return false;
}
} // namespace
class NetworkChangeNotifierWinTest : public TestWithScopedTaskEnvironment {
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();
}
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<TestNetworkChangeNotifierWin> network_change_notifier_;
// Must be created after |network_change_notifier_|, so it can add itself as
// an IPAddressObserver.
StrictMock<TestIPAddressObserver> 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