- add sources.
[platform/framework/web/crosswalk.git] / src / net / proxy / multi_threaded_proxy_resolver_unittest.cc
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.
4
5 #include "net/proxy/multi_threaded_proxy_resolver.h"
6
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"
21
22 namespace net {
23
24 namespace {
25
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_(base::MessageLoop::current()),
34         request_count_(0),
35         purge_count_(0) {}
36
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_);
45
46     CheckIsOnWorkerThread();
47
48     EXPECT_TRUE(callback.is_null());
49     EXPECT_TRUE(request == NULL);
50
51     // Write something into |net_log| (doesn't really have any meaning.)
52     net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT);
53
54     results->UseNamedProxy(query_url.host());
55
56     // Return a success code which represents the request's order.
57     return request_count_++;
58   }
59
60   virtual void CancelRequest(RequestHandle request) OVERRIDE {
61     NOTREACHED();
62   }
63
64   virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
65     NOTREACHED();
66     return LOAD_STATE_IDLE;
67   }
68
69   virtual void CancelSetPacScript() OVERRIDE {
70     NOTREACHED();
71   }
72
73   virtual int SetPacScript(
74       const scoped_refptr<ProxyResolverScriptData>& script_data,
75       const CompletionCallback& callback) OVERRIDE {
76     CheckIsOnWorkerThread();
77     last_script_data_ = script_data;
78     return OK;
79   }
80
81   virtual void PurgeMemory() OVERRIDE {
82     CheckIsOnWorkerThread();
83     ++purge_count_;
84   }
85
86   int purge_count() const { return purge_count_; }
87   int request_count() const { return request_count_; }
88
89   const ProxyResolverScriptData* last_script_data() const {
90     return last_script_data_.get();
91   }
92
93   void SetResolveLatency(base::TimeDelta latency) {
94     resolve_latency_ = latency;
95   }
96
97  private:
98   void CheckIsOnWorkerThread() {
99     // We should be running on the worker thread -- while we don't know the
100     // message loop of MultiThreadedProxyResolver's worker thread, we do
101     // know that it is going to be distinct from the loop running the
102     // test, so at least make sure it isn't the main loop.
103     EXPECT_NE(base::MessageLoop::current(), wrong_loop_);
104   }
105
106   base::MessageLoop* wrong_loop_;
107   int request_count_;
108   int purge_count_;
109   scoped_refptr<ProxyResolverScriptData> last_script_data_;
110   base::TimeDelta resolve_latency_;
111 };
112
113
114 // A mock synchronous ProxyResolver which can be set to block upon reaching
115 // GetProxyForURL().
116 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
117 //               otherwise there will be a race on |should_block_| since it is
118 //               read without any synchronization.
119 class BlockableProxyResolver : public MockProxyResolver {
120  public:
121   BlockableProxyResolver()
122       : should_block_(false),
123         unblocked_(true, true),
124         blocked_(true, false) {
125   }
126
127   void Block() {
128     should_block_ = true;
129     unblocked_.Reset();
130   }
131
132   void Unblock() {
133     should_block_ = false;
134     blocked_.Reset();
135     unblocked_.Signal();
136   }
137
138   void WaitUntilBlocked() {
139     blocked_.Wait();
140   }
141
142   virtual int GetProxyForURL(const GURL& query_url,
143                              ProxyInfo* results,
144                              const CompletionCallback& callback,
145                              RequestHandle* request,
146                              const BoundNetLog& net_log) OVERRIDE {
147     if (should_block_) {
148       blocked_.Signal();
149       unblocked_.Wait();
150     }
151
152     return MockProxyResolver::GetProxyForURL(
153         query_url, results, callback, request, net_log);
154   }
155
156  private:
157   bool should_block_;
158   base::WaitableEvent unblocked_;
159   base::WaitableEvent blocked_;
160 };
161
162 // ForwardingProxyResolver forwards all requests to |impl|.
163 class ForwardingProxyResolver : public ProxyResolver {
164  public:
165   explicit ForwardingProxyResolver(ProxyResolver* impl)
166       : ProxyResolver(impl->expects_pac_bytes()),
167         impl_(impl) {}
168
169   virtual int GetProxyForURL(const GURL& query_url,
170                              ProxyInfo* results,
171                              const CompletionCallback& callback,
172                              RequestHandle* request,
173                              const BoundNetLog& net_log) OVERRIDE {
174     return impl_->GetProxyForURL(
175         query_url, results, callback, request, net_log);
176   }
177
178   virtual void CancelRequest(RequestHandle request) OVERRIDE {
179     impl_->CancelRequest(request);
180   }
181
182   virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
183     NOTREACHED();
184     return LOAD_STATE_IDLE;
185   }
186
187   virtual void CancelSetPacScript() OVERRIDE {
188     impl_->CancelSetPacScript();
189   }
190
191   virtual int SetPacScript(
192       const scoped_refptr<ProxyResolverScriptData>& script_data,
193       const CompletionCallback& callback) OVERRIDE {
194     return impl_->SetPacScript(script_data, callback);
195   }
196
197   virtual void PurgeMemory() OVERRIDE {
198     impl_->PurgeMemory();
199   }
200
201  private:
202   ProxyResolver* impl_;
203 };
204
205 // This factory returns ProxyResolvers that forward all requests to
206 // |resolver|.
207 class ForwardingProxyResolverFactory : public ProxyResolverFactory {
208  public:
209   explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
210       : ProxyResolverFactory(resolver->expects_pac_bytes()),
211         resolver_(resolver) {}
212
213   virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
214     return new ForwardingProxyResolver(resolver_);
215   }
216
217  private:
218   ProxyResolver* resolver_;
219 };
220
221 // This factory returns new instances of BlockableProxyResolver.
222 class BlockableProxyResolverFactory : public ProxyResolverFactory {
223  public:
224   BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
225
226   virtual ~BlockableProxyResolverFactory() {
227     STLDeleteElements(&resolvers_);
228   }
229
230   virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
231     BlockableProxyResolver* resolver = new BlockableProxyResolver;
232     resolvers_.push_back(resolver);
233     return new ForwardingProxyResolver(resolver);
234   }
235
236   std::vector<BlockableProxyResolver*> resolvers() {
237     return resolvers_;
238   }
239
240  private:
241   std::vector<BlockableProxyResolver*> resolvers_;
242 };
243
244 TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
245   const size_t kNumThreads = 1u;
246   scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
247   MultiThreadedProxyResolver resolver(
248       new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
249
250   int rv;
251
252   EXPECT_TRUE(resolver.expects_pac_bytes());
253
254   // Call SetPacScriptByData() -- verify that it reaches the synchronous
255   // resolver.
256   TestCompletionCallback set_script_callback;
257   rv = resolver.SetPacScript(
258       ProxyResolverScriptData::FromUTF8("pac script bytes"),
259       set_script_callback.callback());
260   EXPECT_EQ(ERR_IO_PENDING, rv);
261   EXPECT_EQ(OK, set_script_callback.WaitForResult());
262   EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
263             mock->last_script_data()->utf16());
264
265   // Start request 0.
266   TestCompletionCallback callback0;
267   CapturingBoundNetLog log0;
268   ProxyInfo results0;
269   rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
270                                callback0.callback(), NULL, log0.bound());
271   EXPECT_EQ(ERR_IO_PENDING, rv);
272
273   // Wait for request 0 to finish.
274   rv = callback0.WaitForResult();
275   EXPECT_EQ(0, rv);
276   EXPECT_EQ("PROXY request0:80", results0.ToPacString());
277
278   // The mock proxy resolver should have written 1 log entry. And
279   // on completion, this should have been copied into |log0|.
280   // We also have 1 log entry that was emitted by the
281   // MultiThreadedProxyResolver.
282   CapturingNetLog::CapturedEntryList entries0;
283   log0.GetEntries(&entries0);
284
285   ASSERT_EQ(2u, entries0.size());
286   EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
287
288   // Start 3 more requests (request1 to request3).
289
290   TestCompletionCallback callback1;
291   ProxyInfo results1;
292   rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
293                                callback1.callback(), NULL, BoundNetLog());
294   EXPECT_EQ(ERR_IO_PENDING, rv);
295
296   TestCompletionCallback callback2;
297   ProxyInfo results2;
298   rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
299                                callback2.callback(), NULL, BoundNetLog());
300   EXPECT_EQ(ERR_IO_PENDING, rv);
301
302   TestCompletionCallback callback3;
303   ProxyInfo results3;
304   rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
305                                callback3.callback(), NULL, BoundNetLog());
306   EXPECT_EQ(ERR_IO_PENDING, rv);
307
308   // Wait for the requests to finish (they must finish in the order they were
309   // started, which is what we check for from their magic return value)
310
311   rv = callback1.WaitForResult();
312   EXPECT_EQ(1, rv);
313   EXPECT_EQ("PROXY request1:80", results1.ToPacString());
314
315   rv = callback2.WaitForResult();
316   EXPECT_EQ(2, rv);
317   EXPECT_EQ("PROXY request2:80", results2.ToPacString());
318
319   rv = callback3.WaitForResult();
320   EXPECT_EQ(3, rv);
321   EXPECT_EQ("PROXY request3:80", results3.ToPacString());
322
323   // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the
324   // right thread.
325   EXPECT_EQ(0, mock->purge_count());
326   resolver.PurgeMemory();
327   // There is no way to get a callback directly when PurgeMemory() completes, so
328   // we queue up a dummy request after the PurgeMemory() call and wait until it
329   // finishes to ensure PurgeMemory() has had a chance to run.
330   TestCompletionCallback dummy_callback;
331   rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"),
332                              dummy_callback.callback());
333   EXPECT_EQ(OK, dummy_callback.WaitForResult());
334   EXPECT_EQ(1, mock->purge_count());
335 }
336
337 // Tests that the NetLog is updated to include the time the request was waiting
338 // to be scheduled to a thread.
339 TEST(MultiThreadedProxyResolverTest,
340      SingleThread_UpdatesNetLogWithThreadWait) {
341   const size_t kNumThreads = 1u;
342   scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
343   MultiThreadedProxyResolver resolver(
344       new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
345
346   int rv;
347
348   // Initialize the resolver.
349   TestCompletionCallback init_callback;
350   rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
351                              init_callback.callback());
352   EXPECT_EQ(OK, init_callback.WaitForResult());
353
354   // Block the proxy resolver, so no request can complete.
355   mock->Block();
356
357   // Start request 0.
358   ProxyResolver::RequestHandle request0;
359   TestCompletionCallback callback0;
360   ProxyInfo results0;
361   CapturingBoundNetLog log0;
362   rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
363                                callback0.callback(), &request0, log0.bound());
364   EXPECT_EQ(ERR_IO_PENDING, rv);
365
366   // Start 2 more requests (request1 and request2).
367
368   TestCompletionCallback callback1;
369   ProxyInfo results1;
370   CapturingBoundNetLog log1;
371   rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
372                                callback1.callback(), NULL, log1.bound());
373   EXPECT_EQ(ERR_IO_PENDING, rv);
374
375   ProxyResolver::RequestHandle request2;
376   TestCompletionCallback callback2;
377   ProxyInfo results2;
378   CapturingBoundNetLog log2;
379   rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
380                                callback2.callback(), &request2, log2.bound());
381   EXPECT_EQ(ERR_IO_PENDING, rv);
382
383   // Unblock the worker thread so the requests can continue running.
384   mock->WaitUntilBlocked();
385   mock->Unblock();
386
387   // Check that request 0 completed as expected.
388   // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
389   // 1 entry from the mock proxy resolver.
390   EXPECT_EQ(0, callback0.WaitForResult());
391   EXPECT_EQ("PROXY request0:80", results0.ToPacString());
392
393   CapturingNetLog::CapturedEntryList entries0;
394   log0.GetEntries(&entries0);
395
396   ASSERT_EQ(2u, entries0.size());
397   EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
398             entries0[0].type);
399
400   // Check that request 1 completed as expected.
401   EXPECT_EQ(1, callback1.WaitForResult());
402   EXPECT_EQ("PROXY request1:80", results1.ToPacString());
403
404   CapturingNetLog::CapturedEntryList entries1;
405   log1.GetEntries(&entries1);
406
407   ASSERT_EQ(4u, entries1.size());
408   EXPECT_TRUE(LogContainsBeginEvent(
409       entries1, 0,
410       NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
411   EXPECT_TRUE(LogContainsEndEvent(
412       entries1, 1,
413       NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
414
415   // Check that request 2 completed as expected.
416   EXPECT_EQ(2, callback2.WaitForResult());
417   EXPECT_EQ("PROXY request2:80", results2.ToPacString());
418
419   CapturingNetLog::CapturedEntryList entries2;
420   log2.GetEntries(&entries2);
421
422   ASSERT_EQ(4u, entries2.size());
423   EXPECT_TRUE(LogContainsBeginEvent(
424       entries2, 0,
425       NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
426   EXPECT_TRUE(LogContainsEndEvent(
427       entries2, 1,
428       NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
429 }
430
431 // Cancel a request which is in progress, and then cancel a request which
432 // is pending.
433 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
434   const size_t kNumThreads = 1u;
435   scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
436   MultiThreadedProxyResolver resolver(
437       new ForwardingProxyResolverFactory(mock.get()),
438                                       kNumThreads);
439
440   int rv;
441
442   // Initialize the resolver.
443   TestCompletionCallback init_callback;
444   rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
445                              init_callback.callback());
446   EXPECT_EQ(OK, init_callback.WaitForResult());
447
448   // Block the proxy resolver, so no request can complete.
449   mock->Block();
450
451   // Start request 0.
452   ProxyResolver::RequestHandle request0;
453   TestCompletionCallback callback0;
454   ProxyInfo results0;
455   rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
456                                callback0.callback(), &request0, BoundNetLog());
457   EXPECT_EQ(ERR_IO_PENDING, rv);
458
459   // Wait until requests 0 reaches the worker thread.
460   mock->WaitUntilBlocked();
461
462   // Start 3 more requests (request1 : request3).
463
464   TestCompletionCallback callback1;
465   ProxyInfo results1;
466   rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
467                                callback1.callback(), NULL, BoundNetLog());
468   EXPECT_EQ(ERR_IO_PENDING, rv);
469
470   ProxyResolver::RequestHandle request2;
471   TestCompletionCallback callback2;
472   ProxyInfo results2;
473   rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
474                                callback2.callback(), &request2, BoundNetLog());
475   EXPECT_EQ(ERR_IO_PENDING, rv);
476
477   TestCompletionCallback callback3;
478   ProxyInfo results3;
479   rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
480                                callback3.callback(), NULL, BoundNetLog());
481   EXPECT_EQ(ERR_IO_PENDING, rv);
482
483   // Cancel request0 (inprogress) and request2 (pending).
484   resolver.CancelRequest(request0);
485   resolver.CancelRequest(request2);
486
487   // Unblock the worker thread so the requests can continue running.
488   mock->Unblock();
489
490   // Wait for requests 1 and 3 to finish.
491
492   rv = callback1.WaitForResult();
493   EXPECT_EQ(1, rv);
494   EXPECT_EQ("PROXY request1:80", results1.ToPacString());
495
496   rv = callback3.WaitForResult();
497   // Note that since request2 was cancelled before reaching the resolver,
498   // the request count is 2 and not 3 here.
499   EXPECT_EQ(2, rv);
500   EXPECT_EQ("PROXY request3:80", results3.ToPacString());
501
502   // Requests 0 and 2 which were cancelled, hence their completion callbacks
503   // were never summoned.
504   EXPECT_FALSE(callback0.have_result());
505   EXPECT_FALSE(callback2.have_result());
506 }
507
508 // Test that deleting MultiThreadedProxyResolver while requests are
509 // outstanding cancels them (and doesn't leak anything).
510 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
511   const size_t kNumThreads = 1u;
512   scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
513   scoped_ptr<MultiThreadedProxyResolver> resolver(
514       new MultiThreadedProxyResolver(
515           new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
516
517   int rv;
518
519   // Initialize the resolver.
520   TestCompletionCallback init_callback;
521   rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
522                               init_callback.callback());
523   EXPECT_EQ(OK, init_callback.WaitForResult());
524
525   // Block the proxy resolver, so no request can complete.
526   mock->Block();
527
528   // Start 3 requests.
529
530   TestCompletionCallback callback0;
531   ProxyInfo results0;
532   rv = resolver->GetProxyForURL(GURL("http://request0"), &results0,
533                                 callback0.callback(), NULL, BoundNetLog());
534   EXPECT_EQ(ERR_IO_PENDING, rv);
535
536   TestCompletionCallback callback1;
537   ProxyInfo results1;
538   rv = resolver->GetProxyForURL(GURL("http://request1"), &results1,
539                                 callback1.callback(), NULL, BoundNetLog());
540   EXPECT_EQ(ERR_IO_PENDING, rv);
541
542   TestCompletionCallback callback2;
543   ProxyInfo results2;
544   rv = resolver->GetProxyForURL(GURL("http://request2"), &results2,
545                                 callback2.callback(), NULL, BoundNetLog());
546   EXPECT_EQ(ERR_IO_PENDING, rv);
547
548   // Wait until request 0 reaches the worker thread.
549   mock->WaitUntilBlocked();
550
551   // Add some latency, to improve the chance that when
552   // MultiThreadedProxyResolver is deleted below we are still running inside
553   // of the worker thread. The test will pass regardless, so this race doesn't
554   // cause flakiness. However the destruction during execution is a more
555   // interesting case to test.
556   mock->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
557
558   // Unblock the worker thread and delete the underlying
559   // MultiThreadedProxyResolver immediately.
560   mock->Unblock();
561   resolver.reset();
562
563   // Give any posted tasks a chance to run (in case there is badness).
564   base::MessageLoop::current()->RunUntilIdle();
565
566   // Check that none of the outstanding requests were completed.
567   EXPECT_FALSE(callback0.have_result());
568   EXPECT_FALSE(callback1.have_result());
569   EXPECT_FALSE(callback2.have_result());
570 }
571
572 // Cancel an outstanding call to SetPacScriptByData().
573 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
574   const size_t kNumThreads = 1u;
575   scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
576   MultiThreadedProxyResolver resolver(
577       new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
578
579   int rv;
580
581   TestCompletionCallback set_pac_script_callback;
582   rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
583                              set_pac_script_callback.callback());
584   EXPECT_EQ(ERR_IO_PENDING, rv);
585
586   // Cancel the SetPacScriptByData request.
587   resolver.CancelSetPacScript();
588
589   // Start another SetPacScript request
590   TestCompletionCallback set_pac_script_callback2;
591   rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
592                              set_pac_script_callback2.callback());
593   EXPECT_EQ(ERR_IO_PENDING, rv);
594
595   // Wait for the initialization to complete.
596
597   rv = set_pac_script_callback2.WaitForResult();
598   EXPECT_EQ(0, rv);
599   EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
600
601   // The first SetPacScript callback should never have been completed.
602   EXPECT_FALSE(set_pac_script_callback.have_result());
603 }
604
605 // Tests setting the PAC script once, lazily creating new threads, and
606 // cancelling requests.
607 TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
608   const size_t kNumThreads = 3u;
609   BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
610   MultiThreadedProxyResolver resolver(factory, kNumThreads);
611
612   int rv;
613
614   EXPECT_TRUE(resolver.expects_pac_bytes());
615
616   // Call SetPacScriptByData() -- verify that it reaches the synchronous
617   // resolver.
618   TestCompletionCallback set_script_callback;
619   rv = resolver.SetPacScript(
620       ProxyResolverScriptData::FromUTF8("pac script bytes"),
621       set_script_callback.callback());
622   EXPECT_EQ(ERR_IO_PENDING, rv);
623   EXPECT_EQ(OK, set_script_callback.WaitForResult());
624   // One thread has been provisioned (i.e. one ProxyResolver was created).
625   ASSERT_EQ(1u, factory->resolvers().size());
626   EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
627             factory->resolvers()[0]->last_script_data()->utf16());
628
629   const int kNumRequests = 9;
630   TestCompletionCallback callback[kNumRequests];
631   ProxyInfo results[kNumRequests];
632   ProxyResolver::RequestHandle request[kNumRequests];
633
634   // Start request 0 -- this should run on thread 0 as there is nothing else
635   // going on right now.
636   rv = resolver.GetProxyForURL(
637       GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
638       BoundNetLog());
639   EXPECT_EQ(ERR_IO_PENDING, rv);
640
641   // Wait for request 0 to finish.
642   rv = callback[0].WaitForResult();
643   EXPECT_EQ(0, rv);
644   EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
645   ASSERT_EQ(1u, factory->resolvers().size());
646   EXPECT_EQ(1, factory->resolvers()[0]->request_count());
647
648   base::MessageLoop::current()->RunUntilIdle();
649
650   // We now start 8 requests in parallel -- this will cause the maximum of
651   // three threads to be provisioned (an additional two from what we already
652   // have).
653
654   for (int i = 1; i < kNumRequests; ++i) {
655     rv = resolver.GetProxyForURL(
656         GURL(base::StringPrintf("http://request%d", i)), &results[i],
657         callback[i].callback(), &request[i], BoundNetLog());
658     EXPECT_EQ(ERR_IO_PENDING, rv);
659   }
660
661   // We should now have a total of 3 threads, each with its own ProxyResolver
662   // that will get initialized with the same data. (We check this later since
663   // the assignment happens on the worker threads and may not have occurred
664   // yet.)
665   ASSERT_EQ(3u, factory->resolvers().size());
666
667   // Cancel 3 of the 8 oustanding requests.
668   resolver.CancelRequest(request[1]);
669   resolver.CancelRequest(request[3]);
670   resolver.CancelRequest(request[6]);
671
672   // Wait for the remaining requests to complete.
673   int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
674   for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
675     int request_index = kNonCancelledRequests[i];
676     EXPECT_GE(callback[request_index].WaitForResult(), 0);
677   }
678
679   // Check that the cancelled requests never invoked their callback.
680   EXPECT_FALSE(callback[1].have_result());
681   EXPECT_FALSE(callback[3].have_result());
682   EXPECT_FALSE(callback[6].have_result());
683
684   // We call SetPacScript again, solely to stop the current worker threads.
685   // (That way we can test to see the values observed by the synchronous
686   // resolvers in a non-racy manner).
687   TestCompletionCallback set_script_callback2;
688   rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
689                              set_script_callback2.callback());
690   EXPECT_EQ(ERR_IO_PENDING, rv);
691   EXPECT_EQ(OK, set_script_callback2.WaitForResult());
692   ASSERT_EQ(4u, factory->resolvers().size());
693
694   for (int i = 0; i < 3; ++i) {
695     EXPECT_EQ(
696         ASCIIToUTF16("pac script bytes"),
697         factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
698   }
699
700   EXPECT_EQ(ASCIIToUTF16("xyz"),
701             factory->resolvers()[3]->last_script_data()->utf16());
702
703   // We don't know the exact ordering that requests ran on threads with,
704   // but we do know the total count that should have reached the threads.
705   // 8 total were submitted, and three were cancelled. Of the three that
706   // were cancelled, one of them (request 1) was cancelled after it had
707   // already been posted to the worker thread. So the resolvers will
708   // have seen 6 total (and 1 from the run prior).
709   ASSERT_EQ(4u, factory->resolvers().size());
710   int total_count = 0;
711   for (int i = 0; i < 3; ++i) {
712     total_count += factory->resolvers()[i]->request_count();
713   }
714   EXPECT_EQ(7, total_count);
715 }
716
717 // Tests using two threads. The first request hangs the first thread. Checks
718 // that other requests are able to complete while this first request remains
719 // stalled.
720 TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
721   const size_t kNumThreads = 2u;
722   BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
723   MultiThreadedProxyResolver resolver(factory, kNumThreads);
724
725   int rv;
726
727   EXPECT_TRUE(resolver.expects_pac_bytes());
728
729   // Initialize the resolver.
730   TestCompletionCallback set_script_callback;
731   rv = resolver.SetPacScript(
732       ProxyResolverScriptData::FromUTF8("pac script bytes"),
733       set_script_callback.callback());
734   EXPECT_EQ(ERR_IO_PENDING, rv);
735   EXPECT_EQ(OK, set_script_callback.WaitForResult());
736   // One thread has been provisioned (i.e. one ProxyResolver was created).
737   ASSERT_EQ(1u, factory->resolvers().size());
738   EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
739             factory->resolvers()[0]->last_script_data()->utf16());
740
741   const int kNumRequests = 4;
742   TestCompletionCallback callback[kNumRequests];
743   ProxyInfo results[kNumRequests];
744   ProxyResolver::RequestHandle request[kNumRequests];
745
746   // Start a request that will block the first thread.
747
748   factory->resolvers()[0]->Block();
749
750   rv = resolver.GetProxyForURL(
751       GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
752       BoundNetLog());
753
754   EXPECT_EQ(ERR_IO_PENDING, rv);
755   factory->resolvers()[0]->WaitUntilBlocked();
756
757   // Start 3 more requests -- they should all be serviced by thread #2
758   // since thread #1 is blocked.
759
760   for (int i = 1; i < kNumRequests; ++i) {
761     rv = resolver.GetProxyForURL(
762         GURL(base::StringPrintf("http://request%d", i)),
763         &results[i], callback[i].callback(), &request[i], BoundNetLog());
764     EXPECT_EQ(ERR_IO_PENDING, rv);
765   }
766
767   // Wait for the three requests to complete (they should complete in FIFO
768   // order).
769   for (int i = 1; i < kNumRequests; ++i) {
770     EXPECT_EQ(i - 1, callback[i].WaitForResult());
771   }
772
773   // Unblock the first thread.
774   factory->resolvers()[0]->Unblock();
775   EXPECT_EQ(0, callback[0].WaitForResult());
776
777   // All in all, the first thread should have seen just 1 request. And the
778   // second thread 3 requests.
779   ASSERT_EQ(2u, factory->resolvers().size());
780   EXPECT_EQ(1, factory->resolvers()[0]->request_count());
781   EXPECT_EQ(3, factory->resolvers()[1]->request_count());
782 }
783
784 }  // namespace
785
786 }  // namespace net