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