RepairCommand.cs (15513B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="RepairCommand.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.IO; 10 using AppInstallerCLIE2ETests.Helpers; 11 using NUnit.Framework; 12 13 /// <summary> 14 /// Test Repair command. 15 /// </summary> 16 public class RepairCommand : BaseCommand 17 { 18 /// <summary> 19 /// One time setup. 20 /// </summary> 21 [OneTimeSetUp] 22 public void OneTimeSetup() 23 { 24 // Try clean up AppInstallerTest.TestMsiInstaller for failure cases where cleanup is not successful 25 TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestMsiInstaller"); 26 } 27 28 /// <summary> 29 /// Test MSI installer repair. 30 /// </summary> 31 [Test] 32 public void RepairMSIInstaller() 33 { 34 if (string.IsNullOrEmpty(TestIndex.MsiInstallerV2)) 35 { 36 Assert.Ignore("MSI installer not available"); 37 } 38 39 var installDir = TestCommon.GetRandomTestDir(); 40 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMsiRepair --silent -l {installDir}"); 41 42 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 43 Assert.True(result.StdOut.Contains("Successfully installed")); 44 45 // Note: The 'msiexec repair' command requires the original installer file to be present at the location registered in the ARP (Add/Remove Programs). 46 // In our test scenario, the MSI installer file is initially placed in a temporary location and then deleted, which can cause the repair operation to fail. 47 // To work around this, we copy the installer file to the ARP source directory before running the repair command. 48 // A more permanent solution would be to modify the MSI installer to cache the installer file in a known location and register that location as the installer source. 49 // This would allow the 'msiexec repair' command to function as expected. 50 string installerSourceDir = TestCommon.CopyInstallerFileToARPInstallSourceDirectory(TestCommon.GetTestDataFile("AppInstallerTestMsiInstallerV2.msi"), Constants.MsiInstallerProductCode, true); 51 52 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestMsiRepair"); 53 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 54 Assert.True(result.StdOut.Contains("Repair operation completed successfully")); 55 Assert.True(TestCommon.VerifyTestMsiInstalledAndCleanup(installDir)); 56 57 if (installerSourceDir != null && Directory.Exists(installerSourceDir)) 58 { 59 Directory.Delete(installerSourceDir, true); 60 } 61 } 62 63 /// <summary> 64 /// Test MSIX non-store package repair. 65 /// </summary> 66 [Test] 67 public void RepairNonStoreMSIXPackage() 68 { 69 // install a test msix package from TestSource and then repair it. 70 var installDir = TestCommon.GetRandomTestDir(); 71 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMsixInstaller --silent -l {installDir}"); 72 73 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 74 Assert.True(result.StdOut.Contains("Successfully installed")); 75 76 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestMsixInstaller"); 77 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 78 Assert.True(result.StdOut.Contains("Repair operation completed successfully")); 79 Assert.True(TestCommon.VerifyTestMsixInstalledAndCleanup()); 80 } 81 82 /// <summary> 83 /// Test MSIX non-store package repair with machine scope. 84 /// </summary> 85 [Test] 86 public void RepairNonStoreMsixPackageWithMachineScope() 87 { 88 // Selecting Microsoft.Paint_8wekyb3d8bbwe because it's a system package suitable for this scenario. 89 // First, we need to ensure this package is installed, otherwise, we skip the test. 90 var result = TestCommon.RunAICLICommand("list", "Microsoft.Paint_8wekyb3d8bbwe"); 91 92 if (result.ExitCode != Constants.ErrorCode.S_OK) 93 { 94 Assert.Ignore("Test skipped as Microsoft.Paint_8wekyb3d8bbwe is not installed."); 95 } 96 97 Assert.True(result.StdOut.Contains("Microsoft.Paint")); 98 99 result = TestCommon.RunAICLICommand("repair", "Microsoft.Paint_8wekyb3d8bbwe --scope machine"); 100 Assert.AreEqual(Constants.ErrorCode.ERROR_INSTALL_SYSTEM_NOT_SUPPORTED, result.ExitCode); 101 Assert.True(result.StdOut.Contains("The current system configuration does not support the repair of this package.")); 102 } 103 104 /// <summary> 105 /// Test repair of a Burn installer that has a "modify" repair behavior specified in the manifest. 106 /// </summary> 107 [Test] 108 public void RepairBurnInstallerWithModifyBehavior() 109 { 110 // install a test burn package from TestSource and then repair it. 111 var installDir = TestCommon.GetRandomTestDir(); 112 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestModifyRepair -v 2.0.0.0 --silent -l {installDir}"); 113 114 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 115 Assert.True(result.StdOut.Contains("Successfully installed")); 116 117 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestModifyRepair"); 118 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 119 Assert.True(result.StdOut.Contains("Repair operation completed successfully")); 120 Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Modify Repair operation")); 121 } 122 123 /// <summary> 124 /// Tests the repair operation of a Burn installer that was installed in user scope but is being repaired in an admin context. 125 /// </summary> 126 [Test] 127 public void RepairBurnInstallerInAdminContextWithUserScopeInstall() 128 { 129 // install a test burn package from TestSource and then repair it. 130 var installDir = TestCommon.GetRandomTestDir(); 131 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestUserScopeInstallRepairInAdminContext -v 2.0.0.0 --silent -l {installDir}"); 132 133 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 134 Assert.True(result.StdOut.Contains("Successfully installed")); 135 136 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestUserScopeInstallRepairInAdminContext"); 137 Assert.AreEqual(Constants.ErrorCode.ERROR_ADMIN_CONTEXT_REPAIR_PROHIBITED, result.ExitCode); 138 Assert.True(result.StdOut.Contains("The package installed for user scope cannot be repaired when running with administrator privileges.")); 139 TestCommon.CleanupTestExeAndDirectory(installDir); 140 } 141 142 /// <summary> 143 /// Tests the repair operation of a Burn installer that lacks a repair behavior. 144 /// </summary> 145 [Test] 146 public void RepairBurnInstallerMissingRepairBehavior() 147 { 148 // install a test burn package from TestSource and then repair it. 149 var installDir = TestCommon.GetRandomTestDir(); 150 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestMissingRepairBehavior -v 2.0.0.0 --silent -l {installDir}"); 151 152 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 153 Assert.True(result.StdOut.Contains("Successfully installed")); 154 155 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestMissingRepairBehavior"); 156 Assert.AreEqual(Constants.ErrorCode.ERROR_NO_REPAIR_INFO_FOUND, result.ExitCode); 157 Assert.True(result.StdOut.Contains("The repair command for this package cannot be found. Please reach out to the package publisher for support.")); 158 TestCommon.CleanupTestExeAndDirectory(installDir); 159 } 160 161 /// <summary> 162 /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest and NoModify ARP flag set. 163 /// </summary> 164 [Test] 165 public void RepairBurnInstallerWithWithModifyBehaviorAndNoModifyFlag() 166 { 167 // install a test Exe package from TestSource and then repair it. 168 var installDir = TestCommon.GetRandomTestDir(); 169 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestModifyRepairWithNoModify -v 2.0.0.0 --silent -l {installDir}"); 170 171 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 172 Assert.True(result.StdOut.Contains("Successfully installed")); 173 174 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestModifyRepairWithNoModify"); 175 Assert.AreEqual(Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED, result.ExitCode); 176 Assert.True(result.StdOut.Contains("The installer technology in use does not support repair.")); 177 TestCommon.CleanupTestExeAndDirectory(installDir); 178 } 179 180 /// <summary> 181 /// Tests the scenario where the repair operation is not supported for Portable Installer type. 182 /// </summary> 183 [Test] 184 public void RepairOperationNotSupportedForPortableInstaller() 185 { 186 string installDir = TestCommon.GetPortablePackagesDirectory(); 187 string packageId, commandAlias, fileName, packageDirName, productCode; 188 packageId = "AppInstallerTest.TestPortableExe"; 189 packageDirName = productCode = packageId + "_" + Constants.TestSourceIdentifier; 190 commandAlias = fileName = "AppInstallerTestExeInstaller.exe"; 191 192 var result = TestCommon.RunAICLICommand("install", $"{packageId}"); 193 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 194 Assert.True(result.StdOut.Contains("Successfully installed")); 195 196 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestPortableExe"); 197 Assert.AreEqual(Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED, result.ExitCode); 198 Assert.True(result.StdOut.Contains("The installer technology in use does not support repair.")); 199 200 // If no location specified, default behavior is to create a package directory with the name "{packageId}_{sourceId}" 201 TestCommon.VerifyPortablePackage(Path.Combine(installDir, packageDirName), commandAlias, fileName, productCode, true); 202 } 203 204 /// <summary> 205 /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest. 206 /// </summary> 207 [Test] 208 public void RepairExeInstallerWithUninstallerBehavior() 209 { 210 // install a test Exe package from TestSource and then repair it. 211 var installDir = TestCommon.GetRandomTestDir(); 212 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.UninstallerRepair -v 2.0.0.0 --silent -l {installDir}"); 213 214 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 215 Assert.True(result.StdOut.Contains("Successfully installed")); 216 217 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.UninstallerRepair"); 218 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 219 Assert.True(result.StdOut.Contains("Repair operation completed successfully")); 220 Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Uninstaller Repair operation")); 221 } 222 223 /// <summary> 224 /// Test repair of a Exe installer that has a "uninstaller" repair behavior specified in the manifest and NoRepair ARP flag set. 225 /// </summary> 226 [Test] 227 public void RepairExeInstallerWithUninstallerBehaviorAndNoRepairFlag() 228 { 229 // install a test Exe package from TestSource and then repair it. 230 var installDir = TestCommon.GetRandomTestDir(); 231 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.UninstallerRepairWithNoRepair -v 2.0.0.0 --silent -l {installDir}"); 232 233 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 234 Assert.True(result.StdOut.Contains("Successfully installed")); 235 236 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.UninstallerRepairWithNoRepair"); 237 Assert.AreEqual(Constants.ErrorCode.ERROR_REPAIR_NOT_SUPPORTED, result.ExitCode); 238 Assert.True(result.StdOut.Contains("The installer technology in use does not support repair.")); 239 TestCommon.CleanupTestExeAndDirectory(installDir); 240 } 241 242 /// <summary> 243 /// Test repair of a Nullsoft installer that has a "uninstaller" repair behavior specified in the manifest. 244 /// </summary> 245 [Test] 246 public void RepairNullsoftInstallerWithUninstallerBehavior() 247 { 248 // install a test Nullsoft package from TestSource and then repair it. 249 var installDir = TestCommon.GetRandomTestDir(); 250 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.UninstallerRepair -v 2.0.0.0 --silent -l {installDir}"); 251 252 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 253 Assert.True(result.StdOut.Contains("Successfully installed")); 254 255 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.UninstallerRepair"); 256 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 257 Assert.True(result.StdOut.Contains("Repair operation completed successfully")); 258 Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Uninstaller Repair operation")); 259 } 260 261 /// <summary> 262 /// Test repair of a Inno installer that has a "installer" repair behavior specified in the manifest. 263 /// </summary> 264 [Test] 265 public void RepairInnoInstallerWithInstallerBehavior() 266 { 267 // install a test Inno package from TestSource and then repair it. 268 var installDir = TestCommon.GetRandomTestDir(); 269 var result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestInstallerRepair -v 2.0.0.0 --silent -l {installDir}"); 270 271 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 272 Assert.True(result.StdOut.Contains("Successfully installed")); 273 274 result = TestCommon.RunAICLICommand("repair", "AppInstallerTest.TestInstallerRepair"); 275 Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); 276 Assert.True(result.StdOut.Contains("Repair operation completed successfully")); 277 Assert.True(TestCommon.VerifyTestExeRepairCompletedAndCleanup(installDir, "Installer Repair operation")); 278 } 279 } 280 }