InstallCommand.cs (40764B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="InstallCommand.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace AppInstallerCLIE2ETests 8 { 9 using System; 10 using System.IO; 11 using AppInstallerCLIE2ETests.Helpers; 12 using NUnit.Framework; 13 14 /// <summary> 15 /// Test install command. 16 /// </summary> 17 public class InstallCommand : BaseCommand 18 { 19 /// <summary> 20 /// Set up. 21 /// </summary> 22 [SetUp] 23 public void Setup() 24 { 25 // Try clean up TestExeInstaller for failure cases where cleanup is not successful 26 TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); 27 } 28 29 /// <summary> 30 /// Test package doesn't exist. 31 /// </summary> 32 [Test] 33 public void InstallAppDoesNotExist() 34 { 35 var result = TestCommon.RunAICLICommand("install", "DoesNotExist"); 36 Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); 37 Assert.True(result.StdOut.Contains("No package found matching input criteria.")); 38 } 39 40 /// <summary> 41 /// Test multiple matches found. 42 /// </summary> 43 [Test] 44 public void InstallWithMultipleAppsMatchingQuery() 45 { 46 var result = TestCommon.RunAICLICommand("install", "TestExeInstaller"); 47 Assert.AreEqual(Constants.ErrorCode.ERROR_MULTIPLE_APPLICATIONS_FOUND, result.ExitCode); 48 Assert.True(result.StdOut.Contains("Multiple packages found matching input criteria. Please refine the input.")); 49 } 50 51 /// <summary> 52 /// Test install exe. 53 /// </summary> 54 [Test] 55 public void InstallExe() 56 { 57 var installDir = TestCommon.GetRandomTestDir(); 58 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); 59 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 60 Assert.True(result.StdOut.Contains("Successfully installed")); 61 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); 62 } 63 64 /// <summary> 65 /// Test inapplicable os version. 66 /// </summary> 67 [Test] 68 public void InstallExeWithInsufficientMinOsVersion() 69 { 70 var installDir = TestCommon.GetRandomTestDir(); 71 var result = TestCommon.RunAICLICommand("install", $"InapplicableOsVersion --silent -l {installDir}"); 72 73 // MinOSVersion is moved to installer level, the check is performed during installer selection 74 Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICABLE_INSTALLER, result.ExitCode); 75 Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); 76 } 77 78 /// <summary> 79 /// Test install exe hash mismatch. 80 /// </summary> 81 [Test] 82 public void InstallExeWithHashMismatch() 83 { 84 var installDir = TestCommon.GetRandomTestDir(); 85 var result = TestCommon.RunAICLICommand("install", $"TestExeSha256Mismatch --silent -l {installDir}"); 86 Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_HASH_MISMATCH, result.ExitCode); 87 Assert.True(result.StdOut.Contains("Installer hash does not match")); 88 Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); 89 } 90 91 /// <summary> 92 /// Test install inno. 93 /// </summary> 94 [Test] 95 public void InstallWithInno() 96 { 97 // Install test inno, manifest does not provide silent switch, we should be populating the default 98 var installDir = TestCommon.GetRandomTestDir(); 99 var result = TestCommon.RunAICLICommand("install", $"TestInnoInstaller --silent -l {installDir}"); 100 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 101 Assert.True(result.StdOut.Contains("Successfully installed")); 102 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/VERYSILENT")); 103 } 104 105 /// <summary> 106 /// Test install burn. 107 /// </summary> 108 [Test] 109 public void InstallBurn() 110 { 111 // Install test burn, manifest does not provide silent switch, we should be populating the default 112 var installDir = TestCommon.GetRandomTestDir(); 113 var result = TestCommon.RunAICLICommand("install", $"TestBurnInstaller --silent -l {installDir}"); 114 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 115 Assert.True(result.StdOut.Contains("Successfully installed")); 116 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/quiet")); 117 } 118 119 /// <summary> 120 /// Test install nullsoft. 121 /// </summary> 122 [Test] 123 public void InstallNullSoft() 124 { 125 // Install test Nullsoft, manifest does not provide silent switch, we should be populating the default 126 var installDir = TestCommon.GetRandomTestDir(); 127 var result = TestCommon.RunAICLICommand("install", $"TestNullsoftInstaller --silent -l {installDir}"); 128 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 129 Assert.True(result.StdOut.Contains("Successfully installed")); 130 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/S")); 131 } 132 133 /// <summary> 134 /// Test install msi. 135 /// </summary> 136 [Test] 137 public void InstallMSI() 138 { 139 var installDir = TestCommon.GetRandomTestDir(); 140 var result = TestCommon.RunAICLICommand("install", $"TestMsiInstaller --silent -l {installDir}"); 141 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 142 Assert.True(result.StdOut.Contains("Successfully installed")); 143 Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(installDir)); 144 } 145 146 /// <summary> 147 /// Test install msix. 148 /// </summary> 149 [Test] 150 public void InstallMSIX() 151 { 152 var result = TestCommon.RunAICLICommand("install", $"TestMsixInstaller"); 153 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 154 Assert.True(result.StdOut.Contains("Successfully installed")); 155 Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); 156 } 157 158 /// <summary> 159 /// Test install msix machine scope. 160 /// </summary> 161 [Test] 162 public void InstallMSIXMachineScope() 163 { 164 // TODO: Provision and Deprovision api not supported in build server. 165 Assert.Ignore(); 166 167 var result = TestCommon.RunAICLICommand("install", $"TestMsixInstaller --scope machine"); 168 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 169 Assert.True(result.StdOut.Contains("Successfully installed")); 170 Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup(true)); 171 } 172 173 /// <summary> 174 /// Test install msix with signature hash. 175 /// </summary> 176 [Test] 177 public void InstallMSIXWithSignature() 178 { 179 var result = TestCommon.RunAICLICommand("install", $"TestMsixWithSignatureHash"); 180 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 181 Assert.True(result.StdOut.Contains("Successfully installed")); 182 Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); 183 } 184 185 /// <summary> 186 /// Test install msix with signature hash machine scope. 187 /// </summary> 188 [Test] 189 public void InstallMSIXWithSignatureMachineScope() 190 { 191 // TODO: Provision and Deprovision api not supported in build server. 192 Assert.Ignore(); 193 194 var result = TestCommon.RunAICLICommand("install", $"TestMsixWithSignatureHash --scope machine"); 195 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 196 Assert.True(result.StdOut.Contains("Successfully installed")); 197 Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup(true)); 198 } 199 200 /// <summary> 201 /// Test msix hash mismatch. 202 /// </summary> 203 [Test] 204 public void InstallMSIXWithSignatureHashMismatch() 205 { 206 var result = TestCommon.RunAICLICommand("install", $"TestMsixSignatureHashMismatch"); 207 Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALLER_HASH_MISMATCH, result.ExitCode); 208 Assert.True(result.StdOut.Contains("Installer hash does not match")); 209 Assert.False(TestCommon.VerifyTestMsixInstalledAndCleanup()); 210 } 211 212 /// <summary> 213 /// Test install with alternate source failure. 214 /// </summary> 215 [Test] 216 public void InstallExeWithAlternateSourceFailure() 217 { 218 TestCommon.RunAICLICommand("source add", "failSearch \"{ \"\"SearchHR\"\": \"\"0x80070002\"\" }\" Microsoft.Test.Configurable --header \"{}\""); 219 220 try 221 { 222 var installDir = TestCommon.GetRandomTestDir(); 223 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); 224 Assert.AreEqual(unchecked((int)0x80070002), result.ExitCode); 225 Assert.True(result.StdOut.Contains("Failed when searching source: failSearch")); 226 Assert.True(result.StdOut.Contains("AppInstallerTest.TestExeInstaller")); 227 Assert.False(result.StdOut.Contains("Successfully installed")); 228 Assert.False(TestCommon.VerifyTestExeInstalledAndCleanup(installDir)); 229 } 230 finally 231 { 232 this.ResetTestSource(); 233 } 234 } 235 236 /// <summary> 237 /// Test install portable package. 238 /// </summary> 239 [Test] 240 public void InstallPortableExe() 241 { 242 string installDir = TestCommon.GetPortablePackagesDirectory(); 243 string packageId, commandAlias, fileName, packageDirName, productCode; 244 packageId = "AppInstallerTest.TestPortableExe"; 245 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 246 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 247 248 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 249 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 250 Assert.True(result.StdOut.Contains("Successfully installed")); 251 252 // If no location specified, default behavior is to create a package directory with the name "{packageId}_{sourceId}" 253 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); 254 } 255 256 /// <summary> 257 /// Test install portable package with command. 258 /// </summary> 259 [Test] 260 public void InstallPortableExeWithCommand() 261 { 262 var installDir = TestCommon.GetRandomTestDir(); 263 string packageId, commandAlias, fileName, productCode; 264 packageId = "AppInstallerTest.TestPortableExeWithCommand"; 265 productCode = packageId + "_" + Constants.TestSourceIdentifier; 266 fileName = "AppInstallerTestExeInstaller.exe"; 267 commandAlias = "testCommand.exe"; 268 269 var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir}"); 270 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 271 Assert.True(result.StdOut.Contains("Successfully installed")); 272 TestCommon.VerifyPortablePackage(installDir, commandAlias, fileName, productCode, true); 273 } 274 275 /// <summary> 276 /// Test install portable package with rename. 277 /// </summary> 278 [Test] 279 public void InstallPortableExeWithRename() 280 { 281 var installDir = TestCommon.GetRandomTestDir(); 282 string packageId, productCode, renameArgValue; 283 packageId = "AppInstallerTest.TestPortableExeWithCommand"; 284 productCode = packageId + "_" + Constants.TestSourceIdentifier; 285 renameArgValue = "testRename.exe"; 286 287 var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir} --rename {renameArgValue}"); 288 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 289 Assert.True(result.StdOut.Contains("Successfully installed")); 290 TestCommon.VerifyPortablePackage(installDir, renameArgValue, renameArgValue, productCode, true); 291 } 292 293 /// <summary> 294 /// Test install portable package invalid rename. 295 /// </summary> 296 [Test] 297 public void InstallPortableInvalidRename() 298 { 299 var installDir = TestCommon.GetRandomTestDir(); 300 string packageId, renameArgValue; 301 packageId = "AppInstallerTest.TestPortableExeWithCommand"; 302 renameArgValue = "test?"; 303 304 var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir} --rename {renameArgValue}"); 305 Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); 306 Assert.True(result.StdOut.Contains("The specified filename is not a valid filename")); 307 } 308 309 /// <summary> 310 /// Test install portable package with reserve names. 311 /// </summary> 312 [Test] 313 public void InstallPortableReservedNames() 314 { 315 var installDir = TestCommon.GetRandomTestDir(); 316 string packageId, renameArgValue; 317 packageId = "AppInstallerTest.TestPortableExeWithCommand"; 318 renameArgValue = "CON"; 319 320 var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {installDir} --rename {renameArgValue}"); 321 Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); 322 Assert.True(result.StdOut.Contains("The specified filename is not a valid filename")); 323 } 324 325 /// <summary> 326 /// Test install portable package to an existing directory. 327 /// </summary> 328 [Test] 329 public void InstallPortableToExistingDirectory() 330 { 331 var installDir = TestCommon.GetRandomTestDir(); 332 var existingDir = Path.Combine(installDir, "testDirectory"); 333 Directory.CreateDirectory(existingDir); 334 335 string packageId, commandAlias, fileName, productCode; 336 packageId = "AppInstallerTest.TestPortableExe"; 337 productCode = packageId + "_" + Constants.TestSourceIdentifier; 338 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 339 340 var result = TestCommon.RunAICLICommand("install", $"{packageId} -l {existingDir}"); 341 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 342 Assert.True(result.StdOut.Contains("Successfully installed")); 343 TestCommon.VerifyPortablePackage(existingDir, commandAlias, fileName, productCode, true); 344 } 345 346 /// <summary> 347 /// Test install portable package. Symlink is a directory. 348 /// </summary> 349 [Test] 350 public void InstallPortableFailsWithCleanup() 351 { 352 string packageId, commandAlias; 353 packageId = "AppInstallerTest.TestPortableExe"; 354 commandAlias = "AppInstallerTestExeInstaller.exe"; 355 356 // Create a directory with the same name as the symlink in order to cause install to fail. 357 string symlinkDirectory = TestCommon.GetPortableSymlinkDirectory(TestCommon.Scope.User); 358 string conflictDirectory = Path.Combine(symlinkDirectory, commandAlias); 359 360 Directory.CreateDirectory(conflictDirectory); 361 362 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 363 364 // Remove directory prior to assertions as this will impact other tests if assertions fail. 365 Directory.Delete(conflictDirectory, true); 366 367 Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); 368 Assert.True(result.StdOut.Contains("Unable to create symlink")); 369 } 370 371 /// <summary> 372 /// Test reinstalling portable package. 373 /// </summary> 374 [Test] 375 public void ReinstallPortable() 376 { 377 string installDir = TestCommon.GetPortablePackagesDirectory(); 378 string packageId, commandAlias, fileName, packageDirName, productCode; 379 packageId = "AppInstallerTest.TestPortableExe"; 380 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 381 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 382 383 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 384 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 385 386 string symlinkDirectory = TestCommon.GetPortableSymlinkDirectory(TestCommon.Scope.User); 387 string symlinkPath = Path.Combine(symlinkDirectory, commandAlias); 388 389 // Clean first install should not display file overwrite message. 390 Assert.True(result.StdOut.Contains("Successfully installed")); 391 Assert.False(result.StdOut.Contains($"Overwriting existing file: {symlinkPath}")); 392 393 // Perform second install and verify that file overwrite message is displayed. 394 var result2 = TestCommon.RunAICLICommand("install", $"{packageId} --force"); 395 Assert.AreEqual(Constants.ErrorCode.S_OK, result2.ExitCode); 396 Assert.True(result2.StdOut.Contains("Successfully installed")); 397 398 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); 399 } 400 401 /// <summary> 402 /// Test installing portable package user scope. 403 /// </summary> 404 [Test] 405 public void InstallPortable_UserScope() 406 { 407 string installDir = TestCommon.GetRandomTestDir(); 408 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, installDir); 409 410 string packageId, commandAlias, fileName, packageDirName, productCode; 411 packageId = "AppInstallerTest.TestPortableExe"; 412 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 413 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 414 415 var result = TestCommon.RunAICLICommand("install", $"{packageId} --scope user"); 416 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, string.Empty); 417 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 418 Assert.True(result.StdOut.Contains("Successfully installed")); 419 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); 420 } 421 422 /// <summary> 423 /// Test install portable package machine scope. 424 /// </summary> 425 [Test] 426 public void InstallPortable_MachineScope() 427 { 428 string installDir = TestCommon.GetRandomTestDir(); 429 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, installDir); 430 431 string packageId, commandAlias, fileName, packageDirName, productCode; 432 packageId = "AppInstallerTest.TestPortableExe"; 433 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 434 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 435 436 var result = TestCommon.RunAICLICommand("install", $"{packageId} --scope machine"); 437 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, string.Empty); 438 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 439 Assert.True(result.StdOut.Contains("Successfully installed")); 440 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.Machine); 441 } 442 443 /// <summary> 444 /// Test install portable package with settings set to user install scope. 445 /// </summary> 446 [Test] 447 public void InstallPortable_InstallScopePreference_User() 448 { 449 string installDir = TestCommon.GetRandomTestDir(); 450 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, installDir); 451 WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallBehaviorScope, "user"); 452 453 string packageId, commandAlias, fileName, packageDirName, productCode; 454 packageId = "AppInstallerTest.TestPortableExe"; 455 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 456 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 457 458 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 459 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageUserRoot, string.Empty); 460 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 461 Assert.True(result.StdOut.Contains("Successfully installed")); 462 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); 463 } 464 465 /// <summary> 466 /// Test install portable package with settings set to machine install scope. 467 /// </summary> 468 [Test] 469 public void InstallPortable_InstallScopePreference_Machine() 470 { 471 string installDir = TestCommon.GetRandomTestDir(); 472 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, installDir); 473 WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallBehaviorScope, "machine"); 474 475 string packageId, commandAlias, fileName, packageDirName, productCode; 476 packageId = "AppInstallerTest.TestPortableExe"; 477 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 478 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 479 480 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 481 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.PortablePackageMachineRoot, string.Empty); 482 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 483 Assert.True(result.StdOut.Contains("Successfully installed")); 484 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.Machine); 485 } 486 487 /// <summary> 488 /// Test install zip exe. 489 /// </summary> 490 [Test] 491 public void InstallZip_Exe() 492 { 493 var installDir = TestCommon.GetRandomTestDir(); 494 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithExe --silent -l {installDir}"); 495 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 496 Assert.True(result.StdOut.Contains("Successfully installed")); 497 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); 498 } 499 500 /// <summary> 501 /// Test install zip portable. 502 /// </summary> 503 [Test] 504 public void InstallZip_Portable() 505 { 506 string installDir = TestCommon.GetPortablePackagesDirectory(); 507 string packageId, commandAlias, fileName, packageDirName, productCode; 508 packageId = "AppInstallerTest.TestZipInstallerWithPortable"; 509 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 510 commandAlias = "TestPortable.exe"; 511 fileName = "AppInstallerTestExeInstaller.exe"; 512 513 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 514 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 515 Assert.True(result.StdOut.Contains("Successfully installed")); 516 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.User); 517 } 518 519 /// <summary> 520 /// Test install zip portable with binaries that depend on PATH variable. 521 /// </summary> 522 [Test] 523 public void InstallZip_ArchivePortableWithBinariesDependentOnPath() 524 { 525 string installDir = TestCommon.GetPortablePackagesDirectory(); 526 string packageId, commandAlias, fileName, packageDirName, productCode; 527 packageId = "AppInstallerTest.ArchivePortableWithBinariesDependentOnPath"; 528 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 529 commandAlias = "TestPortable.exe"; 530 fileName = "AppInstallerTestExeInstaller.exe"; 531 532 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 533 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 534 Assert.True(result.StdOut.Contains("Successfully installed")); 535 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true, TestCommon.Scope.User, true); 536 } 537 538 /// <summary> 539 /// Test install zip with invalid relative file path. 540 /// </summary> 541 [Test] 542 public void InstallZipWithInvalidRelativeFilePath() 543 { 544 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInvalidRelativePath"); 545 Assert.AreNotEqual(Constants.ErrorCode.S_OK, result.ExitCode); 546 Assert.True(result.StdOut.Contains("Invalid relative file path to the nested installer; path points to a location outside of the install directory")); 547 } 548 549 /// <summary> 550 /// Test install zip msi. 551 /// </summary> 552 [Test] 553 public void InstallZipWithMsi() 554 { 555 var installDir = TestCommon.GetRandomTestDir(); 556 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithMsi --silent -l {installDir}"); 557 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 558 Assert.True(result.StdOut.Contains("Successfully installed")); 559 Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(installDir)); 560 } 561 562 /// <summary> 563 /// Test install zip msix. 564 /// </summary> 565 [Test] 566 public void InstallZipWithMsix() 567 { 568 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithMsix"); 569 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 570 Assert.True(result.StdOut.Contains("Successfully installed")); 571 Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); 572 } 573 574 /// <summary> 575 /// Test install zip exe by extracting with tar. 576 /// </summary> 577 [Test] 578 public void InstallZip_ExtractWithTar() 579 { 580 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.ArchiveExtractionMethod, "tar"); 581 var installDir = TestCommon.GetRandomTestDir(); 582 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestZipInstallerWithExe --silent -l {installDir}"); 583 WinGetSettingsHelper.ConfigureInstallBehavior(Constants.ArchiveExtractionMethod, string.Empty); 584 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 585 Assert.True(result.StdOut.Contains("Successfully installed")); 586 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); 587 } 588 589 /// <summary> 590 /// Test install an installed package and convert to upgrade. 591 /// </summary> 592 [Test] 593 public void InstallExeFoundExistingConvertToUpgrade() 594 { 595 var baseDir = TestCommon.GetRandomTestDir(); 596 var baseResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 1.0.0.0 --silent -l {baseDir}"); 597 Assert.AreEqual(Constants.ErrorCode.S_OK, baseResult.ExitCode); 598 Assert.True(baseResult.StdOut.Contains("Successfully installed")); 599 600 // Install will convert to upgrade 601 var upgradeDir = TestCommon.GetRandomTestDir(); 602 var upgradeResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {upgradeDir}"); 603 Assert.AreEqual(Constants.ErrorCode.S_OK, upgradeResult.ExitCode); 604 Assert.True(upgradeResult.StdOut.Contains("Trying to upgrade the installed package...")); 605 Assert.True(upgradeResult.StdOut.Contains("Successfully installed")); 606 607 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); 608 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(upgradeDir, "/Version 2.0.0.0")); 609 } 610 611 /// <summary> 612 /// Test install an installed package without an available upgrade. 613 /// </summary> 614 [Test] 615 public void InstallExeFoundExistingConvertToUpgradeNoAvailableUpgrade() 616 { 617 var baseDir = TestCommon.GetRandomTestDir(); 618 var baseResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 2.0.0.0 --silent -l {baseDir}"); 619 Assert.AreEqual(Constants.ErrorCode.S_OK, baseResult.ExitCode); 620 Assert.True(baseResult.StdOut.Contains("Successfully installed")); 621 622 // Install will convert to upgrade 623 var upgradeDir = TestCommon.GetRandomTestDir(); 624 var upgradeResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {upgradeDir}"); 625 Assert.AreEqual(Constants.ErrorCode.ERROR_UPDATE_NOT_APPLICABLE, upgradeResult.ExitCode); 626 Assert.True(upgradeResult.StdOut.Contains("Trying to upgrade the installed package...")); 627 Assert.True(upgradeResult.StdOut.Contains("No available upgrade")); 628 629 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); 630 } 631 632 /// <summary> 633 /// Test force installing a package. 634 /// </summary> 635 [Test] 636 public void InstallExeWithLatestInstalledWithForce() 637 { 638 var baseDir = TestCommon.GetRandomTestDir(); 639 var baseResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 2.0.0.0 --silent -l {baseDir}"); 640 Assert.AreEqual(Constants.ErrorCode.S_OK, baseResult.ExitCode); 641 Assert.True(baseResult.StdOut.Contains("Successfully installed")); 642 643 // Install will not convert to upgrade 644 var installDir = TestCommon.GetRandomTestDir(); 645 var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller -v 1.0.0.0 --silent -l {installDir} --force"); 646 Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); 647 Assert.True(installResult.StdOut.Contains("Successfully installed")); 648 649 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(baseDir)); 650 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); 651 } 652 653 /// <summary> 654 /// Test install a package with an invalid Windows Feature dependency. 655 /// </summary> 656 [Test] 657 public void InstallWithWindowsFeatureDependency_FeatureNotFound() 658 { 659 var testDir = TestCommon.GetRandomTestDir(); 660 var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.WindowsFeature -l {testDir}"); 661 Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_DEPENDENCIES, installResult.ExitCode); 662 Assert.True(installResult.StdOut.Contains("The feature [invalidFeature] was not found.")); 663 Assert.True(installResult.StdOut.Contains("Failed to enable Windows Feature dependencies. To proceed with installation, use '--force'.")); 664 } 665 666 /// <summary> 667 /// Test install a package with a Windows Feature dependency using the force argument. 668 /// </summary> 669 [Test] 670 public void InstallWithWindowsFeatureDependency_Force() 671 { 672 var testDir = TestCommon.GetRandomTestDir(); 673 var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.WindowsFeature --silent --force -l {testDir}"); 674 Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); 675 Assert.True(installResult.StdOut.Contains("Failed to enable Windows Feature dependencies; proceeding due to --force")); 676 Assert.True(installResult.StdOut.Contains("Successfully installed")); 677 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(testDir)); 678 } 679 680 /// <summary> 681 /// Test install a package with a package dependency that requires the PATH environment variable to be refreshed between dependency installs. 682 /// </summary> 683 [Test] 684 public void InstallWithPackageDependency_RefreshPathVariable() 685 { 686 var testDir = TestCommon.GetRandomTestDir(); 687 string installDir = TestCommon.GetPortablePackagesDirectory(); 688 var installResult = TestCommon.RunAICLICommand("install", $"AppInstallerTest.PackageDependencyRequiresPathRefresh -l {testDir}"); 689 Assert.AreEqual(Constants.ErrorCode.S_OK, installResult.ExitCode); 690 Assert.True(installResult.StdOut.Contains("Successfully installed")); 691 692 // Portable package is used as a dependency. Ensure that it is installed and cleaned up successfully. 693 string portablePackageId, commandAlias, fileName, packageDirName, productCode; 694 portablePackageId = "AppInstallerTest.TestPortableExeWithCommand"; 695 packageDirName = productCode = portablePackageId + "_" + Constants.TestSourceIdentifier; 696 fileName = "AppInstallerTestExeInstaller.exe"; 697 commandAlias = "testCommand.exe"; 698 699 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); 700 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(testDir)); 701 } 702 703 /// <summary> 704 /// Test install a package using a specific installer type. 705 /// </summary> 706 [Test] 707 public void InstallWithInstallerTypeArgument() 708 { 709 var installDir = TestCommon.GetRandomTestDir(); 710 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMultipleInstallers --silent -l {installDir} --installer-type exe"); 711 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 712 Assert.True(result.StdOut.Contains("Successfully installed")); 713 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir, "/execustom")); 714 } 715 716 /// <summary> 717 /// Test install package with installer type preference settings. 718 /// </summary> 719 [Test] 720 public void InstallWithInstallerTypePreference() 721 { 722 string[] installerTypePreference = { "nullsoft" }; 723 WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallerTypes, installerTypePreference); 724 725 string installDir = TestCommon.GetRandomTestDir(); 726 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMultipleInstallers --silent -l {installDir}"); 727 728 // Reset installer type preferences. 729 WinGetSettingsHelper.ConfigureInstallBehaviorPreferences(Constants.InstallerTypes, Array.Empty<string>()); 730 731 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 732 Assert.True(result.StdOut.Contains("Successfully installed")); 733 Assert.True(TestCommon.VerifyTestExeInstalledAndCleanup(installDir), "/S"); 734 } 735 736 /// <summary> 737 /// Test install package with installer type requirement settings. 738 /// </summary> 739 [Test] 740 public void InstallWithInstallerTypeRequirement() 741 { 742 string[] installerTypeRequirement = { "inno" }; 743 WinGetSettingsHelper.ConfigureInstallBehaviorRequirements(Constants.InstallerTypes, installerTypeRequirement); 744 745 string installDir = TestCommon.GetRandomTestDir(); 746 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMultipleInstallers --silent -l {installDir}"); 747 748 // Reset installer type requirements. 749 WinGetSettingsHelper.ConfigureInstallBehaviorRequirements(Constants.InstallerTypes, Array.Empty<string>()); 750 751 Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICABLE_INSTALLER, result.ExitCode); 752 Assert.True(result.StdOut.Contains("No applicable installer found; see logs for more details.")); 753 } 754 755 /// <summary> 756 /// This test flow is intended to test an EXE that actually installs an MSIX internally, and whose name+publisher 757 /// information resembles an existing installation. Given this, the goal is to get correlation to stick to the 758 /// MSIX rather than the ARP entry that we would match with in the absence of the package family name being present. 759 /// </summary> 760 [Test] 761 public void InstallExeThatInstallsMSIX() 762 { 763 string targetPackageIdentifier = "AppInstallerTest.TestExeInstallerInstallsMSIX"; 764 string fakeProductCode = "e35f5799-cce3-41fd-886c-c36fcb7104fe"; 765 766 // Insert fake ARP entry as if a non-MSIX version of the package is already installed. 767 // The name here must not match the normalized name of the package, but be close enough to meet 768 // the confidence requirements for correlation after an install operation (so we drop one word here). 769 TestCommon.CreateARPEntry(fakeProductCode, new 770 { 771 DisplayName = "EXE Installer that Installs MSIX", 772 Publisher = "AppInstallerTest", 773 DisplayVersion = "1.0.0", 774 }); 775 776 // We should not find it before installing because the normalized name doesn't match 777 var result = TestCommon.RunAICLICommand("list", targetPackageIdentifier); 778 Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); 779 780 // Add the MSIX to simulate an installer doing it 781 TestCommon.InstallMsix(TestIndex.MsixInstaller); 782 783 // Install our exe that "installs" the MSIX 784 result = TestCommon.RunAICLICommand("install", $"{targetPackageIdentifier} --force"); 785 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 786 787 // We should find the package now, and it should be correlated to the MSIX (although we don't actually know that from this probe) 788 result = TestCommon.RunAICLICommand("list", targetPackageIdentifier); 789 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 790 791 // Remove the MSIX outside of winget's knowledge to keep the tracking data. 792 TestCommon.RemoveMsix(Constants.MsixInstallerName); 793 794 // We should not find the package now that the MSIX is gone, confirming that it was correlated 795 result = TestCommon.RunAICLICommand("list", targetPackageIdentifier); 796 Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); 797 798 TestCommon.RemoveARPEntry(fakeProductCode); 799 } 800 } 801 }