Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(293)

Side by Side Diff: net/dns/dns_config_service_posix_unittest.cc

Issue 7518028: DnsConfigService and a posix implementation (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Addressing lint errors. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 <arpa/inet.h>
6 #include <resolv.h>
7
8 #include "base/bind.h"
9 #include "base/message_loop.h"
10 #include "base/message_loop_proxy.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "net/base/ip_endpoint.h"
14 #include "net/dns/dns_config_service_posix.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace net {
18
19 namespace {
20
21 void CompareConfig(const struct __res_state &res, const DnsConfig& config) {
22 EXPECT_EQ(config.ndots, static_cast<int>(res.ndots));
23 EXPECT_EQ(config.edns0, (res.options & RES_USE_EDNS0) != 0);
24 EXPECT_EQ(config.rotate, (res.options & RES_ROTATE) != 0);
25 EXPECT_EQ(config.timeout.InSeconds(), res.retrans);
26 EXPECT_EQ(config.attempts, res.retry);
27
28 // Compare nameservers. IPv6 precede IPv4.
29 #if OS_LINUX
30 size_t nscount6 = res._u._ext.nscount6;
31 #else
32 size_t nscount6 = 0;
33 #endif
34 size_t nscount4 = res.nscount;
35 ASSERT_EQ(config.nameservers.size(), nscount6 + nscount4);
36 #if OS_LINUX
37 for (size_t i = 0; i < nscount6; ++i) {
38 IPEndPoint ipe;
39 EXPECT_TRUE(ipe.FromSockAddr(
40 reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]),
41 sizeof *res._u._ext.nsaddrs[i]));
42 EXPECT_EQ(config.nameservers[i], ipe);
43 }
44 #endif
45 for (size_t i = 0; i < nscount4; ++i) {
46 IPEndPoint ipe;
47 EXPECT_TRUE(ipe.FromSockAddr(
48 reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
49 sizeof res.nsaddr_list[i]));
50 EXPECT_EQ(config.nameservers[nscount6 + i], ipe);
51 }
52
53 ASSERT_TRUE(config.search.size() <= MAXDNSRCH);
54 EXPECT_TRUE(res.dnsrch[config.search.size()] == NULL);
55 for (size_t i = 0; i < config.search.size(); ++i) {
56 EXPECT_EQ(config.search[i], res.dnsrch[i]);
57 }
58 }
59
60 // Fills in |res| with sane configuration. Change |generation| to add diversity.
61 void InitializeResState(res_state res, int generation) {
62 memset(res, 0, sizeof(*res));
63 res->options = RES_INIT | RES_ROTATE;
64 res->ndots = 2;
65 res->retrans = 8;
66 res->retry = 7;
67
68 const char kDnsrch[] = "chromium.org" "\0" "example.com";
69 memcpy(res->defdname, kDnsrch, sizeof(kDnsrch));
70 memset(res->dnsrch, 0, sizeof(res->dnsrch));
71 res->dnsrch[0] = res->defdname;
72 res->dnsrch[1] = res->defdname + sizeof("chromium.org");
73
74 const char* ip4addr[3] = {
75 "8.8.8.8",
76 "192.168.1.1",
77 "63.1.2.4",
78 };
79
80 for (int i = 0; i < 3; ++i) {
81 struct sockaddr_in sa;
82 sa.sin_family = AF_INET;
83 sa.sin_port = htons(NAMESERVER_PORT + i - generation);
84 inet_pton(AF_INET, ip4addr[i], &sa.sin_addr);
85 res->nsaddr_list[i] = sa;
86 }
87 res->nscount = 3;
88
89 #ifdef OS_LINUX
90 const char* ip6addr[2] = {
91 "2001:db8:0::42",
92 "::FFFF:129.144.52.38",
93 };
94
95 for (int i = 0; i < 2; ++i) {
96 // Must use malloc to mimick res_ninit.
97 struct sockaddr_in6 *sa6;
98 sa6 = (struct sockaddr_in6 *)malloc(sizeof(*sa6));
99 sa6->sin6_family = AF_INET6;
100 sa6->sin6_port = htons(NAMESERVER_PORT - i);
101 inet_pton(AF_INET6, ip6addr[i], &sa6->sin6_addr);
102 res->_u._ext.nsaddrs[i] = sa6;
103 }
104 res->_u._ext.nscount6 = 2;
105 #endif
106 }
107
108
cbentzel 2011/08/15 18:16:34 Nit: extra line.
szym 2011/08/15 22:02:01 Done.
109 class DnsConfigServiceTest : public testing::Test,
110 public DnsConfigService::Observer {
111 public:
112 // Mocks
113
114 // DnsConfigService owns the instances of ResolverLib and
115 // FilePathWatcherFactory that it gets, so use simple proxies to call
116 // DnsConfigServiceTest.
117
118 // ResolverLib is owned by WatcherDelegate which is posted to WorkerPool so
119 // it must be cancelled before the test is over.
120 class MockResolverLib : public ResolverLib {
121 public:
122 explicit MockResolverLib(DnsConfigServiceTest *test) : test_(test) {}
123 virtual ~MockResolverLib() {
124 base::AutoLock lock(lock_);
125 if (test_) {
126 EXPECT_TRUE(test_->IsComplete());
127 }
128 }
129 virtual int ninit(res_state res) OVERRIDE {
cbentzel 2011/08/15 18:16:34 Does this need an nclose()?
szym 2011/08/15 22:02:01 Done. There were a few missing nclose calls.
130 base::AutoLock lock(lock_);
131 if (test_)
132 return test_->OnNinit(res);
133 else
134 return -1;
135 }
136 void Cancel() {
137 base::AutoLock lock(lock_);
138 test_ = NULL;
139 }
140 private:
141 base::Lock lock_;
142 DnsConfigServiceTest *test_;
143 };
144
145 class MockFilePathWatcherShim : public FilePathWatcherShim {
146 public:
147 typedef base::files::FilePathWatcher::Delegate Delegate;
148
149 explicit MockFilePathWatcherShim(DnsConfigServiceTest* t) : test_(t) {}
150 virtual ~MockFilePathWatcherShim() {
151 test_->OnShimDestroyed(this);
152 }
153
154 // Enforce one-Watch-per-lifetime as the original FilePathWatcher
155 virtual bool Watch(const FilePath& path,
156 Delegate* delegate) OVERRIDE {
157 EXPECT_TRUE(path_.empty()) << "Only one-Watch-per-lifetime allowed.";
158 EXPECT_TRUE(!delegate_.get());
159 path_ = path;
160 delegate_ = delegate;
161 return test_->OnWatch();
162 }
163
164 void PathChanged() {
165 delegate_->OnFilePathChanged(path_);
166 }
167
168 void PathError() {
169 delegate_->OnFilePathError(path_);
170 }
171
172 private:
173 FilePath path_;
174 scoped_refptr<Delegate> delegate_;
175 DnsConfigServiceTest* test_;
176 };
177
178 struct MockFilePathWatcherFactory : public FilePathWatcherFactory {
179 explicit MockFilePathWatcherFactory(DnsConfigServiceTest* t) : test(t) {}
180 virtual ~MockFilePathWatcherFactory() {
181 EXPECT_TRUE(test->IsComplete());
182 }
183 virtual FilePathWatcherShim* CreateFilePathWatcher() OVERRIDE {
184 return test->CreateFilePathWatcher();
185 }
186 DnsConfigServiceTest* test;
187 };
188
189 // Helpers for mocks.
190
191 FilePathWatcherShim* CreateFilePathWatcher() {
192 watcher_shim_ = new MockFilePathWatcherShim(this);
193 return watcher_shim_;
194 }
195
196 void OnShimDestroyed(MockFilePathWatcherShim* destroyed_shim) {
197 // Precaution to avoid segfault.
198 if (watcher_shim_ == destroyed_shim)
199 watcher_shim_ = NULL;
200 }
201
202 // On each event, post QuitTask to allow use of MessageLoop::Run() to
203 // synchronize the threads.
204
205 bool OnWatch() {
206 EXPECT_TRUE(message_loop_ == MessageLoop::current());
207 watch_called_ = true;
208 message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
209 return !fail_on_watch_;
210 }
211
212 int OnNinit(res_state res) {
213 { // Check that res_ninit is executed serially.
214 base::AutoLock lock(ninit_lock_);
215 EXPECT_FALSE(ninit_running_) << "res_ninit is not called serially!";
216 ninit_running_ = true;
217 }
218
219 message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
220
221 ninit_allowed_.Wait();
222 // Calling from another thread is a bit dirty, but it works.
223 int rv = OnNinitNonThreadSafe(res);
224
225 // This lock might be destroyed after ninit_called_ is signalled.
226 {
227 base::AutoLock lock(ninit_lock_);
228 ninit_running_ = false;
229 }
230 ninit_called_.Signal();
231 return rv;
232 }
233
234 virtual void OnConfigChanged(const DnsConfig& new_config) OVERRIDE {
235 EXPECT_TRUE(message_loop_ == MessageLoop::current());
236 CompareConfig(res_, new_config);
237 EXPECT_FALSE(new_config.Equals(last_config_)) <<
238 "Config must be different from last call.";
239 last_config_ = new_config;
240 got_config_ = true;
241 message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
242 }
243
244 bool IsComplete() {
245 return complete_;
246 }
247
248 protected:
249 DnsConfigServiceTest()
250 : res_lib_(new MockResolverLib(this)),
cbentzel 2011/08/15 18:16:34 Does this get leaked in favor of the one created i
szym 2011/08/15 22:02:01 Good catch. Done.
251 watcher_shim_(NULL),
252 res_generation_(1),
253 watch_called_(false),
254 got_config_(false),
255 fail_on_watch_(false),
256 fail_on_ninit_(false),
257 complete_(false),
258 ninit_allowed_(false, false),
259 ninit_called_(false, false),
260 ninit_running_(false) {
261 }
262
263 // This is on WorkerPool, but protected by ninit_allowed_.
264 int OnNinitNonThreadSafe(res_state res) {
265 if (fail_on_ninit_)
266 return -1;
267 InitializeResState(res, res_generation_);
268 // Store a (deep) copy in the fixture to later verify correctness.
269 InitializeResState(&res_, res_generation_);
270 return 0;
271 }
272
273 // Helpers for tests.
274
275 // Lets OnNinit run OnNinitNonThreadSafe and waits for it to complete.
276 // Might get OnConfigChanged scheduled on the loop but we have no certainty.
277 void WaitForNinit() {
278 message_loop_->Run(); // Until OnNinit
279 ninit_allowed_.Signal();
280 ninit_called_.Wait();
281 }
282
283 // test::Test methods
284 virtual void SetUp() OVERRIDE {
285 message_loop_ = MessageLoop::current();
286 service_.reset(new DnsConfigServicePosix());
287 res_lib_ = new MockResolverLib(this);
288 service_->set_resolver_lib(res_lib_);
289 service_->set_watcher_factory(new MockFilePathWatcherFactory(this));
290 }
291
292 virtual void TearDown() OVERRIDE {
293 // res_lib_ could outlive the test, so make sure it doesn't call it.
294 res_lib_->Cancel();
295 // Allow service_ to clean up ResolverLib and FilePathWatcherFactory.
296 complete_ = true;
297 }
298
299 MockResolverLib* res_lib_;
300 MockFilePathWatcherShim* watcher_shim_;
301 // Adds variety to the content of res_state.
302 int res_generation_;
303 struct __res_state res_;
304 DnsConfig last_config_;
305
306 bool watch_called_;
307 bool got_config_;
308 bool fail_on_watch_;
309 bool fail_on_ninit_;
310 bool complete_;
311
312 // Ninit is called on WorkerPool so we need to synchronize with it.
313 base::WaitableEvent ninit_allowed_;
314 base::WaitableEvent ninit_called_;
315
316 // Protected by ninit_lock_. Used to verify that Ninit calls are serialized.
317 bool ninit_running_;
318 base::Lock ninit_lock_;
319
320 // Loop for this thread.
321 MessageLoop* message_loop_;
322
323 // Service under test.
324 scoped_ptr<DnsConfigServicePosix> service_;
325 };
326
327 TEST(DnsConfigTest, ResolverConfigConvertAndEquals) {
328 struct __res_state res[2];
329 DnsConfig config[2];
330 for (int i = 0; i < 2; ++i) {
331 InitializeResState(&res[i], i);
332 ASSERT_TRUE(ConvertResToConfig(res[i], &config[i]));
333 }
334 for (int i = 0; i < 2; ++i) {
335 CompareConfig(res[0], config[0]);
336 }
337 EXPECT_TRUE(config[0].Equals(config[0]));
338 EXPECT_FALSE(config[0].Equals(config[1]));
339 EXPECT_FALSE(config[1].Equals(config[0]));
340 }
341
342 TEST_F(DnsConfigServiceTest, FilePathWatcherFailures) {
343 // For this tests, disable ninit.
344 res_lib_->Cancel();
345
346 fail_on_watch_ = true;
347 service_->Watch();
348 message_loop_->Run(); // until Watch
349 EXPECT_TRUE(watch_called_) << "Must call FilePathWatcher::Watch().";
cbentzel 2011/08/15 18:16:34 I need to think through this some more, but I'm wo
350
351 fail_on_watch_ = false;
352 watch_called_ = false;
353 message_loop_->Run(); // until Watch, takes 100ms
cbentzel 2011/08/15 18:16:34 You may want to make it clear that there isn't a s
szym 2011/08/15 22:02:01 Done.
354 EXPECT_TRUE(watch_called_) <<
355 "Must restart on FilePathWatcher::Watch() failure.";
356
357 watch_called_ = false;
358 ASSERT_TRUE(watcher_shim_);
359 watcher_shim_->PathError();
360 message_loop_->Run(); // until Watch
361 EXPECT_TRUE(watch_called_) <<
362 "Must restart on FilePathWatcher::Delegate::OnFilePathError().";
363
364 // Worker thread could still be posting OnResultAvailable to the message loop
365 }
366
367 TEST_F(DnsConfigServiceTest, NotifyOnValidAndDistinctConfig) {
368 service_->AddObserver(this);
369 service_->Watch();
370 watch_called_ = false;
371 fail_on_ninit_ = true;
372 WaitForNinit();
373
374 fail_on_ninit_ = false;
375 ASSERT_TRUE(watcher_shim_);
376 watcher_shim_->PathChanged();
377 WaitForNinit();
378 message_loop_->Run(); // until OnConfigChanged
379 EXPECT_TRUE(got_config_);
380
381 got_config_ = false;
382 // Forget about the config to test if we get it again on AddObserver.
383 last_config_ = DnsConfig();
384 service_->RemoveObserver(this);
385 service_->AddObserver(this);
386 EXPECT_TRUE(got_config_) << "Did not get config after AddObserver.";
387
388 // OnConfigChanged will catch that the config did not actually change.
389 got_config_ = false;
390 ASSERT_TRUE(watcher_shim_);
391 watcher_shim_->PathChanged();
392 WaitForNinit();
393
394 got_config_ = false;
395 ++res_generation_;
396 ASSERT_TRUE(watcher_shim_);
397 watcher_shim_->PathChanged();
398 WaitForNinit();
399 message_loop_->Run(); // until OnConfigchanged
400 EXPECT_TRUE(got_config_) << "Did not get config after change";
401
402 message_loop_->AssertIdle();
403
404 // Schedule two calls. OnNinit checks if it is called serially.
405 ++res_generation_;
406 ASSERT_TRUE(watcher_shim_);
407 watcher_shim_->PathChanged();
408 watcher_shim_->PathChanged();
409 WaitForNinit();
410 ++res_generation_;
411 WaitForNinit();
412 message_loop_->Run(); // until OnConfigchanged
413 EXPECT_TRUE(got_config_) << "Did not get config after change";
414
415 // This check might be too strict.
416 EXPECT_FALSE(watch_called_) << "Unexpected Watch() without failures.";
417
418 // We should be done with all tasks.
419 message_loop_->AssertIdle();
420 }
421
422 } // namespace
423
424 } // namespace net
425
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698