| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | namespace WixToolsetTest.CoreIntegration |
| 4 | { |
| 5 | using System.IO; |
| 6 | using System.Linq; |
| 7 | using WixInternal.TestSupport; |
| 8 | using WixInternal.Core.TestPackage; |
| 9 | using WixToolset.Data.WindowsInstaller; |
| 10 | using Xunit; |
| 11 | |
| 12 | public class CustomActionFixture |
| 13 | { |
| 14 | [Fact] |
| 15 | public void CanBuildSetProperty() |
| 16 | { |
| 17 | var folder = TestData.Get("TestData", "SetProperty"); |
| 18 | |
| 19 | using (var fs = new DisposableFileSystem()) |
| 20 | { |
| 21 | var baseFolder = fs.GetFolder(); |
| 22 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 23 | |
| 24 | var result = WixRunner.Execute(new[] |
| 25 | { |
| 26 | "build", |
| 27 | Path.Combine(folder, "Package.wxs"), |
| 28 | Path.Combine(folder, "PackageComponents.wxs"), |
| 29 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), |
| 30 | "-bindpath", Path.Combine(folder, "data"), |
| 31 | "-intermediateFolder", intermediateFolder, |
| 32 | "-o", Path.Combine(baseFolder, @"bin\test.msi") |
| 33 | }); |
| 34 | |
| 35 | result.AssertSuccess(); |
| 36 | |
| 37 | var output = WindowsInstallerData.Load(Path.Combine(baseFolder, "bin", "test.wixpdb"), false); |
| 38 | var caRows = output.Tables["CustomAction"].Rows.Single(); |
| 39 | WixAssert.StringEqual("SetINSTALLLOCATION", caRows.FieldAsString(0)); |
| 40 | WixAssert.StringEqual("51", caRows.FieldAsString(1)); |
| 41 | WixAssert.StringEqual("INSTALLLOCATION", caRows.FieldAsString(2)); |
| 42 | WixAssert.StringEqual("[INSTALLFOLDER]", caRows.FieldAsString(3)); |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | [Fact] |
| 47 | public void CannotBuildWhenSetPropertyReferencesMissingAction() |
| 48 | { |
| 49 | var folder = TestData.Get("TestData", "SetProperty"); |
| 50 | |
| 51 | using (var fs = new DisposableFileSystem()) |
| 52 | { |
| 53 | var baseFolder = fs.GetFolder(); |
| 54 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 55 | |
| 56 | var result = WixRunner.Execute(new[] |
| 57 | { |
| 58 | "build", |
| 59 | Path.Combine(folder, "CannotBuildWhenSetPropertyReferencesMissingAction.wxs"), |
| 60 | "-bindpath", Path.Combine(folder, "data"), |
| 61 | "-intermediateFolder", intermediateFolder, |
| 62 | "-o", Path.Combine(baseFolder, @"bin\test.msi") |
| 63 | }); |
| 64 | |
| 65 | var messages = result.Messages.Select(m => m.ToString()).ToArray(); |
| 66 | WixAssert.CompareLineByLine(new[] |
| 67 | { |
| 68 | "The identifier 'WixAction:InstallUISequence/OnlyScheduledInExecuteSequence' could not be found. Ensure you have typed the reference correctly and that all the necessary inputs are provided to the linker." |
| 69 | }, messages); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | [Fact] |
| 74 | public void CanDetectCustomActionCycle() |
| 75 | { |
| 76 | var folder = TestData.Get(@"TestData"); |
| 77 | |
| 78 | using (var fs = new DisposableFileSystem()) |
| 79 | { |
| 80 | var baseFolder = fs.GetFolder(); |
| 81 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 82 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); |
| 83 | |
| 84 | var result = WixRunner.Execute(new[] |
| 85 | { |
| 86 | "build", |
| 87 | Path.Combine(folder, "CustomAction", "CustomActionCycle.wxs"), |
| 88 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), |
| 89 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), |
| 90 | "-bindpath", Path.Combine(folder, "SingleFile", "data"), |
| 91 | "-intermediateFolder", intermediateFolder, |
| 92 | "-o", msiPath |
| 93 | }); |
| 94 | |
| 95 | Assert.Equal(176, result.ExitCode); |
| 96 | WixAssert.StringEqual("The InstallExecuteSequence table contains an action 'Action1' that is scheduled to come before or after action 'Action3', which is also scheduled to come before or after action 'Action1'. Please remove this circular dependency by changing the Before or After attribute for one of the actions.", result.Messages[0].ToString()); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | [Fact] |
| 101 | public void WarnsOnVBScriptCustomAction() |
| 102 | { |
| 103 | var folder = TestData.Get(@"TestData"); |
| 104 | |
| 105 | using (var fs = new DisposableFileSystem()) |
| 106 | { |
| 107 | var baseFolder = fs.GetFolder(); |
| 108 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 109 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); |
| 110 | |
| 111 | var result = WixRunner.Execute(new[] |
| 112 | { |
| 113 | "build", |
| 114 | Path.Combine(folder, "CustomAction", "VBScriptCustomAction.wxs"), |
| 115 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), |
| 116 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), |
| 117 | "-bindpath", Path.Combine(folder, "SingleFile", "data"), |
| 118 | "-intermediateFolder", intermediateFolder, |
| 119 | "-o", msiPath |
| 120 | }); |
| 121 | |
| 122 | Assert.Equal(1163, result.ExitCode); |
| 123 | Assert.Equal(3, result.Messages.Length); |
| 124 | Assert.Equal(3, result.Messages.Where(m => m.Id == 1163).Count()); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | [Fact] |
| 129 | public void CanDetectCustomActionCycleWithTail() |
| 130 | { |
| 131 | var folder = TestData.Get(@"TestData"); |
| 132 | |
| 133 | using (var fs = new DisposableFileSystem()) |
| 134 | { |
| 135 | var baseFolder = fs.GetFolder(); |
| 136 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 137 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); |
| 138 | |
| 139 | var result = WixRunner.Execute(new[] |
| 140 | { |
| 141 | "build", |
| 142 | Path.Combine(folder, "CustomAction", "CustomActionCycleWithTail.wxs"), |
| 143 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), |
| 144 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), |
| 145 | "-bindpath", Path.Combine(folder, "SingleFile", "data"), |
| 146 | "-intermediateFolder", intermediateFolder, |
| 147 | "-o", msiPath |
| 148 | }); |
| 149 | |
| 150 | Assert.Equal(176, result.ExitCode); |
| 151 | WixAssert.StringEqual("The InstallExecuteSequence table contains an action 'Action2' that is scheduled to come before or after action 'Action4', which is also scheduled to come before or after action 'Action2'. Please remove this circular dependency by changing the Before or After attribute for one of the actions.", result.Messages[0].ToString()); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | [Fact] |
| 156 | public void CanScheduleCustomActionInModule() |
| 157 | { |
| 158 | var folder = TestData.Get(@"TestData"); |
| 159 | |
| 160 | using (var fs = new DisposableFileSystem()) |
| 161 | { |
| 162 | var baseFolder = fs.GetFolder(); |
| 163 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 164 | var msmPath = Path.Combine(baseFolder, "bin", "test.msm"); |
| 165 | |
| 166 | var result = WixRunner.Execute(new[] |
| 167 | { |
| 168 | "build", |
| 169 | Path.Combine(folder, "CustomAction", "MsmCustomAction.wxs"), |
| 170 | Path.Combine(folder, "CustomAction", "SimpleCustomAction.wxs"), |
| 171 | "-bindpath", Path.Combine(folder, ".Data"), |
| 172 | "-intermediateFolder", intermediateFolder, |
| 173 | "-o", msmPath |
| 174 | }); |
| 175 | |
| 176 | result.AssertSuccess(); |
| 177 | |
| 178 | var rows = Query.QueryDatabase(msmPath, new[] { "CustomAction", "ModuleInstallExecuteSequence" }); |
| 179 | WixAssert.CompareLineByLine(new[] |
| 180 | { |
| 181 | "CustomAction:Action1.243FB739_4D05_472F_9CFB_EF6B1017B6DE\t1\tBinary1.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tEntryPoint1\t", |
| 182 | "ModuleInstallExecuteSequence:Action1.243FB739_4D05_472F_9CFB_EF6B1017B6DE\t\tInstallFiles\t1\t", |
| 183 | "ModuleInstallExecuteSequence:CreateFolders\t3700\t\t\t", |
| 184 | "ModuleInstallExecuteSequence:InstallFiles\t4000\t\t\t", |
| 185 | "ModuleInstallExecuteSequence:RemoveFiles\t3500\t\t\t", |
| 186 | "ModuleInstallExecuteSequence:RemoveFolders\t3600\t\t\t" |
| 187 | }, rows); |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | [Fact] |
| 192 | public void CanScheduleSetPropertyInModule() |
| 193 | { |
| 194 | var folder = TestData.Get(@"TestData"); |
| 195 | |
| 196 | using (var fs = new DisposableFileSystem()) |
| 197 | { |
| 198 | var baseFolder = fs.GetFolder(); |
| 199 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 200 | var msmPath = Path.Combine(baseFolder, "bin", "test.msm"); |
| 201 | |
| 202 | var result = WixRunner.Execute(new[] |
| 203 | { |
| 204 | "build", |
| 205 | Path.Combine(folder, "SetProperty", "MsmSetProperty.wxs"), |
| 206 | "-bindpath", Path.Combine(folder, "SetProperty", "data"), |
| 207 | "-intermediateFolder", intermediateFolder, |
| 208 | "-o", msmPath |
| 209 | }); |
| 210 | |
| 211 | result.AssertSuccess(); |
| 212 | |
| 213 | var rows = Query.QueryDatabase(msmPath, new[] { "CustomAction", "ModuleInstallExecuteSequence" }); |
| 214 | WixAssert.CompareLineByLine(new[] |
| 215 | { |
| 216 | "CustomAction:SetINSTALLLOCATION.243FB739_4D05_472F_9CFB_EF6B1017B6DE\t51\tINSTALLLOCATION.243FB739_4D05_472F_9CFB_EF6B1017B6DE\t[INSTALLFOLDER.243FB739_4D05_472F_9CFB_EF6B1017B6DE]\t", |
| 217 | "ModuleInstallExecuteSequence:CostFinalize\t1000\t\t\t", |
| 218 | "ModuleInstallExecuteSequence:CreateFolders\t3700\t\t\t", |
| 219 | "ModuleInstallExecuteSequence:InstallFiles\t4000\t\t\t", |
| 220 | "ModuleInstallExecuteSequence:RemoveFiles\t3500\t\t\t", |
| 221 | "ModuleInstallExecuteSequence:RemoveFolders\t3600\t\t\t","" + |
| 222 | "ModuleInstallExecuteSequence:SetINSTALLLOCATION.243FB739_4D05_472F_9CFB_EF6B1017B6DE\t\tCostFinalize\t1\t" |
| 223 | }, rows); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | [Fact] |
| 228 | public void PopulatesCustomActionTable() |
| 229 | { |
| 230 | var folder = TestData.Get(@"TestData"); |
| 231 | |
| 232 | using (var fs = new DisposableFileSystem()) |
| 233 | { |
| 234 | var baseFolder = fs.GetFolder(); |
| 235 | var intermediateFolder = Path.Combine(baseFolder, "obj"); |
| 236 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); |
| 237 | |
| 238 | var result = WixRunner.Execute(new[] |
| 239 | { |
| 240 | "build", |
| 241 | Path.Combine(folder, "CustomAction", "UnscheduledCustomAction.wxs"), |
| 242 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), |
| 243 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), |
| 244 | "-bindpath", Path.Combine(folder, "SingleFile", "data"), |
| 245 | "-intermediateFolder", intermediateFolder, |
| 246 | "-o", msiPath |
| 247 | }); |
| 248 | |
| 249 | result.AssertSuccess(); |
| 250 | |
| 251 | Assert.True(File.Exists(msiPath)); |
| 252 | var results = Query.QueryDatabase(msiPath, new[] { |
| 253 | "ActionText", |
| 254 | "AdminExecuteSequence", |
| 255 | "AdminUISequence", |
| 256 | "AdvtExecuteSequence", |
| 257 | "Binary", |
| 258 | "CustomAction", |
| 259 | "InstallExecuteSequence", |
| 260 | "InstallUISequence", |
| 261 | "Property", |
| 262 | }).Where(x => !x.StartsWith("Property:") || x.StartsWith("Property:MsiHiddenProperties\t")).ToArray(); |
| 263 | WixAssert.CompareLineByLine(new[] |
| 264 | { |
| 265 | "ActionText:CustomAction2\tProgess2Text\t", |
| 266 | "AdminExecuteSequence:CostFinalize\t\t1000", |
| 267 | "AdminExecuteSequence:CostInitialize\t\t800", |
| 268 | "AdminExecuteSequence:CustomAction2\t\t801", |
| 269 | "AdminExecuteSequence:FileCost\t\t900", |
| 270 | "AdminExecuteSequence:InstallAdminPackage\t\t3900", |
| 271 | "AdminExecuteSequence:InstallFiles\t\t4000", |
| 272 | "AdminExecuteSequence:InstallFinalize\t\t6600", |
| 273 | "AdminExecuteSequence:InstallInitialize\t\t1500", |
| 274 | "AdminExecuteSequence:InstallValidate\t\t1400", |
| 275 | "AdminUISequence:CostFinalize\t\t1000", |
| 276 | "AdminUISequence:CostInitialize\t\t800", |
| 277 | "AdminUISequence:CustomAction2\t\t801", |
| 278 | "AdminUISequence:ExecuteAction\t\t1300", |
| 279 | "AdminUISequence:FileCost\t\t900", |
| 280 | "AdvtExecuteSequence:CostFinalize\t\t1000", |
| 281 | "AdvtExecuteSequence:CostInitialize\t\t800", |
| 282 | "AdvtExecuteSequence:CustomAction2\t\t801", |
| 283 | "AdvtExecuteSequence:InstallFinalize\t\t6600", |
| 284 | "AdvtExecuteSequence:InstallInitialize\t\t1500", |
| 285 | "AdvtExecuteSequence:InstallValidate\t\t1400", |
| 286 | "AdvtExecuteSequence:PublishFeatures\t\t6300", |
| 287 | "AdvtExecuteSequence:PublishProduct\t\t6400", |
| 288 | "Binary:Binary1\t[Binary data]", |
| 289 | "CustomAction:CustomAction1\t1\tBinary1\tInvalidEntryPoint\t", |
| 290 | "CustomAction:CustomAction2\t51\tTestAdvtExecuteSequenceProperty\t1\t", |
| 291 | "CustomAction:CustomActionWithHiddenTarget\t9217\tBinary1\tInvalidEntryPoint\t", |
| 292 | "CustomAction:DiscardOptimismAllBeingsWhoProceed\t19\t\tAbandon hope all ye who enter here.\t", |
| 293 | "InstallExecuteSequence:CostFinalize\t\t1000", |
| 294 | "InstallExecuteSequence:CostInitialize\t\t800", |
| 295 | "InstallExecuteSequence:CreateFolders\t\t3700", |
| 296 | "InstallExecuteSequence:CustomAction2\t\t801", |
| 297 | "InstallExecuteSequence:FileCost\t\t900", |
| 298 | "InstallExecuteSequence:FindRelatedProducts\t\t25", |
| 299 | "InstallExecuteSequence:InstallFiles\t\t4000", |
| 300 | "InstallExecuteSequence:InstallFinalize\t\t6600", |
| 301 | "InstallExecuteSequence:InstallInitialize\t\t1500", |
| 302 | "InstallExecuteSequence:InstallValidate\t\t1400", |
| 303 | "InstallExecuteSequence:LaunchConditions\t\t100", |
| 304 | "InstallExecuteSequence:MigrateFeatureStates\t\t1200", |
| 305 | "InstallExecuteSequence:ProcessComponents\t\t1600", |
| 306 | "InstallExecuteSequence:PublishFeatures\t\t6300", |
| 307 | "InstallExecuteSequence:PublishProduct\t\t6400", |
| 308 | "InstallExecuteSequence:RegisterProduct\t\t6100", |
| 309 | "InstallExecuteSequence:RegisterUser\t\t6000", |
| 310 | "InstallExecuteSequence:RemoveExistingProducts\t\t1401", |
| 311 | "InstallExecuteSequence:RemoveFiles\t\t3500", |
| 312 | "InstallExecuteSequence:RemoveFolders\t\t3600", |
| 313 | "InstallExecuteSequence:UnpublishFeatures\t\t1800", |
| 314 | "InstallExecuteSequence:ValidateProductID\t\t700", |
| 315 | "InstallUISequence:CostFinalize\t\t1000", |
| 316 | "InstallUISequence:CostInitialize\t\t800", |
| 317 | "InstallUISequence:CustomAction2\t\t801", |
| 318 | "InstallUISequence:ExecuteAction\t\t1300", |
| 319 | "InstallUISequence:FileCost\t\t900", |
| 320 | "InstallUISequence:FindRelatedProducts\t\t25", |
| 321 | "InstallUISequence:LaunchConditions\t\t100", |
| 322 | "InstallUISequence:MigrateFeatureStates\t\t1200", |
| 323 | "InstallUISequence:ValidateProductID\t\t700", |
| 324 | "Property:MsiHiddenProperties\tCustomActionWithHiddenTarget", |
| 325 | }, results); |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | } |