Enabling tests which should be fixed by r173829.
[chromium-blink-merge.git] / net / proxy / multi_threaded_proxy_resolver_unittest.cc
blobdbd2f5d7478135b37085fc9d64ba7cd81efce50d
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.h"
8 #include "base/stl_util.h"
9 #include "base/string_util.h"
10 #include "base/stringprintf.h"
11 #include "base/threading/platform_thread.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "googleurl/src/gurl.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_log.h"
17 #include "net/base/net_log_unittest.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/proxy/proxy_info.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 namespace net {
24 namespace {
26 // A synchronous mock ProxyResolver implementation, which can be used in
27 // conjunction with MultiThreadedProxyResolver.
28 // - returns a single-item proxy list with the query's host.
29 class MockProxyResolver : public ProxyResolver {
30 public:
31 MockProxyResolver()
32 : ProxyResolver(true /*expects_pac_bytes*/),
33 wrong_loop_(MessageLoop::current()),
34 request_count_(0),
35 purge_count_(0) {}
37 // ProxyResolver implementation.
38 virtual int GetProxyForURL(const GURL& query_url,
39 ProxyInfo* results,
40 const CompletionCallback& callback,
41 RequestHandle* request,
42 const BoundNetLog& net_log) OVERRIDE {
43 if (resolve_latency_ != base::TimeDelta())
44 base::PlatformThread::Sleep(resolve_latency_);
46 CheckIsOnWorkerThread();
48 EXPECT_TRUE(callback.is_null());
49 EXPECT_TRUE(request == NULL);
51 // Write something into |net_log| (doesn't really have any meaning.)
52 net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE);
54 results->UseNamedProxy(query_url.host());
56 // Return a success code which represents the request's order.
57 return request_count_++;
60 virtual void CancelRequest(RequestHandle request) OVERRIDE {
61 NOTREACHED();
64 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
65 NOTREACHED();
66 return LOAD_STATE_IDLE;
69 virtual LoadState GetLoadStateThreadSafe(
70 RequestHandle request) const OVERRIDE {
71 NOTREACHED();
72 return LOAD_STATE_IDLE;
75 virtual void CancelSetPacScript() OVERRIDE {
76 NOTREACHED();
79 virtual int SetPacScript(
80 const scoped_refptr<ProxyResolverScriptData>& script_data,
81 const CompletionCallback& callback) OVERRIDE {
82 CheckIsOnWorkerThread();
83 last_script_data_ = script_data;
84 return OK;
87 virtual void PurgeMemory() OVERRIDE {
88 CheckIsOnWorkerThread();
89 ++purge_count_;
92 int purge_count() const { return purge_count_; }
93 int request_count() const { return request_count_; }
95 const ProxyResolverScriptData* last_script_data() const {
96 return last_script_data_;
99 void SetResolveLatency(base::TimeDelta latency) {
100 resolve_latency_ = latency;
103 private:
104 void CheckIsOnWorkerThread() {
105 // We should be running on the worker thread -- while we don't know the
106 // message loop of MultiThreadedProxyResolver's worker thread, we do
107 // know that it is going to be distinct from the loop running the
108 // test, so at least make sure it isn't the main loop.
109 EXPECT_NE(MessageLoop::current(), wrong_loop_);
112 MessageLoop* wrong_loop_;
113 int request_count_;
114 int purge_count_;
115 scoped_refptr<ProxyResolverScriptData> last_script_data_;
116 base::TimeDelta resolve_latency_;
120 // A mock synchronous ProxyResolver which can be set to block upon reaching
121 // GetProxyForURL().
122 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
123 // otherwise there will be a race on |should_block_| since it is
124 // read without any synchronization.
125 class BlockableProxyResolver : public MockProxyResolver {
126 public:
127 BlockableProxyResolver()
128 : should_block_(false),
129 unblocked_(true, true),
130 blocked_(true, false) {
133 void Block() {
134 should_block_ = true;
135 unblocked_.Reset();
138 void Unblock() {
139 should_block_ = false;
140 blocked_.Reset();
141 unblocked_.Signal();
144 void WaitUntilBlocked() {
145 blocked_.Wait();
148 virtual int GetProxyForURL(const GURL& query_url,
149 ProxyInfo* results,
150 const CompletionCallback& callback,
151 RequestHandle* request,
152 const BoundNetLog& net_log) OVERRIDE {
153 if (should_block_) {
154 blocked_.Signal();
155 unblocked_.Wait();
158 return MockProxyResolver::GetProxyForURL(
159 query_url, results, callback, request, net_log);
162 private:
163 bool should_block_;
164 base::WaitableEvent unblocked_;
165 base::WaitableEvent blocked_;
168 // ForwardingProxyResolver forwards all requests to |impl|.
169 class ForwardingProxyResolver : public ProxyResolver {
170 public:
171 explicit ForwardingProxyResolver(ProxyResolver* impl)
172 : ProxyResolver(impl->expects_pac_bytes()),
173 impl_(impl) {}
175 virtual int GetProxyForURL(const GURL& query_url,
176 ProxyInfo* results,
177 const CompletionCallback& callback,
178 RequestHandle* request,
179 const BoundNetLog& net_log) OVERRIDE {
180 return impl_->GetProxyForURL(
181 query_url, results, callback, request, net_log);
184 virtual void CancelRequest(RequestHandle request) OVERRIDE {
185 impl_->CancelRequest(request);
188 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
189 NOTREACHED();
190 return LOAD_STATE_IDLE;
193 virtual LoadState GetLoadStateThreadSafe(
194 RequestHandle request) const OVERRIDE {
195 NOTREACHED();
196 return LOAD_STATE_IDLE;
199 virtual void CancelSetPacScript() OVERRIDE {
200 impl_->CancelSetPacScript();
203 virtual int SetPacScript(
204 const scoped_refptr<ProxyResolverScriptData>& script_data,
205 const CompletionCallback& callback) OVERRIDE {
206 return impl_->SetPacScript(script_data, callback);
209 virtual void PurgeMemory() OVERRIDE {
210 impl_->PurgeMemory();
213 private:
214 ProxyResolver* impl_;
217 // This factory returns ProxyResolvers that forward all requests to
218 // |resolver|.
219 class ForwardingProxyResolverFactory : public ProxyResolverFactory {
220 public:
221 explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
222 : ProxyResolverFactory(resolver->expects_pac_bytes()),
223 resolver_(resolver) {}
225 virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
226 return new ForwardingProxyResolver(resolver_);
229 private:
230 ProxyResolver* resolver_;
233 // This factory returns new instances of BlockableProxyResolver.
234 class BlockableProxyResolverFactory : public ProxyResolverFactory {
235 public:
236 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
238 ~BlockableProxyResolverFactory() {
239 STLDeleteElements(&resolvers_);
242 virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
243 BlockableProxyResolver* resolver = new BlockableProxyResolver;
244 resolvers_.push_back(resolver);
245 return new ForwardingProxyResolver(resolver);
248 std::vector<BlockableProxyResolver*> resolvers() {
249 return resolvers_;
252 private:
253 std::vector<BlockableProxyResolver*> resolvers_;
256 TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
257 const size_t kNumThreads = 1u;
258 scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
259 MultiThreadedProxyResolver resolver(
260 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
262 int rv;
264 EXPECT_TRUE(resolver.expects_pac_bytes());
266 // Call SetPacScriptByData() -- verify that it reaches the synchronous
267 // resolver.
268 TestCompletionCallback set_script_callback;
269 rv = resolver.SetPacScript(
270 ProxyResolverScriptData::FromUTF8("pac script bytes"),
271 set_script_callback.callback());
272 EXPECT_EQ(ERR_IO_PENDING, rv);
273 EXPECT_EQ(OK, set_script_callback.WaitForResult());
274 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
275 mock->last_script_data()->utf16());
277 // Start request 0.
278 TestCompletionCallback callback0;
279 CapturingBoundNetLog log0;
280 ProxyInfo results0;
281 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
282 callback0.callback(), NULL, log0.bound());
283 EXPECT_EQ(ERR_IO_PENDING, rv);
285 // Wait for request 0 to finish.
286 rv = callback0.WaitForResult();
287 EXPECT_EQ(0, rv);
288 EXPECT_EQ("PROXY request0:80", results0.ToPacString());
290 // The mock proxy resolver should have written 1 log entry. And
291 // on completion, this should have been copied into |log0|.
292 // We also have 1 log entry that was emitted by the
293 // MultiThreadedProxyResolver.
294 CapturingNetLog::CapturedEntryList entries0;
295 log0.GetEntries(&entries0);
297 ASSERT_EQ(2u, entries0.size());
298 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
300 // Start 3 more requests (request1 to request3).
302 TestCompletionCallback callback1;
303 ProxyInfo results1;
304 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
305 callback1.callback(), NULL, BoundNetLog());
306 EXPECT_EQ(ERR_IO_PENDING, rv);
308 TestCompletionCallback callback2;
309 ProxyInfo results2;
310 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
311 callback2.callback(), NULL, BoundNetLog());
312 EXPECT_EQ(ERR_IO_PENDING, rv);
314 TestCompletionCallback callback3;
315 ProxyInfo results3;
316 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
317 callback3.callback(), NULL, BoundNetLog());
318 EXPECT_EQ(ERR_IO_PENDING, rv);
320 // Wait for the requests to finish (they must finish in the order they were
321 // started, which is what we check for from their magic return value)
323 rv = callback1.WaitForResult();
324 EXPECT_EQ(1, rv);
325 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
327 rv = callback2.WaitForResult();
328 EXPECT_EQ(2, rv);
329 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
331 rv = callback3.WaitForResult();
332 EXPECT_EQ(3, rv);
333 EXPECT_EQ("PROXY request3:80", results3.ToPacString());
335 // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the
336 // right thread.
337 EXPECT_EQ(0, mock->purge_count());
338 resolver.PurgeMemory();
339 // There is no way to get a callback directly when PurgeMemory() completes, so
340 // we queue up a dummy request after the PurgeMemory() call and wait until it
341 // finishes to ensure PurgeMemory() has had a chance to run.
342 TestCompletionCallback dummy_callback;
343 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"),
344 dummy_callback.callback());
345 EXPECT_EQ(OK, dummy_callback.WaitForResult());
346 EXPECT_EQ(1, mock->purge_count());
349 // Tests that the NetLog is updated to include the time the request was waiting
350 // to be scheduled to a thread.
351 TEST(MultiThreadedProxyResolverTest,
352 SingleThread_UpdatesNetLogWithThreadWait) {
353 const size_t kNumThreads = 1u;
354 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
355 MultiThreadedProxyResolver resolver(
356 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
358 int rv;
360 // Initialize the resolver.
361 TestCompletionCallback init_callback;
362 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
363 init_callback.callback());
364 EXPECT_EQ(OK, init_callback.WaitForResult());
366 // Block the proxy resolver, so no request can complete.
367 mock->Block();
369 // Start request 0.
370 ProxyResolver::RequestHandle request0;
371 TestCompletionCallback callback0;
372 ProxyInfo results0;
373 CapturingBoundNetLog log0;
374 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
375 callback0.callback(), &request0, log0.bound());
376 EXPECT_EQ(ERR_IO_PENDING, rv);
378 // Start 2 more requests (request1 and request2).
380 TestCompletionCallback callback1;
381 ProxyInfo results1;
382 CapturingBoundNetLog log1;
383 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
384 callback1.callback(), NULL, log1.bound());
385 EXPECT_EQ(ERR_IO_PENDING, rv);
387 ProxyResolver::RequestHandle request2;
388 TestCompletionCallback callback2;
389 ProxyInfo results2;
390 CapturingBoundNetLog log2;
391 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
392 callback2.callback(), &request2, log2.bound());
393 EXPECT_EQ(ERR_IO_PENDING, rv);
395 // Unblock the worker thread so the requests can continue running.
396 mock->WaitUntilBlocked();
397 mock->Unblock();
399 // Check that request 0 completed as expected.
400 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
401 // 1 entry from the mock proxy resolver.
402 EXPECT_EQ(0, callback0.WaitForResult());
403 EXPECT_EQ("PROXY request0:80", results0.ToPacString());
405 CapturingNetLog::CapturedEntryList entries0;
406 log0.GetEntries(&entries0);
408 ASSERT_EQ(2u, entries0.size());
409 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
410 entries0[0].type);
412 // Check that request 1 completed as expected.
413 EXPECT_EQ(1, callback1.WaitForResult());
414 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
416 CapturingNetLog::CapturedEntryList entries1;
417 log1.GetEntries(&entries1);
419 ASSERT_EQ(4u, entries1.size());
420 EXPECT_TRUE(LogContainsBeginEvent(
421 entries1, 0,
422 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
423 EXPECT_TRUE(LogContainsEndEvent(
424 entries1, 1,
425 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
427 // Check that request 2 completed as expected.
428 EXPECT_EQ(2, callback2.WaitForResult());
429 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
431 CapturingNetLog::CapturedEntryList entries2;
432 log2.GetEntries(&entries2);
434 ASSERT_EQ(4u, entries2.size());
435 EXPECT_TRUE(LogContainsBeginEvent(
436 entries2, 0,
437 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
438 EXPECT_TRUE(LogContainsEndEvent(
439 entries2, 1,
440 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
443 // Cancel a request which is in progress, and then cancel a request which
444 // is pending.
445 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
446 const size_t kNumThreads = 1u;
447 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
448 MultiThreadedProxyResolver resolver(
449 new ForwardingProxyResolverFactory(mock.get()),
450 kNumThreads);
452 int rv;
454 // Initialize the resolver.
455 TestCompletionCallback init_callback;
456 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
457 init_callback.callback());
458 EXPECT_EQ(OK, init_callback.WaitForResult());
460 // Block the proxy resolver, so no request can complete.
461 mock->Block();
463 // Start request 0.
464 ProxyResolver::RequestHandle request0;
465 TestCompletionCallback callback0;
466 ProxyInfo results0;
467 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
468 callback0.callback(), &request0, BoundNetLog());
469 EXPECT_EQ(ERR_IO_PENDING, rv);
471 // Wait until requests 0 reaches the worker thread.
472 mock->WaitUntilBlocked();
474 // Start 3 more requests (request1 : request3).
476 TestCompletionCallback callback1;
477 ProxyInfo results1;
478 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
479 callback1.callback(), NULL, BoundNetLog());
480 EXPECT_EQ(ERR_IO_PENDING, rv);
482 ProxyResolver::RequestHandle request2;
483 TestCompletionCallback callback2;
484 ProxyInfo results2;
485 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
486 callback2.callback(), &request2, BoundNetLog());
487 EXPECT_EQ(ERR_IO_PENDING, rv);
489 TestCompletionCallback callback3;
490 ProxyInfo results3;
491 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
492 callback3.callback(), NULL, BoundNetLog());
493 EXPECT_EQ(ERR_IO_PENDING, rv);
495 // Cancel request0 (inprogress) and request2 (pending).
496 resolver.CancelRequest(request0);
497 resolver.CancelRequest(request2);
499 // Unblock the worker thread so the requests can continue running.
500 mock->Unblock();
502 // Wait for requests 1 and 3 to finish.
504 rv = callback1.WaitForResult();
505 EXPECT_EQ(1, rv);
506 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
508 rv = callback3.WaitForResult();
509 // Note that since request2 was cancelled before reaching the resolver,
510 // the request count is 2 and not 3 here.
511 EXPECT_EQ(2, rv);
512 EXPECT_EQ("PROXY request3:80", results3.ToPacString());
514 // Requests 0 and 2 which were cancelled, hence their completion callbacks
515 // were never summoned.
516 EXPECT_FALSE(callback0.have_result());
517 EXPECT_FALSE(callback2.have_result());
520 // Test that deleting MultiThreadedProxyResolver while requests are
521 // outstanding cancels them (and doesn't leak anything).
522 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
523 const size_t kNumThreads = 1u;
524 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
525 scoped_ptr<MultiThreadedProxyResolver> resolver(
526 new MultiThreadedProxyResolver(
527 new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
529 int rv;
531 // Initialize the resolver.
532 TestCompletionCallback init_callback;
533 rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
534 init_callback.callback());
535 EXPECT_EQ(OK, init_callback.WaitForResult());
537 // Block the proxy resolver, so no request can complete.
538 mock->Block();
540 // Start 3 requests.
542 TestCompletionCallback callback0;
543 ProxyInfo results0;
544 rv = resolver->GetProxyForURL(GURL("http://request0"), &results0,
545 callback0.callback(), NULL, BoundNetLog());
546 EXPECT_EQ(ERR_IO_PENDING, rv);
548 TestCompletionCallback callback1;
549 ProxyInfo results1;
550 rv = resolver->GetProxyForURL(GURL("http://request1"), &results1,
551 callback1.callback(), NULL, BoundNetLog());
552 EXPECT_EQ(ERR_IO_PENDING, rv);
554 TestCompletionCallback callback2;
555 ProxyInfo results2;
556 rv = resolver->GetProxyForURL(GURL("http://request2"), &results2,
557 callback2.callback(), NULL, BoundNetLog());
558 EXPECT_EQ(ERR_IO_PENDING, rv);
560 // Wait until request 0 reaches the worker thread.
561 mock->WaitUntilBlocked();
563 // Add some latency, to improve the chance that when
564 // MultiThreadedProxyResolver is deleted below we are still running inside
565 // of the worker thread. The test will pass regardless, so this race doesn't
566 // cause flakiness. However the destruction during execution is a more
567 // interesting case to test.
568 mock->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
570 // Unblock the worker thread and delete the underlying
571 // MultiThreadedProxyResolver immediately.
572 mock->Unblock();
573 resolver.reset();
575 // Give any posted tasks a chance to run (in case there is badness).
576 MessageLoop::current()->RunUntilIdle();
578 // Check that none of the outstanding requests were completed.
579 EXPECT_FALSE(callback0.have_result());
580 EXPECT_FALSE(callback1.have_result());
581 EXPECT_FALSE(callback2.have_result());
584 // Cancel an outstanding call to SetPacScriptByData().
585 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
586 const size_t kNumThreads = 1u;
587 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
588 MultiThreadedProxyResolver resolver(
589 new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
591 int rv;
593 TestCompletionCallback set_pac_script_callback;
594 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
595 set_pac_script_callback.callback());
596 EXPECT_EQ(ERR_IO_PENDING, rv);
598 // Cancel the SetPacScriptByData request.
599 resolver.CancelSetPacScript();
601 // Start another SetPacScript request
602 TestCompletionCallback set_pac_script_callback2;
603 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
604 set_pac_script_callback2.callback());
605 EXPECT_EQ(ERR_IO_PENDING, rv);
607 // Wait for the initialization to complete.
609 rv = set_pac_script_callback2.WaitForResult();
610 EXPECT_EQ(0, rv);
611 EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
613 // The first SetPacScript callback should never have been completed.
614 EXPECT_FALSE(set_pac_script_callback.have_result());
617 // Tests setting the PAC script once, lazily creating new threads, and
618 // cancelling requests.
619 TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
620 const size_t kNumThreads = 3u;
621 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
622 MultiThreadedProxyResolver resolver(factory, kNumThreads);
624 int rv;
626 EXPECT_TRUE(resolver.expects_pac_bytes());
628 // Call SetPacScriptByData() -- verify that it reaches the synchronous
629 // resolver.
630 TestCompletionCallback set_script_callback;
631 rv = resolver.SetPacScript(
632 ProxyResolverScriptData::FromUTF8("pac script bytes"),
633 set_script_callback.callback());
634 EXPECT_EQ(ERR_IO_PENDING, rv);
635 EXPECT_EQ(OK, set_script_callback.WaitForResult());
636 // One thread has been provisioned (i.e. one ProxyResolver was created).
637 ASSERT_EQ(1u, factory->resolvers().size());
638 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
639 factory->resolvers()[0]->last_script_data()->utf16());
641 const int kNumRequests = 9;
642 TestCompletionCallback callback[kNumRequests];
643 ProxyInfo results[kNumRequests];
644 ProxyResolver::RequestHandle request[kNumRequests];
646 // Start request 0 -- this should run on thread 0 as there is nothing else
647 // going on right now.
648 rv = resolver.GetProxyForURL(
649 GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
650 BoundNetLog());
651 EXPECT_EQ(ERR_IO_PENDING, rv);
653 // Wait for request 0 to finish.
654 rv = callback[0].WaitForResult();
655 EXPECT_EQ(0, rv);
656 EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
657 ASSERT_EQ(1u, factory->resolvers().size());
658 EXPECT_EQ(1, factory->resolvers()[0]->request_count());
660 MessageLoop::current()->RunUntilIdle();
662 // We now start 8 requests in parallel -- this will cause the maximum of
663 // three threads to be provisioned (an additional two from what we already
664 // have).
666 for (int i = 1; i < kNumRequests; ++i) {
667 rv = resolver.GetProxyForURL(
668 GURL(base::StringPrintf("http://request%d", i)), &results[i],
669 callback[i].callback(), &request[i], BoundNetLog());
670 EXPECT_EQ(ERR_IO_PENDING, rv);
673 // We should now have a total of 3 threads, each with its own ProxyResolver
674 // that will get initialized with the same data. (We check this later since
675 // the assignment happens on the worker threads and may not have occurred
676 // yet.)
677 ASSERT_EQ(3u, factory->resolvers().size());
679 // Cancel 3 of the 8 oustanding requests.
680 resolver.CancelRequest(request[1]);
681 resolver.CancelRequest(request[3]);
682 resolver.CancelRequest(request[6]);
684 // Wait for the remaining requests to complete.
685 int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
686 for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
687 int request_index = kNonCancelledRequests[i];
688 EXPECT_GE(callback[request_index].WaitForResult(), 0);
691 // Check that the cancelled requests never invoked their callback.
692 EXPECT_FALSE(callback[1].have_result());
693 EXPECT_FALSE(callback[3].have_result());
694 EXPECT_FALSE(callback[6].have_result());
696 // We call SetPacScript again, solely to stop the current worker threads.
697 // (That way we can test to see the values observed by the synchronous
698 // resolvers in a non-racy manner).
699 TestCompletionCallback set_script_callback2;
700 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
701 set_script_callback2.callback());
702 EXPECT_EQ(ERR_IO_PENDING, rv);
703 EXPECT_EQ(OK, set_script_callback2.WaitForResult());
704 ASSERT_EQ(4u, factory->resolvers().size());
706 for (int i = 0; i < 3; ++i) {
707 EXPECT_EQ(
708 ASCIIToUTF16("pac script bytes"),
709 factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
712 EXPECT_EQ(ASCIIToUTF16("xyz"),
713 factory->resolvers()[3]->last_script_data()->utf16());
715 // We don't know the exact ordering that requests ran on threads with,
716 // but we do know the total count that should have reached the threads.
717 // 8 total were submitted, and three were cancelled. Of the three that
718 // were cancelled, one of them (request 1) was cancelled after it had
719 // already been posted to the worker thread. So the resolvers will
720 // have seen 6 total (and 1 from the run prior).
721 ASSERT_EQ(4u, factory->resolvers().size());
722 int total_count = 0;
723 for (int i = 0; i < 3; ++i) {
724 total_count += factory->resolvers()[i]->request_count();
726 EXPECT_EQ(7, total_count);
729 // Tests using two threads. The first request hangs the first thread. Checks
730 // that other requests are able to complete while this first request remains
731 // stalled.
732 TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
733 const size_t kNumThreads = 2u;
734 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
735 MultiThreadedProxyResolver resolver(factory, kNumThreads);
737 int rv;
739 EXPECT_TRUE(resolver.expects_pac_bytes());
741 // Initialize the resolver.
742 TestCompletionCallback set_script_callback;
743 rv = resolver.SetPacScript(
744 ProxyResolverScriptData::FromUTF8("pac script bytes"),
745 set_script_callback.callback());
746 EXPECT_EQ(ERR_IO_PENDING, rv);
747 EXPECT_EQ(OK, set_script_callback.WaitForResult());
748 // One thread has been provisioned (i.e. one ProxyResolver was created).
749 ASSERT_EQ(1u, factory->resolvers().size());
750 EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
751 factory->resolvers()[0]->last_script_data()->utf16());
753 const int kNumRequests = 4;
754 TestCompletionCallback callback[kNumRequests];
755 ProxyInfo results[kNumRequests];
756 ProxyResolver::RequestHandle request[kNumRequests];
758 // Start a request that will block the first thread.
760 factory->resolvers()[0]->Block();
762 rv = resolver.GetProxyForURL(
763 GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
764 BoundNetLog());
766 EXPECT_EQ(ERR_IO_PENDING, rv);
767 factory->resolvers()[0]->WaitUntilBlocked();
769 // Start 3 more requests -- they should all be serviced by thread #2
770 // since thread #1 is blocked.
772 for (int i = 1; i < kNumRequests; ++i) {
773 rv = resolver.GetProxyForURL(
774 GURL(base::StringPrintf("http://request%d", i)),
775 &results[i], callback[i].callback(), &request[i], BoundNetLog());
776 EXPECT_EQ(ERR_IO_PENDING, rv);
779 // Wait for the three requests to complete (they should complete in FIFO
780 // order).
781 for (int i = 1; i < kNumRequests; ++i) {
782 EXPECT_EQ(i - 1, callback[i].WaitForResult());
785 // Unblock the first thread.
786 factory->resolvers()[0]->Unblock();
787 EXPECT_EQ(0, callback[0].WaitForResult());
789 // All in all, the first thread should have seen just 1 request. And the
790 // second thread 3 requests.
791 ASSERT_EQ(2u, factory->resolvers().size());
792 EXPECT_EQ(1, factory->resolvers()[0]->request_count());
793 EXPECT_EQ(3, factory->resolvers()[1]->request_count());
796 } // namespace
798 } // namespace net