1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/base/network_change_notifier_win.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/functional/bind.h"
12 #include "base/run_loop.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "base/test/scoped_os_info_override_win.h"
16 #include "base/win/windows_version.h"
17 #include "net/base/network_change_notifier.h"
18 #include "net/base/network_change_notifier_factory.h"
19 #include "net/base/network_cost_change_notifier_win.h"
20 #include "net/test/test_connection_cost_observer.h"
21 #include "net/test/test_with_task_environment.h"
22 #include "net/test/win/fake_network_cost_manager.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using ::testing::AtLeast;
27 using ::testing::Invoke;
28 using ::testing::Return;
29 using ::testing::StrictMock;
30
31 namespace net {
32
33 // Subclass of NetworkChangeNotifierWin that overrides functions so that no
34 // Windows API networking function results effect tests.
35 class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin {
36 public:
TestNetworkChangeNotifierWin()37 TestNetworkChangeNotifierWin() {
38 last_computed_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
39 last_announced_offline_ = false;
40 sequence_runner_for_registration_ =
41 base::SequencedTaskRunner::GetCurrentDefault();
42 }
43
44 TestNetworkChangeNotifierWin(const TestNetworkChangeNotifierWin&) = delete;
45 TestNetworkChangeNotifierWin& operator=(const TestNetworkChangeNotifierWin&) =
46 delete;
47
~TestNetworkChangeNotifierWin()48 ~TestNetworkChangeNotifierWin() override {
49 // This is needed so we don't try to stop watching for IP address changes,
50 // as we never actually started.
51 set_is_watching(false);
52 }
53
54 // From NetworkChangeNotifierWin.
RecomputeCurrentConnectionTypeOnBlockingSequence(base::OnceCallback<void (ConnectionType)> reply_callback) const55 void RecomputeCurrentConnectionTypeOnBlockingSequence(
56 base::OnceCallback<void(ConnectionType)> reply_callback) const override {
57 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
58 FROM_HERE, base::BindOnce(std::move(reply_callback),
59 NetworkChangeNotifier::CONNECTION_UNKNOWN));
60 }
61
62 // From NetworkChangeNotifierWin.
63 MOCK_METHOD0(WatchForAddressChangeInternal, bool());
64
65 // Allow tests to compare results with the default implementation that does
66 // not depend on the `INetworkCostManager` Windows OS API. The default
67 // implementation is used as a fall back when `INetworkCostManager` fails.
GetCurrentConnectionCostFromDefaultImplementationForTesting()68 ConnectionCost GetCurrentConnectionCostFromDefaultImplementationForTesting() {
69 return NetworkChangeNotifier::GetCurrentConnectionCost();
70 }
71 };
72
73 class TestIPAddressObserver : public NetworkChangeNotifier::IPAddressObserver {
74 public:
TestIPAddressObserver()75 TestIPAddressObserver() { NetworkChangeNotifier::AddIPAddressObserver(this); }
76
77 TestIPAddressObserver(const TestIPAddressObserver&) = delete;
78 TestIPAddressObserver& operator=(const TestIPAddressObserver&) = delete;
79
~TestIPAddressObserver()80 ~TestIPAddressObserver() override {
81 NetworkChangeNotifier::RemoveIPAddressObserver(this);
82 }
83
84 MOCK_METHOD0(OnIPAddressChanged, void());
85 };
86
87 class NetworkChangeNotifierWinTest : public TestWithTaskEnvironment {
88 public:
89 // Calls WatchForAddressChange, and simulates a WatchForAddressChangeInternal
90 // success. Expects that |network_change_notifier_| has just been created, so
91 // it's not watching anything yet, and there have been no previous
92 // WatchForAddressChangeInternal failures.
StartWatchingAndSucceed()93 void StartWatchingAndSucceed() {
94 EXPECT_FALSE(network_change_notifier_.is_watching());
95 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
96
97 EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(0);
98 EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
99 .WillOnce(Return(true));
100
101 network_change_notifier_.WatchForAddressChange();
102
103 EXPECT_TRUE(network_change_notifier_.is_watching());
104 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
105
106 // If a task to notify observers of the IP address change event was
107 // incorrectly posted, make sure it gets run to trigger a failure.
108 base::RunLoop().RunUntilIdle();
109 }
110
111 // Calls WatchForAddressChange, and simulates a WatchForAddressChangeInternal
112 // failure.
StartWatchingAndFail()113 void StartWatchingAndFail() {
114 EXPECT_FALSE(network_change_notifier_.is_watching());
115 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
116
117 EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(0);
118 EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
119 // Due to an expected race, it's theoretically possible for more than
120 // one call to occur, though unlikely.
121 .Times(AtLeast(1))
122 .WillRepeatedly(Return(false));
123
124 network_change_notifier_.WatchForAddressChange();
125
126 EXPECT_FALSE(network_change_notifier_.is_watching());
127 EXPECT_LT(0, network_change_notifier_.sequential_failures());
128
129 // If a task to notify observers of the IP address change event was
130 // incorrectly posted, make sure it gets run.
131 base::RunLoop().RunUntilIdle();
132 }
133
134 // Simulates a network change event, resulting in a call to OnObjectSignaled.
135 // The resulting call to WatchForAddressChangeInternal then succeeds.
SignalAndSucceed()136 void SignalAndSucceed() {
137 EXPECT_TRUE(network_change_notifier_.is_watching());
138 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
139
140 EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(1);
141 EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
142 .WillOnce(Return(true));
143
144 network_change_notifier_.OnObjectSignaled(INVALID_HANDLE_VALUE);
145
146 EXPECT_TRUE(network_change_notifier_.is_watching());
147 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
148
149 // Run the task to notify observers of the IP address change event.
150 base::RunLoop().RunUntilIdle();
151 }
152
153 // Simulates a network change event, resulting in a call to OnObjectSignaled.
154 // The resulting call to WatchForAddressChangeInternal then fails.
SignalAndFail()155 void SignalAndFail() {
156 EXPECT_TRUE(network_change_notifier_.is_watching());
157 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
158
159 EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(1);
160 EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
161 // Due to an expected race, it's theoretically possible for more than
162 // one call to occur, though unlikely.
163 .Times(AtLeast(1))
164 .WillRepeatedly(Return(false));
165
166 network_change_notifier_.OnObjectSignaled(INVALID_HANDLE_VALUE);
167
168 EXPECT_FALSE(network_change_notifier_.is_watching());
169 EXPECT_LT(0, network_change_notifier_.sequential_failures());
170
171 // Run the task to notify observers of the IP address change event.
172 base::RunLoop().RunUntilIdle();
173 }
174
175 // Runs the message loop until WatchForAddressChange is called again, as a
176 // result of the already posted task after a WatchForAddressChangeInternal
177 // failure. Simulates a success on the resulting call to
178 // WatchForAddressChangeInternal.
RetryAndSucceed()179 void RetryAndSucceed() {
180 EXPECT_FALSE(network_change_notifier_.is_watching());
181 EXPECT_LT(0, network_change_notifier_.sequential_failures());
182
183 base::RunLoop run_loop;
184
185 EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged())
186 .WillOnce(Invoke(&run_loop, &base::RunLoop::QuitWhenIdle));
187 EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
188 .WillOnce(Return(true));
189
190 run_loop.Run();
191
192 EXPECT_TRUE(network_change_notifier_.is_watching());
193 EXPECT_EQ(0, network_change_notifier_.sequential_failures());
194 }
195
196 // Runs the message loop until WatchForAddressChange is called again, as a
197 // result of the already posted task after a WatchForAddressChangeInternal
198 // failure. Simulates a failure on the resulting call to
199 // WatchForAddressChangeInternal.
RetryAndFail()200 void RetryAndFail() {
201 base::RunLoop loop;
202 EXPECT_FALSE(network_change_notifier_.is_watching());
203 EXPECT_LT(0, network_change_notifier_.sequential_failures());
204
205 int initial_sequential_failures =
206 network_change_notifier_.sequential_failures();
207
208 EXPECT_CALL(test_ip_address_observer_, OnIPAddressChanged()).Times(0);
209 EXPECT_CALL(network_change_notifier_, WatchForAddressChangeInternal())
210 // Due to an expected race, it's theoretically possible for more than
211 // one call to occur, though unlikely.
212 .Times(AtLeast(1))
213 .WillRepeatedly(Invoke([&loop]() {
214 loop.QuitWhenIdle();
215 return false;
216 }));
217
218 loop.Run();
219
220 EXPECT_FALSE(network_change_notifier_.is_watching());
221 EXPECT_LT(initial_sequential_failures,
222 network_change_notifier_.sequential_failures());
223
224 // If a task to notify observers of the IP address change event was
225 // incorrectly posted, make sure it gets run.
226 base::RunLoop().RunUntilIdle();
227 }
228
GetCurrentConnectionCost()229 NetworkChangeNotifier::ConnectionCost GetCurrentConnectionCost() {
230 return network_change_notifier_.GetCurrentConnectionCost();
231 }
232
233 NetworkChangeNotifier::ConnectionCost
GetCurrentConnectionCostFromDefaultImplementationForTesting()234 GetCurrentConnectionCostFromDefaultImplementationForTesting() {
235 return network_change_notifier_
236 .GetCurrentConnectionCostFromDefaultImplementationForTesting();
237 }
238
239 protected:
240 FakeNetworkCostManagerEnvironment fake_network_cost_manager_environment_;
241
242 private:
243 // Note that the order of declaration here is important.
244
245 // Allows creating a new NetworkChangeNotifier. Must be created before
246 // |network_change_notifier_| and destroyed after it to avoid DCHECK failures.
247 NetworkChangeNotifier::DisableForTest disable_for_test_;
248
249 StrictMock<TestNetworkChangeNotifierWin> network_change_notifier_;
250
251 // Must be created after |network_change_notifier_|, so it can add itself as
252 // an IPAddressObserver.
253 StrictMock<TestIPAddressObserver> test_ip_address_observer_;
254 };
255
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinBasic)256 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinBasic) {
257 StartWatchingAndSucceed();
258 }
259
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinFailStart)260 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailStart) {
261 StartWatchingAndFail();
262 }
263
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinFailStartOnce)264 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailStartOnce) {
265 StartWatchingAndFail();
266 RetryAndSucceed();
267 }
268
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinFailStartTwice)269 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailStartTwice) {
270 StartWatchingAndFail();
271 RetryAndFail();
272 RetryAndSucceed();
273 }
274
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinSignal)275 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinSignal) {
276 StartWatchingAndSucceed();
277 SignalAndSucceed();
278 }
279
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinFailSignalOnce)280 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailSignalOnce) {
281 StartWatchingAndSucceed();
282 SignalAndFail();
283 RetryAndSucceed();
284 }
285
TEST_F(NetworkChangeNotifierWinTest,NetChangeWinFailSignalTwice)286 TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailSignalTwice) {
287 StartWatchingAndSucceed();
288 SignalAndFail();
289 RetryAndFail();
290 RetryAndSucceed();
291 }
292
TEST_F(NetworkChangeNotifierWinTest,GetCurrentCost)293 TEST_F(NetworkChangeNotifierWinTest, GetCurrentCost) {
294 if (base::win::GetVersion() <
295 NetworkCostChangeNotifierWin::kSupportedOsVersion) {
296 GTEST_SKIP();
297 }
298
299 fake_network_cost_manager_environment_.SetCost(
300 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
301
302 // Wait for `NetworkCostChangeNotifierWin` to finish initializing.
303 RunUntilIdle();
304
305 EXPECT_EQ(GetCurrentConnectionCost(),
306 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
307
308 fake_network_cost_manager_environment_.SetCost(
309 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
310
311 // Wait for `NetworkCostChangeNotifierWin` to handle the cost changed event.
312 RunUntilIdle();
313
314 EXPECT_EQ(GetCurrentConnectionCost(),
315 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
316 }
317
TEST_F(NetworkChangeNotifierWinTest,CostChangeObserver)318 TEST_F(NetworkChangeNotifierWinTest, CostChangeObserver) {
319 if (base::win::GetVersion() <
320 NetworkCostChangeNotifierWin::kSupportedOsVersion) {
321 GTEST_SKIP();
322 }
323
324 fake_network_cost_manager_environment_.SetCost(
325 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNMETERED);
326
327 // Wait for `NetworkCostChangeNotifierWin` to finish initializing.
328 RunUntilIdle();
329
330 TestConnectionCostObserver cost_observer;
331 NetworkChangeNotifier::AddConnectionCostObserver(&cost_observer);
332
333 fake_network_cost_manager_environment_.SetCost(
334 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
335
336 cost_observer.WaitForConnectionCostChanged();
337
338 ASSERT_EQ(cost_observer.cost_changed_calls(), 1u);
339 EXPECT_EQ(cost_observer.last_cost_changed_input(),
340 NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_METERED);
341
342 NetworkChangeNotifier::RemoveConnectionCostObserver(&cost_observer);
343 }
344
345 // Uses the fake implementation of `INetworkCostManager` to simulate `GetCost()`
346 // returning an error `HRESULT`.
347 class NetworkChangeNotifierWinCostErrorTest
348 : public NetworkChangeNotifierWinTest {
SetUp()349 void SetUp() override {
350 if (base::win::GetVersion() <
351 NetworkCostChangeNotifierWin::kSupportedOsVersion) {
352 GTEST_SKIP();
353 }
354
355 fake_network_cost_manager_environment_.SimulateError(
356 NetworkCostManagerStatus::kErrorGetCostFailed);
357
358 NetworkChangeNotifierWinTest::SetUp();
359 }
360 };
361
TEST_F(NetworkChangeNotifierWinCostErrorTest,CostError)362 TEST_F(NetworkChangeNotifierWinCostErrorTest, CostError) {
363 // Wait for `NetworkCostChangeNotifierWin` to finish initializing, which
364 // should fail with an error.
365 RunUntilIdle();
366
367 // `NetworkChangeNotifierWin` must use the default implementation when
368 // `NetworkCostChangeNotifierWin` returns an unknown cost.
369 EXPECT_EQ(GetCurrentConnectionCost(),
370 GetCurrentConnectionCostFromDefaultImplementationForTesting());
371 }
372
373 // Override the Windows OS version to simulate running on an OS that does not
374 // support `INetworkCostManager`.
375 class NetworkChangeNotifierWinCostUnsupportedOsTest
376 : public NetworkChangeNotifierWinTest {
377 public:
NetworkChangeNotifierWinCostUnsupportedOsTest()378 NetworkChangeNotifierWinCostUnsupportedOsTest()
379 : os_override_(base::test::ScopedOSInfoOverride::Type::kWinServer2016) {}
380
381 protected:
382 base::test::ScopedOSInfoOverride os_override_;
383 };
384
TEST_F(NetworkChangeNotifierWinCostUnsupportedOsTest,CostWithUnsupportedOS)385 TEST_F(NetworkChangeNotifierWinCostUnsupportedOsTest, CostWithUnsupportedOS) {
386 // Wait for `NetworkCostChangeNotifierWin` to finish initializing, which
387 // should initialize with an unknown cost on an unsupported OS.
388 RunUntilIdle();
389
390 // `NetworkChangeNotifierWin` must use the default implementation when
391 // `NetworkCostChangeNotifierWin` returns an unknown cost.
392 EXPECT_EQ(GetCurrentConnectionCost(),
393 GetCurrentConnectionCostFromDefaultImplementationForTesting());
394 }
395
396 } // namespace net
397