// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "apps/shell_window.h"
-#include "apps/shell_window_registry.h"
+#include "apps/app_window.h"
+#include "apps/app_window_registry.h"
#include "apps/ui/native_app_window.h"
#include "ash/desktop_background/desktop_background_controller.h"
#include "ash/desktop_background/desktop_background_controller_observer.h"
#include "ash/shell.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
#include "chrome/browser/chromeos/login/fake_user_manager.h"
#include "chrome/browser/chromeos/login/mock_user_manager.h"
#include "chrome/browser/chromeos/login/oobe_base_test.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/test/app_window_waiter.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/browser/profiles/profile_impl.h"
+#include "chrome/browser/ui/webui/chromeos/login/kiosk_app_menu_handler.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "components/signin/core/common/signin_pref_names.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/test/browser_test_utils.h"
+#include "crypto/sha2.h"
+#include "extensions/browser/extension_system.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_switches.h"
#include "google_apis/gaia/gaia_urls.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::BasicHttpResponse;
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
namespace em = enterprise_management;
// detail/ibjkkfdnfcaoapcpheeijckmpcfkifob
const char kTestEnterpriseKioskApp[] = "ibjkkfdnfcaoapcpheeijckmpcfkifob";
+// An offline enable test app. Webstore data json is in
+// chrome/test/data/chromeos/app_mode/webstore/inlineinstall/
+// detail/ajoggoflpgplnnjkjamcmbepjdjdnpdp
+// An app profile with version 1.0.0 installed is in
+// chrome/test/data/chromeos/app_mode/offline_enabled_app_profile
+// The version 2.0.0 crx is in
+// chrome/test/data/chromeos/app_mode/webstore/downloads/
+const char kTestOfflineEnabledKioskApp[] = "ajoggoflpgplnnjkjamcmbepjdjdnpdp";
+
+// An app to test local fs data persistence across app update. V1 app writes
+// data into local fs. V2 app reads and verifies the data.
+// Webstore data json is in
+// chrome/test/data/chromeos/app_mode/webstore/inlineinstall/
+// detail/bmbpicmpniaclbbpdkfglgipkkebnbjf
+const char kTestLocalFsKioskApp[] = "bmbpicmpniaclbbpdkfglgipkkebnbjf";
+
// Timeout while waiting for network connectivity during tests.
const int kTestNetworkTimeoutSeconds = 1;
const char kTestAppScope[] =
"https://www.googleapis.com/auth/userinfo.profile";
-// Helper function for GetConsumerKioskModeStatusCallback.
-void ConsumerKioskModeStatusCheck(
- KioskAppManager::ConsumerKioskModeStatus* out_status,
+// Test JS API.
+const char kLaunchAppForTestNewAPI[] =
+ "login.AccountPickerScreen.runAppForTesting";
+const char kLaunchAppForTestOldAPI[] =
+ "login.AppsMenuButton.runAppForTesting";
+const char kCheckDiagnosticModeNewAPI[] =
+ "$('oobe').confirmDiagnosticMode_";
+const char kCheckDiagnosticModeOldAPI[] =
+ "$('show-apps-button').confirmDiagnosticMode_";
+
+// Helper function for GetConsumerKioskAutoLaunchStatusCallback.
+void ConsumerKioskAutoLaunchStatusCheck(
+ KioskAppManager::ConsumerKioskAutoLaunchStatus* out_status,
const base::Closure& runner_quit_task,
- KioskAppManager::ConsumerKioskModeStatus in_status) {
+ KioskAppManager::ConsumerKioskAutoLaunchStatus in_status) {
LOG(INFO) << "KioskAppManager::ConsumerKioskModeStatus = " << in_status;
*out_status = in_status;
runner_quit_task.Run();
}
// Helper KioskAppManager::EnableKioskModeCallback implementation.
-void ConsumerKioskModeLockCheck(
+void ConsumerKioskModeAutoStartLockCheck(
bool* out_locked,
const base::Closure& runner_quit_task,
bool in_locked) {
runner_quit_task.Run();
}
-// Helper function for DeviceOAuth2TokenServiceFactory::Get().
-void CopyTokenService(DeviceOAuth2TokenService** out_token_service,
- DeviceOAuth2TokenService* in_token_service) {
- *out_token_service = in_token_service;
-}
-
// Helper functions for CanConfigureNetwork mock.
class ScopedCanConfigureNetwork {
public:
DISALLOW_COPY_AND_ASSIGN(ScopedCanConfigureNetwork);
};
-} // namespace
-
-// Helper class that monitors app windows to wait for a window to appear.
-class ShellWindowObserver : public apps::ShellWindowRegistry::Observer {
+// Helper class to wait until a js condition becomes true.
+class JsConditionWaiter {
public:
- ShellWindowObserver(apps::ShellWindowRegistry* registry,
- const std::string& app_id)
- : registry_(registry), app_id_(app_id), window_(NULL), running_(false) {
- registry_->AddObserver(this);
- }
- virtual ~ShellWindowObserver() {
- registry_->RemoveObserver(this);
- }
-
- apps::ShellWindow* Wait() {
- running_ = true;
- message_loop_runner_ = new content::MessageLoopRunner;
- message_loop_runner_->Run();
- EXPECT_TRUE(window_);
- return window_;
+ JsConditionWaiter(content::WebContents* web_contents,
+ const std::string& js)
+ : web_contents_(web_contents),
+ js_(js) {
}
- // ShellWindowRegistry::Observer
- virtual void OnShellWindowAdded(apps::ShellWindow* shell_window) OVERRIDE {
- if (!running_)
+ void Wait() {
+ if (CheckJs())
return;
- if (shell_window->extension_id() == app_id_) {
- window_ = shell_window;
- message_loop_runner_->Quit();
- running_ = false;
- }
+ base::RepeatingTimer<JsConditionWaiter> check_timer;
+ check_timer.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(10),
+ this,
+ &JsConditionWaiter::OnTimer);
+
+ runner_ = new content::MessageLoopRunner;
+ runner_->Run();
}
- virtual void OnShellWindowIconChanged(
- apps::ShellWindow* shell_window) OVERRIDE {}
- virtual void OnShellWindowRemoved(apps::ShellWindow* shell_window) OVERRIDE {}
private:
- apps::ShellWindowRegistry* registry_;
- std::string app_id_;
- scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
- apps::ShellWindow* window_;
- bool running_;
+ bool CheckJs() {
+ bool result;
+ CHECK(content::ExecuteScriptAndExtractBool(
+ web_contents_,
+ "window.domAutomationController.send(!!(" + js_ + "));",
+ &result));
+ return result;
+ }
- DISALLOW_COPY_AND_ASSIGN(ShellWindowObserver);
+ void OnTimer() {
+ DCHECK(runner_);
+ if (CheckJs())
+ runner_->Quit();
+ }
+
+ content::WebContents* web_contents_;
+ const std::string js_;
+ scoped_refptr<content::MessageLoopRunner> runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsConditionWaiter);
};
+} // namespace
+
class KioskTest : public OobeBaseTest {
public:
KioskTest() {
virtual ~KioskTest() {}
protected:
-
virtual void SetUp() OVERRIDE {
+ test_app_id_ = kTestKioskApp;
mock_user_manager_.reset(new MockUserManager);
AppLaunchController::SkipSplashWaitForTesting();
AppLaunchController::SetNetworkWaitForTesting(kTestNetworkTimeoutSeconds);
// Create gaia and webstore URL from test server url but using different
// host names. This is to avoid gaia response being tagged as from
// webstore in chrome_resource_dispatcher_host_delegate.cc.
- const GURL& server_url = embedded_test_server()->base_url();
- std::string webstore_host("webstore");
- GURL::Replacements replace_webstore_host;
- replace_webstore_host.SetHostStr(webstore_host);
- GURL webstore_url = server_url.ReplaceComponents(replace_webstore_host);
+ GURL webstore_url = GetTestWebstoreUrl();
command_line->AppendSwitchASCII(
::switches::kAppsGalleryURL,
webstore_url.Resolve("/chromeos/app_mode/webstore").spec());
"/chromeos/app_mode/webstore/downloads/%s.crx").spec());
}
+ GURL GetTestWebstoreUrl() {
+ const GURL& server_url = embedded_test_server()->base_url();
+ std::string webstore_host("webstore");
+ GURL::Replacements replace_webstore_host;
+ replace_webstore_host.SetHostStr(webstore_host);
+ return server_url.ReplaceComponents(replace_webstore_host);
+ }
+
+ void LaunchApp(const std::string& app_id, bool diagnostic_mode) {
+ bool new_kiosk_ui = KioskAppMenuHandler::EnableNewKioskUI();
+ GetLoginUI()->CallJavascriptFunction(new_kiosk_ui ?
+ kLaunchAppForTestNewAPI : kLaunchAppForTestOldAPI,
+ base::StringValue(app_id),
+ base::FundamentalValue(diagnostic_mode));
+ }
+
void ReloadKioskApps() {
- KioskAppManager::Get()->AddApp(kTestKioskApp);
+ // Remove then add to ensure NOTIFICATION_KIOSK_APPS_LOADED fires.
+ KioskAppManager::Get()->RemoveApp(test_app_id_);
+ KioskAppManager::Get()->AddApp(test_app_id_);
}
void ReloadAutolaunchKioskApps() {
- KioskAppManager::Get()->AddApp(kTestKioskApp);
- KioskAppManager::Get()->SetAutoLaunchApp(kTestKioskApp);
+ KioskAppManager::Get()->AddApp(test_app_id_);
+ KioskAppManager::Get()->SetAutoLaunchApp(test_app_id_);
}
- void StartAppLaunchFromLoginScreen(const base::Closure& network_setup_cb) {
+ void PrepareAppLaunch() {
EnableConsumerKioskMode();
- // Start UI, find menu entry for this app and launch it.
+ // Start UI
content::WindowedNotificationObserver login_signal(
- chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
- content::NotificationService::AllSources());
+ chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
+ content::NotificationService::AllSources());
chromeos::WizardController::SkipPostLoginScreensForTesting();
chromeos::WizardController* wizard_controller =
chromeos::WizardController::default_controller();
- CHECK(wizard_controller);
- wizard_controller->SkipToLoginForTesting(LoginScreenContext());
- login_signal.Wait();
+ if (wizard_controller) {
+ wizard_controller->SkipToLoginForTesting(LoginScreenContext());
+ login_signal.Wait();
+ } else {
+ // No wizard and running with an existing profile and it should land
+ // on account picker when new kiosk UI is enabled. Otherwise, just
+ // wait for the login signal from Gaia.
+ if (KioskAppMenuHandler::EnableNewKioskUI())
+ OobeScreenWaiter(OobeDisplay::SCREEN_ACCOUNT_PICKER).Wait();
+ else
+ login_signal.Wait();
+ }
- // Wait for the Kiosk App configuration to reload, then launch the app.
+ // Wait for the Kiosk App configuration to reload.
content::WindowedNotificationObserver apps_loaded_signal(
chrome::NOTIFICATION_KIOSK_APPS_LOADED,
content::NotificationService::AllSources());
ReloadKioskApps();
apps_loaded_signal.Wait();
+ }
+
+ void StartAppLaunchFromLoginScreen(const base::Closure& network_setup_cb) {
+ PrepareAppLaunch();
if (!network_setup_cb.is_null())
network_setup_cb.Run();
- GetLoginUI()->CallJavascriptFunction(
- "login.AppsMenuButton.runAppForTesting",
- base::StringValue(kTestKioskApp));
+ LaunchApp(test_app_id(), false);
}
- void WaitForAppLaunchSuccess() {
- SimulateNetworkOnline();
+ const extensions::Extension* GetInstalledApp() {
+ Profile* app_profile = ProfileManager::GetPrimaryUserProfile();
+ return extensions::ExtensionSystem::Get(app_profile)->
+ extension_service()->GetInstalledExtension(test_app_id_);
+ }
+ const Version& GetInstalledAppVersion() {
+ return *GetInstalledApp()->version();
+ }
+
+ void WaitForAppLaunchSuccess() {
ExtensionTestMessageListener
launch_data_check_listener("launchData.isKioskSession = true", false);
Profile* app_profile = ProfileManager::GetPrimaryUserProfile();
ASSERT_TRUE(app_profile);
+ // Check ChromeOS preference is initialized.
+ EXPECT_TRUE(
+ static_cast<ProfileImpl*>(app_profile)->chromeos_preferences_);
+
// Check installer status.
EXPECT_EQ(chromeos::KioskAppLaunchError::NONE,
chromeos::KioskAppLaunchError::Get());
// Check if the kiosk webapp is really installed for the default profile.
const extensions::Extension* app =
extensions::ExtensionSystem::Get(app_profile)->
- extension_service()->GetInstalledExtension(kTestKioskApp);
+ extension_service()->GetInstalledExtension(test_app_id_);
EXPECT_TRUE(app);
// App should appear with its window.
- apps::ShellWindowRegistry* shell_window_registry =
- apps::ShellWindowRegistry::Get(app_profile);
- apps::ShellWindow* window =
- ShellWindowObserver(shell_window_registry, kTestKioskApp).Wait();
+ apps::AppWindowRegistry* app_window_registry =
+ apps::AppWindowRegistry::Get(app_profile);
+ apps::AppWindow* window =
+ AppWindowWaiter(app_window_registry, test_app_id_).Wait();
EXPECT_TRUE(window);
// Login screen should be gone or fading out.
0.0f);
// Wait until the app terminates if it is still running.
- if (!shell_window_registry->GetShellWindowsForApp(kTestKioskApp).empty())
+ if (!app_window_registry->GetAppWindowsForApp(test_app_id_).empty())
content::RunMessageLoop();
// Check that the app had been informed that it is running in a kiosk
scoped_ptr<bool> locked(new bool(false));
scoped_refptr<content::MessageLoopRunner> runner =
new content::MessageLoopRunner;
- KioskAppManager::Get()->EnableConsumerModeKiosk(
- base::Bind(&ConsumerKioskModeLockCheck,
+ KioskAppManager::Get()->EnableConsumerKioskAutoLaunch(
+ base::Bind(&ConsumerKioskModeAutoStartLockCheck,
locked.get(),
runner->QuitClosure()));
runner->Run();
EXPECT_TRUE(*locked.get());
}
- KioskAppManager::ConsumerKioskModeStatus GetConsumerKioskModeStatus() {
- KioskAppManager::ConsumerKioskModeStatus status =
- static_cast<KioskAppManager::ConsumerKioskModeStatus>(-1);
+ KioskAppManager::ConsumerKioskAutoLaunchStatus
+ GetConsumerKioskModeStatus() {
+ KioskAppManager::ConsumerKioskAutoLaunchStatus status =
+ static_cast<KioskAppManager::ConsumerKioskAutoLaunchStatus>(-1);
scoped_refptr<content::MessageLoopRunner> runner =
new content::MessageLoopRunner;
- KioskAppManager::Get()->GetConsumerKioskModeStatus(
- base::Bind(&ConsumerKioskModeStatusCheck,
+ KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(
+ base::Bind(&ConsumerKioskAutoLaunchStatusCheck,
&status,
runner->QuitClosure()));
runner->Run();
- CHECK_NE(status, static_cast<KioskAppManager::ConsumerKioskModeStatus>(-1));
+ CHECK_NE(status,
+ static_cast<KioskAppManager::ConsumerKioskAutoLaunchStatus>(-1));
return status;
}
+ // Copies the app profile from |relative_app_profile_dir| from test directory
+ // to the app profile directory (assuming "user") under testing profile. This
+ // is for that needs to have a kiosk app already installed from a previous
+ // run. Note this must be called before app profile is loaded.
+ void SetupAppProfile(const std::string& relative_app_profile_dir) {
+ base::FilePath app_profile_dir;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ ::switches::kMultiProfiles)) {
+ KioskAppManager::App app_data;
+ CHECK(KioskAppManager::Get()->GetApp(test_app_id(), &app_data));
+ std::string app_user_id_hash =
+ CryptohomeClient::GetStubSanitizedUsername(app_data.user_id);
+ app_profile_dir =
+ ProfileHelper::GetProfilePathByUserIdHash(app_user_id_hash);
+ } else {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &app_profile_dir));
+ app_profile_dir = app_profile_dir.Append(
+ ProfileHelper::GetProfileDirByLegacyLoginProfileSwitch());
+ }
+ ASSERT_TRUE(base::CreateDirectory(app_profile_dir));
+
+ base::FilePath test_data_dir;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
+ test_data_dir = test_data_dir.AppendASCII(relative_app_profile_dir);
+ ASSERT_TRUE(
+ base::CopyFile(test_data_dir.Append(chrome::kPreferencesFilename),
+ app_profile_dir.Append(chrome::kPreferencesFilename)));
+ ASSERT_TRUE(
+ base::CopyDirectory(test_data_dir.AppendASCII("Extensions"),
+ app_profile_dir,
+ true));
+ }
+
+ void RunAppLaunchNetworkDownTest() {
+ // Mock network could be configured with owner's password.
+ ScopedCanConfigureNetwork can_configure_network(true, true);
+
+ // Start app launch and wait for network connectivity timeout.
+ StartAppLaunchFromLoginScreen(SimulateNetworkOfflineClosure());
+ OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH);
+ splash_waiter.Wait();
+ WaitForAppLaunchNetworkTimeout();
+
+ // Configure network link should be visible.
+ JsExpect("$('splash-config-network').hidden == false");
+
+ // Set up fake user manager with an owner for the test.
+ mock_user_manager()->SetActiveUser(kTestOwnerEmail);
+ AppLaunchSigninScreen::SetUserManagerForTesting(mock_user_manager());
+ static_cast<LoginDisplayHostImpl*>(LoginDisplayHostImpl::default_host())
+ ->GetOobeUI()->ShowOobeUI(false);
+
+ // Configure network should bring up lock screen for owner.
+ OobeScreenWaiter lock_screen_waiter(OobeDisplay::SCREEN_ACCOUNT_PICKER);
+ static_cast<AppLaunchSplashScreenActor::Delegate*>(GetAppLaunchController())
+ ->OnConfigureNetwork();
+ lock_screen_waiter.Wait();
+
+ // There should be only one owner pod on this screen.
+ JsExpect("$('pod-row').isSinglePod");
+
+ // A network error screen should be shown after authenticating.
+ OobeScreenWaiter error_screen_waiter(OobeDisplay::SCREEN_ERROR_MESSAGE);
+ static_cast<AppLaunchSigninScreen::Delegate*>(GetAppLaunchController())
+ ->OnOwnerSigninSuccess();
+ error_screen_waiter.Wait();
+
+ ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog());
+
+ SimulateNetworkOnline();
+ WaitForAppLaunchSuccess();
+ }
+
AppLaunchController* GetAppLaunchController() {
return chromeos::LoginDisplayHostImpl::default_host()
->GetAppLaunchController();
}
+ MockUserManager* mock_user_manager() { return mock_user_manager_.get(); }
+
+ void set_test_app_id(const std::string& test_app_id) {
+ test_app_id_ = test_app_id;
+ }
+ const std::string& test_app_id() const { return test_app_id_; }
+
+ private:
+ std::string test_app_id_;
scoped_ptr<MockUserManager> mock_user_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskTest);
};
IN_PROC_BROWSER_TEST_F(KioskTest, InstallAndLaunchApp) {
WaitForAppLaunchSuccess();
}
+IN_PROC_BROWSER_TEST_F(KioskTest, NotSignedInWithGAIAAccount) {
+ // Tests that the kiosk session is not considered to be logged in with a GAIA
+ // account.
+ StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure());
+ WaitForAppLaunchSuccess();
+
+ Profile* app_profile = ProfileManager::GetPrimaryUserProfile();
+ ASSERT_TRUE(app_profile);
+ EXPECT_FALSE(app_profile->GetPrefs()->HasPrefPath(
+ prefs::kGoogleServicesUsername));
+}
+
+IN_PROC_BROWSER_TEST_F(KioskTest, PRE_LaunchAppNetworkDown) {
+ // Tests the network down case for the initial app download and launch.
+ RunAppLaunchNetworkDownTest();
+}
+
IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkDown) {
- // Mock network could be configured with owner's password.
- ScopedCanConfigureNetwork can_configure_network(true, true);
+ // Tests the network down case for launching an existing app that is
+ // installed in PRE_LaunchAppNetworkDown.
+ RunAppLaunchNetworkDownTest();
+}
+
+// TODO(zelidrag): Figure out why this test is flaky on bbots.
+IN_PROC_BROWSER_TEST_F(KioskTest,
+ DISABLED_LaunchAppWithNetworkConfigAccelerator) {
+ ScopedCanConfigureNetwork can_configure_network(true, false);
// Start app launch and wait for network connectivity timeout.
- StartAppLaunchFromLoginScreen(SimulateNetworkOfflineClosure());
+ StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure());
OobeScreenWaiter splash_waiter(OobeDisplay::SCREEN_APP_LAUNCH_SPLASH);
splash_waiter.Wait();
- WaitForAppLaunchNetworkTimeout();
-
- // Configure network link should be visible.
- JsExpect("$('splash-config-network').hidden == false");
-
- // Set up fake user manager with an owner for the test.
- mock_user_manager_->SetActiveUser(kTestOwnerEmail);
- AppLaunchSigninScreen::SetUserManagerForTesting(mock_user_manager_.get());
- static_cast<LoginDisplayHostImpl*>(LoginDisplayHostImpl::default_host())
- ->GetOobeUI()->ShowOobeUI(false);
-
- // Configure network should bring up lock screen for owner.
- OobeScreenWaiter lock_screen_waiter(OobeDisplay::SCREEN_ACCOUNT_PICKER);
- static_cast<AppLaunchSplashScreenActor::Delegate*>(GetAppLaunchController())
- ->OnConfigureNetwork();
- lock_screen_waiter.Wait();
// A network error screen should be shown after authenticating.
OobeScreenWaiter error_screen_waiter(OobeDisplay::SCREEN_ERROR_MESSAGE);
- static_cast<AppLaunchSigninScreen::Delegate*>(GetAppLaunchController())
- ->OnOwnerSigninSuccess();
+ // Simulate Ctrl+Alt+N accelerator.
+ GetLoginUI()->CallJavascriptFunction(
+ "cr.ui.Oobe.handleAccelerator",
+ base::StringValue("app_launch_network_config"));
error_screen_waiter.Wait();
-
ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog());
+ // Continue button should be visible since we are online.
+ JsExpect("$('continue-network-config-btn').hidden == false");
+
+ // Click on [Continue] button.
+ ASSERT_TRUE(content::ExecuteScript(
+ GetLoginUI()->GetWebContents(),
+ "(function() {"
+ "var e = new Event('click');"
+ "$('continue-network-config-btn').dispatchEvent(e);"
+ "})();"));
+
WaitForAppLaunchSuccess();
}
JsExpect("$('splash-config-network').hidden == true");
// Network becomes online and app launch is resumed.
+ SimulateNetworkOnline();
WaitForAppLaunchSuccess();
}
-IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppNetworkPortal) {
+// Flaky on bots. http://crbug.com/365507
+IN_PROC_BROWSER_TEST_F(KioskTest, DISABLED_LaunchAppNetworkPortal) {
// Mock network could be configured without the owner password.
ScopedCanConfigureNetwork can_configure_network(true, false);
OobeScreenWaiter(OobeDisplay::SCREEN_ERROR_MESSAGE).Wait();
ASSERT_TRUE(GetAppLaunchController()->showing_network_dialog());
+ SimulateNetworkOnline();
WaitForAppLaunchSuccess();
}
chromeos::KioskAppLaunchError::Get());
}
+IN_PROC_BROWSER_TEST_F(KioskTest, LaunchInDiagnosticMode) {
+ PrepareAppLaunch();
+ SimulateNetworkOnline();
+
+ LaunchApp(kTestKioskApp, true);
+
+ content::WebContents* login_contents = GetLoginUI()->GetWebContents();
+
+ bool new_kiosk_ui = KioskAppMenuHandler::EnableNewKioskUI();
+ JsConditionWaiter(login_contents, new_kiosk_ui ?
+ kCheckDiagnosticModeNewAPI : kCheckDiagnosticModeOldAPI).Wait();
+
+ std::string diagnosticMode(new_kiosk_ui ?
+ kCheckDiagnosticModeNewAPI : kCheckDiagnosticModeOldAPI);
+ ASSERT_TRUE(content::ExecuteScript(
+ login_contents,
+ "(function() {"
+ "var e = new Event('click');" +
+ diagnosticMode + "."
+ "okButton_.dispatchEvent(e);"
+ "})();"));
+
+ WaitForAppLaunchSuccess();
+}
+
IN_PROC_BROWSER_TEST_F(KioskTest, AutolaunchWarningCancel) {
EnableConsumerKioskMode();
// Start UI, find menu entry for this app and launch it.
CHECK(wizard_controller);
// Check Kiosk mode status.
- EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_CONFIGURABLE,
+ EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE,
GetConsumerKioskModeStatus());
// Wait for the login UI to come up and switch to the kiosk_enable screen.
content::NotificationService::AllSources()).Wait();
// Check that the status still says configurable.
- EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_CONFIGURABLE,
+ EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE,
GetConsumerKioskModeStatus());
}
CHECK(wizard_controller);
// Check Kiosk mode status.
- EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_CONFIGURABLE,
+ EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE,
GetConsumerKioskModeStatus());
wizard_controller->SkipToLoginForTesting(LoginScreenContext());
content::WindowedNotificationObserver(
chrome::NOTIFICATION_KIOSK_ENABLED,
content::NotificationService::AllSources()).Wait();
- EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_ENABLED,
+ EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_ENABLED,
GetConsumerKioskModeStatus());
}
CHECK(wizard_controller);
// Check Kiosk mode status.
- EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_CONFIGURABLE,
+ EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE,
GetConsumerKioskModeStatus());
wizard_controller->SkipToLoginForTesting(LoginScreenContext());
}
IN_PROC_BROWSER_TEST_F(KioskTest, KioskEnableAfter2ndSigninScreen) {
- // Fake an auto enrollment is not going to be enforced.
- CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kEnterpriseEnrollmentInitialModulus, "1");
- CommandLine::ForCurrentProcess()->AppendSwitchASCII(
- switches::kEnterpriseEnrollmentModulusLimit, "2");
- g_browser_process->local_state()->SetBoolean(prefs::kShouldAutoEnroll, false);
- g_browser_process->local_state()->SetInteger(
- prefs::kAutoEnrollmentPowerLimit, -1);
-
chromeos::WizardController::SkipPostLoginScreensForTesting();
chromeos::WizardController* wizard_controller =
chromeos::WizardController::default_controller();
CHECK(wizard_controller);
// Check Kiosk mode status.
- EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_MODE_CONFIGURABLE,
+ EXPECT_EQ(KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE,
GetConsumerKioskModeStatus());
// Wait for the login UI to come up and switch to the kiosk_enable screen.
content::NotificationService::AllSources()).Wait();
}
+class KioskUpdateTest : public KioskTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ KioskUpdateTest() {}
+ virtual ~KioskUpdateTest() {}
+
+ protected:
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ // Needs background networking so that ExtensionDownloader works.
+ needs_background_networking_ = true;
+
+ KioskTest::SetUpCommandLine(command_line);
+ if (GetParam())
+ command_line->AppendSwitch(::switches::kMultiProfiles);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ KioskTest::SetUpOnMainThread();
+
+ GURL webstore_url = GetTestWebstoreUrl();
+ CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ ::switches::kAppsGalleryUpdateURL,
+ webstore_url.Resolve("/update_check.xml").spec());
+
+ embedded_test_server()->RegisterRequestHandler(
+ base::Bind(&KioskUpdateTest::HandleRequest,
+ base::Unretained(this)));
+ }
+
+ void SetNoUpdate() {
+ SetUpdateCheckContent(
+ "chromeos/app_mode/webstore/update_check/no_update.xml",
+ GURL(),
+ "",
+ "",
+ "");
+ }
+
+ void SetUpdateCrx(const std::string& crx_file, const std::string& version) {
+ GURL webstore_url = GetTestWebstoreUrl();
+ GURL crx_download_url = webstore_url.Resolve(
+ "/chromeos/app_mode/webstore/downloads/" + crx_file);
+ base::FilePath test_data_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ base::FilePath crx_file_path =
+ test_data_dir.AppendASCII("chromeos/app_mode/webstore/downloads")
+ .AppendASCII(crx_file);
+
+ std::string crx_content;
+ ASSERT_TRUE(base::ReadFileToString(crx_file_path, &crx_content));
+
+ const std::string sha256 = crypto::SHA256HashString(crx_content);
+ const std::string sha256_hex =
+ base::HexEncode(sha256.c_str(), sha256.size());
+
+ SetUpdateCheckContent(
+ "chromeos/app_mode/webstore/update_check/has_update.xml",
+ crx_download_url,
+ sha256_hex,
+ base::UintToString(crx_content.size()),
+ version);
+ }
+
+ private:
+ void SetUpdateCheckContent(const std::string& update_check_file,
+ const GURL& crx_download_url,
+ const std::string& crx_fp,
+ const std::string& crx_size,
+ const std::string& version) {
+ base::FilePath test_data_dir;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
+ base::FilePath update_file =
+ test_data_dir.AppendASCII(update_check_file.c_str());
+ ASSERT_TRUE(base::ReadFileToString(update_file, &update_check_content_));
+
+ ReplaceSubstringsAfterOffset(
+ &update_check_content_, 0, "$AppId", test_app_id());
+ ReplaceSubstringsAfterOffset(
+ &update_check_content_, 0, "$CrxDownloadUrl", crx_download_url.spec());
+ ReplaceSubstringsAfterOffset(&update_check_content_, 0, "$FP", crx_fp);
+ ReplaceSubstringsAfterOffset(&update_check_content_, 0, "$Size", crx_size);
+ ReplaceSubstringsAfterOffset(
+ &update_check_content_, 0, "$Version", version);
+ }
+
+ scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
+ GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
+ std::string request_path = request_url.path();
+ if (!update_check_content_.empty() &&
+ request_path == "/update_check.xml") {
+ scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
+ http_response->set_code(net::HTTP_OK);
+ http_response->set_content_type("text/xml");
+ http_response->set_content(update_check_content_);
+ return http_response.PassAs<HttpResponse>();
+ }
+
+ return scoped_ptr<HttpResponse>();
+ }
+
+ std::string update_check_content_;
+
+ DISALLOW_COPY_AND_ASSIGN(KioskUpdateTest);
+};
+
+IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchOfflineEnabledAppNoNetwork) {
+ set_test_app_id(kTestOfflineEnabledKioskApp);
+
+ PrepareAppLaunch();
+ SimulateNetworkOffline();
+ SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile");
+
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchSuccess();
+}
+
+IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchOfflineEnabledAppNoUpdate) {
+ set_test_app_id(kTestOfflineEnabledKioskApp);
+
+ SetNoUpdate();
+
+ PrepareAppLaunch();
+ SimulateNetworkOnline();
+ SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile");
+
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchSuccess();
+
+ EXPECT_EQ("1.0.0", GetInstalledAppVersion().GetString());
+}
+
+IN_PROC_BROWSER_TEST_P(KioskUpdateTest, LaunchOfflineEnabledAppHasUpdate) {
+ set_test_app_id(kTestOfflineEnabledKioskApp);
+
+ SetUpdateCrx("ajoggoflpgplnnjkjamcmbepjdjdnpdp.crx", "2.0.0");
+
+ PrepareAppLaunch();
+ SimulateNetworkOnline();
+ SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile");
+
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchSuccess();
+
+ EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString());
+}
+
+IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PermissionChange) {
+ set_test_app_id(kTestOfflineEnabledKioskApp);
+
+ SetUpdateCrx("ajoggoflpgplnnjkjamcmbepjdjdnpdp_v2_permission_change.crx",
+ "2.0.0");
+
+ PrepareAppLaunch();
+ SimulateNetworkOnline();
+ SetupAppProfile("chromeos/app_mode/offline_enabled_app_profile");
+
+ LaunchApp(test_app_id(), false);
+ WaitForAppLaunchSuccess();
+
+ EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString());
+}
+
+IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PRE_PreserveLocalData) {
+ // Installs v1 app and writes some local data.
+ set_test_app_id(kTestLocalFsKioskApp);
+
+ ResultCatcher catcher;
+ StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure());
+ WaitForAppLaunchSuccess();
+ ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+IN_PROC_BROWSER_TEST_P(KioskUpdateTest, PreserveLocalData) {
+ // Update existing v1 app installed in PRE_PreserveLocalData to v2
+ // that reads and verifies the local data.
+ set_test_app_id(kTestLocalFsKioskApp);
+
+ SetUpdateCrx("bmbpicmpniaclbbpdkfglgipkkebnbjf_v2_read_and_verify_data.crx",
+ "2.0.0");
+
+ ResultCatcher catcher;
+ StartAppLaunchFromLoginScreen(SimulateNetworkOnlineClosure());
+ WaitForAppLaunchSuccess();
+
+ EXPECT_EQ("2.0.0", GetInstalledAppVersion().GetString());
+ ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+// TODO(xiyuan): Remove this after multi profile is turned on by default.
+INSTANTIATE_TEST_CASE_P(KioskUpdateTestInstantiation,
+ KioskUpdateTest,
+ testing::Bool());
+
class KioskEnterpriseTest : public KioskTest {
protected:
KioskEnterpriseTest() {}
device_policy_test_helper_.device_policy()->policy_data();
policy_data.set_service_account_identity(kTestEnterpriseServiceAccountId);
device_policy_test_helper_.device_policy()->Build();
+
+ base::RunLoop run_loop;
DBusThreadManager::Get()->GetSessionManagerClient()->StoreDevicePolicy(
device_policy_test_helper_.device_policy()->GetBlob(),
- base::Bind(&KioskEnterpriseTest::StorePolicyCallback));
+ base::Bind(&KioskEnterpriseTest::StorePolicyCallback,
+ run_loop.QuitClosure()));
+ run_loop.Run();
DeviceSettingsService::Get()->Load();
"https://www.googleapis.com/auth/userinfo.email");
userinfo_token_info.audience = gaia_urls->oauth2_chrome_client_id();
userinfo_token_info.email = kTestEnterpriseServiceAccountId;
- fake_gaia_.IssueOAuthToken(kTestRefreshToken, userinfo_token_info);
+ fake_gaia_->IssueOAuthToken(kTestRefreshToken, userinfo_token_info);
// The any-api access token for accessing the token minting endpoint.
FakeGaia::AccessTokenInfo login_token_info;
login_token_info.token = kTestLoginToken;
login_token_info.scopes.insert(GaiaConstants::kAnyApiOAuth2Scope);
login_token_info.audience = gaia_urls->oauth2_chrome_client_id();
- fake_gaia_.IssueOAuthToken(kTestRefreshToken, login_token_info);
+ fake_gaia_->IssueOAuthToken(kTestRefreshToken, login_token_info);
// This is the access token requested by the app via the identity API.
FakeGaia::AccessTokenInfo access_token_info;
access_token_info.scopes.insert(kTestAppScope);
access_token_info.audience = kTestClientId;
access_token_info.email = kTestEnterpriseServiceAccountId;
- fake_gaia_.IssueOAuthToken(kTestLoginToken, access_token_info);
+ fake_gaia_->IssueOAuthToken(kTestLoginToken, access_token_info);
- DeviceOAuth2TokenService* token_service = NULL;
- DeviceOAuth2TokenServiceFactory::Get(
- base::Bind(&CopyTokenService, &token_service));
+ DeviceOAuth2TokenService* token_service =
+ DeviceOAuth2TokenServiceFactory::Get();
+ token_service->SetAndSaveRefreshToken(
+ kTestRefreshToken, DeviceOAuth2TokenService::StatusCallback());
base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(token_service);
- token_service->SetAndSaveRefreshToken(kTestRefreshToken);
}
- static void StorePolicyCallback(bool result) {
+ static void StorePolicyCallback(const base::Closure& callback, bool result) {
ASSERT_TRUE(result);
+ callback.Run();
}
policy::DevicePolicyCrosTestHelper device_policy_test_helper_;
base::Unretained(KioskAppManager::Get()),
kTestEnterpriseKioskApp, &app)).Wait();
- GetLoginUI()->CallJavascriptFunction(
- "login.AppsMenuButton.runAppForTesting",
- base::StringValue(kTestEnterpriseKioskApp));
+ LaunchApp(kTestEnterpriseKioskApp, false);
// Wait for the Kiosk App to launch.
content::WindowedNotificationObserver(
chromeos::KioskAppLaunchError::Get());
// Wait for the window to appear.
- apps::ShellWindow* window = ShellWindowObserver(
- apps::ShellWindowRegistry::Get(ProfileManager::GetPrimaryUserProfile()),
- kTestEnterpriseKioskApp).Wait();
+ apps::AppWindow* window =
+ AppWindowWaiter(
+ apps::AppWindowRegistry::Get(ProfileManager::GetPrimaryUserProfile()),
+ kTestEnterpriseKioskApp).Wait();
ASSERT_TRUE(window);
// Check whether the app can retrieve an OAuth2 access token.
&result));
EXPECT_EQ(kTestAccessToken, result);
+ // Verify that the session is not considered to be logged in with a GAIA
+ // account.
+ Profile* app_profile = ProfileManager::GetPrimaryUserProfile();
+ ASSERT_TRUE(app_profile);
+ EXPECT_FALSE(app_profile->GetPrefs()->HasPrefPath(
+ prefs::kGoogleServicesUsername));
+
// Terminate the app.
window->GetBaseWindow()->Close();
content::RunAllPendingInMessageLoop();
// KioskTest overrides:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
KioskTest::SetUpCommandLine(command_line);
- command_line->AppendSwitchASCII(switches::kDeviceRegistered, "1");
command_line->AppendSwitch(switches::kDisableBootAnimation);
- command_line->AppendSwitch(switches::kDisableOobeAnimation);
}
virtual void SetUpOnMainThread() OVERRIDE {
KioskTest::SetUpOnMainThread();
ash::Shell::GetInstance()->desktop_background_controller()
->AddObserver(this);
+ StartupUtils::MarkDeviceRegistered();
}
virtual void TearDownOnMainThread() OVERRIDE {