diff options
author | weitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-15 10:58:17 +0000 |
---|---|---|
committer | weitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-15 10:58:17 +0000 |
commit | d43097897412d64ee7978c032842cc6ef12c415c (patch) | |
tree | 2e61f316d23fc5d1a24b85b650be8fdcdf2de9c4 /chrome/test/remoting | |
parent | 83fc55e4c8c0da1e74d2efde3818de9876f283ad (diff) | |
download | chromium_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/DEPS | 4 | ||||
-rw-r--r-- | chrome/test/remoting/OWNERS | 7 | ||||
-rw-r--r-- | chrome/test/remoting/auth_browsertest.cc | 22 | ||||
-rw-r--r-- | chrome/test/remoting/key_code_conv.cc | 36 | ||||
-rw-r--r-- | chrome/test/remoting/key_code_conv.h | 25 | ||||
-rw-r--r-- | chrome/test/remoting/key_code_map.h | 94 | ||||
-rw-r--r-- | chrome/test/remoting/launch_browsertest.cc | 19 | ||||
-rw-r--r-- | chrome/test/remoting/me2me_browsertest.cc | 108 | ||||
-rw-r--r-- | chrome/test/remoting/remote_desktop_browsertest.cc | 638 | ||||
-rw-r--r-- | chrome/test/remoting/remote_desktop_browsertest.h | 290 | ||||
-rw-r--r-- | chrome/test/remoting/waiter.cc | 81 | ||||
-rw-r--r-- | chrome/test/remoting/waiter.h | 74 |
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_ |