winget-cli

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

ManifestComparator.cpp (37822B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "TestCommon.h"
      5 #include <ExecutionContext.h>
      6 #include <COMContext.h>
      7 #include <winget/ManifestComparator.h>
      8 #include <winget/UserSettings.h>
      9 #include <Workflows/WorkflowBase.h>
     10 
     11 using namespace std::string_literals;
     12 using namespace std::string_view_literals;
     13 using namespace TestCommon;
     14 using namespace AppInstaller::CLI::Workflow;
     15 using namespace AppInstaller::CLI::Execution;
     16 using namespace AppInstaller::Manifest;
     17 using namespace AppInstaller::Repository;
     18 using namespace AppInstaller::Settings;
     19 using namespace AppInstaller::Utility;
     20 
     21 using Manifest = ::AppInstaller::Manifest::Manifest;
     22 
     23 struct ManifestComparatorTestContext : public NullStream, Context
     24 {
     25     ManifestComparatorTestContext() : NullStream(), Context(*m_nullOut, *m_nullIn) {}
     26 };
     27 
     28 const ManifestInstaller& AddInstaller(
     29     Manifest& manifest,
     30     Architecture architecture,
     31     InstallerTypeEnum installerType,
     32     ScopeEnum scope = ScopeEnum::Unknown,
     33     std::string minOSVersion = {},
     34     std::string locale = {},
     35     std::vector<Architecture> unsupportedOSArchitectures = {},
     36     MarketsInfo markets = {})
     37 {
     38     ManifestInstaller toAdd;
     39     toAdd.Arch = architecture;
     40     toAdd.BaseInstallerType = installerType;
     41     toAdd.Scope = scope;
     42     toAdd.MinOSVersion = minOSVersion;
     43     toAdd.Locale = locale;
     44     toAdd.UnsupportedOSArchitectures = unsupportedOSArchitectures;
     45     toAdd.Markets = markets;
     46 
     47     manifest.Installers.emplace_back(std::move(toAdd));
     48 
     49     return manifest.Installers.back();
     50 }
     51 
     52 template<typename T>
     53 void RequireVectorsEqual(const std::vector<T>& actual, const std::vector<T>& expected)
     54 {
     55     REQUIRE(actual.size() == expected.size());
     56     
     57     for (std::size_t i = 0; i < actual.size(); ++i)
     58     {
     59         REQUIRE(actual[i] == expected[i]);
     60     }
     61 }
     62 
     63 void RequireInstaller(const std::optional<ManifestInstaller>& actual, const ManifestInstaller& expected)
     64 {
     65     REQUIRE(actual);
     66     REQUIRE(actual->Arch == expected.Arch);
     67     REQUIRE(actual->EffectiveInstallerType() == expected.EffectiveInstallerType());
     68     REQUIRE(actual->Scope == expected.Scope);
     69     REQUIRE(actual->MinOSVersion == expected.MinOSVersion);
     70     REQUIRE(actual->Locale == expected.Locale);
     71 
     72     RequireVectorsEqual(actual->Markets.AllowedMarkets, expected.Markets.AllowedMarkets);
     73     RequireVectorsEqual(actual->Markets.ExcludedMarkets, expected.Markets.ExcludedMarkets);
     74 }
     75 
     76 void RequireInapplicabilities(const std::vector<InapplicabilityFlags>& inapplicabilities, const std::vector<InapplicabilityFlags>& expected)
     77 {
     78     RequireVectorsEqual(inapplicabilities, expected);
     79 }
     80 
     81 void RequireInapplicabilityType(const std::vector<InapplicabilityFlags>& inapplicabilities, InapplicabilityFlags expected)
     82 {
     83     REQUIRE(!inapplicabilities.empty());
     84     for (std::size_t i = 0; i < inapplicabilities.size(); ++i)
     85     {
     86         REQUIRE(inapplicabilities[i] == expected);
     87     }
     88 }
     89 
     90 TEST_CASE("ManifestComparator_OSFilter_Low", "[manifest_comparator]")
     91 {
     92     Manifest manifest;
     93     AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.99999.0");
     94 
     95     ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
     96     auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
     97 
     98     REQUIRE(!result);
     99     RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::OSVersion });
    100 }
    101 
    102 TEST_CASE("ManifestComparator_OSFilter_High", "[manifest_comparator]")
    103 {
    104     Manifest manifest;
    105     ManifestInstaller expected = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.0.0");
    106 
    107     ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    108     auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    109 
    110     RequireInstaller(result, expected);
    111     REQUIRE(inapplicabilities.size() == 0);
    112 }
    113 
    114 TEST_CASE("ManifestComparator_InstalledScopeFilter_Unknown", "[manifest_comparator]")
    115 {
    116     Manifest manifest;
    117     ManifestInstaller unknown = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Unknown);
    118 
    119     SECTION("Nothing Installed")
    120     {
    121         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    122         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    123 
    124         // Only because it is first
    125         RequireInstaller(result, unknown);
    126         REQUIRE(inapplicabilities.size() == 0);
    127     }
    128     SECTION("User Installed")
    129     {
    130         IPackageVersion::Metadata metadata;
    131         metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User);
    132 
    133         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    134         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    135 
    136         RequireInstaller(result, unknown);
    137         REQUIRE(inapplicabilities.size() == 0);
    138     }
    139     SECTION("Machine Installed")
    140     {
    141         IPackageVersion::Metadata metadata;
    142         metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine);
    143 
    144         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    145         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    146 
    147         RequireInstaller(result, unknown);
    148         REQUIRE(inapplicabilities.size() == 0);
    149     }
    150 }
    151 
    152 TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]")
    153 {
    154     Manifest manifest;
    155     ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User);
    156     ManifestInstaller machine = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Machine);
    157 
    158     SECTION("Nothing Installed")
    159     {
    160         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    161         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    162 
    163         // Only because it is first
    164         RequireInstaller(result, user);
    165         REQUIRE(inapplicabilities.size() == 0);
    166     }
    167     SECTION("User Installed")
    168     {
    169         IPackageVersion::Metadata metadata;
    170         metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User);
    171 
    172         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    173         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    174 
    175         RequireInstaller(result, user);
    176         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledScope });
    177     }
    178     SECTION("Machine Installed")
    179     {
    180         IPackageVersion::Metadata metadata;
    181         metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine);
    182 
    183         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    184         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    185 
    186         RequireInstaller(result, machine);
    187         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledScope });
    188     }
    189 }
    190 
    191 TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]")
    192 {
    193     Manifest manifest;
    194     ManifestInstaller msi = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi);
    195     ManifestInstaller msix = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msix);
    196 
    197     SECTION("Nothing Installed")
    198     {
    199         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    200         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    201 
    202         // Only because it is first
    203         RequireInstaller(result, msi);
    204         REQUIRE(inapplicabilities.size() == 0);
    205     }
    206     SECTION("MSI Installed")
    207     {
    208         IPackageVersion::Metadata metadata;
    209         metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msi);
    210 
    211         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    212         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    213 
    214         RequireInstaller(result, msi);
    215         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledType });
    216     }
    217     SECTION("MSIX Installed")
    218     {
    219         IPackageVersion::Metadata metadata;
    220         metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix);
    221 
    222         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    223         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    224 
    225         RequireInstaller(result, msix);
    226         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledType });
    227     }
    228 }
    229 
    230 TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]")
    231 {
    232     Manifest manifest;
    233     ManifestInstaller burn = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Burn);
    234     ManifestInstaller exe = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe);
    235 
    236     SECTION("Nothing Installed")
    237     {
    238         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    239         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    240 
    241         // Only because it is first
    242         RequireInstaller(result, burn);
    243         REQUIRE(inapplicabilities.size() == 0);
    244     }
    245     SECTION("Exe Installed")
    246     {
    247         IPackageVersion::Metadata metadata;
    248         metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Exe);
    249 
    250         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    251         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    252 
    253         RequireInstaller(result, burn);
    254         REQUIRE(inapplicabilities.size() == 0);
    255     }
    256     SECTION("Inno Installed")
    257     {
    258         IPackageVersion::Metadata metadata;
    259         metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Inno);
    260 
    261         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    262         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    263 
    264         RequireInstaller(result, burn);
    265         REQUIRE(inapplicabilities.size() == 0);
    266     }
    267 }
    268 
    269 TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]")
    270 {
    271     Manifest manifest;
    272     ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User);
    273     ManifestInstaller machine = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Machine);
    274 
    275     SECTION("Nothing Required")
    276     {
    277         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    278         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    279 
    280         // Only because it is first
    281         RequireInstaller(result, user);
    282         REQUIRE(inapplicabilities.size() == 0);
    283     }
    284     SECTION("User Required")
    285     {
    286         ManifestComparatorTestContext context;
    287         context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User));
    288 
    289         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    290         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    291 
    292         RequireInstaller(result, user);
    293         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope });
    294     }
    295     SECTION("Machine Required")
    296     {
    297         ManifestComparatorTestContext context;
    298         context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine));
    299 
    300         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    301         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    302 
    303         RequireInstaller(result, machine);
    304         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope });
    305     }
    306 }
    307 
    308 TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]")
    309 {
    310     Manifest manifest;
    311     ManifestInstaller machine = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Machine);
    312     ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User);
    313 
    314     SECTION("No Preference")
    315     {
    316         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    317         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    318 
    319         // The default preference is user
    320         RequireInstaller(result, user);
    321         REQUIRE(inapplicabilities.size() == 0);
    322     }
    323     SECTION("User Preference")
    324     {
    325         TestUserSettings settings;
    326         settings.Set<Setting::InstallScopePreference>(ScopeEnum::User);
    327 
    328         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    329         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    330 
    331         RequireInstaller(result, user);
    332         REQUIRE(inapplicabilities.size() == 0);
    333     }
    334     SECTION("Machine Preference")
    335     {
    336         TestUserSettings settings;
    337         settings.Set<Setting::InstallScopePreference>(ScopeEnum::Machine);
    338 
    339         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    340         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    341 
    342         RequireInstaller(result, machine);
    343         REQUIRE(inapplicabilities.size() == 0);
    344     }
    345 }
    346 
    347 TEST_CASE("ManifestComparator_LocaleComparator_Installed_WithUnknown", "[manifest_comparator]")
    348 {
    349     Manifest manifest;
    350     ManifestInstaller unknown = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    351     ManifestInstaller enGB = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "en-GB");
    352 
    353     SECTION("Nothing Installed en-US preference")
    354     {
    355         TestUserSettings settings;
    356         settings.Set<Setting::InstallLocalePreference>({ "en-US" });
    357 
    358         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    359         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    360 
    361         // Only because it is first
    362         RequireInstaller(result, enGB);
    363         REQUIRE(inapplicabilities.size() == 0);
    364     }
    365     SECTION("en-US Installed")
    366     {
    367         IPackageVersion::Metadata metadata;
    368         metadata[PackageVersionMetadata::InstalledLocale] = "en-US";
    369 
    370         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    371         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    372 
    373         RequireInstaller(result, enGB);
    374         REQUIRE(inapplicabilities.size() == 0);
    375     }
    376     SECTION("zh-CN Installed")
    377     {
    378         IPackageVersion::Metadata metadata;
    379         metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN";
    380 
    381         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    382         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    383 
    384         RequireInstaller(result, unknown);
    385         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale });
    386     }
    387 }
    388 
    389 TEST_CASE("ManifestComparator_LocaleComparator_Installed", "[manifest_comparator]")
    390 {
    391     Manifest manifest;
    392     ManifestInstaller frFR = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "fr-FR");
    393     ManifestInstaller enGB = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "en-GB");
    394 
    395     SECTION("Nothing Installed en-US preference")
    396     {
    397         TestUserSettings settings;
    398         settings.Set<Setting::InstallLocalePreference>({ "en-US" });
    399 
    400         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    401         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    402 
    403         // Only because it is first
    404         RequireInstaller(result, enGB);
    405         REQUIRE(inapplicabilities.size() == 0);
    406     }
    407     SECTION("en-US Installed")
    408     {
    409         IPackageVersion::Metadata metadata;
    410         metadata[PackageVersionMetadata::InstalledLocale] = "en-US";
    411 
    412         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    413         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    414 
    415         RequireInstaller(result, enGB);
    416         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale });
    417     }
    418     SECTION("zh-CN Installed")
    419     {
    420         IPackageVersion::Metadata metadata;
    421         metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN";
    422 
    423         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    424         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    425 
    426         REQUIRE(!result);
    427         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale, InapplicabilityFlags::InstalledLocale });
    428     }
    429     SECTION("en-US installed but fr-fr as user intent")
    430     {
    431         IPackageVersion::Metadata metadata;
    432         metadata[PackageVersionMetadata::InstalledLocale] = "en-US";
    433         metadata[PackageVersionMetadata::UserIntentLocale] = "fr-FR";
    434 
    435         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    436         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    437 
    438         RequireInstaller(result, frFR);
    439         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale }); // en-US inapplicable
    440     }
    441     SECTION("en-US installed but zh-CN as user intent")
    442     {
    443         IPackageVersion::Metadata metadata;
    444         metadata[PackageVersionMetadata::InstalledLocale] = "en-US";
    445         metadata[PackageVersionMetadata::UserIntentLocale] = "zh-CN";
    446 
    447         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    448         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    449 
    450         REQUIRE(!result);
    451         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale, InapplicabilityFlags::InstalledLocale });
    452     }
    453 }
    454 
    455 TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]")
    456 {
    457     Manifest manifest;
    458     ManifestInstaller unknown = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    459     ManifestInstaller frFR = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "fr-FR");
    460     ManifestInstaller enGB = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "en-GB");
    461 
    462     SECTION("en-GB Required")
    463     {
    464         ManifestComparatorTestContext context;
    465         context.Args.AddArg(Args::Type::Locale, "en-GB"s);
    466 
    467         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    468         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    469 
    470         RequireInstaller(result, enGB);
    471         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Locale , InapplicabilityFlags::Locale });
    472     }
    473     SECTION("zh-CN Required")
    474     {
    475         ManifestComparatorTestContext context;
    476         context.Args.AddArg(Args::Type::Locale, "zh-CN"s);
    477 
    478         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    479         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    480 
    481         REQUIRE(!result);
    482         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Locale , InapplicabilityFlags::Locale, InapplicabilityFlags::Locale });
    483     }
    484     SECTION("en-US Preference")
    485     {
    486         TestUserSettings settings;
    487         settings.Set<Setting::InstallLocalePreference>({ "en-US" });
    488 
    489         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    490         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    491 
    492         RequireInstaller(result, enGB);
    493         REQUIRE(inapplicabilities.size() == 0);
    494     }
    495     SECTION("zh-CN Preference")
    496     {
    497         TestUserSettings settings;
    498         settings.Set<Setting::InstallLocalePreference>({ "zh-CN" });
    499 
    500         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    501         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    502 
    503         RequireInstaller(result, unknown);
    504         REQUIRE(inapplicabilities.size() == 0);
    505     }
    506 }
    507 
    508 TEST_CASE("ManifestComparator_AllowedArchitecture_x64_only", "[manifest_comparator]")
    509 {
    510     Manifest manifest;
    511     ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    512 
    513     ManifestComparatorTestContext context;
    514     context.Add<Data::AllowedArchitectures>({ Architecture::X64 });
    515 
    516     ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    517     auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    518 
    519     REQUIRE(!result);
    520     RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture });
    521 }
    522 
    523 TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]")
    524 {
    525     Manifest manifest;
    526     ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    527     ManifestInstaller x64 = AddInstaller(manifest, Architecture::X64, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    528     ManifestInstaller arm = AddInstaller(manifest, Architecture::Arm, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    529     ManifestInstaller arm64 = AddInstaller(manifest, Architecture::Arm64, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    530 
    531     SECTION("x86 Preference")
    532     {
    533         ManifestComparatorTestContext context;
    534         context.Add<Data::AllowedArchitectures>({ Architecture::X86, Architecture::X64 });
    535 
    536         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    537         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    538 
    539         RequireInstaller(result, x86);
    540         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture, InapplicabilityFlags::MachineArchitecture });
    541     }
    542     SECTION("Unknown")
    543     {
    544         ManifestComparatorTestContext context;
    545         context.Add<Data::AllowedArchitectures>({ Architecture::Unknown });
    546 
    547         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    548         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    549 
    550         REQUIRE(result);
    551         REQUIRE(result->Arch == GetApplicableArchitectures()[0]);
    552         RequireInapplicabilityType(inapplicabilities, InapplicabilityFlags::MachineArchitecture);
    553     }
    554     SECTION("x86 and Unknown")
    555     {
    556         ManifestComparatorTestContext context;
    557         context.Add<Data::AllowedArchitectures>({ Architecture::X86, Architecture::Unknown });
    558 
    559         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    560         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    561 
    562         RequireInstaller(result, x86);
    563         RequireInapplicabilityType(inapplicabilities, InapplicabilityFlags::MachineArchitecture);
    564     }
    565 }
    566 
    567 TEST_CASE("ManifestComparator_Architectures_WithUserIntent", "[manifest_comparator]")
    568 {
    569     Manifest manifest;
    570     ManifestInstaller x86 = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    571     ManifestInstaller x64 = AddInstaller(manifest, Architecture::X64, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    572 
    573     SECTION("x86 installed")
    574     {
    575         IPackageVersion::Metadata metadata;
    576         metadata[PackageVersionMetadata::InstalledArchitecture] = "x86";
    577 
    578         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    579         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    580 
    581         RequireInstaller(result, x86);
    582         REQUIRE(inapplicabilities.size() == 0);
    583     }
    584     SECTION("x86 installed but x64 as user intent")
    585     {
    586         IPackageVersion::Metadata metadata;
    587         metadata[PackageVersionMetadata::InstalledArchitecture] = "x86";
    588         metadata[PackageVersionMetadata::UserIntentArchitecture] = "x64";
    589 
    590         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    591         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    592 
    593         RequireInstaller(result, x64);
    594         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture });
    595     }
    596     SECTION("x86 installed but x64 as user intent")
    597     {
    598         Manifest x86OnlyManifest;
    599         AddInstaller(x86OnlyManifest, Architecture::X86, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    600 
    601         IPackageVersion::Metadata metadata;
    602         metadata[PackageVersionMetadata::InstalledArchitecture] = "x86";
    603         metadata[PackageVersionMetadata::UserIntentArchitecture] = "x64";
    604 
    605         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, metadata));
    606         auto [result, inapplicabilities] = mc.GetPreferredInstaller(x86OnlyManifest);
    607 
    608         REQUIRE(!result);
    609         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture });
    610     }
    611 }
    612 
    613 TEST_CASE("ManifestComparator_UnsupportedOSArchitecture", "[manifest_comparator]")
    614 {
    615     auto systemArchitecture = GetSystemArchitecture();
    616     auto applicableArchitectures = GetApplicableArchitectures();
    617 
    618     // Try to find an applicable architecture that is not the system architecture
    619     auto itr = std::find_if(applicableArchitectures.begin(), applicableArchitectures.end(), [&](const auto& arch) { return arch != systemArchitecture; });
    620     if (itr == applicableArchitectures.end())
    621     {
    622         // This test requires having an applicable architecture different from the system one.
    623         // TODO: Does this actually happen in any arch we use?
    624         return;
    625     }
    626 
    627     auto applicableArchitecture = *itr;
    628 
    629     Manifest manifest;
    630 
    631     SECTION("System is unsupported")
    632     {
    633         ManifestInstaller installer = AddInstaller(manifest, applicableArchitecture, InstallerTypeEnum::Msi, {}, {}, {}, { systemArchitecture });
    634 
    635         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    636         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    637 
    638         REQUIRE(!result);
    639         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture });
    640     }
    641     SECTION("Other is unsupported")
    642     {
    643         ManifestInstaller installer = AddInstaller(manifest, applicableArchitecture, InstallerTypeEnum::Msi, {}, {}, {}, { applicableArchitecture });
    644 
    645         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    646         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    647 
    648         RequireInstaller(result, installer);
    649         REQUIRE(inapplicabilities.empty());
    650     }
    651 }
    652 
    653 TEST_CASE("ManifestComparator_Inapplicabilities", "[manifest_comparator]")
    654 {
    655     Manifest manifest;
    656     ManifestInstaller installer = AddInstaller(manifest, Architecture::Arm64, InstallerTypeEnum::Exe, ScopeEnum::User, "10.0.99999.0", "es-MX");
    657 
    658     ManifestComparatorTestContext context;
    659     context.Add<Data::AllowedArchitectures>({ Architecture::X86 });
    660     context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine));
    661     context.Args.AddArg(Args::Type::Locale, "en-GB"s);
    662 
    663     IPackageVersion::Metadata metadata;
    664     metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix);
    665 
    666     ManifestComparator mc(GetManifestComparatorOptions(context, metadata));
    667     auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    668 
    669     REQUIRE(!result);
    670     RequireInapplicabilities(
    671         inapplicabilities,
    672         { InapplicabilityFlags::OSVersion | InapplicabilityFlags::InstalledType | InapplicabilityFlags::Locale | InapplicabilityFlags::Scope | InapplicabilityFlags::MachineArchitecture });
    673 }
    674 
    675 TEST_CASE("ManifestComparator_MarketFilter", "[manifest_comparator]")
    676 {
    677     Manifest manifest;
    678 
    679     // Get current market.
    680     winrt::Windows::Globalization::GeographicRegion region;
    681     Manifest::string_t currentMarket{ region.CodeTwoLetter() };
    682 
    683     SECTION("Applicable")
    684     {
    685         MarketsInfo markets;
    686         SECTION("Allowed")
    687         {
    688             markets.AllowedMarkets = { currentMarket };
    689         }
    690         SECTION("Not excluded")
    691         {
    692             markets.ExcludedMarkets = { "XX" };
    693         }
    694 
    695         ManifestInstaller installer = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Exe, {}, {}, {}, {}, markets);
    696         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    697         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    698 
    699         RequireInstaller(result, installer);
    700         REQUIRE(inapplicabilities.empty());
    701     }
    702 
    703     SECTION("Inapplicable")
    704     {
    705         MarketsInfo markets;
    706         SECTION("Excluded")
    707         {
    708             markets.ExcludedMarkets = { currentMarket };
    709         }
    710         SECTION("Not allowed")
    711         {
    712             markets.AllowedMarkets = { "XX" };
    713         }
    714 
    715         ManifestInstaller installer = AddInstaller(manifest, Architecture::X86, InstallerTypeEnum::Exe, {}, {}, {}, {}, markets);
    716         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    717         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    718 
    719         REQUIRE(!result);
    720         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Market});
    721     }
    722 }
    723 
    724 TEST_CASE("ManifestComparator_Scope_AllowUnknown", "[manifest_comparator]")
    725 {
    726     Manifest manifest;
    727     ManifestInstaller expected = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown);
    728 
    729     ManifestComparatorTestContext testContext;
    730     testContext.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User));
    731 
    732     SECTION("Default")
    733     {
    734         ManifestComparator mc(GetManifestComparatorOptions(testContext, {}));
    735         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    736 
    737         REQUIRE(!result);
    738         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope });
    739     }
    740     SECTION("Allow Unknown")
    741     {
    742         testContext.Add<Data::AllowUnknownScope>(true);
    743 
    744         ManifestComparator mc(GetManifestComparatorOptions(testContext, {}));
    745         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    746 
    747         RequireInstaller(result, expected);
    748         REQUIRE(inapplicabilities.size() == 0);
    749     }
    750 }
    751 
    752 TEST_CASE("ManifestComparator_InstallerType", "[manifest_comparator]")
    753 {
    754     Manifest manifest;
    755     ManifestInstaller msi = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User);
    756     ManifestInstaller exe = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::User);
    757     ManifestInstaller msix = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msix, ScopeEnum::User);
    758 
    759     SECTION("Msi arg requirement")
    760     {
    761         ManifestComparatorTestContext context;
    762         context.Args.AddArg(Args::Type::InstallerType, "msi"s);
    763 
    764         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    765         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    766 
    767         RequireInstaller(result, msi);
    768         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType });
    769     }
    770     SECTION("Msix arg requirement")
    771     {
    772         ManifestComparatorTestContext context;
    773         context.Args.AddArg(Args::Type::InstallerType, "msix"s);
    774 
    775         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    776         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    777 
    778         RequireInstaller(result, msix);
    779         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType });
    780     }
    781     SECTION("Portable arg requirement")
    782     {
    783         ManifestComparatorTestContext context;
    784         context.Args.AddArg(Args::Type::InstallerType, "portable"s);
    785 
    786         ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    787         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    788 
    789         REQUIRE(!result);
    790         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType });
    791     }
    792     SECTION("Exe preference")
    793     {
    794         TestUserSettings settings;
    795         settings.Set<Setting::InstallerTypePreference>({ InstallerTypeEnum::Exe });
    796 
    797         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    798         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    799 
    800         RequireInstaller(result, exe);
    801         REQUIRE(inapplicabilities.size() == 0);
    802     }
    803     SECTION("Preference does not exist")
    804     {
    805         TestUserSettings settings;
    806         settings.Set<Setting::InstallerTypePreference>({ InstallerTypeEnum::Portable });
    807 
    808         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    809         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    810 
    811         RequireInstaller(result, msi);
    812         REQUIRE(inapplicabilities.size() == 0);
    813     }
    814     SECTION("Multiple preferences")
    815     {
    816         TestUserSettings settings;
    817         settings.Set<Setting::InstallerTypePreference>({ InstallerTypeEnum::Exe, InstallerTypeEnum::Msix });
    818 
    819         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    820         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    821 
    822         RequireInstaller(result, exe);
    823         REQUIRE(inapplicabilities.size() == 0);
    824     }
    825     SECTION("Multiple preferences alternate order")
    826     {
    827         TestUserSettings settings;
    828         settings.Set<Setting::InstallerTypePreference>({ InstallerTypeEnum::Msix, InstallerTypeEnum::Exe });
    829 
    830         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    831         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    832 
    833         RequireInstaller(result, msix);
    834         REQUIRE(inapplicabilities.size() == 0);
    835     }
    836     SECTION("Exe requirement")
    837     {
    838         TestUserSettings settings;
    839         settings.Set<Setting::InstallerTypeRequirement>({ InstallerTypeEnum::Exe });
    840 
    841         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    842         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    843 
    844         RequireInstaller(result, exe);
    845         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType });
    846     }
    847     SECTION("Inno requirement")
    848     {
    849         TestUserSettings settings;
    850         settings.Set<Setting::InstallerTypeRequirement>({ InstallerTypeEnum::Inno });
    851 
    852         ManifestComparator mc(GetManifestComparatorOptions(ManifestComparatorTestContext{}, {}));
    853         auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    854 
    855         REQUIRE(!result);
    856         RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType, InapplicabilityFlags::InstallerType });
    857     }
    858 }
    859 
    860 TEST_CASE("ManifestComparator_MachineArchitecture_Strong_Scope_Weak", "[manifest_comparator]")
    861 {
    862     Manifest manifest;
    863     ManifestInstaller system = AddInstaller(manifest, GetSystemArchitecture(), InstallerTypeEnum::Msi, ScopeEnum::Unknown, "", "");
    864     ManifestInstaller user = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::User, "", "");
    865 
    866     ManifestComparatorTestContext context;
    867 
    868     ManifestComparator mc(GetManifestComparatorOptions(context, {}));
    869     auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    870 
    871     RequireInstaller(result, system);
    872 }
    873 
    874 TEST_CASE("ManifestComparator_InstallerCompatibilitySet_Weaker_Than_Architecture", "[manifest_comparator]")
    875 {
    876     Manifest manifest;
    877     ManifestInstaller target = AddInstaller(manifest, GetSystemArchitecture(), InstallerTypeEnum::Wix, ScopeEnum::Unknown, "", "");
    878     ManifestInstaller foil = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Msi, ScopeEnum::Unknown, "", "");
    879 
    880     ManifestComparatorTestContext context;
    881 
    882     IPackageVersion::Metadata installationMetadata;
    883     installationMetadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(foil.EffectiveInstallerType());
    884 
    885     ManifestComparator mc(GetManifestComparatorOptions(context, installationMetadata));
    886     auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
    887 
    888     RequireInstaller(result, target);
    889 }