OLD | NEW |
---|---|
(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 "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h" | |
6 | |
7 #include "base/perftimer.h" | |
8 #include "base/timer.h" | |
9 #include "net/base/net_errors.h" | |
10 #include "net/base/test_completion_callback.h" | |
11 #include "net/proxy/mock_proxy_script_fetcher.h" | |
12 #include "net/proxy/proxy_script_fetcher_impl.h" | |
13 #include "net/test/test_server.h" | |
14 #include "net/url_request/url_request_test_util.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 | |
17 namespace net { | |
18 | |
19 namespace { | |
20 | |
21 const char* const kPacUrl = "http://pacserver/script.pac"; | |
22 | |
23 // In net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc there are a few | |
24 // tests that exercise DhcpProxyScriptAdapterFetcher end-to-end along with | |
25 // WindowsDhcpProxyScriptFetcher, i.e. it tests the end-to-end usage of Win32 | |
26 // APIs and the network. In this file we test only by stubbing out | |
27 // functionality. | |
28 | |
29 // Version of DhcpProxyScriptAdapterFetcher that mocks out dependencies | |
30 // to allow unit testing. | |
31 class MockDhcpProxyScriptAdapterFetcher | |
32 : public DhcpProxyScriptAdapterFetcher { | |
33 public: | |
34 explicit MockDhcpProxyScriptAdapterFetcher(URLRequestContext* context) | |
35 : DhcpProxyScriptAdapterFetcher(context), | |
36 test_finished_(false), | |
37 dhcp_delay_ms_(1), | |
38 timeout_ms_(100), | |
39 configured_url_(kPacUrl), | |
40 fetcher_delay_ms_(1), | |
41 fetcher_result_(OK), | |
42 pac_script_("bingo") { | |
43 } | |
44 | |
45 ~MockDhcpProxyScriptAdapterFetcher() { | |
46 if (worker_thread_) | |
47 worker_thread_->test_owner_ = NULL; | |
48 } | |
49 | |
50 void Cancel() { | |
51 DhcpProxyScriptAdapterFetcher::Cancel(); | |
52 fetcher_ = NULL; | |
53 } | |
54 | |
55 virtual ProxyScriptFetcher* ImplCreateScriptFetcher() OVERRIDE { | |
56 // We don't maintain ownership of the fetcher, it is transferred to | |
57 // the caller. | |
58 fetcher_ = new MockProxyScriptFetcher(); | |
59 if (fetcher_delay_ms_ != -1) { | |
60 fetcher_timer_.Start( | |
61 base::TimeDelta::FromMilliseconds(fetcher_delay_ms_), | |
62 this, &MockDhcpProxyScriptAdapterFetcher::OnFetcherTimer); | |
63 } | |
64 return fetcher_; | |
65 } | |
66 | |
67 class DelayingWorkerThread : public WorkerThread { | |
68 public: | |
69 explicit DelayingWorkerThread( | |
70 const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) | |
71 : WorkerThread(owner) { | |
72 } | |
73 | |
74 std::string ImplGetPacURLFromDhcp( | |
75 const std::string& adapter_name) OVERRIDE { | |
76 PerfTimer timer; | |
77 while (test_owner_ && | |
eroman
2011/05/17 04:39:39
Per my earlier comment, I do not like spin loops l
Jói
2011/05/17 15:38:35
Thanks for pressing on this. Switched to using a W
| |
78 timer.Elapsed() < TimeDelta::FromMilliseconds( | |
79 test_owner_->dhcp_delay_ms_) && | |
80 !test_owner_->test_finished_) { | |
81 base::PlatformThread::Sleep(5); | |
82 } | |
83 return test_owner_->configured_url_; | |
84 } | |
85 | |
86 MockDhcpProxyScriptAdapterFetcher* test_owner_; | |
87 }; | |
88 | |
89 virtual WorkerThread* ImplCreateWorkerThread( | |
90 const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) OVERRIDE { | |
91 worker_thread_ = new DelayingWorkerThread(owner); | |
92 worker_thread_->test_owner_ = this; | |
93 return worker_thread_; | |
94 } | |
95 | |
96 // Use a shorter timeout so tests can finish more quickly. | |
97 virtual base::TimeDelta ImplGetTimeout() const OVERRIDE { | |
98 return base::TimeDelta::FromMilliseconds(timeout_ms_); | |
99 } | |
100 | |
101 void OnFetcherTimer() { | |
102 // Note that there is an assumption by this mock implementation that | |
103 // DhcpProxyScriptAdapterFetcher::Fetch will call ImplCreateScriptFetcher | |
104 // and call Fetch on the fetcher before the message loop is re-entered. | |
105 // This holds true today, but if you hit this DCHECK the problem can | |
106 // possibly be resolved by having a separate subclass of | |
107 // MockProxyScriptFetcher that adds the delay internally (instead of | |
108 // the simple approach currently used in ImplCreateScriptFetcher above). | |
109 DCHECK(fetcher_ && fetcher_->has_pending_request()); | |
110 fetcher_->NotifyFetchCompletion(fetcher_result_, pac_script_); | |
111 fetcher_ = NULL; | |
112 } | |
113 | |
114 bool IsWaitingForFetcher() const { | |
115 return state_ == STATE_WAIT_URL; | |
116 } | |
117 | |
118 bool WasCancelled() const { | |
119 return state_ == STATE_CANCEL; | |
120 } | |
121 | |
122 volatile bool test_finished_; // Set to true to end delay loop. | |
123 int dhcp_delay_ms_; | |
124 int timeout_ms_; | |
125 std::string configured_url_; | |
126 int fetcher_delay_ms_; | |
127 int fetcher_result_; | |
128 std::string pac_script_; | |
129 MockProxyScriptFetcher* fetcher_; | |
130 base::OneShotTimer<MockDhcpProxyScriptAdapterFetcher> fetcher_timer_; | |
131 scoped_refptr<DelayingWorkerThread> worker_thread_; | |
132 }; | |
133 | |
134 class FetcherClient { | |
135 public: | |
136 FetcherClient() | |
137 : url_request_context_(new TestURLRequestContext()), | |
138 fetcher_( | |
139 new MockDhcpProxyScriptAdapterFetcher(url_request_context_.get())) { | |
140 } | |
141 | |
142 void WaitForResult(int expected_error) { | |
143 EXPECT_EQ(expected_error, callback_.WaitForResult()); | |
144 } | |
145 | |
146 void RunTest() { | |
147 fetcher_->Fetch("adapter name", &callback_); | |
148 } | |
149 | |
150 void FinishTestAllowCleanup() { | |
151 fetcher_->test_finished_ = true; | |
152 base::PlatformThread::Sleep(15); | |
eroman
2011/05/17 04:39:39
why does there need to be a sleep here?
Jói
2011/05/17 15:38:35
The reason is lost in the mists of time :) Remove
| |
153 MessageLoop::current()->RunAllPending(); | |
154 } | |
155 | |
156 TestCompletionCallback callback_; | |
157 scoped_refptr<URLRequestContext> url_request_context_; | |
158 scoped_ptr<MockDhcpProxyScriptAdapterFetcher> fetcher_; | |
159 string16 pac_text_; | |
160 }; | |
161 | |
162 TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLNotInDhcp) { | |
163 FetcherClient client; | |
164 client.fetcher_->configured_url_ = ""; | |
165 client.RunTest(); | |
166 client.WaitForResult(ERR_PAC_NOT_IN_DHCP); | |
167 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
168 EXPECT_EQ(ERR_PAC_NOT_IN_DHCP, client.fetcher_->GetResult()); | |
169 EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript()); | |
170 } | |
171 | |
172 TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLInDhcp) { | |
173 FetcherClient client; | |
174 client.RunTest(); | |
175 client.WaitForResult(OK); | |
176 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
177 EXPECT_EQ(OK, client.fetcher_->GetResult()); | |
178 EXPECT_EQ(string16(L"bingo"), client.fetcher_->GetPacScript()); | |
179 EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL()); | |
180 } | |
181 | |
182 TEST(DhcpProxyScriptAdapterFetcher, TimeoutDuringDhcp) { | |
183 // Does a Fetch() with a long enough delay on accessing DHCP that the | |
184 // fetcher should time out. This is to test a case manual testing found, | |
185 // where under certain circumstances (e.g. adapter enabled for DHCP and | |
186 // needs to retrieve its configuration from DHCP, but no DHCP server | |
187 // present on the network) accessing DHCP can take on the order of tens | |
188 // of seconds. | |
189 FetcherClient client; | |
190 client.fetcher_->dhcp_delay_ms_ = 20 * 1000; | |
191 client.fetcher_->timeout_ms_ = 25; | |
192 | |
193 PerfTimer timer; | |
194 client.RunTest(); | |
195 client.WaitForResult(ERR_TIMED_OUT); | |
196 | |
197 // The timeout should occur within about 25 ms, way before the 20s set as | |
198 // the API delay above. | |
199 ASSERT_GT(base::TimeDelta::FromMilliseconds(35), timer.Elapsed()); | |
200 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
201 EXPECT_EQ(ERR_TIMED_OUT, client.fetcher_->GetResult()); | |
202 EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript()); | |
203 EXPECT_EQ(GURL(), client.fetcher_->GetPacURL()); | |
204 client.FinishTestAllowCleanup(); | |
205 } | |
206 | |
207 TEST(DhcpProxyScriptAdapterFetcher, CancelWhileDhcp) { | |
208 FetcherClient client; | |
209 client.fetcher_->dhcp_delay_ms_ = 10; | |
210 client.RunTest(); | |
211 client.fetcher_->Cancel(); | |
212 MessageLoop::current()->RunAllPending(); | |
213 ASSERT_FALSE(client.fetcher_->DidFinish()); | |
214 ASSERT_TRUE(client.fetcher_->WasCancelled()); | |
215 EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult()); | |
216 EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript()); | |
217 EXPECT_EQ(GURL(), client.fetcher_->GetPacURL()); | |
218 client.FinishTestAllowCleanup(); | |
219 } | |
220 | |
221 TEST(DhcpProxyScriptAdapterFetcher, CancelWhileFetcher) { | |
222 FetcherClient client; | |
223 // This causes the mock fetcher not to pretend the | |
224 // fetcher finishes after a timeout. | |
225 client.fetcher_->fetcher_delay_ms_ = -1; | |
226 client.RunTest(); | |
227 int max_loops = 4; | |
228 while (!client.fetcher_->IsWaitingForFetcher() && max_loops--) { | |
229 base::PlatformThread::Sleep(10); | |
230 MessageLoop::current()->RunAllPending(); | |
231 } | |
232 client.fetcher_->Cancel(); | |
233 MessageLoop::current()->RunAllPending(); | |
234 ASSERT_FALSE(client.fetcher_->DidFinish()); | |
235 ASSERT_TRUE(client.fetcher_->WasCancelled()); | |
236 EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult()); | |
237 EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript()); | |
238 // GetPacURL() still returns the URL fetched in this case. | |
239 EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL()); | |
240 client.FinishTestAllowCleanup(); | |
241 } | |
242 | |
243 TEST(DhcpProxyScriptAdapterFetcher, CancelAtCompletion) { | |
244 FetcherClient client; | |
245 client.RunTest(); | |
246 client.WaitForResult(OK); | |
247 client.fetcher_->Cancel(); | |
248 // Canceling after you're done should have no effect, so these | |
249 // are identical expectations to the NormalCaseURLInDhcp test. | |
250 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
251 EXPECT_EQ(OK, client.fetcher_->GetResult()); | |
252 EXPECT_EQ(string16(L"bingo"), client.fetcher_->GetPacScript()); | |
253 EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL()); | |
254 client.FinishTestAllowCleanup(); | |
255 } | |
256 | |
257 // Does a real fetch on a mock DHCP configuration. | |
258 class MockDhcpRealFetchProxyScriptAdapterFetcher | |
259 : public MockDhcpProxyScriptAdapterFetcher { | |
260 public: | |
261 explicit MockDhcpRealFetchProxyScriptAdapterFetcher( | |
262 URLRequestContext* context) | |
263 : MockDhcpProxyScriptAdapterFetcher(context), | |
264 url_request_context_(context) { | |
265 } | |
266 | |
267 // Returns a real proxy script fetcher. | |
268 ProxyScriptFetcher* ImplCreateScriptFetcher() OVERRIDE { | |
269 ProxyScriptFetcher* fetcher = | |
270 new ProxyScriptFetcherImpl(url_request_context_); | |
271 return fetcher; | |
272 } | |
273 | |
274 URLRequestContext* url_request_context_; | |
275 }; | |
276 | |
277 TEST(DhcpProxyScriptAdapterFetcher, MockDhcpRealFetch) { | |
278 TestServer test_server( | |
279 TestServer::TYPE_HTTP, | |
280 FilePath(FILE_PATH_LITERAL("net/data/proxy_script_fetcher_unittest"))); | |
281 ASSERT_TRUE(test_server.Start()); | |
282 | |
283 GURL configured_url = test_server.GetURL("files/downloadable.pac"); | |
284 | |
285 FetcherClient client; | |
286 scoped_refptr<URLRequestContext> url_request_context( | |
287 new TestURLRequestContext()); | |
288 client.fetcher_.reset( | |
289 new MockDhcpRealFetchProxyScriptAdapterFetcher( | |
290 url_request_context.get())); | |
291 client.fetcher_->configured_url_ = configured_url.spec(); | |
292 client.RunTest(); | |
293 client.WaitForResult(OK); | |
294 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
295 EXPECT_EQ(OK, client.fetcher_->GetResult()); | |
296 EXPECT_EQ(string16(L"-downloadable.pac-\n"), client.fetcher_->GetPacScript()); | |
297 EXPECT_EQ(configured_url, | |
298 client.fetcher_->GetPacURL()); | |
299 } | |
300 | |
301 } // namespace | |
302 | |
303 } // namespace net | |
OLD | NEW |