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
17using WebKit::WebFrame;
18using WebKit::WebURLRequest;
19using WebKit::WebURLResponse;
20using webkit_glue::ResourceFetcher;
21using webkit_glue::ResourceFetcherWithTimeout;
22
23namespace {
24
25class ResourceFetcherTests : public TestShellTest {
26 protected:
27  UnittestTestServer test_server_;
28};
29
30static const int kMaxWaitTimeMs = 5000;
31
32class 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
94FetcherDelegate* FetcherDelegate::instance_ = NULL;
95
96// Test a fetch from the test server.
97// Flaky, http://crbug.com/51622.
98TEST_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.
130TEST_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
151TEST_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
174class 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
192TEST_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