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.
5 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
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"
24 const char kPacUrl
[] = "http://pacserver/script.pac";
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
32 // Version of DhcpProxyScriptAdapterFetcher that mocks out dependencies
33 // to allow unit testing.
34 class MockDhcpProxyScriptAdapterFetcher
35 : public DhcpProxyScriptAdapterFetcher
{
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
),
46 pac_script_("bingo") {
49 void Cancel() override
{
50 DhcpProxyScriptAdapterFetcher::Cancel();
54 ProxyScriptFetcher
* ImplCreateScriptFetcher() override
{
55 // We don't maintain ownership of the fetcher, it is transferred to
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
);
66 class DelayingDhcpQuery
: public DhcpQuery
{
68 explicit DelayingDhcpQuery()
70 test_finished_event_(true, false) {
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_
;
80 base::WaitableEvent test_finished_event_
;
81 base::TimeDelta dhcp_delay_
;
82 std::string configured_url_
;
85 ~DelayingDhcpQuery() override
{}
88 DhcpQuery
* ImplCreateDhcpQuery() override
{
89 dhcp_query_
= new DelayingDhcpQuery();
90 dhcp_query_
->dhcp_delay_
= dhcp_delay_
;
91 dhcp_query_
->configured_url_
= configured_url_
;
92 return dhcp_query_
.get();
95 // Use a shorter timeout so tests can finish more quickly.
96 base::TimeDelta
ImplGetTimeout() const override
{ return timeout_
; }
98 void OnFetcherTimer() {
99 // Note that there is an assumption by this mock implementation that
100 // DhcpProxyScriptAdapterFetcher::Fetch will call ImplCreateScriptFetcher
101 // and call Fetch on the fetcher before the message loop is re-entered.
102 // This holds true today, but if you hit this DCHECK the problem can
103 // possibly be resolved by having a separate subclass of
104 // MockProxyScriptFetcher that adds the delay internally (instead of
105 // the simple approach currently used in ImplCreateScriptFetcher above).
106 DCHECK(fetcher_
&& fetcher_
->has_pending_request());
107 fetcher_
->NotifyFetchCompletion(fetcher_result_
, pac_script_
);
111 bool IsWaitingForFetcher() const {
112 return state() == STATE_WAIT_URL
;
115 bool WasCancelled() const {
116 return state() == STATE_CANCEL
;
120 DCHECK(dhcp_query_
.get());
121 dhcp_query_
->test_finished_event_
.Signal();
124 base::TimeDelta dhcp_delay_
;
125 base::TimeDelta timeout_
;
126 std::string configured_url_
;
127 int fetcher_delay_ms_
;
129 std::string pac_script_
;
130 MockProxyScriptFetcher
* fetcher_
;
131 base::OneShotTimer
<MockDhcpProxyScriptAdapterFetcher
> fetcher_timer_
;
132 scoped_refptr
<DelayingDhcpQuery
> dhcp_query_
;
135 class FetcherClient
{
138 : url_request_context_(new TestURLRequestContext()),
140 new base::SequencedWorkerPool(4, "DhcpAdapterFetcherTest")),
141 fetcher_(new MockDhcpProxyScriptAdapterFetcher(
142 url_request_context_
.get(),
143 worker_pool_
->GetTaskRunnerWithShutdownBehavior(
144 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
))) {
148 worker_pool_
->Shutdown();
151 void WaitForResult(int expected_error
) {
152 EXPECT_EQ(expected_error
, callback_
.WaitForResult());
156 fetcher_
->Fetch("adapter name", callback_
.callback());
159 void FinishTestAllowCleanup() {
160 fetcher_
->FinishTest();
161 base::MessageLoop::current()->RunUntilIdle();
164 TestCompletionCallback callback_
;
165 scoped_ptr
<URLRequestContext
> url_request_context_
;
166 scoped_refptr
<base::SequencedWorkerPool
> worker_pool_
;
167 scoped_ptr
<MockDhcpProxyScriptAdapterFetcher
> fetcher_
;
168 base::string16 pac_text_
;
171 TEST(DhcpProxyScriptAdapterFetcher
, NormalCaseURLNotInDhcp
) {
172 FetcherClient client
;
173 client
.fetcher_
->configured_url_
= "";
175 client
.WaitForResult(ERR_PAC_NOT_IN_DHCP
);
176 ASSERT_TRUE(client
.fetcher_
->DidFinish());
177 EXPECT_EQ(ERR_PAC_NOT_IN_DHCP
, client
.fetcher_
->GetResult());
178 EXPECT_EQ(base::string16(L
""), client
.fetcher_
->GetPacScript());
181 TEST(DhcpProxyScriptAdapterFetcher
, NormalCaseURLInDhcp
) {
182 FetcherClient client
;
184 client
.WaitForResult(OK
);
185 ASSERT_TRUE(client
.fetcher_
->DidFinish());
186 EXPECT_EQ(OK
, client
.fetcher_
->GetResult());
187 EXPECT_EQ(base::string16(L
"bingo"), client
.fetcher_
->GetPacScript());
188 EXPECT_EQ(GURL(kPacUrl
), client
.fetcher_
->GetPacURL());
191 TEST(DhcpProxyScriptAdapterFetcher
, TimeoutDuringDhcp
) {
192 // Does a Fetch() with a long enough delay on accessing DHCP that the
193 // fetcher should time out. This is to test a case manual testing found,
194 // where under certain circumstances (e.g. adapter enabled for DHCP and
195 // needs to retrieve its configuration from DHCP, but no DHCP server
196 // present on the network) accessing DHCP can take on the order of tens
198 FetcherClient client
;
199 client
.fetcher_
->dhcp_delay_
= TestTimeouts::action_max_timeout();
200 client
.fetcher_
->timeout_
= base::TimeDelta::FromMilliseconds(25);
202 base::ElapsedTimer timer
;
204 // An error different from this would be received if the timeout didn't
206 client
.WaitForResult(ERR_TIMED_OUT
);
208 ASSERT_TRUE(client
.fetcher_
->DidFinish());
209 EXPECT_EQ(ERR_TIMED_OUT
, client
.fetcher_
->GetResult());
210 EXPECT_EQ(base::string16(L
""), client
.fetcher_
->GetPacScript());
211 EXPECT_EQ(GURL(), client
.fetcher_
->GetPacURL());
212 client
.FinishTestAllowCleanup();
215 TEST(DhcpProxyScriptAdapterFetcher
, CancelWhileDhcp
) {
216 FetcherClient client
;
218 client
.fetcher_
->Cancel();
219 base::MessageLoop::current()->RunUntilIdle();
220 ASSERT_FALSE(client
.fetcher_
->DidFinish());
221 ASSERT_TRUE(client
.fetcher_
->WasCancelled());
222 EXPECT_EQ(ERR_ABORTED
, client
.fetcher_
->GetResult());
223 EXPECT_EQ(base::string16(L
""), client
.fetcher_
->GetPacScript());
224 EXPECT_EQ(GURL(), client
.fetcher_
->GetPacURL());
225 client
.FinishTestAllowCleanup();
228 TEST(DhcpProxyScriptAdapterFetcher
, CancelWhileFetcher
) {
229 FetcherClient client
;
230 // This causes the mock fetcher not to pretend the
231 // fetcher finishes after a timeout.
232 client
.fetcher_
->fetcher_delay_ms_
= -1;
235 while (!client
.fetcher_
->IsWaitingForFetcher() && max_loops
--) {
236 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
237 base::MessageLoop::current()->RunUntilIdle();
239 client
.fetcher_
->Cancel();
240 base::MessageLoop::current()->RunUntilIdle();
241 ASSERT_FALSE(client
.fetcher_
->DidFinish());
242 ASSERT_TRUE(client
.fetcher_
->WasCancelled());
243 EXPECT_EQ(ERR_ABORTED
, client
.fetcher_
->GetResult());
244 EXPECT_EQ(base::string16(L
""), client
.fetcher_
->GetPacScript());
245 // GetPacURL() still returns the URL fetched in this case.
246 EXPECT_EQ(GURL(kPacUrl
), client
.fetcher_
->GetPacURL());
247 client
.FinishTestAllowCleanup();
250 TEST(DhcpProxyScriptAdapterFetcher
, CancelAtCompletion
) {
251 FetcherClient client
;
253 client
.WaitForResult(OK
);
254 client
.fetcher_
->Cancel();
255 // Canceling after you're done should have no effect, so these
256 // are identical expectations to the NormalCaseURLInDhcp test.
257 ASSERT_TRUE(client
.fetcher_
->DidFinish());
258 EXPECT_EQ(OK
, client
.fetcher_
->GetResult());
259 EXPECT_EQ(base::string16(L
"bingo"), client
.fetcher_
->GetPacScript());
260 EXPECT_EQ(GURL(kPacUrl
), client
.fetcher_
->GetPacURL());
261 client
.FinishTestAllowCleanup();
264 // Does a real fetch on a mock DHCP configuration.
265 class MockDhcpRealFetchProxyScriptAdapterFetcher
266 : public MockDhcpProxyScriptAdapterFetcher
{
268 explicit MockDhcpRealFetchProxyScriptAdapterFetcher(
269 URLRequestContext
* context
,
270 scoped_refptr
<base::TaskRunner
> task_runner
)
271 : MockDhcpProxyScriptAdapterFetcher(context
, task_runner
),
272 url_request_context_(context
) {
275 // Returns a real proxy script fetcher.
276 ProxyScriptFetcher
* ImplCreateScriptFetcher() override
{
277 ProxyScriptFetcher
* fetcher
=
278 new ProxyScriptFetcherImpl(url_request_context_
);
282 URLRequestContext
* url_request_context_
;
285 TEST(DhcpProxyScriptAdapterFetcher
, MockDhcpRealFetch
) {
286 SpawnedTestServer
test_server(
287 SpawnedTestServer::TYPE_HTTP
,
288 SpawnedTestServer::kLocalhost
,
290 FILE_PATH_LITERAL("net/data/proxy_script_fetcher_unittest")));
291 ASSERT_TRUE(test_server
.Start());
293 GURL configured_url
= test_server
.GetURL("files/downloadable.pac");
295 FetcherClient client
;
296 TestURLRequestContext url_request_context
;
297 scoped_refptr
<base::TaskRunner
> runner
=
298 client
.worker_pool_
->GetTaskRunnerWithShutdownBehavior(
299 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
300 client
.fetcher_
.reset(
301 new MockDhcpRealFetchProxyScriptAdapterFetcher(
302 &url_request_context
, runner
));
303 client
.fetcher_
->configured_url_
= configured_url
.spec();
305 client
.WaitForResult(OK
);
306 ASSERT_TRUE(client
.fetcher_
->DidFinish());
307 EXPECT_EQ(OK
, client
.fetcher_
->GetResult());
308 EXPECT_EQ(base::string16(L
"-downloadable.pac-\n"),
309 client
.fetcher_
->GetPacScript());
310 EXPECT_EQ(configured_url
,
311 client
.fetcher_
->GetPacURL());
314 #define BASE_URL "http://corpserver/proxy.pac"
316 TEST(DhcpProxyScriptAdapterFetcher
, SanitizeDhcpApiString
) {
317 const size_t kBaseUrlLen
= strlen(BASE_URL
);
321 DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
322 BASE_URL
, kBaseUrlLen
));
324 // Trailing \n and no null-termination.
326 DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
327 BASE_URL
"\nblablabla", kBaseUrlLen
+ 1));
331 DhcpProxyScriptAdapterFetcher::SanitizeDhcpApiString(
332 BASE_URL
"\0foo\0blat", kBaseUrlLen
+ 9));