blob: 4e8324e53addc1fdd8378e30d4fbf6f92f5042dc [file] [log] [blame]
// 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