Index: net/proxy/single_threaded_proxy_resolver_unittest.cc |
=================================================================== |
--- net/proxy/single_threaded_proxy_resolver_unittest.cc (revision 0) |
+++ net/proxy/single_threaded_proxy_resolver_unittest.cc (revision 0) |
@@ -0,0 +1,313 @@ |
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/waitable_event.h" |
+#include "googleurl/src/gurl.h" |
+#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. |
+// - returns a single-item proxy list with the query's host. |
+class MockProxyResolver : public ProxyResolver { |
+ public: |
+ MockProxyResolver() |
+ : ProxyResolver(true /*expects_pac_bytes*/), |
+ wrong_loop_(MessageLoop::current()), |
+ request_count_(0), |
+ resolve_latency_ms_(0) {} |
+ |
+ // ProxyResolver implementation: |
+ virtual int GetProxyForURL(const GURL& query_url, |
+ ProxyInfo* results, |
+ CompletionCallback* callback, |
+ RequestHandle* request) { |
+ if (resolve_latency_ms_) |
+ PlatformThread::Sleep(resolve_latency_ms_); |
+ |
+ CheckIsOnWorkerThread(); |
+ |
+ EXPECT_EQ(NULL, callback); |
+ EXPECT_EQ(NULL, request); |
+ |
+ results->UseNamedProxy(query_url.host()); |
+ |
+ // Return a success code which represents the request's order. |
+ return request_count_++; |
+ } |
+ |
+ virtual void CancelRequest(RequestHandle request) { |
+ NOTREACHED(); |
+ } |
+ |
+ virtual void SetPacScriptByDataInternal(const std::string& bytes) { |
+ CheckIsOnWorkerThread(); |
+ last_pac_bytes_ = bytes; |
+ } |
+ |
+ const std::string& last_pac_bytes() const { return last_pac_bytes_; } |
+ |
+ void SetResolveLatency(int latency_ms) { |
+ resolve_latency_ms_ = latency_ms; |
+ } |
+ |
+ 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 |
+ // 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_); |
+ } |
+ |
+ MessageLoop* wrong_loop_; |
+ int request_count_; |
+ std::string last_pac_bytes_; |
+ int resolve_latency_ms_; |
+}; |
+ |
+ |
+// A mock synchronous ProxyResolver which can be set to block upon reaching |
+// GetProxyForURL(). |
+class BlockableProxyResolver : public MockProxyResolver { |
+ public: |
+ BlockableProxyResolver() |
+ : should_block_(false), |
+ unblocked_(true, true), |
+ blocked_(true, false) { |
+ } |
+ |
+ void Block() { |
+ should_block_ = true; |
+ unblocked_.Reset(); |
+ } |
+ |
+ void Unblock() { |
+ should_block_ = false; |
+ blocked_.Reset(); |
+ unblocked_.Signal(); |
+ } |
+ |
+ void WaitUntilBlocked() { |
+ blocked_.Wait(); |
+ } |
+ |
+ virtual int GetProxyForURL(const GURL& query_url, |
+ ProxyInfo* results, |
+ CompletionCallback* callback, |
+ RequestHandle* request) { |
+ if (should_block_) { |
+ blocked_.Signal(); |
+ unblocked_.Wait(); |
+ } |
+ |
+ return MockProxyResolver::GetProxyForURL( |
+ query_url, results, callback, request); |
+ } |
+ |
+ private: |
+ bool should_block_; |
+ base::WaitableEvent unblocked_; |
+ base::WaitableEvent blocked_; |
+}; |
+ |
+TEST(SingleThreadedProxyResolverTest, Basic) { |
+ MockProxyResolver* mock = new MockProxyResolver; |
+ scoped_ptr<SingleThreadedProxyResolver> resolver( |
+ new SingleThreadedProxyResolver(mock)); |
+ |
+ int rv; |
+ |
+ EXPECT_TRUE(resolver->expects_pac_bytes()); |
+ |
+ // Call SetPacScriptByData() -- we will make sure it reaches the sync resolver |
+ // later on. |
+ resolver->SetPacScriptByData("pac script bytes"); |
+ |
+ // Start request 0. |
+ TestCompletionCallback callback0; |
+ ProxyInfo results0; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request0"), &results0, &callback0, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ // Wait for request 0 to finish. |
+ rv = callback0.WaitForResult(); |
+ EXPECT_EQ(0, rv); |
+ EXPECT_EQ("PROXY request0:80", results0.ToPacString()); |
+ |
+ // Verify that the data from SetPacScriptByData() reached the resolver. |
+ // (Since we waited for the first request to complete, we are guaranteed |
+ // that the earlier post completed). |
+ EXPECT_EQ("pac script bytes", mock->last_pac_bytes()); |
+ |
+ // Start 3 more requests (request1 to request3). |
+ |
+ TestCompletionCallback callback1; |
+ ProxyInfo results1; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request1"), &results1, &callback1, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ TestCompletionCallback callback2; |
+ ProxyInfo results2; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request2"), &results2, &callback2, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ TestCompletionCallback callback3; |
+ ProxyInfo results3; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request3"), &results3, &callback3, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ // Wait for the requests to finish (they must finish in the order they were |
+ // started, which is what we check for from their magic return value) |
+ |
+ rv = callback1.WaitForResult(); |
+ EXPECT_EQ(1, rv); |
+ EXPECT_EQ("PROXY request1:80", results1.ToPacString()); |
+ |
+ rv = callback2.WaitForResult(); |
+ EXPECT_EQ(2, rv); |
+ EXPECT_EQ("PROXY request2:80", results2.ToPacString()); |
+ |
+ rv = callback3.WaitForResult(); |
+ EXPECT_EQ(3, rv); |
+ EXPECT_EQ("PROXY request3:80", results3.ToPacString()); |
+} |
+ |
+// Cancel a request which is in progress, and then cancel a request which |
+// is pending. |
+TEST(SingleThreadedProxyResolverTest, CancelRequest) { |
+ BlockableProxyResolver* mock = new BlockableProxyResolver; |
+ scoped_ptr<SingleThreadedProxyResolver> resolver( |
+ new SingleThreadedProxyResolver(mock)); |
+ |
+ int rv; |
+ |
+ // Block the proxy resolver, so no request can complete. |
+ mock->Block(); |
+ |
+ // Start request 0. |
+ ProxyResolver::RequestHandle request0; |
+ TestCompletionCallback callback0; |
+ ProxyInfo results0; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request0"), &results0, &callback0, &request0); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ // Wait until requests 0 reaches the worker thread. |
+ mock->WaitUntilBlocked(); |
+ |
+ // Start 3 more requests (request1 : request3). |
+ |
+ TestCompletionCallback callback1; |
+ ProxyInfo results1; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request1"), &results1, &callback1, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ ProxyResolver::RequestHandle request2; |
+ TestCompletionCallback callback2; |
+ ProxyInfo results2; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request2"), &results2, &callback2, &request2); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ TestCompletionCallback callback3; |
+ ProxyInfo results3; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request3"), &results3, &callback3, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ // Cancel request0 (inprogress) and request2 (pending). |
+ resolver->CancelRequest(request0); |
+ resolver->CancelRequest(request2); |
+ |
+ // Unblock the worker thread so the requests can continue running. |
+ mock->Unblock(); |
+ |
+ // Wait for requests 1 and 3 to finish. |
+ |
+ rv = callback1.WaitForResult(); |
+ EXPECT_EQ(1, rv); |
+ EXPECT_EQ("PROXY request1:80", results1.ToPacString()); |
+ |
+ rv = callback3.WaitForResult(); |
+ // Note that since request2 was cancelled before reaching the resolver, |
+ // the request count is 2 and not 3 here. |
+ EXPECT_EQ(2, rv); |
+ EXPECT_EQ("PROXY request3:80", results3.ToPacString()); |
+ |
+ // Requests 0 and 2 which were cancelled, hence their completion callbacks |
+ // were never summoned. |
+ EXPECT_FALSE(callback0.have_result()); |
+ EXPECT_FALSE(callback2.have_result()); |
+} |
+ |
+// Test that deleting SingleThreadedProxyResolver 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)); |
+ |
+ int rv; |
+ |
+ // Block the proxy resolver, so no request can complete. |
+ mock->Block(); |
+ |
+ // Start 3 requests. |
+ |
+ TestCompletionCallback callback0; |
+ ProxyInfo results0; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request0"), &results0, &callback0, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ TestCompletionCallback callback1; |
+ ProxyInfo results1; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request1"), &results1, &callback1, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ TestCompletionCallback callback2; |
+ ProxyInfo results2; |
+ rv = resolver->GetProxyForURL( |
+ GURL("http://request2"), &results2, &callback2, NULL); |
+ EXPECT_EQ(ERR_IO_PENDING, rv); |
+ |
+ // Wait until request 0 reaches the worker thread. |
+ mock->WaitUntilBlocked(); |
+ |
+ // Add some latency, to improve the chance that when |
+ // SingleThreadedProxyResolver 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. |
+ mock->Unblock(); |
+ resolver.reset(); |
+ |
+ // Give any posted tasks a chance to run (in case there is badness). |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ // Check that none of the outstanding requests were completed. |
+ EXPECT_FALSE(callback0.have_result()); |
+ EXPECT_FALSE(callback1.have_result()); |
+ EXPECT_FALSE(callback2.have_result()); |
+} |
+ |
+} // namespace |
+} // namespace net |
Property changes on: net\proxy\single_threaded_proxy_resolver_unittest.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |