chromium/chrome/browser/sync/test/integration/sync_app_list_helper.cc

// Copyright 2013 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/sync/test/integration/sync_app_list_helper.h"

#include <map>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/ash/app_list/app_list_syncable_service.h"
#include "chrome/browser/ash/app_list/app_list_syncable_service_factory.h"
#include "chrome/browser/ash/app_list/chrome_app_list_item.h"
#include "chrome/browser/ash/app_list/chrome_app_list_model_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/common/extensions/sync_helper.h"
#include "extensions/browser/app_sorting.h"
#include "extensions/browser/extension_system.h"

using app_list::AppListSyncableService;
using app_list::AppListSyncableServiceFactory;

SyncAppListHelper* SyncAppListHelper::GetInstance() {
  SyncAppListHelper* instance = base::Singleton<SyncAppListHelper>::get();
  instance->SetupIfNecessary(sync_datatype_helper::test());
  return instance;
}

SyncAppListHelper::SyncAppListHelper() = default;

SyncAppListHelper::~SyncAppListHelper() = default;

void SyncAppListHelper::SetupIfNecessary(SyncTest* test) {
  if (setup_completed_) {
    DCHECK_EQ(test, test_);
    return;
  }
  test_ = test;
  for (Profile* profile : test_->GetAllProfiles()) {
    extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(
        true /* extensions_enabled */);
  }

  setup_completed_ = true;
}

bool SyncAppListHelper::AppListMatch(Profile* profile1, Profile* profile2) {
  AppListSyncableService* service1 =
      AppListSyncableServiceFactory::GetForProfile(profile1);
  AppListSyncableService* service2 =
      AppListSyncableServiceFactory::GetForProfile(profile2);
  // Note: sync item entries may not exist in verifier, but item lists should
  // match.
  if (service1->GetModelUpdater()->ItemCount() !=
      service2->GetModelUpdater()->ItemCount()) {
    LOG(ERROR) << "Model item count: "
               << service1->GetModelUpdater()->ItemCount()
               << " != " << service2->GetModelUpdater()->ItemCount();
    return false;
  }
  bool res = true;
  for (size_t i = 0; i < service1->GetModelUpdater()->ItemCount(); ++i) {
    ChromeAppListItem* item1 = service1->GetModelUpdater()->ItemAtForTest(i);
    size_t index2;
    if (!service2->GetModelUpdater()->FindItemIndexForTest(item1->id(),
                                                           &index2)) {
      LOG(ERROR) << " Item(" << i << ") in profile1: " << item1->ToDebugString()
                 << " Not in profile2.";
      res = false;
      continue;
    }

    ChromeAppListItem* item2 =
        service2->GetModelUpdater()->ItemAtForTest(index2);
    if (item1->CompareForTest(item2)) {
      continue;
    }

    LOG(ERROR) << "Item(" << i << ") in profile1: " << item1->ToDebugString()
               << " != "
               << "Item(" << i << ") in profile2: " << item2->ToDebugString();
    res = false;
  }
  return res;
}

bool SyncAppListHelper::AllProfilesHaveSameAppList(size_t* size_out) {
  const std::vector<raw_ptr<Profile, VectorExperimental>>& profiles =
      test_->GetAllProfiles();
  for (Profile* profile : profiles) {
    if (profile != profiles.front() &&
        !AppListMatch(profiles.front(), profile)) {
      DVLOG(1) << "Profile1: "
               << AppListSyncableServiceFactory::GetForProfile(profile);
      PrintAppList(profile);
      DVLOG(1) << "Profile2: "
               << AppListSyncableServiceFactory::GetForProfile(
                      profiles.front());
      PrintAppList(profiles.front());
      return false;
    }
  }
  if (size_out) {
    *size_out = AppListSyncableServiceFactory::GetForProfile(profiles.front())
                    ->GetModelUpdater()
                    ->ItemCount();
  }
  return true;
}

void SyncAppListHelper::MoveAppToFolder(Profile* profile,
                                        const std::string& id,
                                        const std::string& folder_id) {
  AppListSyncableService* service =
      AppListSyncableServiceFactory::GetForProfile(profile);
  service->GetModelUpdater()->SetItemFolderId(id, folder_id);
}

void SyncAppListHelper::MoveAppFromFolder(Profile* profile,
                                          const std::string& id,
                                          const std::string& folder_id) {
  AppListSyncableService* service =
      AppListSyncableServiceFactory::GetForProfile(profile);
  ChromeAppListItem* folder =
      service->GetModelUpdater()->FindFolderItem(folder_id);
  if (!folder) {
    LOG(ERROR) << "Folder not found: " << folder_id;
    return;
  }
  service->GetModelUpdater()->SetItemFolderId(id, "");
}

void SyncAppListHelper::PrintAppList(Profile* profile) {
  AppListSyncableService* service =
      AppListSyncableServiceFactory::GetForProfile(profile);
  // Build a map from each folder item's id to a list of its child items.
  std::map<const std::string, std::vector<ChromeAppListItem*>> children;
  for (size_t i = 0; i < service->GetModelUpdater()->ItemCount(); ++i) {
    ChromeAppListItem* item = service->GetModelUpdater()->ItemAtForTest(i);
    if (!item->folder_id().empty()) {
      children[item->folder_id()].push_back(item);
    }
  }
  for (size_t i = 0; i < service->GetModelUpdater()->ItemCount(); ++i) {
    ChromeAppListItem* item = service->GetModelUpdater()->ItemAtForTest(i);
    // Skip if it's not a top level item.
    if (!item->folder_id().empty()) {
      continue;
    }
    std::string label = base::StringPrintf("Item(%d): ", static_cast<int>(i));
    PrintItem(profile, item, label);
    // Print children if it has any.
    if (children.count(item->id())) {
      DCHECK(item->is_folder());
      std::vector<ChromeAppListItem*>& child_items =
          children[item->folder_id()];
      for (size_t j = 0; j < child_items.size(); ++j) {
        ChromeAppListItem* child_item = child_items[j];
        std::string child_label =
            base::StringPrintf(" Folder Item(%d): ", static_cast<int>(j));
        PrintItem(profile, child_item, child_label);
      }
    }
  }
}

void SyncAppListHelper::PrintItem(Profile* profile,
                                  ChromeAppListItem* item,
                                  const std::string& label) {
  extensions::AppSorting* s =
      extensions::ExtensionSystem::Get(profile)->app_sorting();
  std::string id = item->id();
  DVLOG(1) << label << item->ToDebugString()
           << " Page: " << s->GetPageOrdinal(id).ToDebugString().substr(0, 8)
           << " Item: "
           << s->GetAppLaunchOrdinal(id).ToDebugString().substr(0, 8);
}