// Copyright 2021 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_sync_bridge.h"
#include <stddef.h>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include "ash/public/cpp/desk_template.h"
#include "base/containers/fixed_flat_set.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.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/metrics/histogram_tester.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_storage_metrics_util.h"
#include "components/desks_storage/core/desk_template_conversion.h"
#include "components/desks_storage/core/desk_test_util.h"
#include "components/desks_storage/core/saved_desk_builder.h"
#include "components/services/app_service/public/cpp/app_registry_cache.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/data_type_state.pb.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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace desks_storage {
using BrowserAppTab =
sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow_BrowserAppTab;
using ArcApp = sync_pb::WorkspaceDeskSpecifics_ArcApp;
using ArcSize = sync_pb::WorkspaceDeskSpecifics_ArcApp_WindowSize;
using BrowserAppWindow = sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow;
using ChromeApp = sync_pb::WorkspaceDeskSpecifics_ChromeApp;
using Desk = sync_pb::WorkspaceDeskSpecifics_Desk;
using ProgressiveWebApp = sync_pb::WorkspaceDeskSpecifics_ProgressiveWebApp;
using SyncDeskType = sync_pb::WorkspaceDeskSpecifics_DeskType;
using DeskType = ash::DeskTemplateType;
using WindowBound = sync_pb::WorkspaceDeskSpecifics_WindowBound;
using WindowState = sync_pb::WorkspaceDeskSpecifics_WindowState;
using WorkspaceDeskSpecifics_App = sync_pb::WorkspaceDeskSpecifics_App;
using SyncTabGroup = sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow_TabGroup;
using SyncTabGroupColor = sync_pb::WorkspaceDeskSpecifics_TabGroupColor;
using TestUuidId = base::StrongAlias<class TestUuidIdTag, int>;
namespace {
using ash::DeskTemplate;
using ash::DeskTemplateSource;
using ash::DeskTemplateType;
using sync_pb::DataTypeState;
using sync_pb::WorkspaceDeskSpecifics;
using syncer::DataTypeStore;
using syncer::DataTypeStoreTestUtil;
using syncer::EntityChange;
using syncer::EntityChangeList;
using syncer::EntityData;
using syncer::HasEncryptionKeyName;
using syncer::InMemoryMetadataChangeList;
using syncer::MetadataBatchContains;
using syncer::MetadataChangeList;
using syncer::MockDataTypeLocalChangeProcessor;
using syncer::ModelError;
using testing::_;
using testing::Return;
using testing::SizeIs;
using testing::StrEq;
constexpr char kUuidFormat[] = "9e186d5a-502e-49ce-9ee1-00000000000%d";
constexpr char kNameFormat[] = "template %d";
constexpr char kTestUrlFormat[] = "https://www.testdomain%d.com/";
constexpr char kTestAppNameFormat[] = "_some_prefix_%s";
constexpr int kDefaultTemplateIndex = 1;
constexpr int kBrowserWindowId = 1555;
constexpr int kPwaWindowId = 1666;
constexpr int kChromeAppWindowId = 1777;
// Example app index as set in `ExampleWorkspaceDeskSpecifics`.
constexpr int kExampleDeskChromeAppIndex = 2;
constexpr int kExampleDeskProgressiveWebAppIndex = 3;
constexpr auto kWindowOpenDispositionValues = base::MakeFixedFlatSet<
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition>(
{sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_UNKNOWN,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_CURRENT_TAB,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_SINGLETON_TAB,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_FOREGROUND_TAB,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_BACKGROUND_TAB,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_POPUP,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_SAVE_TO_DISK,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_OFF_THE_RECORD,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_IGNORE_ACTION,
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_SWITCH_TO_TAB,
sync_pb::
WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_PICTURE_IN_PICTURE});
constexpr auto kLaunchContainerValues = base::MakeFixedFlatSet<
sync_pb::WorkspaceDeskSpecifics_LaunchContainer>({
sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_WINDOW,
sync_pb::
WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_PANEL_DEPRECATED,
sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_TAB,
sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_NONE,
});
constexpr auto kTabGroupColors = base::MakeFixedFlatSet<SyncTabGroupColor>(
{SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN,
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE});
base::Uuid MakeTestUuid(TestUuidId uuid_id) {
return base::Uuid::ParseCaseInsensitive(
base::StringPrintf(kUuidFormat, uuid_id.value()));
}
base::Uuid MakeAdminTestUuid(TestUuidId uuid_id) {
return base::Uuid::ParseCaseInsensitive(base::StringPrintf(
"59dbe2b8-671f-4fd0-92ec-11111111100%d", uuid_id.value()));
}
std::string GetPolicyWithTwoTemplates() {
return "[{\"version\":1,\"uuid\":\"" + base::StringPrintf(kUuidFormat, 8) +
"\",\"name\":\""
"Example Template"
"\",\"created_time_usec\":\"1633535632\",\"updated_time_usec\": "
"\"1633535632\",\"desk_type\":\"TEMPLATE\",\"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\"}],\"tab_groups\":[{\"range_"
"start\":1,\"range_end\":2,\"title\":\"sample_tab_"
"group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_"
"index\":"
"1,\"first_non_pinned_tab_index\":1,\"window_id\":0,"
"\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}},"
"{\"version\":1,\"uuid\":\"" +
base::StringPrintf(kUuidFormat, 9) +
"\",\"name\":\""
"Example Template 2"
"\",\"created_time_usec\":\"1633535632\",\"updated_time_usec\": "
"\"1633535632\",\"desk_type\":\"TEMPLATE\",\"desk\":{\"apps\":[{"
"\"window_"
"bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
"state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
"\"url\":\"https://google.com\",\"title\":\"Example "
"2\"},{\"url\":\"https://"
"gmail.com.com/"
"2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"first_non_"
"pinned_"
"tab_index\":1,\"window_id\":0,"
"\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}}"
"]";
}
void FillDefaultBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
BrowserAppWindow* app_window,
int number_of_tabs) {
for (int i = 0; i < number_of_tabs; ++i) {
BrowserAppTab* tab = app_window->add_tabs();
tab->set_url(base::StringPrintf(kTestUrlFormat, i));
}
app_window->set_active_tab_index(number_of_tabs - 1);
app_window->set_first_non_pinned_tab_index(number_of_tabs - 1);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(110);
window_bound->set_top(120);
window_bound->set_width(1330);
window_bound->set_height(1440);
app->set_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_PRIMARY_SNAPPED);
app->set_display_id(99887766l);
app->set_z_index(133);
app->set_window_id(1555);
app->set_snap_percentage(75);
}
void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
int number_of_tabs = 2) {
BrowserAppWindow* app_window =
app->mutable_app()->mutable_browser_app_window();
FillDefaultBrowserAppWindow(app, app_window, number_of_tabs);
SyncTabGroup* tab_group = app_window->add_tab_groups();
tab_group->set_first_index(1);
tab_group->set_last_index(2);
tab_group->set_title("test_tab_group");
tab_group->set_color(
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN);
}
void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
int tab_group_first_index,
int tab_group_last_index,
const std::string& tab_group_title,
bool tab_group_is_collapsed,
SyncTabGroupColor tab_group_color) {
BrowserAppWindow* app_window =
app->mutable_app()->mutable_browser_app_window();
FillDefaultBrowserAppWindow(app, app_window, tab_group_last_index);
SyncTabGroup* tab_group = app_window->add_tab_groups();
tab_group->set_first_index(tab_group_first_index);
tab_group->set_last_index(tab_group_last_index);
tab_group->set_title(tab_group_title);
if (tab_group_is_collapsed)
tab_group->set_is_collapsed(tab_group_is_collapsed);
tab_group->set_color(tab_group_color);
}
void FillExampleProgressiveWebAppWindow(WorkspaceDeskSpecifics_App* app) {
ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
app_window->set_app_id(desk_test_util::kTestPwaAppId);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(210);
window_bound->set_top(220);
window_bound->set_width(2330);
window_bound->set_height(2440);
app->set_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_MINIMIZED);
app->set_pre_minimized_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_FULLSCREEN);
app->set_container(
sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_WINDOW);
app->set_disposition(
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW);
app->set_app_name(
base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestPwaAppId));
app->set_display_id(99887766l);
app->set_z_index(233);
app->set_window_id(2555);
}
void FillExampleSystemWebAppWindow(WorkspaceDeskSpecifics_App* app) {
ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
app_window->set_app_id(desk_test_util::kTestSwaAppId);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(220);
window_bound->set_top(230);
window_bound->set_width(2340);
window_bound->set_height(2450);
app->set_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_MINIMIZED);
app->set_pre_minimized_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_FULLSCREEN);
app->set_container(
sync_pb::WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_WINDOW);
app->set_disposition(
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW);
app->set_app_name(
base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestSwaAppId));
app->set_display_id(99887766l);
app->set_z_index(234);
app->set_window_id(2556);
}
void FillExampleChromeAppWindow(WorkspaceDeskSpecifics_App* app) {
ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
app_window->set_app_id(desk_test_util::kTestChromeAppId);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(210);
window_bound->set_top(220);
window_bound->set_width(2330);
window_bound->set_height(2440);
app->set_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_MAXIMIZED);
app->set_container(
sync_pb::
WorkspaceDeskSpecifics_LaunchContainer_LAUNCH_CONTAINER_PANEL_DEPRECATED);
app->set_disposition(
sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition_NEW_WINDOW);
app->set_app_name(
base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestChromeAppId));
app->set_display_id(99887766l);
app->set_z_index(233);
app->set_window_id(2555);
}
void FillExampleArcAppWindow(WorkspaceDeskSpecifics_App* app) {
ArcApp* app_window = app->mutable_app()->mutable_arc_app();
app_window->set_app_id(desk_test_util::kTestArcAppId);
ArcSize* minimum_size = app_window->mutable_minimum_size();
minimum_size->set_width(1);
minimum_size->set_height(1);
ArcSize* maximum_size = app_window->mutable_maximum_size();
maximum_size->set_width(256);
maximum_size->set_height(256);
WindowBound* bounds_in_root = app_window->mutable_bounds_in_root();
bounds_in_root->set_width(1024);
bounds_in_root->set_height(1024);
bounds_in_root->set_left(0);
bounds_in_root->set_top(0);
WindowBound* window_bound = app->mutable_window_bound();
window_bound->set_left(210);
window_bound->set_top(220);
window_bound->set_width(2330);
window_bound->set_height(2440);
app->set_window_state(
WindowState::WorkspaceDeskSpecifics_WindowState_MAXIMIZED);
app->set_app_name(
base::StringPrintf(kTestAppNameFormat, desk_test_util::kTestArcAppId));
app->set_display_id(99887766l);
app->set_z_index(233);
app->set_window_id(2555);
app->set_title("test_arc_app_title");
}
WorkspaceDeskSpecifics ExampleWorkspaceDeskSpecificsWithoutDeskType(
const std::string uuid,
const std::string template_name,
base::Time created_time = base::Time::Now(),
int number_of_tabs = 2) {
WorkspaceDeskSpecifics specifics;
specifics.set_uuid(uuid);
specifics.set_name(template_name);
specifics.set_created_time_windows_epoch_micros(
created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
specifics.set_updated_time_windows_epoch_micros(
(created_time + base::Minutes(5))
.ToDeltaSinceWindowsEpoch()
.InMicroseconds());
Desk* desk = specifics.mutable_desk();
FillExampleBrowserAppWindow(desk->add_apps(), number_of_tabs);
FillExampleArcAppWindow(desk->add_apps());
FillExampleChromeAppWindow(desk->add_apps());
FillExampleProgressiveWebAppWindow(desk->add_apps());
FillExampleSystemWebAppWindow(desk->add_apps());
specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
return specifics;
}
WorkspaceDeskSpecifics ExampleWorkspaceDeskSpecifics(
const std::string uuid,
const std::string template_name,
base::Time created_time = base::Time::Now(),
int number_of_tabs = 2,
SyncDeskType desk_type =
SyncDeskType::WorkspaceDeskSpecifics_DeskType_TEMPLATE) {
WorkspaceDeskSpecifics specifics =
ExampleWorkspaceDeskSpecificsWithoutDeskType(
uuid, template_name, created_time, number_of_tabs);
specifics.set_desk_type(desk_type);
specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
return specifics;
}
WorkspaceDeskSpecifics ExampleWorkspaceDeskSpecifics(
const std::string& uuid,
const std::string& template_name,
int tab_group_first_index,
int tab_group_last_index,
const std::string& tab_group_title,
bool tab_group_is_collapsed,
SyncTabGroupColor tab_group_color) {
base::Time created_time = base::Time::Now();
WorkspaceDeskSpecifics specifics;
specifics.set_uuid(uuid);
specifics.set_name(template_name);
specifics.set_created_time_windows_epoch_micros(
created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
specifics.set_updated_time_windows_epoch_micros(
(created_time + base::Minutes(5))
.ToDeltaSinceWindowsEpoch()
.InMicroseconds());
specifics.set_desk_type(
SyncDeskType::WorkspaceDeskSpecifics_DeskType_SAVE_AND_RECALL);
Desk* desk = specifics.mutable_desk();
FillExampleBrowserAppWindow(desk->add_apps(), tab_group_first_index,
tab_group_last_index, tab_group_title,
tab_group_is_collapsed, tab_group_color);
FillExampleArcAppWindow(desk->add_apps());
FillExampleChromeAppWindow(desk->add_apps());
FillExampleProgressiveWebAppWindow(desk->add_apps());
FillExampleSystemWebAppWindow(desk->add_apps());
specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
return specifics;
}
WorkspaceDeskSpecifics CreateWorkspaceDeskSpecifics(
int templateIndex,
base::Time created_time = base::Time::Now()) {
return ExampleWorkspaceDeskSpecifics(
base::StringPrintf(kUuidFormat, templateIndex),
base::StringPrintf(kNameFormat, templateIndex), created_time);
}
WorkspaceDeskSpecifics CreateUnknownDeskType() {
return ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(),
base::StringPrintf(kNameFormat, 1), base::Time::Now(),
/*number_of_tabs=*/2,
SyncDeskType::WorkspaceDeskSpecifics_DeskType_UNKNOWN_TYPE);
}
WorkspaceDeskSpecifics CreateWorkspaceDeskWithoutDeskType() {
return ExampleWorkspaceDeskSpecificsWithoutDeskType(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(),
base::StringPrintf(kNameFormat, 1), base::Time::Now(),
/*number_of_tabs=*/2);
}
WorkspaceDeskSpecifics CreateFloatingWorkspaceTemplateExpectedValue(
std::string cache_guid) {
WorkspaceDeskSpecifics expected_desk_specifics =
ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(),
base::StringPrintf(kNameFormat, 1), base::Time::Now(),
/*number_of_tabs=*/2,
SyncDeskType::WorkspaceDeskSpecifics_DeskType_FLOATING_WORKSPACE);
expected_desk_specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
expected_desk_specifics.set_client_cache_guid(cache_guid);
return expected_desk_specifics;
}
WorkspaceDeskSpecifics CreateBrowserTemplateExpectedValue(
int template_index,
const base::Time& created_time) {
WorkspaceDeskSpecifics expected_desk_specifics;
expected_desk_specifics.set_uuid(
base::StringPrintf(kUuidFormat, template_index));
expected_desk_specifics.set_name(
base::StringPrintf(kNameFormat, template_index));
expected_desk_specifics.set_created_time_windows_epoch_micros(
created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
expected_desk_specifics.set_desk_type(
SyncDeskType::WorkspaceDeskSpecifics_DeskType_TEMPLATE);
expected_desk_specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
Desk* expected_desk = expected_desk_specifics.mutable_desk();
WorkspaceDeskSpecifics_App* app = expected_desk->add_apps();
app->set_window_id(kBrowserWindowId);
BrowserAppWindow* browser_window =
app->mutable_app()->mutable_browser_app_window();
BrowserAppTab* first_tab = browser_window->add_tabs();
first_tab->set_url(GURL(base::StringPrintf(kTestUrlFormat, 1)).spec());
BrowserAppTab* second_tab = browser_window->add_tabs();
second_tab->set_url(GURL(base::StringPrintf(kTestUrlFormat, 2)).spec());
return expected_desk_specifics;
}
WorkspaceDeskSpecifics CreatePwaTemplateExpectedValue(
int template_index,
const base::Time& created_time) {
WorkspaceDeskSpecifics expected_desk_specifics;
expected_desk_specifics.set_uuid(
base::StringPrintf(kUuidFormat, template_index));
expected_desk_specifics.set_name(
base::StringPrintf(kNameFormat, template_index));
expected_desk_specifics.set_created_time_windows_epoch_micros(
created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
expected_desk_specifics.set_desk_type(
SyncDeskType::WorkspaceDeskSpecifics_DeskType_TEMPLATE);
expected_desk_specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
Desk* expected_desk = expected_desk_specifics.mutable_desk();
WorkspaceDeskSpecifics_App* app = expected_desk->add_apps();
app->set_window_id(kPwaWindowId);
BrowserAppWindow* browser_window =
app->mutable_app()->mutable_browser_app_window();
BrowserAppTab* first_tab = browser_window->add_tabs();
first_tab->set_url(GURL(base::StringPrintf(kTestUrlFormat, 1)).spec());
browser_window->set_show_as_app(true);
return expected_desk_specifics;
}
WorkspaceDeskSpecifics CreateChromeAppTemplateExpectedValue(
int template_index,
const base::Time& created_time,
int window_id,
const std::string& app_id) {
WorkspaceDeskSpecifics expected_desk_specifics;
expected_desk_specifics.set_uuid(
base::StringPrintf(kUuidFormat, template_index));
expected_desk_specifics.set_name(
base::StringPrintf(kNameFormat, template_index));
expected_desk_specifics.set_created_time_windows_epoch_micros(
created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
expected_desk_specifics.set_desk_type(
SyncDeskType::WorkspaceDeskSpecifics_DeskType_TEMPLATE);
expected_desk_specifics.set_device_form_factor(
sync_pb::SyncEnums::DeviceFormFactor::
SyncEnums_DeviceFormFactor_DEVICE_FORM_FACTOR_DESKTOP);
Desk* expected_desk = expected_desk_specifics.mutable_desk();
WorkspaceDeskSpecifics_App* app = expected_desk->add_apps();
app->set_window_id(window_id);
ChromeApp* app_window = app->mutable_app()->mutable_chrome_app();
app_window->set_app_id(app_id);
return expected_desk_specifics;
}
DataTypeState StateWithEncryption(const std::string& encryption_key_name) {
DataTypeState state;
state.set_encryption_key_name(encryption_key_name);
return state;
}
class MockDeskModelObserver : public DeskModelObserver {
public:
MOCK_METHOD0(DeskModelLoaded, void());
MOCK_METHOD1(
EntriesAddedOrUpdatedRemotely,
void(
const std::vector<raw_ptr<const DeskTemplate, VectorExperimental>>&));
MOCK_METHOD1(EntriesRemovedRemotely, void(const std::vector<base::Uuid>&));
};
MATCHER_P(UuidIs, e, "") {
return testing::ExplainMatchResult(e, arg->uuid(), result_listener);
}
MATCHER_P(EqualsSpecifics, expected, "") {
if (arg.SerializeAsString() != expected.SerializeAsString()) {
*result_listener << "Expected:\n"
<< expected.SerializeAsString() << "\nActual\n"
<< arg.SerializeAsString() << "\n";
return false;
}
return true;
}
class DeskSyncBridgeTest : public testing::Test {
public:
DeskSyncBridgeTest(const DeskSyncBridgeTest&) = delete;
DeskSyncBridgeTest& operator=(const DeskSyncBridgeTest&) = delete;
protected:
DeskSyncBridgeTest()
: store_(DataTypeStoreTestUtil::CreateInMemoryStoreForTest()),
cache_(std::make_unique<apps::AppRegistryCache>()),
account_id_(AccountId::FromUserEmail("[email protected]")) {}
void CreateBridge() {
ON_CALL(mock_processor_, IsTrackingMetadata()).WillByDefault(Return(true));
bridge_ = std::make_unique<DeskSyncBridge>(
mock_processor_.CreateForwardingProcessor(),
DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get()),
account_id_);
bridge_->AddObserver(&mock_observer_);
}
void FinishInitialization() { base::RunLoop().RunUntilIdle(); }
void InitializeBridge() {
CreateBridge();
FinishInitialization();
}
void DisableBridgeSync() {
ON_CALL(mock_processor_, IsTrackingMetadata()).WillByDefault(Return(false));
}
void ShutdownBridge() {
base::RunLoop().RunUntilIdle();
bridge_->RemoveObserver(&mock_observer_);
}
void RestartBridge() {
ShutdownBridge();
InitializeBridge();
}
void WriteToStoreWithMetadata(
const std::vector<WorkspaceDeskSpecifics>& specifics_list,
DataTypeState state) {
std::unique_ptr<DataTypeStore::WriteBatch> batch =
store_->CreateWriteBatch();
for (auto& specifics : specifics_list) {
batch->WriteData(specifics.uuid(), specifics.SerializeAsString());
}
batch->GetMetadataChangeList()->UpdateDataTypeState(state);
CommitToStoreAndWait(std::move(batch));
}
void CommitToStoreAndWait(std::unique_ptr<DataTypeStore::WriteBatch> batch) {
base::RunLoop loop;
store_->CommitWriteBatch(
std::move(batch),
base::BindOnce(
[](base::RunLoop* loop, const std::optional<ModelError>& result) {
EXPECT_FALSE(result.has_value()) << result->ToString();
loop->Quit();
},
&loop));
loop.Run();
}
EntityData MakeEntityData(
const WorkspaceDeskSpecifics& workspace_desk_specifics) {
EntityData entity_data;
*entity_data.specifics.mutable_workspace_desk() = workspace_desk_specifics;
entity_data.name = workspace_desk_specifics.name();
return entity_data;
}
EntityData MakeEntityData(const DeskTemplate& desk_template) {
return MakeEntityData(
desk_template_conversion::ToSyncProto(&desk_template, app_cache()));
}
// Helper method to reduce duplicated code between tests. Wraps the given
// specifics objects in an EntityData and EntityChange of type ACTION_ADD,
// and returns an EntityChangeList containing them all. Order is maintained.
EntityChangeList EntityAddList(
const std::vector<WorkspaceDeskSpecifics>& specifics_list) {
EntityChangeList changes;
for (const auto& specifics : specifics_list) {
changes.push_back(
EntityChange::CreateAdd(specifics.uuid(), MakeEntityData(specifics)));
}
return changes;
}
base::Time AdvanceAndGetTime(base::TimeDelta delta = base::Milliseconds(10)) {
clock_.Advance(delta);
return clock_.Now();
}
void AddTwoTemplates() {
auto desk_template1 =
desk_template_conversion::FromSyncProto(ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1",
AdvanceAndGetTime()));
auto desk_template2 =
desk_template_conversion::FromSyncProto(ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(2)).AsLowercaseString(), "template 2",
AdvanceAndGetTime()));
base::RunLoop loop1;
bridge()->AddOrUpdateEntry(
std::move(desk_template1),
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;
bridge()->AddOrUpdateEntry(
std::move(desk_template2),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop2.Quit();
}));
loop2.Run();
}
void AddTwoTemplatesWithDuplicatedNames() {
// These two templates will have new UUIDs but with names that collides
// with "template 1"
auto desk_template1 =
desk_template_conversion::FromSyncProto(ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(8)).AsLowercaseString(), "template 1",
AdvanceAndGetTime()));
auto desk_template2 =
desk_template_conversion::FromSyncProto(ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(9)).AsLowercaseString(), "template 1",
AdvanceAndGetTime()));
base::RunLoop loop1;
bridge()->AddOrUpdateEntry(
std::move(desk_template1),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(DeskModel::AddOrUpdateEntryStatus::kOk, status);
loop1.Quit();
}));
loop1.Run();
base::RunLoop loop2;
bridge()->AddOrUpdateEntry(
std::move(desk_template2),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(DeskModel::AddOrUpdateEntryStatus::kOk, status);
loop2.Quit();
}));
loop2.Run();
}
void SetOneAdminTemplate() {
auto admin_template1 =
desk_template_conversion::FromSyncProto(ExampleWorkspaceDeskSpecifics(
MakeAdminTestUuid(TestUuidId(1)).AsLowercaseString(),
"admin template 1", AdvanceAndGetTime()));
std::string policy_json;
base::Value::List template_list;
template_list.Append(
desk_template_conversion::SerializeDeskTemplateAsBaseValue(
admin_template1.get(), cache_.get()));
bool conversion_success =
base::JSONWriter::Write(template_list, &policy_json);
EXPECT_TRUE(conversion_success);
bridge()->SetPolicyDeskTemplates(policy_json);
}
// testing::test.
void SetUp() override {
desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
}
MockDataTypeLocalChangeProcessor* processor() { return &mock_processor_; }
DeskSyncBridge* bridge() { return bridge_.get(); }
MockDeskModelObserver* mock_observer() { return &mock_observer_; }
base::SimpleTestClock* clock() { return &clock_; }
apps::AppRegistryCache* app_cache() { return cache_.get(); }
private:
base::SimpleTestClock clock_;
// In memory data type store needs to be able to post tasks.
base::test::TaskEnvironment task_environment_;
std::unique_ptr<DataTypeStore> store_;
testing::NiceMock<MockDataTypeLocalChangeProcessor> mock_processor_;
testing::NiceMock<MockDeskModelObserver> mock_observer_;
std::unique_ptr<DeskSyncBridge> bridge_;
std::unique_ptr<apps::AppRegistryCache> cache_;
AccountId account_id_;
};
TEST_F(DeskSyncBridgeTest, DeskTemplateConversionShouldBeLossless) {
CreateBridge();
WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1");
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache());
std::unique_ptr<DeskTemplate> converted_desk_template =
desk_template_conversion::FromSyncProto(converted_desk_proto);
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
TEST_F(DeskSyncBridgeTest, DeskTemplateJsonConversionShouldBeLossless) {
CreateBridge();
WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1");
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
base::Value template_value =
desk_template_conversion::SerializeDeskTemplateAsBaseValue(
desk_template.get(), app_cache());
auto converted_desk_template =
desk_template_conversion::ParseDeskTemplateFromBaseValue(
template_value, ash::DeskTemplateSource::kPolicy);
EXPECT_TRUE(converted_desk_template.has_value());
EXPECT_EQ(
desk_template->desk_restore_data()->ConvertToValue(),
converted_desk_template.value()->desk_restore_data()->ConvertToValue());
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(
converted_desk_template.value().get(), app_cache());
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
TEST_F(DeskSyncBridgeTest, AppNameConversionShouldBeLossless) {
constexpr int kExampleDeskBrowserAppIndex = 0;
constexpr int kExampleDeskArcAppIndex = 1;
constexpr int kExampleDeskSystemWebAppIndex = 4;
CreateBridge();
WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskBrowserAppIndex)
->set_app_name("app name 1");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskArcAppIndex)
->set_app_name("app name 2");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskChromeAppIndex)
->set_app_name("app name 3");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskProgressiveWebAppIndex)
->set_app_name("app name 4");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskSystemWebAppIndex)
->set_app_name("app name 5");
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache());
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
TEST_F(DeskSyncBridgeTest, WindowOpenDispositionConversionShouldBeLossless) {
CreateBridge();
for (const auto& test_value : kWindowOpenDispositionValues) {
WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskChromeAppIndex)
->set_disposition(test_value);
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskProgressiveWebAppIndex)
->set_disposition(test_value);
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache());
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
}
TEST_F(DeskSyncBridgeTest, LaunchContainerConversionShouldBeLossless) {
CreateBridge();
for (const auto& test_value : kLaunchContainerValues) {
WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1");
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskChromeAppIndex)
->set_container(test_value);
desk_proto.mutable_desk()
->mutable_apps(kExampleDeskProgressiveWebAppIndex)
->set_container(test_value);
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache());
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
}
TEST_F(DeskSyncBridgeTest, TabGroupInfoConversionShouldBeLossless) {
CreateBridge();
// Iterate over colors, changing the values contained within the
// tab group for each iteration.
size_t curr_start = 0;
for (const auto& test_color_value : kTabGroupColors) {
// We test with the color GREEN by default.
if (test_color_value ==
SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN) {
continue;
}
WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1",
/*Set a different range for each iteration.*/
curr_start, curr_start + 1,
/*Change name for each iteration.*/
base::StringPrintf("test_tab_group_%zu", curr_start),
/*Modulate between true and false for is_collapsed value on each
iteration.*/
static_cast<bool>(curr_start % 2),
/*Set the color to the current iteration*/
test_color_value);
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache());
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
// Shift range up by one.
++curr_start;
}
}
TEST_F(DeskSyncBridgeTest, FloatingWorkspaceConversionShouldBeLossless) {
CreateBridge();
WorkspaceDeskSpecifics desk_proto =
CreateFloatingWorkspaceTemplateExpectedValue("cache_guid");
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(desk_proto);
WorkspaceDeskSpecifics converted_desk_proto =
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache());
std::unique_ptr<DeskTemplate> converted_desk_template =
desk_template_conversion::FromSyncProto(converted_desk_proto);
EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
}
// Tests that URLs are saved properly when converting a DeskTemplate
// to its Protobuf form.
TEST_F(DeskSyncBridgeTest, EnsureAshBrowserWindowsSavedProperly) {
CreateBridge();
// Uses a different method to instantiate the template that doesn't rely
// on the assumption that the template is instantiated from a proto, but
// rather is captured and saved for the first time.
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(
SavedDeskBrowserBuilder()
.SetUrls({GURL(base::StringPrintf(kTestUrlFormat, 1)),
GURL(base::StringPrintf(kTestUrlFormat, 2))})
.SetIsLacros(false)
.SetGenericBuilder(SavedDeskGenericAppBuilder().SetWindowId(
kBrowserWindowId))
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreateBrowserTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time())));
}
TEST_F(DeskSyncBridgeTest, EnsureLacrosBrowserWindowsCanBeSavedProperly) {
CreateBridge();
// Uses a different method to instantiate the template that doesn't rely
// on the assumption that the template is instantiated from a proto, but
// rather is captured and saved for the first time.
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(
SavedDeskBrowserBuilder()
.SetUrls({GURL(base::StringPrintf(kTestUrlFormat, 1)),
GURL(base::StringPrintf(kTestUrlFormat, 2))})
.SetIsLacros(true)
.SetGenericBuilder(SavedDeskGenericAppBuilder().SetWindowId(
kBrowserWindowId))
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreateBrowserTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time())));
}
TEST_F(DeskSyncBridgeTest, EnsurePwaInAshChromeCanBeSavedProperly) {
CreateBridge();
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(
SavedDeskBrowserBuilder()
.SetUrls({GURL(base::StringPrintf(kTestUrlFormat, 1))})
.SetIsLacros(false)
.SetIsApp(true)
.SetGenericBuilder(
SavedDeskGenericAppBuilder().SetWindowId(kPwaWindowId))
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreatePwaTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time())));
}
TEST_F(DeskSyncBridgeTest, EnsurePwaInLacrosChromeCanBeSavedProperly) {
CreateBridge();
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(
SavedDeskBrowserBuilder()
.SetUrls({GURL(base::StringPrintf(kTestUrlFormat, 1))})
.SetIsLacros(true)
.SetIsApp(true)
.SetGenericBuilder(
SavedDeskGenericAppBuilder().SetWindowId(kPwaWindowId))
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreatePwaTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time())));
}
TEST_F(DeskSyncBridgeTest, EnsureChromeAppCanBeSavedProperly) {
CreateBridge();
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(SavedDeskGenericAppBuilder()
.SetAppId(desk_test_util::kTestChromeAppId)
.SetWindowId(kChromeAppWindowId)
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreateChromeAppTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time(),
kChromeAppWindowId, desk_test_util::kTestChromeAppId)));
}
TEST_F(DeskSyncBridgeTest, EnsureLacrosChromeAppCanBeSavedProperly) {
CreateBridge();
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(SavedDeskGenericAppBuilder()
.SetAppId(desk_test_util::kTestLacrosChromeAppId)
.SetWindowId(kChromeAppWindowId)
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreateChromeAppTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time(),
kChromeAppWindowId, desk_test_util::kTestLacrosChromeAppId)));
}
TEST_F(DeskSyncBridgeTest, EnsureUnsupportedAppCanBeIgnored) {
constexpr int kUnsupportedAppWindowId = 1888;
CreateBridge();
std::unique_ptr<DeskTemplate> desk_template =
SavedDeskBuilder()
.SetUuid(base::StringPrintf(kUuidFormat, kDefaultTemplateIndex))
.SetName(base::StringPrintf(kNameFormat, kDefaultTemplateIndex))
.AddAppWindow(SavedDeskGenericAppBuilder()
.SetWindowId(kChromeAppWindowId)
.SetAppId(desk_test_util::kTestChromeAppId)
.Build())
.AddAppWindow(SavedDeskGenericAppBuilder()
.SetWindowId(kUnsupportedAppWindowId)
.SetAppId(desk_test_util::kTestUnsupportedAppId)
.Build())
.Build();
EXPECT_THAT(
desk_template_conversion::ToSyncProto(desk_template.get(), app_cache()),
EqualsSpecifics(CreateChromeAppTemplateExpectedValue(
kDefaultTemplateIndex, desk_template->created_time(),
kChromeAppWindowId, desk_test_util::kTestChromeAppId)));
}
// Tests that the sync bridge appropriately handles explicitly unknown desk
// type as invalid.
TEST_F(DeskSyncBridgeTest, EnsureGracefulHandlingOfUnknownDeskTypes) {
WorkspaceDeskSpecifics unknown_desk = CreateUnknownDeskType();
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(unknown_desk);
EXPECT_EQ(desk_template, nullptr);
}
// Tests that the sync bridge treat saved desk with missing desk type as desk
// template.
TEST_F(DeskSyncBridgeTest, EnsureHandlingOfMissingDeskTypes) {
WorkspaceDeskSpecifics unknown_desk = CreateWorkspaceDeskWithoutDeskType();
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(unknown_desk);
EXPECT_EQ(desk_template->type(), DeskType::kTemplate);
}
TEST_F(DeskSyncBridgeTest, IsBridgeReady) {
CreateBridge();
ASSERT_FALSE(bridge()->IsReady());
FinishInitialization();
ASSERT_TRUE(bridge()->IsReady());
}
TEST_F(DeskSyncBridgeTest, IsBridgeSyncing) {
InitializeBridge();
ASSERT_TRUE(bridge()->IsSyncing());
DisableBridgeSync();
ASSERT_FALSE(bridge()->IsSyncing());
}
TEST_F(DeskSyncBridgeTest, InitializationWithLocalDataAndMetadata) {
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(1);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(2);
DataTypeState state = StateWithEncryption("test_encryption_key");
WriteToStoreWithMetadata({template1, template2}, state);
EXPECT_CALL(*processor(), ModelReadyToSync(MetadataBatchContains(
HasEncryptionKeyName("test_encryption_key"),
/*entities=*/_)));
InitializeBridge();
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Verify both local specifics are loaded correctly.
EXPECT_EQ(template1.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(
base::Uuid::ParseCaseInsensitive(template1.uuid())),
app_cache())
.SerializeAsString());
EXPECT_EQ(template2.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(
base::Uuid::ParseCaseInsensitive(template2.uuid())),
app_cache())
.SerializeAsString());
}
TEST_F(DeskSyncBridgeTest, GetAllEntriesIncludesPolicyEntries) {
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(1);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(2);
DataTypeState state = StateWithEncryption("test_encryption_key");
WriteToStoreWithMetadata({template1, template2}, state);
EXPECT_CALL(*processor(), ModelReadyToSync(MetadataBatchContains(
HasEncryptionKeyName("test_encryption_key"),
/*entities=*/_)));
InitializeBridge();
bridge()->SetPolicyDeskTemplates(GetPolicyWithTwoTemplates());
EXPECT_EQ(4ul, bridge()->GetAllEntryUuids().size());
auto result = bridge()->GetAllEntries();
EXPECT_EQ(result.status, DeskModel::GetAllEntriesStatus::kOk);
EXPECT_EQ(result.entries.size(), 4ul);
// Two 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;
}),
2l);
bridge()->SetPolicyDeskTemplates("");
}
TEST_F(DeskSyncBridgeTest, AddEntriesLocally) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
EXPECT_EQ(0ul, bridge()->GetAllEntryUuids().size());
auto specifics1 = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1",
AdvanceAndGetTime());
auto specifics2 = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(2)).AsLowercaseString(), "template 2",
AdvanceAndGetTime());
base::RunLoop loop1;
bridge()->AddOrUpdateEntry(
desk_template_conversion::FromSyncProto(specifics1),
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;
bridge()->AddOrUpdateEntry(
desk_template_conversion::FromSyncProto(specifics2),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop2.Quit();
}));
loop2.Run();
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Verify the added desk template content.
EXPECT_EQ(specifics1.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(MakeTestUuid(TestUuidId(1))),
app_cache())
.SerializeAsString());
EXPECT_EQ(specifics2.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(MakeTestUuid(TestUuidId(2))),
app_cache())
.SerializeAsString());
}
TEST_F(DeskSyncBridgeTest, AddEntryShouldFailWhenEntryIsTooLarge) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
EXPECT_EQ(0ul, bridge()->GetAllEntryUuids().size());
// Create a large entry with 500 tabs. This entry should be too large for
// Sync.
constexpr int number_of_tabs = 500;
auto specifics = ExampleWorkspaceDeskSpecifics(
MakeTestUuid(TestUuidId(1)).AsLowercaseString(), "template 1",
AdvanceAndGetTime(), number_of_tabs);
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
desk_template_conversion::FromSyncProto(specifics),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status,
DeskModel::AddOrUpdateEntryStatus::kEntryTooLarge);
loop.Quit();
}));
loop.Run();
}
TEST_F(DeskSyncBridgeTest, AddEntryShouldSucceedWhenSyncIsDisabled) {
InitializeBridge();
DisableBridgeSync();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
// Add entry should fail when the sync bridge is not ready.
EXPECT_CALL(*processor(), Put(_, _, _)).Times(1);
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
std::make_unique<DeskTemplate>(
MakeTestUuid(TestUuidId(1)), DeskTemplateSource::kUser, "template 1",
AdvanceAndGetTime(), DeskTemplateType::kTemplate),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop.Quit();
}));
loop.Run();
}
TEST_F(DeskSyncBridgeTest, AddEntryShouldFailWhenBridgeIsNotReady) {
// Only create sync bridge but do not allow it to finish initialization.
CreateBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
// Add entry should fail when the sync bridge is not ready.
EXPECT_CALL(*processor(), Put(_, _, _)).Times(0);
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
std::make_unique<DeskTemplate>(
MakeTestUuid(TestUuidId(1)), DeskTemplateSource::kUser, "template 1",
AdvanceAndGetTime(), DeskTemplateType::kTemplate),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kFailure);
loop.Quit();
}));
loop.Run();
}
TEST_F(DeskSyncBridgeTest, CanDetectDuplicateName) {
InitializeBridge();
AddTwoTemplates();
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
AddTwoTemplatesWithDuplicatedNames();
// The two duplicated templates should be added.
EXPECT_EQ(4ul, bridge()->GetAllEntryUuids().size());
EXPECT_TRUE(bridge()->FindOtherEntryWithName(
bridge()
->GetUserEntryByUUID(MakeTestUuid(TestUuidId(9)))
->template_name(),
bridge()->GetUserEntryByUUID(MakeTestUuid(TestUuidId(9)))->type(),
bridge()->GetUserEntryByUUID(MakeTestUuid(TestUuidId(9)))->uuid()));
}
TEST_F(DeskSyncBridgeTest, CanDetectNoDuplicateName) {
InitializeBridge();
AddTwoTemplates();
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
EXPECT_FALSE(bridge()->FindOtherEntryWithName(
bridge()
->GetUserEntryByUUID(MakeTestUuid(TestUuidId(1)))
->template_name(),
bridge()->GetUserEntryByUUID(MakeTestUuid(TestUuidId(1)))->type(),
bridge()->GetUserEntryByUUID(MakeTestUuid(TestUuidId(1)))->uuid()));
}
TEST_F(DeskSyncBridgeTest, GetEntryByUUIDShouldSucceed) {
InitializeBridge();
AddTwoTemplates();
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
auto result = bridge()->GetEntryByUUID(MakeTestUuid(TestUuidId(1)));
EXPECT_EQ(result.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_TRUE(result.entry);
}
TEST_F(DeskSyncBridgeTest, GetAllEntriesByUuidsReturnsCorrectSet) {
InitializeBridge();
AddTwoTemplates();
std::set<base::Uuid> entry_uuids = bridge()->GetAllEntryUuids();
EXPECT_EQ(2ul, entry_uuids.size());
entry_uuids.erase(MakeTestUuid(TestUuidId(1)));
entry_uuids.erase(MakeTestUuid(TestUuidId(2)));
// IFF the set is correct it should be empty.
EXPECT_TRUE(entry_uuids.empty());
}
// Verify that event_flag placeholder has been set. This is a short-term
// fix for https://crbug.com/1301798
TEST_F(DeskSyncBridgeTest, GetEntryByUUIDShouldFillEventFlag) {
InitializeBridge();
AddTwoTemplates();
auto result = bridge()->GetEntryByUUID(MakeTestUuid(TestUuidId(1)));
EXPECT_EQ(result.status, DeskModel::GetEntryByUuidStatus::kOk);
EXPECT_TRUE(result.entry);
for (const auto& [app_id, launch_list] :
result.entry->desk_restore_data()->app_id_to_launch_list()) {
for (const auto& [id, restore_data] : launch_list) {
EXPECT_EQ(restore_data->event_flag, 0);
}
}
}
TEST_F(DeskSyncBridgeTest, GetEntryByUUIDShouldReturnAdminTemplate) {
InitializeBridge();
AddTwoTemplates();
SetOneAdminTemplate();
// There should be 3 templates: 2 user templates + 1 admin template.
EXPECT_EQ(3ul, bridge()->GetAllEntryUuids().size());
base::RunLoop loop;
auto result = bridge()->GetEntryByUUID(MakeAdminTestUuid(TestUuidId(1)));
EXPECT_EQ(DeskModel::GetEntryByUuidStatus::kOk, result.status);
EXPECT_TRUE(result.entry);
}
TEST_F(DeskSyncBridgeTest, GetEntryByUUIDShouldFailWhenUuidIsNotFound) {
InitializeBridge();
AddTwoTemplates();
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
const base::Uuid nonExistingUuid =
base::Uuid::ParseCaseInsensitive(base::StringPrintf(kUuidFormat, 5));
base::RunLoop loop;
auto result = bridge()->GetEntryByUUID(nonExistingUuid);
EXPECT_EQ(result.status, DeskModel::GetEntryByUuidStatus::kNotFound);
EXPECT_FALSE(result.entry);
}
TEST_F(DeskSyncBridgeTest, GetEntryByUUIDShouldFailWhenUuidIsInvalid) {
InitializeBridge();
auto result = bridge()->GetEntryByUUID(base::Uuid());
EXPECT_EQ(result.status, DeskModel::GetEntryByUuidStatus::kInvalidUuid);
EXPECT_FALSE(result.entry);
}
TEST_F(DeskSyncBridgeTest, UpdateEntryLocally) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
EXPECT_EQ(0ul, bridge()->GetAllEntryUuids().size());
// Seed two templates.
AddTwoTemplates();
// We should have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Update template 1
EXPECT_CALL(*processor(), Put(_, _, _)).Times(1);
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
std::make_unique<DeskTemplate>(MakeTestUuid(TestUuidId(1)),
DeskTemplateSource::kUser,
"updated template 1", AdvanceAndGetTime(),
DeskTemplateType::kTemplate),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status, DeskModel::AddOrUpdateEntryStatus::kOk);
loop.Quit();
}));
loop.Run();
// We should still have both templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Template 1 should be updated.
EXPECT_EQ(
"updated template 1",
base::UTF16ToUTF8(bridge()
->GetUserEntryByUUID(MakeTestUuid(TestUuidId(1)))
->template_name()));
// Template 2 should be unchanged.
EXPECT_EQ(
"template 2",
base::UTF16ToUTF8(bridge()
->GetUserEntryByUUID(MakeTestUuid(TestUuidId(2)))
->template_name()));
}
TEST_F(DeskSyncBridgeTest, DeleteEntryLocally) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
EXPECT_EQ(0ul, bridge()->GetAllEntryUuids().size());
// Seed two templates.
AddTwoTemplates();
// We should have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Delete template 1.
base::RunLoop loop;
bridge()->DeleteEntry(
MakeTestUuid(TestUuidId(1)),
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
loop.Quit();
}));
loop.Run();
// We should have only 1 template.
EXPECT_EQ(1ul, bridge()->GetAllEntryUuids().size());
// Template 2 should be unchanged.
EXPECT_EQ(
"template 2",
base::UTF16ToUTF8(bridge()
->GetUserEntryByUUID(MakeTestUuid(TestUuidId(2)))
->template_name()));
}
TEST_F(DeskSyncBridgeTest, DeleteAllEntriesLocally) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
EXPECT_EQ(0ul, bridge()->GetAllEntryUuids().size());
// Seed two templates.
AddTwoTemplates();
// We should have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Delete all templates.
base::RunLoop loop;
bridge()->DeleteAllEntries(
base::BindLambdaForTesting([&](DeskModel::DeleteEntryStatus status) {
EXPECT_EQ(status, DeskModel::DeleteEntryStatus::kOk);
loop.Quit();
}));
loop.Run();
// We should have no templates.
EXPECT_EQ(0ul, bridge()->GetAllEntryUuids().size());
}
TEST_F(DeskSyncBridgeTest, ApplyIncrementalSyncChangesEmpty) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(_)).Times(0);
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
auto error = bridge()->ApplyIncrementalSyncChanges(
bridge()->CreateMetadataChangeList(), EntityChangeList());
EXPECT_FALSE(error);
}
TEST_F(DeskSyncBridgeTest, ApplyIncrementalSyncChangesWithTwoAdditions) {
InitializeBridge();
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(1);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(2);
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(SizeIs(2)));
auto error = bridge()->ApplyIncrementalSyncChanges(
bridge()->CreateMetadataChangeList(),
EntityAddList({template1, template2}));
EXPECT_FALSE(error);
// We should have two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
}
TEST_F(DeskSyncBridgeTest, ApplyIncrementalSyncChangesWithOneUpdate) {
InitializeBridge();
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(1);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(2);
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(SizeIs(2)));
bridge()->ApplyIncrementalSyncChanges(bridge()->CreateMetadataChangeList(),
EntityAddList({template1, template2}));
// We should have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Now update template 1 with a new content.
WorkspaceDeskSpecifics updated_template1 = CreateWorkspaceDeskSpecifics(1);
updated_template1.set_name("updated template 1");
EntityChangeList update_changes;
update_changes.push_back(EntityChange::CreateUpdate(
template1.uuid(), MakeEntityData(updated_template1)));
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(SizeIs(1)));
bridge()->ApplyIncrementalSyncChanges(bridge()->CreateMetadataChangeList(),
std::move(update_changes));
// We should still have both templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Template 1 should be updated to new content.
EXPECT_EQ(updated_template1.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(
base::Uuid::ParseCaseInsensitive(template1.uuid())),
app_cache())
.SerializeAsString());
EXPECT_EQ(template2.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(
base::Uuid::ParseCaseInsensitive(template2.uuid())),
app_cache())
.SerializeAsString());
}
// Tests that remote desk template can be correctly removed.
TEST_F(DeskSyncBridgeTest, ApplyIncrementalSyncChangesWithOneDeletion) {
InitializeBridge();
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(1);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(2);
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(SizeIs(2)));
bridge()->ApplyIncrementalSyncChanges(bridge()->CreateMetadataChangeList(),
EntityAddList({template1, template2}));
// Verify that we have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Now delete template 1.
EntityChangeList delete_changes;
delete_changes.push_back(EntityChange::CreateDelete(template1.uuid()));
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(SizeIs(1)));
bridge()->ApplyIncrementalSyncChanges(bridge()->CreateMetadataChangeList(),
std::move(delete_changes));
// Verify that we only have template 2.
EXPECT_EQ(1ul, bridge()->GetAllEntryUuids().size());
EXPECT_EQ(template2.SerializeAsString(),
desk_template_conversion::ToSyncProto(
bridge()->GetUserEntryByUUID(
base::Uuid::ParseCaseInsensitive(template2.uuid())),
app_cache())
.SerializeAsString());
}
TEST_F(DeskSyncBridgeTest, ApplyIncrementalSyncChangesDeleteNonexistent) {
InitializeBridge();
EXPECT_CALL(*mock_observer(), EntriesRemovedRemotely(_)).Times(0);
std::unique_ptr<MetadataChangeList> metadata_changes =
bridge()->CreateMetadataChangeList();
EXPECT_CALL(*processor(), Delete).Times(0);
EntityChangeList entity_change_list;
entity_change_list.push_back(EntityChange::CreateDelete("no-such-uuid"));
auto error = bridge()->ApplyIncrementalSyncChanges(
std::move(metadata_changes), std::move(entity_change_list));
EXPECT_FALSE(error);
}
TEST_F(DeskSyncBridgeTest, MergeFullSyncDataWithTwoEntries) {
InitializeBridge();
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(1);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(2);
auto metadata_change_list = std::make_unique<InMemoryMetadataChangeList>();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(SizeIs(2)));
bridge()->MergeFullSyncData(std::move(metadata_change_list),
EntityAddList({template1, template2}));
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
}
TEST_F(DeskSyncBridgeTest, MergeFullSyncDataUploadsLocalOnlyEntries) {
InitializeBridge();
// Seed two templates.
// Seeded templates will be "template 1" and "template 2".
AddTwoTemplates();
// We should have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
// Create server-side templates "template 2" and "template 3".
const WorkspaceDeskSpecifics template1 = CreateWorkspaceDeskSpecifics(2);
const WorkspaceDeskSpecifics template2 = CreateWorkspaceDeskSpecifics(3);
auto metadata_change_list = std::make_unique<InMemoryMetadataChangeList>();
EXPECT_CALL(*mock_observer(), EntriesAddedOrUpdatedRemotely(SizeIs(2)));
// MergeFullSyncData should upload the local-only template "template 1".
EXPECT_CALL(*processor(),
Put(StrEq(MakeTestUuid(TestUuidId(1)).AsLowercaseString()), _, _))
.Times(1);
bridge()->MergeFullSyncData(std::move(metadata_change_list),
EntityAddList({template1, template2}));
// Merged data should contain 3 templtes.
EXPECT_EQ(3ul, bridge()->GetAllEntryUuids().size());
}
TEST_F(DeskSyncBridgeTest,
GetEntryCountShouldIncludeBothUserAndAdminTemplates) {
InitializeBridge();
AddTwoTemplates();
SetOneAdminTemplate();
// There should be 3 templates: 2 user templates + 1 admin template.
EXPECT_EQ(3ul, bridge()->GetEntryCount());
}
TEST_F(DeskSyncBridgeTest, GetMaxEntryCountShouldIncreaseWithAdminTemplates) {
InitializeBridge();
AddTwoTemplates();
EXPECT_EQ(6ul, bridge()->GetMaxDeskTemplateEntryCount());
SetOneAdminTemplate();
// The max entry count should increase by 1 since we have set an admin
// template.
EXPECT_EQ(7ul, bridge()->GetMaxDeskTemplateEntryCount());
}
TEST_F(DeskSyncBridgeTest, GetTemplateJsonShouldReturnList) {
InitializeBridge();
// Seed two templates.
// Seeded templates will be "template 1" and "template 2".
AddTwoTemplates();
// We should have seeded two templates.
EXPECT_EQ(2ul, bridge()->GetAllEntryUuids().size());
base::RunLoop loop;
bridge()->GetTemplateJson(
MakeTestUuid(TestUuidId(1)), app_cache(),
base::BindLambdaForTesting([&](DeskModel::GetTemplateJsonStatus status,
const base::Value& templates_json) {
EXPECT_EQ(DeskModel::GetTemplateJsonStatus::kOk, status);
EXPECT_FALSE(templates_json.is_none());
std::string template_json_string;
bool parsed_result =
base::JSONWriter::Write(templates_json, &template_json_string);
EXPECT_TRUE(parsed_result);
// Content of the conversion is tested in:
// components/desks_storage/core/desk_template_conversion_unittests.cc
loop.Quit();
}));
loop.Run();
}
TEST_F(DeskSyncBridgeTest, AddUnknownDeskTypeShouldFail) {
InitializeBridge();
WorkspaceDeskSpecifics unknown_desk = CreateUnknownDeskType();
std::unique_ptr<DeskTemplate> desk_template =
desk_template_conversion::FromSyncProto(unknown_desk);
base::RunLoop loop;
bridge()->AddOrUpdateEntry(
std::move(desk_template),
base::BindLambdaForTesting(
[&](DeskModel::AddOrUpdateEntryStatus status,
std::unique_ptr<ash::DeskTemplate> new_entry) {
EXPECT_EQ(status,
DeskModel::AddOrUpdateEntryStatus::kInvalidArgument);
loop.Quit();
}));
loop.Run();
}
TEST_F(DeskSyncBridgeTest, CanRecordFileSizeMetrics) {
base::HistogramTester histogram_tester;
InitializeBridge();
AddTwoTemplates();
EXPECT_EQ(2ul, bridge()->GetEntryCount());
histogram_tester.ExpectTotalCount(kTemplateSizeHistogramName, 2u);
histogram_tester.ExpectBucketCount(kTemplateSizeHistogramName, 572, 2u);
}
} // namespace
} // namespace desks_storage