// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>

#include <memory>
#include <string>

#include "base/base_paths.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_shortcut_win.h"
#include "base/threading/scoped_blocking_call.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_init_params.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_shortcut_manager.h"
#include "chrome/browser/profiles/profile_shortcut_manager_win.h"
#include "chrome/browser/shell_integration_win.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/shell_util.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/account_id/account_id.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"

class ProfileShortcutManagerTest : public testing::Test {
 protected:
  ProfileShortcutManagerTest()
      : profile_attributes_storage_(nullptr),
        fake_user_desktop_(base::DIR_USER_DESKTOP),
        fake_system_desktop_(base::DIR_COMMON_DESKTOP) {
  }

  void SetUp() override {
    ProfileShortcutManagerWin::DisableUnpinningForTests();
    ProfileShortcutManagerWin::DisableOutOfProcessShortcutOpsForTests();
    TestingBrowserProcess* browser_process =
        TestingBrowserProcess::GetGlobal();
    profile_manager_ = std::make_unique<TestingProfileManager>(browser_process);
    ASSERT_TRUE(profile_manager_->SetUp());
    profile_attributes_storage_ =
        profile_manager_->profile_attributes_storage();
    profile_shortcut_manager_ =
        ProfileShortcutManager::Create(profile_manager_->profile_manager());
    profile_1_name_ = u"My profile";
    profile_1_path_ = CreateProfileDirectory(profile_1_name_);
    profile_2_name_ = u"My profile 2";
    profile_2_path_ = CreateProfileDirectory(profile_2_name_);
    profile_3_name_ = u"My profile 3";
    profile_3_path_ = CreateProfileDirectory(profile_3_name_);
  }

  void TearDown() override {
    task_environment_.RunUntilIdle();

    // Delete all profiles and ensure their shortcuts got removed.
    const size_t num_profiles =
        profile_attributes_storage_->GetNumberOfProfiles();
    for (size_t i = 0; i < num_profiles; ++i) {
      ProfileAttributesEntry* entry =
          profile_attributes_storage_->GetAllProfilesAttributes().front();
      const base::FilePath profile_path = entry->GetPath();
      std::u16string profile_name = entry->GetName();
      profile_attributes_storage_->RemoveProfile(profile_path);
      task_environment_.RunUntilIdle();
      ASSERT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_name));
      // The icon file is not deleted until the profile directory is deleted.
      const base::FilePath icon_path =
          profiles::internal::GetProfileIconPath(profile_path);
      ASSERT_TRUE(base::PathExists(icon_path));
    }
  }

  base::FilePath CreateProfileDirectory(const std::u16string& profile_name) {
    const base::FilePath profile_path =
        profile_manager_->profiles_dir().Append(base::AsWString(profile_name));
    base::CreateDirectory(profile_path);
    return profile_path;
  }

  void SetupDefaultProfileShortcut(const base::Location& location) {
    ASSERT_EQ(0u, profile_attributes_storage_->GetNumberOfProfiles())
        << location.ToString();
    ASSERT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_))
        << location.ToString();
    ProfileAttributesInitParams params;
    params.profile_path = profile_1_path_;
    params.profile_name = profile_1_name_;
    profile_attributes_storage_->AddProfile(std::move(params));
    // Also create a non-badged shortcut for Chrome, which is conveniently done
    // by |CreateProfileShortcut()| since there is only one profile.
    profile_shortcut_manager_->CreateProfileShortcut(profile_1_path_);
    task_environment_.RunUntilIdle();
    // Verify that there's now an unbadged, unmamed shortcut for the single
    // profile.
    ValidateSingleProfileShortcut(location, profile_1_path_);
  }

  void SetupNonProfileShortcut(const base::Location& location) {
    ASSERT_EQ(0u, profile_attributes_storage_->GetNumberOfProfiles())
        << location.ToString();
    ASSERT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_))
        << location.ToString();
    ProfileAttributesInitParams params;
    params.profile_path = profile_1_path_;
    params.profile_name = profile_1_name_;
    profile_attributes_storage_->AddProfile(std::move(params));
    // Create a non profile shortcut for Chrome.
    base::ScopedBlockingCall scoped_blocking_call(
        FROM_HERE, base::BlockingType::MAY_BLOCK);

    base::FilePath chrome_exe;
    if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
      NOTREACHED();
    }

    ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
    ShellUtil::AddDefaultShortcutProperties(chrome_exe, &properties);

    properties.set_app_id(
        shell_integration::win::GetAppUserModelIdForBrowser(profile_1_path_));

    const base::FilePath shortcut_path(
        profiles::internal::GetShortcutFilenameForProfile(std::u16string()));

    const base::FilePath new_shortcut_name =
        shortcut_path.BaseName().RemoveExtension();
    properties.set_shortcut_name(new_shortcut_name.value());
    ShellUtil::CreateOrUpdateShortcut(
        ShellUtil::SHORTCUT_LOCATION_DESKTOP, properties,
        ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
    task_environment_.RunUntilIdle();
    // Verify that there's now an unbadged, unmamed shortcut for the single
    // profile.
    ValidateNonProfileShortcut(location);
  }

  void SetupAndCreateTwoShortcuts(const base::Location& location) {
    SetupDefaultProfileShortcut(location);
    CreateProfileWithShortcut(location, profile_2_name_, profile_2_path_);
    ValidateProfileShortcut(location, profile_1_name_, profile_1_path_);
  }

  void SetupTwoProfilesAndNonProfileShortcut(const base::Location& location) {
    SetupNonProfileShortcut(location);
    CreateProfileWithShortcut(location, profile_2_name_, profile_2_path_);
  }

  // Returns the default shortcut path for this profile.
  base::FilePath GetDefaultShortcutPathForProfile(
      const std::u16string& profile_name) {
    return GetUserShortcutsDirectory().Append(
        profiles::internal::GetShortcutFilenameForProfile(profile_name));
  }

  // Returns true if the shortcut for this profile exists.
  bool ProfileShortcutExistsAtDefaultPath(const std::u16string& profile_name) {
    return base::PathExists(
        GetDefaultShortcutPathForProfile(profile_name));
  }

  // Posts a task to call base::win::ValidateShortcut on the COM thread.
  void PostValidateShortcut(
      const base::Location& location,
      const base::FilePath& shortcut_path,
      const base::win::ShortcutProperties& expected_properties) {
    base::ThreadPool::CreateCOMSTATaskRunner({})->PostTask(
        location, base::BindOnce(&base::win::ValidateShortcut, shortcut_path,
                                 expected_properties));
    task_environment_.RunUntilIdle();
  }

  // Calls base::win::ValidateShortcut() with expected properties for the
  // shortcut at |shortcut_path| for the profile at |profile_path|.
  void ValidateProfileShortcutAtPath(const base::Location& location,
                                     const base::FilePath& shortcut_path,
                                     const base::FilePath& profile_path) {
    EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();

    // Ensure that the corresponding icon exists.
    const base::FilePath icon_path =
        profiles::internal::GetProfileIconPath(profile_path);
    EXPECT_TRUE(base::PathExists(icon_path)) << location.ToString();

    base::win::ShortcutProperties expected_properties;
    expected_properties.set_app_id(
        shell_integration::win::GetAppUserModelIdForBrowser(profile_path));
    expected_properties.set_target(GetExePath());
    expected_properties.set_description(InstallUtil::GetAppDescription());
    expected_properties.set_arguments(
        profiles::internal::CreateProfileShortcutFlags(profile_path));
    expected_properties.set_icon(icon_path, 0);
    PostValidateShortcut(location, shortcut_path, expected_properties);
  }

  // Calls base::win::ValidateShortcut() with expected properties for
  // |profile_name|'s shortcut.
  void ValidateProfileShortcut(const base::Location& location,
                               const std::u16string& profile_name,
                               const base::FilePath& profile_path) {
    ValidateProfileShortcutAtPath(
        location, GetDefaultShortcutPathForProfile(profile_name), profile_path);
  }

  void ValidateSingleProfileShortcutAtPath(
      const base::Location& location,
      const base::FilePath& profile_path,
      const base::FilePath& shortcut_path) {
    EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();

    base::win::ShortcutProperties expected_properties;
    expected_properties.set_target(GetExePath());
    expected_properties.set_arguments(
        profiles::internal::CreateProfileShortcutFlags(profile_path));
    expected_properties.set_icon(GetExePath(), 0);
    expected_properties.set_description(InstallUtil::GetAppDescription());
    PostValidateShortcut(location, shortcut_path, expected_properties);
  }

  void ValidateSingleProfileShortcut(const base::Location& location,
                                     const base::FilePath& profile_path) {
    const base::FilePath shortcut_path =
        GetDefaultShortcutPathForProfile(std::u16string());
    ValidateSingleProfileShortcutAtPath(location, profile_path, shortcut_path);
  }

  void ValidateNonProfileShortcut(const base::Location& location) {
    const base::FilePath shortcut_path =
        GetDefaultShortcutPathForProfile(std::u16string());
    EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();

    base::win::ShortcutProperties expected_properties;
    expected_properties.set_target(GetExePath());
    expected_properties.set_arguments(std::wstring());
    expected_properties.set_icon(GetExePath(), 0);
    expected_properties.set_description(InstallUtil::GetAppDescription());
    PostValidateShortcut(location, shortcut_path, expected_properties);
  }

  void CreateProfileWithShortcut(const base::Location& location,
                                 const std::u16string& profile_name,
                                 const base::FilePath& profile_path) {
    ASSERT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_name))
        << location.ToString();
    ProfileAttributesInitParams params;
    params.profile_path = profile_path;
    params.profile_name = profile_name;
    profile_attributes_storage_->AddProfile(std::move(params));
    profile_shortcut_manager_->CreateProfileShortcut(profile_path);
    task_environment_.RunUntilIdle();
    ValidateProfileShortcut(location, profile_name, profile_path);
  }

  // Posts a task to call ShellUtil::CreateOrUpdateShortcut on the COM thread.
  void PostCreateOrUpdateShortcut(
      const base::Location& location,
      ShellUtil::ShortcutLocation shortcut_location,
      const ShellUtil::ShortcutProperties& properties) {
    base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()})
        ->PostTaskAndReplyWithResult(
            location,
            base::BindOnce(&ShellUtil::CreateOrUpdateShortcut,
                           shortcut_location, properties,
                           ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS,
                           /*pinned=*/nullptr),
            base::BindOnce([](bool succeeded) { EXPECT_TRUE(succeeded); }));
    task_environment_.RunUntilIdle();
  }

  // Creates a regular (non-profile) desktop shortcut with the given name and
  // returns its path. Fails the test if an error occurs.
  base::FilePath CreateRegularShortcutWithName(
      const base::Location& location,
      const std::wstring& shortcut_name) {
    const base::FilePath shortcut_path =
        GetUserShortcutsDirectory().Append(shortcut_name + installer::kLnkExt);
    EXPECT_FALSE(base::PathExists(shortcut_path)) << location.ToString();

    ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
    ShellUtil::AddDefaultShortcutProperties(GetExePath(), &properties);
    properties.set_shortcut_name(shortcut_name);
    PostCreateOrUpdateShortcut(location, ShellUtil::SHORTCUT_LOCATION_DESKTOP,
                               properties);
    EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();

    return shortcut_path;
  }

  base::FilePath CreateRegularSystemLevelShortcut(
      const base::Location& location) {
    ShellUtil::ShortcutProperties properties(ShellUtil::SYSTEM_LEVEL);
    ShellUtil::AddDefaultShortcutProperties(GetExePath(), &properties);
    PostCreateOrUpdateShortcut(location, ShellUtil::SHORTCUT_LOCATION_DESKTOP,
                               properties);
    const base::FilePath system_level_shortcut_path =
        GetSystemShortcutsDirectory().Append(InstallUtil::GetShortcutName() +
                                             installer::kLnkExt);
    EXPECT_TRUE(base::PathExists(system_level_shortcut_path))
        << location.ToString();
    return system_level_shortcut_path;
  }

  void RenameProfile(const base::Location& location,
                     const base::FilePath& profile_path,
                     const std::u16string& new_profile_name) {
    ProfileAttributesEntry* entry =
        profile_attributes_storage_->GetProfileAttributesWithPath(profile_path);
    ASSERT_NE(entry, nullptr);
    ASSERT_NE(entry->GetLocalProfileName(), new_profile_name);
    entry->SetLocalProfileName(new_profile_name, /*is_default_name=*/false);
    task_environment_.RunUntilIdle();
  }

  base::FilePath GetExePath() {
    base::FilePath exe_path;
    EXPECT_TRUE(base::PathService::Get(base::FILE_EXE, &exe_path));
    return exe_path;
  }

  base::FilePath GetUserShortcutsDirectory() {
    base::FilePath user_shortcuts_directory;
    EXPECT_TRUE(ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
                                           ShellUtil::CURRENT_USER,
                                           &user_shortcuts_directory));
    return user_shortcuts_directory;
  }

  base::FilePath GetSystemShortcutsDirectory() {
    base::FilePath system_shortcuts_directory;
    EXPECT_TRUE(ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
                                           ShellUtil::SYSTEM_LEVEL,
                                           &system_shortcuts_directory));
    return system_shortcuts_directory;
  }

  content::BrowserTaskEnvironment task_environment_;

  std::unique_ptr<TestingProfileManager> profile_manager_;
  std::unique_ptr<ProfileShortcutManager> profile_shortcut_manager_;
  raw_ptr<ProfileAttributesStorage> profile_attributes_storage_;
  base::ScopedPathOverride fake_user_desktop_;
  base::ScopedPathOverride fake_system_desktop_;
  std::u16string profile_1_name_;
  base::FilePath profile_1_path_;
  std::u16string profile_2_name_;
  base::FilePath profile_2_path_;
  std::u16string profile_3_name_;
  base::FilePath profile_3_path_;
};

TEST_F(ProfileShortcutManagerTest, ShortcutFilename) {
  const std::u16string kProfileName = u"Harry";
  const std::wstring expected_name =
      base::AsWString(kProfileName) + L" - " +
      base::AsWString(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)) +
      installer::kLnkExt;
  EXPECT_EQ(expected_name,
            profiles::internal::GetShortcutFilenameForProfile(kProfileName));
}

TEST_F(ProfileShortcutManagerTest, ShortcutLongFilenameIsTrimmed) {
  const std::u16string kLongProfileName =
      u"Harry Harry Harry Harry Harry Harry Harry Harry Harry Harry Harry "
      u"Harry Harry Harry Harry Harry Harry Harry Harry Harry Harry Harry "
      u"Harry Harry Harry Harry Harry Harry Harry";
  const std::wstring file_name =
      profiles::internal::GetShortcutFilenameForProfile(kLongProfileName);
  EXPECT_LT(file_name.size(), kLongProfileName.size());
}

TEST_F(ProfileShortcutManagerTest, ShortcutFilenameStripsReservedCharacters) {
  const std::u16string kProfileName = u"<Harry/>";
  const std::wstring kSanitizedProfileName = L"Harry";
  const std::wstring expected_name =
      kSanitizedProfileName + L" - " +
      base::AsWString(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)) +
      installer::kLnkExt;
  EXPECT_EQ(expected_name,
            profiles::internal::GetShortcutFilenameForProfile(kProfileName));
}

TEST_F(ProfileShortcutManagerTest, UnbadgedShortcutFilename) {
  EXPECT_EQ(
      InstallUtil::GetShortcutName() + installer::kLnkExt,
      profiles::internal::GetShortcutFilenameForProfile(std::u16string()));
}

TEST_F(ProfileShortcutManagerTest, ShortcutFlags) {
  const std::wstring kProfileName = L"MyProfileX";
  const base::FilePath profile_path =
      profile_manager_->profiles_dir().Append(kProfileName);
  EXPECT_EQ(L"--profile-directory=\"" + kProfileName + L"\"",
            profiles::internal::CreateProfileShortcutFlags(profile_path));
}

TEST_F(ProfileShortcutManagerTest, DesktopShortcutsCreate) {
  SetupDefaultProfileShortcut(FROM_HERE);
  // Validation is done by |ValidateProfileShortcutAtPath()| which is called
  // by |CreateProfileWithShortcut()|.
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
}

TEST_F(ProfileShortcutManagerTest, DesktopShortcutsUpdate) {
  SetupDefaultProfileShortcut(FROM_HERE);
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);

  // Cause an update in ProfileShortcutManager by modifying the profile info
  // cache.
  const std::u16string new_profile_2_name = u"New Profile Name";
  RenameProfile(FROM_HERE, profile_2_path_, new_profile_2_name);
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));
  ValidateProfileShortcut(FROM_HERE, new_profile_2_name, profile_2_path_);
}

TEST_F(ProfileShortcutManagerTest, CreateSecondProfileBadgesFirstShortcut) {
  SetupDefaultProfileShortcut(FROM_HERE);
  // Assert that a shortcut without a profile name exists.
  ASSERT_TRUE(ProfileShortcutExistsAtDefaultPath(std::u16string()));

  // Create a second profile without a shortcut.
  ProfileAttributesInitParams params;
  params.profile_path = profile_2_path_;
  params.profile_name = profile_2_name_;
  profile_attributes_storage_->AddProfile(std::move(params));
  task_environment_.RunUntilIdle();

  // Ensure that the second profile doesn't have a shortcut and that the first
  // profile's shortcut got renamed and badged.
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(std::u16string()));
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
}

// Test that adding a second profile, when the first profile has a non-profile
// shortcut, updates the non-profile shortcut to be a badged profile shortcut.
// This happens if the user has one profile, created with a version of Chrome
// that creates non-profile shortcuts, upgrades to a newer version of Chrome
// that creates default profile shortcuts, and adds a second profile.
TEST_F(ProfileShortcutManagerTest, NonProfileShortcutUpgrade) {
  SetupTwoProfilesAndNonProfileShortcut(FROM_HERE);
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
}

TEST_F(ProfileShortcutManagerTest, DesktopShortcutsDeleteSecondToLast) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  // Delete one shortcut.
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));

  // Verify that the remaining shortcut points at the remaining profile.
  ValidateSingleProfileShortcut(FROM_HERE, profile_1_path_);
}

TEST_F(ProfileShortcutManagerTest, DeleteSecondToLastProfileWithoutShortcut) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath profile_1_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_1_name_);
  const base::FilePath profile_2_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_2_name_);

  // Delete the shortcut for the first profile, but keep the one for the 2nd.
  ASSERT_TRUE(base::DeleteFile(profile_1_shortcut_path));
  ASSERT_FALSE(base::PathExists(profile_1_shortcut_path));
  ASSERT_TRUE(base::PathExists(profile_2_shortcut_path));

  // Delete the profile that doesn't have a shortcut.
  profile_attributes_storage_->RemoveProfile(profile_1_path_);
  task_environment_.RunUntilIdle();

  // Verify that the remaining shortcut does not have a profile name.
  ValidateSingleProfileShortcut(FROM_HERE, profile_2_path_);
  // Verify that shortcuts with profile names do not exist.
  EXPECT_FALSE(base::PathExists(profile_1_shortcut_path));
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path));
}

TEST_F(ProfileShortcutManagerTest, DeleteSecondToLastProfileWithShortcut) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath profile_1_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_1_name_);
  const base::FilePath profile_2_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_2_name_);

  // Delete the shortcut for the first profile, but keep the one for the 2nd.
  ASSERT_TRUE(base::DeleteFile(profile_1_shortcut_path));
  ASSERT_FALSE(base::PathExists(profile_1_shortcut_path));
  ASSERT_TRUE(base::PathExists(profile_2_shortcut_path));

  // Delete the profile that has a shortcut.
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();

  // Verify that the remaining shortcut is a single profile shortcut.
  ValidateSingleProfileShortcut(FROM_HERE, profile_1_path_);
  // Verify that shortcuts with profile names do not exist.
  EXPECT_FALSE(base::PathExists(profile_1_shortcut_path));
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path));
}

TEST_F(ProfileShortcutManagerTest, DeleteOnlyProfileWithShortcuts) {
  SetupAndCreateTwoShortcuts(FROM_HERE);
  CreateProfileWithShortcut(FROM_HERE, profile_3_name_, profile_3_path_);

  const base::FilePath non_profile_shortcut_path =
      GetDefaultShortcutPathForProfile(std::u16string());
  const base::FilePath profile_1_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_1_name_);
  const base::FilePath profile_2_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_2_name_);
  const base::FilePath profile_3_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_3_name_);

  // Delete shortcuts for the first two profiles.
  ASSERT_TRUE(base::DeleteFile(profile_1_shortcut_path));
  ASSERT_TRUE(base::DeleteFile(profile_2_shortcut_path));

  // Only the shortcut to the third profile should exist.
  ASSERT_FALSE(base::PathExists(profile_1_shortcut_path));
  ASSERT_FALSE(base::PathExists(profile_2_shortcut_path));
  ASSERT_FALSE(base::PathExists(non_profile_shortcut_path));
  ASSERT_TRUE(base::PathExists(profile_3_shortcut_path));

  // Delete the third profile and check that its shortcut is gone and no
  // shortcuts have been re-created.
  profile_attributes_storage_->RemoveProfile(profile_3_path_);
  task_environment_.RunUntilIdle();
  ASSERT_FALSE(base::PathExists(profile_1_shortcut_path));
  ASSERT_FALSE(base::PathExists(profile_2_shortcut_path));
  ASSERT_FALSE(base::PathExists(profile_3_shortcut_path));
  ASSERT_FALSE(base::PathExists(non_profile_shortcut_path));
}

TEST_F(ProfileShortcutManagerTest, DesktopShortcutsCreateSecond) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  // Delete one shortcut.
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();

  // Verify that an unbadged, default named shortcut for profile1 exists.
  ValidateSingleProfileShortcut(FROM_HERE, profile_1_path_);
  // Verify that an additional shortcut, with the first profile's name does
  // not exist.
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_));

  // Create a second profile and shortcut.
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);

  // Verify that the original shortcut received the profile's name.
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
  // Verify that a default shortcut no longer exists.
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(std::u16string()));
}

TEST_F(ProfileShortcutManagerTest, RenamedDesktopShortcuts) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath profile_2_shortcut_path_1 =
      GetDefaultShortcutPathForProfile(profile_2_name_);
  const base::FilePath profile_2_shortcut_path_2 =
      GetUserShortcutsDirectory().Append(L"MyChrome.lnk");
  ASSERT_TRUE(base::Move(profile_2_shortcut_path_1,
                              profile_2_shortcut_path_2));

  // Ensure that a new shortcut does not get made if the old one was renamed.
  profile_shortcut_manager_->CreateProfileShortcut(profile_2_path_);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_2,
                                profile_2_path_);

  // Delete the renamed shortcut and try to create it again, which should work.
  ASSERT_TRUE(base::DeleteFile(profile_2_shortcut_path_2));
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path_2));
  profile_shortcut_manager_->CreateProfileShortcut(profile_2_path_);
  task_environment_.RunUntilIdle();
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
}

TEST_F(ProfileShortcutManagerTest, RenamedDesktopShortcutsGetDeleted) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath profile_2_shortcut_path_1 =
      GetDefaultShortcutPathForProfile(profile_2_name_);
  const base::FilePath profile_2_shortcut_path_2 =
      GetUserShortcutsDirectory().Append(L"MyChrome.lnk");
  // Make a copy of the shortcut.
  ASSERT_TRUE(base::CopyFile(profile_2_shortcut_path_1,
                                  profile_2_shortcut_path_2));
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_1,
                                profile_2_path_);
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_2,
                                profile_2_path_);

  // Also, copy the shortcut for the first user and ensure it gets preserved.
  const base::FilePath preserved_profile_1_shortcut_path =
      GetUserShortcutsDirectory().Append(L"Preserved.lnk");
  ASSERT_TRUE(base::CopyFile(
      GetDefaultShortcutPathForProfile(profile_1_name_),
      preserved_profile_1_shortcut_path));
  EXPECT_TRUE(base::PathExists(preserved_profile_1_shortcut_path));

  // Delete the profile and ensure both shortcuts were also deleted.
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path_1));
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path_2));
  ValidateSingleProfileShortcutAtPath(FROM_HERE, profile_1_path_,
                                      preserved_profile_1_shortcut_path);
}

TEST_F(ProfileShortcutManagerTest, RenamedDesktopShortcutsAfterProfileRename) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath profile_2_shortcut_path_1 =
      GetDefaultShortcutPathForProfile(profile_2_name_);
  const base::FilePath profile_2_shortcut_path_2 =
      GetUserShortcutsDirectory().Append(L"MyChrome.lnk");
  // Make a copy of the shortcut.
  ASSERT_TRUE(base::CopyFile(profile_2_shortcut_path_1,
                                  profile_2_shortcut_path_2));
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_1,
                                profile_2_path_);
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_2,
                                profile_2_path_);

  // Now, rename the profile.
  const std::u16string new_profile_2_name = u"New profile";
  RenameProfile(FROM_HERE, profile_2_path_, new_profile_2_name);

  // The original shortcut should be renamed but the copied shortcut should
  // keep its name.
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path_1));
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_2,
                                profile_2_path_);
  ValidateProfileShortcut(FROM_HERE, new_profile_2_name, profile_2_path_);
}

TEST_F(ProfileShortcutManagerTest, UpdateShortcutWithNoFlags) {
  SetupDefaultProfileShortcut(FROM_HERE);

  // Delete the shortcut that got created for this profile and instead make
  // a new one without any command-line flags.
  ASSERT_TRUE(
      base::DeleteFile(GetDefaultShortcutPathForProfile(std::u16string())));
  const base::FilePath regular_shortcut_path =
      CreateRegularShortcutWithName(FROM_HERE, InstallUtil::GetShortcutName());

  // Add another profile and check that the shortcut was replaced with
  // a badged shortcut with the right command line for the profile
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  EXPECT_FALSE(base::PathExists(regular_shortcut_path));
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
}

TEST_F(ProfileShortcutManagerTest, UpdateTwoShortcutsWithNoFlags) {
  SetupDefaultProfileShortcut(FROM_HERE);

  // Delete the shortcut that got created for this profile and instead make
  // two new ones without any command-line flags.
  ASSERT_TRUE(
      base::DeleteFile(GetDefaultShortcutPathForProfile(std::u16string())));
  const base::FilePath regular_shortcut_path =
      CreateRegularShortcutWithName(FROM_HERE, InstallUtil::GetShortcutName());
  const base::FilePath customized_regular_shortcut_path =
      CreateRegularShortcutWithName(FROM_HERE, L"MyChrome");

  // Add another profile and check that one shortcut was renamed and that the
  // other shortcut was updated but kept the same name.
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  EXPECT_FALSE(base::PathExists(regular_shortcut_path));
  ValidateProfileShortcutAtPath(FROM_HERE, customized_regular_shortcut_path,
                                profile_1_path_);
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
}

TEST_F(ProfileShortcutManagerTest, RemoveProfileShortcuts) {
  SetupAndCreateTwoShortcuts(FROM_HERE);
  CreateProfileWithShortcut(FROM_HERE, profile_3_name_, profile_3_path_);

  const base::FilePath profile_1_shortcut_path_1 =
      GetDefaultShortcutPathForProfile(profile_1_name_);
  const base::FilePath profile_2_shortcut_path_1 =
      GetDefaultShortcutPathForProfile(profile_2_name_);

  // Make copies of the shortcuts for both profiles.
  const base::FilePath profile_1_shortcut_path_2 =
      GetUserShortcutsDirectory().Append(L"Copied1.lnk");
  const base::FilePath profile_2_shortcut_path_2 =
      GetUserShortcutsDirectory().Append(L"Copied2.lnk");
  ASSERT_TRUE(base::CopyFile(profile_1_shortcut_path_1,
                                  profile_1_shortcut_path_2));
  ASSERT_TRUE(base::CopyFile(profile_2_shortcut_path_1,
                                  profile_2_shortcut_path_2));
  ValidateProfileShortcutAtPath(FROM_HERE, profile_1_shortcut_path_2,
                                profile_1_path_);
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_2,
                                profile_2_path_);

  // Delete shortcuts for profile 1 and ensure that they got deleted while the
  // shortcuts for profile 2 were kept.
  profile_shortcut_manager_->RemoveProfileShortcuts(profile_1_path_);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(base::PathExists(profile_1_shortcut_path_1));
  EXPECT_FALSE(base::PathExists(profile_1_shortcut_path_2));
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_1,
                                profile_2_path_);
  ValidateProfileShortcutAtPath(FROM_HERE, profile_2_shortcut_path_2,
                                profile_2_path_);
}

TEST_F(ProfileShortcutManagerTest, HasProfileShortcuts) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  struct HasShortcutsResult {
    bool has_shortcuts;
    void set_has_shortcuts(bool value) { has_shortcuts = value; }
  } result = { false };

  // Profile 2 should have a shortcut initially.
  profile_shortcut_manager_->HasProfileShortcuts(
      profile_2_path_, base::BindOnce(&HasShortcutsResult::set_has_shortcuts,
                                      base::Unretained(&result)));
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(result.has_shortcuts);

  // Delete the shortcut and check that the function returns false.
  const base::FilePath profile_2_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_2_name_);
  ASSERT_TRUE(base::DeleteFile(profile_2_shortcut_path));
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path));
  profile_shortcut_manager_->HasProfileShortcuts(
      profile_2_path_, base::BindOnce(&HasShortcutsResult::set_has_shortcuts,
                                      base::Unretained(&result)));
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(result.has_shortcuts);
}

TEST_F(ProfileShortcutManagerTest, ProfileShortcutsWithSystemLevelShortcut) {
  const base::FilePath system_level_shortcut_path =
      CreateRegularSystemLevelShortcut(FROM_HERE);

  // Create the initial profile.
  ProfileAttributesInitParams params_1;
  params_1.profile_path = profile_1_path_;
  params_1.profile_name = profile_1_name_;
  profile_attributes_storage_->AddProfile(std::move(params_1));
  task_environment_.RunUntilIdle();
  ASSERT_EQ(1u, profile_attributes_storage_->GetNumberOfProfiles());

  // Ensure system-level continues to exist and user-level was not created.
  EXPECT_TRUE(base::PathExists(system_level_shortcut_path));
  EXPECT_FALSE(
      base::PathExists(GetDefaultShortcutPathForProfile(std::u16string())));

  // Create another profile with a shortcut and ensure both profiles receive
  // user-level profile shortcuts and the system-level one still exists.
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  EXPECT_TRUE(base::PathExists(system_level_shortcut_path));

  // Create a third profile without a shortcut and ensure it doesn't get one.
  ProfileAttributesInitParams params_3;
  params_3.profile_path = profile_3_path_;
  params_3.profile_name = profile_3_name_;
  profile_attributes_storage_->AddProfile(std::move(params_3));
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_3_name_));

  // Ensure that changing the avatar icon and the name does not result in a
  // shortcut being created.
  ProfileAttributesEntry* entry_3 =
      profile_attributes_storage_->GetProfileAttributesWithPath(
          profile_3_path_);
  ASSERT_NE(entry_3, nullptr);
  entry_3->SetAvatarIconIndex(3u);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_3_name_));

  const std::u16string new_profile_3_name = u"New Name 3";
  entry_3->SetLocalProfileName(new_profile_3_name, /*is_default_name=*/false);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_3_name_));
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(new_profile_3_name));

  // Rename the second profile and ensure its shortcut got renamed.
  const std::u16string new_profile_2_name = u"New Name 2";
  ProfileAttributesEntry* entry_2 =
      profile_attributes_storage_->GetProfileAttributesWithPath(
          profile_2_path_);
  ASSERT_NE(entry_2, nullptr);
  entry_2->SetLocalProfileName(new_profile_2_name, /*is_default_name=*/false);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));
  ValidateProfileShortcut(FROM_HERE, new_profile_2_name, profile_2_path_);
}

TEST_F(ProfileShortcutManagerTest,
       DeleteSecondToLastProfileWithSystemLevelShortcut) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath system_level_shortcut_path =
      CreateRegularSystemLevelShortcut(FROM_HERE);

  // Delete a profile and verify that only the system-level shortcut still
  // exists.
  profile_attributes_storage_->RemoveProfile(profile_1_path_);
  task_environment_.RunUntilIdle();

  EXPECT_TRUE(base::PathExists(system_level_shortcut_path));
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(std::u16string()));
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_));
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));
}

TEST_F(ProfileShortcutManagerTest,
       DeleteSecondToLastProfileWithShortcutWhenSystemLevelShortcutExists) {
  SetupAndCreateTwoShortcuts(FROM_HERE);

  const base::FilePath profile_1_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_1_name_);
  const base::FilePath profile_2_shortcut_path =
      GetDefaultShortcutPathForProfile(profile_2_name_);

  // Delete the shortcut for the first profile, but keep the one for the 2nd.
  ASSERT_TRUE(base::DeleteFile(profile_1_shortcut_path));
  ASSERT_FALSE(base::PathExists(profile_1_shortcut_path));
  ASSERT_TRUE(base::PathExists(profile_2_shortcut_path));

  const base::FilePath system_level_shortcut_path =
      CreateRegularSystemLevelShortcut(FROM_HERE);

  // Delete the profile that has a shortcut, which will exercise the non-profile
  // shortcut creation path in |DeleteDesktopShortcuts()|, which is
  // not covered by the |DeleteSecondToLastProfileWithSystemLevelShortcut| test.
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();

  // Verify that only the system-level shortcut still exists.
  EXPECT_TRUE(base::PathExists(system_level_shortcut_path));
  EXPECT_FALSE(
      base::PathExists(GetDefaultShortcutPathForProfile(std::u16string())));
  EXPECT_FALSE(base::PathExists(profile_1_shortcut_path));
  EXPECT_FALSE(base::PathExists(profile_2_shortcut_path));
}

TEST_F(ProfileShortcutManagerTest, CreateProfileIcon) {
  SetupDefaultProfileShortcut(FROM_HERE);

  const base::FilePath icon_path =
      profiles::internal::GetProfileIconPath(profile_1_path_);

  EXPECT_TRUE(base::PathExists(icon_path));
  EXPECT_TRUE(base::DeleteFile(icon_path));
  EXPECT_FALSE(base::PathExists(icon_path));

  profile_shortcut_manager_->CreateOrUpdateProfileIcon(profile_1_path_);
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(base::PathExists(icon_path));
}

TEST_F(ProfileShortcutManagerTest, UnbadgeProfileIconOnDeletion) {
  SetupDefaultProfileShortcut(FROM_HERE);
  const base::FilePath icon_path_1 =
      profiles::internal::GetProfileIconPath(profile_1_path_);
  const base::FilePath icon_path_2 =
      profiles::internal::GetProfileIconPath(profile_2_path_);

  // Default profile has unbadged icon to start.
  std::string unbadged_icon_1;
  EXPECT_TRUE(base::ReadFileToString(icon_path_1, &unbadged_icon_1));

  // Creating a new profile adds a badge to both the new profile icon and the
  // default profile icon. Since they use the same icon index, the icon files
  // should be the same.
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);

  std::string badged_icon_1;
  EXPECT_TRUE(base::ReadFileToString(icon_path_1, &badged_icon_1));
  std::string badged_icon_2;
  EXPECT_TRUE(base::ReadFileToString(icon_path_2, &badged_icon_2));

  EXPECT_NE(badged_icon_1, unbadged_icon_1);
  EXPECT_EQ(badged_icon_1, badged_icon_2);

  // Deleting the default profile will unbadge the new profile's icon and should
  // result in an icon that is identical to the unbadged default profile icon.
  profile_attributes_storage_->RemoveProfile(profile_1_path_);
  task_environment_.RunUntilIdle();

  std::string unbadged_icon_2;
  EXPECT_TRUE(base::ReadFileToString(icon_path_2, &unbadged_icon_2));
  EXPECT_EQ(unbadged_icon_1, unbadged_icon_2);
}

TEST_F(ProfileShortcutManagerTest, ProfileIconOnAvatarChange) {
  SetupAndCreateTwoShortcuts(FROM_HERE);
  const base::FilePath icon_path_1 =
      profiles::internal::GetProfileIconPath(profile_1_path_);
  const base::FilePath icon_path_2 =
      profiles::internal::GetProfileIconPath(profile_2_path_);

  std::string badged_icon_1;
  EXPECT_TRUE(base::ReadFileToString(icon_path_1, &badged_icon_1));
  std::string badged_icon_2;
  EXPECT_TRUE(base::ReadFileToString(icon_path_2, &badged_icon_2));

  // Profile 1 and 2 are created with the same icon.
  EXPECT_EQ(badged_icon_1, badged_icon_2);

  // Change profile 1's icon.
  ProfileAttributesEntry* entry_1 =
      profile_attributes_storage_->GetProfileAttributesWithPath(
          profile_1_path_);
  ASSERT_NE(entry_1, nullptr);
  entry_1->SetAvatarIconIndex(1u);
  task_environment_.RunUntilIdle();

  std::string new_badged_icon_1;
  EXPECT_TRUE(base::ReadFileToString(icon_path_1, &new_badged_icon_1));
  EXPECT_NE(new_badged_icon_1, badged_icon_1);

  // Ensure the new icon is not the unbadged icon.
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();

  std::string unbadged_icon_1;
  EXPECT_TRUE(base::ReadFileToString(icon_path_1, &unbadged_icon_1));
  EXPECT_NE(unbadged_icon_1, new_badged_icon_1);

  // Ensure the icon doesn't change on avatar change without 2 profiles.
  entry_1->SetAvatarIconIndex(1u);
  task_environment_.RunUntilIdle();

  std::string unbadged_icon_1_a;
  EXPECT_TRUE(base::ReadFileToString(icon_path_1, &unbadged_icon_1_a));
  EXPECT_EQ(unbadged_icon_1, unbadged_icon_1_a);
}

TEST_F(ProfileShortcutManagerTest, ShortcutFilenameUniquified) {
  const std::wstring suffix =
      base::AsWString(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
  std::set<base::FilePath> excludes;

  std::wstring shortcut_filename =
      profiles::internal::GetUniqueShortcutFilenameForProfile(u"Carrie",
                                                              excludes);
  EXPECT_EQ(
      L"Carrie - " + suffix + installer::kLnkExt, shortcut_filename);
  excludes.insert(GetUserShortcutsDirectory().Append(shortcut_filename));

  shortcut_filename = profiles::internal::GetUniqueShortcutFilenameForProfile(
      u"Carrie", excludes);
  EXPECT_EQ(
      L"Carrie - " + suffix + L" (1)" + installer::kLnkExt, shortcut_filename);
  excludes.insert(GetUserShortcutsDirectory().Append(shortcut_filename));

  shortcut_filename = profiles::internal::GetUniqueShortcutFilenameForProfile(
      u"Carrie", excludes);
  EXPECT_EQ(
      L"Carrie - " + suffix + L" (2)" + installer::kLnkExt, shortcut_filename);
  excludes.insert(GetUserShortcutsDirectory().Append(shortcut_filename));

  shortcut_filename = profiles::internal::GetUniqueShortcutFilenameForProfile(
      u"Steven", excludes);
  EXPECT_EQ(
      L"Steven - " + suffix + installer::kLnkExt, shortcut_filename);
  excludes.insert(GetUserShortcutsDirectory().Append(shortcut_filename));

  shortcut_filename = profiles::internal::GetUniqueShortcutFilenameForProfile(
      u"Steven", excludes);
  EXPECT_EQ(
      L"Steven - " + suffix + L" (1)" + installer::kLnkExt, shortcut_filename);
  excludes.insert(GetUserShortcutsDirectory().Append(shortcut_filename));

  shortcut_filename = profiles::internal::GetUniqueShortcutFilenameForProfile(
      u"Carrie", excludes);
  EXPECT_EQ(
      L"Carrie - " + suffix + L" (3)" + installer::kLnkExt, shortcut_filename);
  excludes.insert(GetUserShortcutsDirectory().Append(shortcut_filename));

  excludes.erase(
      GetUserShortcutsDirectory().Append(
          L"Carrie - " + suffix + installer::kLnkExt));
  shortcut_filename = profiles::internal::GetUniqueShortcutFilenameForProfile(
      u"Carrie", excludes);
  EXPECT_EQ(
      L"Carrie - " + suffix + installer::kLnkExt, shortcut_filename);
}

TEST_F(ProfileShortcutManagerTest, ShortcutFilenameMatcher) {
  profiles::internal::ShortcutFilenameMatcher matcher(u"Carrie");
  const std::wstring suffix =
      base::AsWString(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));

  EXPECT_TRUE(matcher.IsCanonical(L"Carrie - " + suffix + L" (2)" +
                                  installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(L"Carrie - " + suffix + L"(2)" +
                                   installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(L"Carrie - " + suffix + L" 2" +
                                   installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(L"Carrie - " + suffix + L"2" +
                                   installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(L"Carrie - " + suffix + L" - 2" +
                                   installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(L"Carrie - " + suffix + L" (a)" +
                                   installer::kLnkExt));
  EXPECT_TRUE(matcher.IsCanonical(L"Carrie - " + suffix + L" (11)" +
                                  installer::kLnkExt));
  EXPECT_TRUE(matcher.IsCanonical(L"Carrie - " + suffix + L" (03)" +
                                  installer::kLnkExt));
  EXPECT_TRUE(matcher.IsCanonical(L"Carrie - " + suffix + L" (999)" +
                                  installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(L"Carrie - " + suffix + L" (999).lin"));
  EXPECT_FALSE(matcher.IsCanonical(L"ABC Carrie - " + suffix + L" DEF" +
                                   installer::kLnkExt));
  EXPECT_FALSE(matcher.IsCanonical(std::wstring(L"ABC Carrie DEF") +
                                   installer::kLnkExt));
  EXPECT_FALSE(
      matcher.IsCanonical(std::wstring(L"Carrie") + installer::kLnkExt));
}

TEST_F(ProfileShortcutManagerTest, ShortcutsForProfilesWithIdenticalNames) {
  SetupDefaultProfileShortcut(FROM_HERE);

  // Create new profile - profile2.
  CreateProfileWithShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  // Check that nothing is changed for profile1.
  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);

  // Give to profile1 the same name as profile2.
  std::u16string new_profile_1_name = profile_2_name_;
  RenameProfile(FROM_HERE, profile_1_path_, new_profile_1_name);
  const std::wstring profile_1_shortcut_name =
      base::AsWString(new_profile_1_name) + L" - " +
      base::AsWString(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)) +
      L" (1)";
  const auto profile_1_shortcut_path = GetUserShortcutsDirectory()
      .Append(profile_1_shortcut_name + installer::kLnkExt);
  ValidateProfileShortcutAtPath(FROM_HERE,
                                profile_1_shortcut_path,
                                profile_1_path_);
  // Check that nothing is changed for profile2.
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);

  // Create new profile - profile3.
  CreateProfileWithShortcut(FROM_HERE, profile_3_name_, profile_3_path_);
  // Check that nothing is changed for profile1 and profile2.
  ValidateProfileShortcutAtPath(FROM_HERE,
                                profile_1_shortcut_path,
                                profile_1_path_);
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);

  // Give to profile3 the same name as profile2.
  const std::u16string new_profile_3_name = profile_2_name_;
  RenameProfile(FROM_HERE, profile_3_path_, new_profile_3_name);
  const std::wstring profile_3_shortcut_name =
      base::AsWString(new_profile_3_name) + L" - " +
      base::AsWString(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)) +
      L" (2)";
  const auto profile_3_shortcut_path = GetUserShortcutsDirectory()
      .Append(profile_3_shortcut_name + installer::kLnkExt);
  ValidateProfileShortcutAtPath(FROM_HERE,
                                profile_3_shortcut_path,
                                profile_3_path_);
  // Check that nothing is changed for profile1 and profile2.
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  ValidateProfileShortcutAtPath(FROM_HERE,
                                profile_1_shortcut_path,
                                profile_1_path_);

  // Rename profile1 again.
  new_profile_1_name = u"Carrie";
  RenameProfile(FROM_HERE, profile_1_path_, new_profile_1_name);
  ValidateProfileShortcut(FROM_HERE, new_profile_1_name, profile_1_path_);
  // Check that nothing is changed for profile2 and profile3.
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  ValidateProfileShortcutAtPath(FROM_HERE,
                                profile_3_shortcut_path,
                                profile_3_path_);

  // Delete profile1.
  profile_attributes_storage_->RemoveProfile(profile_1_path_);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(new_profile_1_name));
  // Check that nothing is changed for profile2 and profile3.
  ValidateProfileShortcut(FROM_HERE, profile_2_name_, profile_2_path_);
  ValidateProfileShortcutAtPath(FROM_HERE,
                                profile_3_shortcut_path,
                                profile_3_path_);

  // Delete profile2.
  EXPECT_TRUE(base::PathExists(
      GetDefaultShortcutPathForProfile(profile_2_name_)));
  EXPECT_TRUE(base::PathExists(profile_3_shortcut_path));
  profile_attributes_storage_->RemoveProfile(profile_2_path_);
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(base::PathExists(
      GetDefaultShortcutPathForProfile(profile_2_name_)));
  // Only profile3 exists. There should be a single profile shortcut only.
  EXPECT_FALSE(base::PathExists(profile_3_shortcut_path));
  ValidateSingleProfileShortcut(FROM_HERE, profile_3_path_);
}

TEST_F(ProfileShortcutManagerTest, GetPinnedShortcutsForProfile) {
  // Create shortcuts in DIR_TASKBAR_PINS and sub-dirs of
  // DIR_IMPLICIT_APP_SHORTCUTS for the desired profile, and one for a different
  // profile, and check that GetPinnedShortcutsForProfile returns the ones for
  // the desired profile.
  base::FilePath chrome_exe;
  std::set<base::FilePath> expected_files;
  ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &chrome_exe));
  base::ScopedPathOverride override_taskbar_pin{base::DIR_TASKBAR_PINS};
  base::ScopedPathOverride override_implicit_apps{
      base::DIR_IMPLICIT_APP_SHORTCUTS};
  base::FilePath taskbar_pinned;
  base::FilePath implicit_apps;
  ASSERT_TRUE(base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pinned));
  ASSERT_TRUE(
      base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS, &implicit_apps));
  base::win::ShortcutProperties properties;
  std::wstring command_line =
      profiles::internal::CreateProfileShortcutFlags(profile_1_path_);
  properties.set_arguments(command_line);
  properties.set_target(chrome_exe);
  base::FilePath shortcut_path =
      taskbar_pinned.Append(L"Shortcut 1").AddExtension(installer::kLnkExt);
  ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
      shortcut_path, properties, base::win::ShortcutOperation::kCreateAlways));
  expected_files.insert(shortcut_path);

  base::FilePath implicit_apps_subdir;

  // Create a subdirectory of implicit apps dir and create a shortcut with the
  // desired profile.
  base::CreateTemporaryDirInDir(implicit_apps, L"pre", &implicit_apps_subdir);
  shortcut_path =
      taskbar_pinned.Append(L"Shortcut 2").AddExtension(installer::kLnkExt);
  ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
      shortcut_path, properties, base::win::ShortcutOperation::kCreateAlways));
  expected_files.insert(shortcut_path);

  // Create a shortcut using a different profile in the taskbar pinned dir.
  // This should not be returned by GetPinnedShortCutsForProfile.
  command_line =
      profiles::internal::CreateProfileShortcutFlags(profile_2_path_);
  properties.set_arguments(command_line);
  shortcut_path =
      taskbar_pinned.Append(L"Shortcut 3").AddExtension(installer::kLnkExt);
  ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
      shortcut_path, properties, base::win::ShortcutOperation::kCreateAlways));

  std::vector<base::FilePath> pinned_shortcuts =
      profiles::internal::GetPinnedShortCutsForProfile(profile_1_path_);
  EXPECT_EQ(pinned_shortcuts.size(), 2U);
  EXPECT_TRUE(expected_files.find(pinned_shortcuts[0]) != expected_files.end());
  EXPECT_TRUE(expected_files.find(pinned_shortcuts[1]) != expected_files.end());
}
