blob: b68f4c644cf7f8f32f0bcabd4436eda497262413 [file] [log] [blame]
// Copyright (c) 2006-2008 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 "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
#include "third_party/WebKit/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"
#if defined(TOOLKIT_USES_GTK)
#include <gtk/gtk.h>
#endif
using WebKit::WebFrame;
using WebKit::WebURLResponse;
using webkit_glue::ResourceFetcher;
using webkit_glue::ResourceFetcherWithTimeout;
namespace {
class ResourceFetcherTests : public TestShellTest {
public:
void SetUp() {
TestShellTest::SetUp();
}
void TearDown() {
TestShellTest::TearDown();
}
};
static const int kMaxWaitTimeMs = 5000;
static const int kWaitIntervalMs = 100;
class FetcherDelegate {
public:
FetcherDelegate()
: timer_id_(0), completed_(false), time_elapsed_ms_(0) {
// 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;
CreateTimer(kWaitIntervalMs);
}
ResourceFetcher::Callback* NewCallback() {
return ::NewCallback(this, &FetcherDelegate::OnURLFetchComplete);
}
void OnURLFetchComplete(const WebURLResponse& response,
const std::string& data) {
response_ = response;
data_ = data;
completed_ = true;
DestroyTimer();
MessageLoop::current()->Quit();
}
bool completed() const { return completed_; }
bool timed_out() const { return time_elapsed_ms_ > kMaxWaitTimeMs; }
int time_elapsed_ms() const { return time_elapsed_ms_; }
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 CreateTimer(int interval) {
#if defined(OS_WIN)
timer_id_ = ::SetTimer(NULL, NULL, interval,
&FetcherDelegate::TimerCallback);
#elif defined(TOOLKIT_USES_GTK)
timer_id_ = g_timeout_add(interval, &FetcherDelegate::TimerCallback, NULL);
#elif defined(OS_MACOSX)
// CFAbsoluteTime is in seconds and |interval| is in ms, so make sure we
// keep the units correct.
CFTimeInterval interval_in_seconds = static_cast<double>(interval) / 1000.0;
CFAbsoluteTime fire_date =
CFAbsoluteTimeGetCurrent() + interval_in_seconds;
timer_id_ = CFRunLoopTimerCreate(NULL, fire_date, interval_in_seconds, 0,
0, FetcherDelegate::TimerCallback, NULL);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer_id_, kCFRunLoopCommonModes);
#endif
}
void DestroyTimer() {
#if defined(OS_WIN)
::KillTimer(NULL, timer_id_);
#elif defined(TOOLKIT_USES_GTK)
g_source_remove(timer_id_);
#elif defined(OS_MACOSX)
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer_id_,
kCFRunLoopCommonModes);
CFRelease(timer_id_);
#endif
}
#if defined(OS_WIN)
// Static timer callback, just passes through to instance version.
static VOID CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR timer_id,
DWORD ms) {
instance_->TimerFired();
}
#elif defined(TOOLKIT_USES_GTK)
static gboolean TimerCallback(gpointer data) {
instance_->TimerFired();
return true;
}
#elif defined(OS_MACOSX)
static void TimerCallback(CFRunLoopTimerRef timer, void* info) {
instance_->TimerFired();
}
#endif
void TimerFired() {
ASSERT_FALSE(completed_);
if (timed_out()) {
DestroyTimer();
MessageLoop::current()->Quit();
FAIL() << "fetch timed out";
return;
}
time_elapsed_ms_ += kWaitIntervalMs;
}
static FetcherDelegate* instance_;
private:
#if defined(OS_WIN)
UINT_PTR timer_id_;
#elif defined(TOOLKIT_USES_GTK)
guint timer_id_;
#elif defined(OS_MACOSX)
CFRunLoopTimerRef timer_id_;
#endif
bool completed_;
int time_elapsed_ms_;
WebURLResponse response_;
std::string data_;
};
FetcherDelegate* FetcherDelegate::instance_ = NULL;
// Test a fetch from the test server.
TEST_F(ResourceFetcherTests, DISABLED_ResourceFetcherDownload) {
scoped_refptr<UnittestTestServer> server =
UnittestTestServer::CreateServer();
ASSERT_TRUE(NULL != server.get());
WebFrame* frame = test_shell_->webView()->mainFrame();
GURL url = server->TestServerPage("files/test_shell/index.html");
scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher(
url, frame, 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 = server->TestServerPage("files/thisfiledoesntexist.html");
delegate.reset(new FetcherDelegate);
fetcher.reset(new ResourceFetcher(url, frame, delegate->NewCallback()));
delegate->WaitForResponse();
ASSERT_TRUE(delegate->completed());
EXPECT_EQ(delegate->response().httpStatusCode(), 404);
EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
}
TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) {
scoped_refptr<UnittestTestServer> server =
UnittestTestServer::CreateServer();
ASSERT_TRUE(NULL != server.get());
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, 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_TRUE(delegate->time_elapsed_ms() < kMaxWaitTimeMs);
}
TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
scoped_refptr<UnittestTestServer> server =
UnittestTestServer::CreateServer();
ASSERT_TRUE(NULL != server.get());
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 = server->TestServerPage("slow?1");
scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout(
url, frame, 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_TRUE(delegate->time_elapsed_ms() < kMaxWaitTimeMs);
}
} // namespace