| // Copyright 2020 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/crosapi/browser_loader.h" |
| |
| #include "ash/constants/ash_switches.h" |
| #include "base/auto_reset.h" |
| #include "base/command_line.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/callback.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_command_line.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "base/version.h" |
| #include "chrome/browser/ash/crosapi/browser_util.h" |
| #include "chrome/browser/ash/crosapi/lacros_selection_loader.h" |
| #include "chromeos/ash/components/standalone_browser/browser_support.h" |
| #include "components/policy/core/common/policy_map.h" |
| #include "components/policy/policy_constants.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ash::standalone_browser::BrowserSupport; |
| |
| namespace crosapi { |
| namespace { |
| |
| // This implementation of RAII for LacrosSelection is to make it easy reset |
| // the state between runs. |
| class ScopedLacrosSelectionCache { |
| public: |
| explicit ScopedLacrosSelectionCache( |
| browser_util::LacrosSelectionPolicy lacros_selection) { |
| SetLacrosSelection(lacros_selection); |
| } |
| ScopedLacrosSelectionCache(const ScopedLacrosSelectionCache&) = delete; |
| ScopedLacrosSelectionCache& operator=(const ScopedLacrosSelectionCache&) = |
| delete; |
| ~ScopedLacrosSelectionCache() { |
| browser_util::ClearLacrosSelectionCacheForTest(); |
| } |
| |
| private: |
| void SetLacrosSelection( |
| browser_util::LacrosSelectionPolicy lacros_selection) { |
| policy::PolicyMap policy; |
| policy.Set(policy::key::kLacrosSelection, policy::POLICY_LEVEL_MANDATORY, |
| policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, |
| base::Value(GetLacrosSelectionPolicyName(lacros_selection)), |
| /*external_data_fetcher=*/nullptr); |
| browser_util::CacheLacrosSelection(policy); |
| } |
| }; |
| |
| } // namespace |
| |
| // This fake class is used to test BrowserLoader who is responsible for deciding |
| // which lacros selection to use. |
| // This class does not load nor get actual version. Such features are tested in |
| // RootfsLacrosLoaderTest for rootfs and StatefulLacrosLoaderTest for stateful. |
| class FakeLacrosSelectionLoader : public LacrosSelectionLoader { |
| public: |
| FakeLacrosSelectionLoader() { |
| // Create dummy chrome binary path. |
| CHECK(temp_dir_.CreateUniqueTempDir()); |
| chrome_path_ = temp_dir_.GetPath().Append(kLacrosChromeBinary); |
| base::WriteFile(chrome_path_, "I am chrome binary."); |
| } |
| |
| FakeLacrosSelectionLoader(const FakeLacrosSelectionLoader&) = delete; |
| FakeLacrosSelectionLoader& operator=(const FakeLacrosSelectionLoader&) = |
| delete; |
| |
| ~FakeLacrosSelectionLoader() override = default; |
| |
| void Load(LoadCompletionCallback callback) override { |
| if (!callback) { |
| return; |
| } |
| |
| // If version is invalid, returns empty path. Otherwise fill in with some |
| // path. Whether path is empty or not is used as a condition to check |
| // whether error has occurred during loading. |
| const base::FilePath path = |
| version_.IsValid() ? temp_dir_.GetPath() : base::FilePath(); |
| std::move(callback).Run(version_, path); |
| } |
| void Unload() override {} |
| void Reset() override {} |
| void GetVersion(base::OnceCallback<void(base::Version)> callback) override { |
| std::move(callback).Run(version_); |
| } |
| |
| void SetVersionForTesting(const base::Version& version) override { |
| version_ = version; |
| } |
| |
| private: |
| base::Version version_ = base::Version(); |
| base::ScopedTempDir temp_dir_; |
| base::FilePath chrome_path_; |
| }; |
| |
| class BrowserLoaderTest : public testing::Test { |
| public: |
| BrowserLoaderTest() { |
| browser_loader_ = std::make_unique<BrowserLoader>( |
| /*rootfs_lacros_loader=*/std::make_unique<FakeLacrosSelectionLoader>(), |
| /*stateful_lacros_loader=*/std::make_unique< |
| FakeLacrosSelectionLoader>()); |
| EXPECT_TRUE(BrowserLoader::WillLoadStatefulComponentBuilds()); |
| } |
| |
| // Public because this is test code. |
| content::BrowserTaskEnvironment task_environment_; |
| |
| protected: |
| std::unique_ptr<BrowserLoader> browser_loader_; |
| |
| private: |
| base::AutoReset<bool> set_lacros_enabled_ = |
| BrowserSupport::SetLacrosEnabledForTest(true); |
| }; |
| |
| TEST_F(BrowserLoaderTest, OnLoadVersionSelectionNeitherIsAvailable) { |
| // If both stateful and rootfs lacros-chrome version is invalid, the chrome |
| // path should be empty. |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| EXPECT_TRUE(future.Get<0>().empty()); |
| } |
| |
| TEST_F(BrowserLoaderTest, OnLoadVersionSelectionStatefulIsUnavailable) { |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| // Pass invalid `base::Version` to stateful lacros-chrome and set valid |
| // version to rootfs lacros-chrome. |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| EXPECT_EQ(LacrosSelection::kRootfs, future.Get<1>()); |
| EXPECT_EQ(rootfs_lacros_version, future.Get<2>()); |
| } |
| |
| TEST_F(BrowserLoaderTest, OnLoadVersionSelectionRootfsIsUnavailable) { |
| const base::Version stateful_lacros_version = base::Version("1.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| |
| // Pass invalid `base::Version` as a rootfs lacros-chrome version. |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| EXPECT_EQ(LacrosSelection::kStateful, future.Get<1>()); |
| EXPECT_EQ(stateful_lacros_version, future.Get<2>()); |
| } |
| |
| TEST_F(BrowserLoaderTest, OnLoadVersionSelectionRootfsIsNewer) { |
| // Use rootfs when a stateful lacros-chrome version is older. |
| const base::Version stateful_lacros_version = base::Version("1.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| EXPECT_EQ(LacrosSelection::kRootfs, future.Get<1>()); |
| EXPECT_EQ(rootfs_lacros_version, future.Get<2>()); |
| } |
| |
| TEST_F(BrowserLoaderTest, OnLoadVersionSelectionRootfsIsOlder) { |
| // Use stateful when a rootfs lacros-chrome version is older. |
| const base::Version stateful_lacros_version = base::Version("3.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| EXPECT_EQ(LacrosSelection::kStateful, future.Get<1>()); |
| EXPECT_EQ(stateful_lacros_version, future.Get<2>()); |
| } |
| |
| TEST_F(BrowserLoaderTest, OnLoadSelectionPolicyIsRootfs) { |
| ScopedLacrosSelectionCache cache( |
| browser_util::LacrosSelectionPolicy::kRootfs); |
| base::test::ScopedCommandLine command_line; |
| command_line.GetProcessCommandLine()->AppendSwitchASCII( |
| browser_util::kLacrosSelectionSwitch, |
| browser_util::kLacrosSelectionStateful); |
| |
| // Set stateful lacros version newer than rootfs to test that the selection |
| // policy is prioritized higher. |
| const base::Version stateful_lacros_version = base::Version("3.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| |
| const LacrosSelection selection = future.Get<1>(); |
| EXPECT_EQ(selection, LacrosSelection::kRootfs); |
| EXPECT_FALSE(BrowserLoader::WillLoadStatefulComponentBuilds()); |
| } |
| |
| TEST_F(BrowserLoaderTest, |
| OnLoadSelectionPolicyIsUserChoiceAndCommandLineIsRootfs) { |
| ScopedLacrosSelectionCache cache( |
| browser_util::LacrosSelectionPolicy::kUserChoice); |
| base::test::ScopedCommandLine command_line; |
| command_line.GetProcessCommandLine()->AppendSwitchASCII( |
| browser_util::kLacrosSelectionSwitch, |
| browser_util::kLacrosSelectionRootfs); |
| |
| // Set stateful lacros version newer than rootfs to test that the user choice |
| // is prioritized higher. |
| const base::Version stateful_lacros_version = base::Version("3.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| |
| const LacrosSelection selection = future.Get<1>(); |
| EXPECT_EQ(selection, LacrosSelection::kRootfs); |
| EXPECT_FALSE(BrowserLoader::WillLoadStatefulComponentBuilds()); |
| } |
| |
| TEST_F(BrowserLoaderTest, |
| OnLoadSelectionPolicyIsUserChoiceAndCommandLineIsStateful) { |
| ScopedLacrosSelectionCache cache( |
| browser_util::LacrosSelectionPolicy::kUserChoice); |
| base::test::ScopedCommandLine command_line; |
| command_line.GetProcessCommandLine()->AppendSwitchASCII( |
| browser_util::kLacrosSelectionSwitch, |
| browser_util::kLacrosSelectionStateful); |
| |
| // Set rootfs lacros version newer than stateful to test that the user choice |
| // is prioritized higher. |
| const base::Version stateful_lacros_version = base::Version("1.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| |
| base::test::TestFuture<const base::FilePath&, LacrosSelection, base::Version> |
| future; |
| browser_loader_->Load(future.GetCallback()); |
| |
| const LacrosSelection selection = future.Get<1>(); |
| EXPECT_EQ(selection, LacrosSelection::kStateful); |
| EXPECT_TRUE(BrowserLoader::WillLoadStatefulComponentBuilds()); |
| } |
| |
| TEST_F(BrowserLoaderTest, OnLoadLacrosSpecifiedBySwitch) { |
| base::ScopedTempDir temp_dir; |
| CHECK(temp_dir.CreateUniqueTempDir()); |
| const base::FilePath lacros_chrome_path = temp_dir.GetPath(); |
| base::WriteFile(lacros_chrome_path.Append("chrome"), |
| "I am lacros-chrome deployed locally."); |
| |
| // Set created lacros-chrome binary to ash switches. |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| ash::switches::kLacrosChromePath, lacros_chrome_path.MaybeAsASCII()); |
| |
| // Set stateful/rootfs lacros-chrome version to check that specified |
| // lacros-chrome is prioritized higher. |
| const base::Version stateful_lacros_version = base::Version("3.0.0"); |
| browser_loader_->stateful_lacros_loader_->SetVersionForTesting( |
| stateful_lacros_version); |
| const base::Version rootfs_lacros_version = base::Version("2.0.0"); |
| browser_loader_->rootfs_lacros_loader_->SetVersionForTesting( |
| rootfs_lacros_version); |
| |
| base::test::TestFuture<base::FilePath, LacrosSelection, base::Version> future; |
| browser_loader_->Load(future.GetCallback<const base::FilePath&, |
| LacrosSelection, base::Version>()); |
| |
| const base::FilePath path = future.Get<0>(); |
| const LacrosSelection selection = future.Get<1>(); |
| EXPECT_EQ(path, lacros_chrome_path); |
| EXPECT_EQ(selection, LacrosSelection::kDeployedLocally); |
| } |
| |
| } // namespace crosapi |