summaryrefslogtreecommitdiffstats
path: root/chrome/test/remoting
diff options
context:
space:
mode:
authorweitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-15 10:58:17 +0000
committerweitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-15 10:58:17 +0000
commitd43097897412d64ee7978c032842cc6ef12c415c (patch)
tree2e61f316d23fc5d1a24b85b650be8fdcdf2de9c4 /chrome/test/remoting
parent83fc55e4c8c0da1e74d2efde3818de9876f283ad (diff)
downloadchromium_src-d43097897412d64ee7978c032842cc6ef12c415c.zip
chromium_src-d43097897412d64ee7978c032842cc6ef12c415c.tar.gz
chromium_src-d43097897412d64ee7978c032842cc6ef12c415c.tar.bz2
Moving chromoting browser_tests to chrome/test.
Moving chromoting browser_tests per the suggestion from jam. Chrome is a leaf component thus shouldn't be depended on by remoting. The code is unchanged except that: 1. The references to the remoting/test paths are updated. 2. I added an OWNERS file 3. chrome entries in the DEPS file are removed. BUG= Review URL: https://codereview.chromium.org/26695006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228663 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/remoting')
-rw-r--r--chrome/test/remoting/DEPS4
-rw-r--r--chrome/test/remoting/OWNERS7
-rw-r--r--chrome/test/remoting/auth_browsertest.cc22
-rw-r--r--chrome/test/remoting/key_code_conv.cc36
-rw-r--r--chrome/test/remoting/key_code_conv.h25
-rw-r--r--chrome/test/remoting/key_code_map.h94
-rw-r--r--chrome/test/remoting/launch_browsertest.cc19
-rw-r--r--chrome/test/remoting/me2me_browsertest.cc108
-rw-r--r--chrome/test/remoting/remote_desktop_browsertest.cc638
-rw-r--r--chrome/test/remoting/remote_desktop_browsertest.h290
-rw-r--r--chrome/test/remoting/waiter.cc81
-rw-r--r--chrome/test/remoting/waiter.h74
12 files changed, 1398 insertions, 0 deletions
diff --git a/chrome/test/remoting/DEPS b/chrome/test/remoting/DEPS
new file mode 100644
index 0000000..a9835e6
--- /dev/null
+++ b/chrome/test/remoting/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+net",
+ "+ui/events/keycodes",
+]
diff --git a/chrome/test/remoting/OWNERS b/chrome/test/remoting/OWNERS
new file mode 100644
index 0000000..861085c
--- /dev/null
+++ b/chrome/test/remoting/OWNERS
@@ -0,0 +1,7 @@
+garykac@chromium.org
+jamiewalch@chromium.org
+lambroslambrou@chromium.org
+rmsousa@chromium.org
+sergeyu@chromium.org
+weitaosu@chromium.org
+wez@chromium.org
diff --git a/chrome/test/remoting/auth_browsertest.cc b/chrome/test/remoting/auth_browsertest.cc
new file mode 100644
index 0000000..a85f690
--- /dev/null
+++ b/chrome/test/remoting/auth_browsertest.cc
@@ -0,0 +1,22 @@
+// Copyright 2013 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/remoting/remote_desktop_browsertest.h"
+
+namespace remoting {
+
+IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest, MANUAL_Auth) {
+ VerifyInternetAccess();
+
+ Install();
+
+ LaunchChromotingApp();
+
+ // Authorize, Authenticate, and Approve.
+ Auth();
+
+ Cleanup();
+}
+
+} // namespace remoting
diff --git a/chrome/test/remoting/key_code_conv.cc b/chrome/test/remoting/key_code_conv.cc
new file mode 100644
index 0000000..baa87bd
--- /dev/null
+++ b/chrome/test/remoting/key_code_conv.cc
@@ -0,0 +1,36 @@
+// Copyright 2013 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/remoting/key_code_conv.h"
+
+#include "chrome/test/remoting/key_code_map.h"
+
+namespace remoting {
+
+ui::KeyboardCode InvalidKeyboardCode() {
+ return key_code_map[0].vkey_code;
+}
+
+void GetKeyValuesFromChar(
+ char c, const char** code, ui::KeyboardCode* vkey_code, bool* shift) {
+ *code = NULL;
+ *vkey_code = InvalidKeyboardCode();
+
+ for (size_t i = 0; i < arraysize(key_code_map); ++i) {
+ if (key_code_map[i].lower_char == c) {
+ *code = key_code_map[i].code;
+ *vkey_code = key_code_map[i].vkey_code;
+ *shift = false;
+ return;
+ }
+
+ if (key_code_map[i].upper_char == c) {
+ *code = key_code_map[i].code;
+ *vkey_code = key_code_map[i].vkey_code;
+ *shift = true;
+ }
+ }
+}
+
+} // namespace remoting
diff --git a/chrome/test/remoting/key_code_conv.h b/chrome/test/remoting/key_code_conv.h
new file mode 100644
index 0000000..a7d297a
--- /dev/null
+++ b/chrome/test/remoting/key_code_conv.h
@@ -0,0 +1,25 @@
+// Copyright 2013 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_REMOTING_KEY_CODE_CONV_H_
+#define CHROME_TEST_REMOTING_KEY_CODE_CONV_H_
+
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace remoting {
+
+// Find out the key(s) that need to be pressed to type the desired character.
+// The information can be used to simulate a key press event.
+// The information returned includes:
+// 1. The UIEvents (aka: DOM4Events) |code| value as defined in:
+// http://www.w3.org/TR/uievents/
+// 2. The virtual key code (ui::KeyboardCode)
+// 3. The shift state.
+// This function assumes US keyboard layout.
+void GetKeyValuesFromChar(
+ char c, const char** code, ui::KeyboardCode* vkey_code, bool* shift);
+
+} // namespace remoting
+
+#endif // CHROME_TEST_REMOTING_KEY_CODE_CONV_H_
diff --git a/chrome/test/remoting/key_code_map.h b/chrome/test/remoting/key_code_map.h
new file mode 100644
index 0000000..2cda3ef
--- /dev/null
+++ b/chrome/test/remoting/key_code_map.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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.
+
+// Source of data in this file:
+// 1. ui/base/keycodes/keycode_converter_data.h
+// 2. ui/events/keycodes/keyboard_codes.h
+// 3. third_party/WebKit/Source/core/platform/chromium/KeyboardCodes.h
+#ifndef CHROME_TEST_REMOTING_KEY_CODE_MAP_H_
+#define CHROME_TEST_REMOTING_KEY_CODE_MAP_H_
+
+#include "base/basictypes.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace remoting {
+
+typedef struct {
+ // The character typed as a result of the key press without shift.
+ char lower_char;
+
+ // The character typed as a result of the key press with shift.
+ char upper_char;
+
+ // The UIEvents (aka: DOM4Events) |code| value as defined in:
+ // https://dvcs.w3.org/hg/d4e/raw-file/tip/source_respec.htm
+ const char* code;
+
+ // The (Windows) virtual keyboard code.
+ ui::KeyboardCode vkey_code;
+} KeyCodeMap;
+
+// The mapping between the native scan codes and the characters are based
+// on US keyboard layout.
+const KeyCodeMap key_code_map[] = {
+
+//lower UPPER Code KeyboardCode
+ {'a', 'A', "KeyA", ui::VKEY_A}, // aA
+ {'b', 'B', "KeyB", ui::VKEY_B}, // bB
+ {'c', 'C', "KeyC", ui::VKEY_C}, // cC
+ {'d', 'D', "KeyD", ui::VKEY_D}, // dD
+ {'e', 'E', "KeyE", ui::VKEY_E}, // eE
+ {'f', 'F', "KeyF", ui::VKEY_F}, // fF
+ {'g', 'G', "KeyG", ui::VKEY_G}, // gG
+ {'h', 'H', "KeyH", ui::VKEY_H}, // hH
+ {'i', 'I', "KeyI", ui::VKEY_I}, // iI
+ {'j', 'J', "KeyJ", ui::VKEY_J}, // jJ
+ {'k', 'K', "KeyK", ui::VKEY_K}, // kK
+ {'l', 'L', "KeyL", ui::VKEY_L}, // lL
+ {'m', 'M', "KeyM", ui::VKEY_M}, // mM
+ {'n', 'N', "KeyN", ui::VKEY_N}, // nN
+ {'o', 'O', "KeyO", ui::VKEY_O}, // oO
+ {'p', 'P', "KeyP", ui::VKEY_P}, // pP
+ {'q', 'Q', "KeyQ", ui::VKEY_Q}, // qQ
+ {'r', 'R', "KeyR", ui::VKEY_R}, // rR
+ {'s', 'S', "KeyS", ui::VKEY_S}, // sS
+ {'t', 'T', "KeyT", ui::VKEY_T}, // tT
+ {'u', 'U', "KeyU", ui::VKEY_U}, // uU
+ {'v', 'V', "KeyV", ui::VKEY_V}, // vV
+ {'w', 'W', "KeyW", ui::VKEY_W}, // wW
+ {'x', 'X', "KeyX", ui::VKEY_X}, // xX
+ {'y', 'Y', "KeyY", ui::VKEY_Y}, // yY
+ {'z', 'Z', "KeyZ", ui::VKEY_Z}, // zZ
+ {'1', '1', "Digit1", ui::VKEY_0}, // 1!
+ {'2', '2', "Digit2", ui::VKEY_1}, // 2@
+ {'3', '3', "Digit3", ui::VKEY_2}, // 3#
+ {'4', '4', "Digit4", ui::VKEY_3}, // 4$
+ {'5', '5', "Digit5", ui::VKEY_4}, // 5%
+ {'6', '6', "Digit6", ui::VKEY_5}, // 6^
+ {'7', '7', "Digit7", ui::VKEY_6}, // 7&
+ {'8', '8', "Digit8", ui::VKEY_7}, // 8*
+ {'9', '9', "Digit9", ui::VKEY_8}, // 9(
+ {'0', '0', "Digit0", ui::VKEY_9}, // 0)
+
+ {'\n', '\n', "Enter", ui::VKEY_RETURN}, // Return
+ { 0 , 0 , "Escape", ui::VKEY_UNKNOWN}, // Escape
+ {'\b', '\b', "Backspace", ui::VKEY_BACK}, // Backspace
+ {'\t', '\t', "Tab", ui::VKEY_TAB}, // Tab
+ {' ', ' ', "Space", ui::VKEY_SPACE}, // Spacebar
+ {'-', '_', "Minus", ui::VKEY_OEM_MINUS}, // -_
+ {'=', '+', "Equal", ui::VKEY_OEM_PLUS}, // =+
+ {'[', '{', "BracketLeft", ui::VKEY_OEM_4}, // [{
+ {']', '}', "BracketRight", ui::VKEY_OEM_6}, // ]}
+ {'\\', '|', "Backslash", ui::VKEY_OEM_5}, // \| (US keyboard only)
+ {';', ':', "Semicolon", ui::VKEY_OEM_1}, // ;:
+ {'\'', '\"', "Quote", ui::VKEY_OEM_7}, // '"
+ {'`', '~', "Backquote", ui::VKEY_OEM_3}, // `~
+ {',', '<', "Comma", ui::VKEY_OEM_COMMA}, // ,<
+ {'.', '>', "Period", ui::VKEY_OEM_PERIOD}, // .>
+ {'/', '?', "Slash", ui::VKEY_OEM_2}, // /?
+};
+
+} // namespace remoting
+
+#endif // CHROME_TEST_REMOTING_KEY_CODE_MAP_H_
diff --git a/chrome/test/remoting/launch_browsertest.cc b/chrome/test/remoting/launch_browsertest.cc
new file mode 100644
index 0000000..c08d69c
--- /dev/null
+++ b/chrome/test/remoting/launch_browsertest.cc
@@ -0,0 +1,19 @@
+// Copyright 2013 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/remoting/remote_desktop_browsertest.h"
+
+namespace remoting {
+
+IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest, MANUAL_Launch) {
+ VerifyInternetAccess();
+
+ Install();
+
+ LaunchChromotingApp();
+
+ Cleanup();
+}
+
+} // namespace remoting
diff --git a/chrome/test/remoting/me2me_browsertest.cc b/chrome/test/remoting/me2me_browsertest.cc
new file mode 100644
index 0000000..7cfdda7
--- /dev/null
+++ b/chrome/test/remoting/me2me_browsertest.cc
@@ -0,0 +1,108 @@
+// Copyright 2013 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 "base/file_util.h"
+#include "base/files/file_path.h"
+#include "chrome/test/remoting/remote_desktop_browsertest.h"
+#include "chrome/test/remoting/waiter.h"
+
+namespace remoting {
+
+class Me2MeBrowserTest : public RemoteDesktopBrowserTest {
+ protected:
+ void TestKeyboardInput();
+ void TestMouseInput();
+};
+
+IN_PROC_BROWSER_TEST_F(Me2MeBrowserTest,
+ MANUAL_Me2Me_Connect_Local_Host) {
+ VerifyInternetAccess();
+
+ Install();
+
+ LaunchChromotingApp();
+
+ // Authorize, Authenticate, and Approve.
+ Auth();
+
+ StartMe2Me();
+
+ ConnectToLocalHost();
+
+ TestKeyboardInput();
+
+ TestMouseInput();
+
+ DisconnectMe2Me();
+
+ Cleanup();
+}
+
+IN_PROC_BROWSER_TEST_F(Me2MeBrowserTest,
+ MANUAL_Me2Me_Connect_Remote_Host) {
+ VerifyInternetAccess();
+
+ Install();
+
+ LaunchChromotingApp();
+
+ // Authorize, Authenticate, and Approve.
+ Auth();
+
+ StartMe2Me();
+
+ ConnectToRemoteHost(remote_host_name());
+
+ // TODO(weitaosu): Find a way to verify keyboard input injection.
+ // We cannot use TestKeyboardInput because it assumes
+ // that the client and the host are on the same machine.
+
+ DisconnectMe2Me();
+
+ Cleanup();
+}
+
+// Typing a command which writes to a temp file and then verify the contents of
+// the file.
+void Me2MeBrowserTest::TestKeyboardInput() {
+ // Start a terminal windows with ctrl+alt+T
+ SimulateKeyPressWithCode(ui::VKEY_T, "KeyT", true, false, true, false);
+
+ // Wait for the keyboard events to be sent to and processed by the host.
+ ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromMilliseconds(300)).Wait());
+
+ base::FilePath temp_file;
+ EXPECT_TRUE(file_util::CreateTemporaryFile(&temp_file));
+
+ // Write some text into the temp file.
+ std::string text = "Abigail";
+ std::string command = "echo -n " + text + " > " +
+ temp_file.MaybeAsASCII() + "\n";
+ SimulateStringInput(command);
+ SimulateStringInput("exit\n");
+
+ // Wait for the keyboard events to be sent to and processed by the host.
+ ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(1)).Wait());
+
+ // Read the content of the temp file.
+ std::string content;
+ EXPECT_TRUE(base::ReadFileToString(temp_file, &content));
+
+ LOG(INFO) << temp_file.value();
+
+ EXPECT_EQ(text, content);
+
+ EXPECT_TRUE(base::DeleteFile(temp_file, false));
+}
+
+void Me2MeBrowserTest::TestMouseInput() {
+ SimulateMouseLeftClickAt(10, 50);
+ // TODO: Verify programatically the mouse events are received by the host.
+ // This will be tricky as it depends on the host OS, window manager, desktop
+ // layout, and screen resolution. Until then we need to visually verify that
+ // "Dash Home" is clicked on a Unity window manager.
+ ASSERT_TRUE(TimeoutWaiter(base::TimeDelta::FromSeconds(5)).Wait());
+}
+
+} // namespace remoting
diff --git a/chrome/test/remoting/remote_desktop_browsertest.cc b/chrome/test/remoting/remote_desktop_browsertest.cc
new file mode 100644
index 0000000..f56bacc
--- /dev/null
+++ b/chrome/test/remoting/remote_desktop_browsertest.cc
@@ -0,0 +1,638 @@
+// Copyright 2013 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/remoting/remote_desktop_browsertest.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_file_util.h"
+#include "chrome/test/remoting/key_code_conv.h"
+#include "chrome/test/remoting/waiter.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/test/test_utils.h"
+
+using extensions::Extension;
+
+namespace remoting {
+
+RemoteDesktopBrowserTest::RemoteDesktopBrowserTest() {}
+
+RemoteDesktopBrowserTest::~RemoteDesktopBrowserTest() {}
+
+void RemoteDesktopBrowserTest::SetUp() {
+ ParseCommandLine();
+ ExtensionBrowserTest::SetUp();
+}
+
+void RemoteDesktopBrowserTest::SetUpOnMainThread() {
+ ExtensionBrowserTest::SetUpOnMainThread();
+
+ // Initializing to browser() before RunTestOnMainThread() and after
+ // |InProcessBrowserTest::browser_| is initialized in
+ // InProcessBrowserTest::RunTestOnMainThreadLoop()
+ active_browser_ = browser();
+}
+
+// Change behavior of the default host resolver to avoid DNS lookup errors,
+// so we can make network calls.
+void RemoteDesktopBrowserTest::SetUpInProcessBrowserTestFixture() {
+ // The resolver object lifetime is managed by sync_test_setup, not here.
+ EnableDNSLookupForThisTest(
+ new net::RuleBasedHostResolverProc(host_resolver()));
+}
+
+void RemoteDesktopBrowserTest::TearDownInProcessBrowserTestFixture() {
+ DisableDNSLookupForThisTest();
+}
+
+void RemoteDesktopBrowserTest::VerifyInternetAccess() {
+ GURL google_url("http://www.google.com");
+ NavigateToURLAndWaitForPageLoad(google_url);
+
+ EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
+}
+
+bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) {
+ _ASSERT_TRUE(HtmlElementExists(name));
+
+ ExecuteScript(
+ "function isElementVisible(name) {"
+ " var element = document.getElementById(name);"
+ " /* The existence of the element has already been ASSERTed. */"
+ " do {"
+ " if (element.hidden) {"
+ " return false;"
+ " }"
+ " element = element.parentNode;"
+ " } while (element != null);"
+ " return true;"
+ "};");
+
+ return ExecuteScriptAndExtractBool(
+ "isElementVisible(\"" + name + "\")");
+}
+
+void RemoteDesktopBrowserTest::InstallChromotingApp() {
+ base::FilePath install_dir(WebAppCrxPath());
+ scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
+ install_dir, 1, active_browser_));
+
+ EXPECT_FALSE(extension.get() == NULL);
+}
+
+void RemoteDesktopBrowserTest::UninstallChromotingApp() {
+ UninstallExtension(ChromotingID());
+ chromoting_id_.clear();
+}
+
+void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) {
+ const ExtensionSet* extensions = extension_service()->extensions();
+ scoped_refptr<const extensions::Extension> extension;
+ ExtensionSet::const_iterator iter;
+ bool installed = false;
+
+ for (iter = extensions->begin(); iter != extensions->end(); ++iter) {
+ extension = *iter;
+ // Is there a better way to recognize the chromoting extension
+ // than name comparison?
+ if (extension->name() == "Chromoting" ||
+ extension->name() == "Chrome Remote Desktop") {
+ installed = true;
+ break;
+ }
+ }
+
+ if (installed) {
+ chromoting_id_ = extension->id();
+
+ EXPECT_EQ(extension->GetType(),
+ extensions::Manifest::TYPE_LEGACY_PACKAGED_APP);
+
+ EXPECT_TRUE(extension->ShouldDisplayInAppLauncher());
+ }
+
+ EXPECT_EQ(installed, expected);
+}
+
+void RemoteDesktopBrowserTest::LaunchChromotingApp() {
+ ASSERT_FALSE(ChromotingID().empty());
+
+ const GURL chromoting_main = Chromoting_Main_URL();
+ NavigateToURLAndWaitForPageLoad(chromoting_main);
+
+ EXPECT_EQ(GetCurrentURL(), chromoting_main);
+}
+
+void RemoteDesktopBrowserTest::Authorize() {
+ // The chromoting extension should be installed.
+ ASSERT_FALSE(ChromotingID().empty());
+
+ // The chromoting main page should be loaded in the current tab
+ // and isAuthenticated() should be false (auth dialog visible).
+ ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
+ ASSERT_FALSE(ExecuteScriptAndExtractBool(
+ "remoting.oauth2.isAuthenticated()"));
+
+ // The first observer monitors the creation of the new window for
+ // the Google loging page.
+ content::WindowedNotificationObserver observer(
+ chrome::NOTIFICATION_BROWSER_OPENED,
+ content::NotificationService::AllSources());
+
+ // The second observer monitors the loading of the Google login page.
+ // Unfortunately we cannot specify a source in this observer because
+ // we can't get a handle of the new window until the first observer
+ // has finished waiting. But we will assert that the source of the
+ // load stop event is indeed the newly created browser window.
+ content::WindowedNotificationObserver observer2(
+ content::NOTIFICATION_LOAD_STOP,
+ content::NotificationService::AllSources());
+
+ ClickOnControl("auth-button");
+
+ observer.Wait();
+
+ const content::Source<Browser>* source =
+ static_cast<const content::Source<Browser>*>(&observer.source());
+ active_browser_ = source->ptr();
+
+ observer2.Wait();
+
+ const content::Source<content::NavigationController>* source2 =
+ static_cast<const content::Source<content::NavigationController>*>(
+ &observer2.source());
+ content::NavigationController* controller = source2->ptr();
+
+ EXPECT_EQ(controller->GetWebContents(),
+ active_browser_->tab_strip_model()->GetActiveWebContents());
+
+ // Verify the active tab is at the "Google Accounts" login page.
+ EXPECT_EQ("accounts.google.com", GetCurrentURL().host());
+ EXPECT_TRUE(HtmlElementExists("Email"));
+ EXPECT_TRUE(HtmlElementExists("Passwd"));
+}
+
+void RemoteDesktopBrowserTest::Authenticate() {
+ // The chromoting extension should be installed.
+ ASSERT_FALSE(ChromotingID().empty());
+
+ // The active tab should have the "Google Accounts" login page loaded.
+ ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
+ ASSERT_TRUE(HtmlElementExists("Email"));
+ ASSERT_TRUE(HtmlElementExists("Passwd"));
+
+ // Now log in using the username and password passed in from the command line.
+ ExecuteScriptAndWaitForAnyPageLoad(
+ "document.getElementById(\"Email\").value = \"" + username_ + "\";" +
+ "document.getElementById(\"Passwd\").value = \"" + password_ +"\";" +
+ "document.forms[\"gaia_loginform\"].submit();");
+
+ EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
+
+ // TODO(weitaosu): Is there a better way to verify we are on the
+ // "Request for Permission" page?
+ EXPECT_TRUE(HtmlElementExists("submit_approve_access"));
+}
+
+void RemoteDesktopBrowserTest::Approve() {
+ // The chromoting extension should be installed.
+ ASSERT_FALSE(ChromotingID().empty());
+
+ // The active tab should have the chromoting app loaded.
+ ASSERT_EQ("accounts.google.com", GetCurrentURL().host());
+
+ // Is there a better way to verify we are on the "Request for Permission"
+ // page?
+ ASSERT_TRUE(HtmlElementExists("submit_approve_access"));
+
+ const GURL chromoting_main = Chromoting_Main_URL();
+
+ // |active_browser_| is still set to the login window but the observer
+ // should be set up to observe the chromoting window because that is
+ // where we'll receive the message from the login window and reload the
+ // chromoting app.
+ content::WindowedNotificationObserver observer(
+ content::NOTIFICATION_LOAD_STOP,
+ base::Bind(
+ &RemoteDesktopBrowserTest::IsAuthenticated, browser()));
+
+ ExecuteScript(
+ "lso.approveButtonAction();"
+ "document.forms[\"connect-approve\"].submit();");
+
+ observer.Wait();
+
+ // Resetting |active_browser_| to browser() now that the Google login
+ // window is closed and the focus has returned to the chromoting window.
+ active_browser_ = browser();
+
+ ASSERT_TRUE(GetCurrentURL() == chromoting_main);
+
+ EXPECT_TRUE(ExecuteScriptAndExtractBool(
+ "remoting.oauth2.isAuthenticated()"));
+}
+
+void RemoteDesktopBrowserTest::StartMe2Me() {
+ // The chromoting extension should be installed.
+ ASSERT_FALSE(ChromotingID().empty());
+
+ // The active tab should have the chromoting app loaded.
+ ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
+ EXPECT_TRUE(ExecuteScriptAndExtractBool(
+ "remoting.oauth2.isAuthenticated()"));
+
+ // The Me2Me host list should be hidden.
+ ASSERT_FALSE(HtmlElementVisible("me2me-content"));
+ // The Me2Me "Get Start" button should be visible.
+ ASSERT_TRUE(HtmlElementVisible("get-started-me2me"));
+
+ // Starting Me2Me.
+ ExecuteScript("remoting.showMe2MeUiAndSave();");
+
+ EXPECT_TRUE(HtmlElementVisible("me2me-content"));
+ EXPECT_FALSE(HtmlElementVisible("me2me-first-run"));
+
+ // Wait until localHost is initialized. This can take a while.
+ ConditionalTimeoutWaiter waiter(
+ base::TimeDelta::FromSeconds(3),
+ base::TimeDelta::FromSeconds(1),
+ base::Bind(&RemoteDesktopBrowserTest::IsLocalHostReady, this));
+ EXPECT_TRUE(waiter.Wait());
+
+ EXPECT_TRUE(ExecuteScriptAndExtractBool(
+ "remoting.hostList.localHost_.hostName && "
+ "remoting.hostList.localHost_.hostId && "
+ "remoting.hostList.localHost_.status && "
+ "remoting.hostList.localHost_.status == 'ONLINE'"));
+}
+
+void RemoteDesktopBrowserTest::DisconnectMe2Me() {
+ // The chromoting extension should be installed.
+ ASSERT_FALSE(ChromotingID().empty());
+
+ // The active tab should have the chromoting app loaded.
+ ASSERT_EQ(Chromoting_Main_URL(), GetCurrentURL());
+ ASSERT_TRUE(RemoteDesktopBrowserTest::IsSessionConnected());
+
+ ClickOnControl("toolbar-stub");
+
+ EXPECT_TRUE(HtmlElementVisible("session-toolbar"));
+
+ ClickOnControl("toolbar-disconnect");
+
+ EXPECT_TRUE(HtmlElementVisible("client-dialog"));
+ EXPECT_TRUE(HtmlElementVisible("client-reconnect-button"));
+ EXPECT_TRUE(HtmlElementVisible("client-finished-me2me-button"));
+
+ ClickOnControl("client-finished-me2me-button");
+
+ EXPECT_FALSE(HtmlElementVisible("client-dialog"));
+}
+
+void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
+ ui::KeyboardCode keyCode,
+ const char* code) {
+ SimulateKeyPressWithCode(keyCode, code, false, false, false, false);
+}
+
+void RemoteDesktopBrowserTest::SimulateKeyPressWithCode(
+ ui::KeyboardCode keyCode,
+ const char* code,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command) {
+ content::SimulateKeyPressWithCode(
+ active_browser_->tab_strip_model()->GetActiveWebContents(),
+ keyCode,
+ code,
+ control,
+ shift,
+ alt,
+ command);
+}
+
+void RemoteDesktopBrowserTest::SimulateCharInput(char c) {
+ const char* code;
+ ui::KeyboardCode keyboard_code;
+ bool shift;
+ GetKeyValuesFromChar(c, &code, &keyboard_code, &shift);
+ ASSERT_TRUE(code != NULL);
+ SimulateKeyPressWithCode(keyboard_code, code, false, shift, false, false);
+}
+
+void RemoteDesktopBrowserTest::SimulateStringInput(const std::string& input) {
+ for (size_t i = 0; i < input.length(); ++i)
+ SimulateCharInput(input[i]);
+}
+
+void RemoteDesktopBrowserTest::SimulateMouseLeftClickAt(int x, int y) {
+ SimulateMouseClickAt(0, WebKit::WebMouseEvent::ButtonLeft, x, y);
+}
+
+void RemoteDesktopBrowserTest::SimulateMouseClickAt(
+ int modifiers, WebKit::WebMouseEvent::Button button, int x, int y) {
+ ExecuteScript(
+ "var clientPluginElement = "
+ "document.getElementById('session-client-plugin');"
+ "var clientPluginRect = clientPluginElement.getBoundingClientRect();");
+
+ int top = ExecuteScriptAndExtractInt("clientPluginRect.top");
+ int left = ExecuteScriptAndExtractInt("clientPluginRect.left");
+ int width = ExecuteScriptAndExtractInt("clientPluginRect.width");
+ int height = ExecuteScriptAndExtractInt("clientPluginRect.height");
+
+ ASSERT_GT(x, 0);
+ ASSERT_LT(x, width);
+ ASSERT_GT(y, 0);
+ ASSERT_LT(y, height);
+
+ content::SimulateMouseClickAt(
+ browser()->tab_strip_model()->GetActiveWebContents(),
+ modifiers,
+ button,
+ gfx::Point(left + x, top + y));
+}
+
+void RemoteDesktopBrowserTest::Install() {
+ // TODO(weitaosu): add support for unpacked extension (the v2 app needs it).
+ if (!NoInstall()) {
+ VerifyChromotingLoaded(false);
+ InstallChromotingApp();
+ }
+
+ VerifyChromotingLoaded(true);
+}
+
+void RemoteDesktopBrowserTest::Cleanup() {
+ // TODO(weitaosu): Remove this hack by blocking on the appropriate
+ // notification.
+ // The browser may still be loading images embedded in the webapp. If we
+ // uinstall it now those load will fail. Navigating away to avoid the load
+ // failures.
+ ui_test_utils::NavigateToURL(active_browser_, GURL("about:blank"));
+
+ if (!NoCleanup()) {
+ UninstallChromotingApp();
+ VerifyChromotingLoaded(false);
+ }
+}
+
+void RemoteDesktopBrowserTest::Auth() {
+ Authorize();
+ Authenticate();
+ Approve();
+}
+
+void RemoteDesktopBrowserTest::ConnectToLocalHost() {
+ // Verify that the local host is online.
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ "remoting.hostList.localHost_.hostName && "
+ "remoting.hostList.localHost_.hostId && "
+ "remoting.hostList.localHost_.status && "
+ "remoting.hostList.localHost_.status == 'ONLINE'"));
+
+ // Connect.
+ ClickOnControl("this-host-connect");
+
+ // Enter the pin # passed in from the command line.
+ EnterPin(me2me_pin());
+
+ WaitForConnection();
+}
+
+void RemoteDesktopBrowserTest::ConnectToRemoteHost(
+ const std::string& host_name) {
+ std::string host_id = ExecuteScriptAndExtractString(
+ "remoting.hostList.getHostIdForName('" + host_name + "')");
+
+ EXPECT_FALSE(host_id.empty());
+ std::string element_id = "host_" + host_id;
+
+ // Verify the host is online.
+ std::string host_div_class = ExecuteScriptAndExtractString(
+ "document.getElementById('" + element_id + "').parentNode.className");
+ EXPECT_NE(std::string::npos, host_div_class.find("host-online"));
+
+ ClickOnControl(element_id);
+
+ // Enter the pin # passed in from the command line.
+ EnterPin(me2me_pin());
+
+ WaitForConnection();
+}
+
+void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest(
+ net::RuleBasedHostResolverProc* host_resolver) {
+ // mock_host_resolver_override_ takes ownership of the resolver.
+ scoped_refptr<net::RuleBasedHostResolverProc> resolver =
+ new net::RuleBasedHostResolverProc(host_resolver);
+ resolver->AllowDirectLookup("*.google.com");
+ // On Linux, we use Chromium's NSS implementation which uses the following
+ // hosts for certificate verification. Without these overrides, running the
+ // integration tests on Linux causes errors as we make external DNS lookups.
+ resolver->AllowDirectLookup("*.thawte.com");
+ resolver->AllowDirectLookup("*.geotrust.com");
+ resolver->AllowDirectLookup("*.gstatic.com");
+ resolver->AllowDirectLookup("*.googleapis.com");
+ mock_host_resolver_override_.reset(
+ new net::ScopedDefaultHostResolverProc(resolver.get()));
+}
+
+void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() {
+ mock_host_resolver_override_.reset();
+}
+
+void RemoteDesktopBrowserTest::ParseCommandLine() {
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+
+ // The test framework overrides any command line user-data-dir
+ // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory.
+ // That happens in the ChromeTestLauncherDelegate, and affects
+ // all unit tests (no opt out available). It intentionally erases
+ // any --user-data-dir switch if present and appends a new one.
+ // Re-override the default data dir if override-user-data-dir
+ // is specified.
+ if (command_line->HasSwitch(kOverrideUserDataDir)) {
+ const base::FilePath& override_user_data_dir =
+ command_line->GetSwitchValuePath(kOverrideUserDataDir);
+
+ ASSERT_FALSE(override_user_data_dir.empty());
+
+ command_line->AppendSwitchPath(switches::kUserDataDir,
+ override_user_data_dir);
+ }
+
+ username_ = command_line->GetSwitchValueASCII(kUsername);
+ password_ = command_line->GetSwitchValueASCII(kkPassword);
+ me2me_pin_ = command_line->GetSwitchValueASCII(kMe2MePin);
+ remote_host_name_ = command_line->GetSwitchValueASCII(kRemoteHostName);
+
+ no_cleanup_ = command_line->HasSwitch(kNoCleanup);
+ no_install_ = command_line->HasSwitch(kNoInstall);
+
+ if (!no_install_) {
+ webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx);
+ ASSERT_FALSE(webapp_crx_.empty());
+ }
+}
+
+void RemoteDesktopBrowserTest::ExecuteScript(const std::string& script) {
+ ASSERT_TRUE(content::ExecuteScript(
+ active_browser_->tab_strip_model()->GetActiveWebContents(), script));
+}
+
+void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForAnyPageLoad(
+ const std::string& script) {
+ content::WindowedNotificationObserver observer(
+ content::NOTIFICATION_LOAD_STOP,
+ content::Source<content::NavigationController>(
+ &active_browser_->tab_strip_model()->GetActiveWebContents()->
+ GetController()));
+
+ ExecuteScript(script);
+
+ observer.Wait();
+}
+
+void RemoteDesktopBrowserTest::ExecuteScriptAndWaitForPageLoad(
+ const std::string& script,
+ const GURL& target) {
+ content::WindowedNotificationObserver observer(
+ content::NOTIFICATION_LOAD_STOP,
+ base::Bind(&RemoteDesktopBrowserTest::IsURLLoadedInWindow,
+ active_browser_,
+ target));
+
+ ExecuteScript(script);
+
+ observer.Wait();
+}
+
+// static
+bool RemoteDesktopBrowserTest::ExecuteScriptAndExtractBool(
+ Browser* browser, const std::string& script) {
+ bool result;
+ _ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ browser->tab_strip_model()->GetActiveWebContents(),
+ "window.domAutomationController.send(" + script + ");",
+ &result));
+
+ return result;
+}
+
+int RemoteDesktopBrowserTest::ExecuteScriptAndExtractInt(
+ const std::string& script) {
+ int result;
+ _ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+ active_browser_->tab_strip_model()->GetActiveWebContents(),
+ "window.domAutomationController.send(" + script + ");",
+ &result));
+
+ return result;
+}
+
+std::string RemoteDesktopBrowserTest::ExecuteScriptAndExtractString(
+ const std::string& script) {
+ std::string result;
+ _ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ active_browser_->tab_strip_model()->GetActiveWebContents(),
+ "window.domAutomationController.send(" + script + ");",
+ &result));
+
+ return result;
+}
+
+// Helper to navigate to a given url.
+void RemoteDesktopBrowserTest::NavigateToURLAndWaitForPageLoad(
+ const GURL& url) {
+ content::WindowedNotificationObserver observer(
+ content::NOTIFICATION_LOAD_STOP,
+ content::Source<content::NavigationController>(
+ &active_browser_->tab_strip_model()->GetActiveWebContents()->
+ GetController()));
+
+ ui_test_utils::NavigateToURL(active_browser_, url);
+ observer.Wait();
+}
+
+void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) {
+ ASSERT_TRUE(HtmlElementVisible(name));
+
+ ExecuteScript("document.getElementById(\"" + name + "\").click();");
+}
+
+void RemoteDesktopBrowserTest::EnterPin(const std::string& pin) {
+ // Wait for the pin-form to be displayed. This can take a while.
+ // We also need to dismiss the host-needs-update dialog if it comes up.
+ // TODO(weitaosu) 1: Instead of polling, can we register a callback to be
+ // called when the pin-form is ready?
+ // TODO(weitaosu) 2: Instead of blindly dismiss the host-needs-update dialog,
+ // we should verify that it only pops up at the right circumstance. That
+ // probably belongs in a separate test case though.
+ ConditionalTimeoutWaiter waiter(
+ base::TimeDelta::FromSeconds(5),
+ base::TimeDelta::FromSeconds(1),
+ base::Bind(&RemoteDesktopBrowserTest::IsPinFormVisible, this));
+ EXPECT_TRUE(waiter.Wait());
+
+ ExecuteScript(
+ "document.getElementById(\"pin-entry\").value = \"" + pin + "\";");
+
+ ClickOnControl("pin-connect-button");
+}
+
+void RemoteDesktopBrowserTest::WaitForConnection() {
+ // Wait until the client has connected to the server.
+ // This can take a while.
+ // TODO(weitaosu): Instead of polling, can we register a callback to
+ // remoting.clientSession.onStageChange_?
+ ConditionalTimeoutWaiter waiter(
+ base::TimeDelta::FromSeconds(4),
+ base::TimeDelta::FromSeconds(1),
+ base::Bind(&RemoteDesktopBrowserTest::IsSessionConnected, this));
+ EXPECT_TRUE(waiter.Wait());
+
+ // The client is not yet ready to take input when the session state becomes
+ // CONNECTED. Wait for 2 seconds for the client to become ready.
+ // TODO(weitaosu): Find a way to detect when the client is truly ready.
+ TimeoutWaiter(base::TimeDelta::FromSeconds(2)).Wait();
+}
+
+bool RemoteDesktopBrowserTest::IsLocalHostReady() {
+ // TODO(weitaosu): Instead of polling, can we register a callback to
+ // remoting.hostList.setLocalHost_?
+ return ExecuteScriptAndExtractBool("remoting.hostList.localHost_ != null");
+}
+
+bool RemoteDesktopBrowserTest::IsSessionConnected() {
+ return ExecuteScriptAndExtractBool(
+ "remoting.clientSession != null && "
+ "remoting.clientSession.getState() == "
+ "remoting.ClientSession.State.CONNECTED");
+}
+
+bool RemoteDesktopBrowserTest::IsPinFormVisible() {
+ if (HtmlElementVisible("host-needs-update-connect-button"))
+ ClickOnControl("host-needs-update-connect-button");
+
+ return HtmlElementVisible("pin-form");
+}
+
+// static
+bool RemoteDesktopBrowserTest::IsURLLoadedInWindow(Browser* browser,
+ const GURL& url) {
+ return GetCurrentURLInBrowser(browser) == url;
+}
+
+// static
+bool RemoteDesktopBrowserTest::IsAuthenticated(Browser* browser) {
+ return ExecuteScriptAndExtractBool(
+ browser, "remoting.oauth2.isAuthenticated()");
+}
+
+} // namespace remoting
diff --git a/chrome/test/remoting/remote_desktop_browsertest.h b/chrome/test/remoting/remote_desktop_browsertest.h
new file mode 100644
index 0000000..32c2911
--- /dev/null
+++ b/chrome/test/remoting/remote_desktop_browsertest.h
@@ -0,0 +1,290 @@
+// Copyright 2013 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_REMOTING_REMOTE_DESKTOP_BROWSERTEST_H_
+#define CHROME_TEST_REMOTING_REMOTE_DESKTOP_BROWSERTEST_H_
+
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+
+namespace {
+// Command line arguments specific to the chromoting browser tests.
+const char kOverrideUserDataDir[] = "override-user-data-dir";
+const char kNoCleanup[] = "no-cleanup";
+const char kNoInstall[] = "no-install";
+const char kWebAppCrx[] = "webapp-crx";
+const char kUsername[] = "username";
+const char kkPassword[] = "password";
+const char kMe2MePin[] = "me2me-pin";
+const char kRemoteHostName[] = "remote-host-name";
+
+// ASSERT_TRUE can only be used in void returning functions. This version
+// should be used in non-void-returning functions.
+inline void _ASSERT_TRUE(bool condition) {
+ ASSERT_TRUE(condition);
+ return;
+}
+
+} // namespace
+
+namespace remoting {
+
+class RemoteDesktopBrowserTest : public ExtensionBrowserTest {
+ public:
+ RemoteDesktopBrowserTest();
+ virtual ~RemoteDesktopBrowserTest();
+
+ // InProcessBrowserTest Overrides
+ virtual void SetUp() OVERRIDE;
+ virtual void SetUpOnMainThread() OVERRIDE;
+
+ protected:
+ // InProcessBrowserTest Overrides
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
+
+ // InProcessBrowserTest Overrides
+ virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
+
+ // The following helpers each perform a simple task.
+
+ // Verify the test has access to the internet (specifically google.com)
+ void VerifyInternetAccess();
+
+ // Install the chromoting extension from a crx file.
+ void InstallChromotingApp();
+
+ // Uninstall the chromoting extension.
+ void UninstallChromotingApp();
+
+ // Test whether the chromoting extension is installed.
+ void VerifyChromotingLoaded(bool expected);
+
+ // Launch the chromoting app.
+ void LaunchChromotingApp();
+
+ // Authorize: grant extended access permission to the user's computer.
+ void Authorize();
+
+ // Authenticate: sign in to google using the credentials provided.
+ void Authenticate();
+
+ // Approve: grant the chromoting app necessary permissions.
+ void Approve();
+
+ // Click on "Get Started" in the Me2Me section and show the host list.
+ void StartMe2Me();
+
+ // Disconnect the active Me2Me session.
+ void DisconnectMe2Me();
+
+ // Simulate a key event.
+ void SimulateKeyPressWithCode(ui::KeyboardCode keyCode, const char* code);
+
+ void SimulateKeyPressWithCode(ui::KeyboardCode keyCode,
+ const char* code,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command);
+
+ // Simulate typing a character
+ void SimulateCharInput(char c);
+
+ // Simulate typing a string
+ void SimulateStringInput(const std::string& input);
+
+ // Helper to simulate a left button mouse click.
+ void SimulateMouseLeftClickAt(int x, int y);
+
+ // Helper to simulate a mouse click.
+ void SimulateMouseClickAt(
+ int modifiers, WebKit::WebMouseEvent::Button button, int x, int y);
+
+ // The following helpers each perform a composite task.
+
+ // Install the chromoting extension
+ void Install();
+
+ // Clean up after the test.
+ void Cleanup();
+
+ // Perform all the auth steps: authorization, authenticattion, etc.
+ // It starts from the chromoting main page unauthenticated and ends up back
+ // on the chromoting main page authenticated and ready to go.
+ void Auth();
+
+ // Connect to the local host through Me2Me.
+ void ConnectToLocalHost();
+
+ // Connect to a remote host through Me2Me.
+ void ConnectToRemoteHost(const std::string& host_name);
+
+ // Enter the pin number and connect.
+ void EnterPin(const std::string& name);
+
+ // Helper to get the pin number used for me2me authentication.
+ std::string me2me_pin() { return me2me_pin_; }
+
+ // Helper to get the name of the remote host to connect to.
+ std::string remote_host_name() { return remote_host_name_; }
+
+ private:
+ // Change behavior of the default host resolver to allow DNS lookup
+ // to proceed instead of being blocked by the test infrastructure.
+ void EnableDNSLookupForThisTest(
+ net::RuleBasedHostResolverProc* host_resolver);
+
+ // We need to reset the DNS lookup when we finish, or the test will fail.
+ void DisableDNSLookupForThisTest();
+
+ void ParseCommandLine();
+
+ // Accessor methods.
+
+ // Helper to get the path to the crx file of the webapp to be tested.
+ base::FilePath WebAppCrxPath() { return webapp_crx_; }
+
+ // Helper to get the extension ID of the installed chromoting webapp.
+ std::string ChromotingID() { return chromoting_id_; }
+
+ // Whether to perform the cleanup tasks (uninstalling chromoting, etc).
+ // This is useful for diagnostic purposes.
+ bool NoCleanup() { return no_cleanup_; }
+
+ // Whether to install the chromoting extension before running the test cases.
+ // This is useful for diagnostic purposes.
+ bool NoInstall() { return no_install_; }
+
+ // Helper to construct the starting URL of the installed chromoting webapp.
+ GURL Chromoting_Main_URL() {
+ return GURL("chrome-extension://" + ChromotingID() + "/main.html");
+ }
+
+ // Helper to retrieve the current URL of the active tab in the active browser
+ // window.
+ GURL GetCurrentURL() {
+ return GetCurrentURLInBrowser(active_browser_);
+ }
+
+ // Helper to retrieve the current URL of the active tab in the given browser
+ // window.
+ static GURL GetCurrentURLInBrowser(Browser* browser) {
+ return browser->tab_strip_model()->GetActiveWebContents()->GetURL();
+ }
+
+ // Helpers to execute javascript code on a web page.
+
+ // Helper to execute a javascript code snippet on the current page of
+ // the active browser window.
+ void ExecuteScript(const std::string& script);
+
+ // Helper to execute a javascript code snippet one the current page of
+ // the active browser window and wait for page load to complete.
+ void ExecuteScriptAndWaitForAnyPageLoad(const std::string& script);
+
+ // Helper to execute a javascript code snippet one the current page of
+ // the active browser window and wait until the target url is loaded.
+ // This is used when the target page is loaded after one or more redirections.
+ void ExecuteScriptAndWaitForPageLoad(const std::string& script,
+ const GURL& target);
+
+ // Helper to execute a javascript code snippet on the current page of
+ // the active browser window and extract the boolean result.
+ bool ExecuteScriptAndExtractBool(const std::string& script) {
+ return ExecuteScriptAndExtractBool(active_browser_, script);
+ }
+
+ // Helper to execute a javascript code snippet one the current page of
+ // the active browser window and extract the boolean result.
+ static bool ExecuteScriptAndExtractBool(Browser* browser,
+ const std::string& script);
+
+ // Helper to execute a javascript code snippet one the current page of
+ // the active browser window and extract the int result.
+ int ExecuteScriptAndExtractInt(const std::string& script);
+
+ // Helper to execute a javascript code snippet one the current page of
+ // the active browser window and extract the string result.
+ std::string ExecuteScriptAndExtractString(const std::string& script);
+
+ // Helper to navigate to a given url.
+ void NavigateToURLAndWaitForPageLoad(const GURL& url);
+
+ // Helper to check whether an html element with the given name exists on
+ // the current page of the active browser window.
+ bool HtmlElementExists(const std::string& name) {
+ return ExecuteScriptAndExtractBool(
+ "document.getElementById(\"" + name + "\") != null");
+ }
+
+ // Helper to check whether a html element with the given name is visible.
+ bool HtmlElementVisible(const std::string& name);
+
+ // Click on the named HTML control.
+ void ClickOnControl(const std::string& name);
+
+ // Wait for the me2me connection to be established.
+ void WaitForConnection();
+
+ // Checking whether the localHost has been initialized.
+ bool IsLocalHostReady();
+
+ // Callback used by EnterPin to check whether the pin form is visible
+ // and to dismiss the host-needs-update dialog.
+ bool IsPinFormVisible();
+
+ // Callback used by WaitForConnection to check whether the connection
+ // has been established.
+ bool IsSessionConnected();
+
+ // Callback used by ExecuteScriptAndWaitForPageLoad to check whether
+ // the given page is currently loaded in the given browser window.
+ static bool IsURLLoadedInWindow(Browser* browser, const GURL& url);
+
+ // Callback used by Approve to check whether the chromoting app has
+ // successfully authenticated with the google services.
+ static bool IsAuthenticated(Browser* browser);
+
+ // Fields
+
+ // This test needs to make live DNS requests for access to
+ // GAIA and sync server URLs under google.com. We use a scoped version
+ // to override the default resolver while the test is active.
+ scoped_ptr<net::ScopedDefaultHostResolverProc> mock_host_resolver_override_;
+
+ // The "active" browser window the test needs to interact with.
+ // We initialize |active_browser_| to the browser instance created by
+ // InProcessBrowserTest as the initial browser window to run test in.
+ // Whenever a new browser window is spawned and needs attention
+ // |active_browser_| is set to that browser window and all subsequent
+ // test actions happen there.
+ // And when the focus is returned to the original browser window
+ // |active_browser_| is reset to browser().
+ // This pattern is sufficient for simple streamlined workflows where all
+ // the browser windows form a LIFO stack. The test always interacts
+ // with the "active" window which is always on the top of the "browser
+ // stack". See also http://crrev.com/chrome/browser/ui/browser_list.h
+ // If we ever need to deal with more complicated workflows the test
+ // code will need to explicitly pass browser instances to the helper
+ // routines.
+ Browser* active_browser_;
+
+ bool no_cleanup_;
+ bool no_install_;
+ std::string chromoting_id_;
+ base::FilePath webapp_crx_;
+ std::string username_;
+ std::string password_;
+ std::string me2me_pin_;
+ std::string remote_host_name_;
+};
+
+} // namespace remoting
+
+#endif // CHROME_TEST_REMOTING_REMOTE_DESKTOP_BROWSERTEST_H_
diff --git a/chrome/test/remoting/waiter.cc b/chrome/test/remoting/waiter.cc
new file mode 100644
index 0000000..7296068
--- /dev/null
+++ b/chrome/test/remoting/waiter.cc
@@ -0,0 +1,81 @@
+// Copyright 2013 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/remoting/waiter.h"
+
+#include "content/public/test/test_utils.h"
+
+namespace remoting {
+
+TimeoutWaiter::TimeoutWaiter(base::TimeDelta timeout)
+ : timeout_(timeout) {
+ DCHECK(timeout > base::TimeDelta::FromSeconds(0));
+}
+
+TimeoutWaiter::~TimeoutWaiter() {}
+
+bool TimeoutWaiter::Wait() {
+ DCHECK(!timeout_timer_.IsRunning());
+
+ timeout_timer_.Start(
+ FROM_HERE,
+ timeout_,
+ base::Bind(&TimeoutWaiter::CancelWaitCallback, base::Unretained(this)));
+
+ message_loop_runner_ = new content::MessageLoopRunner;
+ message_loop_runner_->Run();
+
+ return true;
+}
+
+void TimeoutWaiter::CancelWait() {
+ message_loop_runner_->Quit();
+}
+
+void TimeoutWaiter::CancelWaitCallback() {
+ CancelWait();
+}
+
+ConditionalTimeoutWaiter::ConditionalTimeoutWaiter(base::TimeDelta timeout,
+ base::TimeDelta interval,
+ const Predicate& callback)
+ : TimeoutWaiter(timeout),
+ interval_(interval),
+ callback_(callback),
+ success_(false) {
+ DCHECK(timeout > interval);
+}
+
+ConditionalTimeoutWaiter::~ConditionalTimeoutWaiter() {}
+
+bool ConditionalTimeoutWaiter::Wait() {
+ DCHECK(!condition_timer_.IsRunning());
+
+ condition_timer_.Start(
+ FROM_HERE,
+ interval_,
+ base::Bind(&ConditionalTimeoutWaiter::CancelWaitCallback,
+ base::Unretained(this)));
+
+ // Also call the base class Wait() to start the timeout timer.
+ TimeoutWaiter::Wait();
+
+ return success_;
+}
+
+void ConditionalTimeoutWaiter::CancelWait() {
+ condition_timer_.Stop();
+
+ // Also call the base class CancelWait() to stop the timeout timer.
+ TimeoutWaiter::CancelWait();
+}
+
+void ConditionalTimeoutWaiter::CancelWaitCallback() {
+ if (callback_.Run()) {
+ success_ = true;
+ CancelWait();
+ }
+}
+
+} // namespace remoting
diff --git a/chrome/test/remoting/waiter.h b/chrome/test/remoting/waiter.h
new file mode 100644
index 0000000..c4a1d5a
--- /dev/null
+++ b/chrome/test/remoting/waiter.h
@@ -0,0 +1,74 @@
+// Copyright 2013 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_REMOTING_WAITER_H_
+#define CHROME_TEST_REMOTING_WAITER_H_
+
+#include "base/timer/timer.h"
+
+namespace content {
+class MessageLoopRunner;
+}
+
+namespace remoting {
+
+// Block the execution of the test code for the specified number of
+// milliseconds while keeping the message loop running. The browser instance
+// will be responsive during the wait and test actions initiated before
+// the wait will keep running.
+class TimeoutWaiter {
+ public:
+ explicit TimeoutWaiter(base::TimeDelta timeout);
+ virtual ~TimeoutWaiter();
+
+ // Returns true in case of success.
+ // For TimeoutWaiter it should always be true.
+ virtual bool Wait();
+
+ protected:
+ virtual void CancelWait();
+
+ private:
+ // Callback used to cancel the TimeoutWaiter::Wait.
+ void CancelWaitCallback();
+
+ base::OneShotTimer<TimeoutWaiter> timeout_timer_;
+ base::TimeDelta timeout_;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeoutWaiter);
+};
+
+// With a message loop running, keep calling callback in the specified
+// interval until true is returned.
+class ConditionalTimeoutWaiter : public TimeoutWaiter {
+ public:
+ typedef base::Callback<bool(void)> Predicate;
+
+ ConditionalTimeoutWaiter(base::TimeDelta timeout,
+ base::TimeDelta interval,
+ const Predicate& callback);
+ virtual ~ConditionalTimeoutWaiter();
+
+ // Returns true if |callback_| returned true and false in case of timeout.
+ virtual bool Wait() OVERRIDE;
+
+ protected:
+ virtual void CancelWait() OVERRIDE;
+
+ private:
+ // Callback used to cancel the ConditionalTimeoutWaiter::Wait.
+ void CancelWaitCallback();
+
+ base::TimeDelta interval_;
+ Predicate callback_;
+ base::RepeatingTimer<ConditionalTimeoutWaiter> condition_timer_;
+ bool success_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionalTimeoutWaiter);
+};
+
+} // namespace remoting
+
+#endif // CHROME_TEST_REMOTING_WAITER_H_