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"
7 #include "base/message_loop/message_loop.h"
8 #include "base/stl_util.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/threading/platform_thread.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/test_completion_callback.h"
16 #include "net/log/net_log.h"
17 #include "net/log/net_log_unittest.h"
18 #include "net/proxy/mock_proxy_resolver.h"
19 #include "net/proxy/proxy_info.h"
20 #include "net/proxy/proxy_resolver_factory.h"
21 #include "testing/gtest/include/gtest/gtest.h"
24 using base::ASCIIToUTF16
;
30 // A synchronous mock ProxyResolver implementation, which can be used in
31 // conjunction with MultiThreadedProxyResolver.
32 // - returns a single-item proxy list with the query's host.
33 class MockProxyResolver
: public ProxyResolver
{
36 : ProxyResolver(true /*expects_pac_bytes*/),
37 wrong_loop_(base::MessageLoop::current()),
40 // ProxyResolver implementation.
41 int GetProxyForURL(const GURL
& query_url
,
43 const CompletionCallback
& callback
,
44 RequestHandle
* request
,
45 const BoundNetLog
& net_log
) override
{
46 if (resolve_latency_
!= base::TimeDelta())
47 base::PlatformThread::Sleep(resolve_latency_
);
49 CheckIsOnWorkerThread();
51 EXPECT_TRUE(callback
.is_null());
52 EXPECT_TRUE(request
== NULL
);
54 // Write something into |net_log| (doesn't really have any meaning.)
55 net_log
.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT
);
57 results
->UseNamedProxy(query_url
.host());
59 // Return a success code which represents the request's order.
60 return request_count_
++;
63 void CancelRequest(RequestHandle request
) override
{ NOTREACHED(); }
65 LoadState
GetLoadState(RequestHandle request
) const override
{
67 return LOAD_STATE_IDLE
;
70 void CancelSetPacScript() override
{ NOTREACHED(); }
72 int SetPacScript(const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
73 const CompletionCallback
& callback
) override
{
74 CheckIsOnWorkerThread();
75 last_script_data_
= script_data
;
79 int request_count() const { return request_count_
; }
81 const ProxyResolverScriptData
* last_script_data() const {
82 return last_script_data_
.get();
85 void SetResolveLatency(base::TimeDelta latency
) {
86 resolve_latency_
= latency
;
90 void CheckIsOnWorkerThread() {
91 // We should be running on the worker thread -- while we don't know the
92 // message loop of MultiThreadedProxyResolver's worker thread, we do
93 // know that it is going to be distinct from the loop running the
94 // test, so at least make sure it isn't the main loop.
95 EXPECT_NE(base::MessageLoop::current(), wrong_loop_
);
98 base::MessageLoop
* wrong_loop_
;
100 scoped_refptr
<ProxyResolverScriptData
> last_script_data_
;
101 base::TimeDelta resolve_latency_
;
105 // A mock synchronous ProxyResolver which can be set to block upon reaching
107 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
108 // otherwise there will be a race on |should_block_| since it is
109 // read without any synchronization.
110 class BlockableProxyResolver
: public MockProxyResolver
{
112 BlockableProxyResolver()
113 : should_block_(false),
114 unblocked_(true, true),
115 blocked_(true, false) {
119 should_block_
= true;
124 should_block_
= false;
129 void WaitUntilBlocked() {
133 int GetProxyForURL(const GURL
& query_url
,
135 const CompletionCallback
& callback
,
136 RequestHandle
* request
,
137 const BoundNetLog
& net_log
) override
{
143 return MockProxyResolver::GetProxyForURL(
144 query_url
, results
, callback
, request
, net_log
);
149 base::WaitableEvent unblocked_
;
150 base::WaitableEvent blocked_
;
153 // This factory returns new instances of BlockableProxyResolver.
154 class BlockableProxyResolverFactory
: public ProxyResolverFactory
{
156 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
158 ~BlockableProxyResolverFactory() override
{ STLDeleteElements(&resolvers_
); }
160 scoped_ptr
<ProxyResolver
> CreateProxyResolver() override
{
161 BlockableProxyResolver
* resolver
= new BlockableProxyResolver
;
162 resolvers_
.push_back(resolver
);
163 return make_scoped_ptr(new ForwardingProxyResolver(resolver
));
166 std::vector
<BlockableProxyResolver
*> resolvers() {
171 std::vector
<BlockableProxyResolver
*> resolvers_
;
174 TEST(MultiThreadedProxyResolverTest
, SingleThread_Basic
) {
175 const size_t kNumThreads
= 1u;
176 scoped_ptr
<MockProxyResolver
> mock(new MockProxyResolver
);
177 MultiThreadedProxyResolver
resolver(
178 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
);
182 EXPECT_TRUE(resolver
.expects_pac_bytes());
184 // Call SetPacScriptByData() -- verify that it reaches the synchronous
186 TestCompletionCallback set_script_callback
;
187 rv
= resolver
.SetPacScript(
188 ProxyResolverScriptData::FromUTF8("pac script bytes"),
189 set_script_callback
.callback());
190 EXPECT_EQ(ERR_IO_PENDING
, rv
);
191 EXPECT_EQ(OK
, set_script_callback
.WaitForResult());
192 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
193 mock
->last_script_data()->utf16());
196 TestCompletionCallback callback0
;
197 CapturingBoundNetLog log0
;
199 rv
= resolver
.GetProxyForURL(GURL("http://request0"), &results0
,
200 callback0
.callback(), NULL
, log0
.bound());
201 EXPECT_EQ(ERR_IO_PENDING
, rv
);
203 // Wait for request 0 to finish.
204 rv
= callback0
.WaitForResult();
206 EXPECT_EQ("PROXY request0:80", results0
.ToPacString());
208 // The mock proxy resolver should have written 1 log entry. And
209 // on completion, this should have been copied into |log0|.
210 // We also have 1 log entry that was emitted by the
211 // MultiThreadedProxyResolver.
212 CapturingNetLog::CapturedEntryList entries0
;
213 log0
.GetEntries(&entries0
);
215 ASSERT_EQ(2u, entries0
.size());
216 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD
, entries0
[0].type
);
218 // Start 3 more requests (request1 to request3).
220 TestCompletionCallback callback1
;
222 rv
= resolver
.GetProxyForURL(GURL("http://request1"), &results1
,
223 callback1
.callback(), NULL
, BoundNetLog());
224 EXPECT_EQ(ERR_IO_PENDING
, rv
);
226 TestCompletionCallback callback2
;
228 rv
= resolver
.GetProxyForURL(GURL("http://request2"), &results2
,
229 callback2
.callback(), NULL
, BoundNetLog());
230 EXPECT_EQ(ERR_IO_PENDING
, rv
);
232 TestCompletionCallback callback3
;
234 rv
= resolver
.GetProxyForURL(GURL("http://request3"), &results3
,
235 callback3
.callback(), NULL
, BoundNetLog());
236 EXPECT_EQ(ERR_IO_PENDING
, rv
);
238 // Wait for the requests to finish (they must finish in the order they were
239 // started, which is what we check for from their magic return value)
241 rv
= callback1
.WaitForResult();
243 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
245 rv
= callback2
.WaitForResult();
247 EXPECT_EQ("PROXY request2:80", results2
.ToPacString());
249 rv
= callback3
.WaitForResult();
251 EXPECT_EQ("PROXY request3:80", results3
.ToPacString());
254 // Tests that the NetLog is updated to include the time the request was waiting
255 // to be scheduled to a thread.
256 TEST(MultiThreadedProxyResolverTest
,
257 SingleThread_UpdatesNetLogWithThreadWait
) {
258 const size_t kNumThreads
= 1u;
259 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
260 MultiThreadedProxyResolver
resolver(
261 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
);
265 // Initialize the resolver.
266 TestCompletionCallback init_callback
;
267 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
268 init_callback
.callback());
269 EXPECT_EQ(OK
, init_callback
.WaitForResult());
271 // Block the proxy resolver, so no request can complete.
275 ProxyResolver::RequestHandle request0
;
276 TestCompletionCallback callback0
;
278 CapturingBoundNetLog log0
;
279 rv
= resolver
.GetProxyForURL(GURL("http://request0"), &results0
,
280 callback0
.callback(), &request0
, log0
.bound());
281 EXPECT_EQ(ERR_IO_PENDING
, rv
);
283 // Start 2 more requests (request1 and request2).
285 TestCompletionCallback callback1
;
287 CapturingBoundNetLog log1
;
288 rv
= resolver
.GetProxyForURL(GURL("http://request1"), &results1
,
289 callback1
.callback(), NULL
, log1
.bound());
290 EXPECT_EQ(ERR_IO_PENDING
, rv
);
292 ProxyResolver::RequestHandle request2
;
293 TestCompletionCallback callback2
;
295 CapturingBoundNetLog log2
;
296 rv
= resolver
.GetProxyForURL(GURL("http://request2"), &results2
,
297 callback2
.callback(), &request2
, log2
.bound());
298 EXPECT_EQ(ERR_IO_PENDING
, rv
);
300 // Unblock the worker thread so the requests can continue running.
301 mock
->WaitUntilBlocked();
304 // Check that request 0 completed as expected.
305 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
306 // 1 entry from the mock proxy resolver.
307 EXPECT_EQ(0, callback0
.WaitForResult());
308 EXPECT_EQ("PROXY request0:80", results0
.ToPacString());
310 CapturingNetLog::CapturedEntryList entries0
;
311 log0
.GetEntries(&entries0
);
313 ASSERT_EQ(2u, entries0
.size());
314 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD
,
317 // Check that request 1 completed as expected.
318 EXPECT_EQ(1, callback1
.WaitForResult());
319 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
321 CapturingNetLog::CapturedEntryList entries1
;
322 log1
.GetEntries(&entries1
);
324 ASSERT_EQ(4u, entries1
.size());
325 EXPECT_TRUE(LogContainsBeginEvent(
327 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
328 EXPECT_TRUE(LogContainsEndEvent(
330 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
332 // Check that request 2 completed as expected.
333 EXPECT_EQ(2, callback2
.WaitForResult());
334 EXPECT_EQ("PROXY request2:80", results2
.ToPacString());
336 CapturingNetLog::CapturedEntryList entries2
;
337 log2
.GetEntries(&entries2
);
339 ASSERT_EQ(4u, entries2
.size());
340 EXPECT_TRUE(LogContainsBeginEvent(
342 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
343 EXPECT_TRUE(LogContainsEndEvent(
345 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD
));
348 // Cancel a request which is in progress, and then cancel a request which
350 TEST(MultiThreadedProxyResolverTest
, SingleThread_CancelRequest
) {
351 const size_t kNumThreads
= 1u;
352 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
353 MultiThreadedProxyResolver
resolver(
354 new ForwardingProxyResolverFactory(mock
.get()),
359 // Initialize the resolver.
360 TestCompletionCallback init_callback
;
361 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
362 init_callback
.callback());
363 EXPECT_EQ(OK
, init_callback
.WaitForResult());
365 // Block the proxy resolver, so no request can complete.
369 ProxyResolver::RequestHandle request0
;
370 TestCompletionCallback callback0
;
372 rv
= resolver
.GetProxyForURL(GURL("http://request0"), &results0
,
373 callback0
.callback(), &request0
, BoundNetLog());
374 EXPECT_EQ(ERR_IO_PENDING
, rv
);
376 // Wait until requests 0 reaches the worker thread.
377 mock
->WaitUntilBlocked();
379 // Start 3 more requests (request1 : request3).
381 TestCompletionCallback callback1
;
383 rv
= resolver
.GetProxyForURL(GURL("http://request1"), &results1
,
384 callback1
.callback(), NULL
, BoundNetLog());
385 EXPECT_EQ(ERR_IO_PENDING
, rv
);
387 ProxyResolver::RequestHandle request2
;
388 TestCompletionCallback callback2
;
390 rv
= resolver
.GetProxyForURL(GURL("http://request2"), &results2
,
391 callback2
.callback(), &request2
, BoundNetLog());
392 EXPECT_EQ(ERR_IO_PENDING
, rv
);
394 TestCompletionCallback callback3
;
396 rv
= resolver
.GetProxyForURL(GURL("http://request3"), &results3
,
397 callback3
.callback(), NULL
, BoundNetLog());
398 EXPECT_EQ(ERR_IO_PENDING
, rv
);
400 // Cancel request0 (inprogress) and request2 (pending).
401 resolver
.CancelRequest(request0
);
402 resolver
.CancelRequest(request2
);
404 // Unblock the worker thread so the requests can continue running.
407 // Wait for requests 1 and 3 to finish.
409 rv
= callback1
.WaitForResult();
411 EXPECT_EQ("PROXY request1:80", results1
.ToPacString());
413 rv
= callback3
.WaitForResult();
414 // Note that since request2 was cancelled before reaching the resolver,
415 // the request count is 2 and not 3 here.
417 EXPECT_EQ("PROXY request3:80", results3
.ToPacString());
419 // Requests 0 and 2 which were cancelled, hence their completion callbacks
420 // were never summoned.
421 EXPECT_FALSE(callback0
.have_result());
422 EXPECT_FALSE(callback2
.have_result());
425 // Test that deleting MultiThreadedProxyResolver while requests are
426 // outstanding cancels them (and doesn't leak anything).
427 TEST(MultiThreadedProxyResolverTest
, SingleThread_CancelRequestByDeleting
) {
428 const size_t kNumThreads
= 1u;
429 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
430 scoped_ptr
<MultiThreadedProxyResolver
> resolver(
431 new MultiThreadedProxyResolver(
432 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
));
436 // Initialize the resolver.
437 TestCompletionCallback init_callback
;
438 rv
= resolver
->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
439 init_callback
.callback());
440 EXPECT_EQ(OK
, init_callback
.WaitForResult());
442 // Block the proxy resolver, so no request can complete.
447 TestCompletionCallback callback0
;
449 rv
= resolver
->GetProxyForURL(GURL("http://request0"), &results0
,
450 callback0
.callback(), NULL
, BoundNetLog());
451 EXPECT_EQ(ERR_IO_PENDING
, rv
);
453 TestCompletionCallback callback1
;
455 rv
= resolver
->GetProxyForURL(GURL("http://request1"), &results1
,
456 callback1
.callback(), NULL
, BoundNetLog());
457 EXPECT_EQ(ERR_IO_PENDING
, rv
);
459 TestCompletionCallback callback2
;
461 rv
= resolver
->GetProxyForURL(GURL("http://request2"), &results2
,
462 callback2
.callback(), NULL
, BoundNetLog());
463 EXPECT_EQ(ERR_IO_PENDING
, rv
);
465 // Wait until request 0 reaches the worker thread.
466 mock
->WaitUntilBlocked();
468 // Add some latency, to improve the chance that when
469 // MultiThreadedProxyResolver is deleted below we are still running inside
470 // of the worker thread. The test will pass regardless, so this race doesn't
471 // cause flakiness. However the destruction during execution is a more
472 // interesting case to test.
473 mock
->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
475 // Unblock the worker thread and delete the underlying
476 // MultiThreadedProxyResolver immediately.
480 // Give any posted tasks a chance to run (in case there is badness).
481 base::MessageLoop::current()->RunUntilIdle();
483 // Check that none of the outstanding requests were completed.
484 EXPECT_FALSE(callback0
.have_result());
485 EXPECT_FALSE(callback1
.have_result());
486 EXPECT_FALSE(callback2
.have_result());
489 // Cancel an outstanding call to SetPacScriptByData().
490 TEST(MultiThreadedProxyResolverTest
, SingleThread_CancelSetPacScript
) {
491 const size_t kNumThreads
= 1u;
492 scoped_ptr
<BlockableProxyResolver
> mock(new BlockableProxyResolver
);
493 MultiThreadedProxyResolver
resolver(
494 new ForwardingProxyResolverFactory(mock
.get()), kNumThreads
);
498 TestCompletionCallback set_pac_script_callback
;
499 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
500 set_pac_script_callback
.callback());
501 EXPECT_EQ(ERR_IO_PENDING
, rv
);
503 // Cancel the SetPacScriptByData request.
504 resolver
.CancelSetPacScript();
506 // Start another SetPacScript request
507 TestCompletionCallback set_pac_script_callback2
;
508 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
509 set_pac_script_callback2
.callback());
510 EXPECT_EQ(ERR_IO_PENDING
, rv
);
512 // Wait for the initialization to complete.
514 rv
= set_pac_script_callback2
.WaitForResult();
516 EXPECT_EQ(ASCIIToUTF16("data2"), mock
->last_script_data()->utf16());
518 // The first SetPacScript callback should never have been completed.
519 EXPECT_FALSE(set_pac_script_callback
.have_result());
522 // Tests setting the PAC script once, lazily creating new threads, and
523 // cancelling requests.
524 TEST(MultiThreadedProxyResolverTest
, ThreeThreads_Basic
) {
525 const size_t kNumThreads
= 3u;
526 BlockableProxyResolverFactory
* factory
= new BlockableProxyResolverFactory
;
527 MultiThreadedProxyResolver
resolver(factory
, kNumThreads
);
531 EXPECT_TRUE(resolver
.expects_pac_bytes());
533 // Call SetPacScriptByData() -- verify that it reaches the synchronous
535 TestCompletionCallback set_script_callback
;
536 rv
= resolver
.SetPacScript(
537 ProxyResolverScriptData::FromUTF8("pac script bytes"),
538 set_script_callback
.callback());
539 EXPECT_EQ(ERR_IO_PENDING
, rv
);
540 EXPECT_EQ(OK
, set_script_callback
.WaitForResult());
541 // One thread has been provisioned (i.e. one ProxyResolver was created).
542 ASSERT_EQ(1u, factory
->resolvers().size());
543 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
544 factory
->resolvers()[0]->last_script_data()->utf16());
546 const int kNumRequests
= 9;
547 TestCompletionCallback callback
[kNumRequests
];
548 ProxyInfo results
[kNumRequests
];
549 ProxyResolver::RequestHandle request
[kNumRequests
];
551 // Start request 0 -- this should run on thread 0 as there is nothing else
552 // going on right now.
553 rv
= resolver
.GetProxyForURL(
554 GURL("http://request0"), &results
[0], callback
[0].callback(), &request
[0],
556 EXPECT_EQ(ERR_IO_PENDING
, rv
);
558 // Wait for request 0 to finish.
559 rv
= callback
[0].WaitForResult();
561 EXPECT_EQ("PROXY request0:80", results
[0].ToPacString());
562 ASSERT_EQ(1u, factory
->resolvers().size());
563 EXPECT_EQ(1, factory
->resolvers()[0]->request_count());
565 base::MessageLoop::current()->RunUntilIdle();
567 // We now start 8 requests in parallel -- this will cause the maximum of
568 // three threads to be provisioned (an additional two from what we already
571 for (int i
= 1; i
< kNumRequests
; ++i
) {
572 rv
= resolver
.GetProxyForURL(
573 GURL(base::StringPrintf("http://request%d", i
)), &results
[i
],
574 callback
[i
].callback(), &request
[i
], BoundNetLog());
575 EXPECT_EQ(ERR_IO_PENDING
, rv
);
578 // We should now have a total of 3 threads, each with its own ProxyResolver
579 // that will get initialized with the same data. (We check this later since
580 // the assignment happens on the worker threads and may not have occurred
582 ASSERT_EQ(3u, factory
->resolvers().size());
584 // Cancel 3 of the 8 oustanding requests.
585 resolver
.CancelRequest(request
[1]);
586 resolver
.CancelRequest(request
[3]);
587 resolver
.CancelRequest(request
[6]);
589 // Wait for the remaining requests to complete.
590 int kNonCancelledRequests
[] = {2, 4, 5, 7, 8};
591 for (size_t i
= 0; i
< arraysize(kNonCancelledRequests
); ++i
) {
592 int request_index
= kNonCancelledRequests
[i
];
593 EXPECT_GE(callback
[request_index
].WaitForResult(), 0);
596 // Check that the cancelled requests never invoked their callback.
597 EXPECT_FALSE(callback
[1].have_result());
598 EXPECT_FALSE(callback
[3].have_result());
599 EXPECT_FALSE(callback
[6].have_result());
601 // We call SetPacScript again, solely to stop the current worker threads.
602 // (That way we can test to see the values observed by the synchronous
603 // resolvers in a non-racy manner).
604 TestCompletionCallback set_script_callback2
;
605 rv
= resolver
.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
606 set_script_callback2
.callback());
607 EXPECT_EQ(ERR_IO_PENDING
, rv
);
608 EXPECT_EQ(OK
, set_script_callback2
.WaitForResult());
609 ASSERT_EQ(4u, factory
->resolvers().size());
611 for (int i
= 0; i
< 3; ++i
) {
613 ASCIIToUTF16("pac script bytes"),
614 factory
->resolvers()[i
]->last_script_data()->utf16()) << "i=" << i
;
617 EXPECT_EQ(ASCIIToUTF16("xyz"),
618 factory
->resolvers()[3]->last_script_data()->utf16());
620 // We don't know the exact ordering that requests ran on threads with,
621 // but we do know the total count that should have reached the threads.
622 // 8 total were submitted, and three were cancelled. Of the three that
623 // were cancelled, one of them (request 1) was cancelled after it had
624 // already been posted to the worker thread. So the resolvers will
625 // have seen 6 total (and 1 from the run prior).
626 ASSERT_EQ(4u, factory
->resolvers().size());
628 for (int i
= 0; i
< 3; ++i
) {
629 total_count
+= factory
->resolvers()[i
]->request_count();
631 EXPECT_EQ(7, total_count
);
634 // Tests using two threads. The first request hangs the first thread. Checks
635 // that other requests are able to complete while this first request remains
637 TEST(MultiThreadedProxyResolverTest
, OneThreadBlocked
) {
638 const size_t kNumThreads
= 2u;
639 BlockableProxyResolverFactory
* factory
= new BlockableProxyResolverFactory
;
640 MultiThreadedProxyResolver
resolver(factory
, kNumThreads
);
644 EXPECT_TRUE(resolver
.expects_pac_bytes());
646 // Initialize the resolver.
647 TestCompletionCallback set_script_callback
;
648 rv
= resolver
.SetPacScript(
649 ProxyResolverScriptData::FromUTF8("pac script bytes"),
650 set_script_callback
.callback());
651 EXPECT_EQ(ERR_IO_PENDING
, rv
);
652 EXPECT_EQ(OK
, set_script_callback
.WaitForResult());
653 // One thread has been provisioned (i.e. one ProxyResolver was created).
654 ASSERT_EQ(1u, factory
->resolvers().size());
655 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
656 factory
->resolvers()[0]->last_script_data()->utf16());
658 const int kNumRequests
= 4;
659 TestCompletionCallback callback
[kNumRequests
];
660 ProxyInfo results
[kNumRequests
];
661 ProxyResolver::RequestHandle request
[kNumRequests
];
663 // Start a request that will block the first thread.
665 factory
->resolvers()[0]->Block();
667 rv
= resolver
.GetProxyForURL(
668 GURL("http://request0"), &results
[0], callback
[0].callback(), &request
[0],
671 EXPECT_EQ(ERR_IO_PENDING
, rv
);
672 factory
->resolvers()[0]->WaitUntilBlocked();
674 // Start 3 more requests -- they should all be serviced by thread #2
675 // since thread #1 is blocked.
677 for (int i
= 1; i
< kNumRequests
; ++i
) {
678 rv
= resolver
.GetProxyForURL(
679 GURL(base::StringPrintf("http://request%d", i
)),
680 &results
[i
], callback
[i
].callback(), &request
[i
], BoundNetLog());
681 EXPECT_EQ(ERR_IO_PENDING
, rv
);
684 // Wait for the three requests to complete (they should complete in FIFO
686 for (int i
= 1; i
< kNumRequests
; ++i
) {
687 EXPECT_EQ(i
- 1, callback
[i
].WaitForResult());
690 // Unblock the first thread.
691 factory
->resolvers()[0]->Unblock();
692 EXPECT_EQ(0, callback
[0].WaitForResult());
694 // All in all, the first thread should have seen just 1 request. And the
695 // second thread 3 requests.
696 ASSERT_EQ(2u, factory
->resolvers().size());
697 EXPECT_EQ(1, factory
->resolvers()[0]->request_count());
698 EXPECT_EQ(3, factory
->resolvers()[1]->request_count());