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