Revert "Revert "Add configurable timeouts to commands.""

This reverts commit b1b3a4c390.
This commit is contained in:
Matthew Horan 2012-10-24 07:55:29 -04:00
parent b1b3a4c390
commit cbb58d05f1
15 changed files with 271 additions and 11 deletions

View File

@ -165,6 +165,14 @@ module Capybara::Webkit
command "Render", path, width, height
end
def timeout=(timeout_in_seconds)
command "SetTimeout", timeout_in_seconds
end
def timeout
command("GetTimeout").to_i
end
def set_cookie(cookie)
command "SetCookie", cookie
end
@ -199,12 +207,26 @@ module Capybara::Webkit
if result.nil?
raise NoResponseError, "No response received from the server."
elsif result != 'ok'
raise InvalidResponseError, read_response
case response = read_response
when "timeout"
raise Capybara::TimeoutError, "Request timed out after #{timeout_seconds}"
else
raise InvalidResponseError, response
end
end
result
end
def timeout_seconds
seconds = timeout
if seconds > 1
"#{seconds} seconds"
else
"1 second"
end
end
def read_response
response_length = @connection.gets.to_i
if response_length > 0

View File

@ -1825,6 +1825,78 @@ describe Capybara::Webkit::Driver do
end
end
describe "timeout for long requests" do
let(:driver) do
driver_for_app do
html = <<-HTML
<html>
<body>
<form action="/form" method="post">
<input type="submit" value="Submit"/>
</form>
</body>
</html>
HTML
get "/" do
sleep(2)
html
end
post "/form" do
sleep(4)
html
end
end
end
it "should not raise a timeout error when zero" do
driver.browser.timeout = 0
lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
end
it "should raise a timeout error" do
driver.browser.timeout = 1
lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError, "Request timed out after 1 second")
end
it "should not raise an error when the timeout is high enough" do
driver.browser.timeout = 10
lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
end
it "should set the timeout for each request" do
driver.browser.timeout = 10
lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
driver.browser.timeout = 1
lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError)
end
it "should set the timeout for each request" do
driver.browser.timeout = 1
lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError)
driver.reset!
driver.browser.timeout = 10
lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
end
it "should raise a timeout on a slow form" do
driver.browser.timeout = 3
driver.visit("/")
driver.status_code.should == 200
driver.browser.timeout = 1
driver.find("//input").first.click
lambda { driver.status_code }.should raise_error(Capybara::TimeoutError)
end
it "get timeout" do
driver.browser.timeout = 10
driver.browser.timeout.should == 10
driver.browser.timeout = 3
driver.browser.timeout.should == 3
end
end
describe "logger app" do
it "logs nothing before turning on the logger" do
driver.visit("/")

View File

@ -22,6 +22,8 @@
#include "ConsoleMessages.h"
#include "RequestedUrl.h"
#include "CurrentUrl.h"
#include "SetTimeout.h"
#include "GetTimeout.h"
#include "ResizeWindow.h"
#include "IgnoreSslErrors.h"
#include "SetSkipImageLoading.h"

View File

@ -4,6 +4,7 @@
#include "CommandParser.h"
#include "CommandFactory.h"
#include "PageLoadingCommand.h"
#include "TimeoutCommand.h"
#include "SocketCommand.h"
#include <QTcpSocket>
@ -24,18 +25,14 @@ Connection::Connection(QTcpSocket *socket, WebPageManager *manager, QObject *par
void Connection::commandReady(Command *command) {
m_queuedCommand = command;
m_manager->logger() << "Received" << command->toString();
if (m_manager->isLoading()) {
m_manager->logger() << command->toString() << "waiting for load to finish";
m_commandWaiting = true;
} else {
startCommand();
}
startCommand();
}
void Connection::startCommand() {
m_commandWaiting = false;
if (m_pageSuccess) {
m_runningCommand = new PageLoadingCommand(m_queuedCommand, m_manager, this);
m_runningCommand = new TimeoutCommand(m_runningCommand, m_manager, this);
connect(m_runningCommand, SIGNAL(finished(Response *)), this, SLOT(finishCommand(Response *)));
m_runningCommand->start();
} else {
@ -45,9 +42,6 @@ void Connection::startCommand() {
void Connection::pendingLoadFinished(bool success) {
m_pageSuccess = m_pageSuccess && success;
if (m_commandWaiting) {
startCommand();
}
}
void Connection::writePageLoadFailure() {

View File

@ -31,7 +31,7 @@ class Connection : public QObject {
WebPageManager *m_manager;
CommandParser *m_commandParser;
CommandFactory *m_commandFactory;
PageLoadingCommand *m_runningCommand;
Command *m_runningCommand;
bool m_pageSuccess;
bool m_commandWaiting;
WebPage *currentPage();

9
src/GetTimeout.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "GetTimeout.h"
#include "WebPageManager.h"
GetTimeout::GetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void GetTimeout::start() {
emit finished(new Response(true, QString::number(manager()->getTimeout())));
}

11
src/GetTimeout.h Normal file
View File

@ -0,0 +1,11 @@
#include "SocketCommand.h"
class WebPageManager;
class GetTimeout : public SocketCommand {
Q_OBJECT;
public:
GetTimeout(WebPageManager *page, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

19
src/SetTimeout.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "SetTimeout.h"
#include "WebPageManager.h"
SetTimeout::SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void SetTimeout::start() {
QString timeoutString = arguments()[0];
bool ok;
int timeout = timeoutString.toInt(&ok);
if (ok) {
manager()->setTimeout(timeout);
emit finished(new Response(true));
} else {
emit finished(new Response(false, QString("Invalid value for timeout")));
}
}

9
src/SetTimeout.h Normal file
View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class SetTimeout : public SocketCommand {
Q_OBJECT
public:
SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

59
src/TimeoutCommand.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "TimeoutCommand.h"
#include "Command.h"
#include "WebPageManager.h"
#include "WebPage.h"
#include <QTimer>
TimeoutCommand::TimeoutCommand(Command *command, WebPageManager *manager, QObject *parent) : Command(parent) {
m_command = command;
m_manager = manager;
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
connect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
}
void TimeoutCommand::start() {
if (m_manager->isLoading()) {
connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
startTimeout();
} else {
startCommand();
}
}
void TimeoutCommand::startCommand() {
connect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
m_command->start();
}
void TimeoutCommand::startTimeout() {
int timeout = m_manager->getTimeout();
if (timeout > 0) {
m_timer->start(timeout * 1000);
}
}
void TimeoutCommand::pendingLoadFinished(bool success) {
if (success) {
startCommand();
} else {
emit finished(new Response(false, m_manager->currentPage()->failureString()));
}
}
void TimeoutCommand::pageLoadingFromCommand() {
startTimeout();
}
void TimeoutCommand::commandTimeout() {
m_manager->currentPage()->triggerAction(QWebPage::Stop);
m_command->deleteLater();
emit finished(new Response(false, QString("timeout")));
}
void TimeoutCommand::commandFinished(Response *response) {
m_command->deleteLater();
emit finished(response);
}

41
src/TimeoutCommand.h Normal file
View File

@ -0,0 +1,41 @@
#include "Command.h"
#include <QObject>
#include <QStringList>
class Response;
class WebPageManager;
class QTimer;
/* Decorates a command with a timeout.
*
* If the timeout, using a QTimer is reached before
* the command is finished, the load page load will
* be stopped and failure response will be issued.
*
*/
class TimeoutCommand : public Command {
Q_OBJECT
public:
TimeoutCommand(Command *command, WebPageManager *page, QObject *parent = 0);
virtual void start();
public slots:
void commandTimeout();
void commandFinished(Response *response);
void pageLoadingFromCommand();
void pendingLoadFinished(bool);
signals:
void finished(Response *response);
protected:
void startCommand();
void startTimeout();
private:
WebPageManager *m_manager;
QTimer *m_timer;
Command *m_command;
};

View File

@ -8,6 +8,7 @@ WebPageManager::WebPageManager(QObject *parent) : QObject(parent) {
m_success = true;
m_loggingEnabled = false;
m_ignoredOutput = new QString();
m_timeout = -1;
createPage(this)->setFocus();
}
@ -85,7 +86,16 @@ bool WebPageManager::ignoreSslErrors() {
return m_ignoreSslErrors;
}
int WebPageManager::getTimeout() {
return m_timeout;
}
void WebPageManager::setTimeout(int timeout) {
m_timeout = timeout;
}
void WebPageManager::reset() {
m_timeout = -1;
m_cookieJar->clearCookies();
m_pages.first()->deleteLater();
m_pages.clear();

View File

@ -22,6 +22,8 @@ class WebPageManager : public QObject {
WebPage *createPage(QObject *parent);
void setIgnoreSslErrors(bool);
bool ignoreSslErrors();
void setTimeout(int);
int getTimeout();
void reset();
NetworkCookieJar *cookieJar();
bool isLoading() const;
@ -50,6 +52,7 @@ class WebPageManager : public QObject {
bool m_success;
bool m_loggingEnabled;
QString *m_ignoredOutput;
int m_timeout;
};
#endif // _WEBPAGEMANAGER_H

View File

@ -39,3 +39,6 @@ CHECK_COMMAND(ClearPromptText)
CHECK_COMMAND(JavascriptAlertMessages)
CHECK_COMMAND(JavascriptConfirmMessages)
CHECK_COMMAND(JavascriptPromptMessages)
CHECK_COMMAND(GetTimeout)
CHECK_COMMAND(SetTimeout)

View File

@ -53,6 +53,9 @@ HEADERS = \
WindowFocus.h \
GetWindowHandles.h \
GetWindowHandle.h \
GetTimeout.h \
SetTimeout.h \
TimeoutCommand.h \
SOURCES = \
EnableLogging.cpp \
@ -102,11 +105,14 @@ SOURCES = \
SetProxy.cpp \
NullCommand.cpp \
PageLoadingCommand.cpp \
SetTimeout.cpp \
GetTimeout.cpp \
SetSkipImageLoading.cpp \
WebPageManager.cpp \
WindowFocus.cpp \
GetWindowHandles.cpp \
GetWindowHandle.cpp \
TimeoutCommand.cpp \
RESOURCES = webkit_server.qrc
QT += network webkit