summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/chrome_tests.gypi8
-rw-r--r--chrome/test/webdriver/commands/command.cc18
-rw-r--r--chrome/test/webdriver/commands/command.h11
-rw-r--r--chrome/test/webdriver/commands/create_session.cc20
-rw-r--r--chrome/test/webdriver/commands/create_session.h2
-rw-r--r--chrome/test/webdriver/commands/navigate_commands.cc40
-rw-r--r--chrome/test/webdriver/commands/navigate_commands.h72
-rw-r--r--chrome/test/webdriver/commands/response.h36
-rw-r--r--chrome/test/webdriver/commands/session_with_id.cc1
-rw-r--r--chrome/test/webdriver/commands/session_with_id.h2
-rw-r--r--chrome/test/webdriver/commands/source_command.cc40
-rw-r--r--chrome/test/webdriver/commands/source_command.h37
-rw-r--r--chrome/test/webdriver/commands/title_command.cc24
-rw-r--r--chrome/test/webdriver/commands/title_command.h36
-rw-r--r--chrome/test/webdriver/commands/url_command.cc44
-rw-r--r--chrome/test/webdriver/commands/url_command.h39
-rw-r--r--chrome/test/webdriver/commands/webdriver_command.cc3
-rw-r--r--chrome/test/webdriver/commands/webdriver_command.h3
-rw-r--r--chrome/test/webdriver/dispatch.cc16
-rw-r--r--chrome/test/webdriver/dispatch.h2
-rw-r--r--chrome/test/webdriver/error_codes.h5
-rw-r--r--chrome/test/webdriver/keymap.cc9
-rw-r--r--chrome/test/webdriver/keymap.h4
-rw-r--r--chrome/test/webdriver/server.cc35
-rw-r--r--chrome/test/webdriver/session.cc140
-rw-r--r--chrome/test/webdriver/session.h28
-rw-r--r--chrome/test/webdriver/session_manager.cc92
-rw-r--r--chrome/test/webdriver/session_manager.h2
-rw-r--r--chrome/test/webdriver/utility_functions.cc1
-rw-r--r--chrome/test/webdriver/utility_functions.h2
-rw-r--r--chrome/test/webdriver/webdriver_remote_tests.py182
31 files changed, 737 insertions, 217 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index fbf0eede..93430ae 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -666,9 +666,17 @@
'test/webdriver/commands/command.cc',
'test/webdriver/commands/create_session.h',
'test/webdriver/commands/create_session.cc',
+ 'test/webdriver/commands/navigate_commands.h',
+ 'test/webdriver/commands/navigate_commands.cc',
'test/webdriver/commands/response.h',
'test/webdriver/commands/session_with_id.h',
'test/webdriver/commands/session_with_id.cc',
+ 'test/webdriver/commands/source_command.h',
+ 'test/webdriver/commands/source_command.cc',
+ 'test/webdriver/commands/title_command.h',
+ 'test/webdriver/commands/title_command.cc',
+ 'test/webdriver/commands/url_command.h',
+ 'test/webdriver/commands/url_command.cc',
'test/webdriver/commands/webdriver_command.h',
'test/webdriver/commands/webdriver_command.cc',
],
diff --git a/chrome/test/webdriver/commands/command.cc b/chrome/test/webdriver/commands/command.cc
index 3f31867..d735295 100644
--- a/chrome/test/webdriver/commands/command.cc
+++ b/chrome/test/webdriver/commands/command.cc
@@ -6,13 +6,31 @@
namespace webdriver {
+// Error message taken from:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
const char* const Response::kStatusKey = "status";
const char* const Response::kValueKey = "value";
+const char* const Response::kMessageKey = "message";
+const char* const Response::kScreenKey = "screen";
+const char* const Response::kClassKey = "class";
+const char* const Response::kStackTrace = "stackTrace";
+const char* const Response::kFileName = "fileName";
+const char* const Response::kLineNumber = "lineNumber";
bool Command::GetStringASCIIParameter(const std::string& key,
std::string* out) const {
return parameters_.get() != NULL && parameters_->GetStringASCII(key, out);
}
+bool Command::GetBooleanParameter(const std::string& key,
+ bool* out) const {
+ return parameters_.get() != NULL && parameters_->GetBoolean(key, out);
+}
+
+bool Command::GetIntegerParameter(const std::string& key,
+ int* out) const {
+ return parameters_.get() != NULL && parameters_->GetInteger(key, out);
+}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/commands/command.h b/chrome/test/webdriver/commands/command.h
index 11674fb3..0969f49 100644
--- a/chrome/test/webdriver/commands/command.h
+++ b/chrome/test/webdriver/commands/command.h
@@ -59,11 +59,22 @@ class Command {
// false if there is no such parameter, or if it is not a string.
bool GetStringASCIIParameter(const std::string& key, std::string* out) const;
+ // Returns the command parameter with the given |key| as a boolean. Returns
+ // false if there is no such parameter, or if it is not a boolean.
+ bool GetBooleanParameter(const std::string& key, bool* out) const;
+
+ // Returns the command parameter with the given |key| as a int. Returns
+ // false if there is no such parameter, or if it is not a int.
+ bool GetIntegerParameter(const std::string& key, int* out) const;
+
private:
const std::vector<std::string> path_segments_;
const scoped_ptr<const DictionaryValue> parameters_;
DISALLOW_COPY_AND_ASSIGN(Command);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_COMMAND_H_
+
diff --git a/chrome/test/webdriver/commands/create_session.cc b/chrome/test/webdriver/commands/create_session.cc
index ceb0e8c..42dd722 100644
--- a/chrome/test/webdriver/commands/create_session.cc
+++ b/chrome/test/webdriver/commands/create_session.cc
@@ -17,18 +17,20 @@ namespace webdriver {
void CreateSession::ExecutePost(Response* const response) {
SessionManager* session_manager = Singleton<SessionManager>::get();
std::string session_id;
- LOG(INFO) << "Creating new session";
- if (session_manager->Create(&session_id)) {
- LOG(INFO) << "Created session " << session_id;
- std::ostringstream stream;
- stream << "http://" << session_manager->GetIPAddress() << "/session/"
- << session_id;
- response->set_status(kSeeOther);
- response->set_value(Value::CreateStringValue(stream.str()));
- } else {
+
+ if (!session_manager->Create(&session_id)) {
response->set_status(kUnknownError);
response->set_value(Value::CreateStringValue("Failed to create session"));
+ return;
}
+
+ LOG(INFO) << "Created session " << session_id;
+ std::ostringstream stream;
+ stream << "http://" << session_manager->GetIPAddress() << "/session/"
+ << session_id;
+ response->set_status(kSeeOther);
+ response->set_value(Value::CreateStringValue(stream.str()));
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/commands/create_session.h b/chrome/test/webdriver/commands/create_session.h
index 2e5fa6d..1d7a81e 100644
--- a/chrome/test/webdriver/commands/create_session.h
+++ b/chrome/test/webdriver/commands/create_session.h
@@ -32,6 +32,8 @@ class CreateSession : public Command {
private:
DISALLOW_COPY_AND_ASSIGN(CreateSession);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_CREATE_SESSION_H_
diff --git a/chrome/test/webdriver/commands/navigate_commands.cc b/chrome/test/webdriver/commands/navigate_commands.cc
new file mode 100644
index 0000000..1e02bce
--- /dev/null
+++ b/chrome/test/webdriver/commands/navigate_commands.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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 "chrome/test/webdriver/commands/navigate_commands.h"
+
+namespace webdriver {
+
+void ForwardCommand::ExecutePost(Response* const response) {
+ if (!tab_->GoForward()) {
+ SET_WEBDRIVER_ERROR(response, "GoForward failed", kInternalServerError);
+ return;
+ }
+
+ session_->set_current_frame_xpath(L"");
+ response->set_status(kSuccess);
+}
+
+void BackCommand::ExecutePost(Response* const response) {
+ if (!tab_->GoBack()) {
+ SET_WEBDRIVER_ERROR(response, "GoBack failed", kInternalServerError);
+ return;
+ }
+
+ session_->set_current_frame_xpath(L"");
+ response->set_status(kSuccess);
+}
+
+void RefreshCommand::ExecutePost(Response* const response) {
+ if (!tab_->Reload()) {
+ SET_WEBDRIVER_ERROR(response, "Reload failed", kInternalServerError);
+ return;
+ }
+
+ session_->set_current_frame_xpath(L"");
+ response->set_status(kSuccess);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/navigate_commands.h b/chrome/test/webdriver/commands/navigate_commands.h
new file mode 100644
index 0000000..a913315
--- /dev/null
+++ b/chrome/test/webdriver/commands/navigate_commands.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_TEST_WEBDRIVER_COMMANDS_NAVIGATE_COMMANDS_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_NAVIGATE_COMMANDS_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Navigate forward in the browser history, if possible. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/forward
+class ForwardCommand : public WebDriverCommand {
+ public:
+ ForwardCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~ForwardCommand() {}
+
+ virtual bool DoesPost() { return true; }
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(ForwardCommand);
+};
+
+// Navigate backwards in the browser history, if possible. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/back
+class BackCommand : public WebDriverCommand {
+ public:
+ BackCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~BackCommand() {}
+
+ virtual bool DoesPost() { return true; }
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(BackCommand);
+};
+
+// Performs a reload on the current page. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/refresh
+class RefreshCommand : public WebDriverCommand {
+ public:
+ RefreshCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~RefreshCommand() {}
+
+ virtual bool DoesPost() { return true; }
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(RefreshCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_NAVIGATE_COMMANDS_H_
+
diff --git a/chrome/test/webdriver/commands/response.h b/chrome/test/webdriver/commands/response.h
index cfe6a08..645ded2 100644
--- a/chrome/test/webdriver/commands/response.h
+++ b/chrome/test/webdriver/commands/response.h
@@ -5,6 +5,7 @@
#ifndef CHROME_TEST_WEBDRIVER_COMMANDS_RESPONSE_H_
#define CHROME_TEST_WEBDRIVER_COMMANDS_RESPONSE_H_
+#include <sstream>
#include <string>
#include "base/basictypes.h"
@@ -14,6 +15,14 @@
namespace webdriver {
+// All errors in webdriver must use this macro in order to send back
+// a proper stack trace to the client
+#define SET_WEBDRIVER_ERROR(response, msg, err) { \
+ LOG(ERROR) << msg; \
+ response->set_error(msg, __FILE__, __LINE__); \
+ response->set_status(err); \
+}
+
// A simple class that encapsulates the information describing the response to
// a |Command|. In Webdriver all responses must be sent back as a JSON value,
// conforming to the spec found at:
@@ -35,7 +44,7 @@ class Response {
inline const Value* value() const {
Value* out = NULL;
LOG_IF(WARNING, !data_.Get(kValueKey, &out))
- << "Accessing unset response value."; // Should never happen.
+ << "Accessing unset response value."; // Should never happen.
return out;
}
@@ -45,6 +54,20 @@ class Response {
data_.Set(kValueKey, value);
}
+ // Sets the |value| of this response, assuming ownership of the object in the
+ // process. This function is mostly used to report error values.
+ inline void set_error(const char msg[], const char file[], const int line) {
+ DictionaryValue* error = new DictionaryValue();
+ DictionaryValue* stack = new DictionaryValue();
+
+ error->SetString(std::string(kMessageKey), msg);
+ stack->SetString(std::string(kFileName), std::string(file));
+ stack->SetInteger(std::string(kLineNumber), line);
+
+ error->Set(std::string(kStackTrace), stack);
+ data_.Set(kValueKey, error);
+ }
+
// Sets a JSON field in this response. The |key| may be a "." delimitted
// string to indicate the value should be set in a nested object. Any
// previously set value for the |key| will be deleted.
@@ -65,6 +88,14 @@ class Response {
static const char* const kStatusKey;
static const char* const kValueKey;
+ // Optional values used for errors.
+ static const char* const kMessageKey;
+ static const char* const kScreenKey;
+ static const char* const kClassKey;
+ static const char* const kStackTrace;
+ static const char* const kFileName;
+ static const char* const kLineNumber;
+
// The response status code. Stored outside of |data_| since it is
// likely to be queried often.
ErrorCode status_;
@@ -72,5 +103,8 @@ class Response {
DISALLOW_COPY_AND_ASSIGN(Response);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_RESPONSE_H_
+
diff --git a/chrome/test/webdriver/commands/session_with_id.cc b/chrome/test/webdriver/commands/session_with_id.cc
index e0c62d8..43000e7 100644
--- a/chrome/test/webdriver/commands/session_with_id.cc
+++ b/chrome/test/webdriver/commands/session_with_id.cc
@@ -44,5 +44,6 @@ void SessionWithID::ExecuteDelete(Response* const response) {
Singleton<SessionManager>::get()->Delete(session_->id());
response->set_status(kSuccess);
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/commands/session_with_id.h b/chrome/test/webdriver/commands/session_with_id.h
index f4fad98..a85bc5f 100644
--- a/chrome/test/webdriver/commands/session_with_id.h
+++ b/chrome/test/webdriver/commands/session_with_id.h
@@ -38,6 +38,8 @@ class SessionWithID : public WebDriverCommand {
DISALLOW_COPY_AND_ASSIGN(SessionWithID);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_SESSION_WITH_ID_H_
diff --git a/chrome/test/webdriver/commands/source_command.cc b/chrome/test/webdriver/commands/source_command.cc
new file mode 100644
index 0000000..54cb8b1
--- /dev/null
+++ b/chrome/test/webdriver/commands/source_command.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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 <string>
+
+#include "base/utf_string_conversions.h"
+#include "chrome/test/webdriver/utility_functions.h"
+#include "chrome/test/webdriver/commands/source_command.h"
+
+namespace webdriver {
+
+// Private atom to find source code of the page.
+const wchar_t* const kSource[] = {
+ L"window.domAutomationController.send(",
+ L"new XMLSerializer().serializeToString(document));",
+};
+
+void SourceCommand::ExecuteGet(Response* const response) {
+ const std::wstring jscript = build_atom(kSource, sizeof kSource);
+ // Get the source code for the current frame only.
+ std::wstring xpath = session_->current_frame_xpath();
+ std::wstring result = L"";
+
+ if (!tab_->ExecuteAndExtractString(xpath, jscript, &result)) {
+ LOG(ERROR) << "Could not execute JavaScript to find source. JavaScript"
+ << " used was:\n" << kSource;
+ LOG(ERROR) << "ExecuteAndExtractString's results was: "
+ << result;
+ SET_WEBDRIVER_ERROR(response, "ExecuteAndExtractString failed",
+ kInternalServerError);
+ return;
+ }
+
+ response->set_value(new StringValue(WideToUTF16(result)));
+ response->set_status(kSuccess);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/source_command.h b/chrome/test/webdriver/commands/source_command.h
new file mode 100644
index 0000000..ce9445b
--- /dev/null
+++ b/chrome/test/webdriver/commands/source_command.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_TEST_WEBDRIVER_COMMANDS_SOURCE_COMMAND_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_SOURCE_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Controls navigate to new web pages for the current tab. A call with
+// an HTTP GET will return the source of the tab. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/source
+class SourceCommand : public WebDriverCommand {
+ public:
+ inline SourceCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~SourceCommand() {}
+
+ virtual bool DoesGet() { return true; }
+ virtual void ExecuteGet(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(SourceCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_SOURCE_COMMAND_H_
+
diff --git a/chrome/test/webdriver/commands/title_command.cc b/chrome/test/webdriver/commands/title_command.cc
new file mode 100644
index 0000000..544aaa6
--- /dev/null
+++ b/chrome/test/webdriver/commands/title_command.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 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 <string>
+
+#include "base/utf_string_conversions.h"
+#include "chrome/test/webdriver/commands/title_command.h"
+
+namespace webdriver {
+
+void TitleCommand::ExecuteGet(Response* const response) {
+ std::wstring title;
+ if (!tab_->GetTabTitle(&title)) {
+ SET_WEBDRIVER_ERROR(response, "GetTabTitle failed", kInternalServerError);
+ return;
+ }
+
+ response->set_value(new StringValue(WideToUTF16(title)));
+ response->set_status(kSuccess);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/title_command.h b/chrome/test/webdriver/commands/title_command.h
new file mode 100644
index 0000000..c9925c2
--- /dev/null
+++ b/chrome/test/webdriver/commands/title_command.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_TEST_WEBDRIVER_COMMANDS_TITLE_COMMAND_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_TITLE_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// A call with HTTP GET will return the title of the tab. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/title
+class TitleCommand : public WebDriverCommand {
+ public:
+ inline TitleCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~TitleCommand() {}
+
+ virtual bool DoesGet() { return true; }
+ virtual void ExecuteGet(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(TitleCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_TITLE_COMMAND_H_
+
diff --git a/chrome/test/webdriver/commands/url_command.cc b/chrome/test/webdriver/commands/url_command.cc
new file mode 100644
index 0000000..4b716a8
--- /dev/null
+++ b/chrome/test/webdriver/commands/url_command.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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 <string>
+
+#include "chrome/test/webdriver/commands/url_command.h"
+#include "googleurl/src/gurl.h"
+
+namespace webdriver {
+
+void URLCommand::ExecuteGet(Response* const response) {
+ GURL url;
+ if (!tab_->GetCurrentURL(&url)) {
+ SET_WEBDRIVER_ERROR(response, "GetCurrentURL failed", kInternalServerError);
+ return;
+ }
+
+ response->set_value(new StringValue(url.spec()));
+ response->set_status(kSuccess);
+}
+
+void URLCommand::ExecutePost(Response* const response) {
+ std::string url;
+
+ if (!GetStringASCIIParameter("url", &url)) {
+ SET_WEBDRIVER_ERROR(response, "URL field not found", kInternalServerError);
+ return;
+ }
+ // TODO(jmikhail): sniff for meta-redirects.
+ GURL rgurl(url);
+ if (!tab_->NavigateToURL(rgurl)) {
+ SET_WEBDRIVER_ERROR(response, "NavigateToURL failed",
+ kInternalServerError);
+ return;
+ }
+
+ session_->set_current_frame_xpath(L"");
+ response->set_value(new StringValue(url));
+ response->set_status(kSuccess);
+}
+
+} // namespace webdriver
+
diff --git a/chrome/test/webdriver/commands/url_command.h b/chrome/test/webdriver/commands/url_command.h
new file mode 100644
index 0000000..db1b7fc
--- /dev/null
+++ b/chrome/test/webdriver/commands/url_command.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_TEST_WEBDRIVER_COMMANDS_URL_COMMAND_H_
+#define CHROME_TEST_WEBDRIVER_COMMANDS_URL_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/test/webdriver/commands/webdriver_command.h"
+
+namespace webdriver {
+
+// Controls navigate to new web pages for the current tab. A call with
+// and HTTP GET will return the URL of the tab. See:
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/url
+class URLCommand : public WebDriverCommand {
+ public:
+ inline URLCommand(const std::vector<std::string>& path_segments,
+ const DictionaryValue* const parameters)
+ : WebDriverCommand(path_segments, parameters) {}
+ virtual ~URLCommand() {}
+
+ virtual bool DoesGet() { return true; }
+ virtual bool DoesPost() { return true; }
+ virtual void ExecuteGet(Response* const response);
+ virtual void ExecutePost(Response* const response);
+
+ private:
+ virtual bool RequiresValidTab() { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(URLCommand);
+};
+
+} // namespace webdriver
+
+#endif // CHROME_TEST_WEBDRIVER_COMMANDS_URL_COMMAND_H_
+
diff --git a/chrome/test/webdriver/commands/webdriver_command.cc b/chrome/test/webdriver/commands/webdriver_command.cc
index 35a8efef..68a42f5 100644
--- a/chrome/test/webdriver/commands/webdriver_command.cc
+++ b/chrome/test/webdriver/commands/webdriver_command.cc
@@ -64,7 +64,7 @@ bool WebDriverCommand::Init(Response* const response) {
}
bool WebDriverCommand::VerifyTabIsValid(Response* response) {
- tab_ = session_->ActivateTab();
+ tab_ = session_->ActiveTab();
if (!tab_.get()) {
response->set_value(Value::CreateStringValue(
"Lost session window handle; did you close the window?"));
@@ -73,5 +73,6 @@ bool WebDriverCommand::VerifyTabIsValid(Response* response) {
}
return true;
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/commands/webdriver_command.h b/chrome/test/webdriver/commands/webdriver_command.h
index be04248..52500cf 100644
--- a/chrome/test/webdriver/commands/webdriver_command.h
+++ b/chrome/test/webdriver/commands/webdriver_command.h
@@ -54,8 +54,11 @@ class WebDriverCommand : public Command {
Session* session_;
scoped_refptr<TabProxy> tab_;
+
DISALLOW_COPY_AND_ASSIGN(WebDriverCommand);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_COMMANDS_WEBDRIVER_COMMAND_H_
diff --git a/chrome/test/webdriver/dispatch.cc b/chrome/test/webdriver/dispatch.cc
index af02a4a..20ddfe3 100644
--- a/chrome/test/webdriver/dispatch.cc
+++ b/chrome/test/webdriver/dispatch.cc
@@ -29,7 +29,7 @@ void SendHttpOk(struct mg_connection* const connection,
if (strcmp(request_info->request_method, "HEAD") != 0) {
out << json << "\r\n";
}
- LOG(INFO) << out.str() << std::endl;
+ LOG(INFO) << out.str();
mg_printf(connection, "%s", out.str().c_str());
}
@@ -48,7 +48,7 @@ void SendHttpSeeOther(struct mg_connection* const connection,
<< "Location: " << location << "\r\n"
<< "Content-Type: text/html\r\n"
<< "Content-Length: 0\r\n\r\n";
- LOG(INFO) << out.str() << std::endl;
+ LOG(INFO) << out.str();
mg_printf(connection, "%s", out.str().c_str());
}
@@ -66,7 +66,7 @@ void SendHttpBadRequest(struct mg_connection* const connection,
if (strcmp(request_info->request_method, "HEAD") != 0) {
out << json << "\r\n";
}
- LOG(INFO) << out.str() << std::endl;
+ LOG(INFO) << out.str();
mg_printf(connection, "%s", out.str().c_str());
}
@@ -84,7 +84,7 @@ void SendHttpNotFound(struct mg_connection* const connection,
if (strcmp(request_info->request_method, "HEAD") != 0) {
out << json << "\r\n";
}
- LOG(INFO) << out.str() << std::endl;
+ LOG(INFO) << out.str();
mg_printf(connection, "%s", out.str().c_str());
}
@@ -108,7 +108,7 @@ void SendHttpMethodNotAllowed(struct mg_connection* const connection,
<< "Content-Type: text/html\r\n"
<< "Content-Length: 0\r\n"
<< "Allow: " << JoinString(allowed_methods, ',') << "\r\n\r\n";
- LOG(INFO) << out.str() << std::endl;
+ LOG(INFO) << out.str();
mg_printf(connection, "%s", out.str().c_str());
}
@@ -126,7 +126,7 @@ void SendHttpInternalError(struct mg_connection* const connection,
if (strcmp(request_info->request_method, "HEAD") != 0) {
out << json << "\r\n";
}
- LOG(INFO) << out.str() << std::endl;
+ LOG(INFO) << out.str();
mg_printf(connection, "%s", out.str().c_str());
}
@@ -190,10 +190,12 @@ void SendResponse(struct mg_connection* const connection,
// All other errors should be treated as generic 500s. The client will be
// responsible for inspecting the message body for details.
- default:
+ case kInternalServerError:
+ default:
SendHttpInternalError(connection, request_info, response);
break;
}
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/dispatch.h b/chrome/test/webdriver/dispatch.h
index 8db3195..d85514a 100644
--- a/chrome/test/webdriver/dispatch.h
+++ b/chrome/test/webdriver/dispatch.h
@@ -73,6 +73,8 @@ void Dispatch(struct mg_connection* connection,
DispatchCommand(ptr.get(), method, &response);
SendResponse(connection, request_info, response);
}
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_DISPATCH_H_
diff --git a/chrome/test/webdriver/error_codes.h b/chrome/test/webdriver/error_codes.h
index f339ee0..ed5a83f 100644
--- a/chrome/test/webdriver/error_codes.h
+++ b/chrome/test/webdriver/error_codes.h
@@ -7,7 +7,7 @@
namespace webdriver {
// These are the error codes defined in the WebDriver wire protcol. For more
-// information, see
+// information, see:
// http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
enum ErrorCode {
kSuccess = 0,
@@ -27,7 +27,10 @@ enum ErrorCode {
kBadRequest = 400,
kSessionNotFound = 404,
kMethodNotAllowed = 405,
+ kInternalServerError = 500,
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_ERROR_CODES_H_
diff --git a/chrome/test/webdriver/keymap.cc b/chrome/test/webdriver/keymap.cc
index 640bf45..57bc171 100644
--- a/chrome/test/webdriver/keymap.cc
+++ b/chrome/test/webdriver/keymap.cc
@@ -78,7 +78,7 @@ KeyMap::KeyMap() {
keys_[L' '] = app::VKEY_SPACE;
- // Alpha keys match their ASCII values
+ // Alpha keys match their ASCII values.
for (int i = 0; i < 26; ++i) {
keys_[static_cast<wchar_t>(L'a' + i)] = \
static_cast<app::KeyboardCode>(app::VKEY_A + i);
@@ -86,7 +86,7 @@ KeyMap::KeyMap() {
static_cast<app::KeyboardCode>(app::VKEY_A + i);
}
- // Numeric keys match their ASCII values
+ // Numeric keys match their ASCII values.
for (int i = 0; i < 10; ++i) {
keys_[static_cast<wchar_t>(L'0' + i)] = \
static_cast<app::KeyboardCode>(app::VKEY_0 + i);
@@ -94,7 +94,7 @@ KeyMap::KeyMap() {
// The following all assumes the standard US keyboard.
// TODO(jmikhail): Lookup correct keycode based on the current system keyboard
- // layout. Right now it's fixed assuming standard ANSI
+ // layout. Right now it's fixed assuming standard ANSI.
keys_[L'='] = shifted_keys_[L'+'] = app::VKEY_OEM_PLUS;
keys_[L'-'] = shifted_keys_[L'_'] = app::VKEY_OEM_MINUS;
keys_[L';'] = shifted_keys_[L':'] = app::VKEY_OEM_1;
@@ -158,7 +158,7 @@ bool KeyMap::Press(const scoped_refptr<WindowProxy>& window,
modifiers = modifiers | views::Event::EF_COMMAND_DOWN;
}
- // TODO(jmikhail): need to be able to capture modifier key up
+ // TODO(jmikhail): need to be able to capture modifier key up.
window->SimulateOSKeyPress(key_code, modifiers);
return true;
@@ -170,5 +170,6 @@ void KeyMap::ClearModifiers() {
control_ = false;
command_ = false;
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/keymap.h b/chrome/test/webdriver/keymap.h
index e4b1594..b0abf46 100644
--- a/chrome/test/webdriver/keymap.h
+++ b/chrome/test/webdriver/keymap.h
@@ -22,7 +22,7 @@ class KeyMap {
const app::KeyboardCode key_code,
const wchar_t& key);
- // Sets the Shift, Alt, Cntl, and Cmd keys to not pressed
+ // Sets the Shift, Alt, Cntl, and Cmd keys to not pressed.
void ClearModifiers();
private:
@@ -34,6 +34,8 @@ class KeyMap {
std::map<wchar_t, app::KeyboardCode> shifted_keys_;
DISALLOW_COPY_AND_ASSIGN(KeyMap);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_KEYMAP_H_
diff --git a/chrome/test/webdriver/server.cc b/chrome/test/webdriver/server.cc
index cb72226..901a379 100644
--- a/chrome/test/webdriver/server.cc
+++ b/chrome/test/webdriver/server.cc
@@ -25,11 +25,15 @@
#include "chrome/test/webdriver/session_manager.h"
#include "chrome/test/webdriver/utility_functions.h"
#include "chrome/test/webdriver/commands/create_session.h"
+#include "chrome/test/webdriver/commands/navigate_commands.h"
#include "chrome/test/webdriver/commands/session_with_id.h"
+#include "chrome/test/webdriver/commands/source_command.h"
+#include "chrome/test/webdriver/commands/title_command.h"
+#include "chrome/test/webdriver/commands/url_command.h"
#include "third_party/mongoose/mongoose.h"
-// Make sure we have ho zombies from CGIs
+// Make sure we have ho zombies from CGIs.
static void
signal_handler(int sig_num) {
switch (sig_num) {
@@ -38,7 +42,7 @@ signal_handler(int sig_num) {
while (waitpid(-1, &sig_num, WNOHANG) > 0) { }
break;
#elif OS_WIN
- case 0: // The win compiler demands at least 1 case statement
+ case 0: // The win compiler demands at least 1 case statement.
#endif
default:
break;
@@ -52,17 +56,23 @@ void SetCallback(struct mg_context* ctx, const char* pattern) {
}
void InitCallbacks(struct mg_context* ctx) {
- SetCallback<CreateSession>(ctx, "/session");
+ SetCallback<CreateSession>(ctx, "/session");
+ SetCallback<BackCommand>(ctx, "/session/*/back");
+ SetCallback<ForwardCommand>(ctx, "/session/*/forward");
+ SetCallback<RefreshCommand>(ctx, "/session/*/refresh");
+ SetCallback<SourceCommand>(ctx, "/session/*/source");
+ SetCallback<TitleCommand>(ctx, "/session/*/title");
+ SetCallback<URLCommand>(ctx, "/session/*/url");
// Since the /session/* is a wild card that would match the above URIs, this
- // line MUST be the last registered URI with the server
+ // line MUST be the last registered URI with the server.
SetCallback<SessionWithID>(ctx, "/session/*");
}
} // namespace webdriver
// Sets up and runs the Mongoose HTTP server for the JSON over HTTP
// protcol of webdriver. The spec is located at:
-// http://code.google.com/p/selenium/wiki/JsonWireProtocol
+// http://code.google.com/p/selenium/wiki/JsonWireProtocol.
int main(int argc, char *argv[]) {
struct mg_context *ctx;
base::AtExitManager exit;
@@ -77,6 +87,9 @@ int main(int argc, char *argv[]) {
CommandLine& cmd_line = CommandLine::FromString(ASCIIToWide(c));
#endif
+ // Init the commandline singleton from the env.
+ CommandLine::Init(0, NULL);
+
#if OS_POSIX
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, &signal_handler);
@@ -94,23 +107,19 @@ int main(int argc, char *argv[]) {
// Initialize SHTTPD context.
// Listen on port 8080 or port specified on command line.
- // TODO(jmikhail) Maybe add port 8081 as a secure connection
+ // TODO(jmikhail) Maybe add port 8081 as a secure connection.
ctx = mg_start();
mg_set_option(ctx, "ports", port.c_str());
webdriver::InitCallbacks(ctx);
std::cout << "Starting server" << std::endl;
- // the default behavior is to run this service forever
+ // The default behavior is to run this service forever.
while (true) {
-#ifdef OS_POSIX
- sleep(3600);
-#elif OS_WIN
- Sleep(3600);
-#endif
+ PlatformThread::Sleep(3600);
}
- // we should not reach here since the service should never quit
+ // We should not reach here since the service should never quit.
// TODO(jmikhail): register a listener for SIGTERM and break the
// message loop gracefully.
mg_stop(ctx);
diff --git a/chrome/test/webdriver/session.cc b/chrome/test/webdriver/session.cc
index 19c5a92..84d2cbd 100644
--- a/chrome/test/webdriver/session.cc
+++ b/chrome/test/webdriver/session.cc
@@ -5,7 +5,9 @@
#include "chrome/test/webdriver/session_manager.h"
#ifdef OS_POSIX
+#include <dirent.h>
#include <unistd.h>
+#include <sys/stat.h>
#endif
#ifdef OS_WIN
#include <windows.h>
@@ -13,13 +15,18 @@
#endif
#include <stdlib.h>
+#ifdef OS_POSIX
+#include <algorithm>
+#endif
#include <vector>
#include "base/command_line.h"
+#include "base/file_util.h"
#include "base/logging.h"
#include "base/process.h"
#include "base/process_util.h"
#include "base/string_util.h"
+#include "base/test/test_timeouts.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/utf_string_conversions.h"
@@ -27,6 +34,7 @@
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/test/test_launcher_utils.h"
#include "chrome/test/webdriver/utility_functions.h"
#include "third_party/webdriver/atoms.h"
@@ -38,12 +46,12 @@ namespace {
std::string GetTempPath() {
DWORD result = ::GetTempPath(0, L"");
if (result == 0)
- LOG(ERROR) << "Could not get system temp path" << std::endl;
+ LOG(ERROR) << "Could not get system temp path";
std::vector<TCHAR> tempPath(result + 1);
result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
if ((result == 0) || (result >= tempPath.size())) {
- LOG(ERROR) << "Could not get system temp path" << std::endl;
+ LOG(ERROR) << "Could not get system temp path";
NOTREACHED();
}
return std::string(tempPath.begin(),
@@ -52,33 +60,26 @@ std::string GetTempPath() {
} // namespace
#endif
-Session::Session(const std::string& id, AutomationProxy* proxy)
- : id_(id), proxy_(proxy), process_(base::kNullProcessHandle),
- window_num_(0), implicit_wait_(0), current_frame_xpath_(L"") {
+Session::Session(const std::string& id)
+ : UITestBase(), id_(id), window_num_(0), implicit_wait_(0),
+ current_frame_xpath_(L"") {
}
-bool Session::Init(base::ProcessHandle process_handle) {
- CHECK(process_.handle() == base::kNullProcessHandle)
- << "Session has already been initialized";
- process_.set_handle(process_handle);
-
- if (!proxy_->WaitForInitialLoads()) {
- LOG(WARNING) << "Failed to wait for initial loads" << std::endl;
- return false;
- }
-
- if (!WaitForLaunch()) {
- return false;
+bool Session::Init() {
+ // Create a temp directory for the new profile.
+ if (!CreateTemporaryProfileDirectory()) {
+ LOG(ERROR) << "Could not make a temp profile directory, "
+ << tmp_profile_dir() << std::endl
+ << "Need to quit, the issue must be fixed";
+ exit(-1);
}
+ SetupCommandLine();
+ LaunchBrowserAndServer();
return LoadProxies();
}
-scoped_refptr<WindowProxy> Session::GetWindow() {
- return ActivateTab() ? browser_->GetWindow() : NULL;
-}
-
-scoped_refptr<TabProxy> Session::ActivateTab() {
+scoped_refptr<TabProxy> Session::ActiveTab() {
int tab_index;
if (!tab_->GetTabIndex(&tab_index) ||
!browser_->ActivateTab(tab_index)) {
@@ -88,36 +89,17 @@ scoped_refptr<TabProxy> Session::ActivateTab() {
return tab_;
}
-bool Session::WaitForLaunch() {
- AutomationLaunchResult result = proxy_->WaitForAppLaunch();
- if (result == AUTOMATION_SUCCESS) {
- LOG(INFO) << "Automation setup is a success" << std::endl;
- } else if (result == AUTOMATION_TIMEOUT) {
- LOG(WARNING) << "Timeout, automation setup failed" << std::endl;
- return false;
- } else {
- LOG(WARNING) << "Failure in automation setup" << std::endl;
- return false;
- }
-
- LOG(INFO) << proxy_->channel_id() << std::endl;
- if (!proxy_->OpenNewBrowserWindow(Browser::TYPE_NORMAL, true)) {
- LOG(WARNING) << "Failed to open a new browser window" << std::endl;
- return false;
- }
- return true;
-}
-
bool Session::LoadProxies() {
- scoped_refptr<BrowserProxy> browser = proxy_->GetBrowserWindow(0);
+ AutomationProxy* proxy = automation();
+ scoped_refptr<BrowserProxy> browser = proxy->GetBrowserWindow(0);
if (!browser.get()) {
LOG(WARNING) << "Failed to get browser window.";
return false;
}
scoped_refptr<TabProxy> tab = browser->GetActiveTab();
- if (!tab->NavigateToURL(GURL("about://config/"))) {
- LOG(WARNING) << "Could not navigate to about://config/" << std::endl;
+ if (!tab.get()) {
+ LOG(ERROR) << "Could not load tab";
return false;
}
@@ -125,6 +107,15 @@ bool Session::LoadProxies() {
return true;
}
+void Session::SetupCommandLine() {
+ test_launcher_utils::PrepareBrowserCommandLineForTests(&launch_arguments_);
+ launch_arguments_.AppendSwitch(switches::kDomAutomationController);
+ launch_arguments_.AppendSwitch(switches::kFullMemoryCrashReport);
+
+ launch_arguments_.AppendSwitchASCII(switches::kUserDataDir,
+ tmp_profile_dir());
+}
+
void Session::SetBrowserAndTab(const int window_num,
const scoped_refptr<BrowserProxy>& browser,
const scoped_refptr<TabProxy>& tab) {
@@ -152,23 +143,23 @@ bool Session::CreateTemporaryProfileDirectory() {
ret = GetTempPathA(sizeof temp_dir, temp_dir);
if (ret == 0 || ret > sizeof temp_dir) {
- LOG(ERROR) << "Could not find the temp directory" << std::endl;
+ LOG(ERROR) << "Could not find the temp directory";
return false;
}
- ret = GetTempFileNameA(temp_dir, // directory for tmp files
- "webdriver", // temp file name prefix
+ ret = GetTempFileNameA(temp_dir, // Directory for tmp files.
+ "webdriver", // Temp file name prefix.
static_cast<int>(time(NULL)) % 65535 + 1,
- tmp_profile_dir_); // buffer for name
+ tmp_profile_dir_); // Buffer for name.
if (ret ==0) {
- LOG(ERROR) << "Could not generate temp directory name" << std::endl;
+ LOG(ERROR) << "Could not generate temp directory name";
return false;
}
if (!CreateDirectoryA(tmp_profile_dir_, NULL)) {
DWORD dw = GetLastError();
- LOG(ERROR) << "Error code: " << dw << std::endl;
+ LOG(ERROR) << "Error code: " << dw;
return false;
}
#endif
@@ -176,45 +167,17 @@ bool Session::CreateTemporaryProfileDirectory() {
return true;
}
-#ifdef OS_WIN
-bool DeleteDirectory(const char* directory) {
- SHFILEOPSTRUCT fileop;
- size_t convertedChars;
- int len = strlen(directory);
- wchar_t *pszFrom = new wchar_t[len+2];
- memset(&fileop, 0, sizeof fileop);
- memset(pszFrom, 0, sizeof pszFrom);
-
- mbstowcs_s(&convertedChars, pszFrom, len+2, directory, len);
- fileop.wFunc = FO_DELETE; // delete operation
- // source file name as double null terminated string
- fileop.pFrom = (LPCWSTR) pszFrom;
- fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user
- fileop.fAnyOperationsAborted = FALSE;
-
- int ret = SHFileOperation(&fileop);
- delete pszFrom;
- return (ret == 0);
-}
-#endif
-
void Session::Terminate() {
- if (!proxy_->SetFilteredInet(false)) {
- LOG(ERROR) << "Error in closing down session" << std::endl;
- }
-
-#ifdef OS_WIN
- if (!DeleteDirectory(tmp_profile_dir_)) {
- LOG(ERROR) << "Could not clean up temp directory: "
- << tmp_profile_dir_ << std::endl;
- }
-#else
- if (rmdir(tmp_profile_dir_)) {
+ QuitBrowser();
+#ifdef OS_POSIX
+ FilePath del_dir = FilePath(tmp_profile_dir());
+#elif OS_WIN
+ FilePath del_dir = FilePath(ASCIIToWide(tmp_profile_dir()));
+#endif
+ if (file_util::PathExists(del_dir) && !file_util::Delete(del_dir, true)) {
LOG(ERROR) << "Could not clean up temp directory: "
- << tmp_profile_dir_ << std::endl;
+ << tmp_profile_dir() << std::endl;
}
-#endif
- process_.Terminate(EXIT_SUCCESS);
}
ErrorCode Session::ExecuteScript(const std::wstring& script,
@@ -242,7 +205,7 @@ ErrorCode Session::ExecuteScript(const std::wstring& script,
LOG(INFO) << "Executing script in frame: " << current_frame_xpath_;
std::wstring result;
- scoped_refptr<TabProxy> tab = ActivateTab();
+ scoped_refptr<TabProxy> tab = ActiveTab();
if (!tab->ExecuteAndExtractString(current_frame_xpath_, jscript, &result)) {
*value = Value::CreateStringValue(
"Unknown internal script execution failure");
@@ -286,4 +249,5 @@ ErrorCode Session::ExecuteScript(const std::wstring& script,
}
return static_cast<ErrorCode>(status);
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/session.h b/chrome/test/webdriver/session.h
index d8f77cc..ad62833 100644
--- a/chrome/test/webdriver/session.h
+++ b/chrome/test/webdriver/session.h
@@ -13,6 +13,7 @@
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/ui/ui_test.h"
#include "chrome/test/webdriver/error_codes.h"
namespace webdriver {
@@ -20,7 +21,9 @@ namespace webdriver {
// Every connection made by WebDriver maps to a session object.
// This object creates the chrome instance and keeps track of the
// state necessary to control the chrome browser created.
-class Session {
+// TODO(phajdan.jr): Abstract UITestBase classes, see:
+// http://code.google.com/p/chromium/issues/detail?id=56865
+class Session : private UITestBase {
public:
#if defined(OS_POSIX)
typedef char ProfileDir[L_tmpnam + 1]; // +1 for \0
@@ -28,17 +31,16 @@ class Session {
typedef char ProfileDir[MAX_PATH + 1]; // +1 for \0
#endif
- Session(const std::string& id, AutomationProxy* proxy);
+ explicit Session(const std::string& id);
- bool Init(base::ProcessHandle process_handle);
- bool CreateTemporaryProfileDirectory();
+ bool Init();
// Terminates this session and disconnects its automation proxy. After
// invoking this method, the Session can safely be deleted.
void Terminate();
- scoped_refptr<WindowProxy> GetWindow();
- scoped_refptr<TabProxy> ActivateTab();
+ // Finds the active tab that webdriver commands should go to.
+ scoped_refptr<TabProxy> ActiveTab();
void SetBrowserAndTab(const int window_num,
const scoped_refptr<BrowserProxy>& browser,
@@ -54,9 +56,7 @@ class Session {
Value** value);
inline const std::string& id() const { return id_; }
- inline AutomationProxy* proxy() { return proxy_.get(); }
- inline int window_num() const { return window_num_; }
inline int implicit_wait() { return implicit_wait_; }
inline void set_implicit_wait(const int& timeout) {
implicit_wait_ = timeout > 0 ? timeout : 0;
@@ -75,13 +75,11 @@ class Session {
}
private:
+ bool CreateTemporaryProfileDirectory();
bool LoadProxies();
- bool WaitForLaunch();
- const std::string id_;
- const scoped_ptr<AutomationProxy> proxy_;
+ void SetupCommandLine();
- // The chrome process that was started for this session.
- base::Process process_;
+ const std::string id_;
int window_num_;
scoped_refptr<BrowserProxy> browser_;
@@ -95,11 +93,13 @@ class Session {
// down the tree (across multiple frame DOMs).
// Example, /html/body/table/tbody/tr/td/iframe\n/frameset/frame[1]
// should break into 2 xpaths
- // /html/body/table/tbody/tr/td/iframe & /frameset/frame[1]
+ // /html/body/table/tbody/tr/td/iframe & /frameset/frame[1].
std::wstring current_frame_xpath_;
DISALLOW_COPY_AND_ASSIGN(Session);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_SESSION_H_
diff --git a/chrome/test/webdriver/session_manager.cc b/chrome/test/webdriver/session_manager.cc
index a7ce731..245143a 100644
--- a/chrome/test/webdriver/session_manager.cc
+++ b/chrome/test/webdriver/session_manager.cc
@@ -24,6 +24,7 @@
#include "base/process_util.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
+#include "base/test/test_timeouts.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
@@ -51,7 +52,7 @@ std::string SessionManager::IPLookup(const std::string& nic) {
if (0 == ioctl(socket_conn, SIOCGIFADDR, &ifr)) {
return std::string(inet_ntoa(sin->sin_addr));
}
-#endif // cann't use else since a warning will be generated
+#endif // Cann't use else since a warning will be generated.
return std::string("");
}
@@ -87,11 +88,10 @@ bool SessionManager::SetIPAddress(const std::string& p) {
wVersionRequested = MAKEWORD(2, 0);
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
LOG(ERROR) << "Could not initialize the Windows Sockets library";
- LOG(ERROR) << std::endl;
return false;
}
if (gethostname(host, sizeof host) != 0) {
- LOG(ERROR) << "Could not find the hostname of this machine" << std::endl;
+ LOG(ERROR) << "Could not find the hostname of this machine";
WSACleanup();
return false;
}
@@ -116,22 +116,22 @@ std::string SessionManager::GenerateSessionID() {
"RSTUVWXYZ0123456789";
session_generation_.Acquire();
std::string id = "";
- count_++; // for every connection made increment the global count
+ count_++; // For every connection made increment the global count.
for (int i = 0; i < 32; ++i) {
#ifdef OS_POSIX
id += text[random() % (sizeof text - 1)];
#else
id += text[rand() % (sizeof text - 1)];
#endif
- id += count_; // append the global count to generate a unique id
+ id += count_; // Append the global count to generate a unique id.
}
session_generation_.Release();
return id;
}
bool SessionManager::Create(std::string* id) {
- FilePath browser_directory; // Path to the browser executable
MessageLoop loop;
+ TestTimeouts::Initialize();
*id = GenerateSessionID();
if (map_.find(*id) != map_.end()) {
@@ -139,83 +139,18 @@ bool SessionManager::Create(std::string* id) {
return false;
}
- // Init the commandline singleton from the env
- CommandLine::Init(0, NULL);
+ // Start chrome, if it doesn't startup quit.
+ const int ap_timeout = TestTimeouts::command_execution_timeout_ms();
+ LOG(INFO) << "Waiting for a max of " << ap_timeout << " ms to "
+ << "start the chrome browser";
- // start chrome, if it doesn't startup in 8 seconds quit
- scoped_ptr<Session> session(new Session(*id, new AutomationProxy(8000,
- false)));
- if (session->proxy() == NULL) {
- LOG(WARNING) << "Could not allocate automation proxy" << std::endl;
- return false;
- }
-
-#if defined(OS_POSIX)
- char* user_data_dir = getenv("CHROME_UI_TESTS_USER_DATA_DIR");
- if (user_data_dir) {
- browser_directory = browser_directory.Append(user_data_dir);
- }
-#endif
-
- FilePath command =
- browser_directory.Append(FilePath::FromWStringHack(
- chrome::kBrowserProcessExecutablePath));
- CommandLine command_line(command);
-
- // setup the automation port to communicate on
- command_line.AppendSwitchASCII(switches::kTestingChannelID,
- session->proxy()->channel_id());
-
- // No first-run dialogs, please.
- command_line.AppendSwitch(switches::kNoFirstRun);
-
- // No default browser check, it would create an info-bar (if we are
- // not the default browser) that could conflicts with some tests
- // expectations.
- command_line.AppendSwitch(switches::kNoDefaultBrowserCheck);
-
- // We need cookies on file:// for things like the page cycler.
- command_line.AppendSwitch(switches::kEnableFileCookies);
- command_line.AppendSwitch(switches::kDomAutomationController);
- command_line.AppendSwitch(switches::kFullMemoryCrashReport);
-
- // create a temp directory for the new profile
- if (!session->CreateTemporaryProfileDirectory()) {
- LOG(ERROR) << "Could not make a temp profile directory, "
- << session->tmp_profile_dir() << std::endl;
- LOG(ERROR) << "Need to quit, the issue must be fixed" << std::endl;
- exit(-1);
- }
-
- command_line.AppendSwitchASCII(switches::kUserDataDir,
- session->tmp_profile_dir());
-
- base::ProcessHandle process_handle;
-#if defined(OS_WIN)
- base::LaunchApp(command_line, false, true, &process_handle);
-#elif defined(OS_POSIX)
- // Sometimes one needs to run the browser under a special environment
- // (e.g. valgrind) without also running the test harness (e.g. python)
- // under the special environment. Provide a way to wrap the browser
- // commandline with a special prefix to invoke the special environment.
- const char* browser_wrapper = getenv("BROWSER_WRAPPER");
-
- if (browser_wrapper) {
- command_line.PrependWrapper(browser_wrapper);
- LOG(INFO) << "BROWSER_WRAPPER was set, prefixing command_line with "
- << browser_wrapper;
- }
+ scoped_ptr<Session> session(new Session(*id));
- base::LaunchApp(command_line.argv(), session->proxy()->fds_to_map(), false,
- &process_handle);
-#endif
-
- if (!session->Init(process_handle)) {
- LOG(WARNING) << "Failed to initialize session";
+ if (!session->Init()) {
+ LOG(ERROR) << "Could not establish a valid connection to the browser";
return false;
}
- LOG(INFO) << "New session was created: " << id << std::endl;
map_[*id] = session.release();
return true;
}
@@ -248,4 +183,5 @@ Session* SessionManager::GetSession(const std::string& id) const {
}
return it->second;
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/session_manager.h b/chrome/test/webdriver/session_manager.h
index 93c8ef1..e93843f 100644
--- a/chrome/test/webdriver/session_manager.h
+++ b/chrome/test/webdriver/session_manager.h
@@ -48,6 +48,8 @@ class SessionManager {
DISALLOW_COPY_AND_ASSIGN(SessionManager);
};
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_SESSION_MANAGER_H_
diff --git a/chrome/test/webdriver/utility_functions.cc b/chrome/test/webdriver/utility_functions.cc
index 0720f47..6744e67 100644
--- a/chrome/test/webdriver/utility_functions.cc
+++ b/chrome/test/webdriver/utility_functions.cc
@@ -84,5 +84,6 @@ bool ParseJSONDictionary(const std::string& json, DictionaryValue** dict,
*dict = static_cast<DictionaryValue*>(params);
return true;
}
+
} // namespace webdriver
diff --git a/chrome/test/webdriver/utility_functions.h b/chrome/test/webdriver/utility_functions.h
index 7f109f6..483f503 100644
--- a/chrome/test/webdriver/utility_functions.h
+++ b/chrome/test/webdriver/utility_functions.h
@@ -27,6 +27,8 @@ void CheckValueType(const Value::ValueType expected, const Value* const actual);
// memory in |dict|.
bool ParseJSONDictionary(const std::string& json, DictionaryValue** dict,
std::string* error);
+
} // namespace webdriver
+
#endif // CHROME_TEST_WEBDRIVER_UTILITY_FUNCTIONS_H_
diff --git a/chrome/test/webdriver/webdriver_remote_tests.py b/chrome/test/webdriver/webdriver_remote_tests.py
new file mode 100644
index 0000000..d12cc3b
--- /dev/null
+++ b/chrome/test/webdriver/webdriver_remote_tests.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+# Copyright (c) 2010 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.
+
+try:
+ import json
+except ImportError: # < 2.6
+ import simplejson as json
+
+import os
+import optparse
+import platform
+import string
+import subprocess
+import sys
+import time
+import unittest
+import urllib2
+from selenium.remote.webdriver import WebDriver
+from selenium.common.exceptions import ErrorInResponseException
+from urlparse import urlparse
+
+
+if sys.version_info < (2,6):
+ # Subprocess.Popen.kill is not available prior to 2.6.
+ if platform.system() == 'Windows':
+ import win32api
+ else:
+ import signal
+
+
+WEBDRIVER_EXE = os.path.abspath(os.path.join('.', 'chromedriver'))
+if platform.system() == 'Windows':
+ WEBDRIVER_EXE = '%s.exe' % WEBDRIVER_EXE
+WEBDRIVER_PORT = 8080
+WEBDRIVER_SERVER_URL = None
+WEBDRIVER_PROCESS = None
+
+if not WEBDRIVER_SERVER_URL:
+ WEBDRIVER_SERVER_URL = 'http://localhost:%d' % WEBDRIVER_PORT
+
+class TestNavigation(unittest.TestCase):
+
+ SEARCH = "http://www.google.com/webhp?hl=en"
+ NEWS = "http://www.google.com/news?hl=en"
+
+ """A new instance of chrome driver is started for every test case"""
+ def setUp(self):
+ global WEBDRIVER_SERVER_URL
+ global WEBDRIVER_PROCESS
+ WEBDRIVER_PROCESS = subprocess.Popen([WEBDRIVER_EXE,
+ '--port=%d' % WEBDRIVER_PORT])
+ if WEBDRIVER_PROCESS == None:
+ print "Chromium executable not found. The path used was: "
+ print WEBDRIVER_EXE
+ sys.exit(-1)
+
+ time.sleep(5)
+ self.driver = WebDriver.WebDriver(WEBDRIVER_SERVER_URL, "chrome", "ANY");
+ self.assertTrue(self.driver)
+
+ def tearDown(self):
+ global WEBDRIVER_PROCESS
+ self.driver.quit()
+ if WEBDRIVER_PROCESS:
+ if sys.version_info < (2,6):
+ # From http://stackoverflow.com/questions/1064335
+ if platform.system() == 'Windows':
+ PROCESS_TERMINATE = 1
+ handle = win32api.OpenProcess(PROCESS_TERMINATE, False,
+ WEBDRIVER_PROCESS.pid)
+ win32api.TerminateProcess(handle, -1)
+ win32api.CloseHandle(handle)
+ else:
+ os.kill(WEBDRIVER_PROCESS.pid, signal.SIGKILL)
+ else:
+ WEBDRIVER_PROCESS.kill()
+ WEBDRIVER_PROCESS = None
+
+ """Verifies that navigation to a specific page is correct by asserting on the
+ the reported URL. The function will not work with pages that redirect."""
+ def navigate(self, url):
+ self.driver.get(url)
+ self.assertURL(url)
+
+ def assertURL(self, url):
+ u = self.driver.get_current_url()
+ self.assertEqual(u, url)
+
+ """Preforms a string search ignoring case"""
+ def assertFind(self, text, search):
+ text = string.lower(text)
+ search = string.lower(search)
+ self.assertNotEqual(-1, string.find(text, search))
+
+ def testNavigateToURL(self):
+ # No redirects are allowed on the google home page.
+ self.navigate(self.SEARCH)
+
+ def testGoBackWithNoHistory(self):
+ # Go back one page with nothing to go back to.
+ self.assertRaises(ErrorInResponseException, self.driver.back)
+
+ def testGoForwardWithNoHistory(self):
+ # Go forward with nothing to move forward to.
+ self.assertRaises(ErrorInResponseException, self.driver.forward)
+
+ def testNavigation(self):
+ # Loads two pages into chrome's navigation history.
+ self.navigate(self.NEWS)
+ self.navigate(self.SEARCH)
+
+ # Go back to news.
+ self.driver.back();
+ self.assertURL(self.NEWS)
+
+ # Move forward to search.
+ self.driver.forward()
+ self.assertURL(self.SEARCH)
+
+ # Verify refresh keeps us on the current page.
+ self.driver.refresh()
+ self.assertURL(self.SEARCH)
+
+ # Go back to the previous URL and refresh making sure
+ # we dont change the page.
+ self.driver.back()
+ self.assertURL(self.NEWS)
+ self.driver.refresh()
+ self.assertURL(self.NEWS)
+
+ def testGetTitle(self):
+ self.navigate(self.SEARCH)
+ title = self.driver.get_title()
+ # The google name must always be in the search title.
+ self.assertFind(title, u"google")
+
+ def testGetSource(self):
+ self.navigate(self.SEARCH)
+ source = self.driver.get_page_source()
+ self.assertFind(source, u"document.body.style") # Basic javascript.
+ self.assertFind(source, u"feeling lucky") # Lucky button.
+ self.assertFind(source, u"google search") # Searh button.
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser('%prog [options]')
+ parser.add_option('-u', '--url', dest='url', action='store',
+ type='string', default=None,
+ help=('Specifies the URL of a remote WebDriver server to '
+ 'test against. If not specified, a server will be '
+ 'started on localhost according to the --exe and '
+ '--port flags'))
+ parser.add_option('-e', '--exe', dest='exe', action='store',
+ type='string', default=None,
+ help=('Path to the WebDriver server executable that should '
+ 'be started for testing; This flag is ignored if '
+ '--url is provided for a remote server.'))
+ parser.add_option('-p', '--port', dest='port', action='store',
+ type='int', default=8080,
+ help=('The port to start the WebDriver server executable '
+ 'on; This flag is ignored if --url is provided for a '
+ 'remote server.'))
+
+ (options, args) = parser.parse_args()
+ # Strip out our flags so unittest.main() correct parses the remaining.
+ sys.argv = sys.argv[:1]
+ sys.argv.extend(args)
+
+ if options.url:
+ WEBDRIVER_SERVER_URL = options.url
+ else:
+ if options.port:
+ WEBDRIVER_PORT = options.port
+ if options.exe:
+ WEBDRIVER_EXE = options.exe
+ if not os.path.exists(WEBDRIVER_EXE):
+ parser.error('WebDriver server executable not found:\n\t%s\n'
+ 'Please specify a valid path with the --exe flag.'
+ % WEBDRIVER_EXE)
+
+ unittest.main()