winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

PredefinedInstalledSource.cpp (16639B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "TestCommon.h"
      5 #include <ISource.h>
      6 #include <AppInstallerRuntime.h>
      7 #include <AppInstallerStrings.h>
      8 #include <Microsoft/PredefinedInstalledSourceFactory.h>
      9 #include <Microsoft/ARPHelper.h>
     10 #include <Microsoft/SQLiteIndexSource.h>
     11 
     12 using namespace std::string_literals;
     13 using namespace std::string_view_literals;
     14 using namespace TestCommon;
     15 using namespace AppInstaller;
     16 using namespace AppInstaller::Manifest;
     17 using namespace AppInstaller::Repository;
     18 using namespace AppInstaller::Runtime;
     19 using namespace AppInstaller::Utility;
     20 
     21 using SQLiteIndex = AppInstaller::Repository::Microsoft::SQLiteIndex;
     22 using SQLiteIndexSource = AppInstaller::Repository::Microsoft::SQLiteIndexSource;
     23 using Factory = AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory;
     24 using ARPHelper = AppInstaller::Repository::Microsoft::ARPHelper;
     25 
     26 constexpr std::string_view s_TestScope = "TestScope"sv;
     27 
     28 struct ARPEntry
     29 {
     30     ARPEntry(std::string entryName) : EntryName(std::move(entryName)) {}
     31     ARPEntry(std::string entryName, std::optional<std::string> displayName, std::optional<std::string> displayVersion, bool systemComponent = false) :
     32         EntryName(std::move(entryName)), DisplayName(std::move(displayName)), DisplayVersion(std::move(displayVersion)), SystemComponent(systemComponent) {}
     33 
     34     std::string EntryName;
     35     std::optional<std::string> DisplayName;
     36     std::optional<std::string> DisplayVersion;
     37     std::optional<std::string> Publisher;
     38     std::optional<std::string> InstallLocation;
     39     std::optional<std::string> UninstallString;
     40     std::optional<std::string> QuietUninstallString;
     41     std::optional<bool> WindowsInstaller;
     42     std::optional<bool> SystemComponent;
     43 };
     44 
     45 void AddARPValueToKey(HKEY key, const std::wstring& name, const std::optional<std::string>& value)
     46 {
     47     if (value)
     48     {
     49         SetRegistryValue(key, name, ConvertToUTF16(value.value()));
     50     }
     51 }
     52 
     53 void AddARPValueToKey(HKEY key, const std::wstring& name, const std::optional<bool>& value)
     54 {
     55     if (value)
     56     {
     57         SetRegistryValue(key, name, (value.value() ? 1 : 0));
     58     }
     59 }
     60 
     61 void AddARPEntryToKey(HKEY key, const ARPHelper& helper, const ARPEntry& entry)
     62 {
     63     auto subkey = RegCreateVolatileSubKey(key, ConvertToUTF16(entry.EntryName));
     64 
     65 #define ADD_ARP_VALUE(_name_) AddARPValueToKey(subkey.get(), helper._name_, entry._name_)
     66     ADD_ARP_VALUE(DisplayName);
     67     ADD_ARP_VALUE(DisplayVersion);
     68     ADD_ARP_VALUE(Publisher);
     69     ADD_ARP_VALUE(InstallLocation);
     70     ADD_ARP_VALUE(UninstallString);
     71     ADD_ARP_VALUE(QuietUninstallString);
     72     ADD_ARP_VALUE(WindowsInstaller);
     73     ADD_ARP_VALUE(SystemComponent);
     74 #undef ADD_ARP_VALUE
     75 }
     76 
     77 void AddARPEntriesToKey(HKEY key, const ARPHelper& helper, const std::vector<ARPEntry>& entries)
     78 {
     79     for (const auto& entry : entries)
     80     {
     81         AddARPEntryToKey(key, helper, entry);
     82     }
     83 }
     84 
     85 SQLiteIndex::MetadataResult::const_iterator Find(const SQLiteIndex::MetadataResult& metadata, PackageVersionMetadata value)
     86 {
     87     return std::find_if(metadata.begin(), metadata.end(), [value](const auto& m) { return m.first == value; });
     88 }
     89 
     90 void VerifyInstalledType(const SQLiteIndex::MetadataResult& metadata, InstallerTypeEnum type)
     91 {
     92     auto itr = Find(metadata, PackageVersionMetadata::InstalledType);
     93     REQUIRE(itr != metadata.end());
     94     REQUIRE(ConvertToInstallerTypeEnum(itr->second) == type);
     95 }
     96 
     97 void VerifyTestScope(const SQLiteIndex::MetadataResult& metadata)
     98 {
     99     auto itr = Find(metadata, PackageVersionMetadata::InstalledScope);
    100     REQUIRE(itr != metadata.end());
    101     REQUIRE(itr->second == s_TestScope);
    102 }
    103 
    104 void VerifyMetadataString(const SQLiteIndex::MetadataResult& metadata, PackageVersionMetadata pvm, const std::optional<std::string>& value)
    105 {
    106     auto itr = Find(metadata, pvm);
    107     if (value)
    108     {
    109         REQUIRE(itr != metadata.end());
    110         REQUIRE(itr->second == value.value());
    111     }
    112     else
    113     {
    114         REQUIRE(itr == metadata.end());
    115     }
    116 }
    117 
    118 void VerifyEntryAgainstIndex(const SQLiteIndex& index, SQLiteIndex::IdType manifestId, const ARPEntry& entry)
    119 {
    120     REQUIRE(index.GetPropertyByPrimaryId(manifestId, PackageVersionProperty::Name) == entry.DisplayName);
    121     REQUIRE(index.GetPropertyByPrimaryId(manifestId, PackageVersionProperty::Version) == entry.DisplayVersion);
    122 
    123     REQUIRE(index.GetMultiPropertyByPrimaryId(manifestId, PackageVersionMultiProperty::PackageFamilyName).empty());
    124     auto productCodes = index.GetMultiPropertyByPrimaryId(manifestId, PackageVersionMultiProperty::ProductCode);
    125     REQUIRE(productCodes.size() == 1);
    126     REQUIRE(productCodes[0] == FoldCase(static_cast<std::string_view>(entry.EntryName)));
    127 
    128     auto metadata = index.GetMetadataByManifestId(manifestId);
    129 
    130     VerifyInstalledType(metadata, entry.WindowsInstaller.value_or(false) ? InstallerTypeEnum::Msi : InstallerTypeEnum::Exe);
    131     VerifyTestScope(metadata);
    132     VerifyMetadataString(metadata, PackageVersionMetadata::Publisher, entry.Publisher);
    133     VerifyMetadataString(metadata, PackageVersionMetadata::InstalledLocation, entry.InstallLocation);
    134     VerifyMetadataString(metadata, PackageVersionMetadata::StandardUninstallCommand, entry.UninstallString);
    135     VerifyMetadataString(metadata, PackageVersionMetadata::SilentUninstallCommand, entry.QuietUninstallString);
    136 }
    137 
    138 std::shared_ptr<ISource> CreatePredefinedInstalledSource(Factory::Filter filter = Factory::Filter::None)
    139 {
    140     SourceDetails details;
    141     details.Type = Factory::Type();
    142     details.Arg = Factory::FilterToString(filter);
    143 
    144     TestProgress progress;
    145 
    146     auto factory = Factory::Create();
    147     return factory->Create(details)->Open(progress);
    148 }
    149 
    150 SQLiteIndex CreateMemoryIndex()
    151 {
    152     return SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless);
    153 }
    154 
    155 TEST_CASE("ARPHelper_GetARPForArchitecture", "[arphelper][list]")
    156 {
    157     auto systemArch = GetSystemArchitecture();
    158 
    159     ARPHelper helper;
    160 
    161     auto nativeMachineKey = helper.GetARPKey(ScopeEnum::Machine, systemArch);
    162     REQUIRE(nativeMachineKey);
    163 }
    164 
    165 TEST_CASE("ARPHelper_GetBoolValue_DoesNotExist", "[arphelper][list]")
    166 {
    167     auto root = RegCreateVolatileTestRoot();
    168     Registry::Key key(root.get());
    169     std::wstring valueName = L"TestValueName";
    170 
    171     ARPHelper helper;
    172 
    173     REQUIRE_FALSE(helper.GetBoolValue(key, valueName));
    174 }
    175 
    176 TEST_CASE("ARPHelper_GetBoolValue_NotDword", "[arphelper][list]")
    177 {
    178     auto root = RegCreateVolatileTestRoot();
    179     Registry::Key key(root.get());
    180     std::wstring valueName = L"TestValueName";
    181 
    182     SetRegistryValue(root.get(), valueName, L"True");
    183 
    184     ARPHelper helper;
    185 
    186     REQUIRE_FALSE(helper.GetBoolValue(key, valueName));
    187 }
    188 
    189 TEST_CASE("ARPHelper_GetBoolValue_Zero", "[arphelper][list]")
    190 {
    191     auto root = RegCreateVolatileTestRoot();
    192     Registry::Key key(root.get());
    193     std::wstring valueName = L"TestValueName";
    194 
    195     SetRegistryValue(root.get(), valueName, 0);
    196 
    197     ARPHelper helper;
    198 
    199     REQUIRE_FALSE(helper.GetBoolValue(key, valueName));
    200 }
    201 
    202 TEST_CASE("ARPHelper_GetBoolValue_One", "[arphelper][list]")
    203 {
    204     auto root = RegCreateVolatileTestRoot();
    205     Registry::Key key(root.get());
    206     std::wstring valueName = L"TestValueName";
    207 
    208     SetRegistryValue(root.get(), valueName, 1);
    209 
    210     ARPHelper helper;
    211 
    212     REQUIRE(helper.GetBoolValue(key, valueName));
    213 }
    214 
    215 TEST_CASE("ARPHelper_GetBoolValue_FortyTwo", "[arphelper][list]")
    216 {
    217     auto root = RegCreateVolatileTestRoot();
    218     Registry::Key key(root.get());
    219     std::wstring valueName = L"TestValueName";
    220 
    221     SetRegistryValue(root.get(), valueName, 42);
    222 
    223     ARPHelper helper;
    224 
    225     REQUIRE(helper.GetBoolValue(key, valueName));
    226 }
    227 
    228 TEST_CASE("ARPHelper_DetermineVersion_DisplayVersion", "[arphelper][list]")
    229 {
    230     auto root = RegCreateVolatileTestRoot();
    231     Registry::Key key(root.get());
    232 
    233     ARPHelper helper;
    234 
    235     SetRegistryValue(root.get(), helper.DisplayVersion, L"1.0");
    236     SetRegistryValue(root.get(), helper.Version, 0x0207002A);
    237     SetRegistryValue(root.get(), helper.VersionMajor, 3);
    238     SetRegistryValue(root.get(), helper.VersionMinor, 14);
    239 
    240     auto result = helper.DetermineVersion(key);
    241     REQUIRE(result == "1.0");
    242 }
    243 
    244 TEST_CASE("ARPHelper_DetermineVersion_Version", "[arphelper][list]")
    245 {
    246     auto root = RegCreateVolatileTestRoot();
    247     Registry::Key key(root.get());
    248 
    249     ARPHelper helper;
    250 
    251     SetRegistryValue(root.get(), helper.Version, 0x0207002A);
    252     SetRegistryValue(root.get(), helper.VersionMajor, 3);
    253     SetRegistryValue(root.get(), helper.VersionMinor, 14);
    254 
    255     auto result = helper.DetermineVersion(key);
    256     REQUIRE(result == "3.14");
    257 }
    258 
    259 TEST_CASE("ARPHelper_DetermineVersion_VersionMajorMinor", "[arphelper][list]")
    260 {
    261     auto root = RegCreateVolatileTestRoot();
    262     Registry::Key key(root.get());
    263 
    264     ARPHelper helper;
    265 
    266     SetRegistryValue(root.get(), helper.VersionMajor, 3);
    267     SetRegistryValue(root.get(), helper.VersionMinor, 14);
    268 
    269     auto result = helper.DetermineVersion(key);
    270     REQUIRE(result == "3.14");
    271 }
    272 
    273 TEST_CASE("ARPHelper_DetermineVersion_Unknown", "[arphelper][list]")
    274 {
    275     auto root = RegCreateVolatileTestRoot();
    276     Registry::Key key(root.get());
    277 
    278     ARPHelper helper;
    279 
    280     auto result = helper.DetermineVersion(key);
    281     REQUIRE(result == Version::CreateUnknown().ToString());
    282 }
    283 
    284 TEST_CASE("ARPHelper_PopulateIndexFromKey_Single", "[arphelper][list]")
    285 {
    286     auto root = RegCreateVolatileTestRoot();
    287     Registry::Key key(root.get());
    288 
    289     ARPHelper helper;
    290 
    291     // Create a single ARP entry under the root
    292     ARPEntry entry("SingleEntry");
    293 
    294     entry.DisplayName = "Test Name";
    295     entry.DisplayVersion = "1.2";
    296     entry.Publisher = "Test Publisher";
    297     entry.InstallLocation = "TestLocation";
    298     entry.UninstallString = "Test Uninstall";
    299     entry.QuietUninstallString = "Test Quiet Uninstall";
    300     entry.WindowsInstaller = true;
    301 
    302     AddARPEntryToKey(root.get(), helper, entry);
    303 
    304     auto index = CreateMemoryIndex();
    305     helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture");
    306 
    307     auto result = index.Search({});
    308 
    309     REQUIRE(result.Matches.size() == 1);
    310     VerifyEntryAgainstIndex(index, result.Matches[0].first, entry);
    311 }
    312 
    313 TEST_CASE("ARPHelper_PopulateIndexFromKey_SingleValid", "[arphelper][list]")
    314 {
    315     auto root = RegCreateVolatileTestRoot();
    316     Registry::Key key(root.get());
    317 
    318     ARPHelper helper;
    319 
    320     // Create a single ARP entry under the root
    321     ARPEntry entry("SingleEntry");
    322 
    323     entry.DisplayName = "Test Name";
    324     entry.DisplayVersion = "1.2";
    325     entry.Publisher = "Test Publisher";
    326     entry.InstallLocation = "TestLocation";
    327     entry.UninstallString = "Test Uninstall";
    328     entry.QuietUninstallString = "Test Quiet Uninstall";
    329     entry.WindowsInstaller = false;
    330 
    331     AddARPEntryToKey(root.get(), helper, entry);
    332 
    333     // Name and version must exist, as well as not being a system component.
    334     AddARPEntriesToKey(root.get(), helper, {
    335         { "ValidButIsSystemComponent", "A", "0.1", true },
    336         { "NoName", {}, "0.2" },
    337         { "Nothing" },
    338         });
    339 
    340     auto index = CreateMemoryIndex();
    341     helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture");
    342 
    343     auto result = index.Search({});
    344 
    345     REQUIRE(result.Matches.size() == 1);
    346     VerifyEntryAgainstIndex(index, result.Matches[0].first, entry);
    347 }
    348 
    349 TEST_CASE("ARPHelper_PopulateIndexFromKey_Two", "[arphelper][list]")
    350 {
    351     auto root = RegCreateVolatileTestRoot();
    352     Registry::Key key(root.get());
    353 
    354     ARPHelper helper;
    355 
    356     ARPEntry entry1("FirstEntry");
    357     entry1.DisplayName = "Test Name";
    358     entry1.DisplayVersion = "1.2";
    359     entry1.Publisher = "Test Publisher";
    360     entry1.InstallLocation = "TestLocation";
    361     entry1.UninstallString = "Test Uninstall";
    362     entry1.QuietUninstallString = "Test Quiet Uninstall";
    363     entry1.WindowsInstaller = true;
    364 
    365     ARPEntry entry2("SecondEntry");
    366     entry2.DisplayName = "Different Test Name";
    367     entry2.DisplayVersion = "31.4";
    368     entry2.Publisher = "Different Test Publisher";
    369     entry2.InstallLocation = "DifferentTestLocation";
    370     entry2.UninstallString = "Different Test Uninstall";
    371     entry2.QuietUninstallString = "Different Test Quiet Uninstall";
    372 
    373     AddARPEntryToKey(root.get(), helper, entry1);
    374     AddARPEntryToKey(root.get(), helper, entry2);
    375 
    376     auto index = CreateMemoryIndex();
    377     helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture");
    378 
    379     REQUIRE(index.Search({}).Matches.size() == 2);
    380 
    381     SearchRequest request;
    382     request.Query = RequestMatch(MatchType::Exact, entry1.EntryName);
    383     auto result = index.Search(request);
    384 
    385     REQUIRE(result.Matches.size() == 1);
    386     VerifyEntryAgainstIndex(index, result.Matches[0].first, entry1);
    387 
    388     request.Query = RequestMatch(MatchType::Exact, entry2.EntryName);
    389     result = index.Search(request);
    390 
    391     REQUIRE(result.Matches.size() == 1);
    392     VerifyEntryAgainstIndex(index, result.Matches[0].first, entry2);
    393 }
    394 
    395 TEST_CASE("PredefinedInstalledSource_Create", "[installed][list]")
    396 {
    397     auto source = CreatePredefinedInstalledSource();
    398 }
    399 
    400 TEST_CASE("PredefinedInstalledSource_Search", "[installed][list]")
    401 {
    402     auto source = CreatePredefinedInstalledSource();
    403 
    404     SearchRequest request;
    405 
    406     auto results = source->Search(request);
    407 
    408     REQUIRE_FALSE(results.Matches.empty());
    409 }
    410 
    411 std::string GetDatabaseIdentifier(const std::shared_ptr<Repository::ISource>& source)
    412 {
    413     return reinterpret_cast<SQLiteIndexSource*>(source->CastTo(ISourceType::SQLiteIndexSource))->GetIndex().GetDatabaseIdentifier();
    414 }
    415 
    416 void RequirePackagesHaveSameNames(std::shared_ptr<ISource>& source1, std::shared_ptr<ISource>& source2)
    417 {
    418     auto result1 = source1->Search({});
    419     REQUIRE(!result1.Matches.empty());
    420 
    421     // Ensure that all packages have the same name values
    422     for (const auto& match : result1.Matches)
    423     {
    424         std::string packageId = match.Package->GetProperty(PackageProperty::Id).get();
    425         INFO(packageId);
    426 
    427         SearchRequest id2;
    428         id2.Inclusions.emplace_back(PackageMatchFilter{ PackageMatchField::Id, MatchType::CaseInsensitive, packageId });
    429         auto result2 = source2->Search(id2);
    430         REQUIRE(result2.Matches.size() == 1);
    431         REQUIRE(match.Package->GetProperty(PackageProperty::Name) == result2.Matches[0].Package->GetProperty(PackageProperty::Name));
    432     }
    433 }
    434 
    435 TEST_CASE("PredefinedInstalledSource_Create_Cached", "[installed][list][installed-cache]")
    436 {
    437     auto source1 = CreatePredefinedInstalledSource();
    438     auto source2 = CreatePredefinedInstalledSource();
    439 
    440     // Ensure the same identifier (which should mean the cache was not updated)
    441     REQUIRE(
    442         GetDatabaseIdentifier(source1)
    443         ==
    444         GetDatabaseIdentifier(source2)
    445     );
    446 
    447     RequirePackagesHaveSameNames(source1, source2);
    448     RequirePackagesHaveSameNames(source2, source1);
    449 }
    450 
    451 TEST_CASE("PredefinedInstalledSource_Create_ForceCacheUpdate", "[installed][list][installed-cache]")
    452 {
    453     auto source1 = CreatePredefinedInstalledSource();
    454     auto source2 = CreatePredefinedInstalledSource(Factory::Filter::NoneWithForcedCacheUpdate);
    455 
    456     // Ensure different identifier (which should mean the cache was updated)
    457     REQUIRE(
    458         GetDatabaseIdentifier(source1)
    459         !=
    460         GetDatabaseIdentifier(source2)
    461     );
    462 
    463     RequirePackagesHaveSameNames(source1, source2);
    464     RequirePackagesHaveSameNames(source2, source1);
    465 }
    466 
    467 TEST_CASE("PredefinedInstalledSource_Create_ForceCacheUpdate_StillCached", "[installed][list][installed-cache]")
    468 {
    469     auto source1 = CreatePredefinedInstalledSource();
    470     auto source2 = CreatePredefinedInstalledSource(Factory::Filter::NoneWithForcedCacheUpdate);
    471     auto source3 = CreatePredefinedInstalledSource();
    472 
    473     CAPTURE(GetDatabaseIdentifier(source1), GetDatabaseIdentifier(source2), GetDatabaseIdentifier(source3));
    474 
    475     // Ensure different identifier (which should mean the cache was updated)
    476     REQUIRE(
    477         GetDatabaseIdentifier(source1)
    478         !=
    479         GetDatabaseIdentifier(source2)
    480     );
    481 
    482     // Ensure the same identifier (which should mean the cache was not updated)
    483     REQUIRE(
    484         GetDatabaseIdentifier(source2)
    485         ==
    486         GetDatabaseIdentifier(source3)
    487     );
    488 }