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