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/multi_threaded_proxy_resolver.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/synchronization/lock.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/threading/platform_thread.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/test_completion_callback.h"
20 #include "net/log/net_log.h"
21 #include "net/log/test_net_log.h"
22 #include "net/log/test_net_log_entry.h"
23 #include "net/log/test_net_log_util.h"
24 #include "net/proxy/mock_proxy_resolver.h"
25 #include "net/proxy/proxy_info.h"
26 #include "net/proxy/proxy_resolver_factory.h"
27 #include "testing/gtest/include/gtest/gtest.h"
30 using base::ASCIIToUTF16
;
36 // A synchronous mock ProxyResolver implementation, which can be used in
37 // conjunction with MultiThreadedProxyResolver.
38 // - returns a single-item proxy list with the query's host.
39 class MockProxyResolver
: public ProxyResolver
{
42 : worker_loop_(base::MessageLoop::current()), request_count_(0) {}
44 // ProxyResolver implementation.
45 int GetProxyForURL(const GURL
& query_url
,
47 const CompletionCallback
& callback
,
48 RequestHandle
* request
,
49 const BoundNetLog
& net_log
) override
{
50 if (resolve_latency_
!= base::TimeDelta())
51 base::PlatformThread::Sleep(resolve_latency_
);
53 CheckIsOnWorkerThread();
55 EXPECT_TRUE(callback
.is_null());
56 EXPECT_TRUE(request
== NULL
);
58 // Write something into |net_log| (doesn't really have any meaning.)
59 net_log
.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT
);
61 results
->UseNamedProxy(query_url
.host());
63 // Return a success code which represents the request's order.
64 return request_count_
++;
67 void CancelRequest(RequestHandle request
) override
{ NOTREACHED(); }
69 LoadState
GetLoadState(RequestHandle request
) const override
{
71 return LOAD_STATE_IDLE
;
74 int request_count() const { return request_count_
; }
76 void SetResolveLatency(base::TimeDelta latency
) {
77 resolve_latency_
= latency
;
81 void CheckIsOnWorkerThread() {
82 EXPECT_EQ(base::MessageLoop::current(), worker_loop_
);
85 base::MessageLoop
* worker_loop_
;
87 base::TimeDelta resolve_latency_
;
91 // A mock synchronous ProxyResolver which can be set to block upon reaching
93 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
94 // otherwise there will be a race on |should_block_| since it is
95 // read without any synchronization.
96 class BlockableProxyResolver
: public MockProxyResolver
{
98 BlockableProxyResolver()
99 : should_block_(false),
100 unblocked_(true, true),
101 blocked_(true, false) {
105 should_block_
= true;
110 should_block_
= false;
115 void WaitUntilBlocked() {
119 int GetProxyForURL(const GURL
& query_url
,
121 const CompletionCallback
& callback
,
122 RequestHandle
* request
,
123 const BoundNetLog
& net_log
) override
{
129 return MockProxyResolver::GetProxyForURL(
130 query_url
, results
, callback
, request
, net_log
);
135 base::WaitableEvent unblocked_
;
136 base::WaitableEvent blocked_
;
139 // This factory returns new instances of BlockableProxyResolver.
140 class BlockableProxyResolverFactory
: public ProxyResolverFactory
{
142 BlockableProxyResolverFactory() : ProxyResolverFactory(false) {}
144 ~BlockableProxyResolverFactory() override
{}
146 int CreateProxyResolver(
147 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
148 scoped_ptr
<ProxyResolver
>* result
,
149 const CompletionCallback
& callback
,
150 scoped_ptr
<Request
>* request
) override
{
151 BlockableProxyResolver
* resolver
= new BlockableProxyResolver
;
152 result
->reset(resolver
);
153 base::AutoLock
l(lock_
);
154 resolvers_
.push_back(resolver
);
155 script_data_
.push_back(script_data
);
159 std::vector
<BlockableProxyResolver
*> resolvers() {
160 base::AutoLock
l(lock_
);
164 const std::vector
<scoped_refptr
<ProxyResolverScriptData
>> script_data() {
165 base::AutoLock
l(lock_
);
170 std::vector
<BlockableProxyResolver
*> resolvers_
;
171 std::vector
<scoped_refptr
<ProxyResolverScriptData
>> script_data_
;
175 class SingleShotMultiThreadedProxyResolverFactory
176 : public MultiThreadedProxyResolverFactory
{
178 SingleShotMultiThreadedProxyResolverFactory(
179 size_t max_num_threads
,
180 scoped_ptr
<ProxyResolverFactory
> factory
)
181 : MultiThreadedProxyResolverFactory(max_num_threads
, false),
182 factory_(factory
.Pass()) {}
184 scoped_ptr
<ProxyResolverFactory
> CreateProxyResolverFactory() override
{
186 return factory_
.Pass();
190 scoped_ptr
<ProxyResolverFactory
> factory_
;
193 class MultiThreadedProxyResolverTest
: public testing::Test
{
195 void Init(size_t num_threads
) {
196 scoped_ptr
<BlockableProxyResolverFactory
> factory_owner(
197 new BlockableProxyResolverFactory
);
198 factory_
= factory_owner
.get();
199 resolver_factory_
.reset(new SingleShotMultiThreadedProxyResolverFactory(
200 num_threads
, factory_owner
.Pass()));
201 TestCompletionCallback ready_callback
;
202 scoped_ptr
<ProxyResolverFactory::Request
> request
;
203 resolver_factory_
->CreateProxyResolver(
204 ProxyResolverScriptData::FromUTF8("pac script bytes"), &resolver_
,
205 ready_callback
.callback(), &request
);
206 EXPECT_TRUE(request
);
207 ASSERT_EQ(OK
, ready_callback
.WaitForResult());
209 // Verify that the script data reaches the synchronous resolver factory.
210 ASSERT_EQ(1u, factory_
->script_data().size());
211 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
212 factory_
->script_data()[0]->utf16());
215 void ClearResolver() { resolver_
.reset(); }
217 BlockableProxyResolverFactory
& factory() {
221 ProxyResolver
& resolver() {
227 BlockableProxyResolverFactory
* factory_
= nullptr;
228 scoped_ptr
<ProxyResolverFactory
> factory_owner_
;
229 scoped_ptr
<MultiThreadedProxyResolverFactory
> resolver_factory_
;
230 scoped_ptr
<ProxyResolver
> resolver_
;
233 TEST_F(MultiThreadedProxyResolverTest
, SingleThread_Basic
) {
234 const size_t kNumThreads
= 1u;
235 ASSERT_NO_FATAL_FAILURE(Init(kNumThreads
));
239 TestCompletionCallback callback0
;
240 BoundTestNetLog log0
;
242 rv
= resolver().GetProxyForURL(GURL("http://request0"), &results0
,
243 callback0
.callback(), NULL
, log0
.bound());
244 EXPECT_EQ(ERR_IO_PENDING
, rv
);
246 // Wait for request 0 to finish.
247 rv
= callback0
.WaitForResult();
249 EXPECT_EQ("PROXY request0:80", results0
.ToPacString());
251 // The mock proxy resolver should have written 1 log entry. And
252 // on completion, this should have been copied into |log0|.
253 // We also have 1 log entry that was emitted by the
254 // MultiThreadedProxyResolver.
255 TestNetLogEntry::List entries0
;
256 log0
.GetEntries(&entries0
);
258 ASSERT_EQ(2u, entries0
.size());
259 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD
, entries0
[0].type
);
261 // Start 3 more requests (request1 to request3).
263 TestCompletionCallback callback1
;
265 rv
= resolver().GetProxyForURL(GURL("http://request1"), &results1
,
266 callback1
.callback(), NULL
, BoundNetLog());
267 EXPECT_EQ(ERR_IO_PENDING
, rv
);
269 TestCompletionCallback callback2
;
271 rv
= resolver().GetProxyForURL(GURL("http://request2"), &results2
,
272 callback2
.callback(), NULL
, BoundNetLog());
273 EXPECT_EQ(ERR_IO_PENDING
, rv
);
275 TestCompletionCallback callback3
;
277 rv
= resolver().GetProxyForURL(GURL("http://request3"), &results3
,
278 callback3
.callback(), NULL
, BoundNetLog());
279 EXPECT_EQ(ERR_IO_PENDING
, rv
);
281 // Wait for the requests to finish (they must finish in the order they were
282 // started, which is what we check for from their magic return value)
284 rv
= callback1
.WaitForResult();
286 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
288 rv
= callback2
.WaitForResult();
290 EXPECT_EQ("PROXY request2:80", results2
.ToPacString());
292 rv
= callback3
.WaitForResult();
294 EXPECT_EQ("PROXY request3:80", results3
.ToPacString());
297 // Tests that the NetLog is updated to include the time the request was waiting
298 // to be scheduled to a thread.
299 TEST_F(MultiThreadedProxyResolverTest
,
300 SingleThread_UpdatesNetLogWithThreadWait
) {
301 const size_t kNumThreads
= 1u;
302 ASSERT_NO_FATAL_FAILURE(Init(kNumThreads
));
306 // Block the proxy resolver, so no request can complete.
307 factory().resolvers()[0]->Block();
310 ProxyResolver::RequestHandle request0
;
311 TestCompletionCallback callback0
;
313 BoundTestNetLog log0
;
314 rv
= resolver().GetProxyForURL(GURL("http://request0"), &results0
,
315 callback0
.callback(), &request0
, log0
.bound());
316 EXPECT_EQ(ERR_IO_PENDING
, rv
);
318 // Start 2 more requests (request1 and request2).
320 TestCompletionCallback callback1
;
322 BoundTestNetLog log1
;
323 rv
= resolver().GetProxyForURL(GURL("http://request1"), &results1
,
324 callback1
.callback(), NULL
, log1
.bound());
325 EXPECT_EQ(ERR_IO_PENDING
, rv
);
327 ProxyResolver::RequestHandle request2
;
328 TestCompletionCallback callback2
;
330 BoundTestNetLog log2
;
331 rv
= resolver().GetProxyForURL(GURL("http://request2"), &results2
,
332 callback2
.callback(), &request2
, log2
.bound());
333 EXPECT_EQ(ERR_IO_PENDING
, rv
);
335 // Unblock the worker thread so the requests can continue running.
336 factory().resolvers()[0]->WaitUntilBlocked();
337 factory().resolvers()[0]->Unblock();
339 // Check that request 0 completed as expected.
340 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
341 // 1 entry from the mock proxy resolver.
342 EXPECT_EQ(0, callback0
.WaitForResult());
343 EXPECT_EQ("PROXY request0:80", results0
.ToPacString());
345 TestNetLogEntry::List entries0
;
346 log0
.GetEntries(&entries0
);
348 ASSERT_EQ(2u, entries0
.size());
349 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD
,
352 // Check that request 1 completed as expected.
353 EXPECT_EQ(1, callback1
.WaitForResult());
354 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
356 TestNetLogEntry::List entries1
;
357 log1
.GetEntries(&entries1
);
359 ASSERT_EQ(4u, entries1
.size());
360 EXPECT_TRUE(LogContainsBeginEvent(
362 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
363 EXPECT_TRUE(LogContainsEndEvent(
365 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
367 // Check that request 2 completed as expected.
368 EXPECT_EQ(2, callback2
.WaitForResult());
369 EXPECT_EQ("PROXY request2:80", results2
.ToPacString());
371 TestNetLogEntry::List entries2
;
372 log2
.GetEntries(&entries2
);
374 ASSERT_EQ(4u, entries2
.size());
375 EXPECT_TRUE(LogContainsBeginEvent(
377 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
378 EXPECT_TRUE(LogContainsEndEvent(
380 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
383 // Cancel a request which is in progress, and then cancel a request which
385 TEST_F(MultiThreadedProxyResolverTest
, SingleThread_CancelRequest
) {
386 const size_t kNumThreads
= 1u;
387 ASSERT_NO_FATAL_FAILURE(Init(kNumThreads
));
391 // Block the proxy resolver, so no request can complete.
392 factory().resolvers()[0]->Block();
395 ProxyResolver::RequestHandle request0
;
396 TestCompletionCallback callback0
;
399 resolver().GetProxyForURL(GURL("http://request0"), &results0
,
400 callback0
.callback(), &request0
, BoundNetLog());
401 EXPECT_EQ(ERR_IO_PENDING
, rv
);
403 // Wait until requests 0 reaches the worker thread.
404 factory().resolvers()[0]->WaitUntilBlocked();
406 // Start 3 more requests (request1 : request3).
408 TestCompletionCallback callback1
;
410 rv
= resolver().GetProxyForURL(GURL("http://request1"), &results1
,
411 callback1
.callback(), NULL
, BoundNetLog());
412 EXPECT_EQ(ERR_IO_PENDING
, rv
);
414 ProxyResolver::RequestHandle request2
;
415 TestCompletionCallback callback2
;
418 resolver().GetProxyForURL(GURL("http://request2"), &results2
,
419 callback2
.callback(), &request2
, BoundNetLog());
420 EXPECT_EQ(ERR_IO_PENDING
, rv
);
422 TestCompletionCallback callback3
;
424 rv
= resolver().GetProxyForURL(GURL("http://request3"), &results3
,
425 callback3
.callback(), NULL
, BoundNetLog());
426 EXPECT_EQ(ERR_IO_PENDING
, rv
);
428 // Cancel request0 (inprogress) and request2 (pending).
429 resolver().CancelRequest(request0
);
430 resolver().CancelRequest(request2
);
432 // Unblock the worker thread so the requests can continue running.
433 factory().resolvers()[0]->Unblock();
435 // Wait for requests 1 and 3 to finish.
437 rv
= callback1
.WaitForResult();
439 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
441 rv
= callback3
.WaitForResult();
442 // Note that since request2 was cancelled before reaching the resolver,
443 // the request count is 2 and not 3 here.
445 EXPECT_EQ("PROXY request3:80", results3
.ToPacString());
447 // Requests 0 and 2 which were cancelled, hence their completion callbacks
448 // were never summoned.
449 EXPECT_FALSE(callback0
.have_result());
450 EXPECT_FALSE(callback2
.have_result());
453 // Test that deleting MultiThreadedProxyResolver while requests are
454 // outstanding cancels them (and doesn't leak anything).
455 TEST_F(MultiThreadedProxyResolverTest
, SingleThread_CancelRequestByDeleting
) {
456 const size_t kNumThreads
= 1u;
457 ASSERT_NO_FATAL_FAILURE(Init(kNumThreads
));
459 ASSERT_EQ(1u, factory().resolvers().size());
461 // Block the proxy resolver, so no request can complete.
462 factory().resolvers()[0]->Block();
467 TestCompletionCallback callback0
;
469 rv
= resolver().GetProxyForURL(GURL("http://request0"), &results0
,
470 callback0
.callback(), NULL
, BoundNetLog());
471 EXPECT_EQ(ERR_IO_PENDING
, rv
);
473 TestCompletionCallback callback1
;
475 rv
= resolver().GetProxyForURL(GURL("http://request1"), &results1
,
476 callback1
.callback(), NULL
, BoundNetLog());
477 EXPECT_EQ(ERR_IO_PENDING
, rv
);
479 TestCompletionCallback callback2
;
481 rv
= resolver().GetProxyForURL(GURL("http://request2"), &results2
,
482 callback2
.callback(), NULL
, BoundNetLog());
483 EXPECT_EQ(ERR_IO_PENDING
, rv
);
485 // Wait until request 0 reaches the worker thread.
486 factory().resolvers()[0]->WaitUntilBlocked();
488 // Add some latency, to improve the chance that when
489 // MultiThreadedProxyResolver is deleted below we are still running inside
490 // of the worker thread. The test will pass regardless, so this race doesn't
491 // cause flakiness. However the destruction during execution is a more
492 // interesting case to test.
493 factory().resolvers()[0]->SetResolveLatency(
494 base::TimeDelta::FromMilliseconds(100));
496 // Unblock the worker thread and delete the underlying
497 // MultiThreadedProxyResolver immediately.
498 factory().resolvers()[0]->Unblock();
501 // Give any posted tasks a chance to run (in case there is badness).
502 base::MessageLoop::current()->RunUntilIdle();
504 // Check that none of the outstanding requests were completed.
505 EXPECT_FALSE(callback0
.have_result());
506 EXPECT_FALSE(callback1
.have_result());
507 EXPECT_FALSE(callback2
.have_result());
510 // Tests setting the PAC script once, lazily creating new threads, and
511 // cancelling requests.
512 TEST_F(MultiThreadedProxyResolverTest
, ThreeThreads_Basic
) {
513 const size_t kNumThreads
= 3u;
514 ASSERT_NO_FATAL_FAILURE(Init(kNumThreads
));
516 // Verify that it reaches the synchronous resolver.
517 // One thread has been provisioned (i.e. one ProxyResolver was created).
518 ASSERT_EQ(1u, factory().resolvers().size());
520 const int kNumRequests
= 8;
522 TestCompletionCallback callback
[kNumRequests
];
523 ProxyInfo results
[kNumRequests
];
524 ProxyResolver::RequestHandle request
[kNumRequests
];
526 // Start request 0 -- this should run on thread 0 as there is nothing else
527 // going on right now.
528 rv
= resolver().GetProxyForURL(GURL("http://request0"), &results
[0],
529 callback
[0].callback(), &request
[0],
531 EXPECT_EQ(ERR_IO_PENDING
, rv
);
533 // Wait for request 0 to finish.
534 rv
= callback
[0].WaitForResult();
536 EXPECT_EQ("PROXY request0:80", results
[0].ToPacString());
537 ASSERT_EQ(1u, factory().resolvers().size());
538 EXPECT_EQ(1, factory().resolvers()[0]->request_count());
540 base::MessageLoop::current()->RunUntilIdle();
542 // We now block the first resolver to ensure a request is sent to the second
544 factory().resolvers()[0]->Block();
545 rv
= resolver().GetProxyForURL(GURL("http://request1"), &results
[1],
546 callback
[1].callback(), &request
[1],
548 EXPECT_EQ(ERR_IO_PENDING
, rv
);
549 factory().resolvers()[0]->WaitUntilBlocked();
550 rv
= resolver().GetProxyForURL(GURL("http://request2"), &results
[2],
551 callback
[2].callback(), &request
[2],
553 EXPECT_EQ(ERR_IO_PENDING
, rv
);
554 EXPECT_EQ(0, callback
[2].WaitForResult());
555 ASSERT_EQ(2u, factory().resolvers().size());
557 // We now block the second resolver as well to ensure a request is sent to the
559 factory().resolvers()[1]->Block();
560 rv
= resolver().GetProxyForURL(GURL("http://request3"), &results
[3],
561 callback
[3].callback(), &request
[3],
563 EXPECT_EQ(ERR_IO_PENDING
, rv
);
564 factory().resolvers()[1]->WaitUntilBlocked();
565 rv
= resolver().GetProxyForURL(GURL("http://request4"), &results
[4],
566 callback
[4].callback(), &request
[4],
568 EXPECT_EQ(ERR_IO_PENDING
, rv
);
569 EXPECT_EQ(0, callback
[4].WaitForResult());
571 // We should now have a total of 3 threads, each with its own ProxyResolver
572 // that will get initialized with the same data.
573 ASSERT_EQ(3u, factory().resolvers().size());
575 ASSERT_EQ(3u, factory().script_data().size());
576 for (int i
= 0; i
< 3; ++i
) {
577 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
578 factory().script_data()[i
]->utf16())
582 // Start and cancel two requests. Since the first two threads are still
583 // blocked, they'll both be serviced by the third thread. The first request
584 // will reach the resolver, but the second will still be queued when canceled.
585 // Start a third request so we can be sure the resolver has completed running
586 // the first request.
587 rv
= resolver().GetProxyForURL(GURL("http://request5"), &results
[5],
588 callback
[5].callback(), &request
[5],
590 EXPECT_EQ(ERR_IO_PENDING
, rv
);
591 rv
= resolver().GetProxyForURL(GURL("http://request6"), &results
[6],
592 callback
[6].callback(), &request
[6],
594 EXPECT_EQ(ERR_IO_PENDING
, rv
);
595 rv
= resolver().GetProxyForURL(GURL("http://request7"), &results
[7],
596 callback
[7].callback(), &request
[7],
598 EXPECT_EQ(ERR_IO_PENDING
, rv
);
599 resolver().CancelRequest(request
[5]);
600 resolver().CancelRequest(request
[6]);
602 EXPECT_EQ(2, callback
[7].WaitForResult());
604 // Check that the cancelled requests never invoked their callback.
605 EXPECT_FALSE(callback
[5].have_result());
606 EXPECT_FALSE(callback
[6].have_result());
608 // Unblock the first two threads and wait for their requests to complete.
609 factory().resolvers()[0]->Unblock();
610 factory().resolvers()[1]->Unblock();
611 EXPECT_EQ(1, callback
[1].WaitForResult());
612 EXPECT_EQ(1, callback
[3].WaitForResult());
614 EXPECT_EQ(2, factory().resolvers()[0]->request_count());
615 EXPECT_EQ(2, factory().resolvers()[1]->request_count());
616 EXPECT_EQ(3, factory().resolvers()[2]->request_count());
619 // Tests using two threads. The first request hangs the first thread. Checks
620 // that other requests are able to complete while this first request remains
622 TEST_F(MultiThreadedProxyResolverTest
, OneThreadBlocked
) {
623 const size_t kNumThreads
= 2u;
624 ASSERT_NO_FATAL_FAILURE(Init(kNumThreads
));
628 // One thread has been provisioned (i.e. one ProxyResolver was created).
629 ASSERT_EQ(1u, factory().resolvers().size());
630 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
631 factory().script_data()[0]->utf16());
633 const int kNumRequests
= 4;
634 TestCompletionCallback callback
[kNumRequests
];
635 ProxyInfo results
[kNumRequests
];
636 ProxyResolver::RequestHandle request
[kNumRequests
];
638 // Start a request that will block the first thread.
640 factory().resolvers()[0]->Block();
642 rv
= resolver().GetProxyForURL(GURL("http://request0"), &results
[0],
643 callback
[0].callback(), &request
[0],
646 EXPECT_EQ(ERR_IO_PENDING
, rv
);
647 factory().resolvers()[0]->WaitUntilBlocked();
649 // Start 3 more requests -- they should all be serviced by thread #2
650 // since thread #1 is blocked.
652 for (int i
= 1; i
< kNumRequests
; ++i
) {
653 rv
= resolver().GetProxyForURL(
654 GURL(base::StringPrintf("http://request%d", i
)), &results
[i
],
655 callback
[i
].callback(), &request
[i
], BoundNetLog());
656 EXPECT_EQ(ERR_IO_PENDING
, rv
);
659 // Wait for the three requests to complete (they should complete in FIFO
661 for (int i
= 1; i
< kNumRequests
; ++i
) {
662 EXPECT_EQ(i
- 1, callback
[i
].WaitForResult());
665 // Unblock the first thread.
666 factory().resolvers()[0]->Unblock();
667 EXPECT_EQ(0, callback
[0].WaitForResult());
669 // All in all, the first thread should have seen just 1 request. And the
670 // second thread 3 requests.
671 ASSERT_EQ(2u, factory().resolvers().size());
672 EXPECT_EQ(1, factory().resolvers()[0]->request_count());
673 EXPECT_EQ(3, factory().resolvers()[1]->request_count());
676 class FailingProxyResolverFactory
: public ProxyResolverFactory
{
678 FailingProxyResolverFactory() : ProxyResolverFactory(false) {}
680 // ProxyResolverFactory override.
681 int CreateProxyResolver(
682 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
683 scoped_ptr
<ProxyResolver
>* result
,
684 const CompletionCallback
& callback
,
685 scoped_ptr
<Request
>* request
) override
{
686 return ERR_PAC_SCRIPT_FAILED
;
690 // Test that an error when creating the synchronous resolver causes the
691 // MultiThreadedProxyResolverFactory create request to fail with that error.
692 TEST_F(MultiThreadedProxyResolverTest
, ProxyResolverFactoryError
) {
693 const size_t kNumThreads
= 1u;
694 SingleShotMultiThreadedProxyResolverFactory
resolver_factory(
695 kNumThreads
, make_scoped_ptr(new FailingProxyResolverFactory
));
696 TestCompletionCallback ready_callback
;
697 scoped_ptr
<ProxyResolverFactory::Request
> request
;
698 scoped_ptr
<ProxyResolver
> resolver
;
699 EXPECT_EQ(ERR_IO_PENDING
,
700 resolver_factory
.CreateProxyResolver(
701 ProxyResolverScriptData::FromUTF8("pac script bytes"),
702 &resolver
, ready_callback
.callback(), &request
));
703 EXPECT_TRUE(request
);
704 EXPECT_EQ(ERR_PAC_SCRIPT_FAILED
, ready_callback
.WaitForResult());
705 EXPECT_FALSE(resolver
);
708 void Fail(int error
) {
709 FAIL() << "Unexpected callback with error " << error
;
712 // Test that cancelling an in-progress create request works correctly.
713 TEST_F(MultiThreadedProxyResolverTest
, CancelCreate
) {
714 const size_t kNumThreads
= 1u;
716 SingleShotMultiThreadedProxyResolverFactory
resolver_factory(
717 kNumThreads
, make_scoped_ptr(new BlockableProxyResolverFactory
));
718 scoped_ptr
<ProxyResolverFactory::Request
> request
;
719 scoped_ptr
<ProxyResolver
> resolver
;
720 EXPECT_EQ(ERR_IO_PENDING
,
721 resolver_factory
.CreateProxyResolver(
722 ProxyResolverScriptData::FromUTF8("pac script bytes"),
723 &resolver
, base::Bind(&Fail
), &request
));
724 EXPECT_TRUE(request
);
727 // The factory destructor will block until the worker thread stops, but it may
728 // post tasks to the origin message loop which are still pending. Run them
729 // now to ensure it works as expected.
730 base::RunLoop().RunUntilIdle();
733 void DeleteRequest(const CompletionCallback
& callback
,
734 scoped_ptr
<ProxyResolverFactory::Request
>* request
,
736 callback
.Run(result
);
740 // Test that delete the Request during the factory callback works correctly.
741 TEST_F(MultiThreadedProxyResolverTest
, DeleteRequestInFactoryCallback
) {
742 const size_t kNumThreads
= 1u;
743 SingleShotMultiThreadedProxyResolverFactory
resolver_factory(
744 kNumThreads
, make_scoped_ptr(new BlockableProxyResolverFactory
));
745 scoped_ptr
<ProxyResolverFactory::Request
> request
;
746 scoped_ptr
<ProxyResolver
> resolver
;
747 TestCompletionCallback callback
;
748 EXPECT_EQ(ERR_IO_PENDING
,
749 resolver_factory
.CreateProxyResolver(
750 ProxyResolverScriptData::FromUTF8("pac script bytes"),
751 &resolver
, base::Bind(&DeleteRequest
, callback
.callback(),
752 base::Unretained(&request
)),
754 EXPECT_TRUE(request
);
755 EXPECT_EQ(OK
, callback
.WaitForResult());
758 // Test that deleting the factory with a request in-progress works correctly.
759 TEST_F(MultiThreadedProxyResolverTest
, DestroyFactoryWithRequestsInProgress
) {
760 const size_t kNumThreads
= 1u;
761 scoped_ptr
<ProxyResolverFactory::Request
> request
;
762 scoped_ptr
<ProxyResolver
> resolver
;
764 SingleShotMultiThreadedProxyResolverFactory
resolver_factory(
765 kNumThreads
, make_scoped_ptr(new BlockableProxyResolverFactory
));
766 EXPECT_EQ(ERR_IO_PENDING
,
767 resolver_factory
.CreateProxyResolver(
768 ProxyResolverScriptData::FromUTF8("pac script bytes"),
769 &resolver
, base::Bind(&Fail
), &request
));
770 EXPECT_TRUE(request
);
772 // The factory destructor will block until the worker thread stops, but it may
773 // post tasks to the origin message loop which are still pending. Run them
774 // now to ensure it works as expected.
775 base::RunLoop().RunUntilIdle();