Index: chrome/browser/net/resolve_proxy_msg_helper_unittest.cc |
=================================================================== |
--- chrome/browser/net/resolve_proxy_msg_helper_unittest.cc (revision 0) |
+++ chrome/browser/net/resolve_proxy_msg_helper_unittest.cc (revision 0) |
@@ -0,0 +1,349 @@ |
+// 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 "chrome/browser/net/resolve_proxy_msg_helper.h" |
+ |
+#include "base/waitable_event.h" |
+#include "net/base/net_errors.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+// This ProxyConfigService always returns "http://pac" as the PAC url to use. |
+class MockProxyConfigService: public net::ProxyConfigService { |
+ public: |
+ virtual int GetProxyConfig(net::ProxyConfig* results) { |
+ results->pac_url = GURL("http://pac"); |
+ return net::OK; |
+ } |
+}; |
+ |
+// This PAC resolver always returns the hostname of the query URL as the |
+// proxy to use. The Block() method will make GetProxyForURL() hang until |
+// Unblock() is called. |
+class MockProxyResolver : public net::ProxyResolver { |
+ public: |
+ explicit MockProxyResolver() : event_(false, false), is_blocked_(false) { |
+ } |
+ |
+ virtual int GetProxyForURL(const GURL& query_url, |
+ const GURL& /*pac_url*/, |
+ net::ProxyInfo* results) { |
+ if (is_blocked_) |
+ event_.Wait(); |
+ results->UseNamedProxy(query_url.host()); |
+ return net::OK; |
+ } |
+ |
+ void Block() { |
+ is_blocked_ = true; |
+ event_.Reset(); |
+ } |
+ |
+ void Unblock() { |
+ is_blocked_ = false; |
+ event_.Signal(); |
+ } |
+ |
+ private: |
+ base::WaitableEvent event_; |
+ bool is_blocked_; |
+}; |
+ |
+// This struct holds the values that were passed to |
+// Delegate::OnResolveProxyCompleted(). The caller should use WaitUntilDone() |
+// to block until the result has been populated. |
+struct ResultFuture { |
+ public: |
+ ResultFuture() |
+ : reply_msg(NULL), |
+ error_code(0), |
+ started_(false, false), |
+ completed_(false, false) { |
+ } |
+ |
+ // Wait until the request has completed. In other words we have invoked: |
+ // ResolveProxyMsgHelper::Delegate::OnResolveProxyCompleted. |
+ void WaitUntilDone() { |
+ completed_.Wait(); |
+ } |
+ |
+ // Wait until the request has been sent to ResolveProxyMsgHelper. |
+ void WaitUntilStarted() { |
+ started_.Wait(); |
+ } |
+ |
+ bool TimedWaitUntilDone(const base::TimeDelta& max_time) { |
+ return completed_.TimedWait(max_time); |
+ } |
+ |
+ // These fields are only valid after returning from WaitUntilDone(). |
+ IPC::Message* reply_msg; |
+ int error_code; |
+ std::string proxy_list; |
+ |
+ private: |
+ friend class AsyncRequestRunner; |
+ base::WaitableEvent started_; |
+ base::WaitableEvent completed_; |
+}; |
+ |
+// This class lives on the io thread. It starts async requests using the |
+// class under test (ResolveProxyMsgHelper), and signals the result future on |
+// completion. |
+class AsyncRequestRunner : public ResolveProxyMsgHelper::Delegate { |
+ public: |
+ AsyncRequestRunner(net::ProxyService* proxy_service) { |
+ resolve_proxy_msg_helper_.reset( |
+ new ResolveProxyMsgHelper(this, proxy_service)); |
+ } |
+ |
+ void Start(ResultFuture* future, const GURL& url, IPC::Message* reply_msg) { |
+ futures_.push_back(future); |
+ resolve_proxy_msg_helper_->Start(url, reply_msg); |
+ |
+ // Notify of request start. |
+ future->started_.Signal(); |
+ } |
+ |
+ virtual void OnResolveProxyCompleted(IPC::Message* reply_msg, |
+ int error_code, |
+ const std::string& proxy_list) { |
+ // Update the result future for this request (top of queue), and signal it. |
+ ResultFuture* future = futures_.front(); |
+ futures_.pop_front(); |
+ |
+ future->reply_msg = reply_msg; |
+ future->error_code = error_code; |
+ future->proxy_list = proxy_list; |
+ |
+ // Notify of request completion. |
+ future->completed_.Signal(); |
+ } |
+ |
+ private: |
+ std::deque<ResultFuture*> futures_; |
+ scoped_ptr<ResolveProxyMsgHelper> resolve_proxy_msg_helper_; |
+}; |
+ |
+// Helper class to start async requests on an io thread, and return a |
+// result future. The caller then uses ResultFuture::WaitUntilDone() to |
+// get at the results. It "bridges" the originating thread with the helper |
+// io thread. |
+class RunnerBridge { |
+ public: |
+ RunnerBridge() : io_thread_("io_thread"), done_(true, false) { |
+ // Start an io thread where we will run the async requests. |
+ base::Thread::Options options; |
+ options.message_loop_type = MessageLoop::TYPE_IO; |
+ io_thread_.StartWithOptions(options); |
+ |
+ // Construct the state that lives on io thread. |
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
+ this, &RunnerBridge::DoConstruct)); |
+ done_.Wait(); |
+ } |
+ |
+ // Start an async request on the io thread. |
+ ResultFuture* Start(const GURL& url, IPC::Message* reply_msg) { |
+ ResultFuture* future = new ResultFuture(); |
+ |
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
+ async_runner_, &AsyncRequestRunner::Start, future, url, reply_msg)); |
+ |
+ return future; |
+ } |
+ |
+ void DestroyAsyncRunner() { |
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
+ this, &RunnerBridge::DoDestroyAsyncRunner)); |
+ done_.Wait(); |
+ } |
+ |
+ ~RunnerBridge() { |
+ io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
+ this, &RunnerBridge::DoDestroy)); |
+ done_.Wait(); |
+ } |
+ |
+ MockProxyResolver* proxy_resolver() { |
+ return proxy_resolver_; |
+ } |
+ |
+ // Called from io thread. |
+ void DoConstruct() { |
+ proxy_resolver_ = new MockProxyResolver(); |
+ proxy_service_ = new net::ProxyService(new MockProxyConfigService(), |
+ proxy_resolver_); |
+ async_runner_ = new AsyncRequestRunner(proxy_service_); |
+ done_.Signal(); |
+ } |
+ |
+ // Called from io thread. |
+ void DoDestroy() { |
+ delete async_runner_; |
+ delete proxy_service_; |
+ done_.Signal(); |
+ } |
+ |
+ // Called from io thread. |
+ void DoDestroyAsyncRunner() { |
+ delete async_runner_; |
+ async_runner_ = NULL; |
+ done_.Signal(); |
+ } |
+ |
+ private: |
+ base::Thread io_thread_; |
+ base::WaitableEvent done_; |
+ |
+ net::ProxyService* proxy_service_; |
+ MockProxyResolver* proxy_resolver_; // Owned by proxy_service_. |
+ |
+ AsyncRequestRunner* async_runner_; |
+}; |
+ |
+// Avoid the need to have an AddRef / Release |
+template<> |
+void RunnableMethodTraits<RunnerBridge>::RetainCallee(RunnerBridge*) {} |
+template<> |
+void RunnableMethodTraits<RunnerBridge>::ReleaseCallee(RunnerBridge*) {} |
+ |
+template<> |
+void RunnableMethodTraits<AsyncRequestRunner>::RetainCallee(AsyncRequestRunner*) {} |
+template<> |
+void RunnableMethodTraits<AsyncRequestRunner>::ReleaseCallee(AsyncRequestRunner*) {} |
+ |
+ |
+// Issue three sequential requests -- each should succeed. |
+TEST(ResolveProxyMsgHelperTest, Sequential) { |
+ RunnerBridge runner; |
+ |
+ GURL url1("http://www.google1.com/"); |
+ GURL url2("http://www.google2.com/"); |
+ GURL url3("http://www.google3.com/"); |
+ |
+ scoped_ptr<IPC::Message> msg1(new IPC::Message()); |
+ scoped_ptr<IPC::Message> msg2(new IPC::Message()); |
+ scoped_ptr<IPC::Message> msg3(new IPC::Message()); |
+ |
+ // Execute each request sequentially (so there are never 2 requests |
+ // outstanding at the same time). |
+ |
+ scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get())); |
+ result1->WaitUntilDone(); |
+ |
+ scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get())); |
+ result2->WaitUntilDone(); |
+ |
+ scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get())); |
+ result3->WaitUntilDone(); |
+ |
+ // Check that each request gave the expected result. |
+ |
+ EXPECT_EQ(msg1.get(), result1->reply_msg); |
+ EXPECT_EQ(net::OK, result1->error_code); |
+ EXPECT_EQ("PROXY www.google1.com", result1->proxy_list); |
+ |
+ EXPECT_EQ(msg2.get(), result2->reply_msg); |
+ EXPECT_EQ(net::OK, result2->error_code); |
+ EXPECT_EQ("PROXY www.google2.com", result2->proxy_list); |
+ |
+ EXPECT_EQ(msg3.get(), result3->reply_msg); |
+ EXPECT_EQ(net::OK, result3->error_code); |
+ EXPECT_EQ("PROXY www.google3.com", result3->proxy_list); |
+} |
+ |
+// Issue a request while one is already in progress -- should be queued. |
+TEST(ResolveProxyMsgHelperTest, QueueRequests) { |
+ RunnerBridge runner; |
+ |
+ GURL url1("http://www.google1.com/"); |
+ GURL url2("http://www.google2.com/"); |
+ GURL url3("http://www.google3.com/"); |
+ |
+ scoped_ptr<IPC::Message> msg1(new IPC::Message()); |
+ scoped_ptr<IPC::Message> msg2(new IPC::Message()); |
+ scoped_ptr<IPC::Message> msg3(new IPC::Message()); |
+ |
+ // Make the proxy resolver hang on the next request. |
+ runner.proxy_resolver()->Block(); |
+ |
+ // Start three requests. Since the proxy resolver is hung, the second two |
+ // will be pending. |
+ |
+ scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get())); |
+ scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get())); |
+ scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get())); |
+ |
+ // Wait for the final request to have been scheduled. Otherwise we may rush |
+ // to calling Unblock() without actually having blocked anything. |
+ result3->WaitUntilStarted(); |
+ |
+ // Unblock the proxy service so requests 1-3 can complete. |
+ runner.proxy_resolver()->Unblock(); |
+ |
+ // Wait for all the requests to finish (they run in FIFO order). |
+ result3->WaitUntilDone(); |
+ |
+ // Check that each call invoked the callback with the right parameters. |
+ |
+ EXPECT_EQ(msg1.get(), result1->reply_msg); |
+ EXPECT_EQ(net::OK, result1->error_code); |
+ EXPECT_EQ("PROXY www.google1.com", result1->proxy_list); |
+ |
+ EXPECT_EQ(msg2.get(), result2->reply_msg); |
+ EXPECT_EQ(net::OK, result2->error_code); |
+ EXPECT_EQ("PROXY www.google2.com", result2->proxy_list); |
+ |
+ EXPECT_EQ(msg3.get(), result3->reply_msg); |
+ EXPECT_EQ(net::OK, result3->error_code); |
+ EXPECT_EQ("PROXY www.google3.com", result3->proxy_list); |
+} |
+ |
+// Delete the helper while a request is in progress, and others are pending. |
+TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) { |
+ RunnerBridge runner; |
+ |
+ GURL url1("http://www.google1.com/"); |
+ GURL url2("http://www.google2.com/"); |
+ GURL url3("http://www.google3.com/"); |
+ |
+ // NOTE: these are not scoped ptr, since they will be deleted by the |
+ // request's cancellation. |
+ IPC::Message* msg1 = new IPC::Message(); |
+ IPC::Message* msg2 = new IPC::Message(); |
+ IPC::Message* msg3 = new IPC::Message(); |
+ |
+ // Make the next request block. |
+ runner.proxy_resolver()->Block(); |
+ |
+ // Start three requests; since the first one blocked, the other two should |
+ // be pending. |
+ |
+ scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1)); |
+ scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2)); |
+ scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3)); |
+ |
+ result3->WaitUntilStarted(); |
+ |
+ // Delete the underlying ResolveProxyMsgHelper -- this should cancel all |
+ // the requests which are outstanding. |
+ runner.DestroyAsyncRunner(); |
+ |
+ // Unblocking the proxy resolver means the three requests can complete -- |
+ // however they should not try to notify the delegate since we have already |
+ // deleted the helper. |
+ runner.proxy_resolver()->Unblock(); |
+ |
+ // Check that none of the requests were sent to the delegate. |
+ EXPECT_FALSE( |
+ result1->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); |
+ EXPECT_FALSE( |
+ result2->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); |
+ EXPECT_FALSE( |
+ result3->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); |
+ |
+ // It should also be the case that msg1, msg2, msg3 were deleted by the |
+ // cancellation. (Else will show up as a leak in Purify). |
+} |
+ |
Property changes on: chrome\browser\net\resolve_proxy_msg_helper_unittest.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |