1 // Copyright (c) 2011 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 "webkit/glue/resource_fetcher.h" 6 7 #include "base/callback.h" 8 #include "base/message_loop.h" 9 #include "base/timer.h" 10 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" 12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" 13 #include "webkit/glue/unittest_test_server.h" 14 #include "webkit/tools/test_shell/simple_resource_loader_bridge.h" 15 #include "webkit/tools/test_shell/test_shell_test.h" 16 17 using WebKit::WebFrame; 18 using WebKit::WebURLRequest; 19 using WebKit::WebURLResponse; 20 using webkit_glue::ResourceFetcher; 21 using webkit_glue::ResourceFetcherWithTimeout; 22 23 namespace { 24 25 class ResourceFetcherTests : public TestShellTest { 26 protected: 27 UnittestTestServer test_server_; 28 }; 29 30 static const int kMaxWaitTimeMs = 5000; 31 32 class FetcherDelegate { 33 public: 34 FetcherDelegate() 35 : completed_(false), 36 timed_out_(false) { 37 // Start a repeating timer waiting for the download to complete. The 38 // callback has to be a static function, so we hold on to our instance. 39 FetcherDelegate::instance_ = this; 40 StartTimer(); 41 } 42 43 ResourceFetcher::Callback* NewCallback() { 44 return ::NewCallback(this, &FetcherDelegate::OnURLFetchComplete); 45 } 46 47 virtual void OnURLFetchComplete(const WebURLResponse& response, 48 const std::string& data) { 49 response_ = response; 50 data_ = data; 51 completed_ = true; 52 timer_.Stop(); 53 MessageLoop::current()->Quit(); 54 } 55 56 bool completed() const { return completed_; } 57 bool timed_out() const { return timed_out_; } 58 59 std::string data() const { return data_; } 60 const WebURLResponse& response() const { return response_; } 61 62 // Wait for the request to complete or timeout. We use a loop here b/c the 63 // testing infrastructure (test_shell) can generate spurious calls to the 64 // MessageLoop's Quit method. 65 void WaitForResponse() { 66 while (!completed() && !timed_out()) 67 MessageLoop::current()->Run(); 68 } 69 70 void StartTimer() { 71 timer_.Start(base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs), 72 this, 73 &FetcherDelegate::TimerFired); 74 } 75 76 void TimerFired() { 77 ASSERT_FALSE(completed_); 78 79 timed_out_ = true; 80 MessageLoop::current()->Quit(); 81 FAIL() << "fetch timed out"; 82 } 83 84 static FetcherDelegate* instance_; 85 86 private: 87 base::OneShotTimer<FetcherDelegate> timer_; 88 bool completed_; 89 bool timed_out_; 90 WebURLResponse response_; 91 std::string data_; 92 }; 93 94 FetcherDelegate* FetcherDelegate::instance_ = NULL; 95 96 // Test a fetch from the test server. 97 // Flaky, http://crbug.com/51622. 98 TEST_F(ResourceFetcherTests, FLAKY_ResourceFetcherDownload) { 99 ASSERT_TRUE(test_server_.Start()); 100 101 WebFrame* frame = test_shell_->webView()->mainFrame(); 102 103 GURL url(test_server_.GetURL("files/test_shell/index.html")); 104 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate); 105 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher( 106 url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback())); 107 108 delegate->WaitForResponse(); 109 110 ASSERT_TRUE(delegate->completed()); 111 EXPECT_EQ(delegate->response().httpStatusCode(), 200); 112 std::string text = delegate->data(); 113 EXPECT_TRUE(text.find("What is this page?") != std::string::npos); 114 115 // Test 404 response. 116 url = test_server_.GetURL("files/thisfiledoesntexist.html"); 117 delegate.reset(new FetcherDelegate); 118 fetcher.reset(new ResourceFetcher(url, frame, 119 WebURLRequest::TargetIsMainFrame, 120 delegate->NewCallback())); 121 122 delegate->WaitForResponse(); 123 124 ASSERT_TRUE(delegate->completed()); 125 EXPECT_EQ(delegate->response().httpStatusCode(), 404); 126 EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos); 127 } 128 129 // Flaky, http://crbug.com/51622. 130 TEST_F(ResourceFetcherTests, FLAKY_ResourceFetcherDidFail) { 131 ASSERT_TRUE(test_server_.Start()); 132 133 WebFrame* frame = test_shell_->webView()->mainFrame(); 134 135 // Try to fetch a page on a site that doesn't exist. 136 GURL url("http://localhost:1339/doesnotexist"); 137 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate); 138 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher( 139 url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback())); 140 141 delegate->WaitForResponse(); 142 143 // When we fail, we still call the Delegate callback but we pass in empty 144 // values. 145 EXPECT_TRUE(delegate->completed()); 146 EXPECT_TRUE(delegate->response().isNull()); 147 EXPECT_EQ(delegate->data(), std::string()); 148 EXPECT_FALSE(delegate->timed_out()); 149 } 150 151 TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) { 152 ASSERT_TRUE(test_server_.Start()); 153 154 WebFrame* frame = test_shell_->webView()->mainFrame(); 155 156 // Grab a page that takes at least 1 sec to respond, but set the fetcher to 157 // timeout in 0 sec. 158 GURL url(test_server_.GetURL("slow?1")); 159 scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate); 160 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout( 161 url, frame, WebURLRequest::TargetIsMainFrame, 162 0, delegate->NewCallback())); 163 164 delegate->WaitForResponse(); 165 166 // When we timeout, we still call the Delegate callback but we pass in empty 167 // values. 168 EXPECT_TRUE(delegate->completed()); 169 EXPECT_TRUE(delegate->response().isNull()); 170 EXPECT_EQ(delegate->data(), std::string()); 171 EXPECT_FALSE(delegate->timed_out()); 172 } 173 174 class EvilFetcherDelegate : public FetcherDelegate { 175 public: 176 void SetFetcher(ResourceFetcher* fetcher) { 177 fetcher_.reset(fetcher); 178 } 179 180 void OnURLFetchComplete(const WebURLResponse& response, 181 const std::string& data) { 182 // Destroy the ResourceFetcher here. We are testing that upon returning 183 // to the ResourceFetcher that it does not crash. 184 fetcher_.reset(); 185 FetcherDelegate::OnURLFetchComplete(response, data); 186 } 187 188 private: 189 scoped_ptr<ResourceFetcher> fetcher_; 190 }; 191 192 TEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) { 193 ASSERT_TRUE(test_server_.Start()); 194 195 WebFrame* frame = test_shell_->webView()->mainFrame(); 196 197 // Grab a page that takes at least 1 sec to respond, but set the fetcher to 198 // timeout in 0 sec. 199 GURL url(test_server_.GetURL("slow?1")); 200 scoped_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate); 201 scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout( 202 url, frame, WebURLRequest::TargetIsMainFrame, 203 0, delegate->NewCallback())); 204 delegate->SetFetcher(fetcher.release()); 205 206 delegate->WaitForResponse(); 207 EXPECT_FALSE(delegate->timed_out()); 208 } 209 210 } // namespace 211