Skip to content
This repository
Browse code

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

This reverts commit b1b3a4c.
  • Loading branch information...
commit cbb58d05f1608817539c963c9c39fc9b324f1203 1 parent b1b3a4c
Matthew Horan authored October 24, 2012
24  lib/capybara/webkit/browser.rb
@@ -165,6 +165,14 @@ def render(path, width, height)
165 165
       command "Render", path, width, height
166 166
     end
167 167
 
  168
+    def timeout=(timeout_in_seconds)
  169
+      command "SetTimeout", timeout_in_seconds
  170
+    end
  171
+
  172
+    def timeout
  173
+      command("GetTimeout").to_i
  174
+    end
  175
+
168 176
     def set_cookie(cookie)
169 177
       command "SetCookie", cookie
170 178
     end
@@ -199,12 +207,26 @@ def check
199 207
       if result.nil?
200 208
         raise NoResponseError, "No response received from the server."
201 209
       elsif result != 'ok'
202  
-        raise InvalidResponseError, read_response
  210
+        case response = read_response
  211
+        when "timeout"
  212
+          raise Capybara::TimeoutError, "Request timed out after #{timeout_seconds}"
  213
+        else
  214
+          raise InvalidResponseError, response
  215
+        end
203 216
       end
204 217
 
205 218
       result
206 219
     end
207 220
 
  221
+    def timeout_seconds
  222
+      seconds = timeout
  223
+      if seconds > 1
  224
+        "#{seconds} seconds"
  225
+      else
  226
+        "1 second"
  227
+      end
  228
+    end
  229
+
208 230
     def read_response
209 231
       response_length = @connection.gets.to_i
210 232
       if response_length > 0
72  spec/driver_spec.rb
@@ -1825,6 +1825,78 @@ def which_for(character)
1825 1825
     end
1826 1826
   end
1827 1827
 
  1828
+  describe "timeout for long requests" do
  1829
+    let(:driver) do
  1830
+      driver_for_app do
  1831
+        html = <<-HTML
  1832
+            <html>
  1833
+              <body>
  1834
+                <form action="/form" method="post">
  1835
+                  <input type="submit" value="Submit"/>
  1836
+                </form>
  1837
+              </body>
  1838
+            </html>
  1839
+        HTML
  1840
+
  1841
+        get "/" do
  1842
+          sleep(2)
  1843
+          html
  1844
+        end
  1845
+
  1846
+        post "/form" do
  1847
+          sleep(4)
  1848
+          html
  1849
+        end
  1850
+      end
  1851
+    end
  1852
+
  1853
+    it "should not raise a timeout error when zero" do
  1854
+      driver.browser.timeout = 0
  1855
+      lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
  1856
+    end
  1857
+
  1858
+    it "should raise a timeout error" do
  1859
+      driver.browser.timeout = 1
  1860
+      lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError, "Request timed out after 1 second")
  1861
+    end
  1862
+
  1863
+    it "should not raise an error when the timeout is high enough" do
  1864
+      driver.browser.timeout = 10
  1865
+      lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
  1866
+    end
  1867
+
  1868
+    it "should set the timeout for each request" do
  1869
+      driver.browser.timeout = 10
  1870
+      lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
  1871
+      driver.browser.timeout = 1
  1872
+      lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError)
  1873
+    end
  1874
+
  1875
+    it "should set the timeout for each request" do
  1876
+      driver.browser.timeout = 1
  1877
+      lambda { driver.visit("/") }.should raise_error(Capybara::TimeoutError)
  1878
+      driver.reset!
  1879
+      driver.browser.timeout = 10
  1880
+      lambda { driver.visit("/") }.should_not raise_error(Capybara::TimeoutError)
  1881
+    end
  1882
+
  1883
+    it "should raise a timeout on a slow form" do
  1884
+      driver.browser.timeout = 3
  1885
+      driver.visit("/")
  1886
+      driver.status_code.should == 200
  1887
+      driver.browser.timeout = 1
  1888
+      driver.find("//input").first.click
  1889
+      lambda { driver.status_code }.should raise_error(Capybara::TimeoutError)
  1890
+    end
  1891
+
  1892
+    it "get timeout" do
  1893
+      driver.browser.timeout = 10
  1894
+      driver.browser.timeout.should == 10
  1895
+      driver.browser.timeout = 3
  1896
+      driver.browser.timeout.should == 3
  1897
+    end
  1898
+  end
  1899
+
1828 1900
   describe "logger app" do
1829 1901
     it "logs nothing before turning on the logger" do
1830 1902
       driver.visit("/")
2  src/CommandFactory.cpp
@@ -22,6 +22,8 @@
22 22
 #include "ConsoleMessages.h"
23 23
 #include "RequestedUrl.h"
24 24
 #include "CurrentUrl.h"
  25
+#include "SetTimeout.h"
  26
+#include "GetTimeout.h"
25 27
 #include "ResizeWindow.h"
26 28
 #include "IgnoreSslErrors.h"
27 29
 #include "SetSkipImageLoading.h"
12  src/Connection.cpp
@@ -4,6 +4,7 @@
4 4
 #include "CommandParser.h"
5 5
 #include "CommandFactory.h"
6 6
 #include "PageLoadingCommand.h"
  7
+#include "TimeoutCommand.h"
7 8
 #include "SocketCommand.h"
8 9
 
9 10
 #include <QTcpSocket>
@@ -24,18 +25,14 @@ Connection::Connection(QTcpSocket *socket, WebPageManager *manager, QObject *par
24 25
 void Connection::commandReady(Command *command) {
25 26
   m_queuedCommand = command;
26 27
   m_manager->logger() << "Received" << command->toString();
27  
-  if (m_manager->isLoading()) {
28  
-    m_manager->logger() << command->toString() << "waiting for load to finish";
29  
-    m_commandWaiting = true;
30  
-  } else {
31  
-    startCommand();
32  
-  }
  28
+  startCommand();
33 29
 }
34 30
 
35 31
 void Connection::startCommand() {
36 32
   m_commandWaiting = false;
37 33
   if (m_pageSuccess) {
38 34
     m_runningCommand = new PageLoadingCommand(m_queuedCommand, m_manager, this);
  35
+    m_runningCommand = new TimeoutCommand(m_runningCommand, m_manager, this);
39 36
     connect(m_runningCommand, SIGNAL(finished(Response *)), this, SLOT(finishCommand(Response *)));
40 37
     m_runningCommand->start();
41 38
   } else {
@@ -45,9 +42,6 @@ void Connection::startCommand() {
45 42
 
46 43
 void Connection::pendingLoadFinished(bool success) {
47 44
   m_pageSuccess = m_pageSuccess && success;
48  
-  if (m_commandWaiting) {
49  
-    startCommand();
50  
-  }
51 45
 }
52 46
 
53 47
 void Connection::writePageLoadFailure() {
2  src/Connection.h
@@ -31,7 +31,7 @@ class Connection : public QObject {
31 31
     WebPageManager *m_manager;
32 32
     CommandParser *m_commandParser;
33 33
     CommandFactory *m_commandFactory;
34  
-    PageLoadingCommand *m_runningCommand;
  34
+    Command *m_runningCommand;
35 35
     bool m_pageSuccess;
36 36
     bool m_commandWaiting;
37 37
     WebPage *currentPage();
9  src/GetTimeout.cpp
... ...
@@ -0,0 +1,9 @@
  1
+#include "GetTimeout.h"
  2
+#include "WebPageManager.h"
  3
+
  4
+GetTimeout::GetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
  5
+}
  6
+
  7
+void GetTimeout::start() {
  8
+  emit finished(new Response(true, QString::number(manager()->getTimeout())));
  9
+}
11  src/GetTimeout.h
... ...
@@ -0,0 +1,11 @@
  1
+#include "SocketCommand.h"
  2
+
  3
+class WebPageManager;
  4
+
  5
+class GetTimeout : public SocketCommand {
  6
+  Q_OBJECT;
  7
+
  8
+ public:
  9
+  GetTimeout(WebPageManager *page, QStringList &arguments, QObject *parent = 0);
  10
+  virtual void start();
  11
+};
19  src/SetTimeout.cpp
... ...
@@ -0,0 +1,19 @@
  1
+#include "SetTimeout.h"
  2
+#include "WebPageManager.h"
  3
+
  4
+SetTimeout::SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
  5
+}
  6
+
  7
+void SetTimeout::start() {
  8
+  QString timeoutString = arguments()[0];
  9
+  bool ok;
  10
+  int timeout = timeoutString.toInt(&ok);
  11
+
  12
+  if (ok) {
  13
+    manager()->setTimeout(timeout);
  14
+    emit finished(new Response(true));
  15
+  } else {
  16
+    emit finished(new Response(false, QString("Invalid value for timeout")));
  17
+  }
  18
+}
  19
+
9  src/SetTimeout.h
... ...
@@ -0,0 +1,9 @@
  1
+#include "SocketCommand.h"
  2
+
  3
+class SetTimeout : public SocketCommand {
  4
+  Q_OBJECT
  5
+
  6
+ public:
  7
+  SetTimeout(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
  8
+  virtual void start();
  9
+};
59  src/TimeoutCommand.cpp
... ...
@@ -0,0 +1,59 @@
  1
+#include "TimeoutCommand.h"
  2
+#include "Command.h"
  3
+#include "WebPageManager.h"
  4
+#include "WebPage.h"
  5
+#include <QTimer>
  6
+
  7
+TimeoutCommand::TimeoutCommand(Command *command, WebPageManager *manager, QObject *parent) : Command(parent) {
  8
+  m_command = command;
  9
+  m_manager = manager;
  10
+  m_timer = new QTimer(this);
  11
+  m_timer->setSingleShot(true);
  12
+  connect(m_timer, SIGNAL(timeout()), this, SLOT(commandTimeout()));
  13
+  connect(m_manager, SIGNAL(loadStarted()), this, SLOT(pageLoadingFromCommand()));
  14
+}
  15
+
  16
+void TimeoutCommand::start() {
  17
+  if (m_manager->isLoading()) {
  18
+    connect(m_manager, SIGNAL(pageFinished(bool)), this, SLOT(pendingLoadFinished(bool)));
  19
+    startTimeout();
  20
+  } else {
  21
+    startCommand();
  22
+  }
  23
+}
  24
+
  25
+void TimeoutCommand::startCommand() {
  26
+  connect(m_command, SIGNAL(finished(Response *)), this, SLOT(commandFinished(Response *)));
  27
+  m_command->start();
  28
+}
  29
+
  30
+void TimeoutCommand::startTimeout() {
  31
+  int timeout = m_manager->getTimeout();
  32
+  if (timeout > 0) {
  33
+    m_timer->start(timeout * 1000);
  34
+  }
  35
+}
  36
+
  37
+void TimeoutCommand::pendingLoadFinished(bool success) {
  38
+  if (success) {
  39
+    startCommand();
  40
+  } else {
  41
+    emit finished(new Response(false, m_manager->currentPage()->failureString()));
  42
+  }
  43
+}
  44
+
  45
+void TimeoutCommand::pageLoadingFromCommand() {
  46
+  startTimeout();
  47
+}
  48
+
  49
+void TimeoutCommand::commandTimeout() {
  50
+  m_manager->currentPage()->triggerAction(QWebPage::Stop);
  51
+  m_command->deleteLater();
  52
+  emit finished(new Response(false, QString("timeout")));
  53
+}
  54
+
  55
+void TimeoutCommand::commandFinished(Response *response) {
  56
+  m_command->deleteLater();
  57
+  emit finished(response);
  58
+}
  59
+
41  src/TimeoutCommand.h
... ...
@@ -0,0 +1,41 @@
  1
+#include "Command.h"
  2
+#include <QObject>
  3
+#include <QStringList>
  4
+
  5
+class Response;
  6
+class WebPageManager;
  7
+class QTimer;
  8
+
  9
+/* Decorates a command with a timeout.
  10
+ *
  11
+ * If the timeout, using a QTimer is reached before
  12
+ * the command is finished, the load page load will
  13
+ * be stopped and failure response will be issued.
  14
+ *
  15
+ */
  16
+class TimeoutCommand : public Command {
  17
+  Q_OBJECT
  18
+ 
  19
+  public:
  20
+   TimeoutCommand(Command *command, WebPageManager *page, QObject *parent = 0);
  21
+  virtual void start();
  22
+
  23
+  public slots:
  24
+    void commandTimeout();
  25
+    void commandFinished(Response *response);
  26
+    void pageLoadingFromCommand();
  27
+    void pendingLoadFinished(bool);
  28
+
  29
+  signals:
  30
+    void finished(Response *response);
  31
+
  32
+  protected:
  33
+    void startCommand();
  34
+    void startTimeout();
  35
+
  36
+  private:
  37
+    WebPageManager *m_manager;
  38
+    QTimer *m_timer;
  39
+    Command *m_command;
  40
+};
  41
+ 
10  src/WebPageManager.cpp
@@ -8,6 +8,7 @@ WebPageManager::WebPageManager(QObject *parent) : QObject(parent) {
8 8
   m_success = true;
9 9
   m_loggingEnabled = false;
10 10
   m_ignoredOutput = new QString();
  11
+  m_timeout = -1;
11 12
   createPage(this)->setFocus();
12 13
 }
13 14
 
@@ -85,7 +86,16 @@ bool WebPageManager::ignoreSslErrors() {
85 86
   return m_ignoreSslErrors;
86 87
 }
87 88
 
  89
+int WebPageManager::getTimeout() {
  90
+  return m_timeout;
  91
+}
  92
+
  93
+void WebPageManager::setTimeout(int timeout) {
  94
+  m_timeout = timeout;
  95
+}
  96
+
88 97
 void WebPageManager::reset() {
  98
+  m_timeout = -1;
89 99
   m_cookieJar->clearCookies();
90 100
   m_pages.first()->deleteLater();
91 101
   m_pages.clear();
3  src/WebPageManager.h
@@ -22,6 +22,8 @@ class WebPageManager : public QObject {
22 22
     WebPage *createPage(QObject *parent);
23 23
     void setIgnoreSslErrors(bool);
24 24
     bool ignoreSslErrors();
  25
+    void setTimeout(int);
  26
+    int getTimeout();
25 27
     void reset();
26 28
     NetworkCookieJar *cookieJar();
27 29
     bool isLoading() const;
@@ -50,6 +52,7 @@ class WebPageManager : public QObject {
50 52
     bool m_success;
51 53
     bool m_loggingEnabled;
52 54
     QString *m_ignoredOutput;
  55
+    int m_timeout;
53 56
 };
54 57
 
55 58
 #endif // _WEBPAGEMANAGER_H
3  src/find_command.h
@@ -39,3 +39,6 @@ CHECK_COMMAND(ClearPromptText)
39 39
 CHECK_COMMAND(JavascriptAlertMessages)
40 40
 CHECK_COMMAND(JavascriptConfirmMessages)
41 41
 CHECK_COMMAND(JavascriptPromptMessages)
  42
+CHECK_COMMAND(GetTimeout)
  43
+CHECK_COMMAND(SetTimeout)
  44
+
6  src/webkit_server.pro
@@ -53,6 +53,9 @@ HEADERS = \
53 53
   WindowFocus.h \
54 54
   GetWindowHandles.h \
55 55
   GetWindowHandle.h \
  56
+  GetTimeout.h \
  57
+  SetTimeout.h \
  58
+  TimeoutCommand.h \
56 59
 
57 60
 SOURCES = \
58 61
   EnableLogging.cpp \
@@ -102,11 +105,14 @@ SOURCES = \
102 105
   SetProxy.cpp \
103 106
   NullCommand.cpp \
104 107
   PageLoadingCommand.cpp \
  108
+  SetTimeout.cpp \
  109
+  GetTimeout.cpp \
105 110
   SetSkipImageLoading.cpp \
106 111
   WebPageManager.cpp \
107 112
   WindowFocus.cpp \
108 113
   GetWindowHandles.cpp \
109 114
   GetWindowHandle.cpp \
  115
+  TimeoutCommand.cpp \
110 116
 
111 117
 RESOURCES = webkit_server.qrc
112 118
 QT += network webkit

0 notes on commit cbb58d0

Please sign in to comment.
Something went wrong with that request. Please try again.