blob: 4242410b1c267ba3aa7bec76162c98b4f3b0c16a [file] [log] [blame]
// Copyright (c) 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/command_line.h"
#include "base/path_service.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.cc"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/manifest.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
using extensions::Extension;
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";
// ASSERT_TRUE can only be used in void returning functions.
void _ASSERT_TRUE(bool condition) {
ASSERT_TRUE(condition);
return;
}
}
namespace remoting {
class RemoteDesktopBrowserTest : public ExtensionBrowserTest {
public:
virtual void SetUp() OVERRIDE {
ParseCommandLine();
ExtensionBrowserTest::SetUp();
}
protected:
// Override InProcessBrowserTest. Change behavior of the default host
// resolver to avoid DNS lookup errors, so we can make network calls.
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
// The resolver object lifetime is managed by sync_test_setup, not here.
EnableDNSLookupForThisTest(
new net::RuleBasedHostResolverProc(host_resolver()));
}
// Override InProcessBrowserTest.
virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
DisableDNSLookupForThisTest();
}
// 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();
// Verify the test has access to the internet (specifically google.com)
void VerifyInternetAccess();
void Authorize();
void Authenticate();
// 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_; }
private:
void ParseCommandLine();
// 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();
// 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_; }
// Helper to retrieve the current URL of the active tab in the browser.
GURL GetCurrentURL() {
return browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
}
// Helper to execute a javascript code snippet on the current page.
void ExecuteScript(const std::string& script) {
ASSERT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(), script));
}
// Helper to execute a javascript code snippet on the current page and
// wait for page load to complete.
void ExecuteScriptAndWait(const std::string& script) {
content::WindowedNotificationObserver observer(
content::NOTIFICATION_LOAD_STOP,
content::Source<content::NavigationController>(
&browser()->tab_strip_model()->GetActiveWebContents()->
GetController()));
ExecuteScript(script);
observer.Wait();
}
// Helper to execute a javascript code snippet on the current page and
// extract the boolean result.
bool ExecuteScriptAndExtractBool(const std::string& script) {
bool result;
// Using a private assert function because ASSERT_TRUE can only be used in
// void returning functions.
_ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send(" + script + ");",
&result));
return result;
}
// Helper to check whether a html element with the given name exists on
// the current page.
bool HtmlElementExists(const std::string& name) {
return ExecuteScriptAndExtractBool(
"document.getElementById(\"" + name + "\") != null");
}
// Helper to navigate to a given url.
void NavigateToURLAndWait(const GURL& url) {
content::WindowedNotificationObserver observer(
content::NOTIFICATION_LOAD_STOP,
content::Source<content::NavigationController>(
&browser()->tab_strip_model()->GetActiveWebContents()->
GetController()));
ui_test_utils::NavigateToURL(browser(), url);
observer.Wait();
}
// 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_;
bool no_cleanup_;
bool no_install_;
std::string chromoting_id_;
base::FilePath webapp_crx_;
std::string username_;
std::string password_;
};
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);
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::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::VerifyInternetAccess() {
GURL google_url("http://www.google.com");
NavigateToURLAndWait(google_url);
EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
}
void RemoteDesktopBrowserTest::InstallChromotingApp() {
base::FilePath install_dir(WebAppCrxPath());
scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
install_dir, 1, browser()));
EXPECT_FALSE(extension.get() == NULL);
}
void RemoteDesktopBrowserTest::UninstallChromotingApp() {
UninstallExtension(ChromotingID());
chromoting_id_.clear();
}
void RemoteDesktopBrowserTest::LaunchChromotingApp() {
ASSERT_FALSE(ChromotingID().empty());
std::string url = "chrome-extension://" + ChromotingID() + "/main.html";
const GURL chromoting_main(url);
NavigateToURLAndWait(chromoting_main);
EXPECT_EQ(GetCurrentURL(), chromoting_main);
}
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::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).
std::string url = "chrome-extension://" + ChromotingID() + "/main.html";
ASSERT_EQ(GetCurrentURL().spec(), url);
ASSERT_FALSE(ExecuteScriptAndExtractBool(
"remoting.OAuth2.prototype.isAuthenticated()"));
ExecuteScriptAndWait("remoting.OAuth2.prototype.doAuthRedirect();");
// Verify the active tab is at the "Google Accounts" login page.
EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
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(GetCurrentURL().host(), "accounts.google.com");
ASSERT_TRUE(HtmlElementExists("Email"));
ASSERT_TRUE(HtmlElementExists("Passwd"));
// Now log in using the username and password passed in from the command line.
ExecuteScriptAndWait(
"document.getElementById(\"Email\").value = \"" + username_ + "\";" +
"document.getElementById(\"Passwd\").value = \"" + password_ +"\";" +
"document.forms[\"gaia_loginform\"].submit();");
EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
// TODO: Is there a better way to verify we are on the
// "Request for Permission" page?
EXPECT_TRUE(HtmlElementExists("submit_approve_access"));
}
IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest, MANUAL_Launch) {
VerifyInternetAccess();
if (!NoInstall()) {
VerifyChromotingLoaded(false);
InstallChromotingApp();
}
VerifyChromotingLoaded(true);
LaunchChromotingApp();
// TODO: 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(browser(), GURL("about:blank"));
if (!NoCleanup()) {
UninstallChromotingApp();
VerifyChromotingLoaded(false);
}
}
IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest, MANUAL_Auth) {
VerifyInternetAccess();
if (!NoInstall()) {
VerifyChromotingLoaded(false);
InstallChromotingApp();
}
VerifyChromotingLoaded(true);
LaunchChromotingApp();
Authorize();
Authenticate();
if (!NoCleanup()) {
UninstallChromotingApp();
VerifyChromotingLoaded(false);
}
}
} // namespace remoting