Enable system token in platformKeys api.
[chromium-blink-merge.git] / net / proxy / multi_threaded_proxy_resolver_unittest.cc
blobe1c9e90b761ef677bda373f8058ba0091dae11d2
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/net_log.h"
16 #include "net/base/net_log_unittest.h"
17 #include "net/base/test_completion_callback.h"
18 #include "net/proxy/proxy_info.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "url/gurl.h"
22 using base::ASCIIToUTF16;
24 namespace net {
26 namespace {
28 // A synchronous mock ProxyResolver implementation, which can be used in
29 // conjunction with MultiThreadedProxyResolver.
30 // - returns a single-item proxy list with the query's host.
31 class MockProxyResolver : public ProxyResolver {
32 public:
33 MockProxyResolver()
34 : ProxyResolver(true /*expects_pac_bytes*/),
35 wrong_loop_(base::MessageLoop::current()),
36 request_count_(0) {}
38 // ProxyResolver implementation.
39 virtual int GetProxyForURL(const GURL& query_url,
40 ProxyInfo* results,
41 const CompletionCallback& callback,
42 RequestHandle* request,
43 const BoundNetLog& net_log) OVERRIDE {
44 if (resolve_latency_ != base::TimeDelta())
45 base::PlatformThread::Sleep(resolve_latency_);
47 CheckIsOnWorkerThread();
49 EXPECT_TRUE(callback.is_null());
50 EXPECT_TRUE(request == NULL);
52 // Write something into |net_log| (doesn't really have any meaning.)
53 net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT);
55 results->UseNamedProxy(query_url.host());
57 // Return a success code which represents the request's order.
58 return request_count_++;
61 virtual void CancelRequest(RequestHandle request) OVERRIDE {
62 NOTREACHED();
65 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
66 NOTREACHED();
67 return LOAD_STATE_IDLE;
70 virtual void CancelSetPacScript() OVERRIDE {
71 NOTREACHED();
74 virtual int SetPacScript(
75 const scoped_refptr<ProxyResolverScriptData>& script_data,
76 const CompletionCallback& callback) OVERRIDE {
77 CheckIsOnWorkerThread();
78 last_script_data_ = script_data;
79 return OK;
82 int request_count() const { return request_count_; }
84 const ProxyResolverScriptData* last_script_data() const {
85 return last_script_data_.get();
88 void SetResolveLatency(base::TimeDelta latency) {
89 resolve_latency_ = latency;
92 private:
93 void CheckIsOnWorkerThread() {
94 // We should be running on the worker thread -- while we don't know the
95 // message loop of MultiThreadedProxyResolver's worker thread, we do
96 // know that it is going to be distinct from the loop running the
97 // test, so at least make sure it isn't the main loop.
98 EXPECT_NE(base::MessageLoop::current(), wrong_loop_);
101 base::MessageLoop* wrong_loop_;
102 int request_count_;
103 scoped_refptr<ProxyResolverScriptData> last_script_data_;
104 base::TimeDelta resolve_latency_;
108 // A mock synchronous ProxyResolver which can be set to block upon reaching
109 // GetProxyForURL().
110 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
111 // otherwise there will be a race on |should_block_| since it is
112 // read without any synchronization.
113 class BlockableProxyResolver : public MockProxyResolver {
114 public:
115 BlockableProxyResolver()
116 : should_block_(false),
117 unblocked_(true, true),
118 blocked_(true, false) {
121 void Block() {
122 should_block_ = true;
123 unblocked_.Reset();
126 void Unblock() {
127 should_block_ = false;
128 blocked_.Reset();
129 unblocked_.Signal();
132 void WaitUntilBlocked() {
133 blocked_.Wait();
136 virtual int GetProxyForURL(const GURL& query_url,
137 ProxyInfo* results,
138 const CompletionCallback& callback,
139 RequestHandle* request,
140 const BoundNetLog& net_log) OVERRIDE {
141 if (should_block_) {
142 blocked_.Signal();
143 unblocked_.Wait();
146 return MockProxyResolver::GetProxyForURL(
147 query_url, results, callback, request, net_log);
150 private:
151 bool should_block_;
152 base::WaitableEvent unblocked_;
153 base::WaitableEvent blocked_;
156 // ForwardingProxyResolver forwards all requests to |impl|.
157 class ForwardingProxyResolver : public ProxyResolver {
158 public:
159 explicit ForwardingProxyResolver(ProxyResolver* impl)
160 : ProxyResolver(impl->expects_pac_bytes()),
161 impl_(impl) {}
163 virtual int GetProxyForURL(const GURL& query_url,
164 ProxyInfo* results,
165 const CompletionCallback& callback,
166 RequestHandle* request,
167 const BoundNetLog& net_log) OVERRIDE {
168 return impl_->GetProxyForURL(
169 query_url, results, callback, request, net_log);
172 virtual void CancelRequest(RequestHandle request) OVERRIDE {
173 impl_->CancelRequest(request);
176 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
177 NOTREACHED();
178 return LOAD_STATE_IDLE;
181 virtual void CancelSetPacScript() OVERRIDE {
182 impl_->CancelSetPacScript();
185 virtual int SetPacScript(
186 const scoped_refptr<ProxyResolverScriptData>& script_data,
187 const CompletionCallback& callback) OVERRIDE {
188 return impl_->SetPacScript(script_data, callback);
191 private:
192 ProxyResolver* impl_;
195 // This factory returns ProxyResolvers that forward all requests to
196 // |resolver|.
197 class ForwardingProxyResolverFactory : public ProxyResolverFactory {
198 public:
199 explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
200 : ProxyResolverFactory(resolver->expects_pac_bytes()),
201 resolver_(resolver) {}
203 virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
204 return new ForwardingProxyResolver(resolver_);
207 private:
208 ProxyResolver* resolver_;
211 // This factory returns new instances of BlockableProxyResolver.
212 class BlockableProxyResolverFactory : public ProxyResolverFactory {
213 public:
214 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
216 virtual ~BlockableProxyResolverFactory() {
217 STLDeleteElements(&resolvers_);
220 virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
221 BlockableProxyResolver* resolver = new BlockableProxyResolver;
222 resolvers_.push_back(resolver);
223 return new ForwardingProxyResolver(resolver);
226 std::vector<BlockableProxyResolver*> resolvers() {
227 return resolvers_;
230 private:
231 std::vector<BlockableProxyResolver*> resolvers_;
234 TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
235 const size_t kNumThreads = 1u;
236 scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
237 MultiThreadedProxyResolver resolver(
238 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
240 int rv;
242 EXPECT_TRUE(resolver.expects_pac_bytes());
244 // Call SetPacScriptByData() -- verify that it reaches the synchronous
245 // resolver.
246 TestCompletionCallback set_script_callback;
247 rv = resolver.SetPacScript(
248 ProxyResolverScriptData::FromUTF8("pac script bytes"),
249 set_script_callback.callback());
250 EXPECT_EQ(ERR_IO_PENDING, rv);
251 EXPECT_EQ(OK, set_script_callback.WaitForResult());
252 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
253 mock->last_script_data()->utf16());
255 // Start request 0.
256 TestCompletionCallback callback0;
257 CapturingBoundNetLog log0;
258 ProxyInfo results0;
259 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
260 callback0.callback(), NULL, log0.bound());
261 EXPECT_EQ(ERR_IO_PENDING, rv);
263 // Wait for request 0 to finish.
264 rv = callback0.WaitForResult();
265 EXPECT_EQ(0, rv);
266 EXPECT_EQ("PROXY request0:80", results0.ToPacString());
268 // The mock proxy resolver should have written 1 log entry. And
269 // on completion, this should have been copied into |log0|.
270 // We also have 1 log entry that was emitted by the
271 // MultiThreadedProxyResolver.
272 CapturingNetLog::CapturedEntryList entries0;
273 log0.GetEntries(&entries0);
275 ASSERT_EQ(2u, entries0.size());
276 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
278 // Start 3 more requests (request1 to request3).
280 TestCompletionCallback callback1;
281 ProxyInfo results1;
282 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
283 callback1.callback(), NULL, BoundNetLog());
284 EXPECT_EQ(ERR_IO_PENDING, rv);
286 TestCompletionCallback callback2;
287 ProxyInfo results2;
288 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
289 callback2.callback(), NULL, BoundNetLog());
290 EXPECT_EQ(ERR_IO_PENDING, rv);
292 TestCompletionCallback callback3;
293 ProxyInfo results3;
294 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
295 callback3.callback(), NULL, BoundNetLog());
296 EXPECT_EQ(ERR_IO_PENDING, rv);
298 // Wait for the requests to finish (they must finish in the order they were
299 // started, which is what we check for from their magic return value)
301 rv = callback1.WaitForResult();
302 EXPECT_EQ(1, rv);
303 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
305 rv = callback2.WaitForResult();
306 EXPECT_EQ(2, rv);
307 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
309 rv = callback3.WaitForResult();
310 EXPECT_EQ(3, rv);
311 EXPECT_EQ("PROXY request3:80", results3.ToPacString());
314 // Tests that the NetLog is updated to include the time the request was waiting
315 // to be scheduled to a thread.
316 TEST(MultiThreadedProxyResolverTest,
317 SingleThread_UpdatesNetLogWithThreadWait) {
318 const size_t kNumThreads = 1u;
319 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
320 MultiThreadedProxyResolver resolver(
321 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
323 int rv;
325 // Initialize the resolver.
326 TestCompletionCallback init_callback;
327 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
328 init_callback.callback());
329 EXPECT_EQ(OK, init_callback.WaitForResult());
331 // Block the proxy resolver, so no request can complete.
332 mock->Block();
334 // Start request 0.
335 ProxyResolver::RequestHandle request0;
336 TestCompletionCallback callback0;
337 ProxyInfo results0;
338 CapturingBoundNetLog log0;
339 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
340 callback0.callback(), &request0, log0.bound());
341 EXPECT_EQ(ERR_IO_PENDING, rv);
343 // Start 2 more requests (request1 and request2).
345 TestCompletionCallback callback1;
346 ProxyInfo results1;
347 CapturingBoundNetLog log1;
348 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
349 callback1.callback(), NULL, log1.bound());
350 EXPECT_EQ(ERR_IO_PENDING, rv);
352 ProxyResolver::RequestHandle request2;
353 TestCompletionCallback callback2;
354 ProxyInfo results2;
355 CapturingBoundNetLog log2;
356 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
357 callback2.callback(), &request2, log2.bound());
358 EXPECT_EQ(ERR_IO_PENDING, rv);
360 // Unblock the worker thread so the requests can continue running.
361 mock->WaitUntilBlocked();
362 mock->Unblock();
364 // Check that request 0 completed as expected.
365 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
366 // 1 entry from the mock proxy resolver.
367 EXPECT_EQ(0, callback0.WaitForResult());
368 EXPECT_EQ("PROXY request0:80", results0.ToPacString());
370 CapturingNetLog::CapturedEntryList entries0;
371 log0.GetEntries(&entries0);
373 ASSERT_EQ(2u, entries0.size());
374 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
375 entries0[0].type);
377 // Check that request 1 completed as expected.
378 EXPECT_EQ(1, callback1.WaitForResult());
379 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
381 CapturingNetLog::CapturedEntryList entries1;
382 log1.GetEntries(&entries1);
384 ASSERT_EQ(4u, entries1.size());
385 EXPECT_TRUE(LogContainsBeginEvent(
386 entries1, 0,
387 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
388 EXPECT_TRUE(LogContainsEndEvent(
389 entries1, 1,
390 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
392 // Check that request 2 completed as expected.
393 EXPECT_EQ(2, callback2.WaitForResult());
394 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
396 CapturingNetLog::CapturedEntryList entries2;
397 log2.GetEntries(&entries2);
399 ASSERT_EQ(4u, entries2.size());
400 EXPECT_TRUE(LogContainsBeginEvent(
401 entries2, 0,
402 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
403 EXPECT_TRUE(LogContainsEndEvent(
404 entries2, 1,
405 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
408 // Cancel a request which is in progress, and then cancel a request which
409 // is pending.
410 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
411 const size_t kNumThreads = 1u;
412 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
413 MultiThreadedProxyResolver resolver(
414 new ForwardingProxyResolverFactory(mock.get()),
415 kNumThreads);
417 int rv;
419 // Initialize the resolver.
420 TestCompletionCallback init_callback;
421 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
422 init_callback.callback());
423 EXPECT_EQ(OK, init_callback.WaitForResult());
425 // Block the proxy resolver, so no request can complete.
426 mock->Block();
428 // Start request 0.
429 ProxyResolver::RequestHandle request0;
430 TestCompletionCallback callback0;
431 ProxyInfo results0;
432 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
433 callback0.callback(), &request0, BoundNetLog());
434 EXPECT_EQ(ERR_IO_PENDING, rv);
436 // Wait until requests 0 reaches the worker thread.
437 mock->WaitUntilBlocked();
439 // Start 3 more requests (request1 : request3).
441 TestCompletionCallback callback1;
442 ProxyInfo results1;
443 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
444 callback1.callback(), NULL, BoundNetLog());
445 EXPECT_EQ(ERR_IO_PENDING, rv);
447 ProxyResolver::RequestHandle request2;
448 TestCompletionCallback callback2;
449 ProxyInfo results2;
450 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
451 callback2.callback(), &request2, BoundNetLog());
452 EXPECT_EQ(ERR_IO_PENDING, rv);
454 TestCompletionCallback callback3;
455 ProxyInfo results3;
456 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
457 callback3.callback(), NULL, BoundNetLog());
458 EXPECT_EQ(ERR_IO_PENDING, rv);
460 // Cancel request0 (inprogress) and request2 (pending).
461 resolver.CancelRequest(request0);
462 resolver.CancelRequest(request2);
464 // Unblock the worker thread so the requests can continue running.
465 mock->Unblock();
467 // Wait for requests 1 and 3 to finish.
469 rv = callback1.WaitForResult();
470 EXPECT_EQ(1, rv);
471 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
473 rv = callback3.WaitForResult();
474 // Note that since request2 was cancelled before reaching the resolver,
475 // the request count is 2 and not 3 here.
476 EXPECT_EQ(2, rv);
477 EXPECT_EQ("PROXY request3:80", results3.ToPacString());
479 // Requests 0 and 2 which were cancelled, hence their completion callbacks
480 // were never summoned.
481 EXPECT_FALSE(callback0.have_result());
482 EXPECT_FALSE(callback2.have_result());
485 // Test that deleting MultiThreadedProxyResolver while requests are
486 // outstanding cancels them (and doesn't leak anything).
487 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
488 const size_t kNumThreads = 1u;
489 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
490 scoped_ptr<MultiThreadedProxyResolver> resolver(
491 new MultiThreadedProxyResolver(
492 new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
494 int rv;
496 // Initialize the resolver.
497 TestCompletionCallback init_callback;
498 rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
499 init_callback.callback());
500 EXPECT_EQ(OK, init_callback.WaitForResult());
502 // Block the proxy resolver, so no request can complete.
503 mock->Block();
505 // Start 3 requests.
507 TestCompletionCallback callback0;
508 ProxyInfo results0;
509 rv = resolver->GetProxyForURL(GURL("http://request0"), &results0,
510 callback0.callback(), NULL, BoundNetLog());
511 EXPECT_EQ(ERR_IO_PENDING, rv);
513 TestCompletionCallback callback1;
514 ProxyInfo results1;
515 rv = resolver->GetProxyForURL(GURL("http://request1"), &results1,
516 callback1.callback(), NULL, BoundNetLog());
517 EXPECT_EQ(ERR_IO_PENDING, rv);
519 TestCompletionCallback callback2;
520 ProxyInfo results2;
521 rv = resolver->GetProxyForURL(GURL("http://request2"), &results2,
522 callback2.callback(), NULL, BoundNetLog());
523 EXPECT_EQ(ERR_IO_PENDING, rv);
525 // Wait until request 0 reaches the worker thread.
526 mock->WaitUntilBlocked();
528 // Add some latency, to improve the chance that when
529 // MultiThreadedProxyResolver is deleted below we are still running inside
530 // of the worker thread. The test will pass regardless, so this race doesn't
531 // cause flakiness. However the destruction during execution is a more
532 // interesting case to test.
533 mock->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
535 // Unblock the worker thread and delete the underlying
536 // MultiThreadedProxyResolver immediately.
537 mock->Unblock();
538 resolver.reset();
540 // Give any posted tasks a chance to run (in case there is badness).
541 base::MessageLoop::current()->RunUntilIdle();
543 // Check that none of the outstanding requests were completed.
544 EXPECT_FALSE(callback0.have_result());
545 EXPECT_FALSE(callback1.have_result());
546 EXPECT_FALSE(callback2.have_result());
549 // Cancel an outstanding call to SetPacScriptByData().
550 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
551 const size_t kNumThreads = 1u;
552 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
553 MultiThreadedProxyResolver resolver(
554 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
556 int rv;
558 TestCompletionCallback set_pac_script_callback;
559 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
560 set_pac_script_callback.callback());
561 EXPECT_EQ(ERR_IO_PENDING, rv);
563 // Cancel the SetPacScriptByData request.
564 resolver.CancelSetPacScript();
566 // Start another SetPacScript request
567 TestCompletionCallback set_pac_script_callback2;
568 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
569 set_pac_script_callback2.callback());
570 EXPECT_EQ(ERR_IO_PENDING, rv);
572 // Wait for the initialization to complete.
574 rv = set_pac_script_callback2.WaitForResult();
575 EXPECT_EQ(0, rv);
576 EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
578 // The first SetPacScript callback should never have been completed.
579 EXPECT_FALSE(set_pac_script_callback.have_result());
582 // Tests setting the PAC script once, lazily creating new threads, and
583 // cancelling requests.
584 TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
585 const size_t kNumThreads = 3u;
586 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
587 MultiThreadedProxyResolver resolver(factory, kNumThreads);
589 int rv;
591 EXPECT_TRUE(resolver.expects_pac_bytes());
593 // Call SetPacScriptByData() -- verify that it reaches the synchronous
594 // resolver.
595 TestCompletionCallback set_script_callback;
596 rv = resolver.SetPacScript(
597 ProxyResolverScriptData::FromUTF8("pac script bytes"),
598 set_script_callback.callback());
599 EXPECT_EQ(ERR_IO_PENDING, rv);
600 EXPECT_EQ(OK, set_script_callback.WaitForResult());
601 // One thread has been provisioned (i.e. one ProxyResolver was created).
602 ASSERT_EQ(1u, factory->resolvers().size());
603 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
604 factory->resolvers()[0]->last_script_data()->utf16());
606 const int kNumRequests = 9;
607 TestCompletionCallback callback[kNumRequests];
608 ProxyInfo results[kNumRequests];
609 ProxyResolver::RequestHandle request[kNumRequests];
611 // Start request 0 -- this should run on thread 0 as there is nothing else
612 // going on right now.
613 rv = resolver.GetProxyForURL(
614 GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
615 BoundNetLog());
616 EXPECT_EQ(ERR_IO_PENDING, rv);
618 // Wait for request 0 to finish.
619 rv = callback[0].WaitForResult();
620 EXPECT_EQ(0, rv);
621 EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
622 ASSERT_EQ(1u, factory->resolvers().size());
623 EXPECT_EQ(1, factory->resolvers()[0]->request_count());
625 base::MessageLoop::current()->RunUntilIdle();
627 // We now start 8 requests in parallel -- this will cause the maximum of
628 // three threads to be provisioned (an additional two from what we already
629 // have).
631 for (int i = 1; i < kNumRequests; ++i) {
632 rv = resolver.GetProxyForURL(
633 GURL(base::StringPrintf("http://request%d", i)), &results[i],
634 callback[i].callback(), &request[i], BoundNetLog());
635 EXPECT_EQ(ERR_IO_PENDING, rv);
638 // We should now have a total of 3 threads, each with its own ProxyResolver
639 // that will get initialized with the same data. (We check this later since
640 // the assignment happens on the worker threads and may not have occurred
641 // yet.)
642 ASSERT_EQ(3u, factory->resolvers().size());
644 // Cancel 3 of the 8 oustanding requests.
645 resolver.CancelRequest(request[1]);
646 resolver.CancelRequest(request[3]);
647 resolver.CancelRequest(request[6]);
649 // Wait for the remaining requests to complete.
650 int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
651 for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
652 int request_index = kNonCancelledRequests[i];
653 EXPECT_GE(callback[request_index].WaitForResult(), 0);
656 // Check that the cancelled requests never invoked their callback.
657 EXPECT_FALSE(callback[1].have_result());
658 EXPECT_FALSE(callback[3].have_result());
659 EXPECT_FALSE(callback[6].have_result());
661 // We call SetPacScript again, solely to stop the current worker threads.
662 // (That way we can test to see the values observed by the synchronous
663 // resolvers in a non-racy manner).
664 TestCompletionCallback set_script_callback2;
665 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
666 set_script_callback2.callback());
667 EXPECT_EQ(ERR_IO_PENDING, rv);
668 EXPECT_EQ(OK, set_script_callback2.WaitForResult());
669 ASSERT_EQ(4u, factory->resolvers().size());
671 for (int i = 0; i < 3; ++i) {
672 EXPECT_EQ(
673 ASCIIToUTF16("pac script bytes"),
674 factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
677 EXPECT_EQ(ASCIIToUTF16("xyz"),
678 factory->resolvers()[3]->last_script_data()->utf16());
680 // We don't know the exact ordering that requests ran on threads with,
681 // but we do know the total count that should have reached the threads.
682 // 8 total were submitted, and three were cancelled. Of the three that
683 // were cancelled, one of them (request 1) was cancelled after it had
684 // already been posted to the worker thread. So the resolvers will
685 // have seen 6 total (and 1 from the run prior).
686 ASSERT_EQ(4u, factory->resolvers().size());
687 int total_count = 0;
688 for (int i = 0; i < 3; ++i) {
689 total_count += factory->resolvers()[i]->request_count();
691 EXPECT_EQ(7, total_count);
694 // Tests using two threads. The first request hangs the first thread. Checks
695 // that other requests are able to complete while this first request remains
696 // stalled.
697 TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
698 const size_t kNumThreads = 2u;
699 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
700 MultiThreadedProxyResolver resolver(factory, kNumThreads);
702 int rv;
704 EXPECT_TRUE(resolver.expects_pac_bytes());
706 // Initialize the resolver.
707 TestCompletionCallback set_script_callback;
708 rv = resolver.SetPacScript(
709 ProxyResolverScriptData::FromUTF8("pac script bytes"),
710 set_script_callback.callback());
711 EXPECT_EQ(ERR_IO_PENDING, rv);
712 EXPECT_EQ(OK, set_script_callback.WaitForResult());
713 // One thread has been provisioned (i.e. one ProxyResolver was created).
714 ASSERT_EQ(1u, factory->resolvers().size());
715 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
716 factory->resolvers()[0]->last_script_data()->utf16());
718 const int kNumRequests = 4;
719 TestCompletionCallback callback[kNumRequests];
720 ProxyInfo results[kNumRequests];
721 ProxyResolver::RequestHandle request[kNumRequests];
723 // Start a request that will block the first thread.
725 factory->resolvers()[0]->Block();
727 rv = resolver.GetProxyForURL(
728 GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
729 BoundNetLog());
731 EXPECT_EQ(ERR_IO_PENDING, rv);
732 factory->resolvers()[0]->WaitUntilBlocked();
734 // Start 3 more requests -- they should all be serviced by thread #2
735 // since thread #1 is blocked.
737 for (int i = 1; i < kNumRequests; ++i) {
738 rv = resolver.GetProxyForURL(
739 GURL(base::StringPrintf("http://request%d", i)),
740 &results[i], callback[i].callback(), &request[i], BoundNetLog());
741 EXPECT_EQ(ERR_IO_PENDING, rv);
744 // Wait for the three requests to complete (they should complete in FIFO
745 // order).
746 for (int i = 1; i < kNumRequests; ++i) {
747 EXPECT_EQ(i - 1, callback[i].WaitForResult());
750 // Unblock the first thread.
751 factory->resolvers()[0]->Unblock();
752 EXPECT_EQ(0, callback[0].WaitForResult());
754 // All in all, the first thread should have seen just 1 request. And the
755 // second thread 3 requests.
756 ASSERT_EQ(2u, factory->resolvers().size());
757 EXPECT_EQ(1, factory->resolvers()[0]->request_count());
758 EXPECT_EQ(3, factory->resolvers()[1]->request_count());
761 } // namespace
763 } // namespace net