// Copyright (c) 2011 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 "webkit/glue/resource_fetcher.h"
#include "base/callback.h"
#include "base/message_loop.h"
#include "base/timer.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "webkit/glue/unittest_test_server.h"
#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
#include "webkit/tools/test_shell/test_shell_test.h"
using WebKit::WebFrame;
using WebKit::WebURLRequest;
using WebKit::WebURLResponse;
using webkit_glue::ResourceFetcher;
using webkit_glue::ResourceFetcherWithTimeout;
namespace {
class ResourceFetcherTests : public TestShellTest {
protected:
UnittestTestServer test_server_;
};
static const int kMaxWaitTimeMs = 5000;
class FetcherDelegate {
public:
FetcherDelegate()
: completed_(false),
timed_out_(false) {
// Start a repeating timer waiting for the download to complete. The
// callback has to be a static function, so we hold on to our instance.
FetcherDelegate::instance_ = this;
StartTimer();
}
ResourceFetcher::Callback* NewCallback() {
return ::NewCallback(this, &FetcherDelegate::OnURLFetchComplete);
}
virtual void OnURLFetchComplete(const WebURLResponse& response,
const std::string& data) {
response_ = response;
data_ = data;
completed_ = true;
timer_.Stop();
MessageLoop::current()->Quit();
}
bool completed() const { return completed_; }
bool timed_out() const { return timed_out_; }
std::string data() const { return data_; }
const WebURLResponse& response() const { return response_; }
// Wait for the request to complete or timeout. We use a loop here b/c the
// testing infrastructure (test_shell) can generate spurious calls to the
// MessageLoop's Quit method.
void WaitForResponse() {
while (!completed() && !timed_out())
MessageLoop::current()->Run();
}
void StartTimer() {
timer_.Start(base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs),
this,
&FetcherDelegate::TimerFired);
}
void TimerFired() {
ASSERT_FALSE(completed_);
timed_out_ = true;
MessageLoop::current()->Quit();
FAIL() << "fetch timed out";
}
static FetcherDelegate* instance_;
private:
base::OneShotTimer<FetcherDelegate> timer_;
bool completed_;
bool timed_out_;
WebURLResponse response_;
std::string data_;
};
FetcherDelegate* FetcherDelegate::instance_ = NULL;
// Test a fetch from the test server.
// Flaky, http://crbug.com/51622.
TEST_F(ResourceFetcherTests, FLAKY_ResourceFetcherDownload) {
ASSERT_TRUE(test_server_.Start());
WebFrame* frame = test_shell_->webView()->mainFrame();
GURL url(test_server_.GetURL("files/test_shell/index.html"));
scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
delegate->WaitForResponse();
ASSERT_TRUE(delegate->completed());
EXPECT_EQ(delegate->response().httpStatusCode(), 200);
std::string text = delegate->data();
EXPECT_TRUE(text.find("What is this page?") != std::string::npos);
// Test 404 response.
url = test_server_.GetURL("files/thisfiledoesntexist.html");
delegate.reset(new FetcherDelegate);
fetcher.reset(new ResourceFetcher(url, frame,
WebURLRequest::TargetIsMainFrame,
delegate->NewCallback()));
delegate->WaitForResponse();
ASSERT_TRUE(delegate->completed());
EXPECT_EQ(delegate->response().httpStatusCode(), 404);
EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
}
// Flaky, http://crbug.com/51622.
TEST_F(ResourceFetcherTests, FLAKY_ResourceFetcherDidFail) {
ASSERT_TRUE(test_server_.Start());
WebFrame* frame = test_shell_->webView()->mainFrame();
// Try to fetch a page on a site that doesn't exist.
GURL url("http://localhost:1339/doesnotexist");
scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
url, frame, WebURLRequest::TargetIsMainFrame, delegate->NewCallback()));
delegate->WaitForResponse();
// When we fail, we still call the Delegate callback but we pass in empty
// values.
EXPECT_TRUE(delegate->completed());
EXPECT_TRUE(delegate->response().isNull());
EXPECT_EQ(delegate->data(), std::string());
EXPECT_FALSE(delegate->timed_out());
}
TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
ASSERT_TRUE(test_server_.Start());
WebFrame* frame = test_shell_->webView()->mainFrame();
// Grab a page that takes at least 1 sec to respond, but set the fetcher to
// timeout in 0 sec.
GURL url(test_server_.GetURL("slow?1"));
scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
url, frame, WebURLRequest::TargetIsMainFrame,
0, delegate->NewCallback()));
delegate->WaitForResponse();
// When we timeout, we still call the Delegate callback but we pass in empty
// values.
EXPECT_TRUE(delegate->completed());
EXPECT_TRUE(delegate->response().isNull());
EXPECT_EQ(delegate->data(), std::string());
EXPECT_FALSE(delegate->timed_out());
}
class EvilFetcherDelegate : public FetcherDelegate {
public:
void SetFetcher(ResourceFetcher* fetcher) {
fetcher_.reset(fetcher);
}
void OnURLFetchComplete(const WebURLResponse& response,
const std::string& data) {
// Destroy the ResourceFetcher here. We are testing that upon returning
// to the ResourceFetcher that it does not crash.
fetcher_.reset();
FetcherDelegate::OnURLFetchComplete(response, data);
}
private:
scoped_ptr<ResourceFetcher> fetcher_;
};
TEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) {
ASSERT_TRUE(test_server_.Start());
WebFrame* frame = test_shell_->webView()->mainFrame();
// Grab a page that takes at least 1 sec to respond, but set the fetcher to
// timeout in 0 sec.
GURL url(test_server_.GetURL("slow?1"));
scoped_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate);
scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
url, frame, WebURLRequest::TargetIsMainFrame,
0, delegate->NewCallback()));
delegate->SetFetcher(fetcher.release());
delegate->WaitForResponse();
EXPECT_FALSE(delegate->timed_out());
}
} // namespace