// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/desks_storage/core/desk_model_wrapper.h"
#include <stddef.h>
#include <memory>
#include "ash/public/cpp/desk_template.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/types/strong_alias.h"
#include "base/uuid.h"
#include "components/account_id/account_id.h"
#include "components/app_constants/constants.h"
#include "components/app_restore/app_launch_info.h"
#include "components/desks_storage/core/desk_model_observer.h"
#include "components/desks_storage/core/desk_sync_bridge.h"
#include "components/desks_storage/core/desk_template_conversion.h"
#include "components/desks_storage/core/desk_template_util.h"
#include "components/desks_storage/core/desk_test_util.h"
#include "components/desks_storage/core/local_desk_data_manager.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/features.h"
#include "components/sync/model/entity_change.h"
#include "components/sync/model/in_memory_metadata_change_list.h"
#include "components/sync/model/metadata_batch.h"
#include "components/sync/protocol/entity_data.h"
#include "components/sync/test/data_type_store_test_util.h"
#include "components/sync/test/mock_data_type_local_change_processor.h"
#include "components/sync/test/test_matchers.h"
#include "desk_model_wrapper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace desks_storage {
namespace {
using GetAllEntriesResult = DeskModel::GetAllEntriesResult;
using GetEntryByUuidResult = DeskModel::GetEntryByUuidResult;
using TestUuidId = base::StrongAlias<class TestUuidIdTag, int>;
constexpr char kUuidFormat[] = "1c186d5a-502e-49ce-9ee1-00000000000%d";
std::string MakeTestUuidString(TestUuidId uuid_id) {
return base::StringPrintf(kUuidFormat, uuid_id.value());
}
std::string GetPolicyStringWithOneTemplate() {
return "[{\"version\":1,\"uuid\":\"" + MakeTestUuidString(TestUuidId(5)) +
"\",\"name\":\""
"Admin Template 1"
"\",\"created_time_usec\":\"1633535632\",\"updated_time_usec\": "
"\"1633535632\",\"desk\":{\"apps\":[{\"window_"
"bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
"state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
"\"url\":\"https://"
"example.com\",\"title\":\"Example\"},{\"url\":\"https://"
"example.com/"
"2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
"\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}}"
"]";
}
// Search `entry_list` for `uuid_query` as a uuid and returns true if
// found, false if not.
bool FindUuidInUuidList(
const std::string& uuid_query,
const std::vector<raw_ptr<const ash::DeskTemplate, VectorExperimental>>&
entry_list) {
base::Uuid guid = base::Uuid::ParseCaseInsensitive(uuid_query);
DCHECK(guid.is_valid());
for (const ash::DeskTemplate* entry : entry_list) {
if (entry->uuid() == guid)
return true;
}
return false;
}
// Verifies that the status passed into it is kOk.
void VerifyEntryAddedCorrectly(DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
}
void VerifyEntryAddedErrorHitMaximumLimit(
DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kHitMaximumLimit);
}
// Verifies that the status passed into it is kInvalidArgument/
void VerifyEntryAddedInvalidArgument(
DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kInvalidArgument);
}
// Make test template with ID containing the index. Defaults to desk template
// type if a type is not specified.
std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
int index,
ash::DeskTemplateType type) {
auto entry = std::make_unique<ash::DeskTemplate>(
base::Uuid::ParseCaseInsensitive(base::StringPrintf(kUuidFormat, index)),
ash::DeskTemplateSource::kUser, base::StringPrintf("desk_%d", index),
base::Time::Now(), type);
entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
return entry;
}
// Make test template with default restore data.
std::unique_ptr<ash::DeskTemplate> MakeTestDeskTemplate(
const std::string& uuid,
ash::DeskTemplateSource source,
const std::string& name,
const base::Time created_time) {
auto entry = std::make_unique<ash::DeskTemplate>(
base::Uuid::ParseCaseInsensitive(uuid), source, name, created_time,
ash::DeskTemplateType::kTemplate);
entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
return entry;
}
// Make test save and recall desk with default restore data.
std::unique_ptr<ash::DeskTemplate> MakeTestSaveAndRecallDesk(
const std::string& uuid,
const std::string& name,
const base::Time created_time) {
auto entry = std::make_unique<ash::DeskTemplate>(
base::Uuid::ParseCaseInsensitive(uuid), ash::DeskTemplateSource::kUser,
name, created_time, ash::DeskTemplateType::kSaveAndRecall);
entry->set_desk_restore_data(std::make_unique<app_restore::RestoreData>());
return entry;
}
} // namespace
class MockDeskModelObserver : public DeskModelObserver {
public:
MOCK_METHOD0(DeskModelLoaded, void());
MOCK_METHOD1(EntriesAddedOrUpdatedRemotely,
void(const std::vector<
raw_ptr<const ash::DeskTemplate, VectorExperimental>>&));
MOCK_METHOD1(EntriesRemovedRemotely, void(const std::vector<base::Uuid>&));
};
// This test class only tests the overall wrapper desk model class. The
// correctness of the underlying desk model storages that
// `DeskModelWrapper` uses are tested in their own unittests.
class DeskModelWrapperTest : public testing::Test {
public:
DeskModelWrapperTest()
: sample_desk_template_one_(
MakeTestDeskTemplate(MakeTestUuidString(TestUuidId(1)),
ash::DeskTemplateSource::kUser,
"desk_01",
base::Time::Now())),
sample_desk_template_two_(
MakeTestDeskTemplate(MakeTestUuidString(TestUuidId(2)),
ash::DeskTemplateSource::kUser,
"desk_02",
base::Time::Now())),
sample_save_and_recall_desk_one_(
MakeTestSaveAndRecallDesk(MakeTestUuidString(TestUuidId(3)),
"save_and_recall_desk_01",
base::Time::Now())),
sample_save_and_recall_desk_two_(
MakeTestSaveAndRecallDesk(MakeTestUuidString(TestUuidId(4)),
"save_and_recall_desk_02",
base::Time::Now())),
task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
cache_(std::make_unique<apps::AppRegistryCache>()),
account_id_(AccountId::FromUserEmail("[email protected]")),
data_manager_(std::unique_ptr<LocalDeskDataManager>()),
store_(syncer::DataTypeStoreTestUtil::CreateInMemoryStoreForTest()) {}
DeskModelWrapperTest(const DeskModelWrapperTest&) = delete;
DeskModelWrapperTest& operator=(const DeskModelWrapperTest&) = delete;
~DeskModelWrapperTest() override = default;
void SetUp() override {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
data_manager_ = std::make_unique<LocalDeskDataManager>(temp_dir_.GetPath(),
account_id_);
desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
model_wrapper_ = std::make_unique<DeskModelWrapper>(data_manager_.get());
task_environment_.RunUntilIdle();
testing::Test::SetUp();
}
void CreateBridge() {
ON_CALL(mock_processor_, IsTrackingMetadata())
.WillByDefault(testing::Return(true));
bridge_ = std::make_unique<DeskSyncBridge>(
mock_processor_.CreateForwardingProcessor(),
syncer::DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get()),
account_id_);
bridge_->AddObserver(&mock_observer_);
}
void FinishInitialization() { base::RunLoop().RunUntilIdle(); }
void InitializeBridge() {
CreateBridge();
FinishInitialization();
model_wrapper_->SetDeskSyncBridge(bridge_.get());
}
void ShutdownBridge() {
base::RunLoop().RunUntilIdle();
bridge_->RemoveObserver(&mock_observer_);
}
void RestartBridge() {
ShutdownBridge();
InitializeBridge();
}
void AddTwoTemplates() {
base::RunLoop loop1;
model_wrapper_->AddOrUpdateEntry(
std::move(sample_desk_template_one_),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop1.Quit();
}));
loop1.Run();
base::RunLoop loop2;
model_wrapper_->AddOrUpdateEntry(
std::move(sample_desk_template_two_),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop2.Quit();
}));
loop2.Run();
}
void AddTwoSaveAndRecallDeskTemplates() {
base::RunLoop loop1;
model_wrapper_->AddOrUpdateEntry(
std::move(sample_save_and_recall_desk_one_),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop1.Quit();
}));
loop1.Run();
base::RunLoop loop2;
model_wrapper_->AddOrUpdateEntry(
std::move(sample_save_and_recall_desk_two_),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop2.Quit();
}));
loop2.Run();
}
void AddSavedDeskToDeskModel(std::unique_ptr<ash::DeskTemplate> entry) {
base::RunLoop loop;
model_wrapper_->AddOrUpdateEntry(
std::move(entry),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop.Quit();
}));
loop.Run();
}
void VerifyAllEntries(size_t expected_size, const std::string& trace_string) {
SCOPED_TRACE(trace_string);
task_environment_.RunUntilIdle();
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), expected_size);
}
base::ScopedTempDir temp_dir_;
std::unique_ptr<ash::DeskTemplate> sample_desk_template_one_;
std::unique_ptr<ash::DeskTemplate> sample_desk_template_two_;
std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_one_;
std::unique_ptr<ash::DeskTemplate> sample_save_and_recall_desk_two_;
base::test::TaskEnvironment task_environment_;
std::unique_ptr<apps::AppRegistryCache> cache_;
AccountId account_id_;
std::unique_ptr<LocalDeskDataManager> data_manager_;
std::unique_ptr<syncer::DataTypeStore> store_;
testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> mock_processor_;
testing::NiceMock<MockDeskModelObserver> mock_observer_;
std::unique_ptr<DeskSyncBridge> bridge_;
std::unique_ptr<DeskModelWrapper> model_wrapper_;
};
TEST_F(DeskModelWrapperTest, CanAddDeskTemplateEntry) {
InitializeBridge();
model_wrapper_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
base::BindOnce(&VerifyEntryAddedCorrectly));
VerifyAllEntries(1ul, "Added one desk template");
// Verify that it's not desk template entry in the save and recall desk
// storage.
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 1ul);
EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kTemplate);
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 1ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 0ul);
}
TEST_F(DeskModelWrapperTest, CanAddSaveAndRecallDeskEntry) {
InitializeBridge();
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(1u, ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedCorrectly));
VerifyAllEntries(1ul, "Added one save and recall desk");
// Verify that it's not SaveAndRecall entry in the desk template storage.
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 1ul);
EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 0ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
}
TEST_F(DeskModelWrapperTest,
ReturnsErrorWhenAddingTooManySaveAndRecallDeskEntry) {
InitializeBridge();
for (size_t index = 0;
index < model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
AddSavedDeskToDeskModel(
MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall));
}
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(
model_wrapper_->GetMaxSaveAndRecallDeskEntryCount() + 1,
ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedErrorHitMaximumLimit));
task_environment_.RunUntilIdle();
}
TEST_F(DeskModelWrapperTest, CanGetAllEntries) {
InitializeBridge();
AddTwoTemplates();
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 2ul);
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(1)), result.entries));
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(2)), result.entries));
// Sanity check for the search function.
EXPECT_FALSE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(3)), result.entries));
}
TEST_F(DeskModelWrapperTest, GetAllEntriesIncludesPolicyValues) {
InitializeBridge();
AddTwoTemplates();
AddTwoSaveAndRecallDeskTemplates();
model_wrapper_->SetPolicyDeskTemplates(GetPolicyStringWithOneTemplate());
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 5ul);
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(1)), result.entries));
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(2)), result.entries));
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(3)), result.entries));
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(4)), result.entries));
EXPECT_TRUE(
FindUuidInUuidList(MakeTestUuidString(TestUuidId(5)), result.entries));
// One of these templates should be from policy.
EXPECT_EQ(base::ranges::count_if(result.entries,
[](const ash::DeskTemplate* entry) {
return entry->source() ==
ash::DeskTemplateSource::kPolicy;
}),
1l);
model_wrapper_->SetPolicyDeskTemplates("");
}
TEST_F(DeskModelWrapperTest, CanDetectDuplicateEntryNames) {
InitializeBridge();
// Add desk template entry to desk model.
AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
// Add desk template entry with the duplicated name to desk model.
auto dupe_template_uuid = base::StringPrintf(kUuidFormat, 6);
AddSavedDeskToDeskModel(MakeTestDeskTemplate(dupe_template_uuid,
ash::DeskTemplateSource::kUser,
"desk_01", base::Time::Now()));
// Add save and recall desk to desk model.
AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
// Add save and recall entry with the duplicated name to desk model.
AddSavedDeskToDeskModel(
MakeTestSaveAndRecallDesk(base::StringPrintf(kUuidFormat, 7),
"save_and_recall_desk_01", base::Time::Now()));
// Add save and recall entry with the duplicated name as a desk template to
// desk model. This is to test that the two desk types don't share the same
// namespace for the sake of duplication checks.
auto dupe_second_save_and_recall_uuid = base::StringPrintf(kUuidFormat, 8);
AddSavedDeskToDeskModel(MakeTestSaveAndRecallDesk(
dupe_second_save_and_recall_uuid, "desk_01", base::Time::Now()));
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 5ul);
EXPECT_TRUE(model_wrapper_->FindOtherEntryWithName(
u"desk_01", ash::DeskTemplateType::kTemplate,
base::Uuid::ParseCaseInsensitive(dupe_template_uuid)));
EXPECT_TRUE(model_wrapper_->FindOtherEntryWithName(
u"save_and_recall_desk_01", ash::DeskTemplateType::kSaveAndRecall,
base::Uuid::ParseCaseInsensitive(dupe_template_uuid)));
EXPECT_FALSE(model_wrapper_->FindOtherEntryWithName(
u"desk_01", ash::DeskTemplateType::kSaveAndRecall,
base::Uuid::ParseCaseInsensitive(dupe_second_save_and_recall_uuid)));
}
TEST_F(DeskModelWrapperTest, CanDetectNoDuplicateEntryNames) {
InitializeBridge();
// Add desk template entry to desk model.
AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
// Add a second desk template entry to the desk model with a unique name.
auto second_template_uuid = base::StringPrintf(kUuidFormat, 7);
AddSavedDeskToDeskModel(MakeTestDeskTemplate(second_template_uuid,
ash::DeskTemplateSource::kUser,
"desk_02", base::Time::Now()));
// Add save and recall desk to desk model.
AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
// Add save and recall entry with the duplicated name to desk model.
auto second_save_and_recall_uuid = base::StringPrintf(kUuidFormat, 7);
AddSavedDeskToDeskModel(MakeTestSaveAndRecallDesk(second_save_and_recall_uuid,
"save_and_recall_desk_02",
base::Time::Now()));
GetAllEntriesResult result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 4ul);
EXPECT_FALSE(model_wrapper_->FindOtherEntryWithName(
u"desk_02", ash::DeskTemplateType::kTemplate,
base::Uuid::ParseCaseInsensitive(second_template_uuid)));
EXPECT_FALSE(model_wrapper_->FindOtherEntryWithName(
u"save_and_recall_desk_02", ash::DeskTemplateType::kSaveAndRecall,
base::Uuid::ParseCaseInsensitive(second_save_and_recall_uuid)));
}
TEST_F(DeskModelWrapperTest, CanGetEntryByUuid) {
InitializeBridge();
// Add desk template entry to desk model.
AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
// Add save and recall desk to desk model.
AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
task_environment_.RunUntilIdle();
// Find the desk template by its uuid.
GetEntryByUuidResult result1 = model_wrapper_->GetEntryByUUID(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(1))));
EXPECT_EQ(result1.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_EQ(result1.entry->uuid(), base::Uuid::ParseCaseInsensitive(
MakeTestUuidString(TestUuidId(1))));
EXPECT_EQ(base::UTF16ToUTF8(result1.entry->template_name()), "desk_01");
// Find the save and recall desk by its uuid.
GetEntryByUuidResult result2 = model_wrapper_->GetEntryByUUID(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(3))));
EXPECT_EQ(result2.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_EQ(result2.entry->uuid(), base::Uuid::ParseCaseInsensitive(
MakeTestUuidString(TestUuidId(3))));
EXPECT_EQ(base::UTF16ToUTF8(result2.entry->template_name()),
"save_and_recall_desk_01");
}
TEST_F(DeskModelWrapperTest, GetEntryByUuidShouldReturnAdminTemplate) {
InitializeBridge();
AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
// Set admin template with UUID: TestUuidId(5).
model_wrapper_->SetPolicyDeskTemplates(GetPolicyStringWithOneTemplate());
// Check that the admin template is included as an entry.
EXPECT_EQ(model_wrapper_->GetAllEntryUuids().size(), 2ul);
GetEntryByUuidResult result = model_wrapper_->GetEntryByUUID(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(5))));
EXPECT_EQ(result.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_EQ(result.entry->uuid(), base::Uuid::ParseCaseInsensitive(
MakeTestUuidString(TestUuidId(5))));
EXPECT_EQ(result.entry->source(), ash::DeskTemplateSource::kPolicy);
EXPECT_EQ(base::UTF16ToUTF8(result.entry->template_name()),
"Admin Template 1");
}
TEST_F(DeskModelWrapperTest, GetEntryByUuidReturnsNotFoundIfEntryDoesNotExist) {
InitializeBridge();
GetEntryByUuidResult result = model_wrapper_->GetEntryByUUID(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(1))));
EXPECT_EQ(result.status, DeskModel::GetEntryByUuidStatus::kNotFound);
}
TEST_F(DeskModelWrapperTest, CanUpdateEntry) {
InitializeBridge();
// Make a clone of a desk template and modify its name.
auto modified_desk_template = sample_desk_template_one_->Clone();
modified_desk_template->set_template_name(u"desk_01_mod");
AddSavedDeskToDeskModel(std::move(sample_desk_template_one_));
AddSavedDeskToDeskModel(std::move(modified_desk_template));
// Make a clone of a save and recall desk and modify its name.
auto modified_save_and_recall_desk =
sample_save_and_recall_desk_one_->Clone();
modified_save_and_recall_desk->set_template_name(
u"save_and_recall_desk_01_mod");
AddSavedDeskToDeskModel(std::move(sample_save_and_recall_desk_one_));
AddSavedDeskToDeskModel(std::move(modified_save_and_recall_desk));
task_environment_.RunUntilIdle();
// Check that the entries are updated.
GetEntryByUuidResult result1 = model_wrapper_->GetEntryByUUID(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(1))));
EXPECT_EQ(result1.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_EQ(result1.entry->uuid(), base::Uuid::ParseCaseInsensitive(
MakeTestUuidString(TestUuidId(1))));
EXPECT_EQ(result1.entry->template_name(),
base::UTF8ToUTF16(std::string("desk_01_mod")));
GetEntryByUuidResult result3 = model_wrapper_->GetEntryByUUID(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(3))));
EXPECT_EQ(result3.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_EQ(result3.entry->uuid(), base::Uuid::ParseCaseInsensitive(
MakeTestUuidString(TestUuidId(3))));
EXPECT_EQ(result3.entry->template_name(), u"save_and_recall_desk_01_mod");
}
TEST_F(DeskModelWrapperTest, CanDeleteDeskTemplateEntry) {
InitializeBridge();
model_wrapper_->AddOrUpdateEntry(std::move(sample_desk_template_one_),
base::BindOnce(&VerifyEntryAddedCorrectly));
model_wrapper_->DeleteEntry(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(1))),
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
}));
VerifyAllEntries(0ul, "Delete desk template");
}
TEST_F(DeskModelWrapperTest, CanDeleteSaveAndRecallDeskEntry) {
InitializeBridge();
model_wrapper_->AddOrUpdateEntry(std::move(sample_save_and_recall_desk_one_),
base::BindOnce(&VerifyEntryAddedCorrectly));
model_wrapper_->DeleteEntry(
base::Uuid::ParseCaseInsensitive(MakeTestUuidString(TestUuidId(3))),
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
}));
VerifyAllEntries(0ul, "Delete save and recall desk");
}
TEST_F(DeskModelWrapperTest, CanDeleteAllEntries) {
InitializeBridge();
AddTwoTemplates();
AddTwoSaveAndRecallDeskTemplates();
model_wrapper_->DeleteAllEntries(
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
}));
VerifyAllEntries(0ul,
"Delete all entries after adding two desk templates and two "
"save and recall desks");
}
TEST_F(DeskModelWrapperTest,
GetEntryCountShouldIncludeBothUserAndAdminTemplates) {
InitializeBridge();
// Add two user templates.
AddTwoTemplates();
// Add two save and recall desks templates.
AddTwoSaveAndRecallDeskTemplates();
// Set one admin template.
model_wrapper_->SetPolicyDeskTemplates(GetPolicyStringWithOneTemplate());
// There should be 5 templates: 2 user templates + 1 admin template + 2 save
// and recall desks.
EXPECT_EQ(model_wrapper_->GetEntryCount(), 5ul);
// MaxEntryCount should be 6 max save and recall desks + 6 max user templates
// + 1 admin template.
size_t max_entry_count = model_wrapper_->GetMaxDeskTemplateEntryCount() +
model_wrapper_->GetMaxSaveAndRecallDeskEntryCount();
EXPECT_EQ(max_entry_count, 13ul);
}
TEST_F(DeskModelWrapperTest, GetMaxEntryCountShouldIncreaseWithAdminTemplates) {
InitializeBridge();
// Add two user templates.
AddTwoTemplates();
size_t max_entry_count = model_wrapper_->GetMaxDeskTemplateEntryCount() +
model_wrapper_->GetMaxSaveAndRecallDeskEntryCount();
// The max entry count should increase by 1 since we have set an admin
// template.
EXPECT_EQ(max_entry_count, 12ul);
// Set one admin template.
model_wrapper_->SetPolicyDeskTemplates(GetPolicyStringWithOneTemplate());
size_t max_entry_count_with_admin_template =
model_wrapper_->GetMaxDeskTemplateEntryCount() +
model_wrapper_->GetMaxSaveAndRecallDeskEntryCount();
// The max entry count should increase by 1 since we have set an admin
// template.
EXPECT_EQ(max_entry_count_with_admin_template, 13ul);
// Sanity check to make sure that save and recall desk max count isn't
// affected by the admin template.
EXPECT_EQ(model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(), 6ul);
}
TEST_F(DeskModelWrapperTest, AddDeskTemplatesAndSaveAndRecallDeskEntries) {
InitializeBridge();
// Add two user templates.
AddTwoTemplates();
// Add two SaveAndRecall desks.
AddTwoSaveAndRecallDeskTemplates();
EXPECT_EQ(model_wrapper_->GetEntryCount(), 4ul);
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 2ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 2ul);
auto result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
VerifyAllEntries(4ul,
"Add two desks templates and two saved and recall desks");
}
TEST_F(DeskModelWrapperTest, AddSaveAndRecallDeskEntry) {
InitializeBridge();
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(1u, ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedCorrectly));
VerifyAllEntries(1ul, "Added one save and recall desk");
// Verify that it's not SaveAndRecall entry in the desk template cache.
auto result = model_wrapper_->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 1ul);
EXPECT_EQ(result.entries[0]->type(), ash::DeskTemplateType::kSaveAndRecall);
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 0ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
}
TEST_F(DeskModelWrapperTest, CanAddMaxEntriesForBothTypes) {
InitializeBridge();
for (size_t index = 0u;
index < model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedCorrectly));
}
for (size_t index = 0u;
index < model_wrapper_->GetMaxDeskTemplateEntryCount(); ++index) {
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
base::BindOnce(&VerifyEntryAddedCorrectly));
}
VerifyAllEntries(
12ul, "Added max number of save and recall desks and desk templates");
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 6ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 6ul);
}
TEST_F(DeskModelWrapperTest,
CanAddMaxEntriesDeskTemplatesAndStillAddEntryForSaveAndRecallDesks) {
InitializeBridge();
for (size_t index = 0u;
index < model_wrapper_->GetMaxDeskTemplateEntryCount(); ++index) {
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(index, ash::DeskTemplateType::kTemplate),
base::BindOnce(&VerifyEntryAddedCorrectly));
}
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedCorrectly));
VerifyAllEntries(7ul,
"Added one save and recall desk after capping "
"desk template entries");
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 6ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 1ul);
}
TEST_F(DeskModelWrapperTest,
CanAddMaxEntriesForSaveAndRecallDeskAndStillAddEntryForDeskTemplate) {
InitializeBridge();
for (size_t index = 0u;
index < model_wrapper_->GetMaxSaveAndRecallDeskEntryCount(); ++index) {
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(index, ash::DeskTemplateType::kSaveAndRecall),
base::BindOnce(&VerifyEntryAddedCorrectly));
}
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(1ul, ash::DeskTemplateType::kTemplate),
base::BindOnce(&VerifyEntryAddedCorrectly));
VerifyAllEntries(7ul,
"Added one desk template after capping "
"save and recall desk entries");
EXPECT_EQ(model_wrapper_->GetDeskTemplateEntryCount(), 1ul);
EXPECT_EQ(model_wrapper_->GetSaveAndRecallDeskEntryCount(), 6ul);
}
TEST_F(DeskModelWrapperTest, AddUnknownDeskTypeShouldFail) {
InitializeBridge();
model_wrapper_->AddOrUpdateEntry(
MakeTestDeskTemplate(1u, ash::DeskTemplateType::kUnknown),
base::BindOnce(&VerifyEntryAddedInvalidArgument));
}
} // namespace desks_storage