| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/app_mode/startup_app_launcher.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/test/ash_test_helper.h" |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/run_loop.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/repeating_test_future.h" |
| #include "base/test/scoped_command_line.h" |
| #include "base/test/test_future.h" |
| #include "base/version.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/apps/app_service/app_service_test.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h" |
| #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h" |
| #include "chrome/browser/ash/app_mode/kiosk_system_session.h" |
| #include "chrome/browser/ash/app_mode/test_kiosk_extension_builder.h" |
| #include "chrome/browser/ash/crosapi/browser_util.h" |
| #include "chrome/browser/ash/crosapi/chrome_app_kiosk_service_ash.h" |
| #include "chrome/browser/ash/crosapi/crosapi_ash.h" |
| #include "chrome/browser/ash/crosapi/crosapi_manager.h" |
| #include "chrome/browser/ash/crosapi/fake_browser_manager.h" |
| #include "chrome/browser/ash/crosapi/idle_service_ash.h" |
| #include "chrome/browser/ash/crosapi/test_crosapi_dependency_registry.h" |
| #include "chrome/browser/ash/extensions/test_external_cache.h" |
| #include "chrome/browser/ash/login/users/avatar/user_image_manager_impl.h" |
| #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" |
| #include "chrome/browser/ash/policy/core/device_local_account.h" |
| #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h" |
| #include "chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_service_test_base.h" |
| #include "chrome/browser/extensions/external_provider_impl.h" |
| #include "chrome/browser/extensions/install_tracker.h" |
| #include "chrome/browser/extensions/pending_extension_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/apps/chrome_app_delegate.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "chromeos/ash/components/login/login_state/login_state.h" |
| #include "chromeos/ash/components/settings/cros_settings_names.h" |
| #include "chromeos/ash/components/standalone_browser/feature_refs.h" |
| #include "chromeos/ash/components/standalone_browser/standalone_browser_features.h" |
| #include "chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom-forward.h" |
| #include "chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom-shared.h" |
| #include "components/account_id/account_id.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "extensions/browser/app_window/test_app_window_contents.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/external_install_info.h" |
| #include "extensions/browser/test_event_router.h" |
| #include "extensions/browser/updater/extension_downloader_delegate.h" |
| #include "extensions/common/api/app_runtime.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_set.h" |
| #include "extensions/common/manifest.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "test_kiosk_extension_builder.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| using extensions::Extension; |
| |
| namespace ash { |
| |
| namespace { |
| using ::extensions::ExternalInstallInfoFile; |
| using ::extensions::ExternalInstallInfoUpdateUrl; |
| using ::extensions::Manifest; |
| using ::extensions::mojom::ManifestLocation; |
| using ::testing::AssertionFailure; |
| using ::testing::AssertionResult; |
| using ::testing::AssertionSuccess; |
| |
| constexpr char kTestPrimaryAppId[] = "abcdefghabcdefghabcdefghabcdefgh"; |
| |
| constexpr char kSecondaryAppId[] = "aaaabbbbaaaabbbbaaaabbbbaaaabbbb"; |
| |
| constexpr char kExtraSecondaryAppId[] = "aaaaccccaaaaccccaaaaccccaaaacccc"; |
| |
| constexpr char kTestUserAccount[] = "user@test"; |
| |
| constexpr char kCwsUrl[] = "http://cws/"; |
| |
| enum class LaunchState { |
| kNotStarted, |
| kInitializingNetwork, |
| kInstallingApp, |
| kReadyToLaunch, |
| kLaunchSucceeded, |
| kLaunchFailed |
| }; |
| |
| class TestAppLaunchDelegate : public KioskAppLauncher::NetworkDelegate, |
| public KioskAppLauncher::Observer { |
| public: |
| TestAppLaunchDelegate() = default; |
| TestAppLaunchDelegate(const TestAppLaunchDelegate&) = delete; |
| TestAppLaunchDelegate& operator=(const TestAppLaunchDelegate&) = delete; |
| ~TestAppLaunchDelegate() override = default; |
| |
| KioskAppLaunchError::Error launch_error() const { return launch_error_; } |
| |
| void set_network_ready(bool network_ready) { network_ready_ = network_ready; } |
| |
| void ClearLaunchStateChanges() { |
| while (!launch_state_changes_.IsEmpty()) { |
| launch_state_changes_.Take(); |
| } |
| } |
| |
| LaunchState WaitForNextLaunchState() { return launch_state_changes_.Take(); } |
| |
| bool ExpectNoLaunchStateChanges() { |
| // Wait a bit to give the state changes a chance to arrive |
| base::RunLoop().RunUntilIdle(); |
| return launch_state_changes_.IsEmpty(); |
| } |
| |
| // `KioskAppLauncher::NetworkDelegate`: |
| void InitializeNetwork() override { |
| SetLaunchState(LaunchState::kInitializingNetwork); |
| } |
| bool IsNetworkReady() const override { return network_ready_; } |
| |
| // `KioskAppLauncher::Observer`: |
| void OnAppInstalling() override { |
| SetLaunchState(LaunchState::kInstallingApp); |
| } |
| void OnAppPrepared() override { SetLaunchState(LaunchState::kReadyToLaunch); } |
| void OnAppLaunched() override { |
| SetLaunchState(LaunchState::kLaunchSucceeded); |
| } |
| void OnLaunchFailed(KioskAppLaunchError::Error error) override { |
| launch_error_ = error; |
| SetLaunchState(LaunchState::kLaunchFailed); |
| } |
| |
| private: |
| void SetLaunchState(LaunchState state) { |
| launch_state_changes_.AddValue(state); |
| } |
| |
| KioskAppLaunchError::Error launch_error_ = KioskAppLaunchError::Error::kNone; |
| |
| bool network_ready_ = false; |
| |
| base::test::RepeatingTestFuture<LaunchState> launch_state_changes_; |
| }; |
| |
| class AppLaunchTracker : public extensions::TestEventRouter::EventObserver { |
| public: |
| AppLaunchTracker(const std::string& app_id, |
| extensions::TestEventRouter* event_router) |
| : app_id_(app_id), event_router_(event_router) { |
| event_router->AddEventObserver(this); |
| } |
| AppLaunchTracker(const AppLaunchTracker&) = delete; |
| AppLaunchTracker& operator=(const AppLaunchTracker&) = delete; |
| ~AppLaunchTracker() override { event_router_->RemoveEventObserver(this); } |
| |
| int kiosk_launch_count() const { return kiosk_launch_count_; } |
| |
| // TestEventRouter::EventObserver: |
| void OnBroadcastEvent(const extensions::Event& event) override { |
| ADD_FAILURE() << "Unexpected broadcast " << event.event_name; |
| } |
| |
| void OnDispatchEventToExtension(const std::string& extension_id, |
| const extensions::Event& event) override { |
| ASSERT_EQ(extension_id, app_id_); |
| |
| ASSERT_EQ(event.event_name, |
| extensions::api::app_runtime::OnLaunched::kEventName); |
| ASSERT_EQ(1u, event.event_args.size()); |
| |
| const base::Value& launch_data = event.event_args[0]; |
| std::optional<bool> is_kiosk_session = |
| launch_data.GetDict().FindBool("isKioskSession"); |
| ASSERT_TRUE(is_kiosk_session); |
| EXPECT_TRUE(*is_kiosk_session); |
| ++kiosk_launch_count_; |
| } |
| |
| private: |
| const std::string app_id_; |
| raw_ptr<extensions::TestEventRouter> event_router_; |
| int kiosk_launch_count_ = 0; |
| }; |
| |
| // Simulates extension service behavior related to external extensions loading, |
| // but does not initiate found extension's CRX installation - instead, it keeps |
| // track of pending extension installations, and expect the test code to finish |
| // the pending extension installations. |
| class TestKioskLoaderVisitor |
| : public extensions::ExternalProviderInterface::VisitorInterface { |
| public: |
| TestKioskLoaderVisitor(content::BrowserContext* browser_context, |
| extensions::ExtensionRegistry* extension_registry, |
| extensions::ExtensionService* extension_service) |
| : browser_context_(browser_context), |
| extension_registry_(extension_registry), |
| extension_service_(extension_service) {} |
| TestKioskLoaderVisitor(const TestKioskLoaderVisitor&) = delete; |
| TestKioskLoaderVisitor& operator=(const TestKioskLoaderVisitor&) = delete; |
| ~TestKioskLoaderVisitor() override = default; |
| |
| const std::set<std::string>& pending_crx_files() const { |
| return pending_crx_files_; |
| } |
| const std::set<std::string>& pending_update_urls() const { |
| return pending_update_urls_; |
| } |
| |
| bool FinishPendingInstall(const Extension* extension) { |
| if (!pending_crx_files_.count(extension->id()) && |
| !pending_update_urls_.count(extension->id())) { |
| return false; |
| } |
| |
| if (!extension_service_->pending_extension_manager()->IsIdPending( |
| extension->id())) { |
| return false; |
| } |
| |
| pending_crx_files_.erase(extension->id()); |
| pending_update_urls_.erase(extension->id()); |
| extension_service_->OnExtensionInstalled( |
| extension, syncer::StringOrdinal::CreateInitialOrdinal(), |
| extensions::kInstallFlagInstallImmediately); |
| auto installer = extensions::CrxInstaller::CreateSilent(extension_service_); |
| extensions::InstallTracker::Get(browser_context_) |
| ->OnFinishCrxInstall(*installer, extension->id(), true); |
| return true; |
| } |
| |
| bool FailPendingInstall(const std::string& extension_id) { |
| if (!pending_crx_files_.count(extension_id) && |
| !pending_update_urls_.count(extension_id)) { |
| return false; |
| } |
| |
| if (!extension_service_->pending_extension_manager()->IsIdPending( |
| extension_id)) { |
| return false; |
| } |
| |
| pending_crx_files_.erase(extension_id); |
| pending_update_urls_.erase(extension_id); |
| auto installer = extensions::CrxInstaller::CreateSilent(extension_service_); |
| extensions::InstallTracker::Get(browser_context_) |
| ->OnFinishCrxInstall(*installer, extension_id, false); |
| extension_service_->pending_extension_manager()->Remove(extension_id); |
| return true; |
| } |
| |
| // extensions::ExternalProviderInterface::VisitorInterface: |
| bool OnExternalExtensionFileFound( |
| const ExternalInstallInfoFile& info) override { |
| const extensions::Extension* existing = |
| extension_registry_->GetExtensionById( |
| info.extension_id, extensions::ExtensionRegistry::EVERYTHING); |
| // Already exists, and does not require update. |
| if (existing && existing->version().CompareTo(info.version) >= 0) { |
| return false; |
| } |
| |
| if (!extension_service_->pending_extension_manager()->AddFromExternalFile( |
| info.extension_id, info.crx_location, info.version, |
| info.creation_flags, info.mark_acknowledged)) { |
| return false; |
| } |
| |
| pending_crx_files_.insert(info.extension_id); |
| auto installer = extensions::CrxInstaller::CreateSilent(extension_service_); |
| extensions::InstallTracker::Get(browser_context_) |
| ->OnBeginCrxInstall(*installer, info.extension_id); |
| return true; |
| } |
| bool OnExternalExtensionUpdateUrlFound( |
| const ExternalInstallInfoUpdateUrl& info, |
| bool force_update) override { |
| if (extension_registry_->GetExtensionById( |
| info.extension_id, extensions::ExtensionRegistry::EVERYTHING)) { |
| return false; |
| } |
| |
| if (!extension_service_->pending_extension_manager() |
| ->AddFromExternalUpdateUrl( |
| info.extension_id, info.install_parameter, info.update_url, |
| info.download_location, info.creation_flags, |
| info.mark_acknowledged)) { |
| return false; |
| } |
| |
| pending_update_urls_.insert(info.extension_id); |
| auto installer = extensions::CrxInstaller::CreateSilent(extension_service_); |
| extensions::InstallTracker::Get(browser_context_) |
| ->OnBeginCrxInstall(*installer, info.extension_id); |
| return true; |
| } |
| void OnExternalProviderReady( |
| const extensions::ExternalProviderInterface* provider) override {} |
| void OnExternalProviderUpdateComplete( |
| const extensions::ExternalProviderInterface* provider, |
| const std::vector<ExternalInstallInfoUpdateUrl>& update_url_extensions, |
| const std::vector<ExternalInstallInfoFile>& file_extensions, |
| const std::set<std::string>& removed_extensions) override { |
| for (const auto& extension : update_url_extensions) { |
| OnExternalExtensionUpdateUrlFound(extension, false); |
| } |
| |
| for (const auto& extension : file_extensions) { |
| OnExternalExtensionFileFound(extension); |
| } |
| |
| for (const auto& extension_id : removed_extensions) { |
| extension_service_->UninstallExtension( |
| extension_id, |
| extensions::UNINSTALL_REASON_ORPHANED_EXTERNAL_EXTENSION, nullptr); |
| } |
| } |
| |
| private: |
| const raw_ptr<content::BrowserContext> browser_context_; |
| const raw_ptr<extensions::ExtensionRegistry> extension_registry_; |
| const raw_ptr<extensions::ExtensionService> extension_service_; |
| |
| std::set<std::string> pending_crx_files_; |
| std::set<std::string> pending_update_urls_; |
| }; |
| |
| void InitAppWindow(extensions::AppWindow* app_window, const gfx::Rect& bounds) { |
| // Create a TestAppWindowContents for the ShellAppDelegate to initialize the |
| // ShellExtensionWebContentsObserver with. |
| std::unique_ptr<content::WebContents> web_contents( |
| content::WebContents::Create( |
| content::WebContents::CreateParams(app_window->browser_context()))); |
| auto app_window_contents = |
| std::make_unique<extensions::TestAppWindowContents>( |
| std::move(web_contents)); |
| |
| // Initialize the web contents and AppWindow. |
| app_window->app_delegate()->InitWebContents( |
| app_window_contents->GetWebContents()); |
| |
| content::RenderFrameHost* main_frame = |
| app_window_contents->GetWebContents()->GetPrimaryMainFrame(); |
| DCHECK(main_frame); |
| |
| extensions::AppWindow::CreateParams params; |
| params.content_spec.bounds = bounds; |
| app_window->Init(GURL(), std::move(app_window_contents), main_frame, params); |
| } |
| |
| extensions::AppWindow* CreateAppWindow(Profile* profile, |
| const Extension& app, |
| gfx::Rect bounds = {}) { |
| extensions::AppWindow* app_window = new extensions::AppWindow( |
| profile, std::make_unique<ChromeAppDelegate>(profile, true), &app); |
| InitAppWindow(app_window, bounds); |
| return app_window; |
| } |
| |
| // This class overrides some of the behaviour of `KioskChromeAppManager`, which |
| // is the `KioskAppManagerBase` implementation for ChromeApp kiosk. Notably it |
| // injects its own `ExternalCache` implementation and overrides the construction |
| // on an `KioskBrowserSession` object. |
| class ScopedKioskAppManagerOverrides : public KioskChromeAppManager::Overrides { |
| public: |
| ScopedKioskAppManagerOverrides() { |
| KioskChromeAppManager::InitializeForTesting(this); |
| CHECK(temp_dir_.CreateUniqueTempDir()); |
| } |
| |
| chromeos::TestExternalCache* external_cache() { return external_cache_; } |
| |
| void InitializePrimaryAppState() { |
| // Inject test kiosk app data to prevent KioskChromeAppManager from |
| // attempting to load it. |
| // TODO(tbarzic): Introducing a test KioskAppData class that overrides app |
| // data load logic, and injecting a KioskAppData object factory to |
| // KioskChromeAppManager would be a cleaner solution here. |
| KioskChromeAppManager::Get()->AddAppForTest( |
| kTestPrimaryAppId, AccountId::FromUserEmail(kTestUserAccount), |
| GURL(kCwsUrl), |
| /*required_platform_version=*/""); |
| |
| accounts_settings_helper_ = std::make_unique<ScopedCrosSettingsTestHelper>( |
| /*create_service=*/false); |
| accounts_settings_helper_->ReplaceDeviceSettingsProviderWithStub(); |
| |
| base::Value::Dict account; |
| account.Set(kAccountsPrefDeviceLocalAccountsKeyId, kTestUserAccount); |
| account.Set(kAccountsPrefDeviceLocalAccountsKeyType, |
| policy::DeviceLocalAccount::TYPE_KIOSK_APP); |
| account.Set( |
| kAccountsPrefDeviceLocalAccountsKeyEphemeralMode, |
| static_cast<int>(policy::DeviceLocalAccount::EphemeralMode::kUnset)); |
| account.Set(kAccountsPrefDeviceLocalAccountsKeyKioskAppId, |
| kTestPrimaryAppId); |
| base::Value::List accounts; |
| accounts.Append(std::move(account)); |
| |
| accounts_settings_helper_->Set(kAccountsPrefDeviceLocalAccounts, |
| base::Value(std::move(accounts))); |
| |
| // Set auto-launch kiosk |
| accounts_settings_helper_->SetString( |
| kAccountsPrefDeviceLocalAccountAutoLoginId, kTestUserAccount); |
| accounts_settings_helper_->SetInteger( |
| kAccountsPrefDeviceLocalAccountAutoLoginDelay, 0); |
| } |
| |
| [[nodiscard]] AssertionResult DownloadPrimaryApp(const Extension& app) { |
| if (!external_cache_) { |
| return AssertionFailure() << "External cache not initialized"; |
| } |
| |
| if (!external_cache_->pending_downloads().count(app.id())) { |
| return AssertionFailure() << "Download not pending: " << app.id(); |
| } |
| |
| if (!external_cache_->SimulateExtensionDownloadFinished( |
| app.id(), GetExtensionPath(app.id()), app.VersionString(), |
| /*is_update=*/false)) { |
| return AssertionFailure() << " Finish download attempt failed"; |
| } |
| |
| return AssertionSuccess(); |
| } |
| |
| [[nodiscard]] AssertionResult PrecachePrimaryApp( |
| const extensions::Extension& app) { |
| if (!external_cache_) { |
| return AssertionFailure() << "External cache not initialized"; |
| } |
| |
| base::test::TestFuture<const std::string&, bool> future; |
| external_cache_->PutExternalExtension( |
| app.id(), base::FilePath(GetExtensionPath(app.id())), |
| app.VersionString(), future.GetCallback()); |
| |
| if (!std::get<1>(future.Get())) { |
| return AssertionFailure() << "Precaching extension failed"; |
| } |
| |
| return AssertionSuccess(); |
| } |
| |
| // KioskChromeAppManager::Overrides: |
| std::unique_ptr<chromeos::ExternalCache> CreateExternalCache( |
| chromeos::ExternalCacheDelegate* delegate, |
| bool always_check_updates) override { |
| auto cache = std::make_unique<chromeos::TestExternalCache>( |
| delegate, always_check_updates); |
| external_cache_ = cache.get(); |
| return cache; |
| } |
| |
| std::unique_ptr<KioskSystemSession> CreateKioskSystemSession() override { |
| EXPECT_FALSE(kiosk_system_session_initialized_); |
| kiosk_system_session_initialized_ = true; |
| return nullptr; |
| } |
| |
| private: |
| // Note: These tests should not actually create files, so the actual returned |
| // path is not too important. Still, putting it under the test's temp dir, in |
| // case something unexpectedly tries to do file I/O with the file paths |
| // returned here. |
| std::string GetExtensionPath(const std::string& app_id) { |
| return temp_dir_.GetPath() |
| .AppendASCII("test_crx_file") |
| .AppendASCII(app_id) |
| .value(); |
| } |
| |
| base::ScopedTempDir temp_dir_; |
| std::unique_ptr<ScopedCrosSettingsTestHelper> accounts_settings_helper_; |
| |
| raw_ptr<chromeos::TestExternalCache, DanglingUntriaged> external_cache_; |
| bool kiosk_system_session_initialized_ = false; |
| }; |
| |
| TestKioskExtensionBuilder PrimaryAppBuilder() { |
| return std::move( |
| TestKioskExtensionBuilder(extensions::Manifest::TYPE_PLATFORM_APP, |
| kTestPrimaryAppId) |
| .set_version("1.0")); |
| } |
| |
| TestKioskExtensionBuilder ExtensionBuilder() { |
| return TestKioskExtensionBuilder(extensions::Manifest::TYPE_EXTENSION, |
| kTestPrimaryAppId); |
| } |
| |
| TestKioskExtensionBuilder SecondaryAppBuilder(const std::string& id) { |
| return TestKioskExtensionBuilder(extensions::Manifest::TYPE_PLATFORM_APP, id); |
| } |
| |
| } // namespace |
| |
| using crosapi::mojom::AppInstallParamsPtr; |
| using crosapi::mojom::ChromeKioskInstallResult; |
| using crosapi::mojom::ChromeKioskLaunchController; |
| using crosapi::mojom::ChromeKioskLaunchResult; |
| |
| // Tests without creating `StartupAppLauncher` object. |
| class StartupAppLauncherNoCreateTest |
| : public extensions::ExtensionServiceTestBase { |
| public: |
| StartupAppLauncherNoCreateTest() |
| : extensions::ExtensionServiceTestBase( |
| std::make_unique<content::BrowserTaskEnvironment>( |
| content::BrowserTaskEnvironment::REAL_IO_THREAD)) {} |
| |
| StartupAppLauncherNoCreateTest(const StartupAppLauncherNoCreateTest&) = |
| delete; |
| StartupAppLauncherNoCreateTest& operator=( |
| const StartupAppLauncherNoCreateTest&) = delete; |
| ~StartupAppLauncherNoCreateTest() override = default; |
| |
| // testing::Test: |
| void SetUp() override { |
| ash_test_helper_.SetUp(); |
| |
| UserImageManagerImpl::SkipDefaultUserImageDownloadForTesting(); |
| command_line_.GetProcessCommandLine()->AppendSwitch( |
| switches::kForceAppMode); |
| command_line_.GetProcessCommandLine()->AppendSwitch(switches::kAppId); |
| |
| extensions::ExtensionServiceTestBase::SetUp(); |
| |
| kiosk_app_manager_overrides_.InitializePrimaryAppState(); |
| |
| InitializeEmptyExtensionService(); |
| external_apps_loader_handler_ = std::make_unique<TestKioskLoaderVisitor>( |
| browser_context(), registry(), service()); |
| CreateAndInitializeKioskAppsProviders(external_apps_loader_handler_.get()); |
| |
| extensions::TestEventRouter* event_router = |
| extensions::CreateAndUseTestEventRouter(browser_context()); |
| app_launch_tracker_ = |
| std::make_unique<AppLaunchTracker>(kTestPrimaryAppId, event_router); |
| } |
| |
| void TearDown() override { |
| primary_app_provider_->ServiceShutdown(); |
| secondary_apps_provider_->ServiceShutdown(); |
| external_apps_loader_handler_.reset(); |
| |
| app_launch_tracker_.reset(); |
| |
| extensions::ExtensionServiceTestBase::TearDown(); |
| |
| ash_test_helper_.TearDown(); |
| } |
| |
| protected: |
| chromeos::TestExternalCache* external_cache() { |
| return kiosk_app_manager_overrides_.external_cache(); |
| } |
| |
| ScopedKioskAppManagerOverrides& kiosk_app_manager_overrides() { |
| return kiosk_app_manager_overrides_; |
| } |
| |
| [[nodiscard]] AssertionResult DownloadPrimaryApp(const Extension& app) { |
| return kiosk_app_manager_overrides_.DownloadPrimaryApp(app); |
| } |
| |
| [[nodiscard]] AssertionResult FinishPrimaryAppInstall(const Extension& app) { |
| const std::string& id = app.id(); |
| if (!external_apps_loader_handler_->pending_crx_files().count(id)) { |
| return AssertionFailure() << "App install not pending: " << id; |
| } |
| |
| if (!external_apps_loader_handler_->FinishPendingInstall(&app)) { |
| return AssertionFailure() << "Finish install attempt failed: " << id; |
| } |
| |
| return AssertionSuccess(); |
| } |
| |
| [[nodiscard]] AssertionResult DownloadAndInstallPrimaryApp( |
| const Extension& app) { |
| AssertionResult download_result = |
| kiosk_app_manager_overrides_.DownloadPrimaryApp(app); |
| if (!download_result) { |
| return download_result; |
| } |
| |
| AssertionResult install_result = FinishPrimaryAppInstall(app); |
| if (!install_result) { |
| return install_result; |
| } |
| |
| return AssertionSuccess(); |
| } |
| |
| [[nodiscard]] AssertionResult FinishSecondaryExtensionInstall( |
| const Extension& extension) { |
| const std::string& id = extension.id(); |
| if (!external_apps_loader_handler_->pending_update_urls().count(id)) { |
| return AssertionFailure() |
| << "Secondary extension install not pending: " << id; |
| } |
| |
| if (!external_apps_loader_handler_->FinishPendingInstall(&extension)) { |
| return AssertionFailure() << "Finish install attempt failed: " << id; |
| } |
| |
| return AssertionSuccess(); |
| } |
| |
| void CreateAndInitializeKioskAppsProviders(TestKioskLoaderVisitor* visitor) { |
| primary_app_provider_ = std::make_unique<extensions::ExternalProviderImpl>( |
| visitor, |
| base::MakeRefCounted<chromeos::KioskAppExternalLoader>( |
| chromeos::KioskAppExternalLoader::AppClass::kPrimary), |
| profile(), ManifestLocation::kExternalPolicy, |
| ManifestLocation::kInvalidLocation, extensions::Extension::NO_FLAGS); |
| InitializeKioskAppsProvider(primary_app_provider_.get()); |
| |
| secondary_apps_provider_ = |
| std::make_unique<extensions::ExternalProviderImpl>( |
| visitor, |
| base::MakeRefCounted<chromeos::KioskAppExternalLoader>( |
| chromeos::KioskAppExternalLoader::AppClass::kSecondary), |
| profile(), ManifestLocation::kExternalPref, |
| ManifestLocation::kExternalPrefDownload, |
| extensions::Extension::NO_FLAGS); |
| InitializeKioskAppsProvider(secondary_apps_provider_.get()); |
| } |
| |
| void InitializeKioskAppsProvider(extensions::ExternalProviderImpl* provider) { |
| provider->set_auto_acknowledge(true); |
| provider->set_install_immediately(true); |
| provider->set_allow_updates(true); |
| provider->VisitRegisteredExtension(); |
| } |
| |
| auto CreateStartupAppLauncher() { |
| return CreateStartupAppLauncherInternal(/*should_skip_install=*/false); |
| } |
| |
| auto CreateStartupAppLauncherForSessionRestore() { |
| return CreateStartupAppLauncherInternal(/*should_skip_install=*/true); |
| } |
| |
| void PreinstallApp(const Extension& app) { service()->AddExtension(&app); } |
| |
| TestAppLaunchDelegate startup_launch_delegate_; |
| |
| std::unique_ptr<AppLaunchTracker> app_launch_tracker_; |
| std::unique_ptr<TestKioskLoaderVisitor> external_apps_loader_handler_; |
| |
| private: |
| std::unique_ptr<KioskAppLauncher> CreateStartupAppLauncherInternal( |
| bool should_skip_install) { |
| std::unique_ptr<KioskAppLauncher> startup_app_launcher = |
| std::make_unique<StartupAppLauncher>(profile(), kTestPrimaryAppId, |
| should_skip_install, |
| &startup_launch_delegate_); |
| startup_app_launcher->AddObserver(&startup_launch_delegate_); |
| return startup_app_launcher; |
| } |
| |
| AshTestHelper ash_test_helper_; |
| base::test::ScopedCommandLine command_line_; |
| |
| ScopedKioskAppManagerOverrides kiosk_app_manager_overrides_; |
| |
| std::unique_ptr<extensions::ExternalProviderImpl> primary_app_provider_; |
| std::unique_ptr<extensions::ExternalProviderImpl> secondary_apps_provider_; |
| }; |
| |
| // Tests that extension download backoff is reduced during Chrome app Kiosk |
| // launch. |
| TEST_F(StartupAppLauncherNoCreateTest, ExtensionDownloadBackoffReduced) { |
| ASSERT_TRUE(external_cache()); |
| EXPECT_FALSE(external_cache()->backoff_policy().has_value()); |
| |
| auto startup_app_launcher = CreateStartupAppLauncher(); |
| |
| ASSERT_TRUE(external_cache()->backoff_policy().has_value()); |
| EXPECT_EQ(external_cache()->backoff_policy()->maximum_backoff_ms, 3000); |
| |
| startup_app_launcher.reset(); |
| EXPECT_FALSE(external_cache()->backoff_policy().has_value()); |
| } |
| |
| TEST_F(StartupAppLauncherNoCreateTest, AppNotKioskEnabledOnSessionRestore) { |
| PreinstallApp(*PrimaryAppBuilder().set_kiosk_enabled(false).Build()); |
| auto startup_app_launcher = CreateStartupAppLauncherForSessionRestore(); |
| |
| startup_app_launcher->Initialize(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher->LaunchApp(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| |
| EXPECT_EQ(startup_launch_delegate_.launch_error(), |
| KioskAppLaunchError::Error::kUnableToLaunch); |
| } |
| |
| // Tests with `StartupAppLauncher` object created. |
| class StartupAppLauncherTest : public StartupAppLauncherNoCreateTest { |
| public: |
| // testing::Test: |
| void SetUp() override { |
| StartupAppLauncherNoCreateTest::SetUp(); |
| // Some tests depend on AppService, so wait AppService to be ready. |
| WaitForAppServiceProxyReady( |
| apps::AppServiceProxyFactory::GetForProfile(profile())); |
| |
| startup_app_launcher_ = CreateStartupAppLauncher(); |
| } |
| |
| void TearDown() override { |
| startup_app_launcher_.reset(); |
| StartupAppLauncherNoCreateTest::TearDown(); |
| } |
| |
| protected: |
| void InitializeLauncherWithNetworkReady() { |
| startup_launch_delegate_.set_network_ready(true); |
| startup_app_launcher_->Initialize(); |
| EXPECT_TRUE(startup_launch_delegate_.ExpectNoLaunchStateChanges()); |
| } |
| |
| std::unique_ptr<KioskAppLauncher> startup_app_launcher_; |
| }; |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppLaunchFlow) { |
| InitializeLauncherWithNetworkReady(); |
| |
| ASSERT_TRUE(external_cache()); |
| EXPECT_EQ(std::set<std::string>({kTestPrimaryAppId}), |
| external_cache()->pending_downloads()); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| |
| scoped_refptr<const Extension> primary_app = PrimaryAppBuilder().Build(); |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, OfflineLaunchWithPrimaryAppPreInstalled) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().set_version("1.0").Build(); |
| PreinstallApp(*primary_app); |
| |
| startup_app_launcher_->Initialize(); |
| |
| // Given that the app is offline enabled and installed, the app should be |
| // launched immediately, without waiting for network or checking for updates. |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| // Primary app cache checks finished after the startup app launcher reports |
| // it's ready should be ignored - i.e. startup app launcher should not attempt |
| // to relaunch the app, nor request the update installation. |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| ASSERT_TRUE( |
| DownloadPrimaryApp(*PrimaryAppBuilder().set_version("1.1").Build())); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_TRUE(startup_launch_delegate_.ExpectNoLaunchStateChanges()); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| OfflineLaunchWithPrimaryAppPreInstalled_UpdateFoundAfterLaunch) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().set_version("1.0").Build(); |
| PreinstallApp(*primary_app); |
| |
| startup_app_launcher_->Initialize(); |
| |
| // Given that the app is offline enabled and installed, the app should be |
| // launched immediately, without waiting for network or checking for updates. |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| |
| // Primary app cache checks finished after the app launch |
| // it's ready should be ignored - i.e. startup app launcher should not attempt |
| // to relaunch the app, nor request the update installation. |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| ASSERT_TRUE( |
| DownloadPrimaryApp(*PrimaryAppBuilder().set_version("1.1").Build())); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_TRUE(startup_launch_delegate_.ExpectNoLaunchStateChanges()); |
| } |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppDownloadFailure) { |
| base::HistogramTester histogram; |
| InitializeLauncherWithNetworkReady(); |
| |
| ASSERT_TRUE(external_cache()); |
| EXPECT_EQ(std::set<std::string>({kTestPrimaryAppId}), |
| external_cache()->pending_downloads()); |
| ASSERT_TRUE(external_cache()->SimulateExtensionDownloadFailed( |
| kTestPrimaryAppId, |
| extensions::ExtensionDownloaderDelegate::Error::CRX_FETCH_FAILED)); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| |
| EXPECT_EQ(KioskAppLaunchError::Error::kUnableToDownload, |
| startup_launch_delegate_.launch_error()); |
| |
| histogram.ExpectUniqueSample( |
| kKioskPrimaryAppInstallErrorHistogram, |
| KioskChromeAppManager::PrimaryAppDownloadResult::kCrxFetchFailed, |
| /*expected_bucket_count=*/1); |
| } |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppCrxInstallFailure) { |
| InitializeLauncherWithNetworkReady(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*PrimaryAppBuilder().Build())); |
| startup_launch_delegate_.ClearLaunchStateChanges(); |
| |
| ASSERT_TRUE( |
| external_apps_loader_handler_->FailPendingInstall(kTestPrimaryAppId)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| |
| EXPECT_EQ(KioskAppLaunchError::Error::kUnableToInstall, |
| startup_launch_delegate_.launch_error()); |
| } |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppNotKioskEnabled) { |
| InitializeLauncherWithNetworkReady(); |
| |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().set_kiosk_enabled(false).Build(); |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| |
| EXPECT_EQ(KioskAppLaunchError::Error::kNotKioskEnabled, |
| startup_launch_delegate_.launch_error()); |
| } |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppIsExtension) { |
| InitializeLauncherWithNetworkReady(); |
| |
| scoped_refptr<const Extension> primary_app = ExtensionBuilder().Build(); |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| |
| EXPECT_EQ(KioskAppLaunchError::Error::kNotKioskEnabled, |
| startup_launch_delegate_.launch_error()); |
| } |
| |
| TEST_F(StartupAppLauncherTest, LaunchWithSecondaryApps) { |
| InitializeLauncherWithNetworkReady(); |
| |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder() |
| .AddSecondaryExtension(kSecondaryAppId) |
| .AddSecondaryExtensionWithEnabledOnLaunch(kExtraSecondaryAppId, false) |
| .Build(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| scoped_refptr<const Extension> secondary_app = |
| SecondaryAppBuilder(kSecondaryAppId).set_kiosk_enabled(false).Build(); |
| ASSERT_TRUE(FinishSecondaryExtensionInstall(*secondary_app)); |
| |
| scoped_refptr<const Extension> disabled_secondary_app = |
| SecondaryAppBuilder(kExtraSecondaryAppId).Build(); |
| ASSERT_TRUE(FinishSecondaryExtensionInstall(*disabled_secondary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kExtraSecondaryAppId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_USER_ACTION, |
| extensions::ExtensionPrefs::Get(browser_context()) |
| ->GetDisableReasons(kExtraSecondaryAppId)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kExtraSecondaryAppId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_USER_ACTION, |
| extensions::ExtensionPrefs::Get(browser_context()) |
| ->GetDisableReasons(kExtraSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, LaunchWithSecondaryExtension) { |
| InitializeLauncherWithNetworkReady(); |
| |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().AddSecondaryExtension(kSecondaryAppId).Build(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| scoped_refptr<const Extension> secondary_extension = |
| SecondaryAppBuilder(kSecondaryAppId).set_kiosk_enabled(false).Build(); |
| ASSERT_TRUE(FinishSecondaryExtensionInstall(*secondary_extension)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, OfflineWithPrimaryAndSecondaryAppInstalled) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder() |
| .set_version("1.0") |
| .AddSecondaryExtension(kSecondaryAppId) |
| .Build(); |
| PreinstallApp(*primary_app); |
| PreinstallApp( |
| *SecondaryAppBuilder(kSecondaryAppId).set_kiosk_enabled(false).Build()); |
| |
| startup_app_launcher_->Initialize(); |
| |
| // Given that the app is offline enabled and installed, the app should be |
| // launched immediately, without waiting for network or checking for updates. |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| // Primary app cache checks finished after the startup app launcher reports |
| // it's ready should be ignored - i.e. startup app launcher should not attempt |
| // to relaunch the app, nor request the update installation. |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| ASSERT_TRUE( |
| DownloadPrimaryApp(*PrimaryAppBuilder().set_version("1.1").Build())); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_TRUE(startup_launch_delegate_.ExpectNoLaunchStateChanges()); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, OfflineInstallPreCachedExtension) { |
| scoped_refptr<const Extension> primary_app = PrimaryAppBuilder().Build(); |
| |
| ASSERT_TRUE(kiosk_app_manager_overrides().PrecachePrimaryApp(*primary_app)); |
| |
| startup_app_launcher_->Initialize(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| OfflineInstallPreCachedExtensionNotOfflineEnabled) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().set_offline_enabled(false).Build(); |
| |
| ASSERT_TRUE(kiosk_app_manager_overrides().PrecachePrimaryApp(*primary_app)); |
| |
| startup_app_launcher_->Initialize(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| // When trying to launch app we should realize that the app is not offline |
| // enabled and request a network connection. |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInitializingNetwork); |
| |
| startup_launch_delegate_.set_network_ready(true); |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| OfflineInstallPreCachedExtensionWithSecondaryApps) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder() |
| .set_offline_enabled(true) |
| .AddSecondaryExtension(kSecondaryAppId) |
| .Build(); |
| |
| scoped_refptr<const Extension> secondary_extension = |
| SecondaryAppBuilder(kSecondaryAppId).Build(); |
| |
| ASSERT_TRUE(kiosk_app_manager_overrides().PrecachePrimaryApp(*primary_app)); |
| |
| startup_app_launcher_->Initialize(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| ASSERT_TRUE( |
| external_apps_loader_handler_->FailPendingInstall(kSecondaryAppId)); |
| |
| // After install is complete we should realize that the app needs to install |
| // secondary apps, so we need to get network set up |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInitializingNetwork); |
| |
| startup_launch_delegate_.set_network_ready(true); |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishSecondaryExtensionInstall(*secondary_extension)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| OfflineInstallUncachedExtensionShouldForceNetwork) { |
| scoped_refptr<const Extension> primary_app = PrimaryAppBuilder().Build(); |
| |
| startup_app_launcher_->Initialize(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInitializingNetwork); |
| |
| startup_launch_delegate_.set_network_ready(true); |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| } |
| |
| TEST_F(StartupAppLauncherTest, IgnoreSecondaryAppsSecondaryApps) { |
| InitializeLauncherWithNetworkReady(); |
| |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().AddSecondaryExtension(kSecondaryAppId).Build(); |
| |
| ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app)); |
| |
| startup_launch_delegate_.ClearLaunchStateChanges(); |
| |
| scoped_refptr<const Extension> secondary_extension = |
| SecondaryAppBuilder(kSecondaryAppId) |
| .set_kiosk_enabled(true) |
| .AddSecondaryExtension(kExtraSecondaryAppId) |
| .Build(); |
| |
| ASSERT_TRUE(FinishSecondaryExtensionInstall(*secondary_extension)); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| CreateAppWindow(profile(), *primary_app); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_FALSE(registry()->GetInstalledExtension(kExtraSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, SecondaryAppCrxInstallFailureTriggersRetry) { |
| InitializeLauncherWithNetworkReady(); |
| |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder().AddSecondaryExtension(kSecondaryAppId).Build(); |
| |
| ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app)); |
| startup_launch_delegate_.ClearLaunchStateChanges(); |
| |
| ASSERT_EQ(std::set<std::string>({kSecondaryAppId}), |
| external_apps_loader_handler_->pending_update_urls()); |
| ASSERT_TRUE( |
| external_apps_loader_handler_->FailPendingInstall(kSecondaryAppId)); |
| |
| // The retry mechanism should trigger a new request to initialize the network |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInitializingNetwork); |
| |
| startup_app_launcher_->ContinueWithNetworkReady(); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| |
| ASSERT_EQ(std::set<std::string>({kSecondaryAppId}), |
| external_apps_loader_handler_->pending_update_urls()); |
| scoped_refptr<const Extension> secondary_app = |
| SecondaryAppBuilder(kSecondaryAppId).set_kiosk_enabled(false).Build(); |
| ASSERT_TRUE(FinishSecondaryExtensionInstall(*secondary_app)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| SecondaryAppEnabledOnLaunchOverridesInstalledAppState) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder() |
| .AddSecondaryExtensionWithEnabledOnLaunch(kSecondaryAppId, false) |
| .AddSecondaryExtensionWithEnabledOnLaunch(kExtraSecondaryAppId, true) |
| .Build(); |
| |
| // Add the secondary app that should be disabled on startup - make it enabled |
| // initially, so the test can verify the app gets disabled regardless of the |
| // initial state. |
| PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build()); |
| |
| // Add the secondary app that should be enabled on startup - make it disabled |
| // initially, so the test can verify the app gets enabled regardless of the |
| // initial state. |
| PreinstallApp(*SecondaryAppBuilder(kExtraSecondaryAppId).Build()); |
| service()->DisableExtension(kExtraSecondaryAppId, |
| extensions::disable_reason::DISABLE_USER_ACTION); |
| |
| InitializeLauncherWithNetworkReady(); |
| ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app)); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kExtraSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| KeepInstalledAppStateWithNoEnabledOnLaunchProperty) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder() |
| .AddSecondaryExtension(kSecondaryAppId) |
| .AddSecondaryExtension(kExtraSecondaryAppId) |
| .Build(); |
| |
| PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build()); |
| |
| PreinstallApp(*SecondaryAppBuilder(kExtraSecondaryAppId).Build()); |
| service()->DisableExtension(kExtraSecondaryAppId, |
| extensions::disable_reason::DISABLE_USER_ACTION); |
| |
| InitializeLauncherWithNetworkReady(); |
| ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app)); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kExtraSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, |
| DoNotEnableSecondayAppsDisabledForNonUserActionReason) { |
| scoped_refptr<const Extension> primary_app = |
| PrimaryAppBuilder() |
| .AddSecondaryExtensionWithEnabledOnLaunch(kSecondaryAppId, true) |
| .Build(); |
| |
| // Add the secondary app that should be enabled on startup - make it disabled |
| // initially, so the test can verify the app gets enabled regardless of the |
| // initial state. |
| PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build()); |
| // Disable the secodnary app for a reason different than user action - that |
| // disable reason should not be overriden during the kiosk launch. |
| service()->DisableExtension( |
| kSecondaryAppId, |
| extensions::disable_reason::DISABLE_USER_ACTION | |
| extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY); |
| |
| InitializeLauncherWithNetworkReady(); |
| ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app)); |
| |
| EXPECT_TRUE(external_apps_loader_handler_->pending_crx_files().empty()); |
| EXPECT_TRUE(external_apps_loader_handler_->pending_update_urls().empty()); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| |
| EXPECT_EQ(1, app_launch_tracker_->kiosk_launch_count()); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY, |
| extensions::ExtensionPrefs::Get(browser_context()) |
| ->GetDisableReasons(kSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppUpdatesToDisabledOnLaunch) { |
| PreinstallApp(*PrimaryAppBuilder() |
| .AddSecondaryExtension(kSecondaryAppId) |
| .set_version("1.0") |
| .set_offline_enabled(false) |
| .Build()); |
| PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build()); |
| |
| scoped_refptr<const Extension> primary_app_update = |
| PrimaryAppBuilder() |
| .AddSecondaryExtensionWithEnabledOnLaunch(kSecondaryAppId, false) |
| .set_version("1.1") |
| .Build(); |
| |
| InitializeLauncherWithNetworkReady(); |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app_update)); |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app_update)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_USER_ACTION, |
| extensions::ExtensionPrefs::Get(browser_context()) |
| ->GetDisableReasons(kSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, PrimaryAppUpdatesToEnabledOnLaunch) { |
| PreinstallApp( |
| *PrimaryAppBuilder() |
| .AddSecondaryExtensionWithEnabledOnLaunch(kSecondaryAppId, false) |
| .set_version("1.0") |
| .set_offline_enabled(false) |
| .Build()); |
| PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build()); |
| service()->DisableExtension(kSecondaryAppId, |
| extensions::disable_reason::DISABLE_USER_ACTION); |
| |
| scoped_refptr<const Extension> primary_app_update = |
| PrimaryAppBuilder() |
| .AddSecondaryExtensionWithEnabledOnLaunch(kSecondaryAppId, true) |
| .set_version("1.1") |
| .Build(); |
| |
| InitializeLauncherWithNetworkReady(); |
| ASSERT_TRUE(DownloadPrimaryApp(*primary_app_update)); |
| ASSERT_TRUE(FinishPrimaryAppInstall(*primary_app_update)); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| startup_app_launcher_->LaunchApp(); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kSecondaryAppId)); |
| } |
| |
| TEST_F(StartupAppLauncherTest, SecondaryExtensionStateOnSessionRestore) { |
| PreinstallApp( |
| *PrimaryAppBuilder() |
| .AddSecondaryExtensionWithEnabledOnLaunch(kSecondaryAppId, false) |
| .AddSecondaryExtensionWithEnabledOnLaunch(kExtraSecondaryAppId, true) |
| .Build()); |
| |
| // Add the secondary app that should be disabled on launch - make it enabled |
| // initially, and let test verify it remains enabled during the launch. |
| PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build()); |
| |
| // Add the secondary app that should be enabled on launch - make it disabled |
| // initially, and let test verify the app remains disabled during the launch. |
| PreinstallApp(*SecondaryAppBuilder(kExtraSecondaryAppId).Build()); |
| service()->DisableExtension(kExtraSecondaryAppId, |
| extensions::disable_reason::DISABLE_USER_ACTION); |
| |
| startup_app_launcher_ = CreateStartupAppLauncherForSessionRestore(); |
| |
| startup_launch_delegate_.set_network_ready(true); |
| startup_app_launcher_->Initialize(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| |
| startup_app_launcher_->LaunchApp(); |
| |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kTestPrimaryAppId)); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(kSecondaryAppId)); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(kExtraSecondaryAppId)); |
| } |
| |
| class FakeChromeKioskLaunchController : public ChromeKioskLaunchController { |
| public: |
| void SetInstallResult(ChromeKioskInstallResult result) { |
| install_result_ = result; |
| } |
| void SetLaunchResult(ChromeKioskLaunchResult result) { |
| launch_result_ = result; |
| } |
| |
| mojo::PendingRemote<ChromeKioskLaunchController> BindNewPipeAndPassRemote() { |
| return receiver_.BindNewPipeAndPassRemote(); |
| } |
| |
| // `ChromeKioskLaunchController` |
| void InstallKioskApp(AppInstallParamsPtr params, |
| InstallKioskAppCallback callback) override { |
| std::move(callback).Run(install_result_); |
| } |
| |
| void LaunchKioskApp(const std::string& app_id, |
| bool is_network_ready, |
| LaunchKioskAppCallback callback) override { |
| std::move(callback).Run(launch_result_); |
| } |
| |
| private: |
| mojo::Receiver<ChromeKioskLaunchController> receiver_{this}; |
| ChromeKioskInstallResult install_result_ = ChromeKioskInstallResult::kUnknown; |
| ChromeKioskLaunchResult launch_result_ = ChromeKioskLaunchResult::kUnknown; |
| }; |
| |
| class StartupAppLauncherUsingLacrosTest : public testing::Test { |
| public: |
| StartupAppLauncherUsingLacrosTest() { |
| std::vector<base::test::FeatureRef> enabled = |
| ash::standalone_browser::GetFeatureRefs(); |
| enabled.push_back( |
| ash::standalone_browser::features::kChromeKioskEnableLacros); |
| scoped_feature_list_.InitWithFeatures(enabled, {}); |
| } |
| |
| void SetUp() override { |
| ASSERT_TRUE(testing_profile_manager_.SetUp()); |
| LoginState::Initialize(); |
| crosapi::IdleServiceAsh::DisableForTesting(); |
| profile_ = testing_profile_manager_.CreateTestingProfile("Default"); |
| crosapi_manager_ = crosapi::CreateCrosapiManagerWithTestRegistry(); |
| const AccountId account_id(AccountId::FromUserEmail(kTestUserAccount)); |
| fake_user_manager_->AddKioskAppUser(account_id); |
| fake_user_manager_->LoginUser(account_id); |
| kiosk_app_manager_ = std::make_unique<KioskChromeAppManager>(); |
| kiosk_app_manager_overrides_.InitializePrimaryAppState(); |
| RegisterFakeCrosapi(); |
| ASSERT_TRUE(crosapi::browser_util::IsLacrosEnabledInChromeKioskSession()); |
| } |
| |
| void TearDown() override { |
| startup_app_launcher_.reset(); |
| kiosk_app_manager_.reset(); |
| crosapi_manager_.reset(); |
| LoginState::Shutdown(); |
| } |
| |
| protected: |
| KioskAppLauncher& launcher() { return *startup_app_launcher_; } |
| |
| crosapi::FakeBrowserManager& fake_browser_manager() { |
| return browser_manager_; |
| } |
| |
| chromeos::TestExternalCache* external_cache() { |
| return kiosk_app_manager_overrides_.external_cache(); |
| } |
| |
| [[nodiscard]] AssertionResult DownloadPrimaryApp(const Extension& app) { |
| return kiosk_app_manager_overrides_.DownloadPrimaryApp(app); |
| } |
| |
| FakeChromeKioskLaunchController& chrome_kiosk_launch_controller() { |
| return launch_controller_; |
| } |
| |
| Profile* profile() { return profile_; } |
| |
| void CreateStartupAppLauncher(bool should_skip_install = false) { |
| startup_app_launcher_ = std::make_unique<StartupAppLauncher>( |
| profile(), kTestPrimaryAppId, should_skip_install, |
| &startup_launch_delegate_); |
| startup_app_launcher_->AddObserver(&startup_launch_delegate_); |
| } |
| |
| void InitializeLauncherWithNetworkReady() { |
| startup_launch_delegate_.set_network_ready(true); |
| startup_app_launcher_->Initialize(); |
| EXPECT_TRUE(startup_launch_delegate_.ExpectNoLaunchStateChanges()); |
| } |
| |
| void AdvanceUntilAppInstalling() { |
| CreateStartupAppLauncher(); |
| InitializeLauncherWithNetworkReady(); |
| |
| ASSERT_TRUE(external_cache()); |
| EXPECT_EQ(std::set<std::string>({kTestPrimaryAppId}), |
| external_cache()->pending_downloads()); |
| |
| ASSERT_TRUE(DownloadPrimaryApp(*PrimaryAppBuilder().Build())); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kInstallingApp); |
| } |
| |
| void AdvanceUntilAppInstalled() { |
| chrome_kiosk_launch_controller().SetInstallResult( |
| ChromeKioskInstallResult::kSuccess); |
| AdvanceUntilAppInstalling(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| } |
| |
| TestAppLaunchDelegate startup_launch_delegate_; |
| |
| private: |
| void RegisterFakeCrosapi() { |
| crosapi::CrosapiManager::Get() |
| ->crosapi_ash() |
| ->chrome_app_kiosk_service() |
| ->BindLaunchController(launch_controller_.BindNewPipeAndPassRemote()); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| user_manager::TypedScopedUserManager<ash::FakeChromeUserManager> |
| fake_user_manager_{std::make_unique<ash::FakeChromeUserManager>()}; |
| TestingProfileManager testing_profile_manager_{ |
| TestingBrowserProcess::GetGlobal()}; |
| raw_ptr<Profile> profile_; |
| FakeChromeKioskLaunchController launch_controller_; |
| crosapi::FakeBrowserManager browser_manager_; |
| |
| ScopedKioskAppManagerOverrides kiosk_app_manager_overrides_; |
| std::unique_ptr<KioskChromeAppManager> kiosk_app_manager_; |
| std::unique_ptr<KioskAppLauncher> startup_app_launcher_; |
| |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<crosapi::CrosapiManager> crosapi_manager_; |
| }; |
| |
| TEST_F(StartupAppLauncherUsingLacrosTest, |
| ShouldRespectInstallSuccessFromCrosapi) { |
| chrome_kiosk_launch_controller().SetInstallResult( |
| ChromeKioskInstallResult::kSuccess); |
| AdvanceUntilAppInstalling(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kReadyToLaunch); |
| } |
| |
| TEST_F(StartupAppLauncherUsingLacrosTest, |
| ShouldRespectInstallFailureFromCrosapi) { |
| chrome_kiosk_launch_controller().SetInstallResult( |
| ChromeKioskInstallResult::kPrimaryAppInstallFailed); |
| AdvanceUntilAppInstalling(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| EXPECT_EQ(startup_launch_delegate_.launch_error(), |
| KioskAppLaunchError::Error::kUnableToInstall); |
| } |
| |
| TEST_F(StartupAppLauncherUsingLacrosTest, |
| ShouldRespectLaunchSuccessFromCrosapi) { |
| AdvanceUntilAppInstalled(); |
| |
| chrome_kiosk_launch_controller().SetLaunchResult( |
| ChromeKioskLaunchResult::kSuccess); |
| launcher().LaunchApp(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchSucceeded); |
| } |
| |
| TEST_F(StartupAppLauncherUsingLacrosTest, |
| ShouldRespectLaunchFailureFromCrosapi) { |
| AdvanceUntilAppInstalled(); |
| |
| chrome_kiosk_launch_controller().SetLaunchResult( |
| ChromeKioskLaunchResult::kUnableToLaunch); |
| launcher().LaunchApp(); |
| |
| EXPECT_EQ(startup_launch_delegate_.WaitForNextLaunchState(), |
| LaunchState::kLaunchFailed); |
| EXPECT_EQ(startup_launch_delegate_.launch_error(), |
| KioskAppLaunchError::Error::kUnableToLaunch); |
| } |
| |
| } // namespace ash |