Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / net / proxy / multi_threaded_proxy_resolver_unittest.cc
blob9d8d728ae7098f31f1b9e8e17419329f1c3e85b6
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"
22 #include "url/gurl.h"
24 using base::ASCIIToUTF16;
26 namespace net {
28 namespace {
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 {
34 public:
35 MockProxyResolver()
36 : ProxyResolver(true /*expects_pac_bytes*/),
37 wrong_loop_(base::MessageLoop::current()),
38 request_count_(0) {}
40 // ProxyResolver implementation.
41 int GetProxyForURL(const GURL& query_url,
42 ProxyInfo* results,
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 {
66 NOTREACHED();
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;
76 return OK;
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;
89 private:
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_;
99 int request_count_;
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
106 // GetProxyForURL().
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 {
111 public:
112 BlockableProxyResolver()
113 : should_block_(false),
114 unblocked_(true, true),
115 blocked_(true, false) {
118 void Block() {
119 should_block_ = true;
120 unblocked_.Reset();
123 void Unblock() {
124 should_block_ = false;
125 blocked_.Reset();
126 unblocked_.Signal();
129 void WaitUntilBlocked() {
130 blocked_.Wait();
133 int GetProxyForURL(const GURL& query_url,
134 ProxyInfo* results,
135 const CompletionCallback& callback,
136 RequestHandle* request,
137 const BoundNetLog& net_log) override {
138 if (should_block_) {
139 blocked_.Signal();
140 unblocked_.Wait();
143 return MockProxyResolver::GetProxyForURL(
144 query_url, results, callback, request, net_log);
147 private:
148 bool should_block_;
149 base::WaitableEvent unblocked_;
150 base::WaitableEvent blocked_;
153 // This factory returns new instances of BlockableProxyResolver.
154 class BlockableProxyResolverFactory : public ProxyResolverFactory {
155 public:
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() {
167 return resolvers_;
170 private:
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);
180 int rv;
182 EXPECT_TRUE(resolver.expects_pac_bytes());
184 // Call SetPacScriptByData() -- verify that it reaches the synchronous
185 // resolver.
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());
195 // Start request 0.
196 TestCompletionCallback callback0;
197 CapturingBoundNetLog log0;
198 ProxyInfo results0;
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();
205 EXPECT_EQ(0, rv);
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;
221 ProxyInfo results1;
222 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
223 callback1.callback(), NULL, BoundNetLog());
224 EXPECT_EQ(ERR_IO_PENDING, rv);
226 TestCompletionCallback callback2;
227 ProxyInfo results2;
228 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
229 callback2.callback(), NULL, BoundNetLog());
230 EXPECT_EQ(ERR_IO_PENDING, rv);
232 TestCompletionCallback callback3;
233 ProxyInfo results3;
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();
242 EXPECT_EQ(1, rv);
243 EXPECT_EQ("PROXY request1:80", results1.ToPacString());
245 rv = callback2.WaitForResult();
246 EXPECT_EQ(2, rv);
247 EXPECT_EQ("PROXY request2:80", results2.ToPacString());
249 rv = callback3.WaitForResult();
250 EXPECT_EQ(3, rv);
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);
263 int rv;
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.
272 mock->Block();
274 // Start request 0.
275 ProxyResolver::RequestHandle request0;
276 TestCompletionCallback callback0;
277 ProxyInfo results0;
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;
286 ProxyInfo results1;
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;
294 ProxyInfo results2;
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();
302 mock->Unblock();
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,
315 entries0[0].type);
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(
326 entries1, 0,
327 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
328 EXPECT_TRUE(LogContainsEndEvent(
329 entries1, 1,
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(
341 entries2, 0,
342 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
343 EXPECT_TRUE(LogContainsEndEvent(
344 entries2, 1,
345 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
348 // Cancel a request which is in progress, and then cancel a request which
349 // is pending.
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()),
355 kNumThreads);
357 int rv;
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.
366 mock->Block();
368 // Start request 0.
369 ProxyResolver::RequestHandle request0;
370 TestCompletionCallback callback0;
371 ProxyInfo results0;
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;
382 ProxyInfo results1;
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;
389 ProxyInfo results2;
390 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
391 callback2.callback(), &request2, BoundNetLog());
392 EXPECT_EQ(ERR_IO_PENDING, rv);
394 TestCompletionCallback callback3;
395 ProxyInfo results3;
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.
405 mock->Unblock();
407 // Wait for requests 1 and 3 to finish.
409 rv = callback1.WaitForResult();
410 EXPECT_EQ(1, rv);
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.
416 EXPECT_EQ(2, rv);
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));
434 int rv;
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.
443 mock->Block();
445 // Start 3 requests.
447 TestCompletionCallback callback0;
448 ProxyInfo results0;
449 rv = resolver->GetProxyForURL(GURL("http://request0"), &results0,
450 callback0.callback(), NULL, BoundNetLog());
451 EXPECT_EQ(ERR_IO_PENDING, rv);
453 TestCompletionCallback callback1;
454 ProxyInfo results1;
455 rv = resolver->GetProxyForURL(GURL("http://request1"), &results1,
456 callback1.callback(), NULL, BoundNetLog());
457 EXPECT_EQ(ERR_IO_PENDING, rv);
459 TestCompletionCallback callback2;
460 ProxyInfo results2;
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.
477 mock->Unblock();
478 resolver.reset();
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);
496 int rv;
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();
515 EXPECT_EQ(0, rv);
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);
529 int rv;
531 EXPECT_TRUE(resolver.expects_pac_bytes());
533 // Call SetPacScriptByData() -- verify that it reaches the synchronous
534 // resolver.
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],
555 BoundNetLog());
556 EXPECT_EQ(ERR_IO_PENDING, rv);
558 // Wait for request 0 to finish.
559 rv = callback[0].WaitForResult();
560 EXPECT_EQ(0, rv);
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
569 // have).
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
581 // yet.)
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) {
612 EXPECT_EQ(
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());
627 int total_count = 0;
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
636 // stalled.
637 TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
638 const size_t kNumThreads = 2u;
639 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
640 MultiThreadedProxyResolver resolver(factory, kNumThreads);
642 int rv;
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],
669 BoundNetLog());
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
685 // order).
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());
701 } // namespace
703 } // namespace net