OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/synchronization/waitable_event.h" | |
8 #include "base/test/test_timeouts.h" | |
9 #include "base/threading/sequenced_worker_pool.h" | |
10 #include "base/timer/elapsed_timer.h" | |
11 #include "base/timer/timer.h" | |
12 #include "net/base/net_errors.h" | |
13 #include "net/base/test_completion_callback.h" | |
14 #include "net/proxy/mock_proxy_script_fetcher.h" | |
15 #include "net/proxy/proxy_script_fetcher_impl.h" | |
16 #include "net/test/spawned_test_server/spawned_test_server.h" | |
17 #include "net/url_request/url_request_test_util.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 namespace net { | |
21 | |
22 namespace { | |
23 | |
24 const char kPacUrl[] = "http://pacserver/script.pac"; | |
25 | |
26 // In net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc there are a few | |
27 // tests that exercise DhcpProxyScriptAdapterFetcher end-to-end along with | |
28 // DhcpProxyScriptFetcherWin, i.e. it tests the end-to-end usage of Win32 | |
29 // APIs and the network. In this file we test only by stubbing out | |
30 // functionality. | |
31 | |
32 // Version of DhcpProxyScriptAdapterFetcher that mocks out dependencies | |
33 // to allow unit testing. | |
34 class MockDhcpProxyScriptAdapterFetcher | |
35 : public DhcpProxyScriptAdapterFetcher { | |
36 public: | |
37 explicit MockDhcpProxyScriptAdapterFetcher( | |
38 URLRequestContext* context, | |
39 scoped_refptr<base::TaskRunner> task_runner) | |
40 : DhcpProxyScriptAdapterFetcher(context, task_runner), | |
41 dhcp_delay_(base::TimeDelta::FromMilliseconds(1)), | |
42 timeout_(TestTimeouts::action_timeout()), | |
43 configured_url_(kPacUrl), | |
44 fetcher_delay_ms_(1), | |
45 fetcher_result_(OK), | |
46 pac_script_("bingo") { | |
47 } | |
48 | |
49 void Cancel() { | |
50 DhcpProxyScriptAdapterFetcher::Cancel(); | |
51 fetcher_ = NULL; | |
52 } | |
53 | |
54 virtual ProxyScriptFetcher* ImplCreateScriptFetcher() override { | |
55 // We don't maintain ownership of the fetcher, it is transferred to | |
56 // the caller. | |
57 fetcher_ = new MockProxyScriptFetcher(); | |
58 if (fetcher_delay_ms_ != -1) { | |
59 fetcher_timer_.Start(FROM_HERE, | |
60 base::TimeDelta::FromMilliseconds(fetcher_delay_ms_), | |
61 this, &MockDhcpProxyScriptAdapterFetcher::OnFetcherTimer); | |
62 } | |
63 return fetcher_; | |
64 } | |
65 | |
66 class DelayingDhcpQuery : public DhcpQuery { | |
67 public: | |
68 explicit DelayingDhcpQuery() | |
69 : DhcpQuery(), | |
70 test_finished_event_(true, false) { | |
71 } | |
72 | |
73 std::string ImplGetPacURLFromDhcp( | |
74 const std::string& adapter_name) override { | |
75 base::ElapsedTimer timer; | |
76 test_finished_event_.TimedWait(dhcp_delay_); | |
77 return configured_url_; | |
78 } | |
79 | |
80 base::WaitableEvent test_finished_event_; | |
81 base::TimeDelta dhcp_delay_; | |
82 std::string configured_url_; | |
83 }; | |
84 | |
85 virtual DhcpQuery* ImplCreateDhcpQuery() override { | |
86 dhcp_query_ = new DelayingDhcpQuery(); | |
87 dhcp_query_->dhcp_delay_ = dhcp_delay_; | |
88 dhcp_query_->configured_url_ = configured_url_; | |
89 return dhcp_query_.get(); | |
90 } | |
91 | |
92 // Use a shorter timeout so tests can finish more quickly. | |
93 virtual base::TimeDelta ImplGetTimeout() const override { | |
94 return timeout_; | |
95 } | |
96 | |
97 void OnFetcherTimer() { | |
98 // Note that there is an assumption by this mock implementation that | |
99 // DhcpProxyScriptAdapterFetcher::Fetch will call ImplCreateScriptFetcher | |
100 // and call Fetch on the fetcher before the message loop is re-entered. | |
101 // This holds true today, but if you hit this DCHECK the problem can | |
102 // possibly be resolved by having a separate subclass of | |
103 // MockProxyScriptFetcher that adds the delay internally (instead of | |
104 // the simple approach currently used in ImplCreateScriptFetcher above). | |
105 DCHECK(fetcher_ && fetcher_->has_pending_request()); | |
106 fetcher_->NotifyFetchCompletion(fetcher_result_, pac_script_); | |
107 fetcher_ = NULL; | |
108 } | |
109 | |
110 bool IsWaitingForFetcher() const { | |
111 return state() == STATE_WAIT_URL; | |
112 } | |
113 | |
114 bool WasCancelled() const { | |
115 return state() == STATE_CANCEL; | |
116 } | |
117 | |
118 void FinishTest() { | |
119 DCHECK(dhcp_query_.get()); | |
120 dhcp_query_->test_finished_event_.Signal(); | |
121 } | |
122 | |
123 base::TimeDelta dhcp_delay_; | |
124 base::TimeDelta timeout_; | |
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<DelayingDhcpQuery> dhcp_query_; | |
132 }; | |
133 | |
134 class FetcherClient { | |
135 public: | |
136 FetcherClient() | |
137 : url_request_context_(new TestURLRequestContext()), | |
138 worker_pool_( | |
139 new base::SequencedWorkerPool(4, "DhcpAdapterFetcherTest")), | |
140 fetcher_(new MockDhcpProxyScriptAdapterFetcher( | |
141 url_request_context_.get(), | |
142 worker_pool_->GetTaskRunnerWithShutdownBehavior( | |
143 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN))) { | |
144 } | |
145 | |
146 ~FetcherClient() { | |
147 worker_pool_->Shutdown(); | |
148 } | |
149 | |
150 void WaitForResult(int expected_error) { | |
151 EXPECT_EQ(expected_error, callback_.WaitForResult()); | |
152 } | |
153 | |
154 void RunTest() { | |
155 fetcher_->Fetch("adapter name", callback_.callback()); | |
156 } | |
157 | |
158 void FinishTestAllowCleanup() { | |
159 fetcher_->FinishTest(); | |
160 base::MessageLoop::current()->RunUntilIdle(); | |
161 } | |
162 | |
163 TestCompletionCallback callback_; | |
164 scoped_ptr<URLRequestContext> url_request_context_; | |
165 scoped_refptr<base::SequencedWorkerPool> worker_pool_; | |
166 scoped_ptr<MockDhcpProxyScriptAdapterFetcher> fetcher_; | |
167 base::string16 pac_text_; | |
168 }; | |
169 | |
170 TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLNotInDhcp) { | |
171 FetcherClient client; | |
172 client.fetcher_->configured_url_ = ""; | |
173 client.RunTest(); | |
174 client.WaitForResult(ERR_PAC_NOT_IN_DHCP); | |
175 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
176 EXPECT_EQ(ERR_PAC_NOT_IN_DHCP, client.fetcher_->GetResult()); | |
177 EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript()); | |
178 } | |
179 | |
180 TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLInDhcp) { | |
181 FetcherClient client; | |
182 client.RunTest(); | |
183 client.WaitForResult(OK); | |
184 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
185 EXPECT_EQ(OK, client.fetcher_->GetResult()); | |
186 EXPECT_EQ(base::string16(L"bingo"), client.fetcher_->GetPacScript()); | |
187 EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL()); | |
188 } | |
189 | |
190 TEST(DhcpProxyScriptAdapterFetcher, TimeoutDuringDhcp) { | |
191 // Does a Fetch() with a long enough delay on accessing DHCP that the | |
192 // fetcher should time out. This is to test a case manual testing found, | |
193 // where under certain circumstances (e.g. adapter enabled for DHCP and | |
194 // needs to retrieve its configuration from DHCP, but no DHCP server | |
195 // present on the network) accessing DHCP can take on the order of tens | |
196 // of seconds. | |
197 FetcherClient client; | |
198 client.fetcher_->dhcp_delay_ = TestTimeouts::action_max_timeout(); | |
199 client.fetcher_->timeout_ = base::TimeDelta::FromMilliseconds(25); | |
200 | |
201 base::ElapsedTimer timer; | |
202 client.RunTest(); | |
203 // An error different from this would be received if the timeout didn't | |
204 // kick in. | |
205 client.WaitForResult(ERR_TIMED_OUT); | |
206 | |
207 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
208 EXPECT_EQ(ERR_TIMED_OUT, client.fetcher_->GetResult()); | |
209 EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript()); | |
210 EXPECT_EQ(GURL(), client.fetcher_->GetPacURL()); | |
211 client.FinishTestAllowCleanup(); | |
212 } | |
213 | |
214 TEST(DhcpProxyScriptAdapterFetcher, CancelWhileDhcp) { | |
215 FetcherClient client; | |
216 client.RunTest(); | |
217 client.fetcher_->Cancel(); | |
218 base::MessageLoop::current()->RunUntilIdle(); | |
219 ASSERT_FALSE(client.fetcher_->DidFinish()); | |
220 ASSERT_TRUE(client.fetcher_->WasCancelled()); | |
221 EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult()); | |
222 EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript()); | |
223 EXPECT_EQ(GURL(), client.fetcher_->GetPacURL()); | |
224 client.FinishTestAllowCleanup(); | |
225 } | |
226 | |
227 TEST(DhcpProxyScriptAdapterFetcher, CancelWhileFetcher) { | |
228 FetcherClient client; | |
229 // This causes the mock fetcher not to pretend the | |
230 // fetcher finishes after a timeout. | |
231 client.fetcher_->fetcher_delay_ms_ = -1; | |
232 client.RunTest(); | |
233 int max_loops = 4; | |
234 while (!client.fetcher_->IsWaitingForFetcher() && max_loops--) { | |
235 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); | |
236 base::MessageLoop::current()->RunUntilIdle(); | |
237 } | |
238 client.fetcher_->Cancel(); | |
239 base::MessageLoop::current()->RunUntilIdle(); | |
240 ASSERT_FALSE(client.fetcher_->DidFinish()); | |
241 ASSERT_TRUE(client.fetcher_->WasCancelled()); | |
242 EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult()); | |
243 EXPECT_EQ(base::string16(L""), client.fetcher_->GetPacScript()); | |
244 // GetPacURL() still returns the URL fetched in this case. | |
245 EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL()); | |
246 client.FinishTestAllowCleanup(); | |
247 } | |
248 | |
249 TEST(DhcpProxyScriptAdapterFetcher, CancelAtCompletion) { | |
250 FetcherClient client; | |
251 client.RunTest(); | |
252 client.WaitForResult(OK); | |
253 client.fetcher_->Cancel(); | |
254 // Canceling after you're done should have no effect, so these | |
255 // are identical expectations to the NormalCaseURLInDhcp test. | |
256 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
257 EXPECT_EQ(OK, client.fetcher_->GetResult()); | |
258 EXPECT_EQ(base::string16(L"bingo"), client.fetcher_->GetPacScript()); | |
259 EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL()); | |
260 client.FinishTestAllowCleanup(); | |
261 } | |
262 | |
263 // Does a real fetch on a mock DHCP configuration. | |
264 class MockDhcpRealFetchProxyScriptAdapterFetcher | |
265 : public MockDhcpProxyScriptAdapterFetcher { | |
266 public: | |
267 explicit MockDhcpRealFetchProxyScriptAdapterFetcher( | |
268 URLRequestContext* context, | |
269 scoped_refptr<base::TaskRunner> task_runner) | |
270 : MockDhcpProxyScriptAdapterFetcher(context, task_runner), | |
271 url_request_context_(context) { | |
272 } | |
273 | |
274 // Returns a real proxy script fetcher. | |
275 ProxyScriptFetcher* ImplCreateScriptFetcher() override { | |
276 ProxyScriptFetcher* fetcher = | |
277 new ProxyScriptFetcherImpl(url_request_context_); | |
278 return fetcher; | |
279 } | |
280 | |
281 URLRequestContext* url_request_context_; | |
282 }; | |
283 | |
284 TEST(DhcpProxyScriptAdapterFetcher, MockDhcpRealFetch) { | |
285 SpawnedTestServer test_server( | |
286 SpawnedTestServer::TYPE_HTTP, | |
287 SpawnedTestServer::kLocalhost, | |
288 base::FilePath( | |
289 FILE_PATH_LITERAL("net/data/proxy_script_fetcher_unittest"))); | |
290 ASSERT_TRUE(test_server.Start()); | |
291 | |
292 GURL configured_url = test_server.GetURL("files/downloadable.pac"); | |
293 | |
294 FetcherClient client; | |
295 TestURLRequestContext url_request_context; | |
296 scoped_refptr<base::TaskRunner> runner = | |
297 client.worker_pool_->GetTaskRunnerWithShutdownBehavior( | |
298 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
299 client.fetcher_.reset( | |
300 new MockDhcpRealFetchProxyScriptAdapterFetcher( | |
301 &url_request_context, runner)); | |
302 client.fetcher_->configured_url_ = configured_url.spec(); | |
303 client.RunTest(); | |
304 client.WaitForResult(OK); | |
305 ASSERT_TRUE(client.fetcher_->DidFinish()); | |
306 EXPECT_EQ(OK, client.fetcher_->GetResult()); | |
307 EXPECT_EQ(base::string16(L"-downloadable.pac-\n"), | |
308 client.fetcher_->GetPacScript()); | |
309 EXPECT_EQ(configured_url, | |
310 client.fetcher_->GetPacURL()); | |
311 } | |
312 | |
313 #define BASE_URL "http://corpserver/proxy.pac" | |
314 | |
315 TEST(DhcpProxyScriptAdapterFetcher, SanitizeDhcpApiString) { | |
316 const size_t kBaseUrlLen = strlen(BASE_URL); | |
317 | |
318 // Default case. | |
319 EXPECT_EQ(BASE_URL, | |
320 DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString( | |
321 BASE_URL, kBaseUrlLen)); | |
322 | |
323 // Trailing \n and no null-termination. | |
324 EXPECT_EQ(BASE_URL, | |
325 DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString( | |
326 BASE_URL "\nblablabla", kBaseUrlLen + 1)); | |
327 | |
328 // Embedded NULLs. | |
329 EXPECT_EQ(BASE_URL, | |
330 DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString( | |
331 BASE_URL "\0foo\0blat", kBaseUrlLen + 9)); | |
332 } | |
333 | |
334 #undef BASE_URL | |
335 | |
336 } // namespace | |
337 | |
338 } // namespace net | |
OLD | NEW |