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

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: Deflaked test. Addressed memory leaks and comments. 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 res->dnsrch[0] = res->defdname;
71 res->dnsrch[1] = res->defdname + sizeof("chromium.org");
72
73 const char* ip4addr[3] = {
74 "8.8.8.8",
75 "192.168.1.1",
76 "63.1.2.4",
77 };
78
79 for (int i = 0; i < 3; ++i) {
80 struct sockaddr_in sa;
81 sa.sin_family = AF_INET;
82 sa.sin_port = htons(NAMESERVER_PORT + i - generation);
83 inet_pton(AF_INET, ip4addr[i], &sa.sin_addr);
84 res->nsaddr_list[i] = sa;
85 }
86 res->nscount = 3;
87
88 #ifdef OS_LINUX
89 const char* ip6addr[2] = {
90 "2001:db8:0::42",
91 "::FFFF:129.144.52.38",
92 };
93
94 for (int i = 0; i < 2; ++i) {
95 // Must use malloc to mimick res_ninit.
96 struct sockaddr_in6 *sa6;
97 sa6 = (struct sockaddr_in6 *)malloc(sizeof(*sa6));
98 sa6->sin6_family = AF_INET6;
99 sa6->sin6_port = htons(NAMESERVER_PORT - i);
100 inet_pton(AF_INET6, ip6addr[i], &sa6->sin6_addr);
101 res->_u._ext.nsaddrs[i] = sa6;
102 }
103 res->_u._ext.nscount6 = 2;
104 #endif
105 }
106
107
108 class DnsConfigServiceTest : public testing::Test,
109 public DnsConfigService::Observer {
110 public:
111 // Mocks
112
113 // DnsConfigService owns the instances of ResolverLib and
114 // FilePathWatcherFactory that it gets, so use simple proxies to call
115 // DnsConfigServiceTest.
116
117 // ResolverLib is owned by WatcherDelegate which is posted to WorkerPool so
118 // it must be canceled before the test is over.
119 class MockResolverLib : public DnsConfigServicePosix::ResolverLib {
120 public:
121 explicit MockResolverLib(DnsConfigServiceTest *test) : test_(test) {}
122 virtual ~MockResolverLib() {
123 base::AutoLock lock(lock_);
124 if (test_) {
125 EXPECT_TRUE(test_->IsComplete());
126 }
127 }
128 virtual int ninit(res_state res) OVERRIDE {
129 base::AutoLock lock(lock_);
130 if (test_)
131 return test_->OnNinit(res);
132 else
133 return -1;
134 }
135 void Cancel() {
136 base::AutoLock lock(lock_);
137 test_ = NULL;
138 }
139 private:
140 base::Lock lock_;
141 DnsConfigServiceTest *test_;
142 };
143
144 class MockFilePathWatcherShim :
145 public DnsConfigServicePosix::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 class MockFilePathWatcherFactory :
179 public DnsConfigServicePosix::FilePathWatcherFactory {
180 public:
181 explicit MockFilePathWatcherFactory(DnsConfigServiceTest* t) : test(t) {}
182 virtual ~MockFilePathWatcherFactory() {
183 EXPECT_TRUE(test->IsComplete());
184 }
185 virtual DnsConfigServicePosix::FilePathWatcherShim*
186 CreateFilePathWatcher() OVERRIDE {
187 return test->CreateFilePathWatcher();
188 }
189 DnsConfigServiceTest* test;
190 };
191
192 // Helpers for mocks.
193
194 DnsConfigServicePosix::FilePathWatcherShim* CreateFilePathWatcher() {
195 watcher_shim_ = new MockFilePathWatcherShim(this);
196 return watcher_shim_;
197 }
198
199 void OnShimDestroyed(MockFilePathWatcherShim* destroyed_shim) {
200 // Precaution to avoid segfault.
201 if (watcher_shim_ == destroyed_shim)
202 watcher_shim_ = NULL;
203 }
204
205 // On each event, post QuitTask to allow use of MessageLoop::Run() to
206 // synchronize the threads.
207
208 bool OnWatch() {
209 EXPECT_TRUE(message_loop_ == MessageLoop::current());
210 watch_called_ = true;
211 BreakNow("OnWatch");
212 return !fail_on_watch_;
213 }
214
215 int OnNinit(res_state res) {
216 { // Check that res_ninit is executed serially.
217 base::AutoLock lock(ninit_lock_);
218 EXPECT_FALSE(ninit_running_) << "res_ninit is not called serially!";
219 ninit_running_ = true;
220 }
221 BreakNow("OnNinit");
222 ninit_allowed_.Wait();
223 // Calling from another thread is a bit dirty, but it's protected.
224 int rv = OnNinitNonThreadSafe(res);
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 BreakNow("OnConfigChanged");
242 }
243
244 bool IsComplete() {
245 return complete_;
246 }
247
248 protected:
249 friend class BreakTask;
250 class BreakTask : public Task {
251 public:
252 BreakTask(DnsConfigServiceTest* test, std::string breakpoint)
253 : test_(test), breakpoint_(breakpoint) {}
254 virtual ~BreakTask() {}
255 virtual void Run() OVERRIDE {
256 test_->breakpoint_ = breakpoint_;
257 MessageLoop::current()->QuitNow();
258 }
259 private:
260 DnsConfigServiceTest* test_;
261 std::string breakpoint_;
262 };
263
264 void BreakNow(std::string b) {
265 message_loop_->PostTask(FROM_HERE, new BreakTask(this, b));
266 }
267
268 void RunUntilBreak(std::string b) {
269 message_loop_->Run();
270 ASSERT_EQ(breakpoint_, b);
271 }
272
273 DnsConfigServiceTest()
274 : res_lib_(new MockResolverLib(this)),
275 watcher_shim_(NULL),
276 res_generation_(1),
277 watch_called_(false),
278 got_config_(false),
279 fail_on_watch_(false),
280 fail_on_ninit_(false),
281 complete_(false),
282 ninit_allowed_(false, false),
283 ninit_called_(false, false),
284 ninit_running_(false) {
285 }
286
287 // This is on WorkerPool, but protected by ninit_allowed_.
288 int OnNinitNonThreadSafe(res_state res) {
289 if (fail_on_ninit_)
290 return -1;
291 InitializeResState(res, res_generation_);
292 // Store a (deep) copy in the fixture to later verify correctness.
293 res_nclose(&res_);
294 InitializeResState(&res_, res_generation_);
295 return 0;
296 }
297
298 // Helpers for tests.
299
300 // Lets OnNinit run OnNinitNonThreadSafe and waits for it to complete.
301 // Might get OnConfigChanged scheduled on the loop but we have no certainty.
302 void WaitForNinit() {
303 RunUntilBreak("OnNinit");
304 ninit_allowed_.Signal();
305 ninit_called_.Wait();
306 }
307
308 // test::Test methods
309 virtual void SetUp() OVERRIDE {
310 message_loop_ = MessageLoop::current();
311 service_.reset(new DnsConfigServicePosix());
312 service_->set_resolver_lib(res_lib_);
313 service_->set_watcher_factory(new MockFilePathWatcherFactory(this));
314 memset(&res_, 0, sizeof(res_));
315 }
316
317 virtual void TearDown() OVERRIDE {
318 // res_lib_ could outlive the test, so make sure it doesn't call it.
319 res_lib_->Cancel();
320 res_nclose(&res_);
321 // Allow service_ to clean up ResolverLib and FilePathWatcherFactory.
322 complete_ = true;
323 }
324
325 MockResolverLib* res_lib_;
326 MockFilePathWatcherShim* watcher_shim_;
327 // Adds variety to the content of res_state.
328 int res_generation_;
329 struct __res_state res_;
330 DnsConfig last_config_;
331
332 bool watch_called_;
333 bool got_config_;
334 bool fail_on_watch_;
335 bool fail_on_ninit_;
336 bool complete_;
337
338 // Ninit is called on WorkerPool so we need to synchronize with it.
339 base::WaitableEvent ninit_allowed_;
340 base::WaitableEvent ninit_called_;
341
342 // Protected by ninit_lock_. Used to verify that Ninit calls are serialized.
343 bool ninit_running_;
344 base::Lock ninit_lock_;
345
346 // Loop for this thread.
347 MessageLoop* message_loop_;
348
349 // Service under test.
350 scoped_ptr<DnsConfigServicePosix> service_;
351
352 std::string breakpoint_;
353 };
354
355 TEST(DnsConfigTest, ResolverConfigConvertAndEquals) {
356 struct __res_state res[2];
357 DnsConfig config[2];
358 for (int i = 0; i < 2; ++i) {
359 InitializeResState(&res[i], i);
360 ASSERT_TRUE(ConvertResToConfig(res[i], &config[i]));
361 }
362 for (int i = 0; i < 2; ++i) {
363 CompareConfig(res[i], config[i]);
364 res_nclose(&res[i]);
365 }
366 EXPECT_TRUE(config[0].Equals(config[0]));
367 EXPECT_FALSE(config[0].Equals(config[1]));
368 EXPECT_FALSE(config[1].Equals(config[0]));
369 }
370
371 TEST_F(DnsConfigServiceTest, FilePathWatcherFailures) {
372 // For these tests, disable ninit.
373 res_lib_->Cancel();
374
375 fail_on_watch_ = true;
376 service_->Watch();
377 RunUntilBreak("OnWatch");
378 EXPECT_TRUE(watch_called_) << "Must call FilePathWatcher::Watch().";
379
380 fail_on_watch_ = false;
381 watch_called_ = false;
382 RunUntilBreak("OnWatch"); // Due to backoff this will take 100ms.
383 EXPECT_TRUE(watch_called_) <<
384 "Must restart on FilePathWatcher::Watch() failure.";
385
386 watch_called_ = false;
387 ASSERT_TRUE(watcher_shim_);
388 watcher_shim_->PathError();
389 RunUntilBreak("OnWatch");
390 EXPECT_TRUE(watch_called_) <<
391 "Must restart on FilePathWatcher::Delegate::OnFilePathError().";
392
393 // Worker thread could still be posting OnResultAvailable to the message loop
394 }
395
396 TEST_F(DnsConfigServiceTest, NotifyOnValidAndDistinctConfig) {
397 service_->AddObserver(this);
398 service_->Watch();
399 RunUntilBreak("OnWatch");
400 fail_on_ninit_ = true;
401 WaitForNinit();
402
403 // If OnNinit posts OnResultAvailable before the next call, then this test
404 // verifies that failure on ninit should not cause OnConfigChanged.
405 // Otherwise, this only verifies that ninit calls are serialized.
406
407 fail_on_ninit_ = false;
408 ASSERT_TRUE(watcher_shim_);
409 watcher_shim_->PathChanged();
410 WaitForNinit();
411
412 RunUntilBreak("OnConfigChanged");
413 EXPECT_TRUE(got_config_);
414
415 message_loop_->AssertIdle();
416
417 got_config_ = false;
418 // Forget about the config to test if we get it again on AddObserver.
419 last_config_ = DnsConfig();
420 service_->RemoveObserver(this);
421 service_->AddObserver(this);
422 RunUntilBreak("OnConfigChanged");
423 EXPECT_TRUE(got_config_) << "Did not get config after AddObserver.";
424
425 // Simulate spurious FilePathChanged.
426 ASSERT_TRUE(watcher_shim_);
427 watcher_shim_->PathChanged();
428 WaitForNinit();
429
430 // OnConfigChanged will catch that the config did not actually change.
431
432 got_config_ = false;
433 ++res_generation_;
434 ASSERT_TRUE(watcher_shim_);
435 watcher_shim_->PathChanged();
436 WaitForNinit();
437 RunUntilBreak("OnConfigChanged");
438 EXPECT_TRUE(got_config_) << "Did not get config after change";
439
440 message_loop_->AssertIdle();
441
442 // Schedule two calls. OnNinit checks if it is called serially.
443 ++res_generation_;
444 ASSERT_TRUE(watcher_shim_);
445 watcher_shim_->PathChanged();
446 // ninit is blocked, so this will have to induce read_pending
447 watcher_shim_->PathChanged();
448 WaitForNinit();
449 WaitForNinit();
450 RunUntilBreak("OnConfigChanged");
451 EXPECT_TRUE(got_config_) << "Did not get config after change";
452
453 // We should be done with all tasks.
454 message_loop_->AssertIdle();
455 }
456
457 } // namespace
458
459 } // namespace net
460
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698