winget-cli

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

UpdateFlow.cpp (48335B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "WorkflowCommon.h"
      5 #include "TestHooks.h"
      6 #include <Commands/InstallCommand.h>
      7 #include <Commands/UninstallCommand.h>
      8 #include <Commands/UpgradeCommand.h>
      9 #include <winget/PathVariable.h>
     10 #include <Workflows/ShellExecuteInstallerHandler.h>
     11 
     12 using namespace TestCommon;
     13 using namespace AppInstaller::CLI;
     14 using namespace AppInstaller::CLI::Execution;
     15 using namespace AppInstaller::Settings;
     16 using namespace AppInstaller::Utility::literals;
     17 
     18 TEST_CASE("UpdateFlow_UpdateWithManifest", "[UpdateFlow][workflow]")
     19 {
     20     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
     21 
     22     std::ostringstream updateOutput;
     23     TestContext context{ updateOutput, std::cin };
     24     auto previousThreadGlobals = context.SetForCurrentThread();
     25     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
     26     OverrideForShellExecute(context);
     27     context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("UpdateFlowTest_Exe.yaml").GetPath().u8string());
     28 
     29     UpgradeCommand update({});
     30     update.Execute(context);
     31     INFO(updateOutput.str());
     32 
     33     // Verify Installer is called and parameters are passed in.
     34     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
     35     std::ifstream updateResultFile(updateResultPath.GetPath());
     36     REQUIRE(updateResultFile.is_open());
     37     std::string updateResultStr;
     38     std::getline(updateResultFile, updateResultStr);
     39     REQUIRE(updateResultStr.find("/update") != std::string::npos);
     40     REQUIRE(updateResultStr.find("/silentwithprogress") != std::string::npos);
     41 }
     42 
     43 TEST_CASE("UpdateFlow_UpdateWithManifestMSStore", "[UpdateFlow][workflow]")
     44 {
     45     TestCommon::TempFile updateResultPath("TestMSStoreUpdated.txt");
     46 
     47     std::ostringstream updateOutput;
     48     TestContext context{ updateOutput, std::cin };
     49     auto previousThreadGlobals = context.SetForCurrentThread();
     50     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_MSStore }));
     51     OverrideForMSStore(context, true);
     52     context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_MSStore.yaml").GetPath().u8string());
     53 
     54     UpgradeCommand update({});
     55     update.Execute(context);
     56     INFO(updateOutput.str());
     57 
     58     // Verify Installer is called and parameters are passed in.
     59     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
     60     std::ifstream updateResultFile(updateResultPath.GetPath());
     61     REQUIRE(updateResultFile.is_open());
     62     std::string updateResultStr;
     63     std::getline(updateResultFile, updateResultStr);
     64     REQUIRE(updateResultStr.find("9WZDNCRFJ364") != std::string::npos);
     65 }
     66 
     67 TEST_CASE("UpdateFlow_UpdateWithManifestAppNotInstalled", "[UpdateFlow][workflow]")
     68 {
     69     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
     70 
     71     std::ostringstream updateOutput;
     72     TestContext context{ updateOutput, std::cin };
     73     auto previousThreadGlobals = context.SetForCurrentThread();
     74     OverrideForCompositeInstalledSource(context, CreateTestSource({}));
     75     context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml").GetPath().u8string());
     76 
     77     UpgradeCommand update({});
     78     update.Execute(context);
     79     INFO(updateOutput.str());
     80 
     81     // Verify Installer is not called.
     82     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
     83     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::NoInstalledPackageFound).get()) != std::string::npos);
     84     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND);
     85 }
     86 
     87 TEST_CASE("UpdateFlow_UpdateWithManifestVersionAlreadyInstalled", "[UpdateFlow][workflow]")
     88 {
     89     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
     90 
     91     std::ostringstream updateOutput;
     92     TestContext context{ updateOutput, std::cin };
     93     auto previousThreadGlobals = context.SetForCurrentThread();
     94     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
     95     context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string());
     96 
     97     UpgradeCommand update({});
     98     update.Execute(context);
     99     INFO(updateOutput.str());
    100 
    101     // Verify Installer is not called.
    102     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    103     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos);
    104     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos);
    105     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    106 }
    107 
    108 TEST_CASE("UpdateFlow_UpdateExe", "[UpdateFlow][workflow]")
    109 {
    110     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    111 
    112     std::ostringstream updateOutput;
    113     TestContext context{ updateOutput, std::cin };
    114     auto previousThreadGlobals = context.SetForCurrentThread();
    115     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
    116     OverrideForShellExecute(context);
    117     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query);
    118     context.Args.AddArg(Execution::Args::Type::Silent);
    119 
    120     UpgradeCommand update({});
    121     update.Execute(context);
    122     INFO(updateOutput.str());
    123 
    124     // Verify Installer is called and parameters are passed in.
    125     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    126     std::ifstream updateResultFile(updateResultPath.GetPath());
    127     REQUIRE(updateResultFile.is_open());
    128     std::string updateResultStr;
    129     std::getline(updateResultFile, updateResultStr);
    130     REQUIRE(updateResultStr.find("/update") != std::string::npos);
    131     REQUIRE(updateResultStr.find("/silence") != std::string::npos);
    132     REQUIRE(updateResultStr.find("/ver3.0.0.0") != std::string::npos);
    133 }
    134 
    135 TEST_CASE("UpdateFlow_UpdateZip_Exe", "[UpdateFlow][workflow]")
    136 {
    137     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    138 
    139     std::ostringstream updateOutput;
    140     TestContext context{ updateOutput, std::cin };
    141     auto previousThreadGlobals = context.SetForCurrentThread();
    142     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Zip }));
    143     OverrideForShellExecute(context);
    144     OverrideForExtractInstallerFromArchive(context);
    145     OverrideForVerifyAndSetNestedInstaller(context);
    146     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Zip.Query);
    147     context.Args.AddArg(Execution::Args::Type::Silent);
    148 
    149     UpgradeCommand update({});
    150     update.Execute(context);
    151     INFO(updateOutput.str());
    152 
    153     // Verify Installer is called and parameters are passed in.
    154     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    155     std::ifstream updateResultFile(updateResultPath.GetPath());
    156     REQUIRE(updateResultFile.is_open());
    157     std::string updateResultStr;
    158     std::getline(updateResultFile, updateResultStr);
    159     REQUIRE(updateResultStr.find("/custom") != std::string::npos);
    160     REQUIRE(updateResultStr.find("/silence") != std::string::npos);
    161     REQUIRE(updateResultStr.find("/ver2.0.0.0") != std::string::npos);
    162 }
    163 
    164 TEST_CASE("UpdateFlow_UpdatePortable", "[UpdateFlow][workflow]")
    165 {
    166     TestCommon::TempFile updateResultPath("TestPortableInstalled.txt");
    167 
    168     std::ostringstream updateOutput;
    169     TestContext context{ updateOutput, std::cin };
    170     auto previousThreadGlobals = context.SetForCurrentThread();
    171     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable }));
    172     OverrideForPortableInstallFlow(context);
    173     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query);
    174 
    175     UpgradeCommand update({});
    176     update.Execute(context);
    177     INFO(updateOutput.str());
    178     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    179 }
    180 
    181 TEST_CASE("UpdateFlow_Portable_SymlinkCreationFail", "[UpdateFlow][workflow]")
    182 {
    183     // Update portable with symlink creation failure verify that it succeeds.
    184     TestCommon::TempDirectory tempDirectory("TestPortableInstallRoot", false);
    185     std::ostringstream updateOutput;
    186     TestContext context{ updateOutput, std::cin };
    187     auto PreviousThreadGlobals = context.SetForCurrentThread();
    188     bool overrideCreateSymlinkStatus = false;
    189     AppInstaller::Filesystem::TestHook_SetCreateSymlinkResult_Override(&overrideCreateSymlinkStatus);
    190     OverridePortableInstaller(context);
    191     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable }));
    192     const auto& targetDirectory = tempDirectory.GetPath();
    193     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query);
    194     context.Args.AddArg(Execution::Args::Type::InstallLocation, targetDirectory.u8string());
    195     context.Args.AddArg(Execution::Args::Type::InstallScope, "user"sv);
    196 
    197     UpgradeCommand update({});
    198     update.Execute(context);
    199     INFO(updateOutput.str());
    200     const auto& portableTargetPath = targetDirectory / "AppInstallerTestExeInstaller.exe";
    201     REQUIRE(std::filesystem::exists(portableTargetPath));
    202     REQUIRE(AppInstaller::Registry::Environment::PathVariable(AppInstaller::Manifest::ScopeEnum::User).Contains(targetDirectory));
    203 
    204     // Perform uninstall
    205     std::ostringstream uninstallOutput;
    206     TestContext uninstallContext{ uninstallOutput, std::cin };
    207     auto previousThreadGlobals = uninstallContext.SetForCurrentThread();
    208     OverrideForCompositeInstalledSource(uninstallContext, CreateTestSource({ TSR::TestInstaller_Portable }));
    209     uninstallContext.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Portable.Query);
    210 
    211     UninstallCommand uninstall({});
    212     uninstall.Execute(uninstallContext);
    213     INFO(uninstallOutput.str());
    214 
    215     REQUIRE_FALSE(std::filesystem::exists(portableTargetPath));
    216 }
    217 
    218 TEST_CASE("UpdateFlow_UpdateExeWithUnsupportedArgs", "[UpdateFlow][workflow]")
    219 {
    220     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    221     TestCommon::TempDirectory tempDirectory("TempDirectory", false);
    222 
    223     std::ostringstream updateOutput;
    224     TestContext context{ updateOutput, std::cin };
    225     auto previousThreadGlobals = context.SetForCurrentThread();
    226     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UnsupportedArguments }));
    227     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UnsupportedArguments.Query);
    228     context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory);
    229 
    230     UpgradeCommand update({});
    231     context.SetExecutingCommand(&update);
    232     update.Execute(context);
    233     INFO(updateOutput.str());
    234 
    235     // Verify unsupported arguments error message is shown 
    236     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UNSUPPORTED_ARGUMENT);
    237     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    238     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UnsupportedArgument).get()) != std::string::npos);
    239     REQUIRE(updateOutput.str().find("-l,--location") != std::string::npos);
    240 }
    241 
    242 TEST_CASE("UpdateFlow_UnknownVersion", "[UpdateFlow][workflow]")
    243 {
    244     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    245     TestCommon::TempDirectory tempDirectory("TempDirectory", false);
    246 
    247     std::ostringstream updateOutput;
    248     TestContext context{ updateOutput, std::cin };
    249     auto previousThreadGlobals = context.SetForCurrentThread();
    250     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UnknownVersion }));
    251     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UnknownVersion.Query);
    252     context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory);
    253 
    254     UpgradeCommand update({});
    255     context.SetExecutingCommand(&update);
    256     update.Execute(context);
    257     INFO(updateOutput.str());
    258 
    259     // Verify help message is shown the user to use --include-unknown
    260     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    261     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    262     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeUnknownVersionExplanation).get()) != std::string::npos);
    263 }
    264 
    265 TEST_CASE("UpdateFlow_UnknownVersion_IncludeUnknownArg", "[UpdateFlow][workflow]")
    266 {
    267     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    268     TestCommon::TempDirectory tempDirectory("TempDirectory", false);
    269 
    270     std::ostringstream updateOutput;
    271     TestContext context{ updateOutput, std::cin };
    272     auto previousThreadGlobals = context.SetForCurrentThread();
    273     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UnknownVersion }));
    274     OverrideForShellExecute(context);
    275     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UnknownVersion.Query);
    276     context.Args.AddArg(Execution::Args::Type::InstallLocation, tempDirectory);
    277     context.Args.AddArg(Execution::Args::Type::IncludeUnknown);
    278 
    279     UpgradeCommand update({});
    280     context.SetExecutingCommand(&update);
    281     update.Execute(context);
    282     INFO(updateOutput.str());
    283     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    284 }
    285 
    286 TEST_CASE("UpdateFlow_NoArgs_UnknownVersion", "[UpdateFlow][workflow]")
    287 {
    288     std::ostringstream updateOutput;
    289     TestContext context{ updateOutput, std::cin };
    290     auto previousThreadGlobals = context.SetForCurrentThread();
    291     OverrideForCompositeInstalledSource(context, CreateTestSource({
    292         TSR::TestInstaller_Exe,
    293         TSR::TestInstaller_Exe_UnknownVersion,
    294         TSR::TestInstaller_Msix,
    295         TSR::TestInstaller_MSStore,
    296         TSR::TestInstaller_Portable,
    297         TSR::TestInstaller_Zip,
    298         }));
    299 
    300     UpgradeCommand update({});
    301     context.SetExecutingCommand(&update);
    302     update.Execute(context);
    303     INFO(updateOutput.str());
    304 
    305     // Verify --include-unknown help text is displayed if update is executed with no args and an unknown version package is available for upgrade.
    306     REQUIRE(updateOutput.str().find(Resource::String::UpgradeUnknownVersionCount(1)) != std::string::npos);
    307 }
    308 
    309 TEST_CASE("UpdateFlow_IncludeUnknown", "[UpdateFlow][workflow]")
    310 {
    311     std::ostringstream updateOutput;
    312     TestContext context{ updateOutput, std::cin };
    313     auto previousThreadGlobals = context.SetForCurrentThread();
    314     OverrideForCompositeInstalledSource(context, CreateTestSource({
    315         TSR::TestInstaller_Exe,
    316         TSR::TestInstaller_Exe_UnknownVersion,
    317         TSR::TestInstaller_Msix,
    318         TSR::TestInstaller_MSStore,
    319         TSR::TestInstaller_Portable,
    320         TSR::TestInstaller_Zip,
    321         }));
    322     context.Args.AddArg(Execution::Args::Type::IncludeUnknown);
    323 
    324     UpgradeCommand update({});
    325     context.SetExecutingCommand(&update);
    326     update.Execute(context);
    327     INFO(updateOutput.str());
    328 
    329     // Verify unknown version package is displayed available for upgrade.
    330     REQUIRE(updateOutput.str().find(Resource::String::UpgradeUnknownVersionCount(1)) == std::string::npos);
    331     REQUIRE(updateOutput.str().find("unknown") != std::string::npos);
    332 }
    333 
    334 TEST_CASE("UpdateFlow_UpdatePortableWithManifest", "[UpdateFlow][workflow]")
    335 {
    336     TestCommon::TempFile updateResultPath("TestPortableInstalled.txt");
    337 
    338     std::ostringstream updateOutput;
    339     TestContext context{ updateOutput, std::cin };
    340     auto previousThreadGlobals = context.SetForCurrentThread();
    341     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Portable }));
    342     OverrideForPortableInstallFlow(context);
    343     context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("UpdateFlowTest_Portable.yaml").GetPath().u8string());
    344 
    345     UpgradeCommand update({});
    346     update.Execute(context);
    347     INFO(updateOutput.str());
    348     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    349 }
    350 
    351 TEST_CASE("UpdateFlow_UpdateMsix", "[UpdateFlow][workflow]")
    352 {
    353     TestCommon::TempFile updateResultPath("TestMsixInstalled.txt");
    354 
    355     std::ostringstream updateOutput;
    356     TestContext context{ updateOutput, std::cin };
    357     auto previousThreadGlobals = context.SetForCurrentThread();
    358     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Msix }));
    359     OverrideForMSIX(context);
    360     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Msix.Query);
    361 
    362     UpgradeCommand update({});
    363     update.Execute(context);
    364     INFO(updateOutput.str());
    365 
    366     // Verify Installer is called.
    367     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    368 }
    369 
    370 TEST_CASE("UpdateFlow_UpdateMSStore", "[UpdateFlow][workflow]")
    371 {
    372     TestCommon::TempFile updateResultPath("TestMSStoreUpdated.txt");
    373 
    374     std::ostringstream updateOutput;
    375     TestContext context{ updateOutput, std::cin };
    376     auto previousThreadGlobals = context.SetForCurrentThread();
    377     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_MSStore }));
    378     OverrideForMSStore(context, true);
    379     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_MSStore.Query);
    380 
    381     UpgradeCommand update({});
    382     update.Execute(context);
    383     INFO(updateOutput.str());
    384 
    385     // Verify Installer is called.
    386     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    387     std::ifstream updateResultFile(updateResultPath.GetPath());
    388     REQUIRE(updateResultFile.is_open());
    389     std::string updateResultStr;
    390     std::getline(updateResultFile, updateResultStr);
    391     REQUIRE(updateResultStr.find("9WZDNCRFJ364") != std::string::npos);
    392 }
    393 
    394 TEST_CASE("UpdateFlow_UpdateExeLatestAlreadyInstalled", "[UpdateFlow][workflow]")
    395 {
    396     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    397 
    398     std::ostringstream updateOutput;
    399     TestContext context{ updateOutput, std::cin };
    400     auto previousThreadGlobals = context.SetForCurrentThread();
    401     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LatestInstalled }));
    402     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LatestInstalled.Query);
    403 
    404     UpgradeCommand update({});
    405     update.Execute(context);
    406     INFO(updateOutput.str());
    407 
    408     // Verify Installer is not called.
    409     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    410     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos);
    411     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos);
    412     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    413 }
    414 
    415 TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicable", "[UpdateFlow][workflow]")
    416 {
    417     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    418 
    419     std::ostringstream updateOutput;
    420     TestContext context{ updateOutput, std::cin };
    421     auto previousThreadGlobals = context.SetForCurrentThread();
    422     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType }));
    423     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query);
    424 
    425     UpgradeCommand update({});
    426     update.Execute(context);
    427     INFO(updateOutput.str());
    428 
    429     // Verify Installer is not called.
    430     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    431     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnologyInNewerVersions).get()) != std::string::npos);
    432     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    433 }
    434 
    435 TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicableSpecificVersion", "[UpdateFlow][workflow]")
    436 {
    437     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    438 
    439     std::ostringstream updateOutput;
    440     TestContext context{ updateOutput, std::cin };
    441     auto previousThreadGlobals = context.SetForCurrentThread();
    442     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType }));
    443     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query);
    444     context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv);
    445 
    446     UpgradeCommand update({});
    447     update.Execute(context);
    448     INFO(updateOutput.str());
    449 
    450     // Verify Installer is not called.
    451     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    452     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnology).get()) != std::string::npos);
    453     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    454 }
    455 
    456 TEST_CASE("UpdateFlow_UpdateExeWithDifferentInstalledType", "[UpdateFlow][workflow]")
    457 {
    458     // Tests installer applicability when installed type is different but listed in the manifest
    459     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    460 
    461     std::ostringstream updateOutput;
    462     TestContext context{ updateOutput, std::cin };
    463     auto previousThreadGlobals = context.SetForCurrentThread();
    464     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_DifferentInstallerType }));
    465     OverrideForShellExecute(context);
    466     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_DifferentInstallerType.Query);
    467 
    468     UpgradeCommand update({});
    469     update.Execute(context);
    470     INFO(updateOutput.str());
    471 
    472     // Verify Installer is called.
    473     REQUIRE(context.GetTerminationHR() == S_OK);
    474     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    475 }
    476 
    477 TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotFound", "[UpdateFlow][workflow]")
    478 {
    479     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    480 
    481     std::ostringstream updateOutput;
    482     TestContext context{ updateOutput, std::cin };
    483     auto previousThreadGlobals = context.SetForCurrentThread();
    484     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
    485     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query);
    486     context.Args.AddArg(Execution::Args::Type::Version, "1.2.3.4"sv);
    487 
    488     UpgradeCommand update({});
    489     update.Execute(context);
    490     INFO(updateOutput.str());
    491 
    492     // Verify Installer is not called.
    493     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    494     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::GetManifestResultVersionNotFound("1.2.3.4"_liv)).get()) != std::string::npos);
    495     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND);
    496 }
    497 
    498 TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotApplicable", "[UpdateFlow][workflow]")
    499 {
    500     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    501 
    502     std::ostringstream updateOutput;
    503     TestContext context{ updateOutput, std::cin };
    504     auto previousThreadGlobals = context.SetForCurrentThread();
    505     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_IncompatibleInstallerType }));
    506     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_IncompatibleInstallerType.Query);
    507     // This must be 2.0.0.0 since the version would not be an upgrade otherwise
    508     context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv);
    509 
    510     UpgradeCommand update({});
    511     update.Execute(context);
    512     INFO(updateOutput.str());
    513 
    514     // Verify Installer is not called.
    515     REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
    516     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnology).get()) != std::string::npos);
    517     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    518 }
    519 
    520 TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]")
    521 {
    522     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    523     TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt");
    524     TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt");
    525     TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt");
    526 
    527     std::ostringstream updateOutput;
    528     TestContext context{ updateOutput, std::cin };
    529     auto previousThreadGlobals = context.SetForCurrentThread();
    530     OverrideForCompositeInstalledSource(context, CreateTestSource({
    531         TSR::TestInstaller_Exe,
    532         TSR::TestInstaller_Exe_UnknownVersion,
    533         TSR::TestInstaller_Msix,
    534         TSR::TestInstaller_MSStore,
    535         TSR::TestInstaller_Portable,
    536         TSR::TestInstaller_Zip,
    537         }));
    538     OverrideForShellExecute(context);
    539     OverrideForMSIX(context);
    540     OverrideForMSStore(context, true);
    541     OverrideForPortableInstall(context);
    542     context.Args.AddArg(Execution::Args::Type::All);
    543 
    544     UpgradeCommand update({});
    545     update.Execute(context);
    546     INFO(updateOutput.str());
    547 
    548     // Verify that --include-unknown help message is displayed.
    549     REQUIRE(updateOutput.str().find(Resource::String::UpgradeUnknownVersionCount(1)) != std::string::npos);
    550     REQUIRE(updateOutput.str().find("AppInstallerCliTest.TestExeUnknownVersion") == std::string::npos);
    551 
    552     // Verify installers are called.
    553     REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath()));
    554     REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath()));
    555     REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath()));
    556     REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath()));
    557 }
    558 
    559 TEST_CASE("UpdateFlow_UpdateAll_IncludeUnknown", "[UpdateFlow][workflow]")
    560 {
    561     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    562     TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt");
    563     TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt");
    564     TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt");
    565 
    566     std::ostringstream updateOutput;
    567     TestContext context{ updateOutput, std::cin };
    568     auto previousThreadGlobals = context.SetForCurrentThread();
    569     OverrideForCompositeInstalledSource(context, CreateTestSource({
    570         TSR::TestInstaller_Exe,
    571         TSR::TestInstaller_Exe_UnknownVersion,
    572         TSR::TestInstaller_Msix,
    573         TSR::TestInstaller_MSStore,
    574         TSR::TestInstaller_Portable,
    575         TSR::TestInstaller_Zip,
    576         }));
    577     OverrideForShellExecute(context);
    578     OverrideForMSIX(context);
    579     OverrideForMSStore(context, true);
    580     OverrideForPortableInstall(context);
    581     context.Args.AddArg(Execution::Args::Type::All);
    582     context.Args.AddArg(Execution::Args::Type::IncludeUnknown);
    583 
    584     UpgradeCommand update({});
    585     update.Execute(context);
    586     INFO(updateOutput.str());
    587 
    588     // Verify that --include-unknown help message is NOT displayed and unknown version package is upgraded.
    589     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeUnknownVersionCount).get()) == std::string::npos);
    590     REQUIRE(updateOutput.str().find("AppInstallerCliTest.TestExeUnknownVersion") != std::string::npos);
    591 
    592     // Verify installers are called.
    593     REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath()));
    594     REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath()));
    595     REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath()));
    596     REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath()));
    597 }
    598 
    599 TEST_CASE("UpdateFlow_UpgradeWithDuplicateUpgradeItemsFound", "[UpdateFlow][workflow]")
    600 {
    601     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    602 
    603     std::ostringstream updateOutput;
    604     TestContext context{ updateOutput, std::cin };
    605     auto previousThreadGlobals = context.SetForCurrentThread();
    606     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_UpgradeAllWithDuplicateUpgradeItems }));
    607     // Installer should only be run once since the 2 upgrade items are same.
    608     OverrideForShellExecute(context, 1);
    609     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_UpgradeAllWithDuplicateUpgradeItems.Query);
    610     context.Args.AddArg(Execution::Args::Type::All);
    611 
    612     UpgradeCommand update({});
    613     update.Execute(context);
    614     INFO(updateOutput.str());
    615 
    616     // Verify installers are called.
    617     REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath()));
    618 }
    619 
    620 TEST_CASE("UpdateFlow_Dependencies", "[UpdateFlow][workflow][dependencies]")
    621 {
    622     std::ostringstream updateOutput;
    623     TestContext context{ updateOutput, std::cin };
    624     auto previousThreadGlobals = context.SetForCurrentThread();
    625     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_Dependencies }));
    626     OverrideForShellExecute(context);
    627     OverrideEnableWindowsFeaturesDependencies(context);
    628 
    629     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_Dependencies.Query);;
    630 
    631     UpgradeCommand update({});
    632     update.Execute(context);
    633     INFO(updateOutput.str());
    634 
    635     std::string updateResultStr = updateOutput.str();
    636 
    637     // Verify dependencies are informed
    638     REQUIRE(updateResultStr.find(Resource::LocString(Resource::String::PackageRequiresDependencies).get()) != std::string::npos);
    639     REQUIRE(updateResultStr.find("PreviewIIS") != std::string::npos);
    640     REQUIRE(updateResultStr.find("Preview VC Runtime") != std::string::npos);
    641 }
    642 
    643 TEST_CASE("UpdateFlow_LicenseAgreement", "[UpdateFlow][workflow]")
    644 {
    645     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    646 
    647     std::ostringstream updateOutput;
    648     TestContext context{ updateOutput, std::cin };
    649     auto previousThreadGlobals = context.SetForCurrentThread();
    650     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LicenseAgreement }));
    651     OverrideForShellExecute(context);
    652     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LicenseAgreement.Query);
    653     context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements);
    654 
    655     UpgradeCommand update({});
    656     update.Execute(context);
    657     INFO(updateOutput.str());
    658 
    659     // Verify agreements are shown
    660     REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos);
    661     REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos);
    662 
    663     // Verify Installer is called.
    664     REQUIRE(std::filesystem::exists(updateResultPath.GetPath()));
    665 }
    666 
    667 TEST_CASE("UpdateFlow_LicenseAgreement_NotAccepted", "[UpdateFlow][workflow]")
    668 {
    669     TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
    670 
    671     // Say "No" at the agreements prompt
    672     std::istringstream updateInput{ "n" };
    673 
    674     std::ostringstream updateOutput;
    675     TestContext context{ updateOutput, updateInput };
    676     auto previousThreadGlobals = context.SetForCurrentThread();
    677     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LicenseAgreement }));
    678     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LicenseAgreement.Query);
    679 
    680     UpgradeCommand update({});
    681     update.Execute(context);
    682     INFO(updateOutput.str());
    683 
    684     // Verify agreements are shown
    685     REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos);
    686     REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos);
    687 
    688     // Verify Installer is not called.
    689     REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED);
    690     REQUIRE_FALSE(std::filesystem::exists(updateResultPath.GetPath()));
    691     REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::PackageAgreementsNotAgreedTo).get()) != std::string::npos);
    692 }
    693 
    694 TEST_CASE("UpdateFlow_All_LicenseAgreement", "[UpdateFlow][workflow]")
    695 {
    696     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    697     TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt");
    698     TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt");
    699     TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt");
    700 
    701     std::ostringstream updateOutput;
    702     TestContext context{ updateOutput, std::cin };
    703     auto previousThreadGlobals = context.SetForCurrentThread();
    704     OverrideForCompositeInstalledSource(context, CreateTestSource({
    705         TSR::TestInstaller_Exe_UpgradeUsesAgreements,
    706         TSR::TestInstaller_Exe_UnknownVersion,
    707         TSR::TestInstaller_Msix_UpgradeUsesAgreements,
    708         TSR::TestInstaller_MSStore,
    709         TSR::TestInstaller_Portable,
    710         TSR::TestInstaller_Zip,
    711         }));
    712     OverrideForShellExecute(context);
    713     OverrideForMSIX(context);
    714     OverrideForMSStore(context, true);
    715     OverrideForPortableInstall(context);
    716     context.Args.AddArg(Execution::Args::Type::All);
    717     context.Args.AddArg(Execution::Args::Type::AcceptPackageAgreements);
    718 
    719     UpgradeCommand update({});
    720     update.Execute(context);
    721     INFO(updateOutput.str());
    722 
    723     // Verify agreements are shown
    724     REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos);
    725     REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos);
    726     REQUIRE(updateOutput.str().find("Agreement for MSIX") != std::string::npos);
    727     REQUIRE(updateOutput.str().find("This is the agreement for the MSIX") != std::string::npos);
    728 
    729     // Verify installers are called.
    730     REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath()));
    731     REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath()));
    732     REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath()));
    733     REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath()));
    734 }
    735 
    736 TEST_CASE("UpdateFlow_All_LicenseAgreement_NotAccepted", "[UpdateFlow][workflow]")
    737 {
    738     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    739     TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt");
    740     TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt");
    741 
    742     // Say "No" at the agreements prompt
    743     std::istringstream updateInput{ "n" };
    744 
    745     std::ostringstream updateOutput;
    746     TestContext context{ updateOutput, updateInput };
    747     auto previousThreadGlobals = context.SetForCurrentThread();
    748     OverrideForCompositeInstalledSource(context, CreateTestSource({
    749         TSR::TestInstaller_Exe_UpgradeUsesAgreements,
    750         TSR::TestInstaller_Exe_UnknownVersion,
    751         TSR::TestInstaller_Msix_UpgradeUsesAgreements,
    752         TSR::TestInstaller_MSStore,
    753         TSR::TestInstaller_Portable,
    754         TSR::TestInstaller_Zip,
    755         }));
    756     context.Args.AddArg(Execution::Args::Type::All);
    757 
    758     UpgradeCommand update({});
    759     update.Execute(context);
    760     INFO(updateOutput.str());
    761 
    762     // Verify agreements are shown
    763     REQUIRE(updateOutput.str().find("Agreement for EXE") != std::string::npos);
    764     REQUIRE(updateOutput.str().find("This is the agreement for the EXE") != std::string::npos);
    765     REQUIRE(updateOutput.str().find("Agreement for MSIX") != std::string::npos);
    766     REQUIRE(updateOutput.str().find("This is the agreement for the MSIX") != std::string::npos);
    767 
    768     // Verify installers are not called.
    769     REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED);
    770     REQUIRE_FALSE(std::filesystem::exists(updateExeResultPath.GetPath()));
    771     REQUIRE_FALSE(std::filesystem::exists(updateMsixResultPath.GetPath()));
    772     REQUIRE_FALSE(std::filesystem::exists(updateMSStoreResultPath.GetPath()));
    773 }
    774 
    775 TEST_CASE("UpdateFlow_RequireExplicit", "[UpdateFlow][workflow]")
    776 {
    777     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    778     TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt");
    779 
    780     std::ostringstream updateOutput;
    781     TestContext context{ updateOutput, std::cin };
    782     auto previousThreadGlobals = context.SetForCurrentThread();
    783 
    784     // Msix package has an update that requires explicit upgrade.
    785     // Exe, Portable, MSStore, Zip are also listed with an available upgrade.
    786     OverrideForCompositeInstalledSource(context, CreateTestSource({
    787         TSR::TestInstaller_Exe,
    788         TSR::TestInstaller_Exe_UnknownVersion,
    789         TSR::TestInstaller_Msix_UpgradeRequiresExplicit,
    790         TSR::TestInstaller_MSStore,
    791         TSR::TestInstaller_Portable,
    792         TSR::TestInstaller_Zip,
    793         }));
    794 
    795     SECTION("List available upgrades")
    796     {
    797         UpgradeCommand update({});
    798         update.Execute(context);
    799         INFO(updateOutput.str());
    800 
    801         // The package that requires explicit upgrade is listed below the header for pinned packages
    802         REQUIRE(updateOutput.str().find("AppInstallerCliTest.TestExeInstaller") != std::string::npos);
    803 
    804         auto pinnedPackagesHeaderPosition = updateOutput.str().find(Resource::LocString(Resource::String::UpgradeAvailableForPinned));
    805         auto pinnedPackageLinePosition = updateOutput.str().find("AppInstallerCliTest.TestMsixInstaller");
    806         REQUIRE(pinnedPackagesHeaderPosition != std::string::npos);
    807         REQUIRE(pinnedPackageLinePosition != std::string::npos);
    808         REQUIRE(pinnedPackagesHeaderPosition < pinnedPackageLinePosition);
    809         REQUIRE(updateOutput.str().find(Resource::String::UpgradeRequireExplicitCount(1)) == std::string::npos);
    810     }
    811 
    812     SECTION("Upgrade all except pinned")
    813     {
    814         context.Args.AddArg(Args::Type::All);
    815         OverrideForMSStore(context, true);
    816         OverrideForPortableInstall(context);
    817         OverrideForShellExecute(context);
    818         OverrideForExtractInstallerFromArchive(context);
    819         OverrideForVerifyAndSetNestedInstaller(context);
    820 
    821         UpgradeCommand update({});
    822         update.Execute(context);
    823         INFO(updateOutput.str());
    824 
    825         auto s = updateOutput.str();
    826 
    827         // Verify message is printed for skipped package
    828         REQUIRE(updateOutput.str().find(Resource::String::UpgradeRequireExplicitCount(1)) != std::string::npos);
    829 
    830         // Verify package is not installed, but all others are
    831         REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath()));
    832         REQUIRE(!std::filesystem::exists(updateMsixResultPath.GetPath()));
    833     }
    834 
    835     SECTION("Upgrade explicitly")
    836     {
    837         context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Msix.Query);
    838         OverrideForMSIX(context);
    839 
    840         UpgradeCommand update({});
    841         update.Execute(context);
    842         INFO(updateOutput.str());
    843 
    844         REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath()));
    845     }
    846 
    847     // Command should always succeed
    848     REQUIRE(context.GetTerminationHR() == S_OK);
    849 }
    850 
    851 TEST_CASE("InstallFlow_FoundInstalledAndUpgradeAvailable", "[UpdateFlow][workflow]")
    852 {
    853     TestCommon::TempFile installResultPath("TestExeInstalled.txt");
    854 
    855     std::ostringstream installOutput;
    856     TestContext context{ installOutput, std::cin };
    857     auto previousThreadGlobals = context.SetForCurrentThread();
    858     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
    859     OverrideForShellExecute(context);
    860     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query);
    861     context.Args.AddArg(Execution::Args::Type::Silent);
    862 
    863     InstallCommand install({});
    864     install.Execute(context);
    865     INFO(installOutput.str());
    866 
    867     // Verify Installer is called and parameters are passed in.
    868     REQUIRE(std::filesystem::exists(installResultPath.GetPath()));
    869     std::ifstream installResultFile(installResultPath.GetPath());
    870     REQUIRE(installResultFile.is_open());
    871     std::string installResultStr;
    872     std::getline(installResultFile, installResultStr);
    873     REQUIRE(installResultStr.find("/update") != std::string::npos);
    874     REQUIRE(installResultStr.find("/ver3.0.0.0") != std::string::npos);
    875 }
    876 
    877 TEST_CASE("InstallFlow_FoundInstalledAndUpgradeAvailable_WithNoUpgrade", "[UpdateFlow][workflow]")
    878 {
    879     TestCommon::TempFile installResultPath("TestExeInstalled.txt");
    880 
    881     std::ostringstream installOutput;
    882     TestContext context{ installOutput, std::cin };
    883     auto previousThreadGlobals = context.SetForCurrentThread();
    884     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
    885     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe.Query);
    886     context.Args.AddArg(Execution::Args::Type::NoUpgrade);
    887 
    888     InstallCommand install({});
    889     install.Execute(context);
    890     INFO(installOutput.str());
    891 
    892     // Verify Installer is not called.
    893     REQUIRE(!std::filesystem::exists(installResultPath.GetPath()));
    894     REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::PackageAlreadyInstalled).get()) != std::string::npos);
    895     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_PACKAGE_ALREADY_INSTALLED);
    896 }
    897 
    898 TEST_CASE("InstallFlow_FoundInstalledAndUpgradeNotAvailable", "[UpdateFlow][workflow]")
    899 {
    900     TestCommon::TempFile installResultPath("TestExeInstalled.txt");
    901 
    902     std::ostringstream installOutput;
    903     TestContext context{ installOutput, std::cin };
    904     auto previousThreadGlobals = context.SetForCurrentThread();
    905     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_LatestInstalled }));
    906     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_LatestInstalled.Query);
    907 
    908     InstallCommand install({});
    909     install.Execute(context);
    910     INFO(installOutput.str());
    911 
    912     // Verify Installer is not called.
    913     REQUIRE(!std::filesystem::exists(installResultPath.GetPath()));
    914     REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFound).get()) != std::string::npos);
    915     REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::UpdateNoPackagesFoundReason).get()) != std::string::npos);
    916     REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
    917 }
    918 
    919 TEST_CASE("UpdateFlow_UpdateAll_ForwardArgs", "[UpdateFlow][workflow]")
    920 {
    921     TestCommon::TempFile updateExeResultPath("TestExeInstalled.txt");
    922     TestCommon::TempFile updateMsixResultPath("TestMsixInstalled.txt");
    923     TestCommon::TempFile updateMSStoreResultPath("TestMSStoreUpdated.txt");
    924     TestCommon::TempFile updatePortableResultPath("TestPortableInstalled.txt");
    925 
    926     std::ostringstream updateOutput;
    927     TestContext context{ updateOutput, std::cin };
    928     auto previousThreadGlobals = context.SetForCurrentThread();
    929     OverrideForCompositeInstalledSource(context, CreateTestSource({
    930         TSR::TestInstaller_Exe,
    931         TSR::TestInstaller_Msix,
    932         TSR::TestInstaller_MSStore,
    933         TSR::TestInstaller_Portable,
    934         }));
    935     OverrideForShellExecute(context);
    936     OverrideForMSIX(context);
    937     OverrideForMSStore(context, true);
    938     OverrideForPortableInstall(context);
    939     context.Args.AddArg(Execution::Args::Type::All);
    940     context.Args.AddArg(Execution::Args::Type::Silent);
    941 
    942     UpgradeCommand update({});
    943     update.Execute(context);
    944     INFO(updateOutput.str());
    945 
    946     // Verify installers are called with the silent flags
    947     REQUIRE(std::filesystem::exists(updateExeResultPath.GetPath()));
    948     std::ifstream updateExeResultFile(updateExeResultPath.GetPath());
    949     std::string updateExeResultStr;
    950     std::getline(updateExeResultFile, updateExeResultStr);
    951     REQUIRE(updateExeResultStr.find("/silence") != std::string::npos);
    952 
    953     REQUIRE(std::filesystem::exists(updateMsixResultPath.GetPath()));
    954     REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath()));
    955     REQUIRE(std::filesystem::exists(updatePortableResultPath.GetPath()));
    956 }
    957 
    958 TEST_CASE("UpdateFlow_UpdateMultiple", "[UpdateFlow][workflow][MultiQuery]")
    959 {
    960     TestCommon::TempFile exeUpdateResultPath("TestExeInstalled.txt");
    961     TestCommon::TempFile msixUpdateResultPath("TestMsixInstalled.txt");
    962 
    963     std::ostringstream updateOutput;
    964     TestContext context{ updateOutput, std::cin };
    965     auto previousThreadGlobals = context.SetForCurrentThread();
    966     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe, TSR::TestInstaller_Msix }));
    967     OverrideForShellExecute(context);
    968     OverrideForMSIX(context);
    969     context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query);
    970     context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query);
    971 
    972     UpgradeCommand update({});
    973     update.Execute(context);
    974     INFO(updateOutput.str());
    975 
    976     // Verify Installers are called called.
    977     REQUIRE(std::filesystem::exists(exeUpdateResultPath.GetPath()));
    978     REQUIRE(std::filesystem::exists(exeUpdateResultPath.GetPath()));
    979 }
    980 
    981 TEST_CASE("UpdateFlow_UpdateMultiple_NotAllFound", "[UpdateFlow][workflow][MultiQuery]")
    982 {
    983     TestCommon::TempFile exeUpdateResultPath("TestExeInstalled.txt");
    984 
    985     std::ostringstream updateOutput;
    986     TestContext context{ updateOutput, std::cin };
    987     auto previousThreadGlobals = context.SetForCurrentThread();
    988     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe }));
    989     context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Exe.Query);
    990     context.Args.AddArg(Execution::Args::Type::MultiQuery, TSR::TestInstaller_Msix.Query);
    991 
    992     SECTION("Ignore unavailable")
    993     {
    994         OverrideForShellExecute(context);
    995         context.Args.AddArg(Execution::Args::Type::IgnoreUnavailable);
    996 
    997         UpgradeCommand update({});
    998         update.Execute(context);
    999         INFO(updateOutput.str());
   1000 
   1001         REQUIRE(!context.IsTerminated());
   1002         REQUIRE(std::filesystem::exists(exeUpdateResultPath.GetPath()));
   1003     }
   1004     SECTION("Don't ignore unavailable")
   1005     {
   1006         UpgradeCommand update({});
   1007         update.Execute(context);
   1008         INFO(updateOutput.str());
   1009 
   1010         REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_QUERIES_FOUND_SINGLE);
   1011     }
   1012 }
   1013 
   1014 TEST_CASE("UpdateFlow_UpdateWithReboot", "[UpdateFlow][workflow][reboot]")
   1015 {
   1016     TestCommon::TestUserSettings testSettings;
   1017 
   1018     std::ostringstream updateOutput;
   1019     TestContext context{ updateOutput, std::cin };
   1020     auto previousThreadGlobals = context.SetForCurrentThread();
   1021     OverrideForShellExecute(context);
   1022     OverrideForCompositeInstalledSource(context, CreateTestSource({ TSR::TestInstaller_Exe_ExpectedReturnCodes }));
   1023 
   1024     context.Args.AddArg(Execution::Args::Type::Query, TSR::TestInstaller_Exe_ExpectedReturnCodes.Query);
   1025     context.Args.AddArg(Execution::Args::Type::AllowReboot);
   1026 
   1027     context.Override({ AppInstaller::CLI::Workflow::ShellExecuteInstallImpl, [&](TestContext& context)
   1028     {
   1029         // APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH (not treated as an installer error)
   1030         context.Add<Data::OperationReturnCode>(9);
   1031     } });
   1032 
   1033     SECTION("Reboot success")
   1034     {
   1035         TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(true);
   1036 
   1037         UpgradeCommand update({});
   1038         update.Execute(context);
   1039         INFO(updateOutput.str());
   1040 
   1041         REQUIRE_FALSE(context.IsTerminated());
   1042         REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::InitiatingReboot).get()) != std::string::npos);
   1043         REQUIRE_FALSE(updateOutput.str().find(Resource::LocString(Resource::String::FailedToInitiateReboot).get()) != std::string::npos);
   1044     }
   1045     SECTION("Reboot failed")
   1046     {
   1047         TestHook::SetInitiateRebootResult_Override initiateRebootResultOverride(false);
   1048 
   1049         UpgradeCommand update({});
   1050         update.Execute(context);
   1051         INFO(updateOutput.str());
   1052 
   1053         REQUIRE_FALSE(context.IsTerminated());
   1054         REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::InitiatingReboot).get()) != std::string::npos);
   1055         REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::FailedToInitiateReboot).get()) != std::string::npos);
   1056     }
   1057 }