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 }