OLD | NEW |
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef NET_BASE_HOST_RESOLVER_UNITTEST_H_ | 5 #include "net/base/mock_host_resolver.h" |
6 #define NET_BASE_HOST_RESOLVER_UNITTEST_H_ | |
7 | |
8 #ifdef UNIT_TEST | |
9 | |
10 #include <list> | |
11 | 6 |
12 #include "base/string_util.h" | 7 #include "base/string_util.h" |
13 #include "base/platform_thread.h" | 8 #include "base/platform_thread.h" |
14 #include "base/ref_counted.h" | 9 #include "base/ref_counted.h" |
15 #include "base/waitable_event.h" | |
16 #include "net/base/host_resolver.h" | |
17 #include "net/base/net_errors.h" | 10 #include "net/base/net_errors.h" |
18 | 11 |
19 namespace net { | 12 namespace net { |
20 | 13 |
21 // In most cases, it is important that unit tests avoid making actual DNS | 14 MockHostResolver::MockHostResolver() { |
22 // queries since the resulting tests can be flaky, especially if the network is | 15 Reset(NULL, 0, 0); |
23 // unreliable for some reason. To simplify writing tests that avoid making | 16 } |
24 // actual DNS queries, the following helper class may be used: | 17 |
25 // | 18 int MockHostResolver::Resolve(const RequestInfo& info, |
26 // scoped_refptr<RuleBasedHostMapper> host_mapper = new RuleBasedHostMapper(); | 19 AddressList* addresses, |
27 // host_mapper->AddRule("foo.com", "1.2.3.4"); | 20 CompletionCallback* callback, |
28 // host_mapper->AddRule("bar.com", "2.3.4.5"); | 21 RequestHandle* out_req) { |
29 // | 22 return impl_->Resolve(info, addresses, callback, out_req); |
30 // Don't forget to actually set your mapper, probably with ScopedHostMapper! | 23 } |
31 // | 24 |
32 // The above rules define a static mapping from hostnames to IP address | 25 void MockHostResolver::CancelRequest(RequestHandle req) { |
33 // literals. The first parameter to AddRule specifies a host pattern to match | 26 impl_->CancelRequest(req); |
34 // against, and the second parameter indicates what value should be used to | 27 } |
35 // replace the given hostname. So, the following is also supported: | 28 |
36 // | 29 void MockHostResolver::AddObserver(Observer* observer) { |
37 // host_mapper->AddRule("*.com", "127.0.0.1"); | 30 impl_->AddObserver(observer); |
38 // | 31 } |
39 // Replacement doesn't have to be string representing an IP address. It can | 32 |
40 // re-map one hostname to another as well. | 33 void MockHostResolver::RemoveObserver(Observer* observer) { |
41 class RuleBasedHostMapper : public HostMapper { | 34 impl_->RemoveObserver(observer); |
42 public: | 35 } |
43 // Any hostname matching the given pattern will be replaced with the given | 36 |
44 // replacement value. Usually, replacement should be an IP address literal. | 37 void MockHostResolver::Shutdown() { |
45 void AddRule(const char* host_pattern, const char* replacement) { | 38 impl_->Shutdown(); |
46 rules_.push_back(Rule(host_pattern, replacement)); | 39 } |
| 40 |
| 41 void MockHostResolver::Reset(HostResolverProc* interceptor, |
| 42 int max_cache_entries, |
| 43 int max_cache_age_ms) { |
| 44 // At the root of the chain, map everything to localhost. |
| 45 scoped_refptr<RuleBasedHostResolverProc> catchall = |
| 46 new RuleBasedHostResolverProc(NULL); |
| 47 catchall->AddRule("*", "127.0.0.1"); |
| 48 |
| 49 // Next add a rules-based layer the use controls. |
| 50 rules_ = new RuleBasedHostResolverProc(catchall); |
| 51 |
| 52 HostResolverProc* proc = rules_; |
| 53 |
| 54 // Lastly add the provided interceptor to the front of the chain. |
| 55 if (interceptor) { |
| 56 interceptor->set_previous_proc(proc); |
| 57 proc = interceptor; |
47 } | 58 } |
48 | 59 |
49 void AddRuleWithLatency(const char* host_pattern, const char* replacement, | 60 impl_ = new HostResolverImpl(proc, max_cache_entries, max_cache_age_ms); |
50 int latency) { | 61 } |
51 rules_.push_back(Rule(host_pattern, replacement, latency)); | |
52 } | |
53 | 62 |
54 // Make sure that |host| will not be re-mapped or even processed by underlying | 63 //----------------------------------------------------------------------------- |
55 // host mappers. It can also be a pattern. | |
56 void AllowDirectLookup(const char* host) { | |
57 rules_.push_back(Rule(host, "", true)); | |
58 } | |
59 | 64 |
60 // Simulate a lookup failure for |host| (it also can be a pattern). | 65 struct RuleBasedHostResolverProc::Rule { |
61 void AddSimulatedFailure(const char* host) { | 66 std::string host_pattern; |
62 AddRule(host, ""); | 67 std::string replacement; |
63 } | 68 int latency_ms; // In milliseconds. |
64 | 69 bool direct; // if true, don't mangle hostname and ignore replacement |
65 virtual std::string Map(const std::string& host) { | 70 Rule(const std::string& host_pattern, const std::string& replacement) |
66 RuleList::iterator r; | 71 : host_pattern(host_pattern), |
67 for (r = rules_.begin(); r != rules_.end(); ++r) { | 72 replacement(replacement), |
68 if (MatchPattern(host, r->host_pattern)) { | 73 latency_ms(0), |
69 if (r->latency != 0) { | 74 direct(false) {} |
70 PlatformThread::Sleep(r->latency); | 75 Rule(const std::string& host_pattern, const std::string& replacement, |
71 r->latency = 1; // Simulate cache warmup. | 76 const int latency_ms) |
72 } | 77 : host_pattern(host_pattern), |
73 return r->direct ? host : r->replacement; | 78 replacement(replacement), |
74 } | 79 latency_ms(latency_ms), |
75 } | 80 direct(false) {} |
76 | 81 Rule(const std::string& host_pattern, const std::string& replacement, |
77 return MapUsingPrevious(host); | 82 const bool direct) |
78 } | 83 : host_pattern(host_pattern), |
79 | 84 replacement(replacement), |
80 private: | 85 latency_ms(0), |
81 struct Rule { | 86 direct(direct) {} |
82 std::string host_pattern; | |
83 std::string replacement; | |
84 int latency; // in milliseconds | |
85 bool direct; // if true, don't mangle hostname and ignore replacement | |
86 Rule(const char* h, const char* r) | |
87 : host_pattern(h), | |
88 replacement(r), | |
89 latency(0), | |
90 direct(false) {} | |
91 Rule(const char* h, const char* r, const int l) | |
92 : host_pattern(h), | |
93 replacement(r), | |
94 latency(l), | |
95 direct(false) {} | |
96 Rule(const char* h, const char* r, const bool d) | |
97 : host_pattern(h), | |
98 replacement(r), | |
99 latency(0), | |
100 direct(d) {} | |
101 }; | |
102 typedef std::list<Rule> RuleList; | |
103 RuleList rules_; | |
104 }; | 87 }; |
105 | 88 |
106 // Using WaitingHostMapper you can simulate very long lookups, for example | 89 RuleBasedHostResolverProc::RuleBasedHostResolverProc(HostResolverProc* previous) |
107 // to test code which cancels a request. Example usage: | 90 : HostResolverProc(previous) { |
108 // | 91 } |
109 // scoped_refptr<WaitingHostMapper> mapper = new WaitingHostMapper(); | 92 |
110 // ScopedHostMapper scoped_mapper(mapper.get()); | 93 RuleBasedHostResolverProc::~RuleBasedHostResolverProc() { |
111 // | 94 } |
112 // (start the lookup asynchronously) | 95 |
113 // (cancel the lookup) | 96 void RuleBasedHostResolverProc::AddRule(const std::string& host_pattern, |
114 // | 97 const std::string& replacement) { |
115 // mapper->Signal(); | 98 rules_.push_back(Rule(host_pattern, replacement)); |
116 class WaitingHostMapper : public HostMapper { | 99 } |
117 public: | 100 |
118 WaitingHostMapper() : event_(false, false) { | 101 void RuleBasedHostResolverProc::AddRuleWithLatency( |
| 102 const std::string& host_pattern, |
| 103 const std::string& replacement, int latency_ms) { |
| 104 rules_.push_back(Rule(host_pattern, replacement, latency_ms)); |
| 105 } |
| 106 |
| 107 void RuleBasedHostResolverProc::AllowDirectLookup(const std::string& host) { |
| 108 rules_.push_back(Rule(host, "", true)); |
| 109 } |
| 110 |
| 111 void RuleBasedHostResolverProc::AddSimulatedFailure(const std::string& host) { |
| 112 AddRule(host, ""); |
| 113 } |
| 114 |
| 115 int RuleBasedHostResolverProc::Resolve(const std::string& host, |
| 116 AddressList* addrlist) { |
| 117 RuleList::iterator r; |
| 118 for (r = rules_.begin(); r != rules_.end(); ++r) { |
| 119 if (MatchPattern(host, r->host_pattern)) { |
| 120 if (r->latency_ms != 0) { |
| 121 PlatformThread::Sleep(r->latency_ms); |
| 122 // Hmm, this seems unecessary. |
| 123 r->latency_ms = 1; |
| 124 } |
| 125 const std::string& effective_host = r->direct ? host : r->replacement; |
| 126 if (effective_host.empty()) |
| 127 return ERR_NAME_NOT_RESOLVED; |
| 128 return SystemHostResolverProc(effective_host, addrlist); |
| 129 } |
119 } | 130 } |
| 131 return ResolveUsingPrevious(host, addrlist); |
| 132 } |
120 | 133 |
121 void Signal() { | 134 //----------------------------------------------------------------------------- |
122 event_.Signal(); | |
123 } | |
124 | 135 |
125 private: | 136 ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc( |
126 virtual std::string Map(const std::string& host) { | 137 HostResolverProc* proc) : current_proc_(proc) { |
127 event_.Wait(); | 138 previous_proc_ = HostResolverProc::SetDefault(current_proc_); |
128 return MapUsingPrevious(host); | 139 current_proc_->set_previous_proc(previous_proc_); |
129 } | 140 } |
130 | 141 |
131 base::WaitableEvent event_; | 142 ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() { |
132 }; | 143 HostResolverProc* old_proc = HostResolverProc::SetDefault(previous_proc_); |
| 144 // The lifetimes of multiple instances must be nested. |
| 145 CHECK(old_proc == current_proc_); |
| 146 } |
133 | 147 |
134 // This class sets the HostMapper for a particular scope. If there are multiple | 148 void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) { |
135 // ScopedHostMappers in existence, then the last one allocated will be used. | 149 current_proc_ = proc; |
136 // However, if it does not provide a matching rule, then it should delegate | 150 previous_proc_ = HostResolverProc::SetDefault(current_proc_); |
137 // to the previously set HostMapper (see SetHostMapper). This is true for all | 151 current_proc_->set_previous_proc(previous_proc_); |
138 // mappers defined in this file. If no HostMapper matches a given hostname, then | 152 } |
139 // the hostname will be unmodified. | |
140 class ScopedHostMapper { | |
141 public: | |
142 ScopedHostMapper(HostMapper* mapper) : current_host_mapper_(mapper) { | |
143 previous_host_mapper_ = SetHostMapper(current_host_mapper_.get()); | |
144 current_host_mapper_->set_previous_mapper(previous_host_mapper_.get()); | |
145 } | |
146 | |
147 ScopedHostMapper() {} | |
148 | |
149 ~ScopedHostMapper() { | |
150 HostMapper* old_mapper = SetHostMapper(previous_host_mapper_.get()); | |
151 // The lifetimes of multiple instances must be nested. | |
152 CHECK(old_mapper == current_host_mapper_.get()); | |
153 } | |
154 | |
155 void Init(HostMapper* mapper) { | |
156 current_host_mapper_ = mapper; | |
157 previous_host_mapper_ = SetHostMapper(current_host_mapper_.get()); | |
158 current_host_mapper_->set_previous_mapper(previous_host_mapper_.get()); | |
159 } | |
160 | |
161 private: | |
162 scoped_refptr<HostMapper> current_host_mapper_; | |
163 scoped_refptr<HostMapper> previous_host_mapper_; | |
164 }; | |
165 | 153 |
166 } // namespace net | 154 } // namespace net |
167 | |
168 #endif // UNIT_TEST | |
169 | |
170 #endif // NET_BASE_HOST_RESOLVER_UNITTEST_H_ | |
OLD | NEW |