| Index: net/proxy/multi_threaded_proxy_resolver_unittest.cc
|
| ===================================================================
|
| --- net/proxy/multi_threaded_proxy_resolver_unittest.cc (revision 51914)
|
| +++ net/proxy/multi_threaded_proxy_resolver_unittest.cc (working copy)
|
| @@ -2,6 +2,9 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include "net/proxy/multi_threaded_proxy_resolver.h"
|
| +
|
| +#include "base/stl_util-inl.h"
|
| #include "base/string_util.h"
|
| #include "base/waitable_event.h"
|
| #include "googleurl/src/gurl.h"
|
| @@ -10,14 +13,14 @@
|
| #include "net/base/net_errors.h"
|
| #include "net/base/test_completion_callback.h"
|
| #include "net/proxy/proxy_info.h"
|
| -#include "net/proxy/single_threaded_proxy_resolver.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace net {
|
| +
|
| namespace {
|
|
|
| // A synchronous mock ProxyResolver implementation, which can be used in
|
| -// conjunction with SingleThreadedProxyResolver.
|
| +// conjunction with MultiThreadedProxyResolver.
|
| // - returns a single-item proxy list with the query's host.
|
| class MockProxyResolver : public ProxyResolver {
|
| public:
|
| @@ -69,6 +72,7 @@
|
| }
|
|
|
| int purge_count() const { return purge_count_; }
|
| + int request_count() const { return request_count_; }
|
|
|
| const string16& last_pac_script() const { return last_pac_script_; }
|
|
|
| @@ -79,7 +83,7 @@
|
| private:
|
| void CheckIsOnWorkerThread() {
|
| // We should be running on the worker thread -- while we don't know the
|
| - // message loop of SingleThreadedProxyResolver's worker thread, we do
|
| + // message loop of MultiThreadedProxyResolver's worker thread, we do
|
| // know that it is going to be distinct from the loop running the
|
| // test, so at least make sure it isn't the main loop.
|
| EXPECT_NE(MessageLoop::current(), wrong_loop_);
|
| @@ -141,10 +145,88 @@
|
| base::WaitableEvent blocked_;
|
| };
|
|
|
| -TEST(SingleThreadedProxyResolverTest, Basic) {
|
| - MockProxyResolver* mock = new MockProxyResolver;
|
| - SingleThreadedProxyResolver resolver(mock);
|
| +// ForwardingProxyResolver forwards all requests to |impl|.
|
| +class ForwardingProxyResolver : public ProxyResolver {
|
| + public:
|
| + explicit ForwardingProxyResolver(ProxyResolver* impl)
|
| + : ProxyResolver(impl->expects_pac_bytes()),
|
| + impl_(impl) {}
|
|
|
| + virtual int GetProxyForURL(const GURL& query_url,
|
| + ProxyInfo* results,
|
| + CompletionCallback* callback,
|
| + RequestHandle* request,
|
| + const BoundNetLog& net_log) {
|
| + return impl_->GetProxyForURL(
|
| + query_url, results, callback, request, net_log);
|
| + }
|
| +
|
| + virtual void CancelRequest(RequestHandle request) {
|
| + impl_->CancelRequest(request);
|
| + }
|
| +
|
| + virtual int SetPacScript(const GURL& pac_url,
|
| + const string16& script,
|
| + CompletionCallback* callback) {
|
| + if (impl_->expects_pac_bytes())
|
| + return impl_->SetPacScriptByData(script, callback);
|
| + else
|
| + return impl_->SetPacScriptByUrl(pac_url, callback);
|
| + }
|
| +
|
| + virtual void PurgeMemory() {
|
| + impl_->PurgeMemory();
|
| + }
|
| +
|
| + private:
|
| + ProxyResolver* impl_;
|
| +};
|
| +
|
| +// This factory returns ProxyResolvers that forward all requests to
|
| +// |resolver|.
|
| +class ForwardingProxyResolverFactory : public ProxyResolverFactory {
|
| + public:
|
| + explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
|
| + : ProxyResolverFactory(resolver->expects_pac_bytes()),
|
| + resolver_(resolver) {}
|
| +
|
| + virtual ProxyResolver* CreateProxyResolver() {
|
| + return new ForwardingProxyResolver(resolver_);
|
| + }
|
| +
|
| + private:
|
| + ProxyResolver* resolver_;
|
| +};
|
| +
|
| +// This factory returns new instances of BlockableProxyResolver.
|
| +class BlockableProxyResolverFactory : public ProxyResolverFactory {
|
| + public:
|
| + BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
|
| +
|
| + ~BlockableProxyResolverFactory() {
|
| + STLDeleteElements(&resolvers_);
|
| + }
|
| +
|
| + virtual ProxyResolver* CreateProxyResolver() {
|
| + BlockableProxyResolver* resolver = new BlockableProxyResolver;
|
| + resolvers_.push_back(resolver);
|
| + return new ForwardingProxyResolver(resolver);
|
| + }
|
| +
|
| + std::vector<BlockableProxyResolver*> resolvers() {
|
| + return resolvers_;
|
| + }
|
| +
|
| + private:
|
| + std::vector<BlockableProxyResolver*> resolvers_;
|
| +};
|
| +
|
| +TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
|
| + const size_t kNumThreads = 1u;
|
| + scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
|
| + MultiThreadedProxyResolver resolver(
|
| + new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
|
| +
|
| int rv;
|
|
|
| EXPECT_TRUE(resolver.expects_pac_bytes());
|
| @@ -173,7 +255,11 @@
|
|
|
| // The mock proxy resolver should have written 1 log entry. And
|
| // on completion, this should have been copied into |log0|.
|
| - EXPECT_EQ(1u, log0.entries().size());
|
| + // We also have 1 log entry that was emitted by the
|
| + // MultiThreadedProxyResolver.
|
| + ASSERT_EQ(2u, log0.entries().size());
|
| + EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
|
| + log0.entries()[0].type);
|
|
|
| // Start 3 more requests (request1 to request3).
|
|
|
| @@ -225,12 +311,20 @@
|
|
|
| // Tests that the NetLog is updated to include the time the request was waiting
|
| // to be scheduled to a thread.
|
| -TEST(SingleThreadedProxyResolverTest, UpdatesNetLogWithThreadWait) {
|
| - BlockableProxyResolver* mock = new BlockableProxyResolver;
|
| - SingleThreadedProxyResolver resolver(mock);
|
| +TEST(MultiThreadedProxyResolverTest,
|
| + SingleThread_UpdatesNetLogWithThreadWait) {
|
| + const size_t kNumThreads = 1u;
|
| + scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
|
| + MultiThreadedProxyResolver resolver(
|
| + new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
|
|
|
| int rv;
|
|
|
| + // Initialize the resolver.
|
| + TestCompletionCallback init_callback;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("foo"), &init_callback);
|
| + EXPECT_EQ(OK, init_callback.WaitForResult());
|
| +
|
| // Block the proxy resolver, so no request can complete.
|
| mock->Block();
|
|
|
| @@ -265,42 +359,53 @@
|
| mock->Unblock();
|
|
|
| // Check that request 0 completed as expected.
|
| - // The NetLog only has 1 entry (that came from the mock proxy resolver.)
|
| + // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
|
| + // 1 entry from the mock proxy resolver.
|
| EXPECT_EQ(0, callback0.WaitForResult());
|
| EXPECT_EQ("PROXY request0:80", results0.ToPacString());
|
| - ASSERT_EQ(1u, log0.entries().size());
|
| + ASSERT_EQ(2u, log0.entries().size());
|
| + EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
|
| + log0.entries()[0].type);
|
|
|
| // Check that request 1 completed as expected.
|
| EXPECT_EQ(1, callback1.WaitForResult());
|
| EXPECT_EQ("PROXY request1:80", results1.ToPacString());
|
| - ASSERT_EQ(3u, log1.entries().size());
|
| + ASSERT_EQ(4u, log1.entries().size());
|
| EXPECT_TRUE(LogContainsBeginEvent(
|
| log1.entries(), 0,
|
| - NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
|
| + NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
|
| EXPECT_TRUE(LogContainsEndEvent(
|
| log1.entries(), 1,
|
| - NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
|
| + NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
|
|
|
| // Check that request 2 completed as expected.
|
| EXPECT_EQ(2, callback2.WaitForResult());
|
| EXPECT_EQ("PROXY request2:80", results2.ToPacString());
|
| - ASSERT_EQ(3u, log2.entries().size());
|
| + ASSERT_EQ(4u, log2.entries().size());
|
| EXPECT_TRUE(LogContainsBeginEvent(
|
| log2.entries(), 0,
|
| - NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
|
| + NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
|
| EXPECT_TRUE(LogContainsEndEvent(
|
| log2.entries(), 1,
|
| - NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD));
|
| + NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
|
| }
|
|
|
| // Cancel a request which is in progress, and then cancel a request which
|
| // is pending.
|
| -TEST(SingleThreadedProxyResolverTest, CancelRequest) {
|
| - BlockableProxyResolver* mock = new BlockableProxyResolver;
|
| - SingleThreadedProxyResolver resolver(mock);
|
| +TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
|
| + const size_t kNumThreads = 1u;
|
| + scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
|
| + MultiThreadedProxyResolver resolver(
|
| + new ForwardingProxyResolverFactory(mock.get()),
|
| + kNumThreads);
|
|
|
| int rv;
|
|
|
| + // Initialize the resolver.
|
| + TestCompletionCallback init_callback;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("foo"), &init_callback);
|
| + EXPECT_EQ(OK, init_callback.WaitForResult());
|
| +
|
| // Block the proxy resolver, so no request can complete.
|
| mock->Block();
|
|
|
| @@ -361,15 +466,22 @@
|
| EXPECT_FALSE(callback2.have_result());
|
| }
|
|
|
| -// Test that deleting SingleThreadedProxyResolver while requests are
|
| +// Test that deleting MultiThreadedProxyResolver while requests are
|
| // outstanding cancels them (and doesn't leak anything).
|
| -TEST(SingleThreadedProxyResolverTest, CancelRequestByDeleting) {
|
| - BlockableProxyResolver* mock = new BlockableProxyResolver;
|
| - scoped_ptr<SingleThreadedProxyResolver> resolver(
|
| - new SingleThreadedProxyResolver(mock));
|
| +TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
|
| + const size_t kNumThreads = 1u;
|
| + scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
|
| + scoped_ptr<MultiThreadedProxyResolver> resolver(
|
| + new MultiThreadedProxyResolver(
|
| + new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
|
|
|
| int rv;
|
|
|
| + // Initialize the resolver.
|
| + TestCompletionCallback init_callback;
|
| + rv = resolver->SetPacScriptByData(ASCIIToUTF16("foo"), &init_callback);
|
| + EXPECT_EQ(OK, init_callback.WaitForResult());
|
| +
|
| // Block the proxy resolver, so no request can complete.
|
| mock->Block();
|
|
|
| @@ -397,14 +509,14 @@
|
| mock->WaitUntilBlocked();
|
|
|
| // Add some latency, to improve the chance that when
|
| - // SingleThreadedProxyResolver is deleted below we are still running inside
|
| + // MultiThreadedProxyResolver is deleted below we are still running inside
|
| // of the worker thread. The test will pass regardless, so this race doesn't
|
| // cause flakiness. However the destruction during execution is a more
|
| // interesting case to test.
|
| mock->SetResolveLatency(100);
|
|
|
| // Unblock the worker thread and delete the underlying
|
| - // SingleThreadedProxyResolver immediately.
|
| + // MultiThreadedProxyResolver immediately.
|
| mock->Unblock();
|
| resolver.reset();
|
|
|
| @@ -418,59 +530,213 @@
|
| }
|
|
|
| // Cancel an outstanding call to SetPacScriptByData().
|
| -TEST(SingleThreadedProxyResolverTest, CancelSetPacScript) {
|
| - BlockableProxyResolver* mock = new BlockableProxyResolver;
|
| - SingleThreadedProxyResolver resolver(mock);
|
| +TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
|
| + const size_t kNumThreads = 1u;
|
| + scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
|
| + MultiThreadedProxyResolver resolver(
|
| + new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
|
|
|
| int rv;
|
|
|
| - // Block the proxy resolver, so no request can complete.
|
| - mock->Block();
|
| + TestCompletionCallback set_pac_script_callback;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"),
|
| + &set_pac_script_callback);
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
|
|
| - // Start request 0.
|
| - ProxyResolver::RequestHandle request0;
|
| - TestCompletionCallback callback0;
|
| - ProxyInfo results0;
|
| + // Cancel the SetPacScriptByData request.
|
| + resolver.CancelSetPacScript();
|
| +
|
| + // Start another SetPacScript request
|
| + TestCompletionCallback set_pac_script_callback2;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("data2"),
|
| + &set_pac_script_callback2);
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + // Wait for the initialization to complete.
|
| +
|
| + rv = set_pac_script_callback2.WaitForResult();
|
| + EXPECT_EQ(0, rv);
|
| + EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_pac_script());
|
| +
|
| + // The first SetPacScript callback should never have been completed.
|
| + EXPECT_FALSE(set_pac_script_callback.have_result());
|
| +}
|
| +
|
| +// Tests setting the PAC script once, lazily creating new threads, and
|
| +// cancelling requests.
|
| +TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
|
| + const size_t kNumThreads = 3u;
|
| + BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
|
| + MultiThreadedProxyResolver resolver(factory, kNumThreads);
|
| +
|
| + int rv;
|
| +
|
| + EXPECT_TRUE(resolver.expects_pac_bytes());
|
| +
|
| + // Call SetPacScriptByData() -- verify that it reaches the synchronous
|
| + // resolver.
|
| + TestCompletionCallback set_script_callback;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"),
|
| + &set_script_callback);
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + EXPECT_EQ(OK, set_script_callback.WaitForResult());
|
| + // One thread has been provisioned (i.e. one ProxyResolver was created).
|
| + ASSERT_EQ(1u, factory->resolvers().size());
|
| + EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
|
| + factory->resolvers()[0]->last_pac_script());
|
| +
|
| + const int kNumRequests = 9;
|
| + TestCompletionCallback callback[kNumRequests];
|
| + ProxyInfo results[kNumRequests];
|
| + ProxyResolver::RequestHandle request[kNumRequests];
|
| +
|
| + // Start request 0 -- this should run on thread 0 as there is nothing else
|
| + // going on right now.
|
| rv = resolver.GetProxyForURL(
|
| - GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog());
|
| + GURL("http://request0"), &results[0], &callback[0], &request[0],
|
| + BoundNetLog());
|
| EXPECT_EQ(ERR_IO_PENDING, rv);
|
|
|
| - // Wait until requests 0 reaches the worker thread.
|
| - mock->WaitUntilBlocked();
|
| + // Wait for request 0 to finish.
|
| + rv = callback[0].WaitForResult();
|
| + EXPECT_EQ(0, rv);
|
| + EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
|
| + ASSERT_EQ(1u, factory->resolvers().size());
|
| + EXPECT_EQ(1, factory->resolvers()[0]->request_count());
|
|
|
| - TestCompletionCallback set_pac_script_callback;
|
| - rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"),
|
| - &set_pac_script_callback);
|
| + MessageLoop::current()->RunAllPending();
|
| +
|
| + // We now start 8 requests in parallel -- this will cause the maximum of
|
| + // three threads to be provisioned (an additional two from what we already
|
| + // have).
|
| +
|
| + for (int i = 1; i < kNumRequests; ++i) {
|
| + rv = resolver.GetProxyForURL(
|
| + GURL(StringPrintf("http://request%d", i)), &results[i], &callback[i],
|
| + &request[i], BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + }
|
| +
|
| + // We should now have a total of 3 threads, each with its own ProxyResolver
|
| + // that will get initialized with the same data. (We check this later since
|
| + // the assignment happens on the worker threads and may not have occurred
|
| + // yet.)
|
| + ASSERT_EQ(3u, factory->resolvers().size());
|
| +
|
| + // Cancel 3 of the 8 oustanding requests.
|
| + resolver.CancelRequest(request[1]);
|
| + resolver.CancelRequest(request[3]);
|
| + resolver.CancelRequest(request[6]);
|
| +
|
| + // Wait for the remaining requests to complete.
|
| + int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
|
| + for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
|
| + int request_index = kNonCancelledRequests[i];
|
| + EXPECT_GE(callback[request_index].WaitForResult(), 0);
|
| + }
|
| +
|
| + // Check that the cancelled requests never invoked their callback.
|
| + EXPECT_FALSE(callback[1].have_result());
|
| + EXPECT_FALSE(callback[3].have_result());
|
| + EXPECT_FALSE(callback[6].have_result());
|
| +
|
| + // We call SetPacScript again, solely to stop the current worker threads.
|
| + // (That way we can test to see the values observed by the synchronous
|
| + // resolvers in a non-racy manner).
|
| + TestCompletionCallback set_script_callback2;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("xyz"), &set_script_callback2);
|
| EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + EXPECT_EQ(OK, set_script_callback2.WaitForResult());
|
| + ASSERT_EQ(4u, factory->resolvers().size());
|
|
|
| - // Cancel the SetPacScriptByData request (it can't have finished yet,
|
| - // since the single-thread is currently blocked).
|
| - resolver.CancelSetPacScript();
|
| + for (int i = 0; i < 3; ++i) {
|
| + EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
|
| + factory->resolvers()[i]->last_pac_script()) << "i=" << i;
|
| + }
|
|
|
| - // Start 1 more request.
|
| + EXPECT_EQ(ASCIIToUTF16("xyz"),
|
| + factory->resolvers()[3]->last_pac_script());
|
|
|
| - TestCompletionCallback callback1;
|
| - ProxyInfo results1;
|
| + // We don't know the exact ordering that requests ran on threads with,
|
| + // but we do know the total count that should have reached the threads.
|
| + // 8 total were submitted, and three were cancelled. Of the three that
|
| + // were cancelled, one of them (request 1) was cancelled after it had
|
| + // already been posted to the worker thread. So the resolvers will
|
| + // have seen 6 total (and 1 from the run prior).
|
| + ASSERT_EQ(4u, factory->resolvers().size());
|
| + int total_count = 0;
|
| + for (int i = 0; i < 3; ++i) {
|
| + total_count += factory->resolvers()[i]->request_count();
|
| + }
|
| + EXPECT_EQ(7, total_count);
|
| +}
|
| +
|
| +// Tests using two threads. The first request hangs the first thread. Checks
|
| +// that other requests are able to complete while this first request remains
|
| +// stalled.
|
| +TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
|
| + const size_t kNumThreads = 2u;
|
| + BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
|
| + MultiThreadedProxyResolver resolver(factory, kNumThreads);
|
| +
|
| + int rv;
|
| +
|
| + EXPECT_TRUE(resolver.expects_pac_bytes());
|
| +
|
| + // Initialize the resolver.
|
| + TestCompletionCallback set_script_callback;
|
| + rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"),
|
| + &set_script_callback);
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + EXPECT_EQ(OK, set_script_callback.WaitForResult());
|
| + // One thread has been provisioned (i.e. one ProxyResolver was created).
|
| + ASSERT_EQ(1u, factory->resolvers().size());
|
| + EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
|
| + factory->resolvers()[0]->last_pac_script());
|
| +
|
| + const int kNumRequests = 4;
|
| + TestCompletionCallback callback[kNumRequests];
|
| + ProxyInfo results[kNumRequests];
|
| + ProxyResolver::RequestHandle request[kNumRequests];
|
| +
|
| + // Start a request that will block the first thread.
|
| +
|
| + factory->resolvers()[0]->Block();
|
| +
|
| rv = resolver.GetProxyForURL(
|
| - GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
|
| + GURL("http://request0"), &results[0], &callback[0], &request[0],
|
| + BoundNetLog());
|
| +
|
| EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + factory->resolvers()[0]->WaitUntilBlocked();
|
|
|
| - // Unblock the worker thread so the requests can continue running.
|
| - mock->Unblock();
|
| + // Start 3 more requests -- they should all be serviced by thread #2
|
| + // since thread #1 is blocked.
|
|
|
| - // Wait for requests 0 and 1 to finish.
|
| + for (int i = 1; i < kNumRequests; ++i) {
|
| + rv = resolver.GetProxyForURL(
|
| + GURL(StringPrintf("http://request%d", i)),
|
| + &results[i], &callback[i], &request[i], BoundNetLog());
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + }
|
|
|
| - rv = callback0.WaitForResult();
|
| - EXPECT_EQ(0, rv);
|
| - EXPECT_EQ("PROXY request0:80", results0.ToPacString());
|
| + // Wait for the three requests to complete (they should complete in FIFO
|
| + // order).
|
| + for (int i = 1; i < kNumRequests; ++i) {
|
| + EXPECT_EQ(i - 1, callback[i].WaitForResult());
|
| + }
|
|
|
| - rv = callback1.WaitForResult();
|
| - EXPECT_EQ(1, rv);
|
| - EXPECT_EQ("PROXY request1:80", results1.ToPacString());
|
| + // Unblock the first thread.
|
| + factory->resolvers()[0]->Unblock();
|
| + EXPECT_EQ(0, callback[0].WaitForResult());
|
|
|
| - // The SetPacScript callback should never have been completed.
|
| - EXPECT_FALSE(set_pac_script_callback.have_result());
|
| + // All in all, the first thread should have seen just 1 request. And the
|
| + // second thread 3 requests.
|
| + ASSERT_EQ(2u, factory->resolvers().size());
|
| + EXPECT_EQ(1, factory->resolvers()[0]->request_count());
|
| + EXPECT_EQ(3, factory->resolvers()[1]->request_count());
|
| }
|
|
|
| } // namespace
|
| +
|
| } // namespace net
|
|
|