summaryrefslogtreecommitdiffstats
path: root/content/public/test/browser_test_utils.cc
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-14 18:42:07 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-14 18:42:07 +0000
commitc53ecc726e2fc38816eb60ad898f284577fb07a4 (patch)
tree6a22d11f9c6abf2817e54ed34d99c819652b80a6 /content/public/test/browser_test_utils.cc
parentd378f198bd6a0c56b4a30ea35d6dba8171430f35 (diff)
downloadchromium_src-c53ecc726e2fc38816eb60ad898f284577fb07a4.zip
chromium_src-c53ecc726e2fc38816eb60ad898f284577fb07a4.tar.gz
chromium_src-c53ecc726e2fc38816eb60ad898f284577fb07a4.tar.bz2
Move the corresponding cc files from content\test to be alongside their headers in content\public\test, now that DEPS supports fine-grained exceptions.
BUG=98716 Review URL: https://chromiumcodereview.appspot.com/10826311 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151523 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/public/test/browser_test_utils.cc')
-rw-r--r--content/public/test/browser_test_utils.cc499
1 files changed, 499 insertions, 0 deletions
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
new file mode 100644
index 0000000..e4b5b24
--- /dev/null
+++ b/content/public/test/browser_test_utils.cc
@@ -0,0 +1,499 @@
+// Copyright (c) 2012 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 "content/public/test/browser_test_utils.h"
+
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "base/test/test_timeouts.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_util.h"
+#include "content/public/browser/dom_operation_notification_details.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_view.h"
+#include "content/public/test/test_utils.h"
+#include "net/test/python_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static const int kDefaultWsPort = 8880;
+
+namespace content {
+
+namespace {
+
+class DOMOperationObserver : public NotificationObserver,
+ public WebContentsObserver {
+ public:
+ explicit DOMOperationObserver(RenderViewHost* render_view_host)
+ : WebContentsObserver(WebContents::FromRenderViewHost(render_view_host)),
+ did_respond_(false) {
+ registrar_.Add(this, NOTIFICATION_DOM_OPERATION_RESPONSE,
+ Source<RenderViewHost>(render_view_host));
+ message_loop_runner_ = new MessageLoopRunner;
+ }
+
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE {
+ DCHECK(type == NOTIFICATION_DOM_OPERATION_RESPONSE);
+ Details<DomOperationNotificationDetails> dom_op_details(details);
+ response_ = dom_op_details->json;
+ did_respond_ = true;
+ message_loop_runner_->Quit();
+ }
+
+ // Overridden from WebContentsObserver:
+ virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE {
+ message_loop_runner_->Quit();
+ }
+
+ bool WaitAndGetResponse(std::string* response) WARN_UNUSED_RESULT {
+ message_loop_runner_->Run();
+ *response = response_;
+ return did_respond_;
+ }
+
+ private:
+ NotificationRegistrar registrar_;
+ std::string response_;
+ bool did_respond_;
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(DOMOperationObserver);
+};
+
+// Specifying a prototype so that we can add the WARN_UNUSED_RESULT attribute.
+bool ExecuteJavaScriptHelper(RenderViewHost* render_view_host,
+ const std::wstring& frame_xpath,
+ const std::wstring& original_script,
+ scoped_ptr<Value>* result) WARN_UNUSED_RESULT;
+
+// Executes the passed |original_script| in the frame pointed to by
+// |frame_xpath|. If |result| is not NULL, stores the value that the evaluation
+// of the script in |result|. Returns true on success.
+bool ExecuteJavaScriptHelper(RenderViewHost* render_view_host,
+ const std::wstring& frame_xpath,
+ const std::wstring& original_script,
+ scoped_ptr<Value>* result) {
+ // TODO(jcampan): we should make the domAutomationController not require an
+ // automation id.
+ std::wstring script = L"window.domAutomationController.setAutomationId(0);" +
+ original_script;
+ DOMOperationObserver dom_op_observer(render_view_host);
+ render_view_host->ExecuteJavascriptInWebFrame(WideToUTF16Hack(frame_xpath),
+ WideToUTF16Hack(script));
+ std::string json;
+ if (!dom_op_observer.WaitAndGetResponse(&json)) {
+ DLOG(ERROR) << "Cannot communicate with DOMOperationObserver.";
+ return false;
+ }
+
+ // Nothing more to do for callers that ignore the returned JS value.
+ if (!result)
+ return true;
+
+ base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
+ result->reset(reader.ReadToValue(json));
+ if (!result->get()) {
+ DLOG(ERROR) << reader.GetErrorMessage();
+ return false;
+ }
+
+ return true;
+}
+
+void BuildSimpleWebKeyEvent(WebKit::WebInputEvent::Type type,
+ ui::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command,
+ NativeWebKeyboardEvent* event) {
+ event->nativeKeyCode = 0;
+ event->windowsKeyCode = key;
+ event->setKeyIdentifierFromWindowsKeyCode();
+ event->type = type;
+ event->modifiers = 0;
+ event->isSystemKey = false;
+ event->timeStampSeconds = base::Time::Now().ToDoubleT();
+ event->skip_in_browser = true;
+
+ if (type == WebKit::WebInputEvent::Char ||
+ type == WebKit::WebInputEvent::RawKeyDown) {
+ event->text[0] = key;
+ event->unmodifiedText[0] = key;
+ }
+
+ if (control)
+ event->modifiers |= WebKit::WebInputEvent::ControlKey;
+
+ if (shift)
+ event->modifiers |= WebKit::WebInputEvent::ShiftKey;
+
+ if (alt)
+ event->modifiers |= WebKit::WebInputEvent::AltKey;
+
+ if (command)
+ event->modifiers |= WebKit::WebInputEvent::MetaKey;
+}
+
+} // namespace
+
+
+GURL GetFileUrlWithQuery(const FilePath& path,
+ const std::string& query_string) {
+ GURL url = net::FilePathToFileURL(path);
+ if (!query_string.empty()) {
+ GURL::Replacements replacements;
+ replacements.SetQueryStr(query_string);
+ return url.ReplaceComponents(replacements);
+ }
+ return url;
+}
+
+void WaitForLoadStop(WebContents* web_contents) {
+ WindowedNotificationObserver load_stop_observer(
+ NOTIFICATION_LOAD_STOP,
+ Source<NavigationController>(&web_contents->GetController()));
+ // In many cases, the load may have finished before we get here. Only wait if
+ // the tab still has a pending navigation.
+ if (!web_contents->IsLoading())
+ return;
+ load_stop_observer.Wait();
+}
+
+void CrashTab(WebContents* web_contents) {
+ RenderProcessHost* rph = web_contents->GetRenderProcessHost();
+ WindowedNotificationObserver observer(
+ NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ Source<RenderProcessHost>(rph));
+ base::KillProcess(rph->GetHandle(), 0, false);
+ observer.Wait();
+}
+
+void SimulateMouseClick(WebContents* web_contents) {
+ int x = web_contents->GetView()->GetContainerSize().width() / 2;
+ int y = web_contents->GetView()->GetContainerSize().height() / 2;
+ WebKit::WebMouseEvent mouse_event;
+ mouse_event.type = WebKit::WebInputEvent::MouseDown;
+ mouse_event.button = WebKit::WebMouseEvent::ButtonLeft;
+ mouse_event.x = x;
+ mouse_event.y = y;
+ // Mac needs globalX/globalY for events to plugins.
+ gfx::Rect offset;
+ web_contents->GetView()->GetContainerBounds(&offset);
+ mouse_event.globalX = x + offset.x();
+ mouse_event.globalY = y + offset.y();
+ mouse_event.clickCount = 1;
+ web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
+ mouse_event.type = WebKit::WebInputEvent::MouseUp;
+ web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
+}
+
+void SimulateMouseEvent(WebContents* web_contents,
+ WebKit::WebInputEvent::Type type,
+ const gfx::Point& point) {
+ WebKit::WebMouseEvent mouse_event;
+ mouse_event.type = type;
+ mouse_event.x = point.x();
+ mouse_event.y = point.y();
+ web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event);
+}
+
+void SimulateKeyPress(WebContents* web_contents,
+ ui::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command) {
+ NativeWebKeyboardEvent event_down;
+ BuildSimpleWebKeyEvent(
+ WebKit::WebInputEvent::RawKeyDown, key, control, shift, alt, command,
+ &event_down);
+ web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event_down);
+
+ NativeWebKeyboardEvent char_event;
+ BuildSimpleWebKeyEvent(
+ WebKit::WebInputEvent::Char, key, control, shift, alt, command,
+ &char_event);
+ web_contents->GetRenderViewHost()->ForwardKeyboardEvent(char_event);
+
+ NativeWebKeyboardEvent event_up;
+ BuildSimpleWebKeyEvent(
+ WebKit::WebInputEvent::KeyUp, key, control, shift, alt, command,
+ &event_up);
+ web_contents->GetRenderViewHost()->ForwardKeyboardEvent(event_up);
+}
+
+bool ExecuteJavaScript(RenderViewHost* render_view_host,
+ const std::wstring& frame_xpath,
+ const std::wstring& original_script) {
+ std::wstring script =
+ original_script + L";window.domAutomationController.send(0);";
+ return ExecuteJavaScriptHelper(render_view_host, frame_xpath, script, NULL);
+}
+
+bool ExecuteJavaScriptAndExtractInt(RenderViewHost* render_view_host,
+ const std::wstring& frame_xpath,
+ const std::wstring& script,
+ int* result) {
+ DCHECK(result);
+ scoped_ptr<Value> value;
+ if (!ExecuteJavaScriptHelper(render_view_host, frame_xpath, script, &value) ||
+ !value.get())
+ return false;
+
+ return value->GetAsInteger(result);
+}
+
+bool ExecuteJavaScriptAndExtractBool(RenderViewHost* render_view_host,
+ const std::wstring& frame_xpath,
+ const std::wstring& script,
+ bool* result) {
+ DCHECK(result);
+ scoped_ptr<Value> value;
+ if (!ExecuteJavaScriptHelper(render_view_host, frame_xpath, script, &value) ||
+ !value.get())
+ return false;
+
+ return value->GetAsBoolean(result);
+}
+
+bool ExecuteJavaScriptAndExtractString(RenderViewHost* render_view_host,
+ const std::wstring& frame_xpath,
+ const std::wstring& script,
+ std::string* result) {
+ DCHECK(result);
+ scoped_ptr<Value> value;
+ if (!ExecuteJavaScriptHelper(render_view_host, frame_xpath, script, &value) ||
+ !value.get())
+ return false;
+
+ return value->GetAsString(result);
+}
+
+TitleWatcher::TitleWatcher(WebContents* web_contents,
+ const string16& expected_title)
+ : web_contents_(web_contents),
+ expected_title_observed_(false),
+ quit_loop_on_observation_(false) {
+ EXPECT_TRUE(web_contents != NULL);
+ expected_titles_.push_back(expected_title);
+ notification_registrar_.Add(this,
+ NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
+ Source<WebContents>(web_contents));
+
+ // When navigating through the history, the restored NavigationEntry's title
+ // will be used. If the entry ends up having the same title after we return
+ // to it, as will usually be the case, the
+ // NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED will then be suppressed, since the
+ // NavigationEntry's title hasn't changed.
+ notification_registrar_.Add(
+ this,
+ NOTIFICATION_LOAD_STOP,
+ Source<NavigationController>(&web_contents->GetController()));
+}
+
+void TitleWatcher::AlsoWaitForTitle(const string16& expected_title) {
+ expected_titles_.push_back(expected_title);
+}
+
+TitleWatcher::~TitleWatcher() {
+}
+
+const string16& TitleWatcher::WaitAndGetTitle() {
+ if (expected_title_observed_)
+ return observed_title_;
+ quit_loop_on_observation_ = true;
+ message_loop_runner_ = new MessageLoopRunner;
+ message_loop_runner_->Run();
+ return observed_title_;
+}
+
+void TitleWatcher::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) {
+ WebContents* source_contents = Source<WebContents>(source).ptr();
+ ASSERT_EQ(web_contents_, source_contents);
+ } else if (type == NOTIFICATION_LOAD_STOP) {
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ ASSERT_EQ(&web_contents_->GetController(), controller);
+ } else {
+ FAIL() << "Unexpected notification received.";
+ }
+
+ std::vector<string16>::const_iterator it =
+ std::find(expected_titles_.begin(),
+ expected_titles_.end(),
+ web_contents_->GetTitle());
+ if (it == expected_titles_.end())
+ return;
+ observed_title_ = *it;
+ expected_title_observed_ = true;
+ if (quit_loop_on_observation_) {
+ // Only call Quit once, on first Observe:
+ quit_loop_on_observation_ = false;
+ message_loop_runner_->Quit();
+ }
+}
+
+TestWebSocketServer::TestWebSocketServer()
+ : started_(false),
+ port_(kDefaultWsPort),
+ secure_(false) {
+#if defined(OS_POSIX)
+ process_group_id_ = base::kNullProcessHandle;
+#endif
+}
+
+int TestWebSocketServer::UseRandomPort() {
+ port_ = base::RandInt(1024, 65535);
+ return port_;
+}
+
+void TestWebSocketServer::UseTLS() {
+ secure_ = true;
+}
+
+bool TestWebSocketServer::Start(const FilePath& root_directory) {
+ if (started_)
+ return true;
+ // Append CommandLine arguments after the server script, switches won't work.
+ scoped_ptr<CommandLine> cmd_line(CreateWebSocketServerCommandLine());
+ cmd_line->AppendArg("--server=start");
+ cmd_line->AppendArg("--chromium");
+ cmd_line->AppendArg("--register_cygwin");
+ cmd_line->AppendArgNative(FILE_PATH_LITERAL("--root=") +
+ root_directory.value());
+ cmd_line->AppendArg("--port=" + base::IntToString(port_));
+ if (secure_)
+ cmd_line->AppendArg("--tls");
+ if (!temp_dir_.CreateUniqueTempDir()) {
+ LOG(ERROR) << "Unable to create a temporary directory.";
+ return false;
+ }
+ cmd_line->AppendArgNative(FILE_PATH_LITERAL("--output-dir=") +
+ temp_dir_.path().value());
+ websocket_pid_file_ = temp_dir_.path().AppendASCII("websocket.pid");
+ cmd_line->AppendArgNative(FILE_PATH_LITERAL("--pidfile=") +
+ websocket_pid_file_.value());
+ SetPythonPath();
+
+ base::LaunchOptions options;
+ base::ProcessHandle process_handle;
+
+#if defined(OS_POSIX)
+ options.new_process_group = true;
+#elif defined(OS_WIN)
+ job_handle_.Set(CreateJobObject(NULL, NULL));
+ if (!job_handle_.IsValid()) {
+ LOG(ERROR) << "Could not create JobObject.";
+ return false;
+ }
+
+ if (!base::SetJobObjectAsKillOnJobClose(job_handle_.Get())) {
+ LOG(ERROR) << "Could not SetInformationJobObject.";
+ return false;
+ }
+
+ options.inherit_handles = true;
+ options.job_handle = job_handle_.Get();
+#endif
+
+ // Launch a new WebSocket server process.
+ if (!base::LaunchProcess(*cmd_line.get(), options, &process_handle)) {
+ LOG(ERROR) << "Unable to launch websocket server:\n"
+ << cmd_line.get()->GetCommandLineString();
+ return false;
+ }
+#if defined(OS_POSIX)
+ process_group_id_ = process_handle;
+#endif
+ int exit_code;
+ bool wait_success = base::WaitForExitCodeWithTimeout(
+ process_handle,
+ &exit_code,
+ TestTimeouts::action_max_timeout());
+ base::CloseProcessHandle(process_handle);
+
+ if (!wait_success || exit_code != 0) {
+ LOG(ERROR) << "Failed to run new-run-webkit-websocketserver: "
+ << "wait_success = " << wait_success << ", "
+ << "exit_code = " << exit_code << ", "
+ << "command_line = " << cmd_line.get()->GetCommandLineString();
+ return false;
+ }
+
+ started_ = true;
+ return true;
+}
+
+CommandLine* TestWebSocketServer::CreatePythonCommandLine() {
+ // Note: Python's first argument must be the script; do not append CommandLine
+ // switches, as they would precede the script path and break this CommandLine.
+ FilePath path;
+ CHECK(GetPythonRunTime(&path));
+ return new CommandLine(path);
+}
+
+void TestWebSocketServer::SetPythonPath() {
+ FilePath scripts_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &scripts_path);
+
+ scripts_path = scripts_path
+ .Append(FILE_PATH_LITERAL("third_party"))
+ .Append(FILE_PATH_LITERAL("WebKit"))
+ .Append(FILE_PATH_LITERAL("Tools"))
+ .Append(FILE_PATH_LITERAL("Scripts"));
+ AppendToPythonPath(scripts_path);
+}
+
+CommandLine* TestWebSocketServer::CreateWebSocketServerCommandLine() {
+ FilePath src_path;
+ // Get to 'src' dir.
+ PathService::Get(base::DIR_SOURCE_ROOT, &src_path);
+
+ FilePath script_path(src_path);
+ script_path = script_path.AppendASCII("third_party");
+ script_path = script_path.AppendASCII("WebKit");
+ script_path = script_path.AppendASCII("Tools");
+ script_path = script_path.AppendASCII("Scripts");
+ script_path = script_path.AppendASCII("new-run-webkit-websocketserver");
+
+ CommandLine* cmd_line = CreatePythonCommandLine();
+ cmd_line->AppendArgPath(script_path);
+ return cmd_line;
+}
+
+TestWebSocketServer::~TestWebSocketServer() {
+ if (!started_)
+ return;
+ // Append CommandLine arguments after the server script, switches won't work.
+ scoped_ptr<CommandLine> cmd_line(CreateWebSocketServerCommandLine());
+ cmd_line->AppendArg("--server=stop");
+ cmd_line->AppendArg("--chromium");
+ cmd_line->AppendArgNative(FILE_PATH_LITERAL("--pidfile=") +
+ websocket_pid_file_.value());
+ base::LaunchOptions options;
+ options.wait = true;
+ base::LaunchProcess(*cmd_line.get(), options, NULL);
+
+#if defined(OS_POSIX)
+ // Just to make sure that the server process terminates certainly.
+ if (process_group_id_ != base::kNullProcessHandle)
+ base::KillProcessGroup(process_group_id_);
+#endif
+}
+
+} // namespace content