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 }