diff options
Diffstat (limited to 'chrome')
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() |