• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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/dns/system_dns_config_change_notifier.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include "base/functional/bind.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/run_loop.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "base/task/task_traits.h"
15 #include "base/task/thread_pool.h"
16 #include "net/base/ip_address.h"
17 #include "net/base/ip_endpoint.h"
18 #include "net/dns/dns_hosts.h"
19 #include "net/dns/test_dns_config_service.h"
20 #include "net/test/test_with_task_environment.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 namespace net {
25 
26 namespace {
27 const std::vector<IPEndPoint> kNameservers = {
28     IPEndPoint(IPAddress(1, 2, 3, 4), 95)};
29 const std::vector<IPEndPoint> kNameservers2 = {
30     IPEndPoint(IPAddress(2, 3, 4, 5), 195)};
31 const DnsConfig kConfig(kNameservers);
32 const DnsConfig kConfig2(kNameservers2);
33 }  // namespace
34 
35 class SystemDnsConfigChangeNotifierTest : public TestWithTaskEnvironment {
36  public:
37   // Set up a change notifier, owned on a dedicated blockable task runner, with
38   // a faked underlying DnsConfigService.
SystemDnsConfigChangeNotifierTest()39   SystemDnsConfigChangeNotifierTest()
40       : notifier_task_runner_(
41             base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {
42     auto test_service = std::make_unique<TestDnsConfigService>();
43     notifier_task_runner_->PostTask(
44         FROM_HERE,
45         base::BindOnce(&TestDnsConfigService::OnHostsRead,
46                        base::Unretained(test_service.get()), DnsHosts()));
47     test_config_service_ = test_service.get();
48 
49     notifier_ = std::make_unique<SystemDnsConfigChangeNotifier>(
50         notifier_task_runner_, std::move(test_service));
51   }
52 
53  protected:
54   // Test observer implementation that records all notifications received in a
55   // vector, and also validates that all notifications are received on the
56   // expected sequence.
57   class TestObserver : public SystemDnsConfigChangeNotifier::Observer {
58    public:
OnSystemDnsConfigChanged(absl::optional<DnsConfig> config)59     void OnSystemDnsConfigChanged(absl::optional<DnsConfig> config) override {
60       DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
61       configs_received_.push_back(std::move(config));
62 
63       DCHECK_GT(notifications_remaining_, 0);
64       if (--notifications_remaining_ == 0)
65         run_loop_->Quit();
66     }
67 
WaitForNotification()68     void WaitForNotification() { WaitForNotifications(1); }
WaitForNotifications(int num_notifications)69     void WaitForNotifications(int num_notifications) {
70       DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
71 
72       notifications_remaining_ = num_notifications;
73       run_loop_->Run();
74       run_loop_ = std::make_unique<base::RunLoop>();
75     }
76 
ExpectNoMoreNotifications()77     void ExpectNoMoreNotifications() {
78       DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
79       configs_received_.clear();
80       base::RunLoop().RunUntilIdle();
81       EXPECT_TRUE(configs_received_.empty());
82     }
83 
configs_received()84     std::vector<absl::optional<DnsConfig>>& configs_received() {
85       return configs_received_;
86     }
87 
88    private:
89     int notifications_remaining_ = 0;
90     std::unique_ptr<base::RunLoop> run_loop_ =
91         std::make_unique<base::RunLoop>();
92     std::vector<absl::optional<DnsConfig>> configs_received_;
93     SEQUENCE_CHECKER(sequence_checker_);
94   };
95 
96   // Load a config and wait for it to be received by the notifier.
LoadConfig(const DnsConfig & config,bool already_loaded=false)97   void LoadConfig(const DnsConfig& config, bool already_loaded = false) {
98     TestObserver observer;
99     notifier_->AddObserver(&observer);
100 
101     // If |notifier_| already has a config loaded, |observer| will first get a
102     // notification for that initial config.
103     if (already_loaded)
104       observer.WaitForNotification();
105 
106     notifier_task_runner_->PostTask(
107         FROM_HERE,
108         base::BindOnce(&TestDnsConfigService::OnConfigRead,
109                        base::Unretained(test_config_service_), config));
110     observer.WaitForNotification();
111 
112     notifier_->RemoveObserver(&observer);
113   }
114 
115   scoped_refptr<base::SequencedTaskRunner> notifier_task_runner_;
116   std::unique_ptr<SystemDnsConfigChangeNotifier> notifier_;
117   // Owned by |notifier_|.
118   raw_ptr<TestDnsConfigService> test_config_service_;
119 };
120 
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveNotification)121 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveNotification) {
122   TestObserver observer;
123 
124   notifier_->AddObserver(&observer);
125   notifier_task_runner_->PostTask(
126       FROM_HERE,
127       base::BindOnce(&TestDnsConfigService::OnConfigRead,
128                      base::Unretained(test_config_service_), kConfig));
129   observer.WaitForNotification();
130 
131   EXPECT_THAT(observer.configs_received(),
132               testing::ElementsAre(testing::Optional(kConfig)));
133   observer.ExpectNoMoreNotifications();
134 
135   notifier_->RemoveObserver(&observer);
136 }
137 
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveNotification_Multiple)138 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveNotification_Multiple) {
139   TestObserver observer;
140 
141   notifier_->AddObserver(&observer);
142   notifier_task_runner_->PostTask(
143       FROM_HERE,
144       base::BindOnce(&TestDnsConfigService::OnConfigRead,
145                      base::Unretained(test_config_service_), kConfig));
146   notifier_task_runner_->PostTask(
147       FROM_HERE,
148       base::BindOnce(&TestDnsConfigService::OnConfigRead,
149                      base::Unretained(test_config_service_), kConfig2));
150   observer.WaitForNotifications(2);
151 
152   EXPECT_THAT(observer.configs_received(),
153               testing::ElementsAre(testing::Optional(kConfig),
154                                    testing::Optional(kConfig2)));
155   observer.ExpectNoMoreNotifications();
156 
157   notifier_->RemoveObserver(&observer);
158 }
159 
160 // If the notifier already has a config loaded, a new observer should receive an
161 // initial notification for that config.
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveInitialNotification)162 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveInitialNotification) {
163   LoadConfig(kConfig);
164 
165   TestObserver observer;
166   notifier_->AddObserver(&observer);
167   observer.WaitForNotification();
168 
169   EXPECT_THAT(observer.configs_received(),
170               testing::ElementsAre(testing::Optional(kConfig)));
171   observer.ExpectNoMoreNotifications();
172 
173   notifier_->RemoveObserver(&observer);
174 }
175 
176 // If multiple configs have been read before adding an Observer, should notify
177 // it only of the most recent.
TEST_F(SystemDnsConfigChangeNotifierTest,ReceiveInitialNotification_Multiple)178 TEST_F(SystemDnsConfigChangeNotifierTest, ReceiveInitialNotification_Multiple) {
179   LoadConfig(kConfig);
180   LoadConfig(kConfig2, true /* already_loaded */);
181 
182   TestObserver observer;
183   notifier_->AddObserver(&observer);
184   observer.WaitForNotification();
185 
186   EXPECT_THAT(observer.configs_received(),
187               testing::ElementsAre(testing::Optional(kConfig2)));
188   observer.ExpectNoMoreNotifications();
189 
190   notifier_->RemoveObserver(&observer);
191 }
192 
TEST_F(SystemDnsConfigChangeNotifierTest,NotificationsStopAfterRemoval)193 TEST_F(SystemDnsConfigChangeNotifierTest, NotificationsStopAfterRemoval) {
194   TestObserver observer;
195   notifier_->AddObserver(&observer);
196   notifier_->RemoveObserver(&observer);
197 
198   LoadConfig(kConfig);
199   LoadConfig(kConfig2, true /* already_loaded */);
200 
201   EXPECT_TRUE(observer.configs_received().empty());
202   observer.ExpectNoMoreNotifications();
203 }
204 
TEST_F(SystemDnsConfigChangeNotifierTest,UnchangedConfigs)205 TEST_F(SystemDnsConfigChangeNotifierTest, UnchangedConfigs) {
206   LoadConfig(kConfig);
207 
208   TestObserver observer;
209   notifier_->AddObserver(&observer);
210   observer.WaitForNotification();
211 
212   // Expect no notifications from duplicate configs.
213   notifier_task_runner_->PostTask(
214       FROM_HERE,
215       base::BindOnce(&TestDnsConfigService::OnConfigRead,
216                      base::Unretained(test_config_service_), kConfig));
217   notifier_task_runner_->PostTask(
218       FROM_HERE,
219       base::BindOnce(&TestDnsConfigService::OnConfigRead,
220                      base::Unretained(test_config_service_), kConfig));
221   observer.ExpectNoMoreNotifications();
222 
223   // Notification on new config.
224   notifier_task_runner_->PostTask(
225       FROM_HERE,
226       base::BindOnce(&TestDnsConfigService::OnConfigRead,
227                      base::Unretained(test_config_service_), kConfig2));
228   observer.WaitForNotification();
229   EXPECT_THAT(observer.configs_received(),
230               testing::ElementsAre(testing::Optional(kConfig2)));
231   observer.ExpectNoMoreNotifications();
232 
233   notifier_->RemoveObserver(&observer);
234 }
235 
TEST_F(SystemDnsConfigChangeNotifierTest,UnloadedConfig)236 TEST_F(SystemDnsConfigChangeNotifierTest, UnloadedConfig) {
237   LoadConfig(kConfig);
238 
239   TestObserver observer;
240   notifier_->AddObserver(&observer);
241   // Initial config.
242   observer.WaitForNotification();
243 
244   notifier_task_runner_->PostTask(
245       FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
246                                 base::Unretained(test_config_service_)));
247   observer.WaitForNotification();
248 
249   EXPECT_THAT(observer.configs_received(),
250               testing::ElementsAre(testing::Optional(kConfig), absl::nullopt));
251   observer.ExpectNoMoreNotifications();
252 
253   notifier_->RemoveObserver(&observer);
254 }
255 
256 // All invalid configs are considered the same for notifications, so only expect
257 // a single notification on multiple config invalidations.
TEST_F(SystemDnsConfigChangeNotifierTest,UnloadedConfig_Multiple)258 TEST_F(SystemDnsConfigChangeNotifierTest, UnloadedConfig_Multiple) {
259   LoadConfig(kConfig);
260 
261   TestObserver observer;
262   notifier_->AddObserver(&observer);
263   // Initial config.
264   observer.WaitForNotification();
265 
266   notifier_task_runner_->PostTask(
267       FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
268                                 base::Unretained(test_config_service_)));
269   notifier_task_runner_->PostTask(
270       FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
271                                 base::Unretained(test_config_service_)));
272   observer.WaitForNotification();  // Only 1 notification expected.
273 
274   EXPECT_THAT(observer.configs_received(),
275               testing::ElementsAre(testing::Optional(kConfig), absl::nullopt));
276   observer.ExpectNoMoreNotifications();
277 
278   notifier_->RemoveObserver(&observer);
279 }
280 
TEST_F(SystemDnsConfigChangeNotifierTest,InitialConfigInvalid)281 TEST_F(SystemDnsConfigChangeNotifierTest, InitialConfigInvalid) {
282   // Add and invalidate a config (using an extra observer to wait for
283   // invalidation to complete).
284   LoadConfig(kConfig);
285   TestObserver setup_observer;
286   notifier_->AddObserver(&setup_observer);
287   setup_observer.WaitForNotification();
288   notifier_task_runner_->PostTask(
289       FROM_HERE, base::BindOnce(&TestDnsConfigService::InvalidateConfig,
290                                 base::Unretained(test_config_service_)));
291   setup_observer.WaitForNotification();
292   notifier_->RemoveObserver(&setup_observer);
293 
294   TestObserver observer;
295   notifier_->AddObserver(&observer);
296 
297   // No notification expected until first valid config.
298   observer.ExpectNoMoreNotifications();
299 
300   // Notification on new config.
301   notifier_task_runner_->PostTask(
302       FROM_HERE,
303       base::BindOnce(&TestDnsConfigService::OnConfigRead,
304                      base::Unretained(test_config_service_), kConfig));
305   observer.WaitForNotification();
306   EXPECT_THAT(observer.configs_received(),
307               testing::ElementsAre(testing::Optional(kConfig)));
308   observer.ExpectNoMoreNotifications();
309 
310   notifier_->RemoveObserver(&observer);
311 }
312 
TEST_F(SystemDnsConfigChangeNotifierTest,RefreshConfig)313 TEST_F(SystemDnsConfigChangeNotifierTest, RefreshConfig) {
314   test_config_service_->SetConfigForRefresh(kConfig);
315 
316   TestObserver observer;
317   notifier_->AddObserver(&observer);
318 
319   notifier_->RefreshConfig();
320   observer.WaitForNotification();
321 
322   EXPECT_THAT(observer.configs_received(),
323               testing::ElementsAre(testing::Optional(kConfig)));
324   observer.ExpectNoMoreNotifications();
325 
326   notifier_->RemoveObserver(&observer);
327 }
328 
329 }  // namespace net
330