diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-14 18:42:07 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-14 18:42:07 +0000 |
commit | c53ecc726e2fc38816eb60ad898f284577fb07a4 (patch) | |
tree | 6a22d11f9c6abf2817e54ed34d99c819652b80a6 /content/public/test/browser_test_utils.cc | |
parent | d378f198bd6a0c56b4a30ea35d6dba8171430f35 (diff) | |
download | chromium_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.cc | 499 |
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 |