diff options
Diffstat (limited to 'src')
60 files changed, 9140 insertions, 1 deletions
diff --git a/src/wix/WixToolset.Converters.Symbolizer/ConvertSymbols.cs b/src/wix/WixToolset.Converters.Symbolizer/ConvertSymbols.cs new file mode 100644 index 00000000..6418b2a6 --- /dev/null +++ b/src/wix/WixToolset.Converters.Symbolizer/ConvertSymbols.cs | |||
| @@ -0,0 +1,876 @@ | |||
| 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 WixToolset.Converters.Symbolizer | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Linq; | ||
| 8 | using WixToolset.Data; | ||
| 9 | using WixToolset.Data.Symbols; | ||
| 10 | using WixToolset.Data.WindowsInstaller; | ||
| 11 | using Wix3 = Microsoft.Tools.WindowsInstallerXml; | ||
| 12 | |||
| 13 | #pragma warning disable 1591 // TODO: add documentation | ||
| 14 | public static class ConvertSymbols | ||
| 15 | { | ||
| 16 | public static Intermediate ConvertFile(string path) | ||
| 17 | { | ||
| 18 | var output = Wix3.Output.Load(path, suppressVersionCheck: true, suppressSchema: true); | ||
| 19 | return ConvertOutput(output); | ||
| 20 | } | ||
| 21 | |||
| 22 | public static Intermediate ConvertOutput(Wix3.Output output) | ||
| 23 | #pragma warning restore 1591 | ||
| 24 | { | ||
| 25 | var section = new IntermediateSection(String.Empty, OutputType3ToSectionType4(output.Type)); | ||
| 26 | |||
| 27 | var wixMediaByDiskId = IndexWixMediaTableByDiskId(output); | ||
| 28 | var componentsById = IndexById<Wix3.Row>(output, "Component"); | ||
| 29 | var bindPathsById = IndexById<Wix3.Row>(output, "BindPath"); | ||
| 30 | var fontsById = IndexById<Wix3.Row>(output, "Font"); | ||
| 31 | var selfRegById = IndexById<Wix3.Row>(output, "SelfReg"); | ||
| 32 | var wixDirectoryById = IndexById<Wix3.Row>(output, "WixDirectory"); | ||
| 33 | var wixFileById = IndexById<Wix3.Row>(output, "WixFile"); | ||
| 34 | |||
| 35 | foreach (Wix3.Table table in output.Tables) | ||
| 36 | { | ||
| 37 | foreach (Wix3.Row row in table.Rows) | ||
| 38 | { | ||
| 39 | var symbol = GenerateSymbolFromRow(row, wixMediaByDiskId, componentsById, fontsById, bindPathsById, selfRegById, wixFileById, wixDirectoryById); | ||
| 40 | if (symbol != null) | ||
| 41 | { | ||
| 42 | section.AddSymbol(symbol); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | return new Intermediate(String.Empty, new[] { section }, localizationsByCulture: null); | ||
| 48 | } | ||
| 49 | |||
| 50 | private static Dictionary<int, Wix3.WixMediaRow> IndexWixMediaTableByDiskId(Wix3.Output output) | ||
| 51 | { | ||
| 52 | var wixMediaByDiskId = new Dictionary<int, Wix3.WixMediaRow>(); | ||
| 53 | var wixMediaTable = output.Tables["WixMedia"]; | ||
| 54 | |||
| 55 | if (wixMediaTable != null) | ||
| 56 | { | ||
| 57 | foreach (Wix3.WixMediaRow row in wixMediaTable.Rows) | ||
| 58 | { | ||
| 59 | wixMediaByDiskId.Add(FieldAsInt(row, 0), row); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | return wixMediaByDiskId; | ||
| 64 | } | ||
| 65 | |||
| 66 | private static Dictionary<string, T> IndexById<T>(Wix3.Output output, string tableName) where T : Wix3.Row | ||
| 67 | { | ||
| 68 | var byId = new Dictionary<string, T>(); | ||
| 69 | var table = output.Tables[tableName]; | ||
| 70 | |||
| 71 | if (table != null) | ||
| 72 | { | ||
| 73 | foreach (T row in table.Rows) | ||
| 74 | { | ||
| 75 | byId.Add(FieldAsString(row, 0), row); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | return byId; | ||
| 80 | } | ||
| 81 | |||
| 82 | private static IntermediateSymbol GenerateSymbolFromRow(Wix3.Row row, Dictionary<int, Wix3.WixMediaRow> wixMediaByDiskId, Dictionary<string, Wix3.Row> componentsById, Dictionary<string, Wix3.Row> fontsById, Dictionary<string, Wix3.Row> bindPathsById, Dictionary<string, Wix3.Row> selfRegById, Dictionary<string, Wix3.Row> wixFileById, Dictionary<string, Wix3.Row> wixDirectoryById) | ||
| 83 | { | ||
| 84 | var name = row.Table.Name; | ||
| 85 | switch (name) | ||
| 86 | { | ||
| 87 | case "_SummaryInformation": | ||
| 88 | return DefaultSymbolFromRow(typeof(SummaryInformationSymbol), row, columnZeroIsId: false); | ||
| 89 | case "ActionText": | ||
| 90 | return DefaultSymbolFromRow(typeof(ActionTextSymbol), row, columnZeroIsId: false); | ||
| 91 | case "AppId": | ||
| 92 | return DefaultSymbolFromRow(typeof(AppIdSymbol), row, columnZeroIsId: false); | ||
| 93 | case "AppSearch": | ||
| 94 | return DefaultSymbolFromRow(typeof(AppSearchSymbol), row, columnZeroIsId: false); | ||
| 95 | case "Billboard": | ||
| 96 | return DefaultSymbolFromRow(typeof(BillboardSymbol), row, columnZeroIsId: true); | ||
| 97 | case "Binary": | ||
| 98 | return DefaultSymbolFromRow(typeof(BinarySymbol), row, columnZeroIsId: true); | ||
| 99 | case "BindPath": | ||
| 100 | return null; | ||
| 101 | case "CCPSearch": | ||
| 102 | return DefaultSymbolFromRow(typeof(CCPSearchSymbol), row, columnZeroIsId: true); | ||
| 103 | case "Class": | ||
| 104 | return DefaultSymbolFromRow(typeof(ClassSymbol), row, columnZeroIsId: false); | ||
| 105 | case "CompLocator": | ||
| 106 | return DefaultSymbolFromRow(typeof(CompLocatorSymbol), row, columnZeroIsId: false); | ||
| 107 | case "Component": | ||
| 108 | { | ||
| 109 | var attributes = FieldAsNullableInt(row, 3); | ||
| 110 | |||
| 111 | var location = ComponentLocation.LocalOnly; | ||
| 112 | if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesSourceOnly) == WindowsInstallerConstants.MsidbComponentAttributesSourceOnly) | ||
| 113 | { | ||
| 114 | location = ComponentLocation.SourceOnly; | ||
| 115 | } | ||
| 116 | else if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesOptional) == WindowsInstallerConstants.MsidbComponentAttributesOptional) | ||
| 117 | { | ||
| 118 | location = ComponentLocation.Either; | ||
| 119 | } | ||
| 120 | |||
| 121 | var keyPath = FieldAsString(row, 5); | ||
| 122 | var keyPathType = String.IsNullOrEmpty(keyPath) ? ComponentKeyPathType.Directory : ComponentKeyPathType.File; | ||
| 123 | if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath) == WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath) | ||
| 124 | { | ||
| 125 | keyPathType = ComponentKeyPathType.Registry; | ||
| 126 | } | ||
| 127 | else if ((attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource) == WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource) | ||
| 128 | { | ||
| 129 | keyPathType = ComponentKeyPathType.OdbcDataSource; | ||
| 130 | } | ||
| 131 | |||
| 132 | return new ComponentSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 133 | { | ||
| 134 | ComponentId = FieldAsString(row, 1), | ||
| 135 | DirectoryRef = FieldAsString(row, 2), | ||
| 136 | Condition = FieldAsString(row, 4), | ||
| 137 | KeyPath = keyPath, | ||
| 138 | Location = location, | ||
| 139 | DisableRegistryReflection = (attributes & WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection) == WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection, | ||
| 140 | NeverOverwrite = (attributes & WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite) == WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite, | ||
| 141 | Permanent = (attributes & WindowsInstallerConstants.MsidbComponentAttributesPermanent) == WindowsInstallerConstants.MsidbComponentAttributesPermanent, | ||
| 142 | SharedDllRefCount = (attributes & WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount) == WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount, | ||
| 143 | Shared = (attributes & WindowsInstallerConstants.MsidbComponentAttributesShared) == WindowsInstallerConstants.MsidbComponentAttributesShared, | ||
| 144 | Transitive = (attributes & WindowsInstallerConstants.MsidbComponentAttributesTransitive) == WindowsInstallerConstants.MsidbComponentAttributesTransitive, | ||
| 145 | UninstallWhenSuperseded = (attributes & WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence) == WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence, | ||
| 146 | Win64 = (attributes & WindowsInstallerConstants.MsidbComponentAttributes64bit) == WindowsInstallerConstants.MsidbComponentAttributes64bit, | ||
| 147 | KeyPathType = keyPathType, | ||
| 148 | }; | ||
| 149 | } | ||
| 150 | |||
| 151 | case "Condition": | ||
| 152 | return DefaultSymbolFromRow(typeof(ConditionSymbol), row, columnZeroIsId: false); | ||
| 153 | case "CreateFolder": | ||
| 154 | return DefaultSymbolFromRow(typeof(CreateFolderSymbol), row, columnZeroIsId: false); | ||
| 155 | case "CustomAction": | ||
| 156 | { | ||
| 157 | var caType = FieldAsInt(row, 1); | ||
| 158 | var executionType = DetermineCustomActionExecutionType(caType); | ||
| 159 | var sourceType = DetermineCustomActionSourceType(caType); | ||
| 160 | var targetType = DetermineCustomActionTargetType(caType); | ||
| 161 | |||
| 162 | return new CustomActionSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 163 | { | ||
| 164 | ExecutionType = executionType, | ||
| 165 | SourceType = sourceType, | ||
| 166 | Source = FieldAsString(row, 2), | ||
| 167 | TargetType = targetType, | ||
| 168 | Target = FieldAsString(row, 3), | ||
| 169 | Win64 = (caType & WindowsInstallerConstants.MsidbCustomActionType64BitScript) == WindowsInstallerConstants.MsidbCustomActionType64BitScript, | ||
| 170 | TSAware = (caType & WindowsInstallerConstants.MsidbCustomActionTypeTSAware) == WindowsInstallerConstants.MsidbCustomActionTypeTSAware, | ||
| 171 | Impersonate = (caType & WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate) != WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate, | ||
| 172 | IgnoreResult = (caType & WindowsInstallerConstants.MsidbCustomActionTypeContinue) == WindowsInstallerConstants.MsidbCustomActionTypeContinue, | ||
| 173 | Hidden = (caType & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget) == WindowsInstallerConstants.MsidbCustomActionTypeHideTarget, | ||
| 174 | Async = (caType & WindowsInstallerConstants.MsidbCustomActionTypeAsync) == WindowsInstallerConstants.MsidbCustomActionTypeAsync, | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | case "Directory": | ||
| 179 | { | ||
| 180 | var id = FieldAsString(row, 0); | ||
| 181 | var splits = SplitDefaultDir(FieldAsString(row, 2)); | ||
| 182 | |||
| 183 | var symbol = new DirectorySymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, id)) | ||
| 184 | { | ||
| 185 | ParentDirectoryRef = FieldAsString(row, 1), | ||
| 186 | Name = splits[0], | ||
| 187 | ShortName = splits[1], | ||
| 188 | SourceName = splits[2], | ||
| 189 | SourceShortName = splits[3] | ||
| 190 | }; | ||
| 191 | |||
| 192 | if (wixDirectoryById.TryGetValue(id, out var wixDirectoryRow)) | ||
| 193 | { | ||
| 194 | symbol.ComponentGuidGenerationSeed = FieldAsString(wixDirectoryRow, 1); | ||
| 195 | } | ||
| 196 | |||
| 197 | return symbol; | ||
| 198 | } | ||
| 199 | case "DrLocator": | ||
| 200 | return DefaultSymbolFromRow(typeof(DrLocatorSymbol), row, columnZeroIsId: false); | ||
| 201 | case "DuplicateFile": | ||
| 202 | { | ||
| 203 | var splitName = FieldAsString(row, 3)?.Split('|'); | ||
| 204 | |||
| 205 | var symbol = new DuplicateFileSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 206 | { | ||
| 207 | ComponentRef = FieldAsString(row, 1), | ||
| 208 | FileRef = FieldAsString(row, 2), | ||
| 209 | DestinationName = splitName == null ? null : splitName.Length > 1 ? splitName[1] : splitName[0], | ||
| 210 | DestinationShortName = splitName == null ? null : splitName.Length > 1 ? splitName[0] : null, | ||
| 211 | DestinationFolder = FieldAsString(row, 4) | ||
| 212 | }; | ||
| 213 | |||
| 214 | return symbol; | ||
| 215 | } | ||
| 216 | case "Error": | ||
| 217 | return DefaultSymbolFromRow(typeof(ErrorSymbol), row, columnZeroIsId: false); | ||
| 218 | case "Extension": | ||
| 219 | return DefaultSymbolFromRow(typeof(ExtensionSymbol), row, columnZeroIsId: false); | ||
| 220 | case "Feature": | ||
| 221 | { | ||
| 222 | var attributes = FieldAsInt(row, 7); | ||
| 223 | var installDefault = FeatureInstallDefault.Local; | ||
| 224 | if ((attributes & WindowsInstallerConstants.MsidbFeatureAttributesFollowParent) == WindowsInstallerConstants.MsidbFeatureAttributesFollowParent) | ||
| 225 | { | ||
| 226 | installDefault = FeatureInstallDefault.FollowParent; | ||
| 227 | } | ||
| 228 | else if ((attributes & WindowsInstallerConstants.MsidbFeatureAttributesFavorSource) == WindowsInstallerConstants.MsidbFeatureAttributesFavorSource) | ||
| 229 | { | ||
| 230 | installDefault = FeatureInstallDefault.Source; | ||
| 231 | } | ||
| 232 | |||
| 233 | return new FeatureSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 234 | { | ||
| 235 | ParentFeatureRef = FieldAsString(row, 1), | ||
| 236 | Title = FieldAsString(row, 2), | ||
| 237 | Description = FieldAsString(row, 3), | ||
| 238 | Display = FieldAsInt(row, 4), // BUGBUGBUG: FieldAsNullableInt(row, 4), | ||
| 239 | Level = FieldAsInt(row, 5), | ||
| 240 | DirectoryRef = FieldAsString(row, 6), | ||
| 241 | DisallowAbsent = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent) == WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent, | ||
| 242 | DisallowAdvertise = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise) == WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise, | ||
| 243 | InstallDefault = installDefault, | ||
| 244 | TypicalDefault = (attributes & WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise) == WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise ? FeatureTypicalDefault.Advertise : FeatureTypicalDefault.Install, | ||
| 245 | }; | ||
| 246 | } | ||
| 247 | |||
| 248 | case "FeatureComponents": | ||
| 249 | return DefaultSymbolFromRow(typeof(FeatureComponentsSymbol), row, columnZeroIsId: false); | ||
| 250 | case "File": | ||
| 251 | { | ||
| 252 | var attributes = FieldAsNullableInt(row, 6); | ||
| 253 | |||
| 254 | FileSymbolAttributes symbolAttributes = 0; | ||
| 255 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesReadOnly) == WindowsInstallerConstants.MsidbFileAttributesReadOnly ? FileSymbolAttributes.ReadOnly : 0; | ||
| 256 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesHidden) == WindowsInstallerConstants.MsidbFileAttributesHidden ? FileSymbolAttributes.Hidden : 0; | ||
| 257 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesSystem) == WindowsInstallerConstants.MsidbFileAttributesSystem ? FileSymbolAttributes.System : 0; | ||
| 258 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesVital) == WindowsInstallerConstants.MsidbFileAttributesVital ? FileSymbolAttributes.Vital : 0; | ||
| 259 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesChecksum) == WindowsInstallerConstants.MsidbFileAttributesChecksum ? FileSymbolAttributes.Checksum : 0; | ||
| 260 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed ? FileSymbolAttributes.Uncompressed : 0; | ||
| 261 | symbolAttributes |= (attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed ? FileSymbolAttributes.Compressed : 0; | ||
| 262 | |||
| 263 | var id = FieldAsString(row, 0); | ||
| 264 | var splitName = FieldAsString(row, 2).Split('|'); | ||
| 265 | |||
| 266 | var symbol = new FileSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, id)) | ||
| 267 | { | ||
| 268 | ComponentRef = FieldAsString(row, 1), | ||
| 269 | Name = splitName.Length > 1 ? splitName[1] : splitName[0], | ||
| 270 | ShortName = splitName.Length > 1 ? splitName[0] : null, | ||
| 271 | FileSize = FieldAsInt(row, 3), | ||
| 272 | Version = FieldAsString(row, 4), | ||
| 273 | Language = FieldAsString(row, 5), | ||
| 274 | Attributes = symbolAttributes | ||
| 275 | }; | ||
| 276 | |||
| 277 | if (bindPathsById.TryGetValue(id, out var bindPathRow)) | ||
| 278 | { | ||
| 279 | symbol.BindPath = FieldAsString(bindPathRow, 1) ?? String.Empty; | ||
| 280 | } | ||
| 281 | |||
| 282 | if (fontsById.TryGetValue(id, out var fontRow)) | ||
| 283 | { | ||
| 284 | symbol.FontTitle = FieldAsString(fontRow, 1) ?? String.Empty; | ||
| 285 | } | ||
| 286 | |||
| 287 | if (selfRegById.TryGetValue(id, out var selfRegRow)) | ||
| 288 | { | ||
| 289 | symbol.SelfRegCost = FieldAsNullableInt(selfRegRow, 1) ?? 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | if (wixFileById.TryGetValue(id, out var wixFileRow)) | ||
| 293 | { | ||
| 294 | symbol.DirectoryRef = FieldAsString(wixFileRow, 4); | ||
| 295 | symbol.DiskId = FieldAsNullableInt(wixFileRow, 5) ?? 0; | ||
| 296 | symbol.Source = new IntermediateFieldPathValue { Path = FieldAsString(wixFileRow, 6) }; | ||
| 297 | symbol.PatchGroup = FieldAsInt(wixFileRow, 8); | ||
| 298 | symbol.PatchAttributes = (PatchAttributeType)FieldAsInt(wixFileRow, 10); | ||
| 299 | } | ||
| 300 | |||
| 301 | return symbol; | ||
| 302 | } | ||
| 303 | case "Font": | ||
| 304 | return null; | ||
| 305 | case "Icon": | ||
| 306 | return DefaultSymbolFromRow(typeof(IconSymbol), row, columnZeroIsId: true); | ||
| 307 | case "IniFile": | ||
| 308 | { | ||
| 309 | var splitName = FieldAsString(row, 1).Split('|'); | ||
| 310 | var action = FieldAsInt(row, 6); | ||
| 311 | |||
| 312 | var symbol = new IniFileSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 313 | { | ||
| 314 | FileName = splitName.Length > 1 ? splitName[1] : splitName[0], | ||
| 315 | ShortFileName = splitName.Length > 1 ? splitName[0] : null, | ||
| 316 | DirProperty = FieldAsString(row, 2), | ||
| 317 | Section = FieldAsString(row, 3), | ||
| 318 | Key = FieldAsString(row, 4), | ||
| 319 | Value = FieldAsString(row, 5), | ||
| 320 | Action = action == 3 ? IniFileActionType.AddTag : action == 1 ? IniFileActionType.CreateLine : IniFileActionType.AddLine, | ||
| 321 | ComponentRef = FieldAsString(row, 7), | ||
| 322 | }; | ||
| 323 | |||
| 324 | return symbol; | ||
| 325 | } | ||
| 326 | case "IniLocator": | ||
| 327 | { | ||
| 328 | var splitName = FieldAsString(row, 1).Split('|'); | ||
| 329 | |||
| 330 | var symbol = new IniLocatorSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 331 | { | ||
| 332 | FileName = splitName.Length > 1 ? splitName[1] : splitName[0], | ||
| 333 | ShortFileName = splitName.Length > 1 ? splitName[0] : null, | ||
| 334 | Section = FieldAsString(row, 2), | ||
| 335 | Key = FieldAsString(row, 3), | ||
| 336 | Field = FieldAsInt(row, 4), | ||
| 337 | Type = FieldAsInt(row, 5), | ||
| 338 | }; | ||
| 339 | |||
| 340 | return symbol; | ||
| 341 | } | ||
| 342 | case "LockPermissions": | ||
| 343 | return DefaultSymbolFromRow(typeof(LockPermissionsSymbol), row, columnZeroIsId: false); | ||
| 344 | case "Media": | ||
| 345 | { | ||
| 346 | var diskId = FieldAsInt(row, 0); | ||
| 347 | var symbol = new MediaSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, diskId)) | ||
| 348 | { | ||
| 349 | DiskId = diskId, | ||
| 350 | LastSequence = FieldAsNullableInt(row, 1), | ||
| 351 | DiskPrompt = FieldAsString(row, 2), | ||
| 352 | Cabinet = FieldAsString(row, 3), | ||
| 353 | VolumeLabel = FieldAsString(row, 4), | ||
| 354 | Source = FieldAsString(row, 5) | ||
| 355 | }; | ||
| 356 | |||
| 357 | if (wixMediaByDiskId.TryGetValue(diskId, out var wixMediaRow)) | ||
| 358 | { | ||
| 359 | var compressionLevel = FieldAsString(wixMediaRow, 1); | ||
| 360 | |||
| 361 | symbol.CompressionLevel = String.IsNullOrEmpty(compressionLevel) ? null : (CompressionLevel?)Enum.Parse(typeof(CompressionLevel), compressionLevel, true); | ||
| 362 | symbol.Layout = wixMediaRow.Layout; | ||
| 363 | } | ||
| 364 | |||
| 365 | return symbol; | ||
| 366 | } | ||
| 367 | case "MIME": | ||
| 368 | return DefaultSymbolFromRow(typeof(MIMESymbol), row, columnZeroIsId: false); | ||
| 369 | case "ModuleIgnoreTable": | ||
| 370 | return DefaultSymbolFromRow(typeof(ModuleIgnoreTableSymbol), row, columnZeroIsId: true); | ||
| 371 | case "MoveFile": | ||
| 372 | return DefaultSymbolFromRow(typeof(MoveFileSymbol), row, columnZeroIsId: true); | ||
| 373 | case "MsiAssembly": | ||
| 374 | { | ||
| 375 | var componentId = FieldAsString(row, 0); | ||
| 376 | if (componentsById.TryGetValue(componentId, out var componentRow)) | ||
| 377 | { | ||
| 378 | return new AssemblySymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(componentRow, 5))) | ||
| 379 | { | ||
| 380 | ComponentRef = componentId, | ||
| 381 | FeatureRef = FieldAsString(row, 1), | ||
| 382 | ManifestFileRef = FieldAsString(row, 2), | ||
| 383 | ApplicationFileRef = FieldAsString(row, 3), | ||
| 384 | Type = FieldAsNullableInt(row, 4) == 1 ? AssemblyType.Win32Assembly : AssemblyType.DotNetAssembly, | ||
| 385 | }; | ||
| 386 | } | ||
| 387 | |||
| 388 | return null; | ||
| 389 | } | ||
| 390 | case "MsiLockPermissionsEx": | ||
| 391 | return DefaultSymbolFromRow(typeof(MsiLockPermissionsExSymbol), row, columnZeroIsId: true); | ||
| 392 | case "MsiShortcutProperty": | ||
| 393 | return DefaultSymbolFromRow(typeof(MsiShortcutPropertySymbol), row, columnZeroIsId: true); | ||
| 394 | case "ODBCDataSource": | ||
| 395 | return DefaultSymbolFromRow(typeof(ODBCDataSourceSymbol), row, columnZeroIsId: true); | ||
| 396 | case "ODBCDriver": | ||
| 397 | return DefaultSymbolFromRow(typeof(ODBCDriverSymbol), row, columnZeroIsId: true); | ||
| 398 | case "ODBCTranslator": | ||
| 399 | return DefaultSymbolFromRow(typeof(ODBCTranslatorSymbol), row, columnZeroIsId: true); | ||
| 400 | case "ProgId": | ||
| 401 | return DefaultSymbolFromRow(typeof(ProgIdSymbol), row, columnZeroIsId: false); | ||
| 402 | case "Property": | ||
| 403 | return DefaultSymbolFromRow(typeof(PropertySymbol), row, columnZeroIsId: true); | ||
| 404 | case "PublishComponent": | ||
| 405 | return DefaultSymbolFromRow(typeof(PublishComponentSymbol), row, columnZeroIsId: false); | ||
| 406 | case "Registry": | ||
| 407 | { | ||
| 408 | var value = FieldAsString(row, 4); | ||
| 409 | var valueType = RegistryValueType.String; | ||
| 410 | var valueAction = RegistryValueActionType.Write; | ||
| 411 | |||
| 412 | if (!String.IsNullOrEmpty(value)) | ||
| 413 | { | ||
| 414 | if (value.StartsWith("#x", StringComparison.Ordinal)) | ||
| 415 | { | ||
| 416 | valueType = RegistryValueType.Binary; | ||
| 417 | value = value.Substring(2); | ||
| 418 | } | ||
| 419 | else if (value.StartsWith("#%", StringComparison.Ordinal)) | ||
| 420 | { | ||
| 421 | valueType = RegistryValueType.Expandable; | ||
| 422 | value = value.Substring(2); | ||
| 423 | } | ||
| 424 | else if (value.StartsWith("#", StringComparison.Ordinal)) | ||
| 425 | { | ||
| 426 | valueType = RegistryValueType.Integer; | ||
| 427 | value = value.Substring(1); | ||
| 428 | } | ||
| 429 | else if (value.StartsWith("[~]", StringComparison.Ordinal) && value.EndsWith("[~]", StringComparison.Ordinal)) | ||
| 430 | { | ||
| 431 | value = value.Substring(3, value.Length - 6); | ||
| 432 | valueType = RegistryValueType.MultiString; | ||
| 433 | valueAction = RegistryValueActionType.Write; | ||
| 434 | } | ||
| 435 | else if (value.StartsWith("[~]", StringComparison.Ordinal)) | ||
| 436 | { | ||
| 437 | value = value.Substring(3); | ||
| 438 | valueType = RegistryValueType.MultiString; | ||
| 439 | valueAction = RegistryValueActionType.Append; | ||
| 440 | } | ||
| 441 | else if (value.EndsWith("[~]", StringComparison.Ordinal)) | ||
| 442 | { | ||
| 443 | value = value.Substring(0, value.Length - 3); | ||
| 444 | valueType = RegistryValueType.MultiString; | ||
| 445 | valueAction = RegistryValueActionType.Prepend; | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | return new RegistrySymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 450 | { | ||
| 451 | Root = (RegistryRootType)FieldAsInt(row, 1), | ||
| 452 | Key = FieldAsString(row, 2), | ||
| 453 | Name = FieldAsString(row, 3), | ||
| 454 | Value = value, | ||
| 455 | ComponentRef = FieldAsString(row, 5), | ||
| 456 | ValueAction = valueAction, | ||
| 457 | ValueType = valueType, | ||
| 458 | }; | ||
| 459 | } | ||
| 460 | case "RegLocator": | ||
| 461 | { | ||
| 462 | var type = FieldAsInt(row, 4); | ||
| 463 | |||
| 464 | return new RegLocatorSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 465 | { | ||
| 466 | Root = (RegistryRootType)FieldAsInt(row, 1), | ||
| 467 | Key = FieldAsString(row, 2), | ||
| 468 | Name = FieldAsString(row, 3), | ||
| 469 | Type = (RegLocatorType)(type & 0xF), | ||
| 470 | Win64 = (type & WindowsInstallerConstants.MsidbLocatorType64bit) == WindowsInstallerConstants.MsidbLocatorType64bit | ||
| 471 | }; | ||
| 472 | } | ||
| 473 | case "RemoveFile": | ||
| 474 | { | ||
| 475 | var splitName = FieldAsString(row, 2).Split('|'); | ||
| 476 | var installMode = FieldAsInt(row, 4); | ||
| 477 | |||
| 478 | return new RemoveFileSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 479 | { | ||
| 480 | ComponentRef = FieldAsString(row, 1), | ||
| 481 | FileName = splitName.Length > 1 ? splitName[1] : splitName[0], | ||
| 482 | ShortFileName = splitName.Length > 1 ? splitName[0] : null, | ||
| 483 | DirPropertyRef = FieldAsString(row, 3), | ||
| 484 | OnInstall = (installMode & WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall) == WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall ? (bool?)true : null, | ||
| 485 | OnUninstall = (installMode & WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove) == WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove ? (bool?)true : null | ||
| 486 | }; | ||
| 487 | } | ||
| 488 | case "RemoveRegistry": | ||
| 489 | { | ||
| 490 | return new RemoveRegistrySymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 491 | { | ||
| 492 | Action = RemoveRegistryActionType.RemoveOnInstall, | ||
| 493 | Root = (RegistryRootType)FieldAsInt(row, 1), | ||
| 494 | Key = FieldAsString(row, 2), | ||
| 495 | Name = FieldAsString(row, 3), | ||
| 496 | ComponentRef = FieldAsString(row, 4), | ||
| 497 | }; | ||
| 498 | } | ||
| 499 | |||
| 500 | case "ReserveCost": | ||
| 501 | return DefaultSymbolFromRow(typeof(ReserveCostSymbol), row, columnZeroIsId: true); | ||
| 502 | case "SelfReg": | ||
| 503 | return null; | ||
| 504 | case "ServiceControl": | ||
| 505 | { | ||
| 506 | var events = FieldAsInt(row, 2); | ||
| 507 | var wait = FieldAsNullableInt(row, 4); | ||
| 508 | return new ServiceControlSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 509 | { | ||
| 510 | Name = FieldAsString(row, 1), | ||
| 511 | Arguments = FieldAsString(row, 3), | ||
| 512 | Wait = !wait.HasValue || wait.Value == 1, | ||
| 513 | ComponentRef = FieldAsString(row, 5), | ||
| 514 | InstallRemove = (events & WindowsInstallerConstants.MsidbServiceControlEventDelete) == WindowsInstallerConstants.MsidbServiceControlEventDelete, | ||
| 515 | UninstallRemove = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete) == WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete, | ||
| 516 | InstallStart = (events & WindowsInstallerConstants.MsidbServiceControlEventStart) == WindowsInstallerConstants.MsidbServiceControlEventStart, | ||
| 517 | UninstallStart = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallStart) == WindowsInstallerConstants.MsidbServiceControlEventUninstallStart, | ||
| 518 | InstallStop = (events & WindowsInstallerConstants.MsidbServiceControlEventStop) == WindowsInstallerConstants.MsidbServiceControlEventStop, | ||
| 519 | UninstallStop = (events & WindowsInstallerConstants.MsidbServiceControlEventUninstallStop) == WindowsInstallerConstants.MsidbServiceControlEventUninstallStop, | ||
| 520 | }; | ||
| 521 | } | ||
| 522 | |||
| 523 | case "ServiceInstall": | ||
| 524 | return DefaultSymbolFromRow(typeof(ServiceInstallSymbol), row, columnZeroIsId: true); | ||
| 525 | case "Shortcut": | ||
| 526 | { | ||
| 527 | var splitName = FieldAsString(row, 2).Split('|'); | ||
| 528 | |||
| 529 | return new ShortcutSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 530 | { | ||
| 531 | DirectoryRef = FieldAsString(row, 1), | ||
| 532 | Name = splitName.Length > 1 ? splitName[1] : splitName[0], | ||
| 533 | ShortName = splitName.Length > 1 ? splitName[0] : null, | ||
| 534 | ComponentRef = FieldAsString(row, 3), | ||
| 535 | Target = FieldAsString(row, 4), | ||
| 536 | Arguments = FieldAsString(row, 5), | ||
| 537 | Description = FieldAsString(row, 6), | ||
| 538 | Hotkey = FieldAsNullableInt(row, 7), | ||
| 539 | IconRef = FieldAsString(row, 8), | ||
| 540 | IconIndex = FieldAsNullableInt(row, 9), | ||
| 541 | Show = (ShortcutShowType?)FieldAsNullableInt(row, 10), | ||
| 542 | WorkingDirectory = FieldAsString(row, 11), | ||
| 543 | DisplayResourceDll = FieldAsString(row, 12), | ||
| 544 | DisplayResourceId = FieldAsNullableInt(row, 13), | ||
| 545 | DescriptionResourceDll = FieldAsString(row, 14), | ||
| 546 | DescriptionResourceId= FieldAsNullableInt(row, 15), | ||
| 547 | }; | ||
| 548 | } | ||
| 549 | case "Signature": | ||
| 550 | return DefaultSymbolFromRow(typeof(SignatureSymbol), row, columnZeroIsId: true); | ||
| 551 | case "UIText": | ||
| 552 | return DefaultSymbolFromRow(typeof(UITextSymbol), row, columnZeroIsId: true); | ||
| 553 | case "Upgrade": | ||
| 554 | { | ||
| 555 | var attributes = FieldAsInt(row, 4); | ||
| 556 | return new UpgradeSymbol(SourceLineNumber4(row.SourceLineNumbers), new Identifier(AccessModifier.Global, FieldAsString(row, 0))) | ||
| 557 | { | ||
| 558 | UpgradeCode = FieldAsString(row, 0), | ||
| 559 | VersionMin = FieldAsString(row, 1), | ||
| 560 | VersionMax = FieldAsString(row, 2), | ||
| 561 | Language = FieldAsString(row, 3), | ||
| 562 | Remove = FieldAsString(row, 5), | ||
| 563 | ActionProperty = FieldAsString(row, 6), | ||
| 564 | MigrateFeatures = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures) == WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures, | ||
| 565 | OnlyDetect = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect, | ||
| 566 | IgnoreRemoveFailures = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure) == WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure, | ||
| 567 | VersionMinInclusive = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive, | ||
| 568 | VersionMaxInclusive = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive, | ||
| 569 | ExcludeLanguages = (attributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive, | ||
| 570 | }; | ||
| 571 | } | ||
| 572 | case "Verb": | ||
| 573 | return DefaultSymbolFromRow(typeof(VerbSymbol), row, columnZeroIsId: false); | ||
| 574 | case "WixAction": | ||
| 575 | { | ||
| 576 | var sequenceTable = FieldAsString(row, 0); | ||
| 577 | return new WixActionSymbol(SourceLineNumber4(row.SourceLineNumbers)) | ||
| 578 | { | ||
| 579 | SequenceTable = (SequenceTable)Enum.Parse(typeof(SequenceTable), sequenceTable == "AdvtExecuteSequence" ? nameof(SequenceTable.AdvertiseExecuteSequence) : sequenceTable), | ||
| 580 | Action = FieldAsString(row, 1), | ||
| 581 | Condition = FieldAsString(row, 2), | ||
| 582 | Sequence = FieldAsNullableInt(row, 3), | ||
| 583 | Before = FieldAsString(row, 4), | ||
| 584 | After = FieldAsString(row, 5), | ||
| 585 | Overridable = FieldAsNullableInt(row, 6) != 0, | ||
| 586 | }; | ||
| 587 | } | ||
| 588 | case "WixBootstrapperApplication": | ||
| 589 | return DefaultSymbolFromRow(typeof(WixBootstrapperApplicationSymbol), row, columnZeroIsId: true); | ||
| 590 | case "WixBundleContainer": | ||
| 591 | return DefaultSymbolFromRow(typeof(WixBundleContainerSymbol), row, columnZeroIsId: true); | ||
| 592 | case "WixBundleVariable": | ||
| 593 | return DefaultSymbolFromRow(typeof(WixBundleVariableSymbol), row, columnZeroIsId: true); | ||
| 594 | case "WixChainItem": | ||
| 595 | return DefaultSymbolFromRow(typeof(WixChainItemSymbol), row, columnZeroIsId: true); | ||
| 596 | case "WixCustomTable": | ||
| 597 | return DefaultSymbolFromRow(typeof(WixCustomTableSymbol), row, columnZeroIsId: true); | ||
| 598 | case "WixDirectory": | ||
| 599 | return null; | ||
| 600 | case "WixFile": | ||
| 601 | return null; | ||
| 602 | case "WixInstanceTransforms": | ||
| 603 | return DefaultSymbolFromRow(typeof(WixInstanceTransformsSymbol), row, columnZeroIsId: true); | ||
| 604 | case "WixMedia": | ||
| 605 | return null; | ||
| 606 | case "WixMerge": | ||
| 607 | return DefaultSymbolFromRow(typeof(WixMergeSymbol), row, columnZeroIsId: true); | ||
| 608 | case "WixPatchBaseline": | ||
| 609 | return DefaultSymbolFromRow(typeof(WixPatchBaselineSymbol), row, columnZeroIsId: true); | ||
| 610 | case "WixProperty": | ||
| 611 | { | ||
| 612 | var attributes = FieldAsInt(row, 1); | ||
| 613 | return new WixPropertySymbol(SourceLineNumber4(row.SourceLineNumbers)) | ||
| 614 | { | ||
| 615 | PropertyRef = FieldAsString(row, 0), | ||
| 616 | Admin = (attributes & 0x1) == 0x1, | ||
| 617 | Hidden = (attributes & 0x2) == 0x2, | ||
| 618 | Secure = (attributes & 0x4) == 0x4, | ||
| 619 | }; | ||
| 620 | } | ||
| 621 | case "WixSuppressModularization": | ||
| 622 | { | ||
| 623 | return new WixSuppressModularizationSymbol(SourceLineNumber4(row.SourceLineNumbers)) | ||
| 624 | { | ||
| 625 | SuppressIdentifier = FieldAsString(row, 0) | ||
| 626 | }; | ||
| 627 | } | ||
| 628 | case "WixUI": | ||
| 629 | return DefaultSymbolFromRow(typeof(WixUISymbol), row, columnZeroIsId: true); | ||
| 630 | case "WixVariable": | ||
| 631 | return DefaultSymbolFromRow(typeof(WixVariableSymbol), row, columnZeroIsId: true); | ||
| 632 | default: | ||
| 633 | return GenericSymbolFromCustomRow(row, columnZeroIsId: false); | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | private static CustomActionTargetType DetermineCustomActionTargetType(int type) | ||
| 638 | { | ||
| 639 | var targetType = default(CustomActionTargetType); | ||
| 640 | |||
| 641 | if ((type & WindowsInstallerConstants.MsidbCustomActionTypeVBScript) == WindowsInstallerConstants.MsidbCustomActionTypeVBScript) | ||
| 642 | { | ||
| 643 | targetType = CustomActionTargetType.VBScript; | ||
| 644 | } | ||
| 645 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeJScript) == WindowsInstallerConstants.MsidbCustomActionTypeJScript) | ||
| 646 | { | ||
| 647 | targetType = CustomActionTargetType.JScript; | ||
| 648 | } | ||
| 649 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeTextData) == WindowsInstallerConstants.MsidbCustomActionTypeTextData) | ||
| 650 | { | ||
| 651 | targetType = CustomActionTargetType.TextData; | ||
| 652 | } | ||
| 653 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeExe) == WindowsInstallerConstants.MsidbCustomActionTypeExe) | ||
| 654 | { | ||
| 655 | targetType = CustomActionTargetType.Exe; | ||
| 656 | } | ||
| 657 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeDll) == WindowsInstallerConstants.MsidbCustomActionTypeDll) | ||
| 658 | { | ||
| 659 | targetType = CustomActionTargetType.Dll; | ||
| 660 | } | ||
| 661 | |||
| 662 | return targetType; | ||
| 663 | } | ||
| 664 | |||
| 665 | private static CustomActionSourceType DetermineCustomActionSourceType(int type) | ||
| 666 | { | ||
| 667 | var sourceType = CustomActionSourceType.Binary; | ||
| 668 | |||
| 669 | if ((type & WindowsInstallerConstants.MsidbCustomActionTypeProperty) == WindowsInstallerConstants.MsidbCustomActionTypeProperty) | ||
| 670 | { | ||
| 671 | sourceType = CustomActionSourceType.Property; | ||
| 672 | } | ||
| 673 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeDirectory) == WindowsInstallerConstants.MsidbCustomActionTypeDirectory) | ||
| 674 | { | ||
| 675 | sourceType = CustomActionSourceType.Directory; | ||
| 676 | } | ||
| 677 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeSourceFile) == WindowsInstallerConstants.MsidbCustomActionTypeSourceFile) | ||
| 678 | { | ||
| 679 | sourceType = CustomActionSourceType.File; | ||
| 680 | } | ||
| 681 | |||
| 682 | return sourceType; | ||
| 683 | } | ||
| 684 | |||
| 685 | private static CustomActionExecutionType DetermineCustomActionExecutionType(int type) | ||
| 686 | { | ||
| 687 | var executionType = CustomActionExecutionType.Immediate; | ||
| 688 | |||
| 689 | if ((type & (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit)) == (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit)) | ||
| 690 | { | ||
| 691 | executionType = CustomActionExecutionType.Commit; | ||
| 692 | } | ||
| 693 | else if ((type & (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback)) == (WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback)) | ||
| 694 | { | ||
| 695 | executionType = CustomActionExecutionType.Rollback; | ||
| 696 | } | ||
| 697 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeInScript) == WindowsInstallerConstants.MsidbCustomActionTypeInScript) | ||
| 698 | { | ||
| 699 | executionType = CustomActionExecutionType.Deferred; | ||
| 700 | } | ||
| 701 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat) == WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat) | ||
| 702 | { | ||
| 703 | executionType = CustomActionExecutionType.ClientRepeat; | ||
| 704 | } | ||
| 705 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess) == WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess) | ||
| 706 | { | ||
| 707 | executionType = CustomActionExecutionType.OncePerProcess; | ||
| 708 | } | ||
| 709 | else if ((type & WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence) == WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence) | ||
| 710 | { | ||
| 711 | executionType = CustomActionExecutionType.FirstSequence; | ||
| 712 | } | ||
| 713 | |||
| 714 | return executionType; | ||
| 715 | } | ||
| 716 | |||
| 717 | private static IntermediateFieldType ColumnType3ToIntermediateFieldType4(Wix3.ColumnType columnType) | ||
| 718 | { | ||
| 719 | switch (columnType) | ||
| 720 | { | ||
| 721 | case Wix3.ColumnType.Number: | ||
| 722 | return IntermediateFieldType.Number; | ||
| 723 | case Wix3.ColumnType.Object: | ||
| 724 | return IntermediateFieldType.Path; | ||
| 725 | case Wix3.ColumnType.Unknown: | ||
| 726 | case Wix3.ColumnType.String: | ||
| 727 | case Wix3.ColumnType.Localized: | ||
| 728 | case Wix3.ColumnType.Preserved: | ||
| 729 | default: | ||
| 730 | return IntermediateFieldType.String; | ||
| 731 | } | ||
| 732 | } | ||
| 733 | |||
| 734 | private static IntermediateSymbol DefaultSymbolFromRow(Type symbolType, Wix3.Row row, bool columnZeroIsId) | ||
| 735 | { | ||
| 736 | var id = columnZeroIsId ? GetIdentifierForRow(row) : null; | ||
| 737 | |||
| 738 | var createSymbol = symbolType.GetConstructor(new[] { typeof(SourceLineNumber), typeof(Identifier) }); | ||
| 739 | var symbol = (IntermediateSymbol)createSymbol.Invoke(new object[] { SourceLineNumber4(row.SourceLineNumbers), id }); | ||
| 740 | |||
| 741 | SetSymbolFieldsFromRow(row, symbol, columnZeroIsId); | ||
| 742 | |||
| 743 | return symbol; | ||
| 744 | } | ||
| 745 | |||
| 746 | private static IntermediateSymbol GenericSymbolFromCustomRow(Wix3.Row row, bool columnZeroIsId) | ||
| 747 | { | ||
| 748 | var columnDefinitions = row.Table.Definition.Columns.Cast<Wix3.ColumnDefinition>(); | ||
| 749 | var fieldDefinitions = columnDefinitions.Select(columnDefinition => | ||
| 750 | new IntermediateFieldDefinition(columnDefinition.Name, ColumnType3ToIntermediateFieldType4(columnDefinition.Type))).ToArray(); | ||
| 751 | var symbolDefinition = new IntermediateSymbolDefinition(row.Table.Name, fieldDefinitions, null); | ||
| 752 | |||
| 753 | var id = columnZeroIsId ? GetIdentifierForRow(row) : null; | ||
| 754 | |||
| 755 | var createSymbol = typeof(IntermediateSymbol).GetConstructor(new[] { typeof(IntermediateSymbolDefinition), typeof(SourceLineNumber), typeof(Identifier) }); | ||
| 756 | var symbol = (IntermediateSymbol)createSymbol.Invoke(new object[] { symbolDefinition, SourceLineNumber4(row.SourceLineNumbers), id }); | ||
| 757 | |||
| 758 | SetSymbolFieldsFromRow(row, symbol, columnZeroIsId); | ||
| 759 | |||
| 760 | return symbol; | ||
| 761 | } | ||
| 762 | |||
| 763 | private static void SetSymbolFieldsFromRow(Wix3.Row row, IntermediateSymbol symbol, bool columnZeroIsId) | ||
| 764 | { | ||
| 765 | var offset = 0; | ||
| 766 | if (columnZeroIsId) | ||
| 767 | { | ||
| 768 | offset = 1; | ||
| 769 | } | ||
| 770 | |||
| 771 | for (var i = offset; i < row.Fields.Length; ++i) | ||
| 772 | { | ||
| 773 | var column = row.Fields[i].Column; | ||
| 774 | switch (column.Type) | ||
| 775 | { | ||
| 776 | case Wix3.ColumnType.String: | ||
| 777 | case Wix3.ColumnType.Localized: | ||
| 778 | case Wix3.ColumnType.Object: | ||
| 779 | case Wix3.ColumnType.Preserved: | ||
| 780 | symbol.Set(i - offset, FieldAsString(row, i)); | ||
| 781 | break; | ||
| 782 | case Wix3.ColumnType.Number: | ||
| 783 | int? nullableValue = FieldAsNullableInt(row, i); | ||
| 784 | // TODO: Consider whether null values should be coerced to their default value when | ||
| 785 | // a column is not nullable. For now, just pass through the null. | ||
| 786 | //int value = FieldAsInt(row, i); | ||
| 787 | //symbol.Set(i - offset, column.IsNullable ? nullableValue : value); | ||
| 788 | symbol.Set(i - offset, nullableValue); | ||
| 789 | break; | ||
| 790 | case Wix3.ColumnType.Unknown: | ||
| 791 | break; | ||
| 792 | } | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | private static Identifier GetIdentifierForRow(Wix3.Row row) | ||
| 797 | { | ||
| 798 | var column = row.Fields[0].Column; | ||
| 799 | switch (column.Type) | ||
| 800 | { | ||
| 801 | case Wix3.ColumnType.String: | ||
| 802 | case Wix3.ColumnType.Localized: | ||
| 803 | case Wix3.ColumnType.Object: | ||
| 804 | case Wix3.ColumnType.Preserved: | ||
| 805 | return new Identifier(AccessModifier.Global, (string)row.Fields[0].Data); | ||
| 806 | case Wix3.ColumnType.Number: | ||
| 807 | return new Identifier(AccessModifier.Global, FieldAsInt(row, 0)); | ||
| 808 | default: | ||
| 809 | return null; | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | private static SectionType OutputType3ToSectionType4(Wix3.OutputType outputType) | ||
| 814 | { | ||
| 815 | switch (outputType) | ||
| 816 | { | ||
| 817 | case Wix3.OutputType.Bundle: | ||
| 818 | return SectionType.Bundle; | ||
| 819 | case Wix3.OutputType.Module: | ||
| 820 | return SectionType.Module; | ||
| 821 | case Wix3.OutputType.Patch: | ||
| 822 | return SectionType.Patch; | ||
| 823 | case Wix3.OutputType.PatchCreation: | ||
| 824 | return SectionType.PatchCreation; | ||
| 825 | case Wix3.OutputType.Product: | ||
| 826 | return SectionType.Product; | ||
| 827 | case Wix3.OutputType.Transform: | ||
| 828 | case Wix3.OutputType.Unknown: | ||
| 829 | default: | ||
| 830 | return SectionType.Unknown; | ||
| 831 | } | ||
| 832 | } | ||
| 833 | |||
| 834 | private static SourceLineNumber SourceLineNumber4(Wix3.SourceLineNumberCollection source) | ||
| 835 | { | ||
| 836 | return String.IsNullOrEmpty(source?.EncodedSourceLineNumbers) ? null : SourceLineNumber.CreateFromEncoded(source.EncodedSourceLineNumbers); | ||
| 837 | } | ||
| 838 | |||
| 839 | private static string FieldAsString(Wix3.Row row, int column) | ||
| 840 | { | ||
| 841 | return (string)row[column]; | ||
| 842 | } | ||
| 843 | |||
| 844 | private static int FieldAsInt(Wix3.Row row, int column) | ||
| 845 | { | ||
| 846 | return Convert.ToInt32(row[column]); | ||
| 847 | } | ||
| 848 | |||
| 849 | private static int? FieldAsNullableInt(Wix3.Row row, int column) | ||
| 850 | { | ||
| 851 | var field = row.Fields[column]; | ||
| 852 | if (field.Data == null) | ||
| 853 | { | ||
| 854 | return null; | ||
| 855 | } | ||
| 856 | else | ||
| 857 | { | ||
| 858 | return Convert.ToInt32(field.Data); | ||
| 859 | } | ||
| 860 | } | ||
| 861 | |||
| 862 | private static string[] SplitDefaultDir(string defaultDir) | ||
| 863 | { | ||
| 864 | var split1 = defaultDir.Split(':'); | ||
| 865 | var targetSplit = split1.Length > 1 ? split1[1].Split('|') : split1[0].Split('|'); | ||
| 866 | var sourceSplit = split1.Length > 1 ? split1[0].Split('|') : new[] { String.Empty }; | ||
| 867 | return new[] | ||
| 868 | { | ||
| 869 | targetSplit.Length > 1 ? targetSplit[1] : targetSplit[0], | ||
| 870 | targetSplit.Length > 1 ? targetSplit[0] : null, | ||
| 871 | sourceSplit.Length > 1 ? sourceSplit[1] : sourceSplit[0], | ||
| 872 | sourceSplit.Length > 1 ? sourceSplit[0] : null | ||
| 873 | }; | ||
| 874 | } | ||
| 875 | } | ||
| 876 | } | ||
diff --git a/src/wix/WixToolset.Converters.Symbolizer/WixToolset.Converters.Symbolizer.csproj b/src/wix/WixToolset.Converters.Symbolizer/WixToolset.Converters.Symbolizer.csproj new file mode 100644 index 00000000..445c3500 --- /dev/null +++ b/src/wix/WixToolset.Converters.Symbolizer/WixToolset.Converters.Symbolizer.csproj | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | |||
| 6 | <PropertyGroup> | ||
| 7 | <TargetFrameworks>netstandard2.0</TargetFrameworks> | ||
| 8 | <TargetFrameworks Condition=" '$(Configuration)'=='Release' ">$(TargetFrameworks);net461;net472</TargetFrameworks> | ||
| 9 | <Description>Symbolizer</Description> | ||
| 10 | <Title>WiX Toolset Converters Tuplizer</Title> | ||
| 11 | <DebugType>embedded</DebugType> | ||
| 12 | <PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
| 13 | <CreateDocumentationFile>true</CreateDocumentationFile> | ||
| 14 | </PropertyGroup> | ||
| 15 | |||
| 16 | <ItemGroup> | ||
| 17 | <Reference Include="wix" HintPath="..\deps\wix.dll" /> | ||
| 18 | <None Include="..\deps\wix.dll" Pack="true" PackagePath="lib\net461" /> | ||
| 19 | <None Include="..\deps\wix.dll" Pack="true" PackagePath="lib\net472" /> | ||
| 20 | <None Include="..\deps\wix.dll" Pack="true" PackagePath="lib\netstandard2.0" /> | ||
| 21 | </ItemGroup> | ||
| 22 | |||
| 23 | <ItemGroup> | ||
| 24 | <PackageReference Include="WixToolset.Core" Version="4.0.*" /> | ||
| 25 | <PackageReference Include="WixToolset.Core.WindowsInstaller" Version="4.0.*" /> | ||
| 26 | </ItemGroup> | ||
| 27 | |||
| 28 | <ItemGroup> | ||
| 29 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> | ||
| 30 | <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" /> | ||
| 31 | </ItemGroup> | ||
| 32 | </Project> | ||
diff --git a/src/wix/WixToolset.Converters.sln b/src/wix/WixToolset.Converters.sln new file mode 100644 index 00000000..ae8a3d92 --- /dev/null +++ b/src/wix/WixToolset.Converters.sln | |||
| @@ -0,0 +1,85 @@ | |||
| 1 |  | ||
| 2 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| 3 | # Visual Studio 15 | ||
| 4 | VisualStudioVersion = 15.0.26124.0 | ||
| 5 | MinimumVisualStudioVersion = 15.0.26124.0 | ||
| 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Converters", "WixToolset.Converters\WixToolset.Converters.csproj", "{6FAF6385-6598-4B89-972B-C31AFCA14538}" | ||
| 7 | EndProject | ||
| 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Converters.Symbolizer", "WixToolset.Converters.Symbolizer\WixToolset.Converters.Symbolizer.csproj", "{F051BCAF-698C-41D5-8427-164537CE5C5C}" | ||
| 9 | EndProject | ||
| 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{1B16A6C1-2B5D-4F9A-9DD5-FBC89B3CE31E}" | ||
| 11 | EndProject | ||
| 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Converters", "test\WixToolsetTest.Converters\WixToolsetTest.Converters.csproj", "{485C5038-97E1-4729-A54D-848CC69569FD}" | ||
| 13 | EndProject | ||
| 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Converters.Symbolizer", "test\WixToolsetTest.Converters.Symbolizer\WixToolsetTest.Converters.Symbolizer.csproj", "{9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}" | ||
| 15 | EndProject | ||
| 16 | Global | ||
| 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| 18 | Debug|Any CPU = Debug|Any CPU | ||
| 19 | Debug|x64 = Debug|x64 | ||
| 20 | Debug|x86 = Debug|x86 | ||
| 21 | Release|Any CPU = Release|Any CPU | ||
| 22 | Release|x64 = Release|x64 | ||
| 23 | Release|x86 = Release|x86 | ||
| 24 | EndGlobalSection | ||
| 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| 26 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| 27 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| 28 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| 29 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x64.Build.0 = Debug|Any CPU | ||
| 30 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| 31 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Debug|x86.Build.0 = Debug|Any CPU | ||
| 32 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| 33 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| 34 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x64.ActiveCfg = Release|Any CPU | ||
| 35 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x64.Build.0 = Release|Any CPU | ||
| 36 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x86.ActiveCfg = Release|Any CPU | ||
| 37 | {F051BCAF-698C-41D5-8427-164537CE5C5C}.Release|x86.Build.0 = Release|Any CPU | ||
| 38 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| 39 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| 40 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| 41 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x64.Build.0 = Debug|Any CPU | ||
| 42 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| 43 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Debug|x86.Build.0 = Debug|Any CPU | ||
| 44 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| 45 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| 46 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x64.ActiveCfg = Release|Any CPU | ||
| 47 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x64.Build.0 = Release|Any CPU | ||
| 48 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x86.ActiveCfg = Release|Any CPU | ||
| 49 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C}.Release|x86.Build.0 = Release|Any CPU | ||
| 50 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| 51 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| 52 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| 53 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x64.Build.0 = Debug|Any CPU | ||
| 54 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| 55 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Debug|x86.Build.0 = Debug|Any CPU | ||
| 56 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| 57 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| 58 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x64.ActiveCfg = Release|Any CPU | ||
| 59 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x64.Build.0 = Release|Any CPU | ||
| 60 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x86.ActiveCfg = Release|Any CPU | ||
| 61 | {6FAF6385-6598-4B89-972B-C31AFCA14538}.Release|x86.Build.0 = Release|Any CPU | ||
| 62 | {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| 63 | {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| 64 | {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x64.ActiveCfg = Debug|Any CPU | ||
| 65 | {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x64.Build.0 = Debug|Any CPU | ||
| 66 | {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x86.ActiveCfg = Debug|Any CPU | ||
| 67 | {485C5038-97E1-4729-A54D-848CC69569FD}.Debug|x86.Build.0 = Debug|Any CPU | ||
| 68 | {485C5038-97E1-4729-A54D-848CC69569FD}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| 69 | {485C5038-97E1-4729-A54D-848CC69569FD}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| 70 | {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x64.ActiveCfg = Release|Any CPU | ||
| 71 | {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x64.Build.0 = Release|Any CPU | ||
| 72 | {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x86.ActiveCfg = Release|Any CPU | ||
| 73 | {485C5038-97E1-4729-A54D-848CC69569FD}.Release|x86.Build.0 = Release|Any CPU | ||
| 74 | EndGlobalSection | ||
| 75 | GlobalSection(SolutionProperties) = preSolution | ||
| 76 | HideSolutionNode = FALSE | ||
| 77 | EndGlobalSection | ||
| 78 | GlobalSection(NestedProjects) = preSolution | ||
| 79 | {9DB36DB1-24A1-47A7-9E57-D48A2E33C13C} = {1B16A6C1-2B5D-4F9A-9DD5-FBC89B3CE31E} | ||
| 80 | {485C5038-97E1-4729-A54D-848CC69569FD} = {1B16A6C1-2B5D-4F9A-9DD5-FBC89B3CE31E} | ||
| 81 | EndGlobalSection | ||
| 82 | GlobalSection(ExtensibilityGlobals) = postSolution | ||
| 83 | SolutionGuid = {2E71F0EC-CF75-44DA-8353-7066EBD06564} | ||
| 84 | EndGlobalSection | ||
| 85 | EndGlobal | ||
diff --git a/src/wix/WixToolset.Converters.v3.ncrunchsolution b/src/wix/WixToolset.Converters.v3.ncrunchsolution new file mode 100644 index 00000000..f774ab93 --- /dev/null +++ b/src/wix/WixToolset.Converters.v3.ncrunchsolution | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | <SolutionConfiguration> | ||
| 2 | <Settings> | ||
| 3 | <AllowParallelTestExecution>False</AllowParallelTestExecution> | ||
| 4 | <SolutionConfigured>True</SolutionConfigured> | ||
| 5 | </Settings> | ||
| 6 | </SolutionConfiguration> \ No newline at end of file | ||
diff --git a/src/wix/WixToolset.Converters/ConvertCommand.cs b/src/wix/WixToolset.Converters/ConvertCommand.cs new file mode 100644 index 00000000..b6826f43 --- /dev/null +++ b/src/wix/WixToolset.Converters/ConvertCommand.cs | |||
| @@ -0,0 +1,60 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Threading; | ||
| 7 | using System.Threading.Tasks; | ||
| 8 | using WixToolset.Extensibility.Services; | ||
| 9 | |||
| 10 | internal class ConvertCommand : FixupCommandBase | ||
| 11 | { | ||
| 12 | private const string SettingsFileDefault = "wix.convert.settings.xml"; | ||
| 13 | |||
| 14 | public ConvertCommand(IServiceProvider serviceProvider) | ||
| 15 | { | ||
| 16 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
| 17 | } | ||
| 18 | |||
| 19 | private IMessaging Messaging { get; } | ||
| 20 | |||
| 21 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
| 22 | { | ||
| 23 | if (this.ShowHelp) | ||
| 24 | { | ||
| 25 | DisplayHelp(); | ||
| 26 | return Task.FromResult(-1); | ||
| 27 | } | ||
| 28 | |||
| 29 | this.ParseSettings(SettingsFileDefault); | ||
| 30 | |||
| 31 | var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | ||
| 32 | |||
| 33 | var errors = base.Inspect(Inspector, cancellationToken); | ||
| 34 | |||
| 35 | return Task.FromResult(errors); | ||
| 36 | |||
| 37 | int Inspector(string file, bool fix) | ||
| 38 | { | ||
| 39 | return converter.ConvertFile(file, fix); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | private static void DisplayHelp() | ||
| 44 | { | ||
| 45 | Console.WriteLine(); | ||
| 46 | Console.WriteLine("Usage: wix convert [options] sourceFile [sourceFile ...]"); | ||
| 47 | Console.WriteLine(); | ||
| 48 | Console.WriteLine("Options:"); | ||
| 49 | Console.WriteLine(" -h|--help Show command line help."); | ||
| 50 | Console.WriteLine(" --nologo Suppress displaying the logo information."); | ||
| 51 | Console.WriteLine(" -n|--dry-run Only display errors, do not update files."); | ||
| 52 | Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs."); | ||
| 53 | Console.WriteLine(" -set1<file> Primary settings file."); | ||
| 54 | Console.WriteLine(" -set2<file> Secondary settings file (overrides primary)."); | ||
| 55 | Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4)."); | ||
| 56 | Console.WriteLine(); | ||
| 57 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs b/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs new file mode 100644 index 00000000..06d3658c --- /dev/null +++ b/src/wix/WixToolset.Converters/ConverterExtensionCommandLine.cs | |||
| @@ -0,0 +1,46 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using WixToolset.Extensibility; | ||
| 8 | using WixToolset.Extensibility.Data; | ||
| 9 | using WixToolset.Extensibility.Services; | ||
| 10 | |||
| 11 | /// <summary> | ||
| 12 | /// Parses the "convert" command-line command. See <c>ConvertCommand</c> for | ||
| 13 | /// the bulk of the command-line processing. | ||
| 14 | /// </summary> | ||
| 15 | internal class ConverterExtensionCommandLine : BaseExtensionCommandLine | ||
| 16 | { | ||
| 17 | public ConverterExtensionCommandLine(IServiceProvider serviceProvider) | ||
| 18 | { | ||
| 19 | this.ServiceProvider = serviceProvider; | ||
| 20 | } | ||
| 21 | |||
| 22 | private IServiceProvider ServiceProvider { get; } | ||
| 23 | |||
| 24 | public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[] | ||
| 25 | { | ||
| 26 | new ExtensionCommandLineSwitch { Switch = "convert", Description = "Convert v3 source code to v4 source code." }, | ||
| 27 | new ExtensionCommandLineSwitch { Switch = "format", Description = "Ensures consistent formatting of source code." }, | ||
| 28 | }; | ||
| 29 | |||
| 30 | public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command) | ||
| 31 | { | ||
| 32 | command = null; | ||
| 33 | |||
| 34 | if ("convert".Equals(argument, StringComparison.OrdinalIgnoreCase)) | ||
| 35 | { | ||
| 36 | command = new ConvertCommand(this.ServiceProvider); | ||
| 37 | } | ||
| 38 | else if ("format".Equals(argument, StringComparison.OrdinalIgnoreCase)) | ||
| 39 | { | ||
| 40 | command = new FormatCommand(this.ServiceProvider); | ||
| 41 | } | ||
| 42 | |||
| 43 | return command != null; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
diff --git a/src/wix/WixToolset.Converters/ConverterExtensionFactory.cs b/src/wix/WixToolset.Converters/ConverterExtensionFactory.cs new file mode 100644 index 00000000..d4f480aa --- /dev/null +++ b/src/wix/WixToolset.Converters/ConverterExtensionFactory.cs | |||
| @@ -0,0 +1,30 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using WixToolset.Extensibility; | ||
| 7 | using WixToolset.Extensibility.Services; | ||
| 8 | |||
| 9 | internal class ConverterExtensionFactory : IExtensionFactory | ||
| 10 | { | ||
| 11 | public ConverterExtensionFactory(IServiceProvider serviceProvider) | ||
| 12 | { | ||
| 13 | this.ServiceProvider = serviceProvider; | ||
| 14 | } | ||
| 15 | |||
| 16 | private IServiceProvider ServiceProvider { get; } | ||
| 17 | |||
| 18 | public bool TryCreateExtension(Type extensionType, out object extension) | ||
| 19 | { | ||
| 20 | extension = null; | ||
| 21 | |||
| 22 | if (extensionType == typeof(IExtensionCommandLine)) | ||
| 23 | { | ||
| 24 | extension = new ConverterExtensionCommandLine(this.ServiceProvider); | ||
| 25 | } | ||
| 26 | |||
| 27 | return extension != null; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/src/wix/WixToolset.Converters/FixupCommandBase.cs b/src/wix/WixToolset.Converters/FixupCommandBase.cs new file mode 100644 index 00000000..21282d07 --- /dev/null +++ b/src/wix/WixToolset.Converters/FixupCommandBase.cs | |||
| @@ -0,0 +1,288 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | using System.Threading; | ||
| 9 | using System.Threading.Tasks; | ||
| 10 | using System.Xml; | ||
| 11 | using WixToolset.Extensibility.Data; | ||
| 12 | using WixToolset.Extensibility.Services; | ||
| 13 | |||
| 14 | internal abstract class FixupCommandBase : ICommandLineCommand | ||
| 15 | { | ||
| 16 | protected FixupCommandBase() | ||
| 17 | { | ||
| 18 | this.IndentationAmount = 4; // default indentation amount | ||
| 19 | this.ErrorsAsWarnings = new HashSet<string>(); | ||
| 20 | this.ExemptFiles = new HashSet<string>(); | ||
| 21 | this.IgnoreErrors = new HashSet<string>(); | ||
| 22 | this.SearchPatternResults = new HashSet<string>(); | ||
| 23 | this.SearchPatterns = new List<string>(); | ||
| 24 | } | ||
| 25 | |||
| 26 | public bool ShowLogo { get; private set; } | ||
| 27 | |||
| 28 | public bool StopParsing { get; private set; } | ||
| 29 | |||
| 30 | protected bool ShowHelp { get; set; } | ||
| 31 | |||
| 32 | protected CustomTableTarget CustomTableSetting { get; set; } | ||
| 33 | |||
| 34 | protected bool DryRun { get; set; } | ||
| 35 | |||
| 36 | protected HashSet<string> ErrorsAsWarnings { get; } | ||
| 37 | |||
| 38 | protected HashSet<string> IgnoreErrors { get; } | ||
| 39 | |||
| 40 | protected HashSet<string> ExemptFiles { get; } | ||
| 41 | |||
| 42 | protected int IndentationAmount { get; set; } | ||
| 43 | |||
| 44 | protected bool Recurse { get; set; } | ||
| 45 | |||
| 46 | private HashSet<string> SearchPatternResults { get; } | ||
| 47 | |||
| 48 | private List<string> SearchPatterns { get; } | ||
| 49 | |||
| 50 | private string SettingsFile1 { get; set; } | ||
| 51 | |||
| 52 | private string SettingsFile2 { get; set; } | ||
| 53 | |||
| 54 | public bool TryParseArgument(ICommandLineParser parser, string argument) | ||
| 55 | { | ||
| 56 | if (!parser.IsSwitch(argument)) | ||
| 57 | { | ||
| 58 | this.SearchPatterns.Add(argument); | ||
| 59 | return true; | ||
| 60 | } | ||
| 61 | |||
| 62 | var parameter = argument.Substring(1); | ||
| 63 | switch (parameter.ToLowerInvariant()) | ||
| 64 | { | ||
| 65 | case "?": | ||
| 66 | case "h": | ||
| 67 | case "-help": | ||
| 68 | this.ShowHelp = true; | ||
| 69 | this.ShowLogo = true; | ||
| 70 | this.StopParsing = true; | ||
| 71 | return true; | ||
| 72 | |||
| 73 | case "-custom-table": | ||
| 74 | var customTableSetting = parser.GetNextArgumentOrError(argument); | ||
| 75 | switch (customTableSetting) | ||
| 76 | { | ||
| 77 | case "bundle": | ||
| 78 | this.CustomTableSetting = CustomTableTarget.Bundle; | ||
| 79 | break; | ||
| 80 | case "msi": | ||
| 81 | this.CustomTableSetting = CustomTableTarget.Msi; | ||
| 82 | break; | ||
| 83 | default: | ||
| 84 | parser.ReportErrorArgument(argument); | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | return true; | ||
| 88 | |||
| 89 | case "n": | ||
| 90 | case "-dry-run": | ||
| 91 | this.DryRun = true; | ||
| 92 | return true; | ||
| 93 | |||
| 94 | case "nologo": | ||
| 95 | case "-nologo": | ||
| 96 | this.ShowLogo = false; | ||
| 97 | return true; | ||
| 98 | |||
| 99 | case "s": | ||
| 100 | case "r": | ||
| 101 | case "-recurse": | ||
| 102 | case "-recursive": | ||
| 103 | this.Recurse = true; | ||
| 104 | return true; | ||
| 105 | |||
| 106 | default: // other parameters | ||
| 107 | if (parameter.StartsWith("set1", StringComparison.Ordinal)) | ||
| 108 | { | ||
| 109 | this.SettingsFile1 = parameter.Substring(4); | ||
| 110 | return true; | ||
| 111 | } | ||
| 112 | else if (parameter.StartsWith("set2", StringComparison.Ordinal)) | ||
| 113 | { | ||
| 114 | this.SettingsFile2 = parameter.Substring(4); | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | else if (parameter.StartsWith("indent:", StringComparison.Ordinal)) | ||
| 118 | { | ||
| 119 | try | ||
| 120 | { | ||
| 121 | this.IndentationAmount = Convert.ToInt32(parameter.Substring(7)); | ||
| 122 | } | ||
| 123 | catch | ||
| 124 | { | ||
| 125 | parser.ReportErrorArgument(parameter); // $"Invalid numeric argument: {parameter}"; | ||
| 126 | } | ||
| 127 | return true; | ||
| 128 | } | ||
| 129 | |||
| 130 | return false; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken); | ||
| 135 | |||
| 136 | protected void ParseSettings(string defaultSettingsFile) | ||
| 137 | { | ||
| 138 | // parse the settings if any were specified | ||
| 139 | if (null != this.SettingsFile1 || null != this.SettingsFile2) | ||
| 140 | { | ||
| 141 | this.ParseSettingsFiles(this.SettingsFile1, this.SettingsFile2); | ||
| 142 | } | ||
| 143 | else | ||
| 144 | { | ||
| 145 | if (File.Exists(defaultSettingsFile)) | ||
| 146 | { | ||
| 147 | this.ParseSettingsFiles(defaultSettingsFile, null); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | protected int Inspect(Func<string, bool, int> inspector, CancellationToken cancellationToken) | ||
| 153 | { | ||
| 154 | var errors = this.InspectSubDirectories(inspector, Path.GetFullPath("."), cancellationToken); | ||
| 155 | |||
| 156 | foreach (var searchPattern in this.SearchPatterns) | ||
| 157 | { | ||
| 158 | if (!this.SearchPatternResults.Contains(searchPattern)) | ||
| 159 | { | ||
| 160 | Console.Error.WriteLine("Could not find file \"{0}\"", searchPattern); | ||
| 161 | errors++; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | return errors; | ||
| 166 | } | ||
| 167 | |||
| 168 | /// <summary> | ||
| 169 | /// Inspect sub-directories. | ||
| 170 | /// </summary> | ||
| 171 | /// <param name="inspector"></param> | ||
| 172 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> | ||
| 173 | /// <param name="cancellationToken"></param> | ||
| 174 | /// <returns>The number of errors that were found.</returns> | ||
| 175 | private int InspectSubDirectories(Func<string, bool, int> inspector, string directory, CancellationToken cancellationToken) | ||
| 176 | { | ||
| 177 | var errors = 0; | ||
| 178 | |||
| 179 | foreach (var searchPattern in this.SearchPatterns) | ||
| 180 | { | ||
| 181 | foreach (var sourceFilePath in GetFiles(directory, searchPattern)) | ||
| 182 | { | ||
| 183 | cancellationToken.ThrowIfCancellationRequested(); | ||
| 184 | |||
| 185 | var file = new FileInfo(sourceFilePath); | ||
| 186 | |||
| 187 | if (!this.ExemptFiles.Contains(file.Name.ToUpperInvariant())) | ||
| 188 | { | ||
| 189 | this.SearchPatternResults.Add(searchPattern); | ||
| 190 | errors += inspector(file.FullName, !this.DryRun); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | if (this.Recurse) | ||
| 196 | { | ||
| 197 | foreach (var childDirectoryPath in Directory.GetDirectories(directory)) | ||
| 198 | { | ||
| 199 | errors += this.InspectSubDirectories(inspector, childDirectoryPath, cancellationToken); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | return errors; | ||
| 204 | } | ||
| 205 | |||
| 206 | /// <summary> | ||
| 207 | /// Parse the primary and secondary settings files. | ||
| 208 | /// </summary> | ||
| 209 | /// <param name="localSettingsFile1">The primary settings file.</param> | ||
| 210 | /// <param name="localSettingsFile2">The secondary settings file.</param> | ||
| 211 | private void ParseSettingsFiles(string localSettingsFile1, string localSettingsFile2) | ||
| 212 | { | ||
| 213 | if (null == localSettingsFile1 && null != localSettingsFile2) | ||
| 214 | { | ||
| 215 | throw new ArgumentException("Cannot specify a secondary settings file (set2) without a primary settings file (set1).", nameof(localSettingsFile2)); | ||
| 216 | } | ||
| 217 | |||
| 218 | var settingsFile = localSettingsFile1; | ||
| 219 | while (null != settingsFile) | ||
| 220 | { | ||
| 221 | var doc = new XmlDocument(); | ||
| 222 | doc.Load(settingsFile); | ||
| 223 | |||
| 224 | // get the types of tests that will have their errors displayed as warnings | ||
| 225 | var testsIgnoredElements = doc.SelectNodes("/Settings/IgnoreErrors/Test"); | ||
| 226 | foreach (XmlElement test in testsIgnoredElements) | ||
| 227 | { | ||
| 228 | var key = test.GetAttribute("Id"); | ||
| 229 | this.IgnoreErrors.Add(key); | ||
| 230 | } | ||
| 231 | |||
| 232 | // get the types of tests that will have their errors displayed as warnings | ||
| 233 | var testsAsWarningsElements = doc.SelectNodes("/Settings/ErrorsAsWarnings/Test"); | ||
| 234 | foreach (XmlElement test in testsAsWarningsElements) | ||
| 235 | { | ||
| 236 | var key = test.GetAttribute("Id"); | ||
| 237 | this.ErrorsAsWarnings.Add(key); | ||
| 238 | } | ||
| 239 | |||
| 240 | // get the exempt files | ||
| 241 | var localExemptFiles = doc.SelectNodes("/Settings/ExemptFiles/File"); | ||
| 242 | foreach (XmlElement file in localExemptFiles) | ||
| 243 | { | ||
| 244 | var key = file.GetAttribute("Name").ToUpperInvariant(); | ||
| 245 | this.ExemptFiles.Add(key); | ||
| 246 | } | ||
| 247 | |||
| 248 | settingsFile = localSettingsFile2; | ||
| 249 | localSettingsFile2 = null; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /// <summary> | ||
| 254 | /// Get the files that match a search path pattern. | ||
| 255 | /// </summary> | ||
| 256 | /// <param name="baseDir">The base directory at which to begin the search.</param> | ||
| 257 | /// <param name="searchPath">The search path pattern.</param> | ||
| 258 | /// <returns>The files matching the pattern.</returns> | ||
| 259 | private static string[] GetFiles(string baseDir, string searchPath) | ||
| 260 | { | ||
| 261 | // convert alternate directory separators to the standard one | ||
| 262 | var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
| 263 | var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
| 264 | string[] files = null; | ||
| 265 | |||
| 266 | try | ||
| 267 | { | ||
| 268 | if (0 > lastSeparator) | ||
| 269 | { | ||
| 270 | files = Directory.GetFiles(baseDir, filePath); | ||
| 271 | } | ||
| 272 | else // found directory separator | ||
| 273 | { | ||
| 274 | var searchPattern = filePath.Substring(lastSeparator + 1); | ||
| 275 | |||
| 276 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), searchPattern); | ||
| 277 | } | ||
| 278 | } | ||
| 279 | catch (DirectoryNotFoundException) | ||
| 280 | { | ||
| 281 | // don't let this function throw the DirectoryNotFoundException. (this exception | ||
| 282 | // occurs for non-existant directories and invalid characters in the searchPattern) | ||
| 283 | } | ||
| 284 | |||
| 285 | return files; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
diff --git a/src/wix/WixToolset.Converters/FormatCommand.cs b/src/wix/WixToolset.Converters/FormatCommand.cs new file mode 100644 index 00000000..0861fc51 --- /dev/null +++ b/src/wix/WixToolset.Converters/FormatCommand.cs | |||
| @@ -0,0 +1,60 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Threading; | ||
| 7 | using System.Threading.Tasks; | ||
| 8 | using WixToolset.Extensibility.Services; | ||
| 9 | |||
| 10 | internal class FormatCommand : FixupCommandBase | ||
| 11 | { | ||
| 12 | private const string SettingsFileDefault = "wix.format.settings.xml"; | ||
| 13 | |||
| 14 | public FormatCommand(IServiceProvider serviceProvider) | ||
| 15 | { | ||
| 16 | this.Messaging = serviceProvider.GetService<IMessaging>(); | ||
| 17 | } | ||
| 18 | |||
| 19 | private IMessaging Messaging { get; } | ||
| 20 | |||
| 21 | public override Task<int> ExecuteAsync(CancellationToken cancellationToken) | ||
| 22 | { | ||
| 23 | if (this.ShowHelp) | ||
| 24 | { | ||
| 25 | DisplayHelp(); | ||
| 26 | return Task.FromResult(-1); | ||
| 27 | } | ||
| 28 | |||
| 29 | var converter = new WixConverter(this.Messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | ||
| 30 | |||
| 31 | this.ParseSettings(SettingsFileDefault); | ||
| 32 | |||
| 33 | var errors = base.Inspect(Inspector, cancellationToken); | ||
| 34 | |||
| 35 | return Task.FromResult(errors); | ||
| 36 | |||
| 37 | int Inspector(string file, bool fix) | ||
| 38 | { | ||
| 39 | return converter.FormatFile(file, fix); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | private static void DisplayHelp() | ||
| 44 | { | ||
| 45 | Console.WriteLine(); | ||
| 46 | Console.WriteLine("Usage: wix format [options] sourceFile [sourceFile ...]"); | ||
| 47 | Console.WriteLine(); | ||
| 48 | Console.WriteLine("Options:"); | ||
| 49 | Console.WriteLine(" -h|--help Show command line help."); | ||
| 50 | Console.WriteLine(" --nologo Suppress displaying the logo information."); | ||
| 51 | Console.WriteLine(" -n|--dry-run Only display errors, do not update files."); | ||
| 52 | Console.WriteLine(" -r|--recurse Search for matching files in current dir and subdirs."); | ||
| 53 | Console.WriteLine(" -set1<file> Primary settings file."); | ||
| 54 | Console.WriteLine(" -set2<file> Secondary settings file (overrides primary)."); | ||
| 55 | Console.WriteLine(" -indent:<n> Indentation multiple (overrides default of 4)."); | ||
| 56 | Console.WriteLine(); | ||
| 57 | Console.WriteLine(" sourceFile may use wildcards like *.wxs"); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/src/wix/WixToolset.Converters/WixConverter.cs b/src/wix/WixToolset.Converters/WixConverter.cs new file mode 100644 index 00000000..e42d0605 --- /dev/null +++ b/src/wix/WixToolset.Converters/WixConverter.cs | |||
| @@ -0,0 +1,2435 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Globalization; | ||
| 8 | using System.IO; | ||
| 9 | using System.Linq; | ||
| 10 | using System.Text; | ||
| 11 | using System.Text.RegularExpressions; | ||
| 12 | using System.Xml; | ||
| 13 | using System.Xml.Linq; | ||
| 14 | using WixToolset.Data; | ||
| 15 | using WixToolset.Data.WindowsInstaller; | ||
| 16 | using WixToolset.Extensibility.Services; | ||
| 17 | |||
| 18 | /// <summary> | ||
| 19 | /// How to convert CustomTable elements. | ||
| 20 | /// </summary> | ||
| 21 | public enum CustomTableTarget | ||
| 22 | { | ||
| 23 | /// <summary> | ||
| 24 | /// Ambiguous elements will be left alone. | ||
| 25 | /// </summary> | ||
| 26 | Unknown, | ||
| 27 | |||
| 28 | /// <summary> | ||
| 29 | /// Use CustomTable, CustomTableRef, and Unreal. | ||
| 30 | /// </summary> | ||
| 31 | Msi, | ||
| 32 | |||
| 33 | /// <summary> | ||
| 34 | /// Use BundleCustomData and BundleCustomDataRef. | ||
| 35 | /// </summary> | ||
| 36 | Bundle, | ||
| 37 | } | ||
| 38 | |||
| 39 | /// <summary> | ||
| 40 | /// WiX source code converter. | ||
| 41 | /// </summary> | ||
| 42 | public sealed class WixConverter | ||
| 43 | { | ||
| 44 | private enum ConvertOperation | ||
| 45 | { | ||
| 46 | Convert, | ||
| 47 | Format, | ||
| 48 | } | ||
| 49 | |||
| 50 | private static readonly Regex AddPrefix = new Regex(@"^[^a-zA-Z_]", RegexOptions.Compiled); | ||
| 51 | private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}", RegexOptions.Compiled); // non 'words' and assorted valid characters | ||
| 52 | |||
| 53 | private const char XDocumentNewLine = '\n'; // XDocument normalizes "\r\n" to just "\n". | ||
| 54 | private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
| 55 | private static readonly XNamespace Wix3Namespace = "http://schemas.microsoft.com/wix/2006/wi"; | ||
| 56 | private static readonly XNamespace WixBalNamespace = "http://wixtoolset.org/schemas/v4/wxs/bal"; | ||
| 57 | private static readonly XNamespace WixDependencyNamespace = "http://wixtoolset.org/schemas/v4/wxs/dependency"; | ||
| 58 | private static readonly XNamespace WixUtilNamespace = "http://wixtoolset.org/schemas/v4/wxs/util"; | ||
| 59 | private static readonly XNamespace WixFirewallNamespace = "http://wixtoolset.org/schemas/v4/wxs/firewall"; | ||
| 60 | |||
| 61 | private static readonly XName AdminExecuteSequenceElementName = WixNamespace + "AdminExecuteSequence"; | ||
| 62 | private static readonly XName AdminUISequenceSequenceElementName = WixNamespace + "AdminUISequence"; | ||
| 63 | private static readonly XName AdvertiseExecuteSequenceElementName = WixNamespace + "AdvertiseExecuteSequence"; | ||
| 64 | private static readonly XName InstallExecuteSequenceElementName = WixNamespace + "InstallExecuteSequence"; | ||
| 65 | private static readonly XName InstallUISequenceSequenceElementName = WixNamespace + "InstallUISequence"; | ||
| 66 | private static readonly XName BootstrapperApplicationElementName = WixNamespace + "BootstrapperApplication"; | ||
| 67 | private static readonly XName BootstrapperApplicationDllElementName = WixNamespace + "BootstrapperApplicationDll"; | ||
| 68 | private static readonly XName BootstrapperApplicationRefElementName = WixNamespace + "BootstrapperApplicationRef"; | ||
| 69 | private static readonly XName ApprovedExeForElevationElementName = WixNamespace + "ApprovedExeForElevation"; | ||
| 70 | private static readonly XName BundleAttributeElementName = WixNamespace + "BundleAttribute"; | ||
| 71 | private static readonly XName BundleAttributeDefinitionElementName = WixNamespace + "BundleAttributeDefinition"; | ||
| 72 | private static readonly XName BundleCustomDataElementName = WixNamespace + "BundleCustomData"; | ||
| 73 | private static readonly XName BundleCustomDataRefElementName = WixNamespace + "BundleCustomDataRef"; | ||
| 74 | private static readonly XName BundleElementElementName = WixNamespace + "BundleElement"; | ||
| 75 | private static readonly XName CustomTableElementName = WixNamespace + "CustomTable"; | ||
| 76 | private static readonly XName CustomTableRefElementName = WixNamespace + "CustomTableRef"; | ||
| 77 | private static readonly XName CatalogElementName = WixNamespace + "Catalog"; | ||
| 78 | private static readonly XName ColumnElementName = WixNamespace + "Column"; | ||
| 79 | private static readonly XName ComponentElementName = WixNamespace + "Component"; | ||
| 80 | private static readonly XName ControlElementName = WixNamespace + "Control"; | ||
| 81 | private static readonly XName ConditionElementName = WixNamespace + "Condition"; | ||
| 82 | private static readonly XName CreateFolderElementName = WixNamespace + "CreateFolder"; | ||
| 83 | private static readonly XName DataElementName = WixNamespace + "Data"; | ||
| 84 | private static readonly XName OldProvidesElementName = WixDependencyNamespace + "Provides"; | ||
| 85 | private static readonly XName OldRequiresElementName = WixDependencyNamespace + "Requires"; | ||
| 86 | private static readonly XName OldRequiresRefElementName = WixDependencyNamespace + "RequiresRef"; | ||
| 87 | private static readonly XName DirectoryElementName = WixNamespace + "Directory"; | ||
| 88 | private static readonly XName EmbeddedChainerElementName = WixNamespace + "EmbeddedChainer"; | ||
| 89 | private static readonly XName ErrorElementName = WixNamespace + "Error"; | ||
| 90 | private static readonly XName FeatureElementName = WixNamespace + "Feature"; | ||
| 91 | private static readonly XName FileElementName = WixNamespace + "File"; | ||
| 92 | private static readonly XName FragmentElementName = WixNamespace + "Fragment"; | ||
| 93 | private static readonly XName FirewallRemoteAddressElementName = WixFirewallNamespace + "RemoteAddress"; | ||
| 94 | private static readonly XName LaunchElementName = WixNamespace + "Launch"; | ||
| 95 | private static readonly XName LevelElementName = WixNamespace + "Level"; | ||
| 96 | private static readonly XName ExePackageElementName = WixNamespace + "ExePackage"; | ||
| 97 | private static readonly XName ExePackagePayloadElementName = WixNamespace + "ExePackagePayload"; | ||
| 98 | private static readonly XName ModuleElementName = WixNamespace + "Module"; | ||
| 99 | private static readonly XName MsiPackageElementName = WixNamespace + "MsiPackage"; | ||
| 100 | private static readonly XName MspPackageElementName = WixNamespace + "MspPackage"; | ||
| 101 | private static readonly XName MsuPackageElementName = WixNamespace + "MsuPackage"; | ||
| 102 | private static readonly XName MsuPackagePayloadElementName = WixNamespace + "MsuPackagePayload"; | ||
| 103 | private static readonly XName PackageElementName = WixNamespace + "Package"; | ||
| 104 | private static readonly XName PayloadElementName = WixNamespace + "Payload"; | ||
| 105 | private static readonly XName PermissionExElementName = WixNamespace + "PermissionEx"; | ||
| 106 | private static readonly XName ProductElementName = WixNamespace + "Product"; | ||
| 107 | private static readonly XName ProgressTextElementName = WixNamespace + "ProgressText"; | ||
| 108 | private static readonly XName PropertyRefElementName = WixNamespace + "PropertyRef"; | ||
| 109 | private static readonly XName PublishElementName = WixNamespace + "Publish"; | ||
| 110 | private static readonly XName ProvidesElementName = WixNamespace + "Provides"; | ||
| 111 | private static readonly XName RequiresElementName = WixNamespace + "Requires"; | ||
| 112 | private static readonly XName RequiresRefElementName = WixNamespace + "RequiresRef"; | ||
| 113 | private static readonly XName MultiStringValueElementName = WixNamespace + "MultiStringValue"; | ||
| 114 | private static readonly XName RemotePayloadElementName = WixNamespace + "RemotePayload"; | ||
| 115 | private static readonly XName RegistryKeyElementName = WixNamespace + "RegistryKey"; | ||
| 116 | private static readonly XName RegistrySearchElementName = WixNamespace + "RegistrySearch"; | ||
| 117 | private static readonly XName RequiredPrivilegeElementName = WixNamespace + "RequiredPrivilege"; | ||
| 118 | private static readonly XName RowElementName = WixNamespace + "Row"; | ||
| 119 | private static readonly XName ServiceArgumentElementName = WixNamespace + "ServiceArgument"; | ||
| 120 | private static readonly XName SetDirectoryElementName = WixNamespace + "SetDirectory"; | ||
| 121 | private static readonly XName SetPropertyElementName = WixNamespace + "SetProperty"; | ||
| 122 | private static readonly XName ShortcutPropertyElementName = WixNamespace + "ShortcutProperty"; | ||
| 123 | private static readonly XName SoftwareTagElementName = WixNamespace + "SoftwareTag"; | ||
| 124 | private static readonly XName SoftwareTagRefElementName = WixNamespace + "SoftwareTagRef"; | ||
| 125 | private static readonly XName StandardDirectoryElementName = WixNamespace + "StandardDirectory"; | ||
| 126 | private static readonly XName TagElementName = XNamespace.None + "Tag"; | ||
| 127 | private static readonly XName TagRefElementName = XNamespace.None + "TagRef"; | ||
| 128 | private static readonly XName TextElementName = WixNamespace + "Text"; | ||
| 129 | private static readonly XName UITextElementName = WixNamespace + "UIText"; | ||
| 130 | private static readonly XName VariableElementName = WixNamespace + "Variable"; | ||
| 131 | private static readonly XName VerbElementName = WixNamespace + "Verb"; | ||
| 132 | private static readonly XName BalUseUILanguagesName = WixBalNamespace + "UseUILanguages"; | ||
| 133 | private static readonly XName BalStandardBootstrapperApplicationName = WixBalNamespace + "WixStandardBootstrapperApplication"; | ||
| 134 | private static readonly XName BalManagedBootstrapperApplicationHostName = WixBalNamespace + "WixManagedBootstrapperApplicationHost"; | ||
| 135 | private static readonly XName BalOldDotNetCoreBootstrapperApplicationName = WixBalNamespace + "WixDotNetCoreBootstrapperApplication"; | ||
| 136 | private static readonly XName BalNewDotNetCoreBootstrapperApplicationName = WixBalNamespace + "WixDotNetCoreBootstrapperApplicationHost"; | ||
| 137 | private static readonly XName UtilCloseApplicationElementName = WixUtilNamespace + "CloseApplication"; | ||
| 138 | private static readonly XName UtilPermissionExElementName = WixUtilNamespace + "PermissionEx"; | ||
| 139 | private static readonly XName UtilRegistrySearchName = WixUtilNamespace + "RegistrySearch"; | ||
| 140 | private static readonly XName UtilXmlConfigElementName = WixUtilNamespace + "XmlConfig"; | ||
| 141 | private static readonly XName CustomActionElementName = WixNamespace + "CustomAction"; | ||
| 142 | private static readonly XName CustomActionRefElementName = WixNamespace + "CustomActionRef"; | ||
| 143 | private static readonly XName PropertyElementName = WixNamespace + "Property"; | ||
| 144 | private static readonly XName Wix4ElementName = WixNamespace + "Wix"; | ||
| 145 | private static readonly XName Wix3ElementName = Wix3Namespace + "Wix"; | ||
| 146 | private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix"; | ||
| 147 | private static readonly XName Include4ElementName = WixNamespace + "Include"; | ||
| 148 | private static readonly XName Include3ElementName = Wix3Namespace + "Include"; | ||
| 149 | private static readonly XName IncludeElementWithoutNamespaceName = XNamespace.None + "Include"; | ||
| 150 | private static readonly XName SummaryInformationElementName = WixNamespace + "SummaryInformation"; | ||
| 151 | private static readonly XName MediaTemplateElementName = WixNamespace + "MediaTemplate"; | ||
| 152 | |||
| 153 | private static readonly XName DependencyCheckAttributeName = WixDependencyNamespace + "Check"; | ||
| 154 | private static readonly XName DependencyEnforceAttributeName = WixDependencyNamespace + "Enforce"; | ||
| 155 | |||
| 156 | private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>() | ||
| 157 | { | ||
| 158 | { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" }, | ||
| 159 | { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" }, | ||
| 160 | { "http://schemas.microsoft.com/wix/DependencyExtension", WixDependencyNamespace }, | ||
| 161 | { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" }, | ||
| 162 | { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" }, | ||
| 163 | { "http://schemas.microsoft.com/wix/HttpExtension", "http://wixtoolset.org/schemas/v4/wxs/http" }, | ||
| 164 | { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" }, | ||
| 165 | { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" }, | ||
| 166 | { "http://schemas.microsoft.com/wix/NetFxExtension", "http://wixtoolset.org/schemas/v4/wxs/netfx" }, | ||
| 167 | { "http://schemas.microsoft.com/wix/PSExtension", "http://wixtoolset.org/schemas/v4/wxs/powershell" }, | ||
| 168 | { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" }, | ||
| 169 | { "http://schemas.microsoft.com/wix/TagExtension", XNamespace.None }, | ||
| 170 | { "http://schemas.microsoft.com/wix/UtilExtension", WixUtilNamespace }, | ||
| 171 | { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" }, | ||
| 172 | { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" }, | ||
| 173 | { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" }, | ||
| 174 | { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" }, | ||
| 175 | { "http://schemas.microsoft.com/wix/2006/localization", "http://wixtoolset.org/schemas/v4/wxl" }, | ||
| 176 | { "http://schemas.microsoft.com/wix/2006/libraries", "http://wixtoolset.org/schemas/v4/wixlib" }, | ||
| 177 | { "http://schemas.microsoft.com/wix/2006/objects", "http://wixtoolset.org/schemas/v4/wixobj" }, | ||
| 178 | { "http://schemas.microsoft.com/wix/2006/outputs", "http://wixtoolset.org/schemas/v4/wixout" }, | ||
| 179 | { "http://schemas.microsoft.com/wix/2007/pdbs", "http://wixtoolset.org/schemas/v4/wixpdb" }, | ||
| 180 | { "http://schemas.microsoft.com/wix/2003/04/actions", "http://wixtoolset.org/schemas/v4/wi/actions" }, | ||
| 181 | { "http://schemas.microsoft.com/wix/2006/tables", "http://wixtoolset.org/schemas/v4/wi/tables" }, | ||
| 182 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | ||
| 183 | }; | ||
| 184 | |||
| 185 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; | ||
| 186 | private readonly Regex DeprecatedPrefixRegex = new Regex(@"(?<=(^|[^\$])(\$\$)*)\$(?=\(loc\.[^.].*\))", | ||
| 187 | RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture); | ||
| 188 | |||
| 189 | /// <summary> | ||
| 190 | /// Instantiate a new Converter class. | ||
| 191 | /// </summary> | ||
| 192 | /// <param name="messaging"></param> | ||
| 193 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 194 | /// <param name="errorsAsWarnings">Test errors to display as warnings.</param> | ||
| 195 | /// <param name="ignoreErrors">Test errors to ignore.</param> | ||
| 196 | /// <param name="customTableTarget">How to convert CustomTable elements.</param> | ||
| 197 | public WixConverter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null, CustomTableTarget customTableTarget = CustomTableTarget.Unknown) | ||
| 198 | { | ||
| 199 | this.ConvertElementMapping = new Dictionary<XName, Action<XElement>> | ||
| 200 | { | ||
| 201 | { WixConverter.AdminExecuteSequenceElementName, this.ConvertSequenceElement }, | ||
| 202 | { WixConverter.AdminUISequenceSequenceElementName, this.ConvertSequenceElement }, | ||
| 203 | { WixConverter.AdvertiseExecuteSequenceElementName, this.ConvertSequenceElement }, | ||
| 204 | { WixConverter.InstallUISequenceSequenceElementName, this.ConvertSequenceElement }, | ||
| 205 | { WixConverter.InstallExecuteSequenceElementName, this.ConvertSequenceElement }, | ||
| 206 | { WixConverter.BootstrapperApplicationElementName, this.ConvertBootstrapperApplicationElement }, | ||
| 207 | { WixConverter.BootstrapperApplicationRefElementName, this.ConvertBootstrapperApplicationRefElement }, | ||
| 208 | { WixConverter.ApprovedExeForElevationElementName, this.ConvertApprovedExeForElevationElement }, | ||
| 209 | { WixConverter.CatalogElementName, this.ConvertCatalogElement }, | ||
| 210 | { WixConverter.ColumnElementName, this.ConvertColumnElement }, | ||
| 211 | { WixConverter.ComponentElementName, this.ConvertComponentElement }, | ||
| 212 | { WixConverter.ControlElementName, this.ConvertControlElement }, | ||
| 213 | { WixConverter.CustomActionElementName, this.ConvertCustomActionElement }, | ||
| 214 | { WixConverter.CustomTableElementName, this.ConvertCustomTableElement }, | ||
| 215 | { WixConverter.DataElementName, this.ConvertDataElement }, | ||
| 216 | { WixConverter.DirectoryElementName, this.ConvertDirectoryElement }, | ||
| 217 | { WixConverter.FeatureElementName, this.ConvertFeatureElement }, | ||
| 218 | { WixConverter.FileElementName, this.ConvertFileElement }, | ||
| 219 | { WixConverter.FragmentElementName, this.ConvertFragmentElement }, | ||
| 220 | { WixConverter.FirewallRemoteAddressElementName, this.ConvertFirewallRemoteAddressElement }, | ||
| 221 | { WixConverter.EmbeddedChainerElementName, this.ConvertEmbeddedChainerElement }, | ||
| 222 | { WixConverter.ErrorElementName, this.ConvertErrorElement }, | ||
| 223 | { WixConverter.ExePackageElementName, this.ConvertExePackageElement }, | ||
| 224 | { WixConverter.ModuleElementName, this.ConvertModuleElement }, | ||
| 225 | { WixConverter.MsiPackageElementName, this.ConvertWindowsInstallerPackageElement }, | ||
| 226 | { WixConverter.MspPackageElementName, this.ConvertWindowsInstallerPackageElement }, | ||
| 227 | { WixConverter.MsuPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 228 | { WixConverter.OldProvidesElementName, this.ConvertProvidesElement }, | ||
| 229 | { WixConverter.OldRequiresElementName, this.ConvertRequiresElement }, | ||
| 230 | { WixConverter.OldRequiresRefElementName, this.ConvertRequiresRefElement }, | ||
| 231 | { WixConverter.PayloadElementName, this.ConvertSuppressSignatureValidation }, | ||
| 232 | { WixConverter.PermissionExElementName, this.ConvertPermissionExElement }, | ||
| 233 | { WixConverter.ProductElementName, this.ConvertProductElement }, | ||
| 234 | { WixConverter.ProgressTextElementName, this.ConvertProgressTextElement }, | ||
| 235 | { WixConverter.PropertyRefElementName, this.ConvertPropertyRefElement }, | ||
| 236 | { WixConverter.PublishElementName, this.ConvertPublishElement }, | ||
| 237 | { WixConverter.MultiStringValueElementName, this.ConvertMultiStringValueElement }, | ||
| 238 | { WixConverter.RegistryKeyElementName, this.ConvertRegistryKeyElement }, | ||
| 239 | { WixConverter.RegistrySearchElementName, this.ConvertRegistrySearchElement }, | ||
| 240 | { WixConverter.RemotePayloadElementName, this.ConvertRemotePayloadElement }, | ||
| 241 | { WixConverter.RequiredPrivilegeElementName, this.ConvertRequiredPrivilegeElement }, | ||
| 242 | { WixConverter.CustomActionRefElementName, this.ConvertCustomActionRefElement }, | ||
| 243 | { WixConverter.ServiceArgumentElementName, this.ConvertServiceArgumentElement }, | ||
| 244 | { WixConverter.SetDirectoryElementName, this.ConvertSetDirectoryElement }, | ||
| 245 | { WixConverter.SetPropertyElementName, this.ConvertSetPropertyElement }, | ||
| 246 | { WixConverter.ShortcutPropertyElementName, this.ConvertShortcutPropertyElement }, | ||
| 247 | { WixConverter.TagElementName, this.ConvertTagElement }, | ||
| 248 | { WixConverter.TagRefElementName, this.ConvertTagRefElement }, | ||
| 249 | { WixConverter.TextElementName, this.ConvertTextElement }, | ||
| 250 | { WixConverter.UITextElementName, this.ConvertUITextElement }, | ||
| 251 | { WixConverter.VariableElementName, this.ConvertVariableElement }, | ||
| 252 | { WixConverter.UtilCloseApplicationElementName, this.ConvertUtilCloseApplicationElementName }, | ||
| 253 | { WixConverter.UtilPermissionExElementName, this.ConvertUtilPermissionExElement }, | ||
| 254 | { WixConverter.UtilRegistrySearchName, this.ConvertUtilRegistrySearchElement }, | ||
| 255 | { WixConverter.UtilXmlConfigElementName, this.ConvertUtilXmlConfigElement }, | ||
| 256 | { WixConverter.PropertyElementName, this.ConvertPropertyElement }, | ||
| 257 | { WixConverter.WixElementWithoutNamespaceName, this.ConvertElementWithoutNamespace }, | ||
| 258 | { WixConverter.IncludeElementWithoutNamespaceName, this.ConvertElementWithoutNamespace }, | ||
| 259 | { WixConverter.VerbElementName, this.ConvertVerbElement }, | ||
| 260 | }; | ||
| 261 | |||
| 262 | this.Messaging = messaging; | ||
| 263 | |||
| 264 | this.IndentationAmount = indentationAmount; | ||
| 265 | |||
| 266 | this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings)); | ||
| 267 | |||
| 268 | this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors)); | ||
| 269 | |||
| 270 | this.CustomTableSetting = customTableTarget; | ||
| 271 | } | ||
| 272 | |||
| 273 | private CustomTableTarget CustomTableSetting { get; } | ||
| 274 | |||
| 275 | private int Errors { get; set; } | ||
| 276 | |||
| 277 | private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } | ||
| 278 | |||
| 279 | private HashSet<ConverterTestType> IgnoreErrors { get; set; } | ||
| 280 | |||
| 281 | private IMessaging Messaging { get; } | ||
| 282 | |||
| 283 | private int IndentationAmount { get; set; } | ||
| 284 | |||
| 285 | private ConvertOperation Operation { get; set; } | ||
| 286 | |||
| 287 | private string SourceFile { get; set; } | ||
| 288 | |||
| 289 | private int SourceVersion { get; set; } | ||
| 290 | |||
| 291 | /// <summary> | ||
| 292 | /// Convert a file. | ||
| 293 | /// </summary> | ||
| 294 | /// <param name="sourceFile">The file to convert.</param> | ||
| 295 | /// <param name="saveConvertedFile">Option to save the converted errors that are found.</param> | ||
| 296 | /// <returns>The number of errors found.</returns> | ||
| 297 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | ||
| 298 | { | ||
| 299 | var document = this.OpenSourceFile(sourceFile); | ||
| 300 | |||
| 301 | if (document is null) | ||
| 302 | { | ||
| 303 | return 1; | ||
| 304 | } | ||
| 305 | |||
| 306 | this.ConvertDocument(document); | ||
| 307 | |||
| 308 | // Fix errors if requested and necessary. | ||
| 309 | if (saveConvertedFile && 0 < this.Errors) | ||
| 310 | { | ||
| 311 | this.SaveDocument(document); | ||
| 312 | } | ||
| 313 | |||
| 314 | return this.Errors; | ||
| 315 | } | ||
| 316 | |||
| 317 | /// <summary> | ||
| 318 | /// Convert a document. | ||
| 319 | /// </summary> | ||
| 320 | /// <param name="document">The document to convert.</param> | ||
| 321 | /// <returns>The number of errors found.</returns> | ||
| 322 | public int ConvertDocument(XDocument document) | ||
| 323 | { | ||
| 324 | // Reset the instance info. | ||
| 325 | this.Errors = 0; | ||
| 326 | this.SourceVersion = 0; | ||
| 327 | this.Operation = ConvertOperation.Convert; | ||
| 328 | |||
| 329 | // Remove the declaration. | ||
| 330 | if (null != document.Declaration | ||
| 331 | && this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | ||
| 332 | { | ||
| 333 | document.Declaration = null; | ||
| 334 | TrimLeadingText(document); | ||
| 335 | } | ||
| 336 | |||
| 337 | // Start converting the nodes at the top. | ||
| 338 | this.ConvertNodes(document.Nodes(), 0); | ||
| 339 | this.RemoveUnusedNamespaces(document.Root); | ||
| 340 | |||
| 341 | return this.Errors; | ||
| 342 | } | ||
| 343 | |||
| 344 | /// <summary> | ||
| 345 | /// Format a file. | ||
| 346 | /// </summary> | ||
| 347 | /// <param name="sourceFile">The file to format.</param> | ||
| 348 | /// <param name="saveConvertedFile">Option to save the format errors that are found.</param> | ||
| 349 | /// <returns>The number of errors found.</returns> | ||
| 350 | public int FormatFile(string sourceFile, bool saveConvertedFile) | ||
| 351 | { | ||
| 352 | var document = this.OpenSourceFile(sourceFile); | ||
| 353 | |||
| 354 | if (document is null) | ||
| 355 | { | ||
| 356 | return 1; | ||
| 357 | } | ||
| 358 | |||
| 359 | this.FormatDocument(document); | ||
| 360 | |||
| 361 | // Fix errors if requested and necessary. | ||
| 362 | if (saveConvertedFile && 0 < this.Errors) | ||
| 363 | { | ||
| 364 | this.SaveDocument(document); | ||
| 365 | } | ||
| 366 | |||
| 367 | return this.Errors; | ||
| 368 | } | ||
| 369 | |||
| 370 | /// <summary> | ||
| 371 | /// Format a document. | ||
| 372 | /// </summary> | ||
| 373 | /// <param name="document">The document to format.</param> | ||
| 374 | /// <returns>The number of errors found.</returns> | ||
| 375 | public int FormatDocument(XDocument document) | ||
| 376 | { | ||
| 377 | // Reset the instance info. | ||
| 378 | this.Errors = 0; | ||
| 379 | this.SourceVersion = 0; | ||
| 380 | this.Operation = ConvertOperation.Format; | ||
| 381 | |||
| 382 | // Remove the declaration. | ||
| 383 | if (null != document.Declaration | ||
| 384 | && this.OnError(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | ||
| 385 | { | ||
| 386 | document.Declaration = null; | ||
| 387 | TrimLeadingText(document); | ||
| 388 | } | ||
| 389 | |||
| 390 | // Start converting the nodes at the top. | ||
| 391 | this.ConvertNodes(document.Nodes(), 0); | ||
| 392 | this.RemoveUnusedNamespaces(document.Root); | ||
| 393 | |||
| 394 | return this.Errors; | ||
| 395 | } | ||
| 396 | |||
| 397 | private XDocument OpenSourceFile(string sourceFile) | ||
| 398 | { | ||
| 399 | this.SourceFile = sourceFile; | ||
| 400 | |||
| 401 | try | ||
| 402 | { | ||
| 403 | return XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 404 | } | ||
| 405 | catch (XmlException e) | ||
| 406 | { | ||
| 407 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
| 408 | } | ||
| 409 | |||
| 410 | return null; | ||
| 411 | } | ||
| 412 | |||
| 413 | private void SaveDocument(XDocument document) | ||
| 414 | { | ||
| 415 | var ignoreDeclarationError = this.IgnoreErrors.Contains(ConverterTestType.DeclarationPresent); | ||
| 416 | |||
| 417 | try | ||
| 418 | { | ||
| 419 | using (var writer = XmlWriter.Create(this.SourceFile, new XmlWriterSettings { OmitXmlDeclaration = !ignoreDeclarationError })) | ||
| 420 | { | ||
| 421 | document.Save(writer); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | catch (UnauthorizedAccessException) | ||
| 425 | { | ||
| 426 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) | ||
| 431 | { | ||
| 432 | // Note we operate on a copy of the node list since we may | ||
| 433 | // remove some whitespace nodes during this processing. | ||
| 434 | foreach (var node in nodes.ToList()) | ||
| 435 | { | ||
| 436 | if (node is XText text) | ||
| 437 | { | ||
| 438 | if (null != text.Value) | ||
| 439 | { | ||
| 440 | if (this.TryFixDeprecatedLocalizationPrefixes(node, text.Value, out var newValue, ConverterTestType.DeprecatedLocalizationVariablePrefixInTextValue)) | ||
| 441 | { | ||
| 442 | text.Value = newValue; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | if (!String.IsNullOrWhiteSpace(text.Value)) | ||
| 446 | { | ||
| 447 | text.Value = text.Value.Trim(); | ||
| 448 | } | ||
| 449 | else if (node.NextNode is XCData cdata) | ||
| 450 | { | ||
| 451 | this.EnsurePrecedingWhitespaceRemoved(text, node, ConverterTestType.WhitespacePrecedingNodeWrong); | ||
| 452 | } | ||
| 453 | else if (node.NextNode is XElement element) | ||
| 454 | { | ||
| 455 | this.EnsurePrecedingWhitespaceCorrect(text, node, level, ConverterTestType.WhitespacePrecedingNodeWrong); | ||
| 456 | } | ||
| 457 | else if (node.NextNode is null) // this is the space before the close element | ||
| 458 | { | ||
| 459 | if (node.PreviousNode is null || node.PreviousNode is XCData) | ||
| 460 | { | ||
| 461 | this.EnsurePrecedingWhitespaceRemoved(text, node.Parent, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 462 | } | ||
| 463 | else if (level == 0) // root element's close tag | ||
| 464 | { | ||
| 465 | this.EnsurePrecedingWhitespaceCorrect(text, node, 0, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 466 | } | ||
| 467 | else | ||
| 468 | { | ||
| 469 | this.EnsurePrecedingWhitespaceCorrect(text, node, level - 1, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | } | ||
| 473 | else if (node is XElement element) | ||
| 474 | { | ||
| 475 | this.ConvertElement(element); | ||
| 476 | |||
| 477 | var before = element.Nodes().ToList(); | ||
| 478 | |||
| 479 | this.ConvertNodes(before, level + 1); | ||
| 480 | |||
| 481 | // If any nodes were added during the processing of the children, | ||
| 482 | // ensure those added children get processed as well. | ||
| 483 | var added = element.Nodes().Except(before).ToList(); | ||
| 484 | |||
| 485 | if (added.Any()) | ||
| 486 | { | ||
| 487 | this.ConvertNodes(added, level + 1); | ||
| 488 | } | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | private bool TryFixDeprecatedLocalizationPrefixes(XNode node, string value, out string newValue, ConverterTestType testType) | ||
| 494 | { | ||
| 495 | newValue = this.DeprecatedPrefixRegex.Replace(value, "!"); | ||
| 496 | |||
| 497 | if (object.ReferenceEquals(newValue, value)) | ||
| 498 | { | ||
| 499 | return false; | ||
| 500 | } | ||
| 501 | |||
| 502 | var message = testType == ConverterTestType.DeprecatedLocalizationVariablePrefixInTextValue ? "The prefix on the localization variable in the inner text is incorrect." : "The prefix on the localization variable in the attribute value is incorrect."; | ||
| 503 | |||
| 504 | return this.OnError(testType, node, message); | ||
| 505 | } | ||
| 506 | |||
| 507 | private void EnsurePrecedingWhitespaceCorrect(XText whitespace, XNode node, int level, ConverterTestType testType) | ||
| 508 | { | ||
| 509 | if (!WixConverter.LeadingWhitespaceValid(this.IndentationAmount, level, whitespace.Value)) | ||
| 510 | { | ||
| 511 | var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; | ||
| 512 | |||
| 513 | if (this.OnError(testType, node, message)) | ||
| 514 | { | ||
| 515 | WixConverter.FixupWhitespace(this.IndentationAmount, level, whitespace); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | private void EnsurePrecedingWhitespaceRemoved(XText whitespace, XNode node, ConverterTestType testType) | ||
| 521 | { | ||
| 522 | if (!String.IsNullOrEmpty(whitespace.Value) && whitespace.NodeType != XmlNodeType.CDATA) | ||
| 523 | { | ||
| 524 | var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; | ||
| 525 | |||
| 526 | if (this.OnError(testType, node, message)) | ||
| 527 | { | ||
| 528 | whitespace.Remove(); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | } | ||
| 532 | |||
| 533 | private void ConvertElement(XElement element) | ||
| 534 | { | ||
| 535 | var deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>(); | ||
| 536 | |||
| 537 | foreach (var attribute in element.Attributes()) | ||
| 538 | { | ||
| 539 | if (attribute.IsNamespaceDeclaration) | ||
| 540 | { | ||
| 541 | // Gather any deprecated namespaces, then update this element tree based on those deprecations. | ||
| 542 | var declaration = attribute; | ||
| 543 | |||
| 544 | if (element.Name == Wix3ElementName || element.Name == Include3ElementName) | ||
| 545 | { | ||
| 546 | this.SourceVersion = 3; | ||
| 547 | } | ||
| 548 | else if (element.Name == Wix4ElementName || element.Name == Include4ElementName) | ||
| 549 | { | ||
| 550 | this.SourceVersion = 4; | ||
| 551 | } | ||
| 552 | |||
| 553 | if (WixConverter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns)) | ||
| 554 | { | ||
| 555 | if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) | ||
| 556 | { | ||
| 557 | deprecatedToUpdatedNamespaces.Add(declaration.Value, ns); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | } | ||
| 561 | else | ||
| 562 | { | ||
| 563 | if (null != attribute.Value) | ||
| 564 | { | ||
| 565 | if (this.TryFixDeprecatedLocalizationPrefixes(element, attribute.Value, out var newValue, ConverterTestType.DeprecatedLocalizationVariablePrefixInAttributeValue)) | ||
| 566 | { | ||
| 567 | attribute.Value = newValue; | ||
| 568 | } | ||
| 569 | } | ||
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 573 | if (deprecatedToUpdatedNamespaces.Any()) | ||
| 574 | { | ||
| 575 | WixConverter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); | ||
| 576 | } | ||
| 577 | |||
| 578 | // Apply any specialized conversion actions. | ||
| 579 | if (this.ConvertElementMapping.TryGetValue(element.Name, out var convert)) | ||
| 580 | { | ||
| 581 | convert(element); | ||
| 582 | } | ||
| 583 | } | ||
| 584 | |||
| 585 | private void ConvertBootstrapperApplicationElement(XElement element) | ||
| 586 | { | ||
| 587 | var xUseUILanguages = element.Attribute(BalUseUILanguagesName); | ||
| 588 | if (xUseUILanguages != null && | ||
| 589 | this.OnError(ConverterTestType.BalUseUILanguagesDeprecated, element, "bal:UseUILanguages is deprecated, 'true' is now the standard behavior.")) | ||
| 590 | { | ||
| 591 | xUseUILanguages.Remove(); | ||
| 592 | } | ||
| 593 | |||
| 594 | var xBADll = element.Elements(BootstrapperApplicationDllElementName).FirstOrDefault(); | ||
| 595 | if (xBADll == null) | ||
| 596 | { | ||
| 597 | xBADll = this.CreateBootstrapperApplicationDllElement(element); | ||
| 598 | |||
| 599 | if (xBADll != null) | ||
| 600 | { | ||
| 601 | element.Add(Environment.NewLine); | ||
| 602 | element.Add(xBADll); | ||
| 603 | element.Add(Environment.NewLine); | ||
| 604 | } | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | private XElement CreateBootstrapperApplicationDllElement(XElement element) | ||
| 609 | { | ||
| 610 | XElement xBADll = null; | ||
| 611 | var xSource = element.Attribute("SourceFile"); | ||
| 612 | var xDpiAwareness = element.Attribute("DpiAwareness"); | ||
| 613 | |||
| 614 | if (xSource != null) | ||
| 615 | { | ||
| 616 | if (xBADll != null || CreateBADllElement(element, out xBADll)) | ||
| 617 | { | ||
| 618 | MoveAttribute(element, "SourceFile", xBADll); | ||
| 619 | MoveAttribute(element, "Name", xBADll); | ||
| 620 | } | ||
| 621 | } | ||
| 622 | else if (xDpiAwareness != null || this.SourceVersion < 4) // older code might be relying on old behavior of first Payload element being the BA dll. | ||
| 623 | { | ||
| 624 | var xFirstChild = element.Elements().FirstOrDefault(); | ||
| 625 | if (xFirstChild?.Name == PayloadElementName) | ||
| 626 | { | ||
| 627 | if (xBADll != null || CreateBADllElement(element, out xBADll)) | ||
| 628 | { | ||
| 629 | var attributes = xFirstChild.Attributes().ToList(); | ||
| 630 | xFirstChild.Remove(); | ||
| 631 | |||
| 632 | foreach (var attribute in attributes) | ||
| 633 | { | ||
| 634 | xBADll.Add(attribute); | ||
| 635 | } | ||
| 636 | } | ||
| 637 | } | ||
| 638 | else | ||
| 639 | { | ||
| 640 | this.OnError(ConverterTestType.BootstrapperApplicationDllRequired, element, "The new BootstrapperApplicationDll element is required but could not be added automatically since the bootstrapper application dll was not directly specified."); | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | if (xDpiAwareness != null) | ||
| 645 | { | ||
| 646 | if (xBADll != null || CreateBADllElement(element, out xBADll)) | ||
| 647 | { | ||
| 648 | MoveAttribute(element, "DpiAwareness", xBADll); | ||
| 649 | } | ||
| 650 | } | ||
| 651 | else if (this.SourceVersion < 4 && xBADll != null && | ||
| 652 | this.OnError(ConverterTestType.AssignBootstrapperApplicationDpiAwareness, element, "The BootstrapperApplicationDll DpiAwareness attribute is being set to 'unaware' to ensure it remains the same as the v3 default")) | ||
| 653 | { | ||
| 654 | xBADll.Add(new XAttribute("DpiAwareness", "unaware")); | ||
| 655 | } | ||
| 656 | |||
| 657 | return xBADll; | ||
| 658 | |||
| 659 | bool CreateBADllElement(XObject node, out XElement xCreatedBADll) | ||
| 660 | { | ||
| 661 | var create = this.OnError(ConverterTestType.BootstrapperApplicationDll, node, "The bootstrapper application dll is now specified in the BootstrapperApplicationDll element."); | ||
| 662 | xCreatedBADll = create ? new XElement(BootstrapperApplicationDllElementName) : null; | ||
| 663 | return create; | ||
| 664 | } | ||
| 665 | } | ||
| 666 | |||
| 667 | private void ConvertBootstrapperApplicationRefElement(XElement element) | ||
| 668 | { | ||
| 669 | var xUseUILanguages = element.Attribute(BalUseUILanguagesName); | ||
| 670 | if (xUseUILanguages != null && | ||
| 671 | this.OnError(ConverterTestType.BalUseUILanguagesDeprecated, element, "bal:UseUILanguages is deprecated, 'true' is now the standard behavior.")) | ||
| 672 | { | ||
| 673 | xUseUILanguages.Remove(); | ||
| 674 | } | ||
| 675 | |||
| 676 | var xId = element.Attribute("Id"); | ||
| 677 | if (xId != null) | ||
| 678 | { | ||
| 679 | XName balBAName = null; | ||
| 680 | XName oldBalBAName = null; | ||
| 681 | string theme = null; | ||
| 682 | |||
| 683 | switch (xId.Value) | ||
| 684 | { | ||
| 685 | case "WixStandardBootstrapperApplication.RtfLicense": | ||
| 686 | balBAName = BalStandardBootstrapperApplicationName; | ||
| 687 | theme = "rtfLicense"; | ||
| 688 | break; | ||
| 689 | case "WixStandardBootstrapperApplication.RtfLargeLicense": | ||
| 690 | balBAName = BalStandardBootstrapperApplicationName; | ||
| 691 | theme = "rtfLargeLicense"; | ||
| 692 | break; | ||
| 693 | case "WixStandardBootstrapperApplication.HyperlinkLicense": | ||
| 694 | balBAName = BalStandardBootstrapperApplicationName; | ||
| 695 | theme = "hyperlinkLicense"; | ||
| 696 | break; | ||
| 697 | case "WixStandardBootstrapperApplication.HyperlinkLargeLicense": | ||
| 698 | balBAName = BalStandardBootstrapperApplicationName; | ||
| 699 | theme = "hyperlinkLargeLicense"; | ||
| 700 | break; | ||
| 701 | case "WixStandardBootstrapperApplication.HyperlinkSidebarLicense": | ||
| 702 | balBAName = BalStandardBootstrapperApplicationName; | ||
| 703 | theme = "hyperlinkSidebarLicense"; | ||
| 704 | break; | ||
| 705 | case "WixStandardBootstrapperApplication.Foundation": | ||
| 706 | balBAName = BalStandardBootstrapperApplicationName; | ||
| 707 | theme = "none"; | ||
| 708 | break; | ||
| 709 | case "ManagedBootstrapperApplicationHost": | ||
| 710 | case "ManagedBootstrapperApplicationHost.RtfLicense": | ||
| 711 | balBAName = BalManagedBootstrapperApplicationHostName; | ||
| 712 | theme = "standard"; | ||
| 713 | break; | ||
| 714 | case "ManagedBootstrapperApplicationHost.Minimal": | ||
| 715 | case "ManagedBootstrapperApplicationHost.RtfLicense.Minimal": | ||
| 716 | case "ManagedBootstrapperApplicationHost.Foundation": | ||
| 717 | balBAName = BalManagedBootstrapperApplicationHostName; | ||
| 718 | theme = "none"; | ||
| 719 | break; | ||
| 720 | case "DotNetCoreBootstrapperApplicationHost": | ||
| 721 | case "DotNetCoreBootstrapperApplicationHost.RtfLicense": | ||
| 722 | balBAName = BalNewDotNetCoreBootstrapperApplicationName; | ||
| 723 | oldBalBAName = BalOldDotNetCoreBootstrapperApplicationName; | ||
| 724 | theme = "standard"; | ||
| 725 | break; | ||
| 726 | case "DotNetCoreBootstrapperApplicationHost.Minimal": | ||
| 727 | case "DotNetCoreBootstrapperApplicationHost.RtfLicense.Minimal": | ||
| 728 | case "DotNetCoreBootstrapperApplicationHost.Foundation": | ||
| 729 | balBAName = BalNewDotNetCoreBootstrapperApplicationName; | ||
| 730 | oldBalBAName = BalOldDotNetCoreBootstrapperApplicationName; | ||
| 731 | theme = "none"; | ||
| 732 | break; | ||
| 733 | } | ||
| 734 | |||
| 735 | if (balBAName != null && theme != null && | ||
| 736 | this.OnError(ConverterTestType.BalBootstrapperApplicationRefToElement, element, "Built-in bootstrapper applications must be referenced through their custom element")) | ||
| 737 | { | ||
| 738 | element.Name = BootstrapperApplicationElementName; | ||
| 739 | xId.Remove(); | ||
| 740 | this.ConvertBalBootstrapperApplicationRef(element, theme, balBAName, oldBalBAName); | ||
| 741 | } | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | private void ConvertApprovedExeForElevationElement(XElement element) | ||
| 746 | { | ||
| 747 | this.RenameWin64ToBitness(element); | ||
| 748 | } | ||
| 749 | |||
| 750 | private void ConvertBalBootstrapperApplicationRef(XElement element, string theme, XName balBAElementName, XName oldBalBAElementName = null) | ||
| 751 | { | ||
| 752 | var xBalBa = element.Element(oldBalBAElementName ?? balBAElementName); | ||
| 753 | if (xBalBa == null) | ||
| 754 | { | ||
| 755 | xBalBa = new XElement(balBAElementName); | ||
| 756 | element.Add(Environment.NewLine); | ||
| 757 | element.Add(xBalBa); | ||
| 758 | element.Add(Environment.NewLine); | ||
| 759 | } | ||
| 760 | else if (oldBalBAElementName != null) | ||
| 761 | { | ||
| 762 | xBalBa.Name = BalNewDotNetCoreBootstrapperApplicationName; | ||
| 763 | } | ||
| 764 | |||
| 765 | if (theme != "standard") | ||
| 766 | { | ||
| 767 | xBalBa.Add(new XAttribute("Theme", theme)); | ||
| 768 | } | ||
| 769 | } | ||
| 770 | |||
| 771 | private void ConvertCatalogElement(XElement element) | ||
| 772 | { | ||
| 773 | if (this.OnError(ConverterTestType.BundleSignatureValidationObsolete, element, "The Catalog element is obsolete. Signature validation is no longer supported. The element will be removed.")) | ||
| 774 | { | ||
| 775 | element.Remove(); | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | private void ConvertColumnElement(XElement element) | ||
| 780 | { | ||
| 781 | var category = element.Attribute("Category"); | ||
| 782 | if (category != null) | ||
| 783 | { | ||
| 784 | var camelCaseValue = LowercaseFirstChar(category.Value); | ||
| 785 | if (category.Value != camelCaseValue && | ||
| 786 | this.OnError(ConverterTestType.ColumnCategoryCamelCase, element, "The CustomTable Category attribute contains an incorrectly cased '{0}' value. Lowercase the first character instead.", category.Name)) | ||
| 787 | { | ||
| 788 | category.Value = camelCaseValue; | ||
| 789 | } | ||
| 790 | } | ||
| 791 | |||
| 792 | var modularization = element.Attribute("Modularize"); | ||
| 793 | if (modularization != null) | ||
| 794 | { | ||
| 795 | var camelCaseValue = LowercaseFirstChar(modularization.Value); | ||
| 796 | if (modularization.Value != camelCaseValue && | ||
| 797 | this.OnError(ConverterTestType.ColumnModularizeCamelCase, element, "The CustomTable Modularize attribute contains an incorrectly cased '{0}' value. Lowercase the first character instead.", modularization.Name)) | ||
| 798 | { | ||
| 799 | modularization.Value = camelCaseValue; | ||
| 800 | } | ||
| 801 | } | ||
| 802 | } | ||
| 803 | |||
| 804 | private void ConvertCustomTableElement(XElement element) | ||
| 805 | { | ||
| 806 | var bootstrapperApplicationData = element.Attribute("BootstrapperApplicationData"); | ||
| 807 | if (bootstrapperApplicationData?.Value == "no") | ||
| 808 | { | ||
| 809 | if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name)) | ||
| 810 | { | ||
| 811 | bootstrapperApplicationData.Remove(); | ||
| 812 | } | ||
| 813 | } | ||
| 814 | else | ||
| 815 | { | ||
| 816 | if (element.Elements(ColumnElementName).Any() || bootstrapperApplicationData != null) | ||
| 817 | { | ||
| 818 | // Table definition | ||
| 819 | if (bootstrapperApplicationData != null) | ||
| 820 | { | ||
| 821 | switch (this.CustomTableSetting) | ||
| 822 | { | ||
| 823 | case CustomTableTarget.Bundle: | ||
| 824 | if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'BundleCustomData' element for Bundles.", bootstrapperApplicationData.Name)) | ||
| 825 | { | ||
| 826 | element.Name = WixConverter.BundleCustomDataElementName; | ||
| 827 | bootstrapperApplicationData.Remove(); | ||
| 828 | this.ConvertCustomTableElementToBundle(element); | ||
| 829 | } | ||
| 830 | break; | ||
| 831 | case CustomTableTarget.Msi: | ||
| 832 | if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name)) | ||
| 833 | { | ||
| 834 | element.Add(new XAttribute("Unreal", bootstrapperApplicationData.Value)); | ||
| 835 | bootstrapperApplicationData.Remove(); | ||
| 836 | } | ||
| 837 | break; | ||
| 838 | default: | ||
| 839 | this.OnError(ConverterTestType.CustomTableNotAlwaysConvertable, element, "The CustomTable element contains deprecated '{0}' attribute so can't be converted. Use the 'Unreal' attribute for MSI. Use the 'BundleCustomData' element for Bundles. Use the --custom-table argument to force conversion to 'msi' or 'bundle'", bootstrapperApplicationData.Name); | ||
| 840 | break; | ||
| 841 | } | ||
| 842 | } | ||
| 843 | } | ||
| 844 | else | ||
| 845 | { | ||
| 846 | // Table ref | ||
| 847 | switch (this.CustomTableSetting) | ||
| 848 | { | ||
| 849 | case CustomTableTarget.Bundle: | ||
| 850 | if (this.OnError(ConverterTestType.CustomTableRef, element, "CustomTable elements that don't contain the table definition are now BundleCustomDataRef for Bundles.")) | ||
| 851 | { | ||
| 852 | element.Name = WixConverter.BundleCustomDataRefElementName; | ||
| 853 | this.ConvertCustomTableElementToBundle(element); | ||
| 854 | } | ||
| 855 | break; | ||
| 856 | case CustomTableTarget.Msi: | ||
| 857 | if (this.OnError(ConverterTestType.CustomTableRef, element, "CustomTable elements that don't contain the table definition are now CustomTableRef for MSI.")) | ||
| 858 | { | ||
| 859 | element.Name = WixConverter.CustomTableRefElementName; | ||
| 860 | } | ||
| 861 | break; | ||
| 862 | default: | ||
| 863 | this.OnError(ConverterTestType.CustomTableNotAlwaysConvertable, element, "The CustomTable element contains no 'Column' elements so can't be converted. Use the 'CustomTableRef' element for MSI. Use the 'BundleCustomDataRef' element for Bundles. Use the --custom-table argument to force conversion to 'msi' or 'bundle'"); | ||
| 864 | break; | ||
| 865 | } | ||
| 866 | } | ||
| 867 | } | ||
| 868 | } | ||
| 869 | |||
| 870 | private void ConvertCustomTableElementToBundle(XElement element) | ||
| 871 | { | ||
| 872 | foreach (var xColumn in element.Elements(ColumnElementName)) | ||
| 873 | { | ||
| 874 | xColumn.Name = WixConverter.BundleAttributeDefinitionElementName; | ||
| 875 | |||
| 876 | foreach (var xAttribute in xColumn.Attributes().ToList()) | ||
| 877 | { | ||
| 878 | if (xAttribute.Name.LocalName != "Id" && | ||
| 879 | (xAttribute.Name.Namespace == WixConverter.Wix3Namespace || | ||
| 880 | xAttribute.Name.Namespace == WixConverter.WixNamespace || | ||
| 881 | String.IsNullOrEmpty(xAttribute.Name.Namespace.NamespaceName))) | ||
| 882 | { | ||
| 883 | xAttribute.Remove(); | ||
| 884 | } | ||
| 885 | } | ||
| 886 | } | ||
| 887 | |||
| 888 | foreach (var xRow in element.Elements(RowElementName)) | ||
| 889 | { | ||
| 890 | xRow.Name = WixConverter.BundleElementElementName; | ||
| 891 | |||
| 892 | foreach (var xData in xRow.Elements(DataElementName)) | ||
| 893 | { | ||
| 894 | xData.Name = WixConverter.BundleAttributeElementName; | ||
| 895 | |||
| 896 | var xColumn = xData.Attribute("Column"); | ||
| 897 | if (xColumn != null) | ||
| 898 | { | ||
| 899 | xData.Add(new XAttribute("Id", xColumn.Value)); | ||
| 900 | xColumn.Remove(); | ||
| 901 | } | ||
| 902 | |||
| 903 | this.ConvertInnerTextToAttribute(xData, "Value"); | ||
| 904 | } | ||
| 905 | } | ||
| 906 | } | ||
| 907 | |||
| 908 | private void ConvertControlElement(XElement element) | ||
| 909 | { | ||
| 910 | var remove = new List<XElement>(); | ||
| 911 | |||
| 912 | foreach (var xCondition in element.Elements(ConditionElementName)) | ||
| 913 | { | ||
| 914 | var action = UppercaseFirstChar(xCondition.Attribute("Action")?.Value); | ||
| 915 | if (!String.IsNullOrEmpty(action) && | ||
| 916 | TryGetInnerText(xCondition, out var text) && | ||
| 917 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the '{1}Condition' attribute instead.", xCondition.Name.LocalName, action)) | ||
| 918 | { | ||
| 919 | element.Add(new XAttribute(action + "Condition", text)); | ||
| 920 | remove.Add(xCondition); | ||
| 921 | } | ||
| 922 | } | ||
| 923 | |||
| 924 | for (var i = remove.Count - 1; i >= 0; i--) | ||
| 925 | { | ||
| 926 | remove[i].Remove(); | ||
| 927 | } | ||
| 928 | } | ||
| 929 | |||
| 930 | private void ConvertComponentElement(XElement element) | ||
| 931 | { | ||
| 932 | var guid = element.Attribute("Guid"); | ||
| 933 | if (guid != null && guid.Value == "*") | ||
| 934 | { | ||
| 935 | if (this.OnError(ConverterTestType.AutoGuidUnnecessary, element, "Using '*' for the Component Guid attribute is unnecessary. Remove the attribute to remove the redundancy.")) | ||
| 936 | { | ||
| 937 | guid.Remove(); | ||
| 938 | } | ||
| 939 | } | ||
| 940 | |||
| 941 | var xCondition = element.Element(ConditionElementName); | ||
| 942 | if (xCondition != null) | ||
| 943 | { | ||
| 944 | if (TryGetInnerText(xCondition, out var text) && | ||
| 945 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", xCondition.Name.LocalName)) | ||
| 946 | { | ||
| 947 | element.Add(new XAttribute("Condition", text)); | ||
| 948 | xCondition.Remove(); | ||
| 949 | } | ||
| 950 | } | ||
| 951 | |||
| 952 | this.RenameWin64ToBitness(element); | ||
| 953 | } | ||
| 954 | |||
| 955 | private void ConvertDirectoryElement(XElement element) | ||
| 956 | { | ||
| 957 | if (null == element.Attribute("Name")) | ||
| 958 | { | ||
| 959 | var attribute = element.Attribute("ShortName"); | ||
| 960 | if (null != attribute) | ||
| 961 | { | ||
| 962 | var shortName = attribute.Value; | ||
| 963 | if (this.OnError(ConverterTestType.AssignDirectoryNameFromShortName, element, "The directory ShortName attribute is being renamed to Name since Name wasn't specified for value '{0}'", shortName)) | ||
| 964 | { | ||
| 965 | element.Add(new XAttribute("Name", shortName)); | ||
| 966 | attribute.Remove(); | ||
| 967 | } | ||
| 968 | } | ||
| 969 | } | ||
| 970 | |||
| 971 | var id = element.Attribute("Id")?.Value; | ||
| 972 | |||
| 973 | if (id == "TARGETDIR" && | ||
| 974 | this.OnError(ConverterTestType.TargetDirDeprecated, element, "The TARGETDIR directory should not longer be explicitly defined. Remove the Directory element with Id attribute 'TARGETDIR'.")) | ||
| 975 | { | ||
| 976 | var parentElement = element.Parent; | ||
| 977 | |||
| 978 | element.Remove(); | ||
| 979 | |||
| 980 | if (parentElement.FirstNode is XText text && String.IsNullOrWhiteSpace(text.Value)) | ||
| 981 | { | ||
| 982 | parentElement.FirstNode.Remove(); | ||
| 983 | } | ||
| 984 | |||
| 985 | foreach (var child in element.Nodes()) | ||
| 986 | { | ||
| 987 | parentElement.Add(child); | ||
| 988 | } | ||
| 989 | |||
| 990 | element.RemoveAll(); | ||
| 991 | |||
| 992 | if (parentElement.FirstNode is XText textAgain && String.IsNullOrWhiteSpace(textAgain.Value)) | ||
| 993 | { | ||
| 994 | parentElement.FirstNode.Remove(); | ||
| 995 | } | ||
| 996 | } | ||
| 997 | else if (id != null && | ||
| 998 | WindowsInstallerStandard.IsStandardDirectory(id) && | ||
| 999 | this.OnError(ConverterTestType.DefiningStandardDirectoryDeprecated, element, "Standard directories such as '{0}' should no longer be defined using the Directory element. Use the StandardDirectory element instead.", id)) | ||
| 1000 | { | ||
| 1001 | element.Name = StandardDirectoryElementName; | ||
| 1002 | |||
| 1003 | foreach (var attrib in element.Attributes().Where(a => a.Name.LocalName != "Id").ToList()) | ||
| 1004 | { | ||
| 1005 | attrib.Remove(); | ||
| 1006 | } | ||
| 1007 | } | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | private void ConvertFeatureElement(XElement element) | ||
| 1011 | { | ||
| 1012 | var xAbsent = element.Attribute("Absent"); | ||
| 1013 | if (xAbsent != null && | ||
| 1014 | this.OnError(ConverterTestType.FeatureAbsentAttributeReplaced, element, "The Feature element's Absent attribute has been replaced with the AllowAbsent attribute. Use the 'AllowAbsent' attribute instead.")) | ||
| 1015 | { | ||
| 1016 | if (xAbsent.Value == "disallow") | ||
| 1017 | { | ||
| 1018 | element.Add(new XAttribute("AllowAbsent", "no")); | ||
| 1019 | } | ||
| 1020 | xAbsent.Remove(); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | var xAllowAdvertise = element.Attribute("AllowAdvertise"); | ||
| 1024 | if (xAllowAdvertise != null) | ||
| 1025 | { | ||
| 1026 | if ((xAllowAdvertise.Value == "system" || xAllowAdvertise.Value == "allow") && | ||
| 1027 | this.OnError(ConverterTestType.FeatureAllowAdvertiseValueDeprecated, element, "The AllowAdvertise attribute's '{0}' value is deprecated. Set the value to 'yes' instead.", xAllowAdvertise.Value)) | ||
| 1028 | { | ||
| 1029 | xAllowAdvertise.Value = "yes"; | ||
| 1030 | } | ||
| 1031 | else if (xAllowAdvertise.Value == "disallow" && | ||
| 1032 | this.OnError(ConverterTestType.FeatureAllowAdvertiseValueDeprecated, element, "The AllowAdvertise attribute's '{0}' value is deprecated. Remove the value instead.", xAllowAdvertise.Value)) | ||
| 1033 | { | ||
| 1034 | xAllowAdvertise.Remove(); | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | var xCondition = element.Element(ConditionElementName); | ||
| 1039 | if (xCondition != null) | ||
| 1040 | { | ||
| 1041 | var level = xCondition.Attribute("Level")?.Value; | ||
| 1042 | if (!String.IsNullOrEmpty(level) && | ||
| 1043 | TryGetInnerText(xCondition, out var text) && | ||
| 1044 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Level' element instead.", xCondition.Name.LocalName)) | ||
| 1045 | { | ||
| 1046 | xCondition.AddAfterSelf(new XElement(LevelElementName, | ||
| 1047 | new XAttribute("Value", level), | ||
| 1048 | new XAttribute("Condition", text) | ||
| 1049 | )); | ||
| 1050 | xCondition.Remove(); | ||
| 1051 | } | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | private void ConvertFileElement(XElement element) | ||
| 1056 | { | ||
| 1057 | if (this.SourceVersion < 4 && null == element.Attribute("Id")) | ||
| 1058 | { | ||
| 1059 | var attribute = element.Attribute("Name"); | ||
| 1060 | |||
| 1061 | if (null == attribute) | ||
| 1062 | { | ||
| 1063 | attribute = element.Attribute("Source"); | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | if (null != attribute) | ||
| 1067 | { | ||
| 1068 | var name = Path.GetFileName(attribute.Value); | ||
| 1069 | |||
| 1070 | if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the v3 default", name)) | ||
| 1071 | { | ||
| 1072 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
| 1073 | element.RemoveAttributes(); | ||
| 1074 | element.Add(new XAttribute("Id", GetIdentifierFromName(name))); | ||
| 1075 | element.Add(attributes); | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | private void ConvertFragmentElement(XElement element) | ||
| 1082 | { | ||
| 1083 | var remove = new List<XElement>(); | ||
| 1084 | |||
| 1085 | foreach (var xCondition in element.Elements(ConditionElementName)) | ||
| 1086 | { | ||
| 1087 | var message = xCondition.Attribute("Message")?.Value; | ||
| 1088 | |||
| 1089 | if (!String.IsNullOrEmpty(message) && | ||
| 1090 | TryGetInnerText(xCondition, out var text) && | ||
| 1091 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Launch' element instead.", xCondition.Name.LocalName)) | ||
| 1092 | { | ||
| 1093 | xCondition.AddAfterSelf(new XElement(LaunchElementName, | ||
| 1094 | new XAttribute("Condition", text), | ||
| 1095 | new XAttribute("Message", message) | ||
| 1096 | )); | ||
| 1097 | remove.Add(xCondition); | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | for (var i = remove.Count - 1; i >= 0; i--) | ||
| 1102 | { | ||
| 1103 | remove[i].Remove(); | ||
| 1104 | } | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | private void ConvertFirewallRemoteAddressElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1108 | |||
| 1109 | private void ConvertEmbeddedChainerElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Condition"); | ||
| 1110 | |||
| 1111 | private void ConvertErrorElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Message"); | ||
| 1112 | |||
| 1113 | private void ConvertExePackageElement(XElement element) | ||
| 1114 | { | ||
| 1115 | this.ConvertSuppressSignatureValidation(element); | ||
| 1116 | |||
| 1117 | foreach (var attributeName in new[] { "InstallCommand", "RepairCommand", "UninstallCommand" }) | ||
| 1118 | { | ||
| 1119 | var newName = attributeName.Replace("Command", "Arguments"); | ||
| 1120 | var attribute = element.Attribute(attributeName); | ||
| 1121 | |||
| 1122 | if (attribute != null && | ||
| 1123 | this.OnError(ConverterTestType.RenameExePackageCommandToArguments, element, "The {0} element {1} attribute has been renamed {2}.", element.Name.LocalName, attribute.Name.LocalName, newName)) | ||
| 1124 | { | ||
| 1125 | element.Add(new XAttribute(newName, attribute.Value)); | ||
| 1126 | attribute.Remove(); | ||
| 1127 | } | ||
| 1128 | } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | private void ConvertPermissionExElement(XElement element) | ||
| 1132 | { | ||
| 1133 | var xCondition = element.Element(ConditionElementName); | ||
| 1134 | if (xCondition != null) | ||
| 1135 | { | ||
| 1136 | if (TryGetInnerText(xCondition, out var text) && | ||
| 1137 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", xCondition.Name.LocalName)) | ||
| 1138 | { | ||
| 1139 | element.Add(new XAttribute("Condition", text)); | ||
| 1140 | xCondition.Remove(); | ||
| 1141 | } | ||
| 1142 | } | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | private void ConvertProgressTextElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Message"); | ||
| 1146 | |||
| 1147 | private void ConvertModuleElement(XElement element) | ||
| 1148 | { | ||
| 1149 | if (element.Attribute("Guid") == null // skip already-converted Module elements | ||
| 1150 | && this.OnError(ConverterTestType.ModuleAndPackageRenamed, element, "The Module and Package elements have been renamed and reorganized for simplicity.")) | ||
| 1151 | { | ||
| 1152 | var xModule = element; | ||
| 1153 | |||
| 1154 | var xSummaryInformation = xModule.Element(PackageElementName); | ||
| 1155 | if (xSummaryInformation != null) | ||
| 1156 | { | ||
| 1157 | xSummaryInformation.Name = SummaryInformationElementName; | ||
| 1158 | |||
| 1159 | var xInstallerVersion = xSummaryInformation.Attribute("InstallerVersion"); | ||
| 1160 | if (this.SourceVersion < 4 && xInstallerVersion == null) | ||
| 1161 | { | ||
| 1162 | this.OnError(ConverterTestType.InstallerVersionBehaviorChange, element, "Breaking change: The default value for Package/@InstallerVersion has been changed to '500' regardless of build platform. If you need a lower version, set it manually in the Module element."); | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | RemoveAttribute(xSummaryInformation, "AdminImage"); | ||
| 1166 | RemoveAttribute(xSummaryInformation, "Comments"); | ||
| 1167 | MoveAttribute(xSummaryInformation, "Id", xModule, "Guid"); | ||
| 1168 | MoveAttribute(xSummaryInformation, "InstallerVersion", xModule); | ||
| 1169 | RemoveAttribute(xSummaryInformation, "Languages"); | ||
| 1170 | RemoveAttribute(xSummaryInformation, "Platform"); | ||
| 1171 | RemoveAttribute(xSummaryInformation, "Platforms"); | ||
| 1172 | RemoveAttribute(xSummaryInformation, "ReadOnly"); | ||
| 1173 | MoveAttribute(xSummaryInformation, "SummaryCodepage", xSummaryInformation, "Codepage", defaultValue: "1252"); | ||
| 1174 | |||
| 1175 | if (!xSummaryInformation.HasAttributes) | ||
| 1176 | { | ||
| 1177 | xSummaryInformation.Remove(); | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | private void ConvertProductElement(XElement element) | ||
| 1184 | { | ||
| 1185 | var id = element.Attribute("Id"); | ||
| 1186 | if (id != null && id.Value == "*") | ||
| 1187 | { | ||
| 1188 | if (this.OnError(ConverterTestType.AutoGuidUnnecessary, element, "Using '*' for the Product Id attribute is unnecessary. Remove the attribute to remove the redundancy.")) | ||
| 1189 | { | ||
| 1190 | id.Remove(); | ||
| 1191 | } | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | var xConditions = element.Elements(ConditionElementName).ToList(); | ||
| 1195 | foreach (var xCondition in xConditions) | ||
| 1196 | { | ||
| 1197 | var message = xCondition.Attribute("Message")?.Value; | ||
| 1198 | |||
| 1199 | if (!String.IsNullOrEmpty(message) && | ||
| 1200 | TryGetInnerText(xCondition, out var text) && | ||
| 1201 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Launch' element instead.", xCondition.Name.LocalName)) | ||
| 1202 | { | ||
| 1203 | xCondition.AddAfterSelf(new XElement(LaunchElementName, | ||
| 1204 | new XAttribute("Condition", text), | ||
| 1205 | new XAttribute("Message", message) | ||
| 1206 | )); | ||
| 1207 | xCondition.Remove(); | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | var xMediaTemplate = element.Element(MediaTemplateElementName); | ||
| 1212 | if (xMediaTemplate?.HasAttributes == false | ||
| 1213 | && this.OnError(ConverterTestType.DefaultMediaTemplate, element, "A MediaTemplate with no attributes set is now provided by default. Remove the element.")) | ||
| 1214 | { | ||
| 1215 | xMediaTemplate.Remove(); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | if (this.OnError(ConverterTestType.ProductAndPackageRenamed, element, "The Product and Package elements have been renamed and reorganized for simplicity.")) | ||
| 1219 | { | ||
| 1220 | var xPackage = element; | ||
| 1221 | xPackage.Name = PackageElementName; | ||
| 1222 | |||
| 1223 | var xSummaryInformation = xPackage.Element(PackageElementName); | ||
| 1224 | if (xSummaryInformation != null) | ||
| 1225 | { | ||
| 1226 | xSummaryInformation.Name = SummaryInformationElementName; | ||
| 1227 | |||
| 1228 | var xInstallerVersion = xSummaryInformation.Attribute("InstallerVersion"); | ||
| 1229 | if (this.SourceVersion < 4 && xInstallerVersion == null) | ||
| 1230 | { | ||
| 1231 | this.OnError(ConverterTestType.InstallerVersionBehaviorChange, element, "Breaking change: The default value for Package/@InstallerVersion has been changed to '500' regardless of build platform. If you need a lower version, set it manually in the Package element."); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | if (xSummaryInformation.Attribute("Compressed") == null) | ||
| 1235 | { | ||
| 1236 | xPackage.SetAttributeValue("Compressed", "no"); | ||
| 1237 | } | ||
| 1238 | else | ||
| 1239 | { | ||
| 1240 | MoveAttribute(xSummaryInformation, "Compressed", xPackage, defaultValue: "yes"); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | RemoveAttribute(xSummaryInformation, "AdminImage"); | ||
| 1244 | RemoveAttribute(xSummaryInformation, "Comments"); | ||
| 1245 | RemoveAttribute(xSummaryInformation, "Id"); | ||
| 1246 | MoveAttribute(xSummaryInformation, "InstallerVersion", xPackage, defaultValue: "500"); | ||
| 1247 | MoveAttribute(xSummaryInformation, "InstallScope", xPackage, "Scope", defaultValue: "perMachine"); | ||
| 1248 | RemoveAttribute(xSummaryInformation, "Languages"); | ||
| 1249 | RemoveAttribute(xSummaryInformation, "Platform"); | ||
| 1250 | RemoveAttribute(xSummaryInformation, "Platforms"); | ||
| 1251 | RemoveAttribute(xSummaryInformation, "ReadOnly"); | ||
| 1252 | MoveAttribute(xSummaryInformation, "ShortNames", xPackage); | ||
| 1253 | MoveAttribute(xSummaryInformation, "SummaryCodepage", xSummaryInformation, "Codepage", defaultValue: "1252"); | ||
| 1254 | MoveAttribute(xPackage, "Id", xPackage, "ProductCode"); | ||
| 1255 | |||
| 1256 | var xInstallPrivileges = xSummaryInformation.Attribute("InstallPrivileges"); | ||
| 1257 | switch (xInstallPrivileges?.Value) | ||
| 1258 | { | ||
| 1259 | case "limited": | ||
| 1260 | xPackage.SetAttributeValue("Scope", "perUser"); | ||
| 1261 | break; | ||
| 1262 | case "elevated": | ||
| 1263 | { | ||
| 1264 | var xAllUsers = xPackage.Elements(PropertyElementName).SingleOrDefault(p => p.Attribute("Id")?.Value == "ALLUSERS"); | ||
| 1265 | if (xAllUsers?.Attribute("Value")?.Value == "1") | ||
| 1266 | { | ||
| 1267 | xAllUsers?.Remove(); | ||
| 1268 | } | ||
| 1269 | } | ||
| 1270 | break; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | xInstallPrivileges?.Remove(); | ||
| 1274 | |||
| 1275 | if (!xSummaryInformation.HasAttributes) | ||
| 1276 | { | ||
| 1277 | xSummaryInformation.Remove(); | ||
| 1278 | } | ||
| 1279 | } | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | private static void MoveAttribute(XElement xSource, string attributeName, XElement xDestination, string destinationAttributeName = null, string defaultValue = null) | ||
| 1284 | { | ||
| 1285 | var xAttribute = xSource.Attribute(attributeName); | ||
| 1286 | if (xAttribute != null && (defaultValue == null || xAttribute.Value != defaultValue)) | ||
| 1287 | { | ||
| 1288 | xDestination.SetAttributeValue(destinationAttributeName ?? attributeName, xAttribute.Value); | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | xAttribute?.Remove(); | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | private static void RemoveAttribute(XElement xSummaryInformation, string attributeName) | ||
| 1295 | { | ||
| 1296 | var xAttribute = xSummaryInformation.Attribute(attributeName); | ||
| 1297 | xAttribute?.Remove(); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | private void ConvertPropertyRefElement(XElement element) | ||
| 1301 | { | ||
| 1302 | var newElementName = String.Empty; | ||
| 1303 | |||
| 1304 | var id = element.Attribute("Id"); | ||
| 1305 | switch (id?.Value) | ||
| 1306 | { | ||
| 1307 | case "WIX_SUITE_BACKOFFICE": | ||
| 1308 | case "WIX_SUITE_BLADE": | ||
| 1309 | case "WIX_SUITE_COMMUNICATIONS": | ||
| 1310 | case "WIX_SUITE_COMPUTE_SERVER": | ||
| 1311 | case "WIX_SUITE_DATACENTER": | ||
| 1312 | case "WIX_SUITE_EMBEDDED_RESTRICTED": | ||
| 1313 | case "WIX_SUITE_EMBEDDEDNT": | ||
| 1314 | case "WIX_SUITE_ENTERPRISE": | ||
| 1315 | case "WIX_SUITE_MEDIACENTER": | ||
| 1316 | case "WIX_SUITE_PERSONAL": | ||
| 1317 | case "WIX_SUITE_SECURITY_APPLIANCE": | ||
| 1318 | case "WIX_SUITE_SERVERR2": | ||
| 1319 | case "WIX_SUITE_SINGLEUSERTS": | ||
| 1320 | case "WIX_SUITE_SMALLBUSINESS": | ||
| 1321 | case "WIX_SUITE_SMALLBUSINESS_RESTRICTED": | ||
| 1322 | case "WIX_SUITE_STARTER": | ||
| 1323 | case "WIX_SUITE_STORAGE_SERVER": | ||
| 1324 | case "WIX_SUITE_TABLETPC": | ||
| 1325 | case "WIX_SUITE_TERMINAL": | ||
| 1326 | case "WIX_SUITE_WH_SERVER": | ||
| 1327 | newElementName = "QueryWindowsSuiteInfo"; | ||
| 1328 | break; | ||
| 1329 | case "WIX_DIR_ADMINTOOLS": | ||
| 1330 | case "WIX_DIR_ALTSTARTUP": | ||
| 1331 | case "WIX_DIR_CDBURN_AREA": | ||
| 1332 | case "WIX_DIR_COMMON_ADMINTOOLS": | ||
| 1333 | case "WIX_DIR_COMMON_ALTSTARTUP": | ||
| 1334 | case "WIX_DIR_COMMON_DOCUMENTS": | ||
| 1335 | case "WIX_DIR_COMMON_FAVORITES": | ||
| 1336 | case "WIX_DIR_COMMON_MUSIC": | ||
| 1337 | case "WIX_DIR_COMMON_PICTURES": | ||
| 1338 | case "WIX_DIR_COMMON_VIDEO": | ||
| 1339 | case "WIX_DIR_COOKIES": | ||
| 1340 | case "WIX_DIR_DESKTOP": | ||
| 1341 | case "WIX_DIR_HISTORY": | ||
| 1342 | case "WIX_DIR_INTERNET_CACHE": | ||
| 1343 | case "WIX_DIR_MYMUSIC": | ||
| 1344 | case "WIX_DIR_MYPICTURES": | ||
| 1345 | case "WIX_DIR_MYVIDEO": | ||
| 1346 | case "WIX_DIR_NETHOOD": | ||
| 1347 | case "WIX_DIR_PERSONAL": | ||
| 1348 | case "WIX_DIR_PRINTHOOD": | ||
| 1349 | case "WIX_DIR_PROFILE": | ||
| 1350 | case "WIX_DIR_RECENT": | ||
| 1351 | case "WIX_DIR_RESOURCES": | ||
| 1352 | newElementName = "QueryWindowsDirectories"; | ||
| 1353 | break; | ||
| 1354 | case "WIX_DWM_COMPOSITION_ENABLED": | ||
| 1355 | case "WIX_WDDM_DRIVER_PRESENT": | ||
| 1356 | newElementName = "QueryWindowsDriverInfo"; | ||
| 1357 | break; | ||
| 1358 | case "WIX_ACCOUNT_LOCALSYSTEM": | ||
| 1359 | case "WIX_ACCOUNT_LOCALSERVICE": | ||
| 1360 | case "WIX_ACCOUNT_NETWORKSERVICE": | ||
| 1361 | case "WIX_ACCOUNT_ADMINISTRATORS": | ||
| 1362 | case "WIX_ACCOUNT_USERS": | ||
| 1363 | case "WIX_ACCOUNT_GUESTS": | ||
| 1364 | case "WIX_ACCOUNT_PERFLOGUSERS": | ||
| 1365 | case "WIX_ACCOUNT_PERFLOGUSERS_NODOMAIN": | ||
| 1366 | newElementName = "QueryWindowsWellKnownSIDs"; | ||
| 1367 | break; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | if (!String.IsNullOrEmpty(newElementName) | ||
| 1371 | && this.OnError(ConverterTestType.UtilReferencesReplaced, element, "Custom action and property reference {0} to WixUtilExtension have been replaced with strongly-typed elements.", id)) | ||
| 1372 | { | ||
| 1373 | element.AddAfterSelf(new XElement(WixUtilNamespace + newElementName)); | ||
| 1374 | element.Remove(); | ||
| 1375 | } | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | private void ConvertCustomActionRefElement(XElement element) | ||
| 1379 | { | ||
| 1380 | var newElementName = String.Empty; | ||
| 1381 | |||
| 1382 | var id = element.Attribute("Id"); | ||
| 1383 | switch (id?.Value) | ||
| 1384 | { | ||
| 1385 | case "WixBroadcastSettingChange": | ||
| 1386 | case "WixBroadcastEnvironmentChange": | ||
| 1387 | case "WixCheckRebootRequired": | ||
| 1388 | case "WixExitEarlyWithSuccess": | ||
| 1389 | case "WixFailWhenDeferred": | ||
| 1390 | case "WixWaitForEvent": | ||
| 1391 | case "WixWaitForEventDeferred": | ||
| 1392 | newElementName = id?.Value.Substring(3); // strip leading Wix | ||
| 1393 | break; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | if (!String.IsNullOrEmpty(newElementName) | ||
| 1397 | && this.OnError(ConverterTestType.UtilReferencesReplaced, element, "Custom action and property reference {0} to WixUtilExtension have been replaced with strongly-typed elements.", id)) | ||
| 1398 | { | ||
| 1399 | element.AddAfterSelf(new XElement(WixUtilNamespace + newElementName)); | ||
| 1400 | element.Remove(); | ||
| 1401 | } | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | private void ConvertPublishElement(XElement element) | ||
| 1405 | { | ||
| 1406 | this.ConvertInnerTextToAttribute(element, "Condition"); | ||
| 1407 | |||
| 1408 | var xCondition = element.Attribute("Condition"); | ||
| 1409 | if (xCondition?.Value == "1" && | ||
| 1410 | this.OnError(ConverterTestType.PublishConditionOneUnnecessary, element, "Adding Condition='1' on {0} elements is no longer necessary. Remove the Condition attribute.", xCondition.Name.LocalName)) | ||
| 1411 | { | ||
| 1412 | xCondition.Remove(); | ||
| 1413 | } | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | private void ConvertMultiStringValueElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1417 | |||
| 1418 | private void ConvertRegistryKeyElement(XElement element) | ||
| 1419 | { | ||
| 1420 | var xAction = element.Attribute("Action"); | ||
| 1421 | |||
| 1422 | if (xAction != null | ||
| 1423 | && this.OnError(ConverterTestType.RegistryKeyActionObsolete, element, "The RegistryKey element's Action attribute is obsolete. Action='create' will be converted to ForceCreateOnInstall='yes'. Action='createAndRemoveOnUninstall' will be converted to ForceCreateOnInstall='yes' and ForceDeleteOnUninstall='yes'.")) | ||
| 1424 | { | ||
| 1425 | switch (xAction?.Value) | ||
| 1426 | { | ||
| 1427 | case "create": | ||
| 1428 | element.SetAttributeValue("ForceCreateOnInstall", "yes"); | ||
| 1429 | break; | ||
| 1430 | case "createAndRemoveOnUninstall": | ||
| 1431 | element.SetAttributeValue("ForceCreateOnInstall", "yes"); | ||
| 1432 | element.SetAttributeValue("ForceDeleteOnUninstall", "yes"); | ||
| 1433 | break; | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | xAction.Remove(); | ||
| 1437 | } | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | private void ConvertRemotePayloadElement(XElement element) | ||
| 1441 | { | ||
| 1442 | var xParent = element.Parent; | ||
| 1443 | |||
| 1444 | if (xParent.Name == ExePackageElementName && | ||
| 1445 | this.OnError(ConverterTestType.RemotePayloadRenamed, element, "The RemotePayload element has been renamed. Use the 'ExePackagePayload' instead.")) | ||
| 1446 | { | ||
| 1447 | element.Name = ExePackagePayloadElementName; | ||
| 1448 | } | ||
| 1449 | else if (xParent.Name == MsuPackageElementName && | ||
| 1450 | this.OnError(ConverterTestType.RemotePayloadRenamed, element, "The RemotePayload element has been renamed. Use the 'MsuPackagePayload' instead.")) | ||
| 1451 | { | ||
| 1452 | element.Name = MsuPackagePayloadElementName; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | var xName = xParent.Attribute("Name"); | ||
| 1456 | if (xName != null && | ||
| 1457 | this.OnError(ConverterTestType.NameAttributeMovedToRemotePayload, xParent, "The Name attribute must be specified on the child XxxPackagePayload element when using a remote payload.")) | ||
| 1458 | { | ||
| 1459 | element.SetAttributeValue("Name", xName.Value); | ||
| 1460 | xName.Remove(); | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | var xDownloadUrl = xParent.Attribute("DownloadUrl"); | ||
| 1464 | if (xDownloadUrl != null && | ||
| 1465 | this.OnError(ConverterTestType.DownloadUrlAttributeMovedToRemotePayload, xParent, "The DownloadUrl attribute must be specified on the child XxxPackagePayload element when using a remote payload.")) | ||
| 1466 | { | ||
| 1467 | element.SetAttributeValue("DownloadUrl", xDownloadUrl.Value); | ||
| 1468 | xDownloadUrl.Remove(); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | var xCompressed = xParent.Attribute("Compressed"); | ||
| 1472 | if (xCompressed != null && | ||
| 1473 | this.OnError(ConverterTestType.CompressedAttributeUnnecessaryForRemotePayload, xParent, "The Compressed attribute should not be specified when using a remote payload.")) | ||
| 1474 | { | ||
| 1475 | xCompressed.Remove(); | ||
| 1476 | } | ||
| 1477 | |||
| 1478 | this.OnError(ConverterTestType.BurnHashAlgorithmChanged, element, "The hash algorithm for bundles changed from SHA1 to SHA512."); | ||
| 1479 | |||
| 1480 | this.RemoveAttributeIfPresent(element, "CertificatePublicKey", ConverterTestType.BundleSignatureValidationObsolete, "The {0} element contains obsolete '{1}' attribute. Signature validation is no longer supported. The attribute will be removed."); | ||
| 1481 | this.RemoveAttributeIfPresent(element, "CertificateThumbprint", ConverterTestType.BundleSignatureValidationObsolete, "The {0} element contains obsolete '{1}' attribute. Signature validation is no longer supported. The attribute will be removed."); | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | private void ConvertRegistrySearchElement(XElement element) | ||
| 1485 | { | ||
| 1486 | this.RenameWin64ToBitness(element); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | private void ConvertRequiredPrivilegeElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Name"); | ||
| 1490 | |||
| 1491 | private void ConvertDataElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1492 | |||
| 1493 | private void ConvertSequenceElement(XElement element) | ||
| 1494 | { | ||
| 1495 | foreach (var child in element.Elements()) | ||
| 1496 | { | ||
| 1497 | this.ConvertInnerTextToAttribute(child, "Condition"); | ||
| 1498 | } | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | private void ConvertServiceArgumentElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1502 | |||
| 1503 | private void ConvertSetDirectoryElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Condition"); | ||
| 1504 | |||
| 1505 | private void ConvertSetPropertyElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Condition"); | ||
| 1506 | |||
| 1507 | private void ConvertShortcutPropertyElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1508 | |||
| 1509 | private void ConvertProvidesElement(XElement element) | ||
| 1510 | { | ||
| 1511 | if (this.OnError(ConverterTestType.IntegratedDependencyNamespace, element, "The Provides element has been integrated into the WiX v4 namespace. Remove the namespace.")) | ||
| 1512 | { | ||
| 1513 | element.Name = ProvidesElementName; | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | if (element.Parent.Name == ComponentElementName && | ||
| 1517 | this.OnError(ConverterTestType.IntegratedDependencyNamespace, element, "The Provides element has been integrated into the WiX v4 namespace. Add the 'Check' attribute from the WixDependency.wixext to match v3 runtime behavior.")) | ||
| 1518 | { | ||
| 1519 | element.Add(new XAttribute(DependencyCheckAttributeName, "yes")); | ||
| 1520 | } | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | private void ConvertRequiresElement(XElement element) | ||
| 1524 | { | ||
| 1525 | if (this.OnError(ConverterTestType.IntegratedDependencyNamespace, element, "The Requires element has been integrated into the WiX v4 namespace. Remove the namespace.")) | ||
| 1526 | { | ||
| 1527 | element.Name = RequiresElementName; | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | if (element.Parent.Name == ProvidesElementName && | ||
| 1531 | element.Parent.Parent?.Name == ComponentElementName && | ||
| 1532 | this.OnError(ConverterTestType.IntegratedDependencyNamespace, element, "The Requires element has been integrated into the WiX v4 namespace. Add the 'Enforce' attribute from the WixDependency.wixext to match v3 runtime behavior.")) | ||
| 1533 | { | ||
| 1534 | element.Add(new XAttribute(DependencyEnforceAttributeName, "yes")); | ||
| 1535 | } | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | private void ConvertRequiresRefElement(XElement element) | ||
| 1539 | { | ||
| 1540 | if (this.OnError(ConverterTestType.IntegratedDependencyNamespace, element, "The RequiresRef element has been integrated into the WiX v4 namespace. Remove the namespace.")) | ||
| 1541 | { | ||
| 1542 | element.Name = RequiresRefElementName; | ||
| 1543 | } | ||
| 1544 | |||
| 1545 | if (element.Parent.Name == ProvidesElementName && | ||
| 1546 | element.Parent.Parent?.Name == ComponentElementName && | ||
| 1547 | this.OnError(ConverterTestType.IntegratedDependencyNamespace, element, "The RequiresRef element has been integrated into the WiX v4 namespace. Add the 'Enforce' attribute from the WixDependency.wixext to match v3 runtime behavior.")) | ||
| 1548 | { | ||
| 1549 | element.Add(new XAttribute(DependencyEnforceAttributeName, "yes")); | ||
| 1550 | } | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | private void ConvertSuppressSignatureValidation(XElement element) | ||
| 1554 | { | ||
| 1555 | var suppressSignatureValidation = element.Attribute("SuppressSignatureValidation"); | ||
| 1556 | |||
| 1557 | if (null != suppressSignatureValidation | ||
| 1558 | && this.OnError(ConverterTestType.BundleSignatureValidationObsolete, element, "The chain package element contains obsolete '{0}' attribute. Signature validation is no longer supported. The attribute will be removed.", suppressSignatureValidation.Name)) | ||
| 1559 | { | ||
| 1560 | suppressSignatureValidation.Remove(); | ||
| 1561 | } | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | private void ConvertTagElement(XElement element) | ||
| 1565 | { | ||
| 1566 | if (this.OnError(ConverterTestType.TagElementRenamed, element, "The Tag element has been renamed. Use the 'SoftwareTag' element instead.")) | ||
| 1567 | { | ||
| 1568 | element.Name = SoftwareTagElementName; | ||
| 1569 | } | ||
| 1570 | |||
| 1571 | this.RemoveAttributeIfPresent(element, "Licensed", ConverterTestType.SoftwareTagLicensedObsolete, "The {0} element contains obsolete '{1}' attribute. The attribute will be removed."); | ||
| 1572 | this.RemoveAttributeIfPresent(element, "Type", ConverterTestType.SoftwareTagLicensedObsolete, "The {0} element contains obsolete '{1}' attribute. The attribute will be removed."); | ||
| 1573 | this.RenameWin64ToBitness(element); | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | private void ConvertTagRefElement(XElement element) | ||
| 1577 | { | ||
| 1578 | if (this.OnError(ConverterTestType.TagRefElementRenamed, element, "The TagRef element has been renamed. Use the 'SoftwareTagRef' element instead.")) | ||
| 1579 | { | ||
| 1580 | element.Name = SoftwareTagRefElementName; | ||
| 1581 | } | ||
| 1582 | } | ||
| 1583 | |||
| 1584 | private void ConvertTextElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1585 | |||
| 1586 | private void ConvertUITextElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1587 | |||
| 1588 | private void ConvertWindowsInstallerPackageElement(XElement element) | ||
| 1589 | { | ||
| 1590 | this.ConvertSuppressSignatureValidation(element); | ||
| 1591 | |||
| 1592 | if (null != element.Attribute("DisplayInternalUI")) | ||
| 1593 | { | ||
| 1594 | this.OnError(ConverterTestType.DisplayInternalUiNotConvertable, element, "The DisplayInternalUI functionality has fundamentally changed and requires BootstrapperApplication support."); | ||
| 1595 | } | ||
| 1596 | } | ||
| 1597 | |||
| 1598 | private void ConvertVerbElement(XElement element) | ||
| 1599 | { | ||
| 1600 | if (null != element.Attribute("Target")) | ||
| 1601 | { | ||
| 1602 | this.OnError(ConverterTestType.VerbTargetNotConvertable, element, "The Verb/@Target attribute has been replaced with typed @TargetFile and @TargetProperty attributes."); | ||
| 1603 | } | ||
| 1604 | } | ||
| 1605 | |||
| 1606 | private void ConvertCustomActionElement(XElement xCustomAction) | ||
| 1607 | { | ||
| 1608 | var xBinaryKey = xCustomAction.Attribute("BinaryKey"); | ||
| 1609 | if (xBinaryKey != null && this.OnError(ConverterTestType.CustomActionKeysAreNowRefs, xCustomAction, "The CustomAction attributes have been renamed from BinaryKey and FileKey to BinaryRef and FileRef.")) | ||
| 1610 | { | ||
| 1611 | xCustomAction.SetAttributeValue("BinaryRef", xBinaryKey.Value); | ||
| 1612 | xBinaryKey.Remove(); | ||
| 1613 | xBinaryKey = xCustomAction.Attribute("BinaryRef"); | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | var xFileKey = xCustomAction.Attribute("FileKey"); | ||
| 1617 | if (xFileKey != null && this.OnError(ConverterTestType.CustomActionKeysAreNowRefs, xCustomAction, "The CustomAction attributes have been renamed from BinaryKey and FileKey to BinaryRef and FileRef.")) | ||
| 1618 | { | ||
| 1619 | xCustomAction.SetAttributeValue("FileRef", xFileKey.Value); | ||
| 1620 | xFileKey.Remove(); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | if (xBinaryKey?.Value == "WixCA" || xBinaryKey?.Value == "UtilCA") | ||
| 1624 | { | ||
| 1625 | if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA custom action DLL Binary table id has been renamed. Use the id 'Wix4UtilCA_X86' instead.")) | ||
| 1626 | { | ||
| 1627 | xBinaryKey.Value = "Wix4UtilCA_X86"; | ||
| 1628 | } | ||
| 1629 | } | ||
| 1630 | |||
| 1631 | if (xBinaryKey?.Value == "WixCA_x64" || xBinaryKey?.Value == "UtilCA_x64") | ||
| 1632 | { | ||
| 1633 | if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA_x64 custom action DLL Binary table id has been renamed. Use the id 'Wix4UtilCA_X64' instead.")) | ||
| 1634 | { | ||
| 1635 | xBinaryKey.Value = "Wix4UtilCA_X64"; | ||
| 1636 | } | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | var xDllEntry = xCustomAction.Attribute("DllEntry"); | ||
| 1640 | |||
| 1641 | if (xDllEntry?.Value == "CAQuietExec" || xDllEntry?.Value == "CAQuietExec64") | ||
| 1642 | { | ||
| 1643 | if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The CAQuietExec and CAQuietExec64 custom action ids have been renamed. Use the ids 'WixQuietExec' and 'WixQuietExec64' instead.")) | ||
| 1644 | { | ||
| 1645 | xDllEntry.Value = xDllEntry.Value.Replace("CAQuietExec", "WixQuietExec"); | ||
| 1646 | } | ||
| 1647 | } | ||
| 1648 | |||
| 1649 | var xProperty = xCustomAction.Attribute("Property"); | ||
| 1650 | |||
| 1651 | if (xProperty?.Value == "QtExecCmdLine" || xProperty?.Value == "QtExec64CmdLine") | ||
| 1652 | { | ||
| 1653 | if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The QtExecCmdLine and QtExec64CmdLine property ids have been renamed. Use the ids 'WixQuietExecCmdLine' and 'WixQuietExec64CmdLine' instead.")) | ||
| 1654 | { | ||
| 1655 | xProperty.Value = xProperty.Value.Replace("QtExec", "WixQuietExec"); | ||
| 1656 | } | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | var xScript = xCustomAction.Attribute("Script"); | ||
| 1660 | |||
| 1661 | if (xScript != null && TryGetInnerText(xCustomAction, out var scriptText)) | ||
| 1662 | { | ||
| 1663 | if (this.OnError(ConverterTestType.InnerTextDeprecated, xCustomAction, "Using {0} element text is deprecated. Extract the text to a file and use the 'ScriptSourceFile' attribute to reference it.", xCustomAction.Name.LocalName)) | ||
| 1664 | { | ||
| 1665 | var scriptFolder = Path.GetDirectoryName(this.SourceFile) ?? String.Empty; | ||
| 1666 | var id = xCustomAction.Attribute("Id")?.Value ?? Guid.NewGuid().ToString("N"); | ||
| 1667 | var ext = (xScript.Value == "jscript") ? ".js" : (xScript.Value == "vbscript") ? ".vbs" : ".txt"; | ||
| 1668 | |||
| 1669 | var scriptFile = Path.Combine(scriptFolder, id + ext); | ||
| 1670 | File.WriteAllText(scriptFile, scriptText); | ||
| 1671 | |||
| 1672 | RemoveChildren(xCustomAction); | ||
| 1673 | xCustomAction.Add(new XAttribute("ScriptSourceFile", scriptFile)); | ||
| 1674 | } | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | private void ConvertVariableElement(XElement xVariable) | ||
| 1679 | { | ||
| 1680 | var xType = xVariable.Attribute("Type"); | ||
| 1681 | var xValue = xVariable.Attribute("Value"); | ||
| 1682 | if (this.SourceVersion < 4) | ||
| 1683 | { | ||
| 1684 | if (xType == null) | ||
| 1685 | { | ||
| 1686 | if (WasImplicitlyStringTyped(xValue?.Value) && | ||
| 1687 | this.OnError(ConverterTestType.AssignVariableTypeFormatted, xVariable, "The \"string\" variable type now denotes a literal string. Use \"formatted\" to keep the previous behavior.")) | ||
| 1688 | { | ||
| 1689 | xVariable.Add(new XAttribute("Type", "formatted")); | ||
| 1690 | } | ||
| 1691 | } | ||
| 1692 | else if (xType.Value == "string" && | ||
| 1693 | this.OnError(ConverterTestType.AssignVariableTypeFormatted, xVariable, "The \"string\" variable type now denotes a literal string. Use \"formatted\" to keep the previous behavior.")) | ||
| 1694 | { | ||
| 1695 | xType.Value = "formatted"; | ||
| 1696 | } | ||
| 1697 | } | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | private void ConvertPropertyElement(XElement xProperty) | ||
| 1701 | { | ||
| 1702 | var xId = xProperty.Attribute("Id"); | ||
| 1703 | |||
| 1704 | if (xId.Value == "QtExecCmdTimeout") | ||
| 1705 | { | ||
| 1706 | this.OnError(ConverterTestType.QtExecCmdTimeoutAmbiguous, xProperty, "QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout."); | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | this.ConvertInnerTextToAttribute(xProperty, "Value"); | ||
| 1710 | } | ||
| 1711 | |||
| 1712 | private void ConvertUtilCloseApplicationElementName(XElement element) => this.ConvertInnerTextToAttribute(element, "Condition"); | ||
| 1713 | |||
| 1714 | private void ConvertUtilPermissionExElement(XElement element) | ||
| 1715 | { | ||
| 1716 | if (this.SourceVersion < 4 && null == element.Attribute("Inheritable")) | ||
| 1717 | { | ||
| 1718 | var inheritable = element.Parent.Name == CreateFolderElementName; | ||
| 1719 | if (!inheritable) | ||
| 1720 | { | ||
| 1721 | if (this.OnError(ConverterTestType.AssignPermissionExInheritable, element, "The PermissionEx Inheritable attribute is being set to 'no' to ensure it remains the same as the v3 default.")) | ||
| 1722 | { | ||
| 1723 | element.Add(new XAttribute("Inheritable", "no")); | ||
| 1724 | } | ||
| 1725 | } | ||
| 1726 | } | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | private void ConvertUtilRegistrySearchElement(XElement element) | ||
| 1730 | { | ||
| 1731 | this.RenameWin64ToBitness(element); | ||
| 1732 | |||
| 1733 | if (this.SourceVersion < 4) | ||
| 1734 | { | ||
| 1735 | var result = element.Attribute("Result")?.Value; | ||
| 1736 | if (result == null || result == "value") | ||
| 1737 | { | ||
| 1738 | this.OnError(ConverterTestType.UtilRegistryValueSearchBehaviorChange, element, "Breaking change: util:RegistrySearch for a value no longer clears the variable when the key or value is missing."); | ||
| 1739 | } | ||
| 1740 | } | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | private void ConvertUtilXmlConfigElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); | ||
| 1744 | |||
| 1745 | /// <summary> | ||
| 1746 | /// Converts a Wix element. | ||
| 1747 | /// </summary> | ||
| 1748 | /// <param name="element">The Wix element to convert.</param> | ||
| 1749 | /// <returns>The converted element.</returns> | ||
| 1750 | private void ConvertElementWithoutNamespace(XElement element) | ||
| 1751 | { | ||
| 1752 | if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName)) | ||
| 1753 | { | ||
| 1754 | element.Name = WixNamespace.GetName(element.Name.LocalName); | ||
| 1755 | |||
| 1756 | element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace. | ||
| 1757 | |||
| 1758 | foreach (var elementWithoutNamespace in element.DescendantsAndSelf().Where(e => XNamespace.None == e.Name.Namespace)) | ||
| 1759 | { | ||
| 1760 | elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName); | ||
| 1761 | } | ||
| 1762 | } | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | private void ConvertInnerTextToAttribute(XElement element, string attributeName) | ||
| 1766 | { | ||
| 1767 | if (TryGetInnerText(element, out var text) && | ||
| 1768 | this.OnError(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the '{1}' attribute instead.", element.Name.LocalName, attributeName)) | ||
| 1769 | { | ||
| 1770 | element.Add(new XAttribute(attributeName, text)); | ||
| 1771 | RemoveChildren(element); | ||
| 1772 | } | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | void RemoveAttributeIfPresent(XElement element, string attributeName, ConverterTestType type, string format) | ||
| 1776 | { | ||
| 1777 | var xAttribute = element.Attribute(attributeName); | ||
| 1778 | if (null != xAttribute && this.OnError(type, element, format, element.Name.LocalName, xAttribute.Name)) | ||
| 1779 | { | ||
| 1780 | xAttribute.Remove(); | ||
| 1781 | } | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | private void RenameWin64ToBitness(XElement element) | ||
| 1785 | { | ||
| 1786 | var win64 = element.Attribute("Win64"); | ||
| 1787 | if (win64 != null && this.OnError(ConverterTestType.Win64AttributeRenamed, element, "The {0} element's Win64 attribute has been renamed. Use the Bitness attribute instead.", element.Name.LocalName)) | ||
| 1788 | { | ||
| 1789 | var value = this.UpdateWin64ValueToBitnessValue(win64); | ||
| 1790 | element.Add(new XAttribute("Bitness", value)); | ||
| 1791 | win64.Remove(); | ||
| 1792 | } | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | private string UpdateWin64ValueToBitnessValue(XAttribute xWin64Attribute) | ||
| 1796 | { | ||
| 1797 | var value = xWin64Attribute.Value ?? String.Empty; | ||
| 1798 | switch (value) | ||
| 1799 | { | ||
| 1800 | case "yes": | ||
| 1801 | return "always64"; | ||
| 1802 | case "no": | ||
| 1803 | return "always32"; | ||
| 1804 | default: | ||
| 1805 | this.OnError(ConverterTestType.Win64AttributeRenameCannotBeAutomatic, xWin64Attribute, "Breaking change: The Win64 attribute's value '{0}' cannot be converted automatically to the new Bitness attribute.", value); | ||
| 1806 | return value; | ||
| 1807 | } | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) | ||
| 1811 | { | ||
| 1812 | if (null != types) | ||
| 1813 | { | ||
| 1814 | foreach (var type in types) | ||
| 1815 | { | ||
| 1816 | if (Enum.TryParse<ConverterTestType>(type, true, out var itt)) | ||
| 1817 | { | ||
| 1818 | yield return itt; | ||
| 1819 | } | ||
| 1820 | else // not a known ConverterTestType | ||
| 1821 | { | ||
| 1822 | this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type); | ||
| 1823 | } | ||
| 1824 | } | ||
| 1825 | } | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces) | ||
| 1829 | { | ||
| 1830 | foreach (var element in elements) | ||
| 1831 | { | ||
| 1832 | if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns)) | ||
| 1833 | { | ||
| 1834 | element.Name = ns.GetName(element.Name.LocalName); | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | // Remove all the attributes and add them back to with their namespace updated (as necessary). | ||
| 1838 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
| 1839 | element.RemoveAttributes(); | ||
| 1840 | |||
| 1841 | foreach (var attribute in attributes) | ||
| 1842 | { | ||
| 1843 | var convertedAttribute = attribute; | ||
| 1844 | |||
| 1845 | if (attribute.IsNamespaceDeclaration) | ||
| 1846 | { | ||
| 1847 | if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns)) | ||
| 1848 | { | ||
| 1849 | if (ns == XNamespace.None) | ||
| 1850 | { | ||
| 1851 | continue; | ||
| 1852 | } | ||
| 1853 | |||
| 1854 | convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName); | ||
| 1855 | } | ||
| 1856 | } | ||
| 1857 | else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns)) | ||
| 1858 | { | ||
| 1859 | convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | element.Add(convertedAttribute); | ||
| 1863 | } | ||
| 1864 | } | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | /// <summary> | ||
| 1868 | /// Determine if the whitespace preceding a node is appropriate for its depth level. | ||
| 1869 | /// </summary> | ||
| 1870 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 1871 | /// <param name="level">The depth level that should match this whitespace.</param> | ||
| 1872 | /// <param name="whitespace">The whitespace to validate.</param> | ||
| 1873 | /// <returns>true if the whitespace is legal; false otherwise.</returns> | ||
| 1874 | private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace) | ||
| 1875 | { | ||
| 1876 | // Strip off leading newlines; there can be an arbitrary number of these. | ||
| 1877 | whitespace = whitespace.TrimStart(XDocumentNewLine); | ||
| 1878 | |||
| 1879 | var indentation = new string(' ', level * indentationAmount); | ||
| 1880 | |||
| 1881 | return whitespace == indentation; | ||
| 1882 | } | ||
| 1883 | |||
| 1884 | /// <summary> | ||
| 1885 | /// Fix the whitespace in a whitespace node. | ||
| 1886 | /// </summary> | ||
| 1887 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 1888 | /// <param name="level">The depth level of the desired whitespace.</param> | ||
| 1889 | /// <param name="whitespace">The whitespace node to fix.</param> | ||
| 1890 | private static void FixupWhitespace(int indentationAmount, int level, XText whitespace) | ||
| 1891 | { | ||
| 1892 | var value = new StringBuilder(whitespace.Value.Length); | ||
| 1893 | |||
| 1894 | // Keep any previous preceeding new lines. | ||
| 1895 | var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count(); | ||
| 1896 | |||
| 1897 | // Ensure there is always at least one new line before the indentation. | ||
| 1898 | value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines); | ||
| 1899 | |||
| 1900 | whitespace.Value = value.Append(' ', level * indentationAmount).ToString(); | ||
| 1901 | } | ||
| 1902 | |||
| 1903 | /// <summary> | ||
| 1904 | /// Removes unused namespaces from the element and its children. | ||
| 1905 | /// </summary> | ||
| 1906 | /// <param name="root">Root element to start at.</param> | ||
| 1907 | private void RemoveUnusedNamespaces(XElement root) | ||
| 1908 | { | ||
| 1909 | var declarations = new List<XAttribute>(); | ||
| 1910 | var namespaces = new HashSet<string>(); | ||
| 1911 | |||
| 1912 | VisitElement(root, x => | ||
| 1913 | { | ||
| 1914 | if (x is XAttribute a && a.IsNamespaceDeclaration) | ||
| 1915 | { | ||
| 1916 | declarations.Add(a); | ||
| 1917 | namespaces.Add(a.Value); | ||
| 1918 | } | ||
| 1919 | return true; | ||
| 1920 | }); | ||
| 1921 | |||
| 1922 | foreach (var ns in namespaces.ToList()) | ||
| 1923 | { | ||
| 1924 | VisitElement(root, x => | ||
| 1925 | { | ||
| 1926 | if ((x is XElement e && e.Name.Namespace == ns) || | ||
| 1927 | (x is XAttribute a && !a.IsNamespaceDeclaration && a.Name.Namespace == ns)) | ||
| 1928 | { | ||
| 1929 | namespaces.Remove(ns); | ||
| 1930 | return false; | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | return true; | ||
| 1934 | }); | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | foreach (var declaration in declarations) | ||
| 1938 | { | ||
| 1939 | if (namespaces.Contains(declaration.Value) && | ||
| 1940 | this.OnError(ConverterTestType.RemoveUnusedNamespaces, declaration, "The namespace '{0}' is not used. Remove unused namespaces.", declaration.Value)) | ||
| 1941 | { | ||
| 1942 | declaration.Remove(); | ||
| 1943 | } | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | /// <summary> | ||
| 1948 | /// Output an error message to the console. | ||
| 1949 | /// </summary> | ||
| 1950 | /// <param name="converterTestType">The type of converter test.</param> | ||
| 1951 | /// <param name="node">The node that caused the error.</param> | ||
| 1952 | /// <param name="message">Detailed error message.</param> | ||
| 1953 | /// <param name="args">Additional formatted string arguments.</param> | ||
| 1954 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> | ||
| 1955 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) | ||
| 1956 | { | ||
| 1957 | // Ignore the error if explicitly ignored or outside the range of the current operation. | ||
| 1958 | if (this.IgnoreErrors.Contains(converterTestType) || | ||
| 1959 | (this.Operation == ConvertOperation.Convert && converterTestType < ConverterTestType.EndIgnoreInConvert) || | ||
| 1960 | (this.Operation == ConvertOperation.Format && converterTestType > ConverterTestType.BeginIgnoreInFormat)) | ||
| 1961 | { | ||
| 1962 | return false; | ||
| 1963 | } | ||
| 1964 | |||
| 1965 | // Increase the error count. | ||
| 1966 | this.Errors++; | ||
| 1967 | |||
| 1968 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wix.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | ||
| 1969 | var warning = this.ErrorsAsWarnings.Contains(converterTestType); | ||
| 1970 | var display = String.Format(CultureInfo.CurrentCulture, message, args); | ||
| 1971 | |||
| 1972 | var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString()); | ||
| 1973 | |||
| 1974 | this.Messaging.Write(msg); | ||
| 1975 | |||
| 1976 | return true; | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | /// <summary> | ||
| 1980 | /// Return an identifier based on passed file/directory name | ||
| 1981 | /// </summary> | ||
| 1982 | /// <param name="name">File/directory name to generate identifer from</param> | ||
| 1983 | /// <returns>A version of the name that is a legal identifier.</returns> | ||
| 1984 | /// <remarks>This is duplicated from WiX's Common class.</remarks> | ||
| 1985 | private static string GetIdentifierFromName(string name) | ||
| 1986 | { | ||
| 1987 | var result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_". | ||
| 1988 | |||
| 1989 | // MSI identifiers must begin with an alphabetic character or an | ||
| 1990 | // underscore. Prefix all other values with an underscore. | ||
| 1991 | if (AddPrefix.IsMatch(name)) | ||
| 1992 | { | ||
| 1993 | result = String.Concat("_", result); | ||
| 1994 | } | ||
| 1995 | |||
| 1996 | return result; | ||
| 1997 | } | ||
| 1998 | |||
| 1999 | private static string LowercaseFirstChar(string value) | ||
| 2000 | { | ||
| 2001 | if (!String.IsNullOrEmpty(value)) | ||
| 2002 | { | ||
| 2003 | var c = Char.ToLowerInvariant(value[0]); | ||
| 2004 | if (c != value[0]) | ||
| 2005 | { | ||
| 2006 | var remainder = value.Length > 1 ? value.Substring(1) : String.Empty; | ||
| 2007 | return c + remainder; | ||
| 2008 | } | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | return value; | ||
| 2012 | } | ||
| 2013 | |||
| 2014 | private static string UppercaseFirstChar(string value) | ||
| 2015 | { | ||
| 2016 | if (!String.IsNullOrEmpty(value)) | ||
| 2017 | { | ||
| 2018 | var c = Char.ToUpperInvariant(value[0]); | ||
| 2019 | if (c != value[0]) | ||
| 2020 | { | ||
| 2021 | var remainder = value.Length > 1 ? value.Substring(1) : String.Empty; | ||
| 2022 | return c + remainder; | ||
| 2023 | } | ||
| 2024 | } | ||
| 2025 | |||
| 2026 | return value; | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | private static bool TryGetInnerText(XElement element, out string value) | ||
| 2030 | { | ||
| 2031 | value = null; | ||
| 2032 | |||
| 2033 | var nodes = element.Nodes(); | ||
| 2034 | |||
| 2035 | if (nodes.All(e => e.NodeType == XmlNodeType.Text || e.NodeType == XmlNodeType.CDATA)) | ||
| 2036 | { | ||
| 2037 | value = String.Join(String.Empty, nodes.Cast<XText>().Select(TrimTextValue)); | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | return !String.IsNullOrEmpty(value); | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | private static bool IsTextNode(XNode node, out XText text) | ||
| 2044 | { | ||
| 2045 | text = null; | ||
| 2046 | |||
| 2047 | if (node.NodeType == XmlNodeType.Text || node.NodeType == XmlNodeType.CDATA) | ||
| 2048 | { | ||
| 2049 | text = (XText)node; | ||
| 2050 | } | ||
| 2051 | |||
| 2052 | return text != null; | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | private static void TrimLeadingText(XDocument document) | ||
| 2056 | { | ||
| 2057 | while (IsTextNode(document.Nodes().FirstOrDefault(), out var text)) | ||
| 2058 | { | ||
| 2059 | text.Remove(); | ||
| 2060 | } | ||
| 2061 | } | ||
| 2062 | |||
| 2063 | private static string TrimTextValue(XText text) | ||
| 2064 | { | ||
| 2065 | var value = text.Value; | ||
| 2066 | |||
| 2067 | if (String.IsNullOrEmpty(value)) | ||
| 2068 | { | ||
| 2069 | return String.Empty; | ||
| 2070 | } | ||
| 2071 | else if (text.NodeType == XmlNodeType.CDATA && String.IsNullOrWhiteSpace(value)) | ||
| 2072 | { | ||
| 2073 | return " "; | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | return value.Trim(); | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | private static void RemoveChildren(XElement element) | ||
| 2080 | { | ||
| 2081 | var nodes = element.Nodes().ToList(); | ||
| 2082 | foreach (var node in nodes) | ||
| 2083 | { | ||
| 2084 | node.Remove(); | ||
| 2085 | } | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | private static bool VisitElement(XElement element, Func<XObject, bool> visitor) | ||
| 2089 | { | ||
| 2090 | if (!visitor(element)) | ||
| 2091 | { | ||
| 2092 | return false; | ||
| 2093 | } | ||
| 2094 | |||
| 2095 | if (!element.Attributes().All(a => visitor(a))) | ||
| 2096 | { | ||
| 2097 | return false; | ||
| 2098 | } | ||
| 2099 | |||
| 2100 | return element.Elements().All(e => VisitElement(e, visitor)); | ||
| 2101 | } | ||
| 2102 | |||
| 2103 | private static bool WasImplicitlyStringTyped(string value) | ||
| 2104 | { | ||
| 2105 | if (value == null) | ||
| 2106 | { | ||
| 2107 | return false; | ||
| 2108 | } | ||
| 2109 | else if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase)) | ||
| 2110 | { | ||
| 2111 | if (Int32.TryParse(value.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out var _)) | ||
| 2112 | { | ||
| 2113 | return false; | ||
| 2114 | } | ||
| 2115 | else if (Version.TryParse(value.Substring(1), out var _)) | ||
| 2116 | { | ||
| 2117 | return false; | ||
| 2118 | } | ||
| 2119 | } | ||
| 2120 | else if (Int64.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out var _)) | ||
| 2121 | { | ||
| 2122 | return false; | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | return true; | ||
| 2126 | } | ||
| 2127 | |||
| 2128 | /// <summary> | ||
| 2129 | /// Converter test types. These are used to condition error messages down to warnings. | ||
| 2130 | /// </summary> | ||
| 2131 | private enum ConverterTestType | ||
| 2132 | { | ||
| 2133 | /// <summary> | ||
| 2134 | /// Internal-only: displayed when a string cannot be converted to an ConverterTestType. | ||
| 2135 | /// </summary> | ||
| 2136 | ConverterTestTypeUnknown, | ||
| 2137 | |||
| 2138 | /// <summary> | ||
| 2139 | /// Displayed when an XML loading exception has occurred. | ||
| 2140 | /// </summary> | ||
| 2141 | XmlException, | ||
| 2142 | |||
| 2143 | /// <summary> | ||
| 2144 | /// Displayed when the whitespace preceding a node is wrong. | ||
| 2145 | /// </summary> | ||
| 2146 | WhitespacePrecedingNodeWrong, | ||
| 2147 | |||
| 2148 | /// <summary> | ||
| 2149 | /// Displayed when the whitespace preceding an end element is wrong. | ||
| 2150 | /// </summary> | ||
| 2151 | WhitespacePrecedingEndElementWrong, | ||
| 2152 | |||
| 2153 | /// Before this point, ignore errors on convert operation | ||
| 2154 | EndIgnoreInConvert, | ||
| 2155 | |||
| 2156 | /// <summary> | ||
| 2157 | /// Displayed when the XML declaration is present in the source file. | ||
| 2158 | /// </summary> | ||
| 2159 | DeclarationPresent, | ||
| 2160 | |||
| 2161 | /// <summary> | ||
| 2162 | /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file. | ||
| 2163 | /// </summary> | ||
| 2164 | UnauthorizedAccessException, | ||
| 2165 | |||
| 2166 | /// After this point, ignore errors on format operation | ||
| 2167 | BeginIgnoreInFormat, | ||
| 2168 | |||
| 2169 | /// <summary> | ||
| 2170 | /// Displayed when the xmlns attribute is missing from the document element. | ||
| 2171 | /// </summary> | ||
| 2172 | XmlnsMissing, | ||
| 2173 | |||
| 2174 | /// <summary> | ||
| 2175 | /// Displayed when the xmlns attribute on the document element is wrong. | ||
| 2176 | /// </summary> | ||
| 2177 | XmlnsValueWrong, | ||
| 2178 | |||
| 2179 | /// <summary> | ||
| 2180 | /// Displayed when inner text contains a deprecated $(loc.xxx) reference. | ||
| 2181 | /// </summary> | ||
| 2182 | DeprecatedLocalizationVariablePrefixInTextValue, | ||
| 2183 | |||
| 2184 | /// <summary> | ||
| 2185 | /// Displayed when an attribute value contains a deprecated $(loc.xxx) reference. | ||
| 2186 | /// </summary> | ||
| 2187 | DeprecatedLocalizationVariablePrefixInAttributeValue, | ||
| 2188 | |||
| 2189 | /// <summary> | ||
| 2190 | /// Assign an identifier to a File element when on Id attribute is specified. | ||
| 2191 | /// </summary> | ||
| 2192 | AssignAnonymousFileId, | ||
| 2193 | |||
| 2194 | /// <summary> | ||
| 2195 | /// SuppressSignatureValidation attribute is obsolete and corresponding functionality removed. | ||
| 2196 | /// </summary> | ||
| 2197 | BundleSignatureValidationObsolete, | ||
| 2198 | |||
| 2199 | /// <summary> | ||
| 2200 | /// WixCA Binary/@Id has been renamed to UtilCA. | ||
| 2201 | /// </summary> | ||
| 2202 | WixCABinaryIdRenamed, | ||
| 2203 | |||
| 2204 | /// <summary> | ||
| 2205 | /// QtExec custom actions have been renamed. | ||
| 2206 | /// </summary> | ||
| 2207 | QuietExecCustomActionsRenamed, | ||
| 2208 | |||
| 2209 | /// <summary> | ||
| 2210 | /// QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout. | ||
| 2211 | /// </summary> | ||
| 2212 | QtExecCmdTimeoutAmbiguous, | ||
| 2213 | |||
| 2214 | /// <summary> | ||
| 2215 | /// Directory/@ShortName may only be specified with Directory/@Name. | ||
| 2216 | /// </summary> | ||
| 2217 | AssignDirectoryNameFromShortName, | ||
| 2218 | |||
| 2219 | /// <summary> | ||
| 2220 | /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal for MSI. Use BundleCustomData element for Bundles. | ||
| 2221 | /// </summary> | ||
| 2222 | BootstrapperApplicationDataDeprecated, | ||
| 2223 | |||
| 2224 | /// <summary> | ||
| 2225 | /// Inheritable is new and is now defaulted to 'yes' which is a change in behavior for all but children of CreateFolder. | ||
| 2226 | /// </summary> | ||
| 2227 | AssignPermissionExInheritable, | ||
| 2228 | |||
| 2229 | /// <summary> | ||
| 2230 | /// Column element's Category attribute is camel-case. | ||
| 2231 | /// </summary> | ||
| 2232 | ColumnCategoryCamelCase, | ||
| 2233 | |||
| 2234 | /// <summary> | ||
| 2235 | /// Column element's Modularize attribute is camel-case. | ||
| 2236 | /// </summary> | ||
| 2237 | ColumnModularizeCamelCase, | ||
| 2238 | |||
| 2239 | /// <summary> | ||
| 2240 | /// Inner text value should move to an attribute. | ||
| 2241 | /// </summary> | ||
| 2242 | InnerTextDeprecated, | ||
| 2243 | |||
| 2244 | /// <summary> | ||
| 2245 | /// Explicit auto-GUID unnecessary. | ||
| 2246 | /// </summary> | ||
| 2247 | AutoGuidUnnecessary, | ||
| 2248 | |||
| 2249 | /// <summary> | ||
| 2250 | /// The Feature Absent attribute renamed to AllowAbsent. | ||
| 2251 | /// </summary> | ||
| 2252 | FeatureAbsentAttributeReplaced, | ||
| 2253 | |||
| 2254 | /// <summary> | ||
| 2255 | /// The Feature AllowAdvertise attribute value deprecated. | ||
| 2256 | /// </summary> | ||
| 2257 | FeatureAllowAdvertiseValueDeprecated, | ||
| 2258 | |||
| 2259 | /// <summary> | ||
| 2260 | /// The Condition='1' attribute is unnecessary on Publish elements. | ||
| 2261 | /// </summary> | ||
| 2262 | PublishConditionOneUnnecessary, | ||
| 2263 | |||
| 2264 | /// <summary> | ||
| 2265 | /// DpiAwareness is new and is defaulted to 'perMonitorV2' which is a change in behavior. | ||
| 2266 | /// </summary> | ||
| 2267 | AssignBootstrapperApplicationDpiAwareness, | ||
| 2268 | |||
| 2269 | /// <summary> | ||
| 2270 | /// The string variable type was previously treated as formatted. | ||
| 2271 | /// </summary> | ||
| 2272 | AssignVariableTypeFormatted, | ||
| 2273 | |||
| 2274 | /// <summary> | ||
| 2275 | /// The CustomAction attributes have been renamed from BinaryKey and FileKey to BinaryRef and FileRef. | ||
| 2276 | /// </summary> | ||
| 2277 | CustomActionKeysAreNowRefs, | ||
| 2278 | |||
| 2279 | /// <summary> | ||
| 2280 | /// The Product and Package elements have been renamed and reorganized. | ||
| 2281 | /// </summary> | ||
| 2282 | ProductAndPackageRenamed, | ||
| 2283 | |||
| 2284 | /// <summary> | ||
| 2285 | /// The Module and Package elements have been renamed and reorganized. | ||
| 2286 | /// </summary> | ||
| 2287 | ModuleAndPackageRenamed, | ||
| 2288 | |||
| 2289 | /// <summary> | ||
| 2290 | /// A MediaTemplate with no attributes set is now provided by default. | ||
| 2291 | /// </summary> | ||
| 2292 | DefaultMediaTemplate, | ||
| 2293 | |||
| 2294 | /// <summary> | ||
| 2295 | /// util:RegistrySearch has breaking change when value is missing. | ||
| 2296 | /// </summary> | ||
| 2297 | UtilRegistryValueSearchBehaviorChange, | ||
| 2298 | |||
| 2299 | /// <summary> | ||
| 2300 | /// DisplayInternalUI can't be converted. | ||
| 2301 | /// </summary> | ||
| 2302 | DisplayInternalUiNotConvertable, | ||
| 2303 | |||
| 2304 | /// <summary> | ||
| 2305 | /// InstallerVersion has breaking change when missing. | ||
| 2306 | /// </summary> | ||
| 2307 | InstallerVersionBehaviorChange, | ||
| 2308 | |||
| 2309 | /// <summary> | ||
| 2310 | /// Verb/@Target can't be converted. | ||
| 2311 | /// </summary> | ||
| 2312 | VerbTargetNotConvertable, | ||
| 2313 | |||
| 2314 | /// <summary> | ||
| 2315 | /// The bootstrapper application dll is now specified in its own element. | ||
| 2316 | /// </summary> | ||
| 2317 | BootstrapperApplicationDll, | ||
| 2318 | |||
| 2319 | /// <summary> | ||
| 2320 | /// The new bootstrapper application dll element is required. | ||
| 2321 | /// </summary> | ||
| 2322 | BootstrapperApplicationDllRequired, | ||
| 2323 | |||
| 2324 | /// <summary> | ||
| 2325 | /// bal:UseUILanguages is deprecated, 'true' is now the standard behavior. | ||
| 2326 | /// </summary> | ||
| 2327 | BalUseUILanguagesDeprecated, | ||
| 2328 | |||
| 2329 | /// <summary> | ||
| 2330 | /// The custom elements for built-in BAs are now required. | ||
| 2331 | /// </summary> | ||
| 2332 | BalBootstrapperApplicationRefToElement, | ||
| 2333 | |||
| 2334 | /// <summary> | ||
| 2335 | /// The ExePackage elements "XxxCommand" attributes have been renamed to "XxxArguments". | ||
| 2336 | /// </summary> | ||
| 2337 | RenameExePackageCommandToArguments, | ||
| 2338 | |||
| 2339 | /// <summary> | ||
| 2340 | /// The Win64 attribute has been renamed. Use the Bitness attribute instead. | ||
| 2341 | /// </summary> | ||
| 2342 | Win64AttributeRenamed, | ||
| 2343 | |||
| 2344 | /// <summary> | ||
| 2345 | /// Breaking change: The Win64 attribute's value '{0}' cannot be converted automatically to the new Bitness attribute. | ||
| 2346 | /// </summary> | ||
| 2347 | Win64AttributeRenameCannotBeAutomatic, | ||
| 2348 | |||
| 2349 | /// <summary> | ||
| 2350 | /// The Tag element has been renamed. Use the element 'SoftwareTag' name. | ||
| 2351 | /// </summary> | ||
| 2352 | TagElementRenamed, | ||
| 2353 | |||
| 2354 | /// <summary> | ||
| 2355 | /// The Dependency namespace has been incorporated into WiX v4 namespace. | ||
| 2356 | /// </summary> | ||
| 2357 | IntegratedDependencyNamespace, | ||
| 2358 | |||
| 2359 | /// <summary> | ||
| 2360 | /// Remove unused namespaces. | ||
| 2361 | /// </summary> | ||
| 2362 | RemoveUnusedNamespaces, | ||
| 2363 | |||
| 2364 | /// <summary> | ||
| 2365 | /// The Remote element has been renamed. Use the "XxxPackagePayload" element instead. | ||
| 2366 | /// </summary> | ||
| 2367 | RemotePayloadRenamed, | ||
| 2368 | |||
| 2369 | /// <summary> | ||
| 2370 | /// The XxxPackage/@Name attribute must be specified on the child XxxPackagePayload element when using a remote payload. | ||
| 2371 | /// </summary> | ||
| 2372 | NameAttributeMovedToRemotePayload, | ||
| 2373 | |||
| 2374 | /// <summary> | ||
| 2375 | /// The XxxPackage/@Compressed attribute should not be specified when using a remote payload. | ||
| 2376 | /// </summary> | ||
| 2377 | CompressedAttributeUnnecessaryForRemotePayload, | ||
| 2378 | |||
| 2379 | /// <summary> | ||
| 2380 | /// The XxxPackage/@DownloadUrl attribute must be specified on the child XxxPackagePayload element when using a remote payload. | ||
| 2381 | /// </summary> | ||
| 2382 | DownloadUrlAttributeMovedToRemotePayload, | ||
| 2383 | |||
| 2384 | /// <summary> | ||
| 2385 | /// The hash algorithm used for bundles changed from SHA1 to SHA512. | ||
| 2386 | /// </summary> | ||
| 2387 | BurnHashAlgorithmChanged, | ||
| 2388 | |||
| 2389 | /// <summary> | ||
| 2390 | /// CustomTable elements can't always be converted. | ||
| 2391 | /// </summary> | ||
| 2392 | CustomTableNotAlwaysConvertable, | ||
| 2393 | |||
| 2394 | /// <summary> | ||
| 2395 | /// CustomTable elements that don't contain the table definition are now CustomTableRef. | ||
| 2396 | /// </summary> | ||
| 2397 | CustomTableRef, | ||
| 2398 | |||
| 2399 | /// <summary> | ||
| 2400 | /// The RegistryKey element's Action attribute is obsolete. | ||
| 2401 | /// </summary> | ||
| 2402 | RegistryKeyActionObsolete, | ||
| 2403 | |||
| 2404 | /// <summary> | ||
| 2405 | /// The TagRef element has been renamed. Use the element 'SoftwareTagRef' name. | ||
| 2406 | /// </summary> | ||
| 2407 | TagRefElementRenamed, | ||
| 2408 | |||
| 2409 | /// <summary> | ||
| 2410 | /// The SoftwareTag element's Licensed attribute is obsolete. | ||
| 2411 | /// </summary> | ||
| 2412 | SoftwareTagLicensedObsolete, | ||
| 2413 | |||
| 2414 | /// <summary> | ||
| 2415 | /// The SoftwareTag element's Type attribute is obsolete. | ||
| 2416 | /// </summary> | ||
| 2417 | SoftwareTagTypeObsolete, | ||
| 2418 | |||
| 2419 | /// <summary> | ||
| 2420 | /// TARGETDIR directory should not longer be explicitly defined. | ||
| 2421 | /// </summary> | ||
| 2422 | TargetDirDeprecated, | ||
| 2423 | |||
| 2424 | /// <summary> | ||
| 2425 | /// Standard directories should no longer be defined using the Directory element. | ||
| 2426 | /// </summary> | ||
| 2427 | DefiningStandardDirectoryDeprecated, | ||
| 2428 | |||
| 2429 | /// <summary> | ||
| 2430 | /// Naked custom action and property references replaced with WixUtilExtension elements. | ||
| 2431 | /// </summary> | ||
| 2432 | UtilReferencesReplaced, | ||
| 2433 | } | ||
| 2434 | } | ||
| 2435 | } | ||
diff --git a/src/wix/WixToolset.Converters/WixToolset.Converters.csproj b/src/wix/WixToolset.Converters/WixToolset.Converters.csproj new file mode 100644 index 00000000..7dddefa5 --- /dev/null +++ b/src/wix/WixToolset.Converters/WixToolset.Converters.csproj | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | |||
| 6 | <PropertyGroup> | ||
| 7 | <TargetFrameworks>netstandard2.0</TargetFrameworks> | ||
| 8 | <TargetFrameworks Condition=" '$(Configuration)'=='Release' ">$(TargetFrameworks);net461;net472</TargetFrameworks> | ||
| 9 | <Description>Converter</Description> | ||
| 10 | <Title>WiX Toolset Converters</Title> | ||
| 11 | <DebugType>embedded</DebugType> | ||
| 12 | <PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
| 13 | <CreateDocumentationFile>true</CreateDocumentationFile> | ||
| 14 | </PropertyGroup> | ||
| 15 | |||
| 16 | <ItemGroup> | ||
| 17 | <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" /> | ||
| 18 | </ItemGroup> | ||
| 19 | |||
| 20 | <ItemGroup> | ||
| 21 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> | ||
| 22 | <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" /> | ||
| 23 | </ItemGroup> | ||
| 24 | </Project> | ||
diff --git a/src/wix/WixToolset.Converters/WixToolsetCoreServiceProviderExtensions.cs b/src/wix/WixToolset.Converters/WixToolsetCoreServiceProviderExtensions.cs new file mode 100644 index 00000000..084d3b92 --- /dev/null +++ b/src/wix/WixToolset.Converters/WixToolsetCoreServiceProviderExtensions.cs | |||
| @@ -0,0 +1,25 @@ | |||
| 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 WixToolset.Converters | ||
| 4 | { | ||
| 5 | using WixToolset.Extensibility.Services; | ||
| 6 | |||
| 7 | /// <summary> | ||
| 8 | /// Extension methods for adding Converters services. | ||
| 9 | /// </summary> | ||
| 10 | public static class WixToolsetCoreServiceProviderExtensions | ||
| 11 | { | ||
| 12 | /// <summary> | ||
| 13 | /// Adds Converters services. | ||
| 14 | /// </summary> | ||
| 15 | /// <param name="coreProvider"></param> | ||
| 16 | /// <returns></returns> | ||
| 17 | public static IWixToolsetCoreServiceProvider AddConverter(this IWixToolsetCoreServiceProvider coreProvider) | ||
| 18 | { | ||
| 19 | var extensionManager = coreProvider.GetService<IExtensionManager>(); | ||
| 20 | extensionManager.Add(typeof(ConverterExtensionFactory).Assembly); | ||
| 21 | |||
| 22 | return coreProvider; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/src/wix/deps/wix.dll b/src/wix/deps/wix.dll new file mode 100644 index 00000000..64f70f75 --- /dev/null +++ b/src/wix/deps/wix.dll | |||
| Binary files differ | |||
diff --git a/src/wix/nuget.config b/src/wix/nuget.config index 022f9240..f985e459 100644 --- a/src/wix/nuget.config +++ b/src/wix/nuget.config | |||
| @@ -10,4 +10,4 @@ | |||
| 10 | <add key="wixbuildtools" value="https://ci.appveyor.com/nuget/wixbuildtools" /> | 10 | <add key="wixbuildtools" value="https://ci.appveyor.com/nuget/wixbuildtools" /> |
| 11 | <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> | 11 | <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> |
| 12 | </packageSources> | 12 | </packageSources> |
| 13 | </configuration> \ No newline at end of file | 13 | </configuration> |
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/ConvertSymbolsFixture.cs b/src/wix/test/WixToolsetTest.Converters.Symbolizer/ConvertSymbolsFixture.cs new file mode 100644 index 00000000..01213524 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/ConvertSymbolsFixture.cs | |||
| @@ -0,0 +1,606 @@ | |||
| 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.Converters.Symbolizer | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | using System.Linq; | ||
| 9 | using WixBuildTools.TestSupport; | ||
| 10 | using Wix3 = Microsoft.Tools.WindowsInstallerXml; | ||
| 11 | using WixToolset.Converters.Symbolizer; | ||
| 12 | using WixToolset.Data; | ||
| 13 | using WixToolset.Data.WindowsInstaller; | ||
| 14 | using WixToolset.Data.Symbols; | ||
| 15 | using Xunit; | ||
| 16 | |||
| 17 | public class ConvertSymbolsFixture | ||
| 18 | { | ||
| 19 | [Fact] | ||
| 20 | public void CanLoadWixoutAndConvertToIntermediate() | ||
| 21 | { | ||
| 22 | var rootFolder = TestData.Get(); | ||
| 23 | var dataFolder = TestData.Get(@"TestData\Integration"); | ||
| 24 | |||
| 25 | using (var fs = new DisposableFileSystem()) | ||
| 26 | { | ||
| 27 | var intermediateFolder = fs.GetFolder(); | ||
| 28 | |||
| 29 | var path = Path.Combine(dataFolder, "test.wixout"); | ||
| 30 | |||
| 31 | var intermediate = ConvertSymbols.ConvertFile(path); | ||
| 32 | |||
| 33 | Assert.NotNull(intermediate); | ||
| 34 | Assert.Single(intermediate.Sections); | ||
| 35 | Assert.Equal(String.Empty, intermediate.Id); | ||
| 36 | |||
| 37 | // Save and load to guarantee round-tripping support. | ||
| 38 | // | ||
| 39 | var wixiplFile = Path.Combine(intermediateFolder, "test.wixipl"); | ||
| 40 | intermediate.Save(wixiplFile); | ||
| 41 | |||
| 42 | intermediate = Intermediate.Load(wixiplFile); | ||
| 43 | |||
| 44 | var output = Wix3.Output.Load(path, suppressVersionCheck: true, suppressSchema: true); | ||
| 45 | var wixMediaByDiskId = IndexWixMediaTableByDiskId(output); | ||
| 46 | |||
| 47 | // Dump to text for easy diffing, with some massaging to keep v3 and v4 diffable. | ||
| 48 | // | ||
| 49 | var tables = output.Tables.Cast<Wix3.Table>(); | ||
| 50 | var wix3Dump = tables | ||
| 51 | .SelectMany(table => table.Rows.Cast<Wix3.Row>() | ||
| 52 | .SelectMany(row => RowToStrings(row, wixMediaByDiskId))) | ||
| 53 | .Where(s => !String.IsNullOrEmpty(s)) | ||
| 54 | .OrderBy(s => s) | ||
| 55 | .ToArray(); | ||
| 56 | |||
| 57 | var symbols = intermediate.Sections.SelectMany(s => s.Symbols); | ||
| 58 | |||
| 59 | var assemblySymbolsByFileId = symbols.OfType<AssemblySymbol>().ToDictionary(a => a.Id.Id); | ||
| 60 | |||
| 61 | var wix4Dump = symbols | ||
| 62 | .SelectMany(symbol => SymbolToStrings(symbol, assemblySymbolsByFileId)) | ||
| 63 | .OrderBy(s => s) | ||
| 64 | .ToArray(); | ||
| 65 | |||
| 66 | #if false | ||
| 67 | Assert.Equal(wix3Dump, wix4Dump); | ||
| 68 | #else // useful when you want to diff the outputs with another diff tool. | ||
| 69 | var wix3TextDump = String.Join(Environment.NewLine, wix3Dump); | ||
| 70 | var wix4TextDump = String.Join(Environment.NewLine, wix4Dump); | ||
| 71 | |||
| 72 | var path3 = Path.Combine(Path.GetTempPath(), "~3.txt"); | ||
| 73 | var path4 = Path.Combine(Path.GetTempPath(), "~4.txt"); | ||
| 74 | |||
| 75 | File.WriteAllText(path3, wix3TextDump); | ||
| 76 | File.WriteAllText(path4, wix4TextDump); | ||
| 77 | |||
| 78 | Assert.Equal(wix3TextDump, wix4TextDump); | ||
| 79 | #endif | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | private static Dictionary<int, Wix3.WixMediaRow> IndexWixMediaTableByDiskId(Wix3.Output output) | ||
| 84 | { | ||
| 85 | var wixMediaByDiskId = new Dictionary<int, Wix3.WixMediaRow>(); | ||
| 86 | var wixMediaTable = output.Tables["WixMedia"]; | ||
| 87 | |||
| 88 | if (wixMediaTable != null) | ||
| 89 | { | ||
| 90 | foreach (Wix3.WixMediaRow row in wixMediaTable.Rows) | ||
| 91 | { | ||
| 92 | wixMediaByDiskId.Add((int)row[0], row); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | return wixMediaByDiskId; | ||
| 97 | } | ||
| 98 | |||
| 99 | private static IEnumerable<string> RowToStrings(Wix3.Row row, Dictionary<int, Wix3.WixMediaRow> wixMediaByDiskId) | ||
| 100 | { | ||
| 101 | string fields = null; | ||
| 102 | |||
| 103 | // Massage output to match WiX v3 rows and v4 symbols. | ||
| 104 | // | ||
| 105 | switch (row.Table.Name) | ||
| 106 | { | ||
| 107 | case "Directory": | ||
| 108 | var dirs = SplitDefaultDir((string)row[2]); | ||
| 109 | fields = String.Join(",", row[0], row[1], dirs[0], dirs[1], dirs[2], dirs[3]); | ||
| 110 | break; | ||
| 111 | case "File": | ||
| 112 | { | ||
| 113 | var fieldValues = row.Fields.Take(7).Select(SafeConvertField).ToArray(); | ||
| 114 | if (fieldValues[3] == null) | ||
| 115 | { | ||
| 116 | // "Somebody" sometimes writes out a null field even when the column definition says | ||
| 117 | // it's non-nullable. Not naming names or anything. (SWID tags.) | ||
| 118 | fieldValues[3] = "0"; | ||
| 119 | } | ||
| 120 | fields = String.Join(",", fieldValues); | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | case "Media": | ||
| 124 | var compression = wixMediaByDiskId.TryGetValue((int)row[0], out var wixMedia) ? (CompressionLevel?)Enum.Parse(typeof(CompressionLevel), SafeConvertField(wixMedia.Fields[1]), true) : null; | ||
| 125 | |||
| 126 | fields = String.Join(",", row.Fields.Select(SafeConvertField)); | ||
| 127 | fields = String.Join(",", fields, (int?)compression, SafeConvertField(wixMedia?.Fields[2])); | ||
| 128 | break; | ||
| 129 | case "RegLocator": | ||
| 130 | var type = (int)row[4]; | ||
| 131 | fields = String.Join(",", row[0], row[1], row[2], row[3], type & 0xF, (type & 0x10) == 0x10); | ||
| 132 | break; | ||
| 133 | case "RemoveFile": | ||
| 134 | var attributes = (int)row[4]; | ||
| 135 | var onInstall = (attributes & 1) == 1 ? (bool?)true : null; | ||
| 136 | var onUninstall = (attributes & 2) == 2 ? (bool?)true : null; | ||
| 137 | fields = String.Join(",", row.Fields.Take(4).Select(SafeConvertField)); | ||
| 138 | fields = String.Join(",", fields, onInstall, onUninstall); | ||
| 139 | break; | ||
| 140 | case "Shortcut": | ||
| 141 | var split = ((string)row[2]).Split('|'); | ||
| 142 | var afterName = String.Join(",", row.Fields.Skip(3).Select(SafeConvertField)); | ||
| 143 | fields = String.Join(",", row[0], row[1], split.Length > 1 ? split[1] : split[0], split.Length > 1 ? split[0] : String.Empty, afterName); | ||
| 144 | break; | ||
| 145 | case "WixAction": | ||
| 146 | var table = (int)SequenceStringToSequenceTable(row[0]); | ||
| 147 | fields = String.Join(",", table, row[1], row[2], row[3], row[4], row[5], row[6]); | ||
| 148 | break; | ||
| 149 | case "WixFile": | ||
| 150 | { | ||
| 151 | var fieldValues = row.Fields.Select(SafeConvertField).ToArray(); | ||
| 152 | if (fieldValues[8] == null) | ||
| 153 | { | ||
| 154 | // "Somebody" sometimes writes out a null field even when the column definition says | ||
| 155 | // it's non-nullable. Not naming names or anything. (SWID tags.) | ||
| 156 | fieldValues[8] = "0"; | ||
| 157 | } | ||
| 158 | if (fieldValues[10] == null) | ||
| 159 | { | ||
| 160 | // WixFile rows that come from merge modules will not have the attributes column set | ||
| 161 | // so initilaize with 0. | ||
| 162 | fieldValues[10] = "0"; | ||
| 163 | } | ||
| 164 | fields = String.Join(",", fieldValues); | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | case "WixMedia": | ||
| 168 | break; | ||
| 169 | default: | ||
| 170 | fields = String.Join(",", row.Fields.Select(SafeConvertField)); | ||
| 171 | break; | ||
| 172 | } | ||
| 173 | |||
| 174 | if (fields != null) | ||
| 175 | { | ||
| 176 | yield return $"{row.Table.Name}:{fields}"; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | private static IEnumerable<string> SymbolToStrings(IntermediateSymbol symbol, Dictionary<string, AssemblySymbol> assemblySymbolsByFileId) | ||
| 181 | { | ||
| 182 | var name = symbol.Definition.Type == SymbolDefinitionType.SummaryInformation ? "_SummaryInformation" : symbol.Definition.Name; | ||
| 183 | var id = symbol.Id?.Id ?? String.Empty; | ||
| 184 | |||
| 185 | string fields; | ||
| 186 | switch (symbol.Definition.Name) | ||
| 187 | { | ||
| 188 | // Massage output to match WiX v3 rows and v4 symbols. | ||
| 189 | // | ||
| 190 | case "Component": | ||
| 191 | { | ||
| 192 | var componentSymbol = (ComponentSymbol)symbol; | ||
| 193 | var attributes = ComponentLocation.Either == componentSymbol.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0; | ||
| 194 | attributes |= ComponentLocation.SourceOnly == componentSymbol.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0; | ||
| 195 | attributes |= ComponentKeyPathType.Registry == componentSymbol.KeyPathType ? WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath : 0; | ||
| 196 | attributes |= ComponentKeyPathType.OdbcDataSource == componentSymbol.KeyPathType ? WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource : 0; | ||
| 197 | attributes |= componentSymbol.DisableRegistryReflection ? WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection : 0; | ||
| 198 | attributes |= componentSymbol.NeverOverwrite ? WindowsInstallerConstants.MsidbComponentAttributesNeverOverwrite : 0; | ||
| 199 | attributes |= componentSymbol.Permanent ? WindowsInstallerConstants.MsidbComponentAttributesPermanent : 0; | ||
| 200 | attributes |= componentSymbol.SharedDllRefCount ? WindowsInstallerConstants.MsidbComponentAttributesSharedDllRefCount : 0; | ||
| 201 | attributes |= componentSymbol.Shared ? WindowsInstallerConstants.MsidbComponentAttributesShared : 0; | ||
| 202 | attributes |= componentSymbol.Transitive ? WindowsInstallerConstants.MsidbComponentAttributesTransitive : 0; | ||
| 203 | attributes |= componentSymbol.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; | ||
| 204 | attributes |= componentSymbol.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; | ||
| 205 | |||
| 206 | fields = String.Join(",", | ||
| 207 | componentSymbol.ComponentId, | ||
| 208 | componentSymbol.DirectoryRef, | ||
| 209 | attributes.ToString(), | ||
| 210 | componentSymbol.Condition, | ||
| 211 | componentSymbol.KeyPath | ||
| 212 | ); | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | case "CustomAction": | ||
| 216 | { | ||
| 217 | var customActionSymbol = (CustomActionSymbol)symbol; | ||
| 218 | var type = customActionSymbol.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0; | ||
| 219 | type |= customActionSymbol.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0; | ||
| 220 | type |= customActionSymbol.Impersonate ? 0 : WindowsInstallerConstants.MsidbCustomActionTypeNoImpersonate; | ||
| 221 | type |= customActionSymbol.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0; | ||
| 222 | type |= customActionSymbol.Hidden ? WindowsInstallerConstants.MsidbCustomActionTypeHideTarget : 0; | ||
| 223 | type |= customActionSymbol.Async ? WindowsInstallerConstants.MsidbCustomActionTypeAsync : 0; | ||
| 224 | type |= CustomActionExecutionType.FirstSequence == customActionSymbol.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeFirstSequence : 0; | ||
| 225 | type |= CustomActionExecutionType.OncePerProcess == customActionSymbol.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeOncePerProcess : 0; | ||
| 226 | type |= CustomActionExecutionType.ClientRepeat == customActionSymbol.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeClientRepeat : 0; | ||
| 227 | type |= CustomActionExecutionType.Deferred == customActionSymbol.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript : 0; | ||
| 228 | type |= CustomActionExecutionType.Rollback == customActionSymbol.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeRollback : 0; | ||
| 229 | type |= CustomActionExecutionType.Commit == customActionSymbol.ExecutionType ? WindowsInstallerConstants.MsidbCustomActionTypeInScript | WindowsInstallerConstants.MsidbCustomActionTypeCommit : 0; | ||
| 230 | type |= CustomActionSourceType.File == customActionSymbol.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeSourceFile : 0; | ||
| 231 | type |= CustomActionSourceType.Directory == customActionSymbol.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeDirectory : 0; | ||
| 232 | type |= CustomActionSourceType.Property == customActionSymbol.SourceType ? WindowsInstallerConstants.MsidbCustomActionTypeProperty : 0; | ||
| 233 | type |= CustomActionTargetType.Dll == customActionSymbol.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeDll : 0; | ||
| 234 | type |= CustomActionTargetType.Exe == customActionSymbol.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeExe : 0; | ||
| 235 | type |= CustomActionTargetType.TextData == customActionSymbol.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeTextData : 0; | ||
| 236 | type |= CustomActionTargetType.JScript == customActionSymbol.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeJScript : 0; | ||
| 237 | type |= CustomActionTargetType.VBScript == customActionSymbol.TargetType ? WindowsInstallerConstants.MsidbCustomActionTypeVBScript : 0; | ||
| 238 | |||
| 239 | fields = String.Join(",", | ||
| 240 | type.ToString(), | ||
| 241 | customActionSymbol.Source, | ||
| 242 | customActionSymbol.Target, | ||
| 243 | customActionSymbol.PatchUninstall ? WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall.ToString() : null | ||
| 244 | ); | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | case "Directory": | ||
| 248 | { | ||
| 249 | var directorySymbol = (DirectorySymbol)symbol; | ||
| 250 | |||
| 251 | if (!String.IsNullOrEmpty(directorySymbol.ComponentGuidGenerationSeed)) | ||
| 252 | { | ||
| 253 | yield return $"WixDirectory:{directorySymbol.Id.Id},{directorySymbol.ComponentGuidGenerationSeed}"; | ||
| 254 | } | ||
| 255 | |||
| 256 | fields = String.Join(",", directorySymbol.ParentDirectoryRef, directorySymbol.Name, directorySymbol.ShortName, directorySymbol.SourceName, directorySymbol.SourceShortName); | ||
| 257 | break; | ||
| 258 | } | ||
| 259 | case "Feature": | ||
| 260 | { | ||
| 261 | var featureSymbol = (FeatureSymbol)symbol; | ||
| 262 | var attributes = featureSymbol.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0; | ||
| 263 | attributes |= featureSymbol.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0; | ||
| 264 | attributes |= FeatureInstallDefault.FollowParent == featureSymbol.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFollowParent : 0; | ||
| 265 | attributes |= FeatureInstallDefault.Source == featureSymbol.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0; | ||
| 266 | attributes |= FeatureTypicalDefault.Advertise == featureSymbol.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0; | ||
| 267 | |||
| 268 | fields = String.Join(",", | ||
| 269 | featureSymbol.ParentFeatureRef, | ||
| 270 | featureSymbol.Title, | ||
| 271 | featureSymbol.Description, | ||
| 272 | featureSymbol.Display.ToString(), | ||
| 273 | featureSymbol.Level.ToString(), | ||
| 274 | featureSymbol.DirectoryRef, | ||
| 275 | attributes.ToString()); | ||
| 276 | break; | ||
| 277 | } | ||
| 278 | case "File": | ||
| 279 | { | ||
| 280 | var fileSymbol = (FileSymbol)symbol; | ||
| 281 | |||
| 282 | if (fileSymbol.BindPath != null) | ||
| 283 | { | ||
| 284 | yield return $"BindImage:{fileSymbol.Id.Id},{fileSymbol.BindPath}"; | ||
| 285 | } | ||
| 286 | |||
| 287 | if (fileSymbol.FontTitle != null) | ||
| 288 | { | ||
| 289 | yield return $"Font:{fileSymbol.Id.Id},{fileSymbol.FontTitle}"; | ||
| 290 | } | ||
| 291 | |||
| 292 | if (fileSymbol.SelfRegCost.HasValue) | ||
| 293 | { | ||
| 294 | yield return $"SelfReg:{fileSymbol.Id.Id},{fileSymbol.SelfRegCost}"; | ||
| 295 | } | ||
| 296 | |||
| 297 | int? assemblyAttributes = null; | ||
| 298 | if (assemblySymbolsByFileId.TryGetValue(fileSymbol.Id.Id, out var assemblySymbol)) | ||
| 299 | { | ||
| 300 | if (assemblySymbol.Type == AssemblyType.DotNetAssembly) | ||
| 301 | { | ||
| 302 | assemblyAttributes = 0; | ||
| 303 | } | ||
| 304 | else if (assemblySymbol.Type == AssemblyType.Win32Assembly) | ||
| 305 | { | ||
| 306 | assemblyAttributes = 1; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | yield return "WixFile:" + String.Join(",", | ||
| 311 | fileSymbol.Id.Id, | ||
| 312 | assemblyAttributes, | ||
| 313 | assemblySymbol?.ManifestFileRef, | ||
| 314 | assemblySymbol?.ApplicationFileRef, | ||
| 315 | fileSymbol.DirectoryRef, | ||
| 316 | fileSymbol.DiskId, | ||
| 317 | fileSymbol.Source.Path, | ||
| 318 | null, // assembly processor arch | ||
| 319 | fileSymbol.PatchGroup, | ||
| 320 | 0, | ||
| 321 | (int)fileSymbol.PatchAttributes, | ||
| 322 | fileSymbol.RetainLengths, | ||
| 323 | fileSymbol.IgnoreOffsets, | ||
| 324 | fileSymbol.IgnoreLengths, | ||
| 325 | fileSymbol.RetainOffsets | ||
| 326 | ); | ||
| 327 | |||
| 328 | var fileAttributes = 0; | ||
| 329 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.ReadOnly) != 0 ? WindowsInstallerConstants.MsidbFileAttributesReadOnly : 0; | ||
| 330 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.Hidden) != 0 ? WindowsInstallerConstants.MsidbFileAttributesHidden : 0; | ||
| 331 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.System) != 0 ? WindowsInstallerConstants.MsidbFileAttributesSystem : 0; | ||
| 332 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.Vital) != 0 ? WindowsInstallerConstants.MsidbFileAttributesVital : 0; | ||
| 333 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.Checksum) != 0 ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; | ||
| 334 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.Compressed) != 0 ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; | ||
| 335 | fileAttributes |= (fileSymbol.Attributes & FileSymbolAttributes.Uncompressed) != 0 ? WindowsInstallerConstants.MsidbFileAttributesNoncompressed : 0; | ||
| 336 | |||
| 337 | fields = String.Join(",", | ||
| 338 | fileSymbol.ComponentRef, | ||
| 339 | fileSymbol.Name, | ||
| 340 | fileSymbol.FileSize.ToString(), | ||
| 341 | fileSymbol.Version, | ||
| 342 | fileSymbol.Language, | ||
| 343 | fileAttributes); | ||
| 344 | break; | ||
| 345 | } | ||
| 346 | |||
| 347 | case "Media": | ||
| 348 | fields = String.Join(",", symbol.Fields.Skip(1).Select(SafeConvertField)); | ||
| 349 | break; | ||
| 350 | |||
| 351 | case "Assembly": | ||
| 352 | { | ||
| 353 | var assemblySymbol = (AssemblySymbol)symbol; | ||
| 354 | |||
| 355 | id = null; | ||
| 356 | name = "MsiAssembly"; | ||
| 357 | fields = String.Join(",", assemblySymbol.ComponentRef, assemblySymbol.FeatureRef, assemblySymbol.ManifestFileRef, assemblySymbol.ApplicationFileRef, assemblySymbol.Type == AssemblyType.Win32Assembly ? 1 : 0); | ||
| 358 | break; | ||
| 359 | } | ||
| 360 | case "RegLocator": | ||
| 361 | { | ||
| 362 | var locatorSymbol = (RegLocatorSymbol)symbol; | ||
| 363 | |||
| 364 | fields = String.Join(",", (int)locatorSymbol.Root, locatorSymbol.Key, locatorSymbol.Name, (int)locatorSymbol.Type, locatorSymbol.Win64); | ||
| 365 | break; | ||
| 366 | } | ||
| 367 | case "Registry": | ||
| 368 | { | ||
| 369 | var registrySymbol = (RegistrySymbol)symbol; | ||
| 370 | var value = registrySymbol.Value; | ||
| 371 | |||
| 372 | switch (registrySymbol.ValueType) | ||
| 373 | { | ||
| 374 | case RegistryValueType.Binary: | ||
| 375 | value = String.Concat("#x", value); | ||
| 376 | break; | ||
| 377 | case RegistryValueType.Expandable: | ||
| 378 | value = String.Concat("#%", value); | ||
| 379 | break; | ||
| 380 | case RegistryValueType.Integer: | ||
| 381 | value = String.Concat("#", value); | ||
| 382 | break; | ||
| 383 | case RegistryValueType.MultiString: | ||
| 384 | switch (registrySymbol.ValueAction) | ||
| 385 | { | ||
| 386 | case RegistryValueActionType.Append: | ||
| 387 | value = String.Concat("[~]", value); | ||
| 388 | break; | ||
| 389 | case RegistryValueActionType.Prepend: | ||
| 390 | value = String.Concat(value, "[~]"); | ||
| 391 | break; | ||
| 392 | case RegistryValueActionType.Write: | ||
| 393 | default: | ||
| 394 | if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal)) | ||
| 395 | { | ||
| 396 | value = String.Concat("[~]", value, "[~]"); | ||
| 397 | } | ||
| 398 | break; | ||
| 399 | } | ||
| 400 | break; | ||
| 401 | case RegistryValueType.String: | ||
| 402 | // escape the leading '#' character for string registry keys | ||
| 403 | if (null != value && value.StartsWith("#", StringComparison.Ordinal)) | ||
| 404 | { | ||
| 405 | value = String.Concat("#", value); | ||
| 406 | } | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | |||
| 410 | fields = String.Join(",", | ||
| 411 | ((int)registrySymbol.Root).ToString(), | ||
| 412 | registrySymbol.Key, | ||
| 413 | registrySymbol.Name, | ||
| 414 | value, | ||
| 415 | registrySymbol.ComponentRef | ||
| 416 | ); | ||
| 417 | break; | ||
| 418 | } | ||
| 419 | |||
| 420 | case "RemoveRegistry": | ||
| 421 | { | ||
| 422 | var removeRegistrySymbol = (RemoveRegistrySymbol)symbol; | ||
| 423 | fields = String.Join(",", | ||
| 424 | ((int)removeRegistrySymbol.Root).ToString(), | ||
| 425 | removeRegistrySymbol.Key, | ||
| 426 | removeRegistrySymbol.Name, | ||
| 427 | removeRegistrySymbol.ComponentRef | ||
| 428 | ); | ||
| 429 | break; | ||
| 430 | } | ||
| 431 | |||
| 432 | case "ServiceControl": | ||
| 433 | { | ||
| 434 | var serviceControlSymbol = (ServiceControlSymbol)symbol; | ||
| 435 | |||
| 436 | var events = serviceControlSymbol.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0; | ||
| 437 | events |= serviceControlSymbol.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0; | ||
| 438 | events |= serviceControlSymbol.InstallStart ? WindowsInstallerConstants.MsidbServiceControlEventStart : 0; | ||
| 439 | events |= serviceControlSymbol.UninstallStart ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStart : 0; | ||
| 440 | events |= serviceControlSymbol.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0; | ||
| 441 | events |= serviceControlSymbol.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0; | ||
| 442 | |||
| 443 | fields = String.Join(",", | ||
| 444 | serviceControlSymbol.Name, | ||
| 445 | events.ToString(), | ||
| 446 | serviceControlSymbol.Arguments, | ||
| 447 | serviceControlSymbol.Wait == true ? "1" : "0", | ||
| 448 | serviceControlSymbol.ComponentRef | ||
| 449 | ); | ||
| 450 | break; | ||
| 451 | } | ||
| 452 | |||
| 453 | case "ServiceInstall": | ||
| 454 | { | ||
| 455 | var serviceInstallSymbol = (ServiceInstallSymbol)symbol; | ||
| 456 | |||
| 457 | var errorControl = (int)serviceInstallSymbol.ErrorControl; | ||
| 458 | errorControl |= serviceInstallSymbol.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0; | ||
| 459 | |||
| 460 | var serviceType = (int)serviceInstallSymbol.ServiceType; | ||
| 461 | serviceType |= serviceInstallSymbol.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0; | ||
| 462 | |||
| 463 | fields = String.Join(",", | ||
| 464 | serviceInstallSymbol.Name, | ||
| 465 | serviceInstallSymbol.DisplayName, | ||
| 466 | serviceType.ToString(), | ||
| 467 | ((int)serviceInstallSymbol.StartType).ToString(), | ||
| 468 | errorControl.ToString(), | ||
| 469 | serviceInstallSymbol.LoadOrderGroup, | ||
| 470 | serviceInstallSymbol.Dependencies, | ||
| 471 | serviceInstallSymbol.StartName, | ||
| 472 | serviceInstallSymbol.Password, | ||
| 473 | serviceInstallSymbol.Arguments, | ||
| 474 | serviceInstallSymbol.ComponentRef, | ||
| 475 | serviceInstallSymbol.Description | ||
| 476 | ); | ||
| 477 | break; | ||
| 478 | } | ||
| 479 | |||
| 480 | case "Upgrade": | ||
| 481 | { | ||
| 482 | var upgradeSymbol = (UpgradeSymbol)symbol; | ||
| 483 | |||
| 484 | var attributes = upgradeSymbol.MigrateFeatures ? WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures : 0; | ||
| 485 | attributes |= upgradeSymbol.OnlyDetect ? WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect : 0; | ||
| 486 | attributes |= upgradeSymbol.IgnoreRemoveFailures ? WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure : 0; | ||
| 487 | attributes |= upgradeSymbol.VersionMinInclusive ? WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive : 0; | ||
| 488 | attributes |= upgradeSymbol.VersionMaxInclusive ? WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive : 0; | ||
| 489 | attributes |= upgradeSymbol.ExcludeLanguages ? WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive : 0; | ||
| 490 | |||
| 491 | fields = String.Join(",", | ||
| 492 | upgradeSymbol.VersionMin, | ||
| 493 | upgradeSymbol.VersionMax, | ||
| 494 | upgradeSymbol.Language, | ||
| 495 | attributes.ToString(), | ||
| 496 | upgradeSymbol.Remove, | ||
| 497 | upgradeSymbol.ActionProperty | ||
| 498 | ); | ||
| 499 | break; | ||
| 500 | } | ||
| 501 | |||
| 502 | case "WixAction": | ||
| 503 | { | ||
| 504 | var wixActionSymbol = (WixActionSymbol)symbol; | ||
| 505 | var data = wixActionSymbol.Fields[(int)WixActionSymbolFields.SequenceTable].AsObject(); | ||
| 506 | var sequenceTableAsInt = data is string ? (int)SequenceStringToSequenceTable(data) : (int)wixActionSymbol.SequenceTable; | ||
| 507 | |||
| 508 | fields = String.Join(",", | ||
| 509 | sequenceTableAsInt, | ||
| 510 | wixActionSymbol.Action, | ||
| 511 | wixActionSymbol.Condition, | ||
| 512 | wixActionSymbol.Sequence?.ToString() ?? String.Empty, | ||
| 513 | wixActionSymbol.Before, | ||
| 514 | wixActionSymbol.After, | ||
| 515 | wixActionSymbol.Overridable == true ? "1" : "0" | ||
| 516 | ); | ||
| 517 | break; | ||
| 518 | } | ||
| 519 | |||
| 520 | case "WixComplexReference": | ||
| 521 | { | ||
| 522 | var wixComplexReferenceSymbol = (WixComplexReferenceSymbol)symbol; | ||
| 523 | fields = String.Join(",", | ||
| 524 | wixComplexReferenceSymbol.Parent, | ||
| 525 | (int)wixComplexReferenceSymbol.ParentType, | ||
| 526 | wixComplexReferenceSymbol.ParentLanguage, | ||
| 527 | wixComplexReferenceSymbol.Child, | ||
| 528 | (int)wixComplexReferenceSymbol.ChildType, | ||
| 529 | wixComplexReferenceSymbol.IsPrimary ? "1" : "0" | ||
| 530 | ); | ||
| 531 | break; | ||
| 532 | } | ||
| 533 | |||
| 534 | case "WixProperty": | ||
| 535 | { | ||
| 536 | var wixPropertySymbol = (WixPropertySymbol)symbol; | ||
| 537 | var attributes = wixPropertySymbol.Admin ? 0x1 : 0; | ||
| 538 | attributes |= wixPropertySymbol.Hidden ? 0x2 : 0; | ||
| 539 | attributes |= wixPropertySymbol.Secure ? 0x4 : 0; | ||
| 540 | |||
| 541 | fields = String.Join(",", | ||
| 542 | wixPropertySymbol.PropertyRef, | ||
| 543 | attributes.ToString() | ||
| 544 | ); | ||
| 545 | break; | ||
| 546 | } | ||
| 547 | |||
| 548 | default: | ||
| 549 | fields = String.Join(",", symbol.Fields.Select(SafeConvertField)); | ||
| 550 | break; | ||
| 551 | } | ||
| 552 | |||
| 553 | fields = String.IsNullOrEmpty(id) ? fields : String.IsNullOrEmpty(fields) ? id : $"{id},{fields}"; | ||
| 554 | yield return $"{name}:{fields}"; | ||
| 555 | } | ||
| 556 | |||
| 557 | private static SequenceTable SequenceStringToSequenceTable(object sequenceString) | ||
| 558 | { | ||
| 559 | switch (sequenceString) | ||
| 560 | { | ||
| 561 | case "AdminExecuteSequence": | ||
| 562 | return SequenceTable.AdminExecuteSequence; | ||
| 563 | case "AdminUISequence": | ||
| 564 | return SequenceTable.AdminUISequence; | ||
| 565 | case "AdvtExecuteSequence": | ||
| 566 | return SequenceTable.AdvertiseExecuteSequence; | ||
| 567 | case "InstallExecuteSequence": | ||
| 568 | return SequenceTable.InstallExecuteSequence; | ||
| 569 | case "InstallUISequence": | ||
| 570 | return SequenceTable.InstallUISequence; | ||
| 571 | default: | ||
| 572 | throw new ArgumentException($"Unknown sequence: {sequenceString}"); | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | private static string SafeConvertField(Wix3.Field field) | ||
| 577 | { | ||
| 578 | return field?.Data?.ToString(); | ||
| 579 | } | ||
| 580 | |||
| 581 | private static string SafeConvertField(IntermediateField field) | ||
| 582 | { | ||
| 583 | var data = field.AsObject(); | ||
| 584 | if (data is IntermediateFieldPathValue path) | ||
| 585 | { | ||
| 586 | return path.Path; | ||
| 587 | } | ||
| 588 | |||
| 589 | return data?.ToString(); | ||
| 590 | } | ||
| 591 | |||
| 592 | private static string[] SplitDefaultDir(string defaultDir) | ||
| 593 | { | ||
| 594 | var split1 = defaultDir.Split(':'); | ||
| 595 | var targetSplit = split1.Length > 1 ? split1[1].Split('|') : split1[0].Split('|'); | ||
| 596 | var sourceSplit = split1.Length > 1 ? split1[0].Split('|') : new[] { String.Empty }; | ||
| 597 | return new[] | ||
| 598 | { | ||
| 599 | targetSplit.Length > 1 ? targetSplit[1] : targetSplit[0], | ||
| 600 | targetSplit.Length > 1 ? targetSplit[0] : String.Empty, | ||
| 601 | sourceSplit.Length > 1 ? sourceSplit[1] : sourceSplit[0], | ||
| 602 | sourceSplit.Length > 1 ? sourceSplit[0] : String.Empty | ||
| 603 | }; | ||
| 604 | } | ||
| 605 | } | ||
| 606 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wixout b/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wixout new file mode 100644 index 00000000..da64b8af --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wixout | |||
| Binary files differ | |||
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wixproj b/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wixproj new file mode 100644 index 00000000..d7c4e625 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wixproj | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| 3 | <PropertyGroup> | ||
| 4 | <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
| 5 | <Platform Condition=" '$(Platform)' == '' ">x86</Platform> | ||
| 6 | <ProductVersion>3.10</ProductVersion> | ||
| 7 | <ProjectGuid>d59f1c1e-9238-49fa-bfa2-ec1d9c2dda1d</ProjectGuid> | ||
| 8 | <SchemaVersion>2.0</SchemaVersion> | ||
| 9 | <OutputName>SymbolizerWixout</OutputName> | ||
| 10 | <OutputType>Package</OutputType> | ||
| 11 | </PropertyGroup> | ||
| 12 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> | ||
| 13 | <OutputPath>bin\$(Configuration)\</OutputPath> | ||
| 14 | <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath> | ||
| 15 | <DefineConstants>Debug</DefineConstants> | ||
| 16 | </PropertyGroup> | ||
| 17 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> | ||
| 18 | <OutputPath>bin\$(Configuration)\</OutputPath> | ||
| 19 | <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath> | ||
| 20 | </PropertyGroup> | ||
| 21 | <ItemGroup> | ||
| 22 | <Compile Include="Product.wxs" /> | ||
| 23 | </ItemGroup> | ||
| 24 | <ItemGroup> | ||
| 25 | <WixExtension Include="WixUtilExtension"> | ||
| 26 | <HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath> | ||
| 27 | <Name>WixUtilExtension</Name> | ||
| 28 | </WixExtension> | ||
| 29 | <WixExtension Include="WixNetFxExtension"> | ||
| 30 | <HintPath>$(WixExtDir)\WixNetFxExtension.dll</HintPath> | ||
| 31 | <Name>WixNetFxExtension</Name> | ||
| 32 | </WixExtension> | ||
| 33 | </ItemGroup> | ||
| 34 | <Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " /> | ||
| 35 | <Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " /> | ||
| 36 | <Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' "> | ||
| 37 | <Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" /> | ||
| 38 | </Target> | ||
| 39 | <!-- | ||
| 40 | To modify your build process, add your task inside one of the targets below and uncomment it. | ||
| 41 | Other similar extension points exist, see Wix.targets. | ||
| 42 | <Target Name="BeforeBuild"> | ||
| 43 | </Target> | ||
| 44 | <Target Name="AfterBuild"> | ||
| 45 | </Target> | ||
| 46 | --> | ||
| 47 | </Project> \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wxs b/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wxs new file mode 100644 index 00000000..46d4fb43 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/TestData/Integration/test.wxs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | ||
| 3 | <Product Id="*" Name="SymbolizerWixout" Language="1033" Version="1.0.0.0" Manufacturer="FireGiant" UpgradeCode="14a02d7f-9b32-4c92-b1f1-da518bf4e32a"> | ||
| 4 | <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> | ||
| 5 | |||
| 6 | <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." AllowSameVersionUpgrades="yes" IgnoreRemoveFailure="yes" /> | ||
| 7 | <MediaTemplate /> | ||
| 8 | |||
| 9 | <Feature Id="ProductFeature" Title="SymbolizerWixout" Level="1"> | ||
| 10 | <Feature Id="ChildFeature"> | ||
| 11 | <ComponentGroupRef Id="ProductComponents" /> | ||
| 12 | </Feature> | ||
| 13 | </Feature> | ||
| 14 | |||
| 15 | <PropertyRef Id="WIX_IS_NETFRAMEWORK_462_OR_LATER_INSTALLED" /> | ||
| 16 | <CustomActionRef Id="WixFailWhenDeferred" /> | ||
| 17 | <Property Id="WIXFAILWHENDEFERRED" Value="1" Secure="yes" /> | ||
| 18 | </Product> | ||
| 19 | |||
| 20 | <Fragment> | ||
| 21 | <Directory Id="TARGETDIR" Name="SourceDir"> | ||
| 22 | <Directory Id="ProgramFilesFolder"> | ||
| 23 | <Directory Id="INSTALLFOLDER" Name="SymbolizerWixout" /> | ||
| 24 | </Directory> | ||
| 25 | </Directory> | ||
| 26 | </Fragment> | ||
| 27 | |||
| 28 | <Fragment> | ||
| 29 | <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> | ||
| 30 | <Component Id="ProductComponent"> | ||
| 31 | <File Checksum="yes" ReadOnly="yes" Source="$(env.WIX)\bin\candle.exe" Assembly=".net" AssemblyApplication="candle.exe" /> | ||
| 32 | <RegistryValue Root="HKLM" Key="SOFTWARE\WiX Toolset" Name="[ProductName]Installed" Value="1" Type="integer" /> | ||
| 33 | </Component> | ||
| 34 | </ComponentGroup> | ||
| 35 | </Fragment> | ||
| 36 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.csproj b/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.csproj new file mode 100644 index 00000000..995d9297 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.csproj | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | |||
| 6 | <PropertyGroup> | ||
| 7 | <TargetFramework>net461</TargetFramework> | ||
| 8 | <IsPackable>false</IsPackable> | ||
| 9 | </PropertyGroup> | ||
| 10 | |||
| 11 | <ItemGroup> | ||
| 12 | <Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" /> | ||
| 13 | </ItemGroup> | ||
| 14 | |||
| 15 | <ItemGroup> | ||
| 16 | <ProjectReference Include="..\..\WixToolset.Converters.Symbolizer\WixToolset.Converters.Symbolizer.csproj" /> | ||
| 17 | </ItemGroup> | ||
| 18 | |||
| 19 | <ItemGroup> | ||
| 20 | <PackageReference Include="WixToolset.Data" Version="4.0.*" /> | ||
| 21 | <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" /> | ||
| 22 | </ItemGroup> | ||
| 23 | |||
| 24 | <ItemGroup> | ||
| 25 | <Reference Include="wix" HintPath="..\..\deps\wix.dll" /> | ||
| 26 | </ItemGroup> | ||
| 27 | |||
| 28 | <ItemGroup> | ||
| 29 | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> | ||
| 30 | <PackageReference Include="xunit" Version="2.4.1" /> | ||
| 31 | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" PrivateAssets="All" /> | ||
| 32 | </ItemGroup> | ||
| 33 | </Project> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject b/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject new file mode 100644 index 00000000..18ab4f79 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters.Symbolizer/WixToolsetTest.Converters.Symbolizer.v3.ncrunchproject | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <ProjectConfiguration> | ||
| 2 | <Settings> | ||
| 3 | <IgnoredTests> | ||
| 4 | <NamedTestSelector> | ||
| 5 | <TestName>WixToolsetTest.Converters.Symbolizer.ConvertSymbolsFixture.CanLoadWixoutAndConvertToIntermediate</TestName> | ||
| 6 | </NamedTestSelector> | ||
| 7 | </IgnoredTests> | ||
| 8 | </Settings> | ||
| 9 | </ProjectConfiguration> \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/BaseConverterFixture.cs b/src/wix/test/WixToolsetTest.Converters/BaseConverterFixture.cs new file mode 100644 index 00000000..2421d73b --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/BaseConverterFixture.cs | |||
| @@ -0,0 +1,33 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Text; | ||
| 8 | using System.Xml; | ||
| 9 | using System.Xml.Linq; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public abstract class BaseConverterFixture | ||
| 13 | { | ||
| 14 | protected static string UnformattedDocumentString(XDocument document, bool omitXmlDeclaration = true) | ||
| 15 | { | ||
| 16 | var sb = new StringBuilder(); | ||
| 17 | |||
| 18 | using (var writer = new StringWriter(sb)) | ||
| 19 | using (var xml = XmlWriter.Create(writer, new XmlWriterSettings { OmitXmlDeclaration = omitXmlDeclaration })) | ||
| 20 | { | ||
| 21 | document.Save(xml); | ||
| 22 | } | ||
| 23 | |||
| 24 | return sb.ToString().TrimStart(); | ||
| 25 | } | ||
| 26 | |||
| 27 | protected static string[] UnformattedDocumentLines(XDocument document, bool omitXmlDeclaration = true) | ||
| 28 | { | ||
| 29 | var unformatted = UnformattedDocumentString(document, omitXmlDeclaration); | ||
| 30 | return unformatted.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/BitnessFixture.cs b/src/wix/test/WixToolsetTest.Converters/BitnessFixture.cs new file mode 100644 index 00000000..e45a9388 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/BitnessFixture.cs | |||
| @@ -0,0 +1,187 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class BitnessFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixComponentBitness() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <Component>", | ||
| 22 | " <File Source='default.exe' />", | ||
| 23 | " </Component>", | ||
| 24 | " <Component Win64='no'>", | ||
| 25 | " <File Source='32bit.exe' />", | ||
| 26 | " </Component>", | ||
| 27 | " <Component Win64='yes'>", | ||
| 28 | " <File Source='64bit.exe' />", | ||
| 29 | " </Component>", | ||
| 30 | " <Component Win64='$(var.Win64)'>", | ||
| 31 | " <File Source='unconvert.exe' />", | ||
| 32 | " </Component>", | ||
| 33 | " </Fragment>", | ||
| 34 | "</Wix>"); | ||
| 35 | |||
| 36 | var expected = new[] | ||
| 37 | { | ||
| 38 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 39 | " <Fragment>", | ||
| 40 | " <Component>", | ||
| 41 | " <File Id=\"default.exe\" Source=\"default.exe\" />", | ||
| 42 | " </Component>", | ||
| 43 | " <Component Bitness=\"always32\">", | ||
| 44 | " <File Id=\"_32bit.exe\" Source=\"32bit.exe\" />", | ||
| 45 | " </Component>", | ||
| 46 | " <Component Bitness=\"always64\">", | ||
| 47 | " <File Id=\"_64bit.exe\" Source=\"64bit.exe\" />", | ||
| 48 | " </Component>", | ||
| 49 | " <Component Bitness=\"$(var.Win64)\">", | ||
| 50 | " <File Id=\"unconvert.exe\" Source=\"unconvert.exe\" />", | ||
| 51 | " </Component>", | ||
| 52 | " </Fragment>", | ||
| 53 | "</Wix>" | ||
| 54 | }; | ||
| 55 | |||
| 56 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 57 | |||
| 58 | var messaging = new MockMessaging(); | ||
| 59 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 60 | |||
| 61 | var errors = converter.ConvertDocument(document); | ||
| 62 | Assert.Equal(10, errors); | ||
| 63 | |||
| 64 | var actualLines = UnformattedDocumentLines(document); | ||
| 65 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 66 | } | ||
| 67 | |||
| 68 | [Fact] | ||
| 69 | public void FixRegistrySearchBitness() | ||
| 70 | { | ||
| 71 | var parse = String.Join(Environment.NewLine, | ||
| 72 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 73 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 74 | " <Fragment>", | ||
| 75 | " <Property Id='BITNESSDEFAULT'>", | ||
| 76 | " <RegistrySearch Id='SampleRegSearch' Root='HKLM' Key='SampleReg' Type='raw'></RegistrySearch>", | ||
| 77 | " </Property>", | ||
| 78 | " <Property Id='BITNESS32'>", | ||
| 79 | " <RegistrySearch Id='SampleRegSearch' Root='HKLM' Key='SampleReg' Type='raw' Win64='no'></RegistrySearch>", | ||
| 80 | " </Property>", | ||
| 81 | " <Property Id='BITNESS64'>", | ||
| 82 | " <RegistrySearch Id='SampleRegSearch' Root='HKLM' Key='SampleReg' Type='raw' Win64='yes'></RegistrySearch>", | ||
| 83 | " </Property>", | ||
| 84 | " </Fragment>", | ||
| 85 | "</Wix>"); | ||
| 86 | |||
| 87 | var expected = new[] | ||
| 88 | { | ||
| 89 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 90 | " <Fragment>", | ||
| 91 | " <Property Id=\"BITNESSDEFAULT\">", | ||
| 92 | " <RegistrySearch Id=\"SampleRegSearch\" Root=\"HKLM\" Key=\"SampleReg\" Type=\"raw\"></RegistrySearch>", | ||
| 93 | " </Property>", | ||
| 94 | " <Property Id=\"BITNESS32\">", | ||
| 95 | " <RegistrySearch Id=\"SampleRegSearch\" Root=\"HKLM\" Key=\"SampleReg\" Type=\"raw\" Bitness=\"always32\"></RegistrySearch>", | ||
| 96 | " </Property>", | ||
| 97 | " <Property Id=\"BITNESS64\">", | ||
| 98 | " <RegistrySearch Id=\"SampleRegSearch\" Root=\"HKLM\" Key=\"SampleReg\" Type=\"raw\" Bitness=\"always64\"></RegistrySearch>", | ||
| 99 | " </Property>", | ||
| 100 | " </Fragment>", | ||
| 101 | "</Wix>" | ||
| 102 | }; | ||
| 103 | |||
| 104 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 105 | |||
| 106 | var messaging = new MockMessaging(); | ||
| 107 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 108 | |||
| 109 | var errors = converter.ConvertDocument(document); | ||
| 110 | Assert.Equal(4, errors); | ||
| 111 | |||
| 112 | var actualLines = UnformattedDocumentLines(document); | ||
| 113 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 114 | } | ||
| 115 | |||
| 116 | [Fact] | ||
| 117 | public void FixUtilRegistrySearchBitness() | ||
| 118 | { | ||
| 119 | var parse = String.Join(Environment.NewLine, | ||
| 120 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 121 | " <Fragment>", | ||
| 122 | " <util:RegistrySearch Id='RegValue' Root='HKLM' Key='Converter' Variable='Test' />", | ||
| 123 | " <util:RegistrySearch Id='RegValue2' Root='HKLM' Key='Converter' Variable='Test' Result='value' Win64='no' />", | ||
| 124 | " <util:RegistrySearch Id='RegValue3' Root='HKLM' Key='Converter' Variable='Test' Result='exists' Win64='yes' />", | ||
| 125 | " </Fragment>", | ||
| 126 | "</Wix>"); | ||
| 127 | |||
| 128 | var expected = new[] | ||
| 129 | { | ||
| 130 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 131 | " <Fragment>", | ||
| 132 | " <util:RegistrySearch Id=\"RegValue\" Root=\"HKLM\" Key=\"Converter\" Variable=\"Test\" />", | ||
| 133 | " <util:RegistrySearch Id=\"RegValue2\" Root=\"HKLM\" Key=\"Converter\" Variable=\"Test\" Result=\"value\" Bitness=\"always32\" />", | ||
| 134 | " <util:RegistrySearch Id=\"RegValue3\" Root=\"HKLM\" Key=\"Converter\" Variable=\"Test\" Result=\"exists\" Bitness=\"always64\" />", | ||
| 135 | " </Fragment>", | ||
| 136 | "</Wix>" | ||
| 137 | }; | ||
| 138 | |||
| 139 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 140 | |||
| 141 | var messaging = new MockMessaging(); | ||
| 142 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 143 | |||
| 144 | var errors = converter.ConvertDocument(document); | ||
| 145 | Assert.Equal(6, errors); | ||
| 146 | |||
| 147 | var actualLines = UnformattedDocumentLines(document); | ||
| 148 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 149 | } | ||
| 150 | |||
| 151 | [Fact] | ||
| 152 | public void FixApprovedExeBitness() | ||
| 153 | { | ||
| 154 | var parse = String.Join(Environment.NewLine, | ||
| 155 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 156 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 157 | " <Bundle>", | ||
| 158 | " <ApprovedExeForElevation Id='Default' Key='WixToolset\\BurnTesting' Value='Test' />", | ||
| 159 | " <ApprovedExeForElevation Id='Bitness32' Key='WixToolset\\BurnTesting' Value='Test' Win64='no' />", | ||
| 160 | " <ApprovedExeForElevation Id='Bitness64' Key='WixToolset\\BurnTesting' Value='Test' Win64='yes' />", | ||
| 161 | " </Bundle>", | ||
| 162 | "</Wix>"); | ||
| 163 | |||
| 164 | var expected = new[] | ||
| 165 | { | ||
| 166 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 167 | " <Bundle>", | ||
| 168 | " <ApprovedExeForElevation Id=\"Default\" Key=\"WixToolset\\BurnTesting\" Value=\"Test\" />", | ||
| 169 | " <ApprovedExeForElevation Id=\"Bitness32\" Key=\"WixToolset\\BurnTesting\" Value=\"Test\" Bitness=\"always32\" />", | ||
| 170 | " <ApprovedExeForElevation Id=\"Bitness64\" Key=\"WixToolset\\BurnTesting\" Value=\"Test\" Bitness=\"always64\" />", | ||
| 171 | " </Bundle>", | ||
| 172 | "</Wix>" | ||
| 173 | }; | ||
| 174 | |||
| 175 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 176 | |||
| 177 | var messaging = new MockMessaging(); | ||
| 178 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 179 | |||
| 180 | var errors = converter.ConvertDocument(document); | ||
| 181 | Assert.Equal(4, errors); | ||
| 182 | |||
| 183 | var actualLines = UnformattedDocumentLines(document); | ||
| 184 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/BootstrapperApplicationFixture.cs b/src/wix/test/WixToolsetTest.Converters/BootstrapperApplicationFixture.cs new file mode 100644 index 00000000..158ab3be --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/BootstrapperApplicationFixture.cs | |||
| @@ -0,0 +1,418 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class BootstrapperApplicationFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void CantCreateBootstrapperApplicationDllFromV3PayloadGroupRef() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <BootstrapperApplication Id='ba'>", | ||
| 21 | " <PayloadGroupRef Id='baPayloads' />", | ||
| 22 | " </BootstrapperApplication>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = new[] | ||
| 27 | { | ||
| 28 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 29 | " <Fragment>", | ||
| 30 | " <BootstrapperApplication Id=\"ba\">", | ||
| 31 | " <PayloadGroupRef Id=\"baPayloads\" />", | ||
| 32 | " </BootstrapperApplication>", | ||
| 33 | " </Fragment>", | ||
| 34 | "</Wix>" | ||
| 35 | }; | ||
| 36 | |||
| 37 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 38 | |||
| 39 | var messaging = new MockMessaging(); | ||
| 40 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 41 | |||
| 42 | var errors = converter.ConvertDocument(document); | ||
| 43 | Assert.Equal(2, errors); | ||
| 44 | |||
| 45 | var actualLines = UnformattedDocumentLines(document); | ||
| 46 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 47 | } | ||
| 48 | |||
| 49 | [Fact] | ||
| 50 | public void ConvertDotNetCoreBootstrapperApplicationRefWithExistingElement() | ||
| 51 | { | ||
| 52 | var parse = String.Join(Environment.NewLine, | ||
| 53 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs' xmlns:bal='http://wixtoolset.org/schemas/v4/wxs/bal'>", | ||
| 54 | " <Fragment>", | ||
| 55 | " <BootstrapperApplicationRef Id='DotNetCoreBootstrapperApplicationHost.Minimal'>", | ||
| 56 | " <bal:WixDotNetCoreBootstrapperApplication SelfContainedDeployment='yes' />", | ||
| 57 | " </BootstrapperApplicationRef>", | ||
| 58 | " </Fragment>", | ||
| 59 | "</Wix>"); | ||
| 60 | |||
| 61 | var expected = new[] | ||
| 62 | { | ||
| 63 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", | ||
| 64 | " <Fragment>", | ||
| 65 | " <BootstrapperApplication>", | ||
| 66 | " <bal:WixDotNetCoreBootstrapperApplicationHost SelfContainedDeployment=\"yes\" Theme=\"none\" />", | ||
| 67 | " </BootstrapperApplication>", | ||
| 68 | " </Fragment>", | ||
| 69 | "</Wix>" | ||
| 70 | }; | ||
| 71 | |||
| 72 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 73 | |||
| 74 | var messaging = new MockMessaging(); | ||
| 75 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 76 | |||
| 77 | var errors = converter.ConvertDocument(document); | ||
| 78 | Assert.Equal(1, errors); | ||
| 79 | |||
| 80 | var actualLines = UnformattedDocumentLines(document); | ||
| 81 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 82 | } | ||
| 83 | |||
| 84 | [Fact] | ||
| 85 | public void ConvertDotNetCoreBootstrapperApplicationRefWithoutExistingElement() | ||
| 86 | { | ||
| 87 | var parse = String.Join(Environment.NewLine, | ||
| 88 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs' xmlns:bal='http://wixtoolset.org/schemas/v4/wxs/bal'>", | ||
| 89 | " <Fragment>", | ||
| 90 | " <BootstrapperApplicationRef Id='DotNetCoreBootstrapperApplicationHost' />", | ||
| 91 | " </Fragment>", | ||
| 92 | "</Wix>"); | ||
| 93 | |||
| 94 | var expected = new[] | ||
| 95 | { | ||
| 96 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", | ||
| 97 | " <Fragment>", | ||
| 98 | " <BootstrapperApplication>", | ||
| 99 | "<bal:WixDotNetCoreBootstrapperApplicationHost />", | ||
| 100 | "</BootstrapperApplication>", | ||
| 101 | " </Fragment>", | ||
| 102 | "</Wix>" | ||
| 103 | }; | ||
| 104 | |||
| 105 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 106 | |||
| 107 | var messaging = new MockMessaging(); | ||
| 108 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 109 | |||
| 110 | var errors = converter.ConvertDocument(document); | ||
| 111 | Assert.Equal(1, errors); | ||
| 112 | |||
| 113 | var actualLines = UnformattedDocumentLines(document); | ||
| 114 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 115 | } | ||
| 116 | |||
| 117 | [Fact] | ||
| 118 | public void ConvertFrameworkBootstrapperApplicationRefWithExistingElement() | ||
| 119 | { | ||
| 120 | var parse = String.Join(Environment.NewLine, | ||
| 121 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:bal='http://schemas.microsoft.com/wix/BalExtension'>", | ||
| 122 | " <Fragment>", | ||
| 123 | " <BootstrapperApplicationRef Id='ManagedBootstrapperApplicationHost'>", | ||
| 124 | " <bal:WixManagedBootstrapperApplicationHost LogoFile='logo.png' />", | ||
| 125 | " </BootstrapperApplicationRef>", | ||
| 126 | " </Fragment>", | ||
| 127 | "</Wix>"); | ||
| 128 | |||
| 129 | var expected = new[] | ||
| 130 | { | ||
| 131 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", | ||
| 132 | " <Fragment>", | ||
| 133 | " <BootstrapperApplication>", | ||
| 134 | " <bal:WixManagedBootstrapperApplicationHost LogoFile=\"logo.png\" />", | ||
| 135 | " </BootstrapperApplication>", | ||
| 136 | " </Fragment>", | ||
| 137 | "</Wix>" | ||
| 138 | }; | ||
| 139 | |||
| 140 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 141 | |||
| 142 | var messaging = new MockMessaging(); | ||
| 143 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 144 | |||
| 145 | var errors = converter.ConvertDocument(document); | ||
| 146 | Assert.Equal(3, errors); | ||
| 147 | |||
| 148 | var actualLines = UnformattedDocumentLines(document); | ||
| 149 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 150 | } | ||
| 151 | |||
| 152 | [Fact] | ||
| 153 | public void ConvertFrameworkBootstrapperApplicationRefWithoutExistingElement() | ||
| 154 | { | ||
| 155 | var parse = String.Join(Environment.NewLine, | ||
| 156 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:bal='http://schemas.microsoft.com/wix/BalExtension'>", | ||
| 157 | " <Fragment>", | ||
| 158 | " <BootstrapperApplicationRef Id='ManagedBootstrapperApplicationHost.RtfLicense.Minimal' />", | ||
| 159 | " </Fragment>", | ||
| 160 | "</Wix>"); | ||
| 161 | |||
| 162 | var expected = new[] | ||
| 163 | { | ||
| 164 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", | ||
| 165 | " <Fragment>", | ||
| 166 | " <BootstrapperApplication>", | ||
| 167 | "<bal:WixManagedBootstrapperApplicationHost Theme=\"none\" />", | ||
| 168 | "</BootstrapperApplication>", | ||
| 169 | " </Fragment>", | ||
| 170 | "</Wix>" | ||
| 171 | }; | ||
| 172 | |||
| 173 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 174 | |||
| 175 | var messaging = new MockMessaging(); | ||
| 176 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 177 | |||
| 178 | var errors = converter.ConvertDocument(document); | ||
| 179 | Assert.Equal(3, errors); | ||
| 180 | |||
| 181 | var actualLines = UnformattedDocumentLines(document); | ||
| 182 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 183 | } | ||
| 184 | |||
| 185 | [Fact] | ||
| 186 | public void ConvertStandardBootstrapperApplicationRefWithExistingElement() | ||
| 187 | { | ||
| 188 | var parse = String.Join(Environment.NewLine, | ||
| 189 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:bal='http://schemas.microsoft.com/wix/BalExtension'>", | ||
| 190 | " <Fragment>", | ||
| 191 | " <BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.Foundation'>", | ||
| 192 | " <bal:WixStandardBootstrapperApplication LaunchTarget='[InstallFolder]the.exe' />", | ||
| 193 | " </BootstrapperApplicationRef>", | ||
| 194 | " </Fragment>", | ||
| 195 | "</Wix>"); | ||
| 196 | |||
| 197 | var expected = new[] | ||
| 198 | { | ||
| 199 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", | ||
| 200 | " <Fragment>", | ||
| 201 | " <BootstrapperApplication>", | ||
| 202 | " <bal:WixStandardBootstrapperApplication LaunchTarget=\"[InstallFolder]the.exe\" Theme=\"none\" />", | ||
| 203 | " </BootstrapperApplication>", | ||
| 204 | " </Fragment>", | ||
| 205 | "</Wix>" | ||
| 206 | }; | ||
| 207 | |||
| 208 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 209 | |||
| 210 | var messaging = new MockMessaging(); | ||
| 211 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 212 | |||
| 213 | var errors = converter.ConvertDocument(document); | ||
| 214 | Assert.Equal(3, errors); | ||
| 215 | |||
| 216 | var actualLines = UnformattedDocumentLines(document); | ||
| 217 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 218 | } | ||
| 219 | |||
| 220 | [Fact] | ||
| 221 | public void ConvertStandardBootstrapperApplicationRefWithoutExistingElement() | ||
| 222 | { | ||
| 223 | var parse = String.Join(Environment.NewLine, | ||
| 224 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:bal='http://schemas.microsoft.com/wix/BalExtension'>", | ||
| 225 | " <Fragment>", | ||
| 226 | " <BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.RtfLicense' />", | ||
| 227 | " </Fragment>", | ||
| 228 | "</Wix>"); | ||
| 229 | |||
| 230 | var expected = new[] | ||
| 231 | { | ||
| 232 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", | ||
| 233 | " <Fragment>", | ||
| 234 | " <BootstrapperApplication>", | ||
| 235 | "<bal:WixStandardBootstrapperApplication Theme=\"rtfLicense\" />", | ||
| 236 | "</BootstrapperApplication>", | ||
| 237 | " </Fragment>", | ||
| 238 | "</Wix>" | ||
| 239 | }; | ||
| 240 | |||
| 241 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 242 | |||
| 243 | var messaging = new MockMessaging(); | ||
| 244 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 245 | |||
| 246 | var errors = converter.ConvertDocument(document); | ||
| 247 | Assert.Equal(3, errors); | ||
| 248 | |||
| 249 | var actualLines = UnformattedDocumentLines(document); | ||
| 250 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 251 | } | ||
| 252 | |||
| 253 | [Fact] | ||
| 254 | public void CreateBootstrapperApplicationDllFromV3() | ||
| 255 | { | ||
| 256 | var parse = String.Join(Environment.NewLine, | ||
| 257 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 258 | " <Fragment>", | ||
| 259 | " <BootstrapperApplication Id='ba' SourceFile='ba.dll' />", | ||
| 260 | " </Fragment>", | ||
| 261 | "</Wix>"); | ||
| 262 | |||
| 263 | var expected = new[] | ||
| 264 | { | ||
| 265 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 266 | " <Fragment>", | ||
| 267 | " <BootstrapperApplication Id=\"ba\">", | ||
| 268 | "<BootstrapperApplicationDll SourceFile=\"ba.dll\" DpiAwareness=\"unaware\" />", | ||
| 269 | "</BootstrapperApplication>", | ||
| 270 | " </Fragment>", | ||
| 271 | "</Wix>" | ||
| 272 | }; | ||
| 273 | |||
| 274 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 275 | |||
| 276 | var messaging = new MockMessaging(); | ||
| 277 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 278 | |||
| 279 | var errors = converter.ConvertDocument(document); | ||
| 280 | Assert.Equal(3, errors); | ||
| 281 | |||
| 282 | var actualLines = UnformattedDocumentLines(document); | ||
| 283 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 284 | } | ||
| 285 | |||
| 286 | [Fact] | ||
| 287 | public void CreateBootstrapperApplicationDllFromV3Payload() | ||
| 288 | { | ||
| 289 | var parse = String.Join(Environment.NewLine, | ||
| 290 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 291 | " <Fragment>", | ||
| 292 | " <BootstrapperApplication Id='ba'>", | ||
| 293 | " <Payload SourceFile='ba.dll' />", | ||
| 294 | " </BootstrapperApplication>", | ||
| 295 | " </Fragment>", | ||
| 296 | "</Wix>"); | ||
| 297 | |||
| 298 | var expected = new[] | ||
| 299 | { | ||
| 300 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 301 | " <Fragment>", | ||
| 302 | " <BootstrapperApplication Id=\"ba\">", | ||
| 303 | " ", | ||
| 304 | " ", | ||
| 305 | "<BootstrapperApplicationDll SourceFile=\"ba.dll\" DpiAwareness=\"unaware\" />", | ||
| 306 | "</BootstrapperApplication>", | ||
| 307 | " </Fragment>", | ||
| 308 | "</Wix>" | ||
| 309 | }; | ||
| 310 | |||
| 311 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 312 | |||
| 313 | var messaging = new MockMessaging(); | ||
| 314 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 315 | |||
| 316 | var errors = converter.ConvertDocument(document); | ||
| 317 | Assert.Equal(3, errors); | ||
| 318 | |||
| 319 | var actualLines = UnformattedDocumentLines(document); | ||
| 320 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 321 | } | ||
| 322 | |||
| 323 | [Fact] | ||
| 324 | public void DoesntSetDpiUnawareFromV4() | ||
| 325 | { | ||
| 326 | var parse = String.Join(Environment.NewLine, | ||
| 327 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 328 | " <Fragment>", | ||
| 329 | " <BootstrapperApplication Id='ba' />", | ||
| 330 | " </Fragment>", | ||
| 331 | "</Wix>"); | ||
| 332 | |||
| 333 | var expected = new[] | ||
| 334 | { | ||
| 335 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 336 | " <Fragment>", | ||
| 337 | " <BootstrapperApplication Id=\"ba\" />", | ||
| 338 | " </Fragment>", | ||
| 339 | "</Wix>" | ||
| 340 | }; | ||
| 341 | |||
| 342 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 343 | |||
| 344 | var messaging = new MockMessaging(); | ||
| 345 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 346 | |||
| 347 | var errors = converter.ConvertDocument(document); | ||
| 348 | Assert.Equal(0, errors); | ||
| 349 | |||
| 350 | var actualLines = UnformattedDocumentLines(document); | ||
| 351 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 352 | } | ||
| 353 | |||
| 354 | [Fact] | ||
| 355 | public void KeepsDpiAwarenessFromV4() | ||
| 356 | { | ||
| 357 | var parse = String.Join(Environment.NewLine, | ||
| 358 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 359 | " <Fragment>", | ||
| 360 | " <BootstrapperApplication Id='ba' SourceFile='ba.dll' DpiAwareness='system' />", | ||
| 361 | " </Fragment>", | ||
| 362 | "</Wix>"); | ||
| 363 | |||
| 364 | var expected = new[] | ||
| 365 | { | ||
| 366 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 367 | " <Fragment>", | ||
| 368 | " <BootstrapperApplication Id=\"ba\">", | ||
| 369 | "<BootstrapperApplicationDll SourceFile=\"ba.dll\" DpiAwareness=\"system\" />", | ||
| 370 | "</BootstrapperApplication>", | ||
| 371 | " </Fragment>", | ||
| 372 | "</Wix>" | ||
| 373 | }; | ||
| 374 | |||
| 375 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 376 | |||
| 377 | var messaging = new MockMessaging(); | ||
| 378 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 379 | |||
| 380 | var errors = converter.ConvertDocument(document); | ||
| 381 | Assert.Equal(1, errors); | ||
| 382 | |||
| 383 | var actualLines = UnformattedDocumentLines(document); | ||
| 384 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 385 | } | ||
| 386 | |||
| 387 | [Fact] | ||
| 388 | public void RemovesBalUseUILanguages() | ||
| 389 | { | ||
| 390 | var parse = String.Join(Environment.NewLine, | ||
| 391 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:bal='http://schemas.microsoft.com/wix/BalExtension'>", | ||
| 392 | " <Fragment>", | ||
| 393 | " <BootstrapperApplication Id='ba' bal:UseUILanguages='true' />", | ||
| 394 | " </Fragment>", | ||
| 395 | "</Wix>"); | ||
| 396 | |||
| 397 | var expected = new[] | ||
| 398 | { | ||
| 399 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 400 | " <Fragment>", | ||
| 401 | " <BootstrapperApplication Id=\"ba\" />", | ||
| 402 | " </Fragment>", | ||
| 403 | "</Wix>" | ||
| 404 | }; | ||
| 405 | |||
| 406 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 407 | |||
| 408 | var messaging = new MockMessaging(); | ||
| 409 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 410 | |||
| 411 | var errors = converter.ConvertDocument(document); | ||
| 412 | Assert.Equal(5, errors); | ||
| 413 | |||
| 414 | var actualLines = UnformattedDocumentLines(document); | ||
| 415 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs b/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs new file mode 100644 index 00000000..d3f65aeb --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs | |||
| @@ -0,0 +1,297 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class ConditionFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixControlCondition() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <UI>", | ||
| 22 | " <Dialog Id='Dlg1'>", | ||
| 23 | " <Control Id='Control1'>", | ||
| 24 | " <Condition Action='disable'>x=y</Condition>", | ||
| 25 | " <Condition Action='hide'>a<>b</Condition>", | ||
| 26 | " </Control>", | ||
| 27 | " </Dialog>", | ||
| 28 | " </UI>", | ||
| 29 | " </Fragment>", | ||
| 30 | "</Wix>"); | ||
| 31 | |||
| 32 | var expected = new[] | ||
| 33 | { | ||
| 34 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 35 | " <Fragment>", | ||
| 36 | " <UI>", | ||
| 37 | " <Dialog Id=\"Dlg1\">", | ||
| 38 | " <Control Id=\"Control1\" DisableCondition=\"x=y\" HideCondition=\"a<>b\">", | ||
| 39 | " ", | ||
| 40 | " ", | ||
| 41 | " </Control>", | ||
| 42 | " </Dialog>", | ||
| 43 | " </UI>", | ||
| 44 | " </Fragment>", | ||
| 45 | "</Wix>" | ||
| 46 | }; | ||
| 47 | |||
| 48 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 49 | |||
| 50 | var messaging = new MockMessaging(); | ||
| 51 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 52 | |||
| 53 | var errors = converter.ConvertDocument(document); | ||
| 54 | Assert.Equal(4, errors); | ||
| 55 | |||
| 56 | var actualLines = UnformattedDocumentLines(document); | ||
| 57 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 58 | } | ||
| 59 | |||
| 60 | [Fact] | ||
| 61 | public void FixPublishCondition() | ||
| 62 | { | ||
| 63 | var parse = String.Join(Environment.NewLine, | ||
| 64 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 65 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 66 | " <Fragment>", | ||
| 67 | " <UI>", | ||
| 68 | " <Dialog Id='Dlg1'>", | ||
| 69 | " <Control Id='Control1'>", | ||
| 70 | " <Publish Value='abc'>1<2</Publish>", | ||
| 71 | " <Publish Value='gone'>1</Publish>", | ||
| 72 | " </Control>", | ||
| 73 | " </Dialog>", | ||
| 74 | " </UI>", | ||
| 75 | " </Fragment>", | ||
| 76 | "</Wix>"); | ||
| 77 | |||
| 78 | var expected = new[] | ||
| 79 | { | ||
| 80 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 81 | " <Fragment>", | ||
| 82 | " <UI>", | ||
| 83 | " <Dialog Id=\"Dlg1\">", | ||
| 84 | " <Control Id=\"Control1\">", | ||
| 85 | " <Publish Value=\"abc\" Condition=\"1<2\" />", | ||
| 86 | " <Publish Value=\"gone\" />", | ||
| 87 | " </Control>", | ||
| 88 | " </Dialog>", | ||
| 89 | " </UI>", | ||
| 90 | " </Fragment>", | ||
| 91 | "</Wix>" | ||
| 92 | }; | ||
| 93 | |||
| 94 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 95 | |||
| 96 | var messaging = new MockMessaging(); | ||
| 97 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 98 | |||
| 99 | var errors = converter.ConvertDocument(document); | ||
| 100 | Assert.Equal(5, errors); | ||
| 101 | |||
| 102 | var actualLines = UnformattedDocumentLines(document); | ||
| 103 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 104 | } | ||
| 105 | |||
| 106 | [Fact] | ||
| 107 | public void FixComponentCondition() | ||
| 108 | { | ||
| 109 | var parse = String.Join(Environment.NewLine, | ||
| 110 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 111 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 112 | " <Fragment>", | ||
| 113 | " <Component Id='Comp1' Directory='ApplicationFolder'>", | ||
| 114 | " <Condition>1<2</Condition>", | ||
| 115 | " </Component>", | ||
| 116 | " </Fragment>", | ||
| 117 | "</Wix>"); | ||
| 118 | |||
| 119 | var expected = new[] | ||
| 120 | { | ||
| 121 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 122 | " <Fragment>", | ||
| 123 | " <Component Id=\"Comp1\" Directory=\"ApplicationFolder\" Condition=\"1<2\">", | ||
| 124 | " ", | ||
| 125 | " </Component>", | ||
| 126 | " </Fragment>", | ||
| 127 | "</Wix>" | ||
| 128 | }; | ||
| 129 | |||
| 130 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 131 | |||
| 132 | var messaging = new MockMessaging(); | ||
| 133 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 134 | |||
| 135 | var errors = converter.ConvertDocument(document); | ||
| 136 | Assert.Equal(3, errors); | ||
| 137 | |||
| 138 | var actualLines = UnformattedDocumentLines(document); | ||
| 139 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 140 | } | ||
| 141 | |||
| 142 | [Fact] | ||
| 143 | public void FixFeatureCondition() | ||
| 144 | { | ||
| 145 | var parse = String.Join(Environment.NewLine, | ||
| 146 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 147 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 148 | " <Fragment>", | ||
| 149 | " <Feature Id='Feature1'>", | ||
| 150 | " <Condition Level='0'>PROP = 1</Condition>", | ||
| 151 | " </Feature>", | ||
| 152 | " </Fragment>", | ||
| 153 | "</Wix>"); | ||
| 154 | |||
| 155 | var expected = new[] | ||
| 156 | { | ||
| 157 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 158 | " <Fragment>", | ||
| 159 | " <Feature Id=\"Feature1\">", | ||
| 160 | " <Level Value=\"0\" Condition=\"PROP = 1\" />", | ||
| 161 | " </Feature>", | ||
| 162 | " </Fragment>", | ||
| 163 | "</Wix>" | ||
| 164 | }; | ||
| 165 | |||
| 166 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 167 | |||
| 168 | var messaging = new MockMessaging(); | ||
| 169 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 170 | |||
| 171 | var errors = converter.ConvertDocument(document); | ||
| 172 | Assert.Equal(3, errors); | ||
| 173 | |||
| 174 | var actualLines = UnformattedDocumentLines(document); | ||
| 175 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 176 | } | ||
| 177 | |||
| 178 | [Fact] | ||
| 179 | public void FixLaunchCondition() | ||
| 180 | { | ||
| 181 | var parse = String.Join(Environment.NewLine, | ||
| 182 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 183 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 184 | " <Fragment>", | ||
| 185 | " <Condition Message='Stop the install'>", | ||
| 186 | " 1<2", | ||
| 187 | " </Condition>", | ||
| 188 | " <Condition Message='Do not stop'>", | ||
| 189 | " 1=2", | ||
| 190 | " </Condition>", | ||
| 191 | " </Fragment>", | ||
| 192 | "</Wix>"); | ||
| 193 | |||
| 194 | var expected = new[] | ||
| 195 | { | ||
| 196 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 197 | " <Fragment>", | ||
| 198 | " <Launch Condition=\"1<2\" Message=\"Stop the install\" />", | ||
| 199 | " <Launch Condition=\"1=2\" Message=\"Do not stop\" />", | ||
| 200 | " </Fragment>", | ||
| 201 | "</Wix>" | ||
| 202 | }; | ||
| 203 | |||
| 204 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 205 | |||
| 206 | var messaging = new MockMessaging(); | ||
| 207 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 208 | |||
| 209 | var errors = converter.ConvertDocument(document); | ||
| 210 | Assert.Equal(4, errors); | ||
| 211 | |||
| 212 | var actualLines = UnformattedDocumentLines(document); | ||
| 213 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 214 | } | ||
| 215 | |||
| 216 | [Fact] | ||
| 217 | public void FixLaunchConditionInProduct() | ||
| 218 | { | ||
| 219 | var parse = String.Join(Environment.NewLine, | ||
| 220 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 221 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 222 | " <Product>", | ||
| 223 | " <Package />", | ||
| 224 | " <Condition Message='Stop the install'>", | ||
| 225 | " 1<2", | ||
| 226 | " </Condition>", | ||
| 227 | " <Condition Message='Do not stop'>", | ||
| 228 | " 1=2", | ||
| 229 | " </Condition>", | ||
| 230 | " </Product>", | ||
| 231 | "</Wix>"); | ||
| 232 | |||
| 233 | var expected = new[] | ||
| 234 | { | ||
| 235 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 236 | " <Package Compressed=\"no\">", | ||
| 237 | " ", | ||
| 238 | " <Launch Condition=\"1<2\" Message=\"Stop the install\" />", | ||
| 239 | " <Launch Condition=\"1=2\" Message=\"Do not stop\" />", | ||
| 240 | " </Package>", | ||
| 241 | "</Wix>" | ||
| 242 | }; | ||
| 243 | |||
| 244 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 245 | |||
| 246 | var messaging = new MockMessaging(); | ||
| 247 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 248 | |||
| 249 | var errors = converter.ConvertDocument(document); | ||
| 250 | Assert.Equal(6, errors); | ||
| 251 | |||
| 252 | var actualLines = UnformattedDocumentLines(document); | ||
| 253 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 254 | } | ||
| 255 | |||
| 256 | [Fact] | ||
| 257 | public void FixPermissionExCondition() | ||
| 258 | { | ||
| 259 | var parse = String.Join(Environment.NewLine, | ||
| 260 | "<!-- comment -->", | ||
| 261 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 262 | " <Fragment>", | ||
| 263 | " <Component Id='Comp1' Guid='*' Directory='ApplicationFolder'>", | ||
| 264 | " <PermissionEx Sddl='sddl'>", | ||
| 265 | " <Condition>1<2</Condition>", | ||
| 266 | " </PermissionEx>", | ||
| 267 | " </Component>", | ||
| 268 | " </Fragment>", | ||
| 269 | "</Wix>"); | ||
| 270 | |||
| 271 | var expected = new[] | ||
| 272 | { | ||
| 273 | "<!-- comment -->", | ||
| 274 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 275 | " <Fragment>", | ||
| 276 | " <Component Id=\"Comp1\" Directory=\"ApplicationFolder\">", | ||
| 277 | " <PermissionEx Sddl=\"sddl\" Condition=\"1<2\">", | ||
| 278 | " ", | ||
| 279 | " </PermissionEx>", | ||
| 280 | " </Component>", | ||
| 281 | " </Fragment>", | ||
| 282 | "</Wix>" | ||
| 283 | }; | ||
| 284 | |||
| 285 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 286 | |||
| 287 | var messaging = new MockMessaging(); | ||
| 288 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 289 | |||
| 290 | var errors = converter.ConvertDocument(document); | ||
| 291 | Assert.Equal(3, errors); | ||
| 292 | |||
| 293 | var actualLines = UnformattedDocumentLines(document); | ||
| 294 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs b/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs new file mode 100644 index 00000000..13df9da7 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs | |||
| @@ -0,0 +1,449 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixToolset.Converters; | ||
| 8 | using WixToolsetTest.Converters.Mocks; | ||
| 9 | using Xunit; | ||
| 10 | |||
| 11 | public class ConverterFixture : BaseConverterFixture | ||
| 12 | { | ||
| 13 | private static readonly XNamespace Wix4Namespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
| 14 | |||
| 15 | [Fact] | ||
| 16 | public void EnsuresNoDeclaration() | ||
| 17 | { | ||
| 18 | var parse = String.Join(Environment.NewLine, | ||
| 19 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 20 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 21 | " <Fragment />", | ||
| 22 | "</Wix>"); | ||
| 23 | |||
| 24 | var expected = String.Join(Environment.NewLine, | ||
| 25 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 26 | " <Fragment />", | ||
| 27 | "</Wix>"); | ||
| 28 | |||
| 29 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 30 | |||
| 31 | var messaging = new MockMessaging(); | ||
| 32 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 33 | |||
| 34 | var errors = converter.ConvertDocument(document); | ||
| 35 | |||
| 36 | var actual = UnformattedDocumentString(document); | ||
| 37 | |||
| 38 | Assert.Equal(1, errors); | ||
| 39 | Assert.Equal(expected, actual); | ||
| 40 | } | ||
| 41 | |||
| 42 | [Fact] | ||
| 43 | public void EnsuresDeclarationWhenIgnored() | ||
| 44 | { | ||
| 45 | var parse = String.Join(Environment.NewLine, | ||
| 46 | "<?xml version='1.0' encoding='utf-16'?>", | ||
| 47 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 48 | " <Fragment />", | ||
| 49 | "</Wix>"); | ||
| 50 | |||
| 51 | var expected = String.Join(Environment.NewLine, | ||
| 52 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 53 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 54 | " <Fragment />", | ||
| 55 | "</Wix>"); | ||
| 56 | |||
| 57 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 58 | |||
| 59 | var messaging = new MockMessaging(); | ||
| 60 | var converter = new WixConverter(messaging, 2, ignoreErrors: new[] { "DeclarationPresent" } ); | ||
| 61 | |||
| 62 | var errors = converter.ConvertDocument(document); | ||
| 63 | |||
| 64 | var actual = UnformattedDocumentString(document, omitXmlDeclaration: false); | ||
| 65 | |||
| 66 | Assert.Equal(0, errors); | ||
| 67 | Assert.Equal(expected, actual); | ||
| 68 | } | ||
| 69 | |||
| 70 | [Fact] | ||
| 71 | public void CanConvertMainNamespace() | ||
| 72 | { | ||
| 73 | var parse = String.Join(Environment.NewLine, | ||
| 74 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 75 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 76 | " <Fragment />", | ||
| 77 | "</Wix>"); | ||
| 78 | |||
| 79 | var expected = String.Join(Environment.NewLine, | ||
| 80 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 81 | " <Fragment />", | ||
| 82 | "</Wix>"); | ||
| 83 | |||
| 84 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 85 | |||
| 86 | var messaging = new MockMessaging(); | ||
| 87 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 88 | |||
| 89 | var errors = converter.ConvertDocument(document); | ||
| 90 | |||
| 91 | var actual = UnformattedDocumentString(document); | ||
| 92 | |||
| 93 | Assert.Equal(2, errors); | ||
| 94 | //Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 95 | Assert.Equal(expected, actual); | ||
| 96 | } | ||
| 97 | |||
| 98 | [Fact] | ||
| 99 | public void CanConvertNamedMainNamespace() | ||
| 100 | { | ||
| 101 | var parse = String.Join(Environment.NewLine, | ||
| 102 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 103 | "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 104 | " <w:Fragment />", | ||
| 105 | "</w:Wix>"); | ||
| 106 | |||
| 107 | var expected = String.Join(Environment.NewLine, | ||
| 108 | "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 109 | " <w:Fragment />", | ||
| 110 | "</w:Wix>"); | ||
| 111 | |||
| 112 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 113 | |||
| 114 | var messaging = new MockMessaging(); | ||
| 115 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 116 | |||
| 117 | var errors = converter.ConvertDocument(document); | ||
| 118 | |||
| 119 | var actual = UnformattedDocumentString(document); | ||
| 120 | |||
| 121 | Assert.Equal(2, errors); | ||
| 122 | Assert.Equal(expected, actual); | ||
| 123 | Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); | ||
| 124 | } | ||
| 125 | |||
| 126 | [Fact] | ||
| 127 | public void CanConvertNonWixDefaultNamespace() | ||
| 128 | { | ||
| 129 | var parse = String.Join(Environment.NewLine, | ||
| 130 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 131 | "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi' xmlns='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 132 | " <w:Fragment>", | ||
| 133 | " <Test />", | ||
| 134 | " </w:Fragment>", | ||
| 135 | "</w:Wix>"); | ||
| 136 | |||
| 137 | var expected = String.Join(Environment.NewLine, | ||
| 138 | "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 139 | " <w:Fragment>", | ||
| 140 | " <Test />", | ||
| 141 | " </w:Fragment>", | ||
| 142 | "</w:Wix>"); | ||
| 143 | |||
| 144 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 145 | |||
| 146 | var messaging = new MockMessaging(); | ||
| 147 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 148 | |||
| 149 | var errors = converter.ConvertDocument(document); | ||
| 150 | |||
| 151 | var actual = UnformattedDocumentString(document); | ||
| 152 | |||
| 153 | Assert.Equal(expected, actual); | ||
| 154 | Assert.Equal(3, errors); | ||
| 155 | Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); | ||
| 156 | Assert.Equal("http://wixtoolset.org/schemas/v4/wxs/util", document.Root.GetDefaultNamespace()); | ||
| 157 | } | ||
| 158 | |||
| 159 | [Fact] | ||
| 160 | public void CanRemoveUnusedNamespaces() | ||
| 161 | { | ||
| 162 | var parse = String.Join(Environment.NewLine, | ||
| 163 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 164 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 165 | " <Fragment />", | ||
| 166 | "</Wix>"); | ||
| 167 | |||
| 168 | var expected = String.Join(Environment.NewLine, | ||
| 169 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 170 | " <Fragment />", | ||
| 171 | "</Wix>"); | ||
| 172 | |||
| 173 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 174 | |||
| 175 | var messaging = new MockMessaging(); | ||
| 176 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 177 | |||
| 178 | var errors = converter.ConvertDocument(document); | ||
| 179 | |||
| 180 | var actual = UnformattedDocumentString(document); | ||
| 181 | |||
| 182 | Assert.Equal(4, errors); | ||
| 183 | Assert.Equal(expected, actual); | ||
| 184 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 185 | } | ||
| 186 | |||
| 187 | [Fact] | ||
| 188 | public void CanConvertMissingWixNamespace() | ||
| 189 | { | ||
| 190 | var parse = String.Join(Environment.NewLine, | ||
| 191 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 192 | "<Wix>", | ||
| 193 | " <Fragment />", | ||
| 194 | "</Wix>"); | ||
| 195 | |||
| 196 | var expected = String.Join(Environment.NewLine, | ||
| 197 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 198 | " <Fragment />", | ||
| 199 | "</Wix>"); | ||
| 200 | |||
| 201 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 202 | |||
| 203 | var messaging = new MockMessaging(); | ||
| 204 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 205 | |||
| 206 | var errors = converter.ConvertDocument(document); | ||
| 207 | |||
| 208 | var actual = UnformattedDocumentString(document); | ||
| 209 | |||
| 210 | Assert.Equal(2, errors); | ||
| 211 | Assert.Equal(expected, actual); | ||
| 212 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 213 | } | ||
| 214 | |||
| 215 | [Fact] | ||
| 216 | public void CanConvertMissingIncludeNamespace() | ||
| 217 | { | ||
| 218 | var parse = String.Join(Environment.NewLine, | ||
| 219 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 220 | "<Include>", | ||
| 221 | " <?define Version = 1.2.3 ?>", | ||
| 222 | " <Fragment>", | ||
| 223 | " <DirectoryRef Id='TARGETDIR'>", | ||
| 224 | " <Directory Id='ANOTHERDIR' Name='Another' />", | ||
| 225 | " </DirectoryRef>", | ||
| 226 | " </Fragment>", | ||
| 227 | "</Include>"); | ||
| 228 | |||
| 229 | var expected = String.Join(Environment.NewLine, | ||
| 230 | "<Include xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 231 | " <?define Version = 1.2.3 ?>", | ||
| 232 | " <Fragment>", | ||
| 233 | " <DirectoryRef Id=\"TARGETDIR\">", | ||
| 234 | " <Directory Id=\"ANOTHERDIR\" Name=\"Another\" />", | ||
| 235 | " </DirectoryRef>", | ||
| 236 | " </Fragment>", | ||
| 237 | "</Include>"); | ||
| 238 | |||
| 239 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 240 | |||
| 241 | var messaging = new MockMessaging(); | ||
| 242 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 243 | |||
| 244 | var errors = converter.ConvertDocument(document); | ||
| 245 | |||
| 246 | var actual = UnformattedDocumentString(document); | ||
| 247 | |||
| 248 | Assert.Equal(2, errors); | ||
| 249 | Assert.Equal(expected, actual); | ||
| 250 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 251 | } | ||
| 252 | |||
| 253 | [Fact] | ||
| 254 | public void CanConvertAnonymousFile() | ||
| 255 | { | ||
| 256 | var parse = String.Join(Environment.NewLine, | ||
| 257 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 258 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 259 | " <File Source='path\\to\\foo.txt' />", | ||
| 260 | "</Wix>"); | ||
| 261 | |||
| 262 | var expected = String.Join(Environment.NewLine, | ||
| 263 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 264 | " <File Id=\"foo.txt\" Source=\"path\\to\\foo.txt\" />", | ||
| 265 | "</Wix>"); | ||
| 266 | |||
| 267 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 268 | |||
| 269 | var messaging = new MockMessaging(); | ||
| 270 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 271 | |||
| 272 | var errors = converter.ConvertDocument(document); | ||
| 273 | |||
| 274 | var actual = UnformattedDocumentString(document); | ||
| 275 | |||
| 276 | Assert.Equal(3, errors); | ||
| 277 | Assert.Equal(expected, actual); | ||
| 278 | } | ||
| 279 | |||
| 280 | [Fact] | ||
| 281 | public void CanConvertShortNameDirectoryWithoutName() | ||
| 282 | { | ||
| 283 | var parse = String.Join(Environment.NewLine, | ||
| 284 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 285 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 286 | " <Directory ShortName='iamshort' />", | ||
| 287 | "</Wix>"); | ||
| 288 | |||
| 289 | var expected = String.Join(Environment.NewLine, | ||
| 290 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 291 | " <Directory Name=\"iamshort\" />", | ||
| 292 | "</Wix>"); | ||
| 293 | |||
| 294 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 295 | |||
| 296 | var messaging = new MockMessaging(); | ||
| 297 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 298 | |||
| 299 | var errors = converter.ConvertDocument(document); | ||
| 300 | |||
| 301 | var actual = UnformattedDocumentString(document); | ||
| 302 | |||
| 303 | Assert.Equal(2, errors); | ||
| 304 | Assert.Equal(expected, actual); | ||
| 305 | } | ||
| 306 | |||
| 307 | [Fact] | ||
| 308 | public void CanConvertCatalogElement() | ||
| 309 | { | ||
| 310 | var parse = String.Join(Environment.NewLine, | ||
| 311 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 312 | " <Catalog Id='idCatalog' SourceFile='path\\to\\catalog.cat' />", | ||
| 313 | "</Wix>"); | ||
| 314 | |||
| 315 | var expected = String.Join(Environment.NewLine, | ||
| 316 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 317 | " ", | ||
| 318 | "</Wix>"); | ||
| 319 | |||
| 320 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 321 | |||
| 322 | var messaging = new MockMessaging(); | ||
| 323 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 324 | |||
| 325 | var errors = converter.ConvertDocument(document); | ||
| 326 | |||
| 327 | var actual = UnformattedDocumentString(document); | ||
| 328 | |||
| 329 | Assert.Equal(1, errors); | ||
| 330 | Assert.Equal(expected, actual); | ||
| 331 | } | ||
| 332 | |||
| 333 | [Fact] | ||
| 334 | public void CanConvertSuppressSignatureValidationNo() | ||
| 335 | { | ||
| 336 | var parse = String.Join(Environment.NewLine, | ||
| 337 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 338 | " <MsiPackage SuppressSignatureValidation='no' />", | ||
| 339 | "</Wix>"); | ||
| 340 | |||
| 341 | var expected = String.Join(Environment.NewLine, | ||
| 342 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 343 | " <MsiPackage />", | ||
| 344 | "</Wix>"); | ||
| 345 | |||
| 346 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 347 | |||
| 348 | var messaging = new MockMessaging(); | ||
| 349 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 350 | |||
| 351 | var errors = converter.ConvertDocument(document); | ||
| 352 | |||
| 353 | var actual = UnformattedDocumentString(document); | ||
| 354 | |||
| 355 | Assert.Equal(1, errors); | ||
| 356 | Assert.Equal(expected, actual); | ||
| 357 | } | ||
| 358 | |||
| 359 | [Fact] | ||
| 360 | public void CanConvertSuppressSignatureValidationYes() | ||
| 361 | { | ||
| 362 | var parse = String.Join(Environment.NewLine, | ||
| 363 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 364 | " <Payload SuppressSignatureValidation='yes' />", | ||
| 365 | "</Wix>"); | ||
| 366 | |||
| 367 | var expected = String.Join(Environment.NewLine, | ||
| 368 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 369 | " <Payload />", | ||
| 370 | "</Wix>"); | ||
| 371 | |||
| 372 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 373 | |||
| 374 | var messaging = new MockMessaging(); | ||
| 375 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 376 | |||
| 377 | var errors = converter.ConvertDocument(document); | ||
| 378 | |||
| 379 | var actual = UnformattedDocumentString(document); | ||
| 380 | |||
| 381 | Assert.Equal(1, errors); | ||
| 382 | Assert.Equal(expected, actual); | ||
| 383 | } | ||
| 384 | |||
| 385 | [Fact] | ||
| 386 | public void CantConvertVerbTarget() | ||
| 387 | { | ||
| 388 | var parse = String.Join(Environment.NewLine, | ||
| 389 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 390 | " <Verb Target='anything' />", | ||
| 391 | "</Wix>"); | ||
| 392 | |||
| 393 | var expected = String.Join(Environment.NewLine, | ||
| 394 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 395 | " <Verb Target=\"anything\" />", | ||
| 396 | "</Wix>"); | ||
| 397 | |||
| 398 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 399 | |||
| 400 | var messaging = new MockMessaging(); | ||
| 401 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 402 | |||
| 403 | var errors = converter.ConvertDocument(document); | ||
| 404 | |||
| 405 | var actual = UnformattedDocumentString(document); | ||
| 406 | |||
| 407 | Assert.Equal(2, errors); | ||
| 408 | Assert.Equal(expected, actual); | ||
| 409 | } | ||
| 410 | |||
| 411 | [Fact] | ||
| 412 | public void CanConvertDeprecatedPrefix() | ||
| 413 | { | ||
| 414 | var parse = String.Join(Environment.NewLine, | ||
| 415 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 416 | "<Fragment>", | ||
| 417 | "<ComponentGroup Id=\"$(loc.Variable)\" />", | ||
| 418 | "<ComponentGroup Id=\"$$(loc.Variable)\" />", | ||
| 419 | "<ComponentGroup Id=\"$$$(loc.Variable)\" />", | ||
| 420 | "<ComponentGroup Id=\"$$$$(loc.Variable)\" />", | ||
| 421 | "<ComponentGroup Id=\"$$$$$(loc.Variable)\" />", | ||
| 422 | "</Fragment>", | ||
| 423 | "</Wix>"); | ||
| 424 | |||
| 425 | var expected = String.Join(Environment.NewLine, | ||
| 426 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 427 | "<Fragment>", | ||
| 428 | "<ComponentGroup Id=\"!(loc.Variable)\" />", | ||
| 429 | "<ComponentGroup Id=\"$$(loc.Variable)\" />", | ||
| 430 | "<ComponentGroup Id=\"$$!(loc.Variable)\" />", | ||
| 431 | "<ComponentGroup Id=\"$$$$(loc.Variable)\" />", | ||
| 432 | "<ComponentGroup Id=\"$$$$!(loc.Variable)\" />", | ||
| 433 | "</Fragment>", | ||
| 434 | "</Wix>"); | ||
| 435 | |||
| 436 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 437 | |||
| 438 | var messaging = new MockMessaging(); | ||
| 439 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 440 | |||
| 441 | var errors = converter.ConvertDocument(document); | ||
| 442 | |||
| 443 | var actual = UnformattedDocumentString(document); | ||
| 444 | |||
| 445 | Assert.Equal(3, errors); | ||
| 446 | Assert.Equal(expected, actual); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs b/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs new file mode 100644 index 00000000..a39f6243 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs | |||
| @@ -0,0 +1,195 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Linq; | ||
| 8 | using WixBuildTools.TestSupport; | ||
| 9 | using WixToolset.Converters; | ||
| 10 | using WixToolset.Core; | ||
| 11 | using WixToolset.Core.TestPackage; | ||
| 12 | using WixToolsetTest.Converters.Mocks; | ||
| 13 | using Xunit; | ||
| 14 | |||
| 15 | public class ConverterIntegrationFixture | ||
| 16 | { | ||
| 17 | [Fact] | ||
| 18 | public void CanConvertPermissionExFile() | ||
| 19 | { | ||
| 20 | const string beforeFileName = "v3.wxs"; | ||
| 21 | const string afterFileName = "v4_expected.wxs"; | ||
| 22 | var folder = TestData.Get(@"TestData\PermissionEx"); | ||
| 23 | |||
| 24 | using (var fs = new DisposableFileSystem()) | ||
| 25 | { | ||
| 26 | var baseFolder = fs.GetFolder(true); | ||
| 27 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 28 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 29 | |||
| 30 | var messaging = new MockMessaging(); | ||
| 31 | var converter = new WixConverter(messaging, 4); | ||
| 32 | var errors = converter.ConvertFile(targetFile, true); | ||
| 33 | |||
| 34 | Assert.Equal(8, errors); | ||
| 35 | |||
| 36 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | ||
| 37 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | ||
| 38 | Assert.Equal(expected, actual); | ||
| 39 | |||
| 40 | EnsureFixed(targetFile); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | [Fact] | ||
| 45 | public void CanConvertSingleFile() | ||
| 46 | { | ||
| 47 | const string beforeFileName = "SingleFile.wxs"; | ||
| 48 | const string afterFileName = "ConvertedSingleFile.wxs"; | ||
| 49 | var folder = TestData.Get(@"TestData\SingleFile"); | ||
| 50 | |||
| 51 | using (var fs = new DisposableFileSystem()) | ||
| 52 | { | ||
| 53 | var baseFolder = fs.GetFolder(true); | ||
| 54 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 55 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 56 | |||
| 57 | var messaging = new MockMessaging(); | ||
| 58 | var converter = new WixConverter(messaging, 4); | ||
| 59 | var errors = converter.ConvertFile(targetFile, true); | ||
| 60 | |||
| 61 | Assert.Equal(9, errors); | ||
| 62 | |||
| 63 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | ||
| 64 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | ||
| 65 | Assert.Equal(expected, actual); | ||
| 66 | |||
| 67 | EnsureFixed(targetFile); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | [Fact] | ||
| 72 | public void CanDetectReadOnlyOutputFile() | ||
| 73 | { | ||
| 74 | const string beforeFileName = "SingleFile.wxs"; | ||
| 75 | var folder = TestData.Get(@"TestData\SingleFile"); | ||
| 76 | |||
| 77 | using (var fs = new DisposableFileSystem()) | ||
| 78 | { | ||
| 79 | var baseFolder = fs.GetFolder(true); | ||
| 80 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 81 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 82 | |||
| 83 | var info = new FileInfo(targetFile); | ||
| 84 | info.IsReadOnly = true; | ||
| 85 | |||
| 86 | var messaging = new MockMessaging(); | ||
| 87 | var converter = new WixConverter(messaging, 4); | ||
| 88 | var errors = converter.ConvertFile(targetFile, true); | ||
| 89 | |||
| 90 | Assert.Single(messaging.Messages.Where(m => m.Id == 5/*WixConverter.ConverterTestType.UnauthorizedAccessException*/)); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | [Fact] | ||
| 95 | public void RetainsPreprocessorInstructions() | ||
| 96 | { | ||
| 97 | const string beforeFileName = "Preprocessor.wxs"; | ||
| 98 | const string afterFileName = "ConvertedPreprocessor.wxs"; | ||
| 99 | var folder = TestData.Get(@"TestData\Preprocessor"); | ||
| 100 | |||
| 101 | using (var fs = new DisposableFileSystem()) | ||
| 102 | { | ||
| 103 | var baseFolder = fs.GetFolder(true); | ||
| 104 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 105 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 106 | |||
| 107 | var settingsFile = Path.Combine(folder, "wixcop.settings.xml"); | ||
| 108 | |||
| 109 | var result = RunConversion(targetFile, settingsFile: settingsFile); | ||
| 110 | Assert.Equal(9, result.ExitCode); | ||
| 111 | |||
| 112 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | ||
| 113 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | ||
| 114 | Assert.Equal(expected, actual); | ||
| 115 | |||
| 116 | EnsureFixed(targetFile); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | [Fact] | ||
| 121 | public void CanConvertQtExec() | ||
| 122 | { | ||
| 123 | const string beforeFileName = "v3.wxs"; | ||
| 124 | const string afterFileName = "v4_expected.wxs"; | ||
| 125 | var folder = TestData.Get(@"TestData\QtExec"); | ||
| 126 | |||
| 127 | using (var fs = new DisposableFileSystem()) | ||
| 128 | { | ||
| 129 | var baseFolder = fs.GetFolder(true); | ||
| 130 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 131 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 132 | |||
| 133 | var result = RunConversion(targetFile); | ||
| 134 | Assert.Equal(13, result.ExitCode); | ||
| 135 | |||
| 136 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | ||
| 137 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | ||
| 138 | Assert.Equal(expected, actual); | ||
| 139 | |||
| 140 | EnsureFixed(targetFile); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | [Fact] | ||
| 145 | public void DetectUnconvertableQtExecCmdTimeout() | ||
| 146 | { | ||
| 147 | const string beforeFileName = "v3.wxs"; | ||
| 148 | const string afterFileName = "v4_expected.wxs"; | ||
| 149 | var folder = TestData.Get(@"TestData\QtExec.bad"); | ||
| 150 | |||
| 151 | using (var fs = new DisposableFileSystem()) | ||
| 152 | { | ||
| 153 | var baseFolder = fs.GetFolder(true); | ||
| 154 | var targetFile = Path.Combine(baseFolder, beforeFileName); | ||
| 155 | File.Copy(Path.Combine(folder, beforeFileName), Path.Combine(baseFolder, beforeFileName)); | ||
| 156 | |||
| 157 | var result = RunConversion(targetFile); | ||
| 158 | |||
| 159 | Assert.Equal(13, result.ExitCode); | ||
| 160 | Assert.Single(result.Messages.Where(message => message.ToString().EndsWith("(QtExecCmdTimeoutAmbiguous)"))); | ||
| 161 | |||
| 162 | var expected = File.ReadAllText(Path.Combine(folder, afterFileName)).Replace("\r\n", "\n"); | ||
| 163 | var actual = File.ReadAllText(targetFile).Replace("\r\n", "\n"); | ||
| 164 | Assert.Equal(expected, actual); | ||
| 165 | |||
| 166 | // still fails because QtExecCmdTimeoutAmbiguous is unfixable | ||
| 167 | var result2 = RunConversion(targetFile); | ||
| 168 | Assert.Equal(1, result2.ExitCode); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | private static WixRunnerResult RunConversion(string targetFile, bool fixErrors = true, string settingsFile = null) | ||
| 173 | { | ||
| 174 | var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider().AddConverter(); | ||
| 175 | |||
| 176 | var exitCode = WixRunner.Execute(new[] | ||
| 177 | { | ||
| 178 | "convert", | ||
| 179 | fixErrors ? null : "--dry-run", | ||
| 180 | String.IsNullOrEmpty(settingsFile) ? null : "-set1" + settingsFile, | ||
| 181 | targetFile | ||
| 182 | }, serviceProvider, out var messages); | ||
| 183 | |||
| 184 | return new WixRunnerResult { ExitCode = exitCode.Result, Messages = messages.ToArray() }; | ||
| 185 | } | ||
| 186 | |||
| 187 | private static void EnsureFixed(string targetFile) | ||
| 188 | { | ||
| 189 | var messaging2 = new MockMessaging(); | ||
| 190 | var converter2 = new WixConverter(messaging2, 4); | ||
| 191 | var errors2 = converter2.ConvertFile(targetFile, true); | ||
| 192 | Assert.Equal(0, errors2); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/CustomActionFixture.cs b/src/wix/test/WixToolsetTest.Converters/CustomActionFixture.cs new file mode 100644 index 00000000..eafc171a --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/CustomActionFixture.cs | |||
| @@ -0,0 +1,88 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Xml.Linq; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class CustomActionFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void CanConvertCustomAction() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 19 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 20 | " <CustomAction Id='Foo' BinaryKey='WixCA' DllEntry='CAQuietExec' />", | ||
| 21 | " <CustomAction Id='Foo' BinaryKey='WixCA_x64' DllEntry='CAQuietExec64' />", | ||
| 22 | " <CustomAction Id='Foo' BinaryKey='UtilCA' DllEntry='WixQuietExec' />", | ||
| 23 | " <CustomAction Id='Foo' BinaryKey='UtilCA_x64' DllEntry='WixQuietExec64' />", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = String.Join(Environment.NewLine, | ||
| 27 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 28 | " <CustomAction Id=\"Foo\" DllEntry=\"WixQuietExec\" BinaryRef=\"Wix4UtilCA_X86\" />", | ||
| 29 | " <CustomAction Id=\"Foo\" DllEntry=\"WixQuietExec64\" BinaryRef=\"Wix4UtilCA_X64\" />", | ||
| 30 | " <CustomAction Id=\"Foo\" DllEntry=\"WixQuietExec\" BinaryRef=\"Wix4UtilCA_X86\" />", | ||
| 31 | " <CustomAction Id=\"Foo\" DllEntry=\"WixQuietExec64\" BinaryRef=\"Wix4UtilCA_X64\" />", | ||
| 32 | "</Wix>"); | ||
| 33 | |||
| 34 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 35 | |||
| 36 | var messaging = new MockMessaging(); | ||
| 37 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 38 | |||
| 39 | var errors = converter.ConvertDocument(document); | ||
| 40 | |||
| 41 | var actual = UnformattedDocumentString(document); | ||
| 42 | |||
| 43 | Assert.Equal(11, errors); | ||
| 44 | Assert.Equal(expected, actual); | ||
| 45 | } | ||
| 46 | |||
| 47 | [Fact] | ||
| 48 | public void CanConvertCustomActionScript() | ||
| 49 | { | ||
| 50 | var parse = String.Join(Environment.NewLine, | ||
| 51 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 52 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 53 | " <CustomAction Id='Foo' Script='jscript'>", | ||
| 54 | " function() {", | ||
| 55 | " var x = 0;", | ||
| 56 | " return x;", | ||
| 57 | " }", | ||
| 58 | " </CustomAction>", | ||
| 59 | "</Wix>"); | ||
| 60 | |||
| 61 | var expected = String.Join(Environment.NewLine, | ||
| 62 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 63 | " <CustomAction Id=\"Foo\" Script=\"jscript\" ScriptSourceFile=\"Foo.js\" />", | ||
| 64 | "</Wix>"); | ||
| 65 | |||
| 66 | var expectedScript = String.Join("\n", | ||
| 67 | "function() {", | ||
| 68 | " var x = 0;", | ||
| 69 | " return x;", | ||
| 70 | " }"); | ||
| 71 | |||
| 72 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 73 | |||
| 74 | var messaging = new MockMessaging(); | ||
| 75 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 76 | |||
| 77 | var errors = converter.ConvertDocument(document); | ||
| 78 | |||
| 79 | var actual = UnformattedDocumentString(document); | ||
| 80 | |||
| 81 | Assert.Equal(2, errors); | ||
| 82 | Assert.Equal(expected, actual); | ||
| 83 | |||
| 84 | var script = File.ReadAllText("Foo.js"); | ||
| 85 | Assert.Equal(expectedScript, script); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/CustomTableFixture.cs b/src/wix/test/WixToolsetTest.Converters/CustomTableFixture.cs new file mode 100644 index 00000000..2b81a863 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/CustomTableFixture.cs | |||
| @@ -0,0 +1,363 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class CustomTableFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixCustomTableCategoryAndModularization() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <CustomTable Id='Custom1'>", | ||
| 22 | " <Column Id='Column1' Type='string' Category='Text' Modularize='Column' />", | ||
| 23 | " </CustomTable>", | ||
| 24 | " </Fragment>", | ||
| 25 | "</Wix>"); | ||
| 26 | |||
| 27 | var expected = new[] | ||
| 28 | { | ||
| 29 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 30 | " <Fragment>", | ||
| 31 | " <CustomTable Id=\"Custom1\">", | ||
| 32 | " <Column Id=\"Column1\" Type=\"string\" Category=\"text\" Modularize=\"column\" />", | ||
| 33 | " </CustomTable>", | ||
| 34 | " </Fragment>", | ||
| 35 | "</Wix>" | ||
| 36 | }; | ||
| 37 | |||
| 38 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 39 | |||
| 40 | var messaging = new MockMessaging(); | ||
| 41 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 42 | |||
| 43 | var errors = converter.ConvertDocument(document); | ||
| 44 | Assert.Equal(4, errors); | ||
| 45 | |||
| 46 | var actualLines = UnformattedDocumentLines(document); | ||
| 47 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 48 | } | ||
| 49 | |||
| 50 | [Fact] | ||
| 51 | public void FixCustomRowTextValue() | ||
| 52 | { | ||
| 53 | var parse = String.Join(Environment.NewLine, | ||
| 54 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 55 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 56 | " <Fragment>", | ||
| 57 | " <CustomTable Id='Custom1'>", | ||
| 58 | " <Row>", | ||
| 59 | " <Data Id='Column1'>", | ||
| 60 | " Some value", | ||
| 61 | " </Data>", | ||
| 62 | " </Row>", | ||
| 63 | " </CustomTable>", | ||
| 64 | " </Fragment>", | ||
| 65 | "</Wix>"); | ||
| 66 | |||
| 67 | var expected = new[] | ||
| 68 | { | ||
| 69 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 70 | " <Fragment>", | ||
| 71 | " <CustomTable Id=\"Custom1\">", | ||
| 72 | " <Row>", | ||
| 73 | " <Data Id=\"Column1\" Value=\"Some value\" />", | ||
| 74 | " </Row>", | ||
| 75 | " </CustomTable>", | ||
| 76 | " </Fragment>", | ||
| 77 | "</Wix>" | ||
| 78 | }; | ||
| 79 | |||
| 80 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 81 | |||
| 82 | var messaging = new MockMessaging(); | ||
| 83 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 84 | |||
| 85 | var errors = converter.ConvertDocument(document); | ||
| 86 | Assert.Equal(4, errors); | ||
| 87 | |||
| 88 | var actualLines = UnformattedDocumentLines(document); | ||
| 89 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 90 | } | ||
| 91 | |||
| 92 | [Fact] | ||
| 93 | public void FixCustomRowCdataValue() | ||
| 94 | { | ||
| 95 | var parse = String.Join(Environment.NewLine, | ||
| 96 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 97 | " <Fragment>", | ||
| 98 | " <CustomTable Id='Custom1'>", | ||
| 99 | " <Row>", | ||
| 100 | " <Data Id='Column1'>", | ||
| 101 | " <![CDATA[", | ||
| 102 | " Some value", | ||
| 103 | " ]]>", | ||
| 104 | " </Data>", | ||
| 105 | " </Row>", | ||
| 106 | " </CustomTable>", | ||
| 107 | " </Fragment>", | ||
| 108 | "</Wix>"); | ||
| 109 | |||
| 110 | var expected = new[] | ||
| 111 | { | ||
| 112 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 113 | " <Fragment>", | ||
| 114 | " <CustomTable Id=\"Custom1\">", | ||
| 115 | " <Row>", | ||
| 116 | " <Data Id=\"Column1\" Value=\"Some value\" />", | ||
| 117 | " </Row>", | ||
| 118 | " </CustomTable>", | ||
| 119 | " </Fragment>", | ||
| 120 | "</Wix>" | ||
| 121 | }; | ||
| 122 | |||
| 123 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 124 | |||
| 125 | var messaging = new MockMessaging(); | ||
| 126 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 127 | |||
| 128 | var errors = converter.ConvertDocument(document); | ||
| 129 | Assert.Equal(3, errors); | ||
| 130 | |||
| 131 | var actualLines = UnformattedDocumentLines(document); | ||
| 132 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 133 | } | ||
| 134 | |||
| 135 | [Fact] | ||
| 136 | public void FixCustomRowWithoutValue() | ||
| 137 | { | ||
| 138 | var parse = String.Join(Environment.NewLine, | ||
| 139 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 140 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 141 | " <Fragment>", | ||
| 142 | " <CustomTable Id='Custom1'>", | ||
| 143 | " <Row Id='Column1'></Row>", | ||
| 144 | " </CustomTable>", | ||
| 145 | " </Fragment>", | ||
| 146 | "</Wix>"); | ||
| 147 | |||
| 148 | var expected = new[] | ||
| 149 | { | ||
| 150 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 151 | " <Fragment>", | ||
| 152 | " <CustomTable Id=\"Custom1\">", | ||
| 153 | " <Row Id=\"Column1\"></Row>", | ||
| 154 | " </CustomTable>", | ||
| 155 | " </Fragment>", | ||
| 156 | "</Wix>" | ||
| 157 | }; | ||
| 158 | |||
| 159 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 160 | |||
| 161 | var messaging = new MockMessaging(); | ||
| 162 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 163 | |||
| 164 | var errors = converter.ConvertDocument(document); | ||
| 165 | Assert.Equal(3, errors); | ||
| 166 | |||
| 167 | var actualLines = UnformattedDocumentLines(document); | ||
| 168 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 169 | } | ||
| 170 | |||
| 171 | [Fact] | ||
| 172 | public void CanConvertBundleCustomTableBootstrapperApplicationData() | ||
| 173 | { | ||
| 174 | var parse = String.Join(Environment.NewLine, | ||
| 175 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 176 | " <CustomTable Id='FgAppx' BootstrapperApplicationData='yes'>", | ||
| 177 | " <Column Id='Column1' PrimaryKey='yes' Type='string' Width='0' Category='text' Description='The first custom column.' />", | ||
| 178 | " <Row>", | ||
| 179 | " <Data Column='Column1'>Row1</Data>", | ||
| 180 | " </Row>", | ||
| 181 | " </CustomTable>", | ||
| 182 | "</Wix>"); | ||
| 183 | |||
| 184 | var expected = String.Join(Environment.NewLine, | ||
| 185 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 186 | " <BundleCustomData Id=\"FgAppx\">", | ||
| 187 | " <BundleAttributeDefinition Id=\"Column1\" />", | ||
| 188 | " <BundleElement>", | ||
| 189 | " <BundleAttribute Id=\"Column1\" Value=\"Row1\" />", | ||
| 190 | " </BundleElement>", | ||
| 191 | " </BundleCustomData>", | ||
| 192 | "</Wix>"); | ||
| 193 | |||
| 194 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 195 | |||
| 196 | var messaging = new MockMessaging(); | ||
| 197 | var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Bundle); | ||
| 198 | |||
| 199 | var errors = converter.ConvertDocument(document); | ||
| 200 | |||
| 201 | var actual = UnformattedDocumentString(document); | ||
| 202 | |||
| 203 | Assert.Equal(2, errors); | ||
| 204 | Assert.Equal(expected, actual); | ||
| 205 | } | ||
| 206 | |||
| 207 | [Fact] | ||
| 208 | public void CanConvertBundleCustomTableRef() | ||
| 209 | { | ||
| 210 | var parse = String.Join(Environment.NewLine, | ||
| 211 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 212 | " <CustomTable Id='FgAppx'>", | ||
| 213 | " <Row>", | ||
| 214 | " <Data Column='Column1'>Row1</Data>", | ||
| 215 | " </Row>", | ||
| 216 | " </CustomTable>", | ||
| 217 | "</Wix>"); | ||
| 218 | |||
| 219 | var expected = String.Join(Environment.NewLine, | ||
| 220 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 221 | " <BundleCustomDataRef Id=\"FgAppx\">", | ||
| 222 | " <BundleElement>", | ||
| 223 | " <BundleAttribute Id=\"Column1\" Value=\"Row1\" />", | ||
| 224 | " </BundleElement>", | ||
| 225 | " </BundleCustomDataRef>", | ||
| 226 | "</Wix>"); | ||
| 227 | |||
| 228 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 229 | |||
| 230 | var messaging = new MockMessaging(); | ||
| 231 | var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Bundle); | ||
| 232 | |||
| 233 | var errors = converter.ConvertDocument(document); | ||
| 234 | |||
| 235 | var actual = UnformattedDocumentString(document); | ||
| 236 | |||
| 237 | Assert.Equal(2, errors); | ||
| 238 | Assert.Equal(expected, actual); | ||
| 239 | } | ||
| 240 | |||
| 241 | [Fact] | ||
| 242 | public void CanConvertMsiCustomTableBootstrapperApplicationData() | ||
| 243 | { | ||
| 244 | var parse = String.Join(Environment.NewLine, | ||
| 245 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 246 | " <CustomTable Id='FgAppx' BootstrapperApplicationData='yes'>", | ||
| 247 | " <Column Id='Column1' PrimaryKey='yes' Type='string' Width='0' Category='text' Description='The first custom column.' />", | ||
| 248 | " <Row>", | ||
| 249 | " <Data Column='Column1'>Row1</Data>", | ||
| 250 | " </Row>", | ||
| 251 | " </CustomTable>", | ||
| 252 | "</Wix>"); | ||
| 253 | |||
| 254 | var expected = String.Join(Environment.NewLine, | ||
| 255 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 256 | " <CustomTable Id=\"FgAppx\" Unreal=\"yes\">", | ||
| 257 | " <Column Id=\"Column1\" PrimaryKey=\"yes\" Type=\"string\" Width=\"0\" Category=\"text\" Description=\"The first custom column.\" />", | ||
| 258 | " <Row>", | ||
| 259 | " <Data Column=\"Column1\" Value=\"Row1\" />", | ||
| 260 | " </Row>", | ||
| 261 | " </CustomTable>", | ||
| 262 | "</Wix>"); | ||
| 263 | |||
| 264 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 265 | |||
| 266 | var messaging = new MockMessaging(); | ||
| 267 | var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Msi); | ||
| 268 | |||
| 269 | var errors = converter.ConvertDocument(document); | ||
| 270 | |||
| 271 | var actual = UnformattedDocumentString(document); | ||
| 272 | |||
| 273 | Assert.Equal(2, errors); | ||
| 274 | Assert.Equal(expected, actual); | ||
| 275 | } | ||
| 276 | |||
| 277 | [Fact] | ||
| 278 | public void CanConvertMsiCustomTableRef() | ||
| 279 | { | ||
| 280 | var parse = String.Join(Environment.NewLine, | ||
| 281 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 282 | " <CustomTable Id='FgAppx'>", | ||
| 283 | " <Row>", | ||
| 284 | " <Data Column='Column1'>Row1</Data>", | ||
| 285 | " </Row>", | ||
| 286 | " </CustomTable>", | ||
| 287 | "</Wix>"); | ||
| 288 | |||
| 289 | var expected = String.Join(Environment.NewLine, | ||
| 290 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 291 | " <CustomTableRef Id=\"FgAppx\">", | ||
| 292 | " <Row>", | ||
| 293 | " <Data Column=\"Column1\" Value=\"Row1\" />", | ||
| 294 | " </Row>", | ||
| 295 | " </CustomTableRef>", | ||
| 296 | "</Wix>"); | ||
| 297 | |||
| 298 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 299 | |||
| 300 | var messaging = new MockMessaging(); | ||
| 301 | var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Msi); | ||
| 302 | |||
| 303 | var errors = converter.ConvertDocument(document); | ||
| 304 | |||
| 305 | var actual = UnformattedDocumentString(document); | ||
| 306 | |||
| 307 | Assert.Equal(2, errors); | ||
| 308 | Assert.Equal(expected, actual); | ||
| 309 | } | ||
| 310 | |||
| 311 | [Fact] | ||
| 312 | public void CanDetectAmbiguousCustomTableBootstrapperApplicationData() | ||
| 313 | { | ||
| 314 | var parse = String.Join(Environment.NewLine, | ||
| 315 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 316 | " <CustomTable Id='FgAppx' BootstrapperApplicationData='yes' />", | ||
| 317 | "</Wix>"); | ||
| 318 | |||
| 319 | var expected = String.Join(Environment.NewLine, | ||
| 320 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 321 | " <CustomTable Id=\"FgAppx\" BootstrapperApplicationData=\"yes\" />", | ||
| 322 | "</Wix>"); | ||
| 323 | |||
| 324 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 325 | |||
| 326 | var messaging = new MockMessaging(); | ||
| 327 | var converter = new WixConverter(messaging, 2); | ||
| 328 | |||
| 329 | var errors = converter.ConvertDocument(document); | ||
| 330 | |||
| 331 | var actual = UnformattedDocumentString(document); | ||
| 332 | |||
| 333 | Assert.Equal(1, errors); | ||
| 334 | Assert.Equal(expected, actual); | ||
| 335 | } | ||
| 336 | |||
| 337 | [Fact] | ||
| 338 | public void CanRemoveBootstrapperApplicationDataFromRealCustomTable() | ||
| 339 | { | ||
| 340 | var parse = String.Join(Environment.NewLine, | ||
| 341 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 342 | " <CustomTable Id='FgAppx' BootstrapperApplicationData='no' />", | ||
| 343 | "</Wix>"); | ||
| 344 | |||
| 345 | var expected = String.Join(Environment.NewLine, | ||
| 346 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 347 | " <CustomTable Id=\"FgAppx\" />", | ||
| 348 | "</Wix>"); | ||
| 349 | |||
| 350 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 351 | |||
| 352 | var messaging = new MockMessaging(); | ||
| 353 | var converter = new WixConverter(messaging, 2); | ||
| 354 | |||
| 355 | var errors = converter.ConvertDocument(document); | ||
| 356 | |||
| 357 | var actual = UnformattedDocumentString(document); | ||
| 358 | |||
| 359 | Assert.Equal(1, errors); | ||
| 360 | Assert.Equal(expected, actual); | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/DependencyFixture.cs b/src/wix/test/WixToolsetTest.Converters/DependencyFixture.cs new file mode 100644 index 00000000..41ded927 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/DependencyFixture.cs | |||
| @@ -0,0 +1,178 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class DependencyFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixPackageDependencyProvides() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:dep='http://schemas.microsoft.com/wix/DependencyExtension'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <ComponentGroup Id='Group1' Directory='INSTALLFOLDER'>", | ||
| 22 | " <Component>", | ||
| 23 | " <dep:Provides Key='abc' />", | ||
| 24 | " </Component>", | ||
| 25 | " </ComponentGroup>", | ||
| 26 | " </Fragment>", | ||
| 27 | "</Wix>"); | ||
| 28 | |||
| 29 | var expected = new[] | ||
| 30 | { | ||
| 31 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:dep=\"http://wixtoolset.org/schemas/v4/wxs/dependency\">", | ||
| 32 | " <Fragment>", | ||
| 33 | " <ComponentGroup Id=\"Group1\" Directory=\"INSTALLFOLDER\">", | ||
| 34 | " <Component>", | ||
| 35 | " <Provides Key=\"abc\" dep:Check=\"yes\" />", | ||
| 36 | " </Component>", | ||
| 37 | " </ComponentGroup>", | ||
| 38 | " </Fragment>", | ||
| 39 | "</Wix>" | ||
| 40 | }; | ||
| 41 | |||
| 42 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 43 | |||
| 44 | var messaging = new MockMessaging(); | ||
| 45 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 46 | |||
| 47 | var errors = converter.ConvertDocument(document); | ||
| 48 | Assert.Equal(5, errors); | ||
| 49 | |||
| 50 | var actualLines = UnformattedDocumentLines(document); | ||
| 51 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 52 | } | ||
| 53 | |||
| 54 | [Fact] | ||
| 55 | public void FixPackageDependencyRequires() | ||
| 56 | { | ||
| 57 | var parse = String.Join(Environment.NewLine, | ||
| 58 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 59 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:dep='http://schemas.microsoft.com/wix/DependencyExtension'>", | ||
| 60 | " <Fragment>", | ||
| 61 | " <ComponentGroup Id='Group1' Directory='INSTALLFOLDER'>", | ||
| 62 | " <Component>", | ||
| 63 | " <dep:Provides Key='abc'>", | ||
| 64 | " <dep:Requires Key='xyz' />", | ||
| 65 | " </dep:Provides>", | ||
| 66 | " </Component>", | ||
| 67 | " </ComponentGroup>", | ||
| 68 | " </Fragment>", | ||
| 69 | "</Wix>"); | ||
| 70 | |||
| 71 | var expected = new[] | ||
| 72 | { | ||
| 73 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:dep=\"http://wixtoolset.org/schemas/v4/wxs/dependency\">", | ||
| 74 | " <Fragment>", | ||
| 75 | " <ComponentGroup Id=\"Group1\" Directory=\"INSTALLFOLDER\">", | ||
| 76 | " <Component>", | ||
| 77 | " <Provides Key=\"abc\" dep:Check=\"yes\">", | ||
| 78 | " <Requires Key=\"xyz\" dep:Enforce=\"yes\" />", | ||
| 79 | " </Provides>", | ||
| 80 | " </Component>", | ||
| 81 | " </ComponentGroup>", | ||
| 82 | " </Fragment>", | ||
| 83 | "</Wix>" | ||
| 84 | }; | ||
| 85 | |||
| 86 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 87 | |||
| 88 | var messaging = new MockMessaging(); | ||
| 89 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 90 | |||
| 91 | var errors = converter.ConvertDocument(document); | ||
| 92 | Assert.Equal(7, errors); | ||
| 93 | |||
| 94 | var actualLines = UnformattedDocumentLines(document); | ||
| 95 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 96 | } | ||
| 97 | |||
| 98 | [Fact] | ||
| 99 | public void FixPackageDependencyRequiresRef() | ||
| 100 | { | ||
| 101 | var parse = String.Join(Environment.NewLine, | ||
| 102 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 103 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:dep='http://schemas.microsoft.com/wix/DependencyExtension'>", | ||
| 104 | " <Fragment>", | ||
| 105 | " <ComponentGroup Id='Group1' Directory='INSTALLFOLDER'>", | ||
| 106 | " <Component>", | ||
| 107 | " <dep:Provides Key='abc'>", | ||
| 108 | " <dep:RequiresRef Id='OtherRequires' />", | ||
| 109 | " </dep:Provides>", | ||
| 110 | " </Component>", | ||
| 111 | " </ComponentGroup>", | ||
| 112 | " </Fragment>", | ||
| 113 | "</Wix>"); | ||
| 114 | |||
| 115 | var expected = new[] | ||
| 116 | { | ||
| 117 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:dep=\"http://wixtoolset.org/schemas/v4/wxs/dependency\">", | ||
| 118 | " <Fragment>", | ||
| 119 | " <ComponentGroup Id=\"Group1\" Directory=\"INSTALLFOLDER\">", | ||
| 120 | " <Component>", | ||
| 121 | " <Provides Key=\"abc\" dep:Check=\"yes\">", | ||
| 122 | " <RequiresRef Id=\"OtherRequires\" dep:Enforce=\"yes\" />", | ||
| 123 | " </Provides>", | ||
| 124 | " </Component>", | ||
| 125 | " </ComponentGroup>", | ||
| 126 | " </Fragment>", | ||
| 127 | "</Wix>" | ||
| 128 | }; | ||
| 129 | |||
| 130 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 131 | |||
| 132 | var messaging = new MockMessaging(); | ||
| 133 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 134 | |||
| 135 | var errors = converter.ConvertDocument(document); | ||
| 136 | Assert.Equal(7, errors); | ||
| 137 | |||
| 138 | var actualLines = UnformattedDocumentLines(document); | ||
| 139 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 140 | } | ||
| 141 | |||
| 142 | [Fact] | ||
| 143 | public void FixBundleDependencyProvides() | ||
| 144 | { | ||
| 145 | var parse = String.Join(Environment.NewLine, | ||
| 146 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 147 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:dep='http://schemas.microsoft.com/wix/DependencyExtension'>", | ||
| 148 | " <Fragment>", | ||
| 149 | " <MsiPackage Id='Package1'>", | ||
| 150 | " <dep:Provides Key='abc' />", | ||
| 151 | " </MsiPackage>", | ||
| 152 | " </Fragment>", | ||
| 153 | "</Wix>"); | ||
| 154 | |||
| 155 | var expected = new[] | ||
| 156 | { | ||
| 157 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 158 | " <Fragment>", | ||
| 159 | " <MsiPackage Id=\"Package1\">", | ||
| 160 | " <Provides Key=\"abc\" />", | ||
| 161 | " </MsiPackage>", | ||
| 162 | " </Fragment>", | ||
| 163 | "</Wix>" | ||
| 164 | }; | ||
| 165 | |||
| 166 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 167 | |||
| 168 | var messaging = new MockMessaging(); | ||
| 169 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 170 | |||
| 171 | var errors = converter.ConvertDocument(document); | ||
| 172 | Assert.Equal(5, errors); | ||
| 173 | |||
| 174 | var actualLines = UnformattedDocumentLines(document); | ||
| 175 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/DirectoryFixture.cs b/src/wix/test/WixToolsetTest.Converters/DirectoryFixture.cs new file mode 100644 index 00000000..3c906320 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/DirectoryFixture.cs | |||
| @@ -0,0 +1,92 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class DirectoryFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void RemoveTargetDir() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <Directory Id='TARGETDIR' Name='SourceDir'>", | ||
| 22 | " <!-- Comment -->", | ||
| 23 | " <Directory Id='RootFolder' Name='Root'>", | ||
| 24 | " <Directory Id='ChildFolder' Name='Child' />", | ||
| 25 | " </Directory>", | ||
| 26 | " </Directory>", | ||
| 27 | " </Fragment>", | ||
| 28 | "</Wix>"); | ||
| 29 | |||
| 30 | var expected = new[] | ||
| 31 | { | ||
| 32 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 33 | " <Fragment>", | ||
| 34 | " <!-- Comment -->", | ||
| 35 | " <Directory Id=\"RootFolder\" Name=\"Root\">", | ||
| 36 | " <Directory Id=\"ChildFolder\" Name=\"Child\" />", | ||
| 37 | " </Directory>", | ||
| 38 | " </Fragment>", | ||
| 39 | "</Wix>" | ||
| 40 | }; | ||
| 41 | |||
| 42 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 43 | |||
| 44 | var messaging = new MockMessaging(); | ||
| 45 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 46 | |||
| 47 | var errors = converter.ConvertDocument(document); | ||
| 48 | Assert.Equal(3, errors); | ||
| 49 | |||
| 50 | var actualLines = UnformattedDocumentLines(document); | ||
| 51 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 52 | } | ||
| 53 | |||
| 54 | [Fact] | ||
| 55 | public void FixStandardDirectory() | ||
| 56 | { | ||
| 57 | var parse = String.Join(Environment.NewLine, | ||
| 58 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 59 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 60 | " <Fragment>", | ||
| 61 | " <Directory Id='TARGETDIR' Name='SourceDir'>", | ||
| 62 | " <Directory Id='ProgramFilesFolder' Name='PFiles'>", | ||
| 63 | " <Directory Id='ChildFolder' Name='Child' />", | ||
| 64 | " </Directory>", | ||
| 65 | " </Directory>", | ||
| 66 | " </Fragment>", | ||
| 67 | "</Wix>"); | ||
| 68 | |||
| 69 | var expected = new[] | ||
| 70 | { | ||
| 71 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 72 | " <Fragment>", | ||
| 73 | " <StandardDirectory Id=\"ProgramFilesFolder\">", | ||
| 74 | " <Directory Id=\"ChildFolder\" Name=\"Child\" />", | ||
| 75 | " </StandardDirectory>", | ||
| 76 | " </Fragment>", | ||
| 77 | "</Wix>" | ||
| 78 | }; | ||
| 79 | |||
| 80 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 81 | |||
| 82 | var messaging = new MockMessaging(); | ||
| 83 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 84 | |||
| 85 | var errors = converter.ConvertDocument(document); | ||
| 86 | Assert.Equal(4, errors); | ||
| 87 | |||
| 88 | var actualLines = UnformattedDocumentLines(document); | ||
| 89 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/ExePackageFixture.cs b/src/wix/test/WixToolsetTest.Converters/ExePackageFixture.cs new file mode 100644 index 00000000..0ee8d065 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/ExePackageFixture.cs | |||
| @@ -0,0 +1,49 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class ExePackageFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void CanConvertExePackageCommandToArguments() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <PackageGroup Id='exe'>", | ||
| 21 | " <ExePackage InstallCommand='-install' RepairCommand='-repair' UninstallCommand='-uninstall' SourceFile='test.exe' />", | ||
| 22 | " </PackageGroup>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = new[] | ||
| 27 | { | ||
| 28 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 29 | " <Fragment>", | ||
| 30 | " <PackageGroup Id=\"exe\">", | ||
| 31 | " <ExePackage SourceFile=\"test.exe\" InstallArguments=\"-install\" RepairArguments=\"-repair\" UninstallArguments=\"-uninstall\" />", | ||
| 32 | " </PackageGroup>", | ||
| 33 | " </Fragment>", | ||
| 34 | "</Wix>" | ||
| 35 | }; | ||
| 36 | |||
| 37 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 38 | |||
| 39 | var messaging = new MockMessaging(); | ||
| 40 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 41 | |||
| 42 | var errors = converter.ConvertDocument(document); | ||
| 43 | Assert.Equal(3, errors); | ||
| 44 | |||
| 45 | var actualLines = UnformattedDocumentLines(document); | ||
| 46 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/FeatureFixture.cs b/src/wix/test/WixToolsetTest.Converters/FeatureFixture.cs new file mode 100644 index 00000000..1df94a81 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/FeatureFixture.cs | |||
| @@ -0,0 +1,110 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class FeatureFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixAllowAttributes() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <Feature Absent='allow' AllowAdvertise='system' />", | ||
| 22 | " </Fragment>", | ||
| 23 | "</Wix>"); | ||
| 24 | |||
| 25 | var expected = new[] | ||
| 26 | { | ||
| 27 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 28 | " <Fragment>", | ||
| 29 | " <Feature AllowAdvertise=\"yes\" />", | ||
| 30 | " </Fragment>", | ||
| 31 | "</Wix>" | ||
| 32 | }; | ||
| 33 | |||
| 34 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 35 | |||
| 36 | var messaging = new MockMessaging(); | ||
| 37 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 38 | |||
| 39 | var errors = converter.ConvertDocument(document); | ||
| 40 | Assert.Equal(4, errors); | ||
| 41 | |||
| 42 | var actualLines = UnformattedDocumentLines(document); | ||
| 43 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 44 | } | ||
| 45 | |||
| 46 | [Fact] | ||
| 47 | public void FixDisallowAttributes() | ||
| 48 | { | ||
| 49 | var parse = String.Join(Environment.NewLine, | ||
| 50 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 51 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 52 | " <Fragment>", | ||
| 53 | " <Feature Absent='disallow' AllowAdvertise='no' />", | ||
| 54 | " </Fragment>", | ||
| 55 | "</Wix>"); | ||
| 56 | |||
| 57 | var expected = new[] | ||
| 58 | { | ||
| 59 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 60 | " <Fragment>", | ||
| 61 | " <Feature AllowAdvertise=\"no\" AllowAbsent=\"no\" />", | ||
| 62 | " </Fragment>", | ||
| 63 | "</Wix>" | ||
| 64 | }; | ||
| 65 | |||
| 66 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 67 | |||
| 68 | var messaging = new MockMessaging(); | ||
| 69 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 70 | |||
| 71 | var errors = converter.ConvertDocument(document); | ||
| 72 | Assert.Equal(3, errors); | ||
| 73 | |||
| 74 | var actualLines = UnformattedDocumentLines(document); | ||
| 75 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 76 | } | ||
| 77 | |||
| 78 | [Fact] | ||
| 79 | public void RemoveDeprecatedAllowAdvertiseAttributes() | ||
| 80 | { | ||
| 81 | var parse = String.Join(Environment.NewLine, | ||
| 82 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 83 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 84 | " <Fragment>", | ||
| 85 | " <Feature AllowAdvertise='disallow' />", | ||
| 86 | " </Fragment>", | ||
| 87 | "</Wix>"); | ||
| 88 | |||
| 89 | var expected = new[] | ||
| 90 | { | ||
| 91 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 92 | " <Fragment>", | ||
| 93 | " <Feature />", | ||
| 94 | " </Fragment>", | ||
| 95 | "</Wix>" | ||
| 96 | }; | ||
| 97 | |||
| 98 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 99 | |||
| 100 | var messaging = new MockMessaging(); | ||
| 101 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 102 | |||
| 103 | var errors = converter.ConvertDocument(document); | ||
| 104 | Assert.Equal(3, errors); | ||
| 105 | |||
| 106 | var actualLines = UnformattedDocumentLines(document); | ||
| 107 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/FirewallExtensionFixture.cs b/src/wix/test/WixToolsetTest.Converters/FirewallExtensionFixture.cs new file mode 100644 index 00000000..a101019b --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/FirewallExtensionFixture.cs | |||
| @@ -0,0 +1,47 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class FirewallExtensionFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixRemoteAddressValue() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:fw='http://schemas.microsoft.com/wix/FirewallExtension'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <fw:RemoteAddress>", | ||
| 21 | " 127.0.0.1", | ||
| 22 | " </fw:RemoteAddress>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = new[] | ||
| 27 | { | ||
| 28 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:fw=\"http://wixtoolset.org/schemas/v4/wxs/firewall\">", | ||
| 29 | " <Fragment>", | ||
| 30 | " <fw:RemoteAddress Value=\"127.0.0.1\" />", | ||
| 31 | " </Fragment>", | ||
| 32 | "</Wix>" | ||
| 33 | }; | ||
| 34 | |||
| 35 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 36 | |||
| 37 | var messaging = new MockMessaging(); | ||
| 38 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 39 | |||
| 40 | var errors = converter.ConvertDocument(document); | ||
| 41 | Assert.Equal(3, errors); | ||
| 42 | |||
| 43 | var actualLines = UnformattedDocumentLines(document); | ||
| 44 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/FormatFixture.cs b/src/wix/test/WixToolsetTest.Converters/FormatFixture.cs new file mode 100644 index 00000000..739fba66 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/FormatFixture.cs | |||
| @@ -0,0 +1,117 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixToolset.Converters; | ||
| 8 | using WixToolsetTest.Converters.Mocks; | ||
| 9 | using Xunit; | ||
| 10 | |||
| 11 | public class FormatFixture : BaseConverterFixture | ||
| 12 | { | ||
| 13 | [Fact] | ||
| 14 | public void CanFixWhitespace() | ||
| 15 | { | ||
| 16 | var parse = String.Join(Environment.NewLine, | ||
| 17 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 18 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <Property Id='Prop'", | ||
| 21 | " Value='Val'>", | ||
| 22 | " </Property>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = String.Join(Environment.NewLine, | ||
| 27 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 28 | " <Fragment>", | ||
| 29 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 30 | " </Fragment>", | ||
| 31 | "</Wix>"); | ||
| 32 | |||
| 33 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 34 | |||
| 35 | var messaging = new MockMessaging(); | ||
| 36 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 37 | |||
| 38 | var errors = converter.FormatDocument(document); | ||
| 39 | |||
| 40 | var actual = UnformattedDocumentString(document); | ||
| 41 | |||
| 42 | Assert.Equal(expected, actual); | ||
| 43 | Assert.Equal(5, errors); | ||
| 44 | } | ||
| 45 | |||
| 46 | [Fact] | ||
| 47 | public void CanPreserveNewLines() | ||
| 48 | { | ||
| 49 | var parse = String.Join(Environment.NewLine, | ||
| 50 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 51 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 52 | " <Fragment>", | ||
| 53 | "", | ||
| 54 | " <Property Id='Prop' Value='Val' />", | ||
| 55 | "", | ||
| 56 | " </Fragment>", | ||
| 57 | "</Wix>"); | ||
| 58 | |||
| 59 | var expected = String.Join(Environment.NewLine, | ||
| 60 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 61 | " <Fragment>", | ||
| 62 | "", | ||
| 63 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 64 | "", | ||
| 65 | " </Fragment>", | ||
| 66 | "</Wix>"); | ||
| 67 | |||
| 68 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 69 | |||
| 70 | var messaging = new MockMessaging(); | ||
| 71 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 72 | |||
| 73 | var conversions = converter.FormatDocument(document); | ||
| 74 | |||
| 75 | var actual = UnformattedDocumentString(document); | ||
| 76 | |||
| 77 | Assert.Equal(expected, actual); | ||
| 78 | Assert.Equal(4, conversions); | ||
| 79 | } | ||
| 80 | |||
| 81 | [Fact] | ||
| 82 | public void CanFormatWithNewLineAtEndOfFile() | ||
| 83 | { | ||
| 84 | var parse = String.Join(Environment.NewLine, | ||
| 85 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 86 | " <Fragment>", | ||
| 87 | "", | ||
| 88 | " <Property Id='Prop' Value='Val' />", | ||
| 89 | "", | ||
| 90 | " </Fragment>", | ||
| 91 | "</Wix>", | ||
| 92 | ""); | ||
| 93 | |||
| 94 | var expected = String.Join(Environment.NewLine, | ||
| 95 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 96 | " <Fragment>", | ||
| 97 | "", | ||
| 98 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 99 | "", | ||
| 100 | " </Fragment>", | ||
| 101 | "</Wix>", | ||
| 102 | ""); | ||
| 103 | |||
| 104 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 105 | |||
| 106 | var messaging = new MockMessaging(); | ||
| 107 | var converter = new WixConverter(messaging, 4, null, null); | ||
| 108 | |||
| 109 | var conversions = converter.FormatDocument(document); | ||
| 110 | |||
| 111 | var actual = UnformattedDocumentString(document); | ||
| 112 | |||
| 113 | Assert.Equal(expected, actual); | ||
| 114 | Assert.Equal(3, conversions); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/IncludeFixture.cs b/src/wix/test/WixToolsetTest.Converters/IncludeFixture.cs new file mode 100644 index 00000000..2fd8244f --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/IncludeFixture.cs | |||
| @@ -0,0 +1,68 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class IncludeFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void EnsureNamespace() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Include>", | ||
| 19 | " <Fragment />", | ||
| 20 | "</Include>"); | ||
| 21 | |||
| 22 | var expected = new[] | ||
| 23 | { | ||
| 24 | "<Include xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 25 | " <Fragment />", | ||
| 26 | "</Include>" | ||
| 27 | }; | ||
| 28 | |||
| 29 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 30 | |||
| 31 | var messaging = new MockMessaging(); | ||
| 32 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 33 | |||
| 34 | var errors = converter.ConvertDocument(document); | ||
| 35 | Assert.Equal(1, errors); | ||
| 36 | |||
| 37 | var actualLines = UnformattedDocumentLines(document); | ||
| 38 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 39 | } | ||
| 40 | |||
| 41 | [Fact] | ||
| 42 | public void FixNamespace() | ||
| 43 | { | ||
| 44 | var parse = String.Join(Environment.NewLine, | ||
| 45 | "<Include xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 46 | " <Fragment />", | ||
| 47 | "</Include>"); | ||
| 48 | |||
| 49 | var expected = new[] | ||
| 50 | { | ||
| 51 | "<Include xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 52 | " <Fragment />", | ||
| 53 | "</Include>" | ||
| 54 | }; | ||
| 55 | |||
| 56 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 57 | |||
| 58 | var messaging = new MockMessaging(); | ||
| 59 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 60 | |||
| 61 | var errors = converter.ConvertDocument(document); | ||
| 62 | Assert.Equal(1, errors); | ||
| 63 | |||
| 64 | var actualLines = UnformattedDocumentLines(document); | ||
| 65 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/Mocks/MockCoreServiceProvider.cs b/src/wix/test/WixToolsetTest.Converters/Mocks/MockCoreServiceProvider.cs new file mode 100644 index 00000000..b6bb8a40 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/Mocks/MockCoreServiceProvider.cs | |||
| @@ -0,0 +1,51 @@ | |||
| 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.Converters.Mocks | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using WixToolset.Extensibility.Services; | ||
| 8 | |||
| 9 | public class MockCoreServiceProvider : IWixToolsetCoreServiceProvider | ||
| 10 | { | ||
| 11 | public Dictionary<Type, Func<IWixToolsetCoreServiceProvider, Dictionary<Type, object>, object>> CreationFunctions { get; } = new Dictionary<Type, Func<IWixToolsetCoreServiceProvider, Dictionary<Type, object>, object>>(); | ||
| 12 | |||
| 13 | public Dictionary<Type, object> Singletons { get; } = new Dictionary<Type, object>() | ||
| 14 | { | ||
| 15 | { typeof(IMessaging), new MockMessaging() } | ||
| 16 | }; | ||
| 17 | |||
| 18 | public void AddService(Type serviceType, Func<IWixToolsetCoreServiceProvider, Dictionary<Type, object>, object> creationFunction) => this.CreationFunctions.Add(serviceType, creationFunction); | ||
| 19 | |||
| 20 | public void AddService<T>(Func<IWixToolsetCoreServiceProvider, Dictionary<Type, object>, T> creationFunction) where T : class => this.AddService(typeof(T), creationFunction); | ||
| 21 | |||
| 22 | public T GetService<T>() where T : class => this.TryGetService(typeof(T), out var obj) ? (T)obj : null; | ||
| 23 | |||
| 24 | public object GetService(Type serviceType) => this.TryGetService(serviceType, out var service) ? service : null; | ||
| 25 | |||
| 26 | public bool TryGetService(Type serviceType, out object service) | ||
| 27 | { | ||
| 28 | if (!this.Singletons.TryGetValue(serviceType, out service)) | ||
| 29 | { | ||
| 30 | if (this.CreationFunctions.TryGetValue(serviceType, out var creationFunction)) | ||
| 31 | { | ||
| 32 | service = creationFunction(this, this.Singletons); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return service != null; | ||
| 37 | } | ||
| 38 | |||
| 39 | public bool TryGetService<T>(out T service) where T : class | ||
| 40 | { | ||
| 41 | service = null; | ||
| 42 | |||
| 43 | if (this.TryGetService(typeof(T), out var obj)) | ||
| 44 | { | ||
| 45 | service = (T)obj; | ||
| 46 | } | ||
| 47 | |||
| 48 | return service != null; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/Mocks/MockMessaging.cs b/src/wix/test/WixToolsetTest.Converters/Mocks/MockMessaging.cs new file mode 100644 index 00000000..77821a1c --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/Mocks/MockMessaging.cs | |||
| @@ -0,0 +1,39 @@ | |||
| 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.Converters.Mocks | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using WixToolset.Data; | ||
| 8 | using WixToolset.Extensibility; | ||
| 9 | using WixToolset.Extensibility.Services; | ||
| 10 | |||
| 11 | public class MockMessaging : IMessaging | ||
| 12 | { | ||
| 13 | public List<Message> Messages { get; } = new List<Message>(); | ||
| 14 | |||
| 15 | public bool EncounteredError { get; private set; } | ||
| 16 | |||
| 17 | public int LastErrorNumber { get; } | ||
| 18 | |||
| 19 | public bool ShowVerboseMessages { get; set; } | ||
| 20 | |||
| 21 | public bool SuppressAllWarnings { get; set; } | ||
| 22 | |||
| 23 | public bool WarningsAsError { get; set; } | ||
| 24 | |||
| 25 | public void ElevateWarningMessage(int warningNumber) => throw new NotImplementedException(); | ||
| 26 | |||
| 27 | public void SetListener(IMessageListener listener) => throw new NotImplementedException(); | ||
| 28 | |||
| 29 | public void SuppressWarningMessage(int warningNumber) => throw new NotImplementedException(); | ||
| 30 | |||
| 31 | public void Write(Message message) | ||
| 32 | { | ||
| 33 | this.Messages.Add(message); | ||
| 34 | this.EncounteredError |= message.Level == MessageLevel.Error; | ||
| 35 | } | ||
| 36 | |||
| 37 | public void Write(string message, bool verbose = false) => throw new NotImplementedException(); | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs b/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs new file mode 100644 index 00000000..e01b9789 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs | |||
| @@ -0,0 +1,278 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Xml.Linq; | ||
| 8 | using WixBuildTools.TestSupport; | ||
| 9 | using WixToolset.Converters; | ||
| 10 | using WixToolset.Core.TestPackage; | ||
| 11 | using WixToolsetTest.Converters.Mocks; | ||
| 12 | using Xunit; | ||
| 13 | |||
| 14 | public class ProductPackageFixture : BaseConverterFixture | ||
| 15 | { | ||
| 16 | [Fact] | ||
| 17 | public void FixesCompressedWhenYes() | ||
| 18 | { | ||
| 19 | var parse = String.Join(Environment.NewLine, | ||
| 20 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 21 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 22 | " <Product>", | ||
| 23 | " <Package Compressed='yes' />", | ||
| 24 | " </Product>", | ||
| 25 | "</Wix>"); | ||
| 26 | |||
| 27 | var expected = new[] | ||
| 28 | { | ||
| 29 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 30 | " <Package>", | ||
| 31 | " ", | ||
| 32 | " </Package>", | ||
| 33 | "</Wix>" | ||
| 34 | }; | ||
| 35 | |||
| 36 | AssertSuccess(parse, 4, expected); | ||
| 37 | } | ||
| 38 | |||
| 39 | [Fact] | ||
| 40 | public void FixesCompressedWhenNo() | ||
| 41 | { | ||
| 42 | var parse = String.Join(Environment.NewLine, | ||
| 43 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 44 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 45 | " <Product>", | ||
| 46 | " <Package Compressed='no' />", | ||
| 47 | " </Product>", | ||
| 48 | "</Wix>"); | ||
| 49 | |||
| 50 | var expected = new[] | ||
| 51 | { | ||
| 52 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 53 | " <Package Compressed=\"no\">", | ||
| 54 | " ", | ||
| 55 | " </Package>", | ||
| 56 | "</Wix>" | ||
| 57 | }; | ||
| 58 | |||
| 59 | AssertSuccess(parse, 4, expected); | ||
| 60 | } | ||
| 61 | |||
| 62 | [Fact] | ||
| 63 | public void FixesCompressedWhenOmitted() | ||
| 64 | { | ||
| 65 | var parse = String.Join(Environment.NewLine, | ||
| 66 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 67 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 68 | " <Product>", | ||
| 69 | " <Package />", | ||
| 70 | " </Product>", | ||
| 71 | "</Wix>"); | ||
| 72 | |||
| 73 | var expected = new[] | ||
| 74 | { | ||
| 75 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 76 | " <Package Compressed=\"no\">", | ||
| 77 | " ", | ||
| 78 | " </Package>", | ||
| 79 | "</Wix>" | ||
| 80 | }; | ||
| 81 | |||
| 82 | AssertSuccess(parse, 4, expected); | ||
| 83 | } | ||
| 84 | |||
| 85 | private static void AssertSuccess(string input, int expectedErrorCount, string[] expected) | ||
| 86 | { | ||
| 87 | var document = XDocument.Parse(input, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 88 | |||
| 89 | var messaging = new MockMessaging(); | ||
| 90 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 91 | |||
| 92 | var errors = converter.ConvertDocument(document); | ||
| 93 | Assert.Equal(expectedErrorCount, errors); | ||
| 94 | |||
| 95 | var actualLines = UnformattedDocumentLines(document); | ||
| 96 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 97 | } | ||
| 98 | |||
| 99 | [Fact] | ||
| 100 | public void FixesInstallerVersion() | ||
| 101 | { | ||
| 102 | var parse = String.Join(Environment.NewLine, | ||
| 103 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 104 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 105 | " <Product>", | ||
| 106 | " <Package InstallerVersion='666' />", | ||
| 107 | " </Product>", | ||
| 108 | "</Wix>"); | ||
| 109 | |||
| 110 | var expected = new[] | ||
| 111 | { | ||
| 112 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 113 | " <Package Compressed=\"no\" InstallerVersion=\"666\">", | ||
| 114 | " ", | ||
| 115 | " </Package>", | ||
| 116 | "</Wix>" | ||
| 117 | }; | ||
| 118 | |||
| 119 | AssertSuccess(parse, 3, expected); | ||
| 120 | } | ||
| 121 | |||
| 122 | [Fact] | ||
| 123 | public void FixesDefaultInstallerVersion() | ||
| 124 | { | ||
| 125 | var parse = String.Join(Environment.NewLine, | ||
| 126 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 127 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 128 | " <Product>", | ||
| 129 | " <Package InstallerVersion='500' />", | ||
| 130 | " </Product>", | ||
| 131 | "</Wix>"); | ||
| 132 | |||
| 133 | var expected = new[] | ||
| 134 | { | ||
| 135 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 136 | " <Package Compressed=\"no\">", | ||
| 137 | " ", | ||
| 138 | " </Package>", | ||
| 139 | "</Wix>" | ||
| 140 | }; | ||
| 141 | |||
| 142 | AssertSuccess(parse, 3, expected); | ||
| 143 | } | ||
| 144 | |||
| 145 | [Fact] | ||
| 146 | public void FixesImplicitInstallerVersion() | ||
| 147 | { | ||
| 148 | var parse = String.Join(Environment.NewLine, | ||
| 149 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 150 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 151 | " <Product>", | ||
| 152 | " <Package />", | ||
| 153 | " </Product>", | ||
| 154 | "</Wix>"); | ||
| 155 | |||
| 156 | var expected = new[] | ||
| 157 | { | ||
| 158 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 159 | " <Package Compressed=\"no\">", | ||
| 160 | " ", | ||
| 161 | " </Package>", | ||
| 162 | "</Wix>" | ||
| 163 | }; | ||
| 164 | |||
| 165 | AssertSuccess(parse, 4, expected); | ||
| 166 | } | ||
| 167 | |||
| 168 | [Fact] | ||
| 169 | public void FixesNonDefaultInstallerVersion() | ||
| 170 | { | ||
| 171 | var parse = String.Join(Environment.NewLine, | ||
| 172 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 173 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 174 | " <Product>", | ||
| 175 | " <Package InstallerVersion='200' />", | ||
| 176 | " </Product>", | ||
| 177 | "</Wix>"); | ||
| 178 | |||
| 179 | var expected = new[] | ||
| 180 | { | ||
| 181 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 182 | " <Package Compressed=\"no\" InstallerVersion=\"200\">", | ||
| 183 | " ", | ||
| 184 | " </Package>", | ||
| 185 | "</Wix>" | ||
| 186 | }; | ||
| 187 | |||
| 188 | AssertSuccess(parse, 3, expected); | ||
| 189 | } | ||
| 190 | |||
| 191 | [Fact] | ||
| 192 | public void FixesLimitedInstallerPrivileges() | ||
| 193 | { | ||
| 194 | var parse = String.Join(Environment.NewLine, | ||
| 195 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 196 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 197 | " <Product>", | ||
| 198 | " <Package InstallPrivileges='limited' />", | ||
| 199 | " </Product>", | ||
| 200 | "</Wix>"); | ||
| 201 | |||
| 202 | var expected = new[] | ||
| 203 | { | ||
| 204 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 205 | " <Package Compressed=\"no\" Scope=\"perUser\">", | ||
| 206 | " ", | ||
| 207 | " </Package>", | ||
| 208 | "</Wix>" | ||
| 209 | }; | ||
| 210 | |||
| 211 | AssertSuccess(parse, 4, expected); | ||
| 212 | } | ||
| 213 | |||
| 214 | [Fact] | ||
| 215 | public void FixesElevatedInstallerPrivileges() | ||
| 216 | { | ||
| 217 | var parse = String.Join(Environment.NewLine, | ||
| 218 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 219 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 220 | " <Product>", | ||
| 221 | " <Package InstallPrivileges='elevated' />", | ||
| 222 | " <Property Id='ALLUSERS' Value='1' />", | ||
| 223 | " </Product>", | ||
| 224 | "</Wix>"); | ||
| 225 | |||
| 226 | var expected = new[] | ||
| 227 | { | ||
| 228 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 229 | " <Package Compressed=\"no\">", | ||
| 230 | " ", | ||
| 231 | " ", | ||
| 232 | " </Package>", | ||
| 233 | "</Wix>" | ||
| 234 | }; | ||
| 235 | |||
| 236 | AssertSuccess(parse, 4, expected); | ||
| 237 | } | ||
| 238 | |||
| 239 | [Fact] | ||
| 240 | public void CanDecompileAndRecompile() | ||
| 241 | { | ||
| 242 | using (var fs = new DisposableFileSystem()) | ||
| 243 | { | ||
| 244 | var baseFolder = fs.GetFolder(); | ||
| 245 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
| 246 | var decompiledWxsPath = Path.Combine(baseFolder, "TypicalV3.wxs"); | ||
| 247 | |||
| 248 | var folder = TestData.Get(@"TestData\PackageSummaryInformation"); | ||
| 249 | var v3msiPath = Path.Combine(folder, "TypicalV3.msi"); | ||
| 250 | var result = WixRunner.Execute(new[] | ||
| 251 | { | ||
| 252 | "decompile", v3msiPath, | ||
| 253 | "-intermediateFolder", intermediateFolder, | ||
| 254 | "-o", decompiledWxsPath | ||
| 255 | }); | ||
| 256 | |||
| 257 | result.AssertSuccess(); | ||
| 258 | |||
| 259 | var v4msiPath = Path.Combine(intermediateFolder, "TypicalV4.msi"); | ||
| 260 | result = WixRunner.Execute(new[] | ||
| 261 | { | ||
| 262 | "build", decompiledWxsPath, | ||
| 263 | "-arch", "x64", | ||
| 264 | "-intermediateFolder", intermediateFolder, | ||
| 265 | "-o", v4msiPath | ||
| 266 | }); | ||
| 267 | |||
| 268 | result.AssertSuccess(); | ||
| 269 | |||
| 270 | Assert.True(File.Exists(v4msiPath)); | ||
| 271 | |||
| 272 | var v3results = Query.QueryDatabase(v3msiPath, new[] { "_SummaryInformation", "Property" }); | ||
| 273 | var v4results = Query.QueryDatabase(v4msiPath, new[] { "_SummaryInformation", "Property" }); | ||
| 274 | WixAssert.CompareLineByLine(v3results, v4results); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/PropertyFixture.cs b/src/wix/test/WixToolsetTest.Converters/PropertyFixture.cs new file mode 100644 index 00000000..e50a6518 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/PropertyFixture.cs | |||
| @@ -0,0 +1,108 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixToolset.Converters; | ||
| 8 | using WixToolsetTest.Converters.Mocks; | ||
| 9 | using Xunit; | ||
| 10 | |||
| 11 | public class PropertyFixture : BaseConverterFixture | ||
| 12 | { | ||
| 13 | [Fact] | ||
| 14 | public void CanFixCdataWhitespace() | ||
| 15 | { | ||
| 16 | var parse = String.Join(Environment.NewLine, | ||
| 17 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 18 | " <Fragment>", | ||
| 19 | " <Property Id='Prop'>", | ||
| 20 | " <![CDATA[1<2]]>", | ||
| 21 | " </Property>", | ||
| 22 | " </Fragment>", | ||
| 23 | "</Wix>"); | ||
| 24 | |||
| 25 | var expected = String.Join(Environment.NewLine, | ||
| 26 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 27 | " <Fragment>", | ||
| 28 | " <Property Id=\"Prop\" Value=\"1<2\" />", | ||
| 29 | " </Fragment>", | ||
| 30 | "</Wix>"); | ||
| 31 | |||
| 32 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 33 | |||
| 34 | var messaging = new MockMessaging(); | ||
| 35 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 36 | |||
| 37 | var errors = converter.ConvertDocument(document); | ||
| 38 | |||
| 39 | var actual = UnformattedDocumentString(document); | ||
| 40 | |||
| 41 | Assert.Equal(expected, actual); | ||
| 42 | Assert.Equal(1, errors); | ||
| 43 | } | ||
| 44 | |||
| 45 | [Fact] | ||
| 46 | public void CanFixCdataWithWhitespace() | ||
| 47 | { | ||
| 48 | var parse = String.Join(Environment.NewLine, | ||
| 49 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 50 | " <Fragment>", | ||
| 51 | " <Property Id='Prop'>", | ||
| 52 | " <![CDATA[", | ||
| 53 | " 1<2", | ||
| 54 | " ]]>", | ||
| 55 | " </Property>", | ||
| 56 | " </Fragment>", | ||
| 57 | "</Wix>"); | ||
| 58 | |||
| 59 | var expected = String.Join(Environment.NewLine, | ||
| 60 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 61 | " <Fragment>", | ||
| 62 | " <Property Id=\"Prop\" Value=\"1<2\" />", | ||
| 63 | " </Fragment>", | ||
| 64 | "</Wix>"); | ||
| 65 | |||
| 66 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 67 | |||
| 68 | var messaging = new MockMessaging(); | ||
| 69 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 70 | |||
| 71 | var errors = converter.ConvertDocument(document); | ||
| 72 | |||
| 73 | var actual = UnformattedDocumentString(document); | ||
| 74 | |||
| 75 | Assert.Equal(expected, actual); | ||
| 76 | Assert.Equal(1, errors); | ||
| 77 | } | ||
| 78 | |||
| 79 | [Fact] | ||
| 80 | public void CanKeepCdataWithOnlyWhitespace() | ||
| 81 | { | ||
| 82 | var parse = String.Join(Environment.NewLine, | ||
| 83 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 84 | " <Fragment>", | ||
| 85 | " <Property Id='Prop'><![CDATA[ ]]></Property>", | ||
| 86 | " </Fragment>", | ||
| 87 | "</Wix>"); | ||
| 88 | |||
| 89 | var expected = String.Join(Environment.NewLine, | ||
| 90 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 91 | " <Fragment>", | ||
| 92 | " <Property Id=\"Prop\" Value=\" \" />", | ||
| 93 | " </Fragment>", | ||
| 94 | "</Wix>"); | ||
| 95 | |||
| 96 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 97 | |||
| 98 | var messaging = new MockMessaging(); | ||
| 99 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 100 | var errors = converter.ConvertDocument(document); | ||
| 101 | |||
| 102 | var actual = UnformattedDocumentString(document); | ||
| 103 | |||
| 104 | Assert.Equal(expected, actual); | ||
| 105 | Assert.Equal(1, errors); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/RegistryFixture.cs b/src/wix/test/WixToolsetTest.Converters/RegistryFixture.cs new file mode 100644 index 00000000..405f5416 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/RegistryFixture.cs | |||
| @@ -0,0 +1,54 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class RegistryFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixRegistryKeyAction() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <Component>", | ||
| 22 | " <RegistryKey Id='ExampleRegistryKey1' Action='create' Root='HKLM' Key='TestRegistryKey1' />", | ||
| 23 | " <RegistryKey Id='ExampleRegistryKey2' Action='createAndRemoveOnUninstall' Root='HKLM' Key='TestRegistryKey2' />", | ||
| 24 | " <RegistryKey Id='ExampleRegistryKey3' Action='none' Root='HKLM' Key='TestRegistryKey3' />", | ||
| 25 | " </Component>", | ||
| 26 | " </Fragment>", | ||
| 27 | "</Wix>"); | ||
| 28 | |||
| 29 | var expected = new[] | ||
| 30 | { | ||
| 31 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 32 | " <Fragment>", | ||
| 33 | " <Component>", | ||
| 34 | " <RegistryKey Id=\"ExampleRegistryKey1\" Root=\"HKLM\" Key=\"TestRegistryKey1\" ForceCreateOnInstall=\"yes\" />", | ||
| 35 | " <RegistryKey Id=\"ExampleRegistryKey2\" Root=\"HKLM\" Key=\"TestRegistryKey2\" ForceCreateOnInstall=\"yes\" ForceDeleteOnUninstall=\"yes\" />", | ||
| 36 | " <RegistryKey Id=\"ExampleRegistryKey3\" Root=\"HKLM\" Key=\"TestRegistryKey3\" />", | ||
| 37 | " </Component>", | ||
| 38 | " </Fragment>", | ||
| 39 | "</Wix>" | ||
| 40 | }; | ||
| 41 | |||
| 42 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 43 | |||
| 44 | var messaging = new MockMessaging(); | ||
| 45 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 46 | |||
| 47 | var errors = converter.ConvertDocument(document); | ||
| 48 | Assert.Equal(5, errors); | ||
| 49 | |||
| 50 | var actualLines = UnformattedDocumentLines(document); | ||
| 51 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/RemotePayloadFixture.cs b/src/wix/test/WixToolsetTest.Converters/RemotePayloadFixture.cs new file mode 100644 index 00000000..b2640e13 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/RemotePayloadFixture.cs | |||
| @@ -0,0 +1,104 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class RemotePayloadFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void CanConvertExePackageRemotePayload() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <PackageGroup Id='exe'>", | ||
| 21 | " <ExePackage Name='example.exe' DownloadUrl='example.com'>", | ||
| 22 | " <RemotePayload", | ||
| 23 | " Description='Microsoft ASP.NET Core 3.1.8 - Shared Framework'", | ||
| 24 | " Hash='61DC9EAA0C8968E48E13C5913ED202A2F8F94DBA'", | ||
| 25 | " CertificatePublicKey='3756E9BBF4461DCD0AA68E0D1FCFFA9CEA47AC18'", | ||
| 26 | " CertificateThumbprint='2485A7AFA98E178CB8F30C9838346B514AEA4769'", | ||
| 27 | " ProductName='Microsoft ASP.NET Core 3.1.8 - Shared Framework'", | ||
| 28 | " Size='7841880'", | ||
| 29 | " Version='3.1.8.20421' />", | ||
| 30 | " </ExePackage>", | ||
| 31 | " </PackageGroup>", | ||
| 32 | " </Fragment>", | ||
| 33 | "</Wix>"); | ||
| 34 | |||
| 35 | var expected = new[] | ||
| 36 | { | ||
| 37 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 38 | " <Fragment>", | ||
| 39 | " <PackageGroup Id=\"exe\">", | ||
| 40 | " <ExePackage>", | ||
| 41 | " <ExePackagePayload Description=\"Microsoft ASP.NET Core 3.1.8 - Shared Framework\" Hash=\"61DC9EAA0C8968E48E13C5913ED202A2F8F94DBA\" ProductName=\"Microsoft ASP.NET Core 3.1.8 - Shared Framework\" Size=\"7841880\" Version=\"3.1.8.20421\" Name=\"example.exe\" DownloadUrl=\"example.com\" />", | ||
| 42 | " </ExePackage>", | ||
| 43 | " </PackageGroup>", | ||
| 44 | " </Fragment>", | ||
| 45 | "</Wix>" | ||
| 46 | }; | ||
| 47 | |||
| 48 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 49 | |||
| 50 | var messaging = new MockMessaging(); | ||
| 51 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 52 | |||
| 53 | var errors = converter.ConvertDocument(document); | ||
| 54 | Assert.Equal(6, errors); | ||
| 55 | |||
| 56 | var actualLines = UnformattedDocumentLines(document); | ||
| 57 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 58 | } | ||
| 59 | |||
| 60 | [Fact] | ||
| 61 | public void CanConvertMsuPackageRemotePayload() | ||
| 62 | { | ||
| 63 | var parse = String.Join(Environment.NewLine, | ||
| 64 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 65 | " <Fragment>", | ||
| 66 | " <PackageGroup Id='msu'>", | ||
| 67 | " <MsuPackage Name='example.msu' DownloadUrl='example.com' Compressed='no'>", | ||
| 68 | " <RemotePayload", | ||
| 69 | " Description='msu description'", | ||
| 70 | " Hash='71DC9EAA0C8968E48E13C5913ED202A2F8F94DBB'", | ||
| 71 | " ProductName='msu product name'", | ||
| 72 | " Size='500'", | ||
| 73 | " Version='0.0.0.0' />", | ||
| 74 | " </MsuPackage>", | ||
| 75 | " </PackageGroup>", | ||
| 76 | " </Fragment>", | ||
| 77 | "</Wix>"); | ||
| 78 | |||
| 79 | var expected = new[] | ||
| 80 | { | ||
| 81 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 82 | " <Fragment>", | ||
| 83 | " <PackageGroup Id=\"msu\">", | ||
| 84 | " <MsuPackage>", | ||
| 85 | " <MsuPackagePayload Description=\"msu description\" Hash=\"71DC9EAA0C8968E48E13C5913ED202A2F8F94DBB\" ProductName=\"msu product name\" Size=\"500\" Version=\"0.0.0.0\" Name=\"example.msu\" DownloadUrl=\"example.com\" />", | ||
| 86 | " </MsuPackage>", | ||
| 87 | " </PackageGroup>", | ||
| 88 | " </Fragment>", | ||
| 89 | "</Wix>" | ||
| 90 | }; | ||
| 91 | |||
| 92 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 93 | |||
| 94 | var messaging = new MockMessaging(); | ||
| 95 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 96 | |||
| 97 | var errors = converter.ConvertDocument(document); | ||
| 98 | Assert.Equal(5, errors); | ||
| 99 | |||
| 100 | var actualLines = UnformattedDocumentLines(document); | ||
| 101 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/SequenceFixture.cs b/src/wix/test/WixToolsetTest.Converters/SequenceFixture.cs new file mode 100644 index 00000000..ec6c709b --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/SequenceFixture.cs | |||
| @@ -0,0 +1,49 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class SequenceFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixCondition() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <InstallUISequence>", | ||
| 21 | " <Custom Action='ExampleCA' After='InstallFiles'>NOT Installed</Custom>", | ||
| 22 | " </InstallUISequence>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = new[] | ||
| 27 | { | ||
| 28 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 29 | " <Fragment>", | ||
| 30 | " <InstallUISequence>", | ||
| 31 | " <Custom Action=\"ExampleCA\" After=\"InstallFiles\" Condition=\"NOT Installed\" />", | ||
| 32 | " </InstallUISequence>", | ||
| 33 | " </Fragment>", | ||
| 34 | "</Wix>" | ||
| 35 | }; | ||
| 36 | |||
| 37 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 38 | |||
| 39 | var messaging = new MockMessaging(); | ||
| 40 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 41 | |||
| 42 | var errors = converter.ConvertDocument(document); | ||
| 43 | Assert.Equal(2, errors); | ||
| 44 | |||
| 45 | var actualLines = UnformattedDocumentLines(document); | ||
| 46 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TagFixture.cs b/src/wix/test/WixToolsetTest.Converters/TagFixture.cs new file mode 100644 index 00000000..5e07c83b --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TagFixture.cs | |||
| @@ -0,0 +1,111 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class TagFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixTagExtension() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:tag='http://schemas.microsoft.com/wix/TagExtension'>", | ||
| 19 | " <Product>", | ||
| 20 | " <tag:Tag Regid='wixtoolset.org' InstallDirectory='InstallFolder' />", | ||
| 21 | " </Product>", | ||
| 22 | "</Wix>"); | ||
| 23 | |||
| 24 | var expected = new[] | ||
| 25 | { | ||
| 26 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 27 | " <Package>", | ||
| 28 | " <SoftwareTag Regid=\"wixtoolset.org\" InstallDirectory=\"InstallFolder\" />", | ||
| 29 | " </Package>", | ||
| 30 | "</Wix>" | ||
| 31 | }; | ||
| 32 | |||
| 33 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 34 | |||
| 35 | var messaging = new MockMessaging(); | ||
| 36 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 37 | |||
| 38 | var errors = converter.ConvertDocument(document); | ||
| 39 | Assert.Equal(4, errors); | ||
| 40 | |||
| 41 | var actualLines = UnformattedDocumentLines(document); | ||
| 42 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 43 | } | ||
| 44 | |||
| 45 | [Fact] | ||
| 46 | public void FixTagExtensionDeprecations() | ||
| 47 | { | ||
| 48 | var parse = String.Join(Environment.NewLine, | ||
| 49 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:tag='http://schemas.microsoft.com/wix/TagExtension'>", | ||
| 50 | " <Product>", | ||
| 51 | " <tag:Tag Regid='wixtoolset.org' InstallDirectory='InstallFolder' Licensed='true' Type='component' Win64='yes' />", | ||
| 52 | " </Product>", | ||
| 53 | "</Wix>"); | ||
| 54 | |||
| 55 | var expected = new[] | ||
| 56 | { | ||
| 57 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 58 | " <Package>", | ||
| 59 | " <SoftwareTag Regid=\"wixtoolset.org\" InstallDirectory=\"InstallFolder\" Bitness=\"always64\" />", | ||
| 60 | " </Package>", | ||
| 61 | "</Wix>" | ||
| 62 | }; | ||
| 63 | |||
| 64 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 65 | |||
| 66 | var messaging = new MockMessaging(); | ||
| 67 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 68 | |||
| 69 | var errors = converter.ConvertDocument(document); | ||
| 70 | Assert.Equal(7, errors); | ||
| 71 | |||
| 72 | var actualLines = UnformattedDocumentLines(document); | ||
| 73 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 74 | } | ||
| 75 | |||
| 76 | [Fact] | ||
| 77 | public void FixTagExtensionTagRef() | ||
| 78 | { | ||
| 79 | var parse = String.Join(Environment.NewLine, | ||
| 80 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:tag='http://schemas.microsoft.com/wix/TagExtension'>", | ||
| 81 | " <Fragment>", | ||
| 82 | " <PatchFamily>", | ||
| 83 | " <tag:TagRef Regid='wixtoolset.org' />", | ||
| 84 | " </PatchFamily>", | ||
| 85 | " </Fragment>", | ||
| 86 | "</Wix>"); | ||
| 87 | |||
| 88 | var expected = new[] | ||
| 89 | { | ||
| 90 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 91 | " <Fragment>", | ||
| 92 | " <PatchFamily>", | ||
| 93 | " <SoftwareTagRef Regid=\"wixtoolset.org\" />", | ||
| 94 | " </PatchFamily>", | ||
| 95 | " </Fragment>", | ||
| 96 | "</Wix>" | ||
| 97 | }; | ||
| 98 | |||
| 99 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 100 | |||
| 101 | var messaging = new MockMessaging(); | ||
| 102 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 103 | |||
| 104 | var errors = converter.ConvertDocument(document); | ||
| 105 | Assert.Equal(3, errors); | ||
| 106 | |||
| 107 | var actualLines = UnformattedDocumentLines(document); | ||
| 108 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.msi b/src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.msi new file mode 100644 index 00000000..0d7e1b21 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.msi | |||
| Binary files differ | |||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.wxs new file mode 100644 index 00000000..8c5027b4 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.wxs | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | ||
| 3 | <?define Name = "Package and summary information testing" ?> | ||
| 4 | |||
| 5 | <Product Id="*" | ||
| 6 | Name="$(var.Name)" | ||
| 7 | Language="1033" | ||
| 8 | Version="1.0" | ||
| 9 | Manufacturer="Example Corporation" | ||
| 10 | UpgradeCode="D86CAC27-51EA-46BD-8105-C465109AFA74"> | ||
| 11 | |||
| 12 | <Package InstallerVersion="500" | ||
| 13 | Compressed="yes" | ||
| 14 | InstallScope="perMachine" | ||
| 15 | Platform="x64" | ||
| 16 | InstallPrivileges="elevated" /> | ||
| 17 | |||
| 18 | <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." /> | ||
| 19 | <MediaTemplate EmbedCab="yes" /> | ||
| 20 | |||
| 21 | <Feature Id="Feature" Title="Installer" Level="1"> | ||
| 22 | <ComponentGroupRef Id="ProductComponents" /> | ||
| 23 | </Feature> | ||
| 24 | |||
| 25 | <Directory Id="TARGETDIR" Name="SourceDir"> | ||
| 26 | <Directory Id="ProgramFiles64Folder"> | ||
| 27 | <Directory Id="INSTALLFOLDER" Name="$(var.Name)" /> | ||
| 28 | </Directory> | ||
| 29 | </Directory> | ||
| 30 | |||
| 31 | <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> | ||
| 32 | <Component> | ||
| 33 | <RegistryValue Root="HKLM" Key="SOFTWARE\Example\$(var.Name)" Name="Installed" Value="1" Type="integer" /> | ||
| 34 | </Component> | ||
| 35 | </ComponentGroup> | ||
| 36 | </Product> | ||
| 37 | </Wix> \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/PermissionEx/v3.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/PermissionEx/v3.wxs new file mode 100644 index 00000000..9a739052 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/PermissionEx/v3.wxs | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> | ||
| 2 | <Fragment> | ||
| 3 | <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> | ||
| 4 | <Component> | ||
| 5 | <File Source="example.txt"> | ||
| 6 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 7 | </File> | ||
| 8 | <CreateFolder> | ||
| 9 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 10 | </CreateFolder> | ||
| 11 | <ServiceInstall Name="testsvc" Type="ownProcess" Start="disabled" ErrorControl="normal"> | ||
| 12 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 13 | </ServiceInstall> | ||
| 14 | <Registry Action="createKey" Root="HKLM" Key="TestKey"> | ||
| 15 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 16 | </Registry> | ||
| 17 | <RegistryKey Id="ExampleRegistryKey" ForceCreateOnInstall="yes" Root="HKLM" Key="TestRegistryKey"> | ||
| 18 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 19 | </RegistryKey> | ||
| 20 | <RegistryValue Root="HKLM" Key="TestRegistryValueKey" Value="abc"> | ||
| 21 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 22 | </RegistryValue> | ||
| 23 | </Component> | ||
| 24 | </ComponentGroup> | ||
| 25 | </Fragment> | ||
| 26 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/PermissionEx/v4_expected.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/PermissionEx/v4_expected.wxs new file mode 100644 index 00000000..6bf3c1ea --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/PermissionEx/v4_expected.wxs | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> | ||
| 2 | <Fragment> | ||
| 3 | <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> | ||
| 4 | <Component> | ||
| 5 | <File Id="example.txt" Source="example.txt"> | ||
| 6 | <util:PermissionEx User="Everyone" GenericAll="yes" Inheritable="no" /> | ||
| 7 | </File> | ||
| 8 | <CreateFolder> | ||
| 9 | <util:PermissionEx User="Everyone" GenericAll="yes" /> | ||
| 10 | </CreateFolder> | ||
| 11 | <ServiceInstall Name="testsvc" Type="ownProcess" Start="disabled" ErrorControl="normal"> | ||
| 12 | <util:PermissionEx User="Everyone" GenericAll="yes" Inheritable="no" /> | ||
| 13 | </ServiceInstall> | ||
| 14 | <Registry Action="createKey" Root="HKLM" Key="TestKey"> | ||
| 15 | <util:PermissionEx User="Everyone" GenericAll="yes" Inheritable="no" /> | ||
| 16 | </Registry> | ||
| 17 | <RegistryKey Id="ExampleRegistryKey" ForceCreateOnInstall="yes" Root="HKLM" Key="TestRegistryKey"> | ||
| 18 | <util:PermissionEx User="Everyone" GenericAll="yes" Inheritable="no" /> | ||
| 19 | </RegistryKey> | ||
| 20 | <RegistryValue Root="HKLM" Key="TestRegistryValueKey" Value="abc"> | ||
| 21 | <util:PermissionEx User="Everyone" GenericAll="yes" Inheritable="no" /> | ||
| 22 | </RegistryValue> | ||
| 23 | </Component> | ||
| 24 | </ComponentGroup> | ||
| 25 | </Fragment> | ||
| 26 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs new file mode 100644 index 00000000..8188d900 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/ConvertedPreprocessor.wxs | |||
| @@ -0,0 +1,61 @@ | |||
| 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 | |||
| 4 | |||
| 5 | <?include WixVer.wxi ?> | ||
| 6 | |||
| 7 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> | ||
| 8 | <Package Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D" InstallerVersion="200"> | ||
| 9 | |||
| 10 | <SoftwareTag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 11 | |||
| 12 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 13 | |||
| 14 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 15 | |||
| 16 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 17 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 18 | <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> | ||
| 19 | </Component> | ||
| 20 | |||
| 21 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 22 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 23 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 24 | </RegistryKey> | ||
| 25 | </Component> | ||
| 26 | |||
| 27 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 28 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 29 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 30 | </RegistryKey> | ||
| 31 | </Component> | ||
| 32 | |||
| 33 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 34 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 35 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> | ||
| 36 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 37 | </RegistryKey> | ||
| 38 | |||
| 39 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 40 | </Component> | ||
| 41 | |||
| 42 | <Component Directory="BinFolder"> | ||
| 43 | <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico"> | ||
| 44 | <?include ComRegistration.wxi ?> | ||
| 45 | </File> | ||
| 46 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 47 | </Component> | ||
| 48 | |||
| 49 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 50 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 51 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 52 | <ComponentGroupRef Id="DocComponents" /> | ||
| 53 | </Feature> | ||
| 54 | |||
| 55 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 56 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 57 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 60 | </Package> | ||
| 61 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs new file mode 100644 index 00000000..2eb908c2 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/Preprocessor.wxs | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | <?include WixVer.wxi ?> | ||
| 7 | |||
| 8 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:swid="http://schemas.microsoft.com/wix/TagExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> | ||
| 9 | <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" | ||
| 10 | Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> | ||
| 11 | <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> | ||
| 12 | <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 13 | |||
| 14 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 15 | |||
| 16 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 17 | |||
| 18 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 19 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 20 | <File Source="LICENSE.TXT" /> | ||
| 21 | </Component> | ||
| 22 | |||
| 23 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 24 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 25 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 26 | </RegistryKey> | ||
| 27 | </Component> | ||
| 28 | |||
| 29 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 30 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 31 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 32 | </RegistryKey> | ||
| 33 | </Component> | ||
| 34 | |||
| 35 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 36 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 37 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> | ||
| 38 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 39 | </RegistryKey> | ||
| 40 | |||
| 41 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 42 | </Component> | ||
| 43 | |||
| 44 | <Component Directory="BinFolder"> | ||
| 45 | <File Source="common\wixtoolset.org.ico"> | ||
| 46 | <?include ComRegistration.wxi ?> | ||
| 47 | </File> | ||
| 48 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 49 | </Component> | ||
| 50 | |||
| 51 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 52 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 53 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 54 | <ComponentGroupRef Id="DocComponents" /> | ||
| 55 | </Feature> | ||
| 56 | |||
| 57 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 60 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 61 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 62 | </Product> | ||
| 63 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml b/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml new file mode 100644 index 00000000..9d3ad496 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/Preprocessor/wixcop.settings.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <?xml version="1.0"?> | ||
| 2 | <Settings> | ||
| 3 | <IgnoreErrors> | ||
| 4 | <Test Id="WhitespacePrecedingNodeWrong"/> | ||
| 5 | <Test Id="WhitespacePrecedingEndElementWrong"/> | ||
| 6 | </IgnoreErrors> | ||
| 7 | <ErrorsAsWarnings/> | ||
| 8 | <ExemptFiles/> | ||
| 9 | </Settings> \ No newline at end of file | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs new file mode 100644 index 00000000..b0fcf9c9 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec.bad/v3.wxs | |||
| @@ -0,0 +1,64 @@ | |||
| 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 | |||
| 4 | |||
| 5 | <?include WixVer.wxi ?> | ||
| 6 | |||
| 7 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:swid="http://schemas.microsoft.com/wix/TagExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> | ||
| 8 | <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" | ||
| 9 | Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> | ||
| 10 | <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> | ||
| 11 | <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 12 | |||
| 13 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 14 | |||
| 15 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 16 | |||
| 17 | <Property Id="QtExecCmdTimeout" Value="600000" /> | ||
| 18 | <CustomAction Id="InstallVSTemplateCommand" Property="QtExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> | ||
| 19 | <CustomAction Id="InstallVSTemplate" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="asyncWait" /> | ||
| 20 | |||
| 21 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 22 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 23 | <File Source="LICENSE.TXT" /> | ||
| 24 | </Component> | ||
| 25 | |||
| 26 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 27 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 28 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 29 | </RegistryKey> | ||
| 30 | </Component> | ||
| 31 | |||
| 32 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 33 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 34 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 35 | </RegistryKey> | ||
| 36 | </Component> | ||
| 37 | |||
| 38 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 39 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 40 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> | ||
| 41 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 42 | </RegistryKey> | ||
| 43 | |||
| 44 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 45 | </Component> | ||
| 46 | |||
| 47 | <Component Directory="BinFolder"> | ||
| 48 | <File Source="common\wixtoolset.org.ico" /> | ||
| 49 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 50 | </Component> | ||
| 51 | |||
| 52 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 53 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 54 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 55 | <ComponentGroupRef Id="DocComponents" /> | ||
| 56 | </Feature> | ||
| 57 | |||
| 58 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 60 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 61 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 62 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 63 | </Product> | ||
| 64 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs new file mode 100644 index 00000000..95d2f618 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec.bad/v4_expected.wxs | |||
| @@ -0,0 +1,63 @@ | |||
| 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 | |||
| 4 | |||
| 5 | <?include WixVer.wxi ?> | ||
| 6 | |||
| 7 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> | ||
| 8 | <Package Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D" InstallerVersion="200"> | ||
| 9 | |||
| 10 | <SoftwareTag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 11 | |||
| 12 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 13 | |||
| 14 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 15 | |||
| 16 | <Property Id="QtExecCmdTimeout" Value="600000" /> | ||
| 17 | <CustomAction Id="InstallVSTemplateCommand" Property="WixQuietExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> | ||
| 18 | <CustomAction Id="InstallVSTemplate" DllEntry="WixQuietExec" Return="asyncWait" BinaryRef="Wix4UtilCA_X86" /> | ||
| 19 | |||
| 20 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 21 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 22 | <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> | ||
| 23 | </Component> | ||
| 24 | |||
| 25 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 26 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 27 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 28 | </RegistryKey> | ||
| 29 | </Component> | ||
| 30 | |||
| 31 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 32 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 33 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 34 | </RegistryKey> | ||
| 35 | </Component> | ||
| 36 | |||
| 37 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 38 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 39 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> | ||
| 40 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 41 | </RegistryKey> | ||
| 42 | |||
| 43 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 44 | </Component> | ||
| 45 | |||
| 46 | <Component Directory="BinFolder"> | ||
| 47 | <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> | ||
| 48 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 49 | </Component> | ||
| 50 | |||
| 51 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 52 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 53 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 54 | <ComponentGroupRef Id="DocComponents" /> | ||
| 55 | </Feature> | ||
| 56 | |||
| 57 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 60 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 61 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 62 | </Package> | ||
| 63 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs new file mode 100644 index 00000000..8d81a758 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec/v3.wxs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | <?include WixVer.wxi ?> | ||
| 7 | |||
| 8 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:swid="http://schemas.microsoft.com/wix/TagExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> | ||
| 9 | <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" | ||
| 10 | Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> | ||
| 11 | <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> | ||
| 12 | <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 13 | |||
| 14 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 15 | |||
| 16 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 17 | |||
| 18 | <CustomAction Id="InstallVSTemplateCommand" Property="QtExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> | ||
| 19 | <CustomAction Id="InstallVSTemplate" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="asyncWait" /> | ||
| 20 | |||
| 21 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 22 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 23 | <File Source="LICENSE.TXT" /> | ||
| 24 | </Component> | ||
| 25 | |||
| 26 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 27 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 28 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 29 | </RegistryKey> | ||
| 30 | </Component> | ||
| 31 | |||
| 32 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 33 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 34 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 35 | </RegistryKey> | ||
| 36 | </Component> | ||
| 37 | |||
| 38 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 39 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 40 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> | ||
| 41 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 42 | </RegistryKey> | ||
| 43 | |||
| 44 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 45 | </Component> | ||
| 46 | |||
| 47 | <Component Directory="BinFolder"> | ||
| 48 | <File Source="common\wixtoolset.org.ico" /> | ||
| 49 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 50 | </Component> | ||
| 51 | |||
| 52 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 53 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 54 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 55 | <ComponentGroupRef Id="DocComponents" /> | ||
| 56 | </Feature> | ||
| 57 | |||
| 58 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 60 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 61 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 62 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 63 | </Product> | ||
| 64 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs new file mode 100644 index 00000000..f24d3f8f --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/QtExec/v4_expected.wxs | |||
| @@ -0,0 +1,62 @@ | |||
| 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 | |||
| 4 | |||
| 5 | <?include WixVer.wxi ?> | ||
| 6 | |||
| 7 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> | ||
| 8 | <Package Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D" InstallerVersion="200"> | ||
| 9 | |||
| 10 | <SoftwareTag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 11 | |||
| 12 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 13 | |||
| 14 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 15 | |||
| 16 | <CustomAction Id="InstallVSTemplateCommand" Property="WixQuietExecCmdLine" Value=""[VSENVPRODUCT80]\devenv.exe" /setup" /> | ||
| 17 | <CustomAction Id="InstallVSTemplate" DllEntry="WixQuietExec" Return="asyncWait" BinaryRef="Wix4UtilCA_X86" /> | ||
| 18 | |||
| 19 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 20 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 21 | <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> | ||
| 22 | </Component> | ||
| 23 | |||
| 24 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 25 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 26 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 27 | </RegistryKey> | ||
| 28 | </Component> | ||
| 29 | |||
| 30 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 31 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 32 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 33 | </RegistryKey> | ||
| 34 | </Component> | ||
| 35 | |||
| 36 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 37 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 38 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> | ||
| 39 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 40 | </RegistryKey> | ||
| 41 | |||
| 42 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 43 | </Component> | ||
| 44 | |||
| 45 | <Component Directory="BinFolder"> | ||
| 46 | <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> | ||
| 47 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 48 | </Component> | ||
| 49 | |||
| 50 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 51 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 52 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 53 | <ComponentGroupRef Id="DocComponents" /> | ||
| 54 | </Feature> | ||
| 55 | |||
| 56 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 57 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 60 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 61 | </Package> | ||
| 62 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs new file mode 100644 index 00000000..5bcdaf59 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/SingleFile/ConvertedSingleFile.wxs | |||
| @@ -0,0 +1,59 @@ | |||
| 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 | |||
| 4 | |||
| 5 | <?include WixVer.wxi ?> | ||
| 6 | |||
| 7 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> | ||
| 8 | <Package Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D" InstallerVersion="200"> | ||
| 9 | |||
| 10 | <SoftwareTag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 11 | |||
| 12 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 13 | |||
| 14 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 15 | |||
| 16 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 17 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 18 | <File Id="LICENSE.TXT" Source="LICENSE.TXT" /> | ||
| 19 | </Component> | ||
| 20 | |||
| 21 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 22 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 23 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 24 | </RegistryKey> | ||
| 25 | </Component> | ||
| 26 | |||
| 27 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 28 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 29 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 30 | </RegistryKey> | ||
| 31 | </Component> | ||
| 32 | |||
| 33 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 34 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 35 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string" /> | ||
| 36 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 37 | </RegistryKey> | ||
| 38 | |||
| 39 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 40 | </Component> | ||
| 41 | |||
| 42 | <Component Directory="BinFolder"> | ||
| 43 | <File Id="wixtoolset.org.ico" Source="common\wixtoolset.org.ico" /> | ||
| 44 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 45 | </Component> | ||
| 46 | |||
| 47 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 48 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 49 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 50 | <ComponentGroupRef Id="DocComponents" /> | ||
| 51 | </Feature> | ||
| 52 | |||
| 53 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 54 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 55 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 56 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 57 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 58 | </Package> | ||
| 59 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs new file mode 100644 index 00000000..310ae811 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/SingleFile/SingleFile.wxs | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | <?include WixVer.wxi ?> | ||
| 7 | |||
| 8 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:swid="http://schemas.microsoft.com/wix/TagExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> | ||
| 9 | <Product Id="*" Name="!(loc.ShortProduct) v$(var.WixMajorMinor) Core" Language="1033" Manufacturer="!(loc.Company)" | ||
| 10 | Version="$(var.WixMsiProductVersion)" UpgradeCode="3618724B-2523-44F9-A908-866AA619504D"> | ||
| 11 | <Package Compressed="yes" InstallerVersion="200" SummaryCodepage="1252" InstallScope="perMachine" /> | ||
| 12 | <swid:Tag Regid="!(loc.Regid)" InstallDirectory="INSTALLFOLDER" /> | ||
| 13 | |||
| 14 | <MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed." /> | ||
| 15 | |||
| 16 | <MediaTemplate CabinetTemplate="core{0}.cab" /> | ||
| 17 | |||
| 18 | <Feature Id="Feature_WiX" Title="WiX Toolset" Level="1"> | ||
| 19 | <Component Id="Licensing" Directory="INSTALLFOLDER"> | ||
| 20 | <File Source="LICENSE.TXT" /> | ||
| 21 | </Component> | ||
| 22 | |||
| 23 | <Component Id="ProductRegistration" Directory="INSTALLFOLDER"> | ||
| 24 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 25 | <RegistryValue Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 26 | </RegistryKey> | ||
| 27 | </Component> | ||
| 28 | |||
| 29 | <Component Id="ProductFamilyRegistration" Directory="INSTALLFOLDER"> | ||
| 30 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajor).x"> | ||
| 31 | <RegistryValue Name="v$(var.WixMajorMinor)" Value="[INSTALLFOLDER]" Type="string" /> | ||
| 32 | </RegistryKey> | ||
| 33 | </Component> | ||
| 34 | |||
| 35 | <Component Id="ProductInformation" Directory="BinFolder"> | ||
| 36 | <RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows Installer XML\$(var.WixMajorMinor)"> | ||
| 37 | <RegistryValue Name="InstallRoot" Value="[BinFolder]" Type="string"/> | ||
| 38 | <RegistryValue Name="ProductVersion" Value="[ProductVersion]" Type="string" /> | ||
| 39 | </RegistryKey> | ||
| 40 | |||
| 41 | <RemoveFolder Id="CleanupShortcutFolder" Directory="ShortcutFolder" On="uninstall" /> | ||
| 42 | </Component> | ||
| 43 | |||
| 44 | <Component Directory="BinFolder"> | ||
| 45 | <File Source="common\wixtoolset.org.ico" /> | ||
| 46 | <util:InternetShortcut Id="wixtoolset.org" Directory="ShortcutFolder" Name="WiX Home Page" Target="http://wixtoolset.org/" IconFile="file://[#wixtoolset.org.ico]" /> | ||
| 47 | </Component> | ||
| 48 | |||
| 49 | <ComponentGroupRef Id="ToolsetComponents" /> | ||
| 50 | <ComponentGroupRef Id="ExtensionComponents" /> | ||
| 51 | <ComponentGroupRef Id="LuxComponents" /> | ||
| 52 | <ComponentGroupRef Id="DocComponents" /> | ||
| 53 | </Feature> | ||
| 54 | |||
| 55 | <FeatureRef Id="Feature_MSBuild" /> | ||
| 56 | <FeatureRef Id="Feature_Intellisense2010" /> | ||
| 57 | <FeatureRef Id="Feature_Intellisense2012" /> | ||
| 58 | <FeatureRef Id="Feature_Intellisense2013" /> | ||
| 59 | <FeatureRef Id="Feature_Intellisense2015" /> | ||
| 60 | </Product> | ||
| 61 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/UtilExtensionFixture.cs b/src/wix/test/WixToolsetTest.Converters/UtilExtensionFixture.cs new file mode 100644 index 00000000..10450c68 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/UtilExtensionFixture.cs | |||
| @@ -0,0 +1,190 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class UtilExtensionFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixCloseAppsCondition() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <util:CloseApplication Id='EndApp' Target='example.exe'>", | ||
| 21 | " a<>b", | ||
| 22 | " </util:CloseApplication>", | ||
| 23 | " </Fragment>", | ||
| 24 | "</Wix>"); | ||
| 25 | |||
| 26 | var expected = new[] | ||
| 27 | { | ||
| 28 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 29 | " <Fragment>", | ||
| 30 | " <util:CloseApplication Id=\"EndApp\" Target=\"example.exe\" Condition=\"a<>b\" />", | ||
| 31 | " </Fragment>", | ||
| 32 | "</Wix>" | ||
| 33 | }; | ||
| 34 | |||
| 35 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 36 | |||
| 37 | var messaging = new MockMessaging(); | ||
| 38 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 39 | |||
| 40 | var errors = converter.ConvertDocument(document); | ||
| 41 | Assert.Equal(3, errors); | ||
| 42 | |||
| 43 | var actualLines = UnformattedDocumentLines(document); | ||
| 44 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 45 | } | ||
| 46 | |||
| 47 | [Fact] | ||
| 48 | public void FixXmlConfigValue() | ||
| 49 | { | ||
| 50 | var parse = String.Join(Environment.NewLine, | ||
| 51 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 52 | " <Fragment>", | ||
| 53 | " <util:XmlConfig Id='Change' ElementPath='book'>", | ||
| 54 | " a<>b", | ||
| 55 | " </util:XmlConfig>", | ||
| 56 | " </Fragment>", | ||
| 57 | "</Wix>"); | ||
| 58 | |||
| 59 | var expected = new[] | ||
| 60 | { | ||
| 61 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 62 | " <Fragment>", | ||
| 63 | " <util:XmlConfig Id=\"Change\" ElementPath=\"book\" Value=\"a<>b\" />", | ||
| 64 | " </Fragment>", | ||
| 65 | "</Wix>" | ||
| 66 | }; | ||
| 67 | |||
| 68 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 69 | |||
| 70 | var messaging = new MockMessaging(); | ||
| 71 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 72 | |||
| 73 | var errors = converter.ConvertDocument(document); | ||
| 74 | Assert.Equal(3, errors); | ||
| 75 | |||
| 76 | var actualLines = UnformattedDocumentLines(document); | ||
| 77 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 78 | } | ||
| 79 | |||
| 80 | [Fact] | ||
| 81 | public void WarnsOnAllRegistryValueSearches() | ||
| 82 | { | ||
| 83 | var parse = String.Join(Environment.NewLine, | ||
| 84 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 85 | " <Fragment>", | ||
| 86 | " <util:RegistrySearch Id='RegValue' Root='HKLM' Key='Converter' Variable='Test' />", | ||
| 87 | " <util:RegistrySearch Id='RegValue2' Root='HKLM' Key='Converter' Variable='Test' Result='value' />", | ||
| 88 | " <util:RegistrySearch Id='RegValue3' Root='HKLM' Key='Converter' Variable='Test' Result='exists' />", | ||
| 89 | " </Fragment>", | ||
| 90 | "</Wix>"); | ||
| 91 | |||
| 92 | var expected = new[] | ||
| 93 | { | ||
| 94 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 95 | " <Fragment>", | ||
| 96 | " <util:RegistrySearch Id=\"RegValue\" Root=\"HKLM\" Key=\"Converter\" Variable=\"Test\" />", | ||
| 97 | " <util:RegistrySearch Id=\"RegValue2\" Root=\"HKLM\" Key=\"Converter\" Variable=\"Test\" Result=\"value\" />", | ||
| 98 | " <util:RegistrySearch Id=\"RegValue3\" Root=\"HKLM\" Key=\"Converter\" Variable=\"Test\" Result=\"exists\" />", | ||
| 99 | " </Fragment>", | ||
| 100 | "</Wix>" | ||
| 101 | }; | ||
| 102 | |||
| 103 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 104 | |||
| 105 | var messaging = new MockMessaging(); | ||
| 106 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 107 | |||
| 108 | var errors = converter.ConvertDocument(document); | ||
| 109 | Assert.Equal(4, errors); | ||
| 110 | |||
| 111 | var actualLines = UnformattedDocumentLines(document); | ||
| 112 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | [Fact] | ||
| 117 | public void FixXmlConfigValueCData() | ||
| 118 | { | ||
| 119 | var parse = String.Join(Environment.NewLine, | ||
| 120 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 121 | " <Fragment>", | ||
| 122 | " <util:XmlConfig Id='Change' ElementPath='book'>", | ||
| 123 | " <![CDATA[a<>b]]>", | ||
| 124 | " </util:XmlConfig>", | ||
| 125 | " </Fragment>", | ||
| 126 | "</Wix>"); | ||
| 127 | |||
| 128 | var expected = new[] | ||
| 129 | { | ||
| 130 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 131 | " <Fragment>", | ||
| 132 | " <util:XmlConfig Id=\"Change\" ElementPath=\"book\" Value=\"a<>b\" />", | ||
| 133 | " </Fragment>", | ||
| 134 | "</Wix>" | ||
| 135 | }; | ||
| 136 | |||
| 137 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 138 | |||
| 139 | var messaging = new MockMessaging(); | ||
| 140 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 141 | |||
| 142 | var errors = converter.ConvertDocument(document); | ||
| 143 | Assert.Equal(3, errors); | ||
| 144 | |||
| 145 | var actualLines = UnformattedDocumentLines(document); | ||
| 146 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 147 | } | ||
| 148 | |||
| 149 | [Fact] | ||
| 150 | public void FixQueryOsPropertyRefs() | ||
| 151 | { | ||
| 152 | var parse = String.Join(Environment.NewLine, | ||
| 153 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 154 | " <Fragment>", | ||
| 155 | " <PropertyRef Id=\"WIX_SUITE_ENTERPRISE\" />", | ||
| 156 | " <PropertyRef Id=\"WIX_DIR_COMMON_DOCUMENTS\" />", | ||
| 157 | " <CustomActionRef Id=\"WixFailWhenDeferred\" />", | ||
| 158 | " <UI>", | ||
| 159 | " <PropertyRef Id=\"WIX_ACCOUNT_LOCALSERVICE\" />", | ||
| 160 | " </UI>", | ||
| 161 | " </Fragment>", | ||
| 162 | "</Wix>"); | ||
| 163 | |||
| 164 | var expected = new[] | ||
| 165 | { | ||
| 166 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 167 | " <Fragment>", | ||
| 168 | " <util:QueryWindowsSuiteInfo />", | ||
| 169 | " <util:QueryWindowsDirectories />", | ||
| 170 | " <util:FailWhenDeferred />", | ||
| 171 | " <UI>", | ||
| 172 | " <util:QueryWindowsWellKnownSIDs />", | ||
| 173 | " </UI>", | ||
| 174 | " </Fragment>", | ||
| 175 | "</Wix>" | ||
| 176 | }; | ||
| 177 | |||
| 178 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 179 | |||
| 180 | var messaging = new MockMessaging(); | ||
| 181 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 182 | |||
| 183 | var errors = converter.ConvertDocument(document); | ||
| 184 | Assert.Equal(6, errors); | ||
| 185 | |||
| 186 | var actualLines = UnformattedDocumentLines(document); | ||
| 187 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/VariableFixture.cs b/src/wix/test/WixToolsetTest.Converters/VariableFixture.cs new file mode 100644 index 00000000..b7b7388f --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/VariableFixture.cs | |||
| @@ -0,0 +1,86 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class VariableFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void FixFormattedType() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 19 | " <Fragment>", | ||
| 20 | " <Variable Name='ExplicitString' Type='string' Value='explicit' />", | ||
| 21 | " <Variable Name='ImplicitNumber' Value='42' />", | ||
| 22 | " <Variable Name='ImplicitString' Value='implicit' />", | ||
| 23 | " <Variable Name='ImplicitVersion' Value='v2' />", | ||
| 24 | " <Variable Name='NoTypeOrValue' />", | ||
| 25 | " </Fragment>", | ||
| 26 | "</Wix>"); | ||
| 27 | |||
| 28 | var expected = new[] | ||
| 29 | { | ||
| 30 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 31 | " <Fragment>", | ||
| 32 | " <Variable Name=\"ExplicitString\" Type=\"formatted\" Value=\"explicit\" />", | ||
| 33 | " <Variable Name=\"ImplicitNumber\" Value=\"42\" />", | ||
| 34 | " <Variable Name=\"ImplicitString\" Value=\"implicit\" Type=\"formatted\" />", | ||
| 35 | " <Variable Name=\"ImplicitVersion\" Value=\"v2\" />", | ||
| 36 | " <Variable Name=\"NoTypeOrValue\" />", | ||
| 37 | " </Fragment>", | ||
| 38 | "</Wix>" | ||
| 39 | }; | ||
| 40 | |||
| 41 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 42 | |||
| 43 | var messaging = new MockMessaging(); | ||
| 44 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 45 | |||
| 46 | var errors = converter.ConvertDocument(document); | ||
| 47 | Assert.Equal(3, errors); | ||
| 48 | |||
| 49 | var actualLines = UnformattedDocumentLines(document); | ||
| 50 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 51 | } | ||
| 52 | |||
| 53 | [Fact] | ||
| 54 | public void DoesntFixFormattedTypeFromV4() | ||
| 55 | { | ||
| 56 | var parse = String.Join(Environment.NewLine, | ||
| 57 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 58 | " <Fragment>", | ||
| 59 | " <Variable Name='ImplicitString' Value='implicit' />", | ||
| 60 | " <Variable Name='ExplicitString' Type='string' Value='explicit' />", | ||
| 61 | " </Fragment>", | ||
| 62 | "</Wix>"); | ||
| 63 | |||
| 64 | var expected = new[] | ||
| 65 | { | ||
| 66 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 67 | " <Fragment>", | ||
| 68 | " <Variable Name=\"ImplicitString\" Value=\"implicit\" />", | ||
| 69 | " <Variable Name=\"ExplicitString\" Type=\"string\" Value=\"explicit\" />", | ||
| 70 | " </Fragment>", | ||
| 71 | "</Wix>" | ||
| 72 | }; | ||
| 73 | |||
| 74 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 75 | |||
| 76 | var messaging = new MockMessaging(); | ||
| 77 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 78 | |||
| 79 | var errors = converter.ConvertDocument(document); | ||
| 80 | Assert.Equal(0, errors); | ||
| 81 | |||
| 82 | var actualLines = UnformattedDocumentLines(document); | ||
| 83 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/Wix4ConversionFixture.cs b/src/wix/test/WixToolsetTest.Converters/Wix4ConversionFixture.cs new file mode 100644 index 00000000..16a68895 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/Wix4ConversionFixture.cs | |||
| @@ -0,0 +1,54 @@ | |||
| 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.Converters | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Xml.Linq; | ||
| 7 | using WixBuildTools.TestSupport; | ||
| 8 | using WixToolset.Converters; | ||
| 9 | using WixToolsetTest.Converters.Mocks; | ||
| 10 | using Xunit; | ||
| 11 | |||
| 12 | public class Wix4ConversionFixture : BaseConverterFixture | ||
| 13 | { | ||
| 14 | [Fact] | ||
| 15 | public void DoesNotAddFileId() | ||
| 16 | { | ||
| 17 | var parse = String.Join(Environment.NewLine, | ||
| 18 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 19 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 20 | " <Fragment>", | ||
| 21 | " <ComponentGroup Id='ProductComponents' Directory='INSTALLFOLDER'>", | ||
| 22 | " <Component>", | ||
| 23 | " <File Source='example.txt' />", | ||
| 24 | " </Component>", | ||
| 25 | " </ComponentGroup>", | ||
| 26 | " </Fragment>", | ||
| 27 | "</Wix>"); | ||
| 28 | |||
| 29 | var expected = new[] | ||
| 30 | { | ||
| 31 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 32 | " <Fragment>", | ||
| 33 | " <ComponentGroup Id=\"ProductComponents\" Directory=\"INSTALLFOLDER\">", | ||
| 34 | " <Component>", | ||
| 35 | " <File Source=\"example.txt\" />", | ||
| 36 | " </Component>", | ||
| 37 | " </ComponentGroup>", | ||
| 38 | " </Fragment>", | ||
| 39 | "</Wix>" | ||
| 40 | }; | ||
| 41 | |||
| 42 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 43 | |||
| 44 | var messaging = new MockMessaging(); | ||
| 45 | var converter = new WixConverter(messaging, 2, null, null); | ||
| 46 | |||
| 47 | var errors = converter.ConvertDocument(document); | ||
| 48 | Assert.Equal(1, errors); | ||
| 49 | |||
| 50 | var actualLines = UnformattedDocumentLines(document); | ||
| 51 | WixAssert.CompareLineByLine(expected, actualLines); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj b/src/wix/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj new file mode 100644 index 00000000..29b02b95 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/WixToolsetTest.Converters.csproj | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <!-- 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. --> | ||
| 3 | |||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | ||
| 5 | <PropertyGroup> | ||
| 6 | <TargetFramework>netcoreapp3.1</TargetFramework> | ||
| 7 | <IsPackable>false</IsPackable> | ||
| 8 | </PropertyGroup> | ||
| 9 | |||
| 10 | <ItemGroup> | ||
| 11 | <Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" /> | ||
| 12 | </ItemGroup> | ||
| 13 | |||
| 14 | <ItemGroup> | ||
| 15 | <ProjectReference Include="..\..\WixToolset.Converters\WixToolset.Converters.csproj" /> | ||
| 16 | </ItemGroup> | ||
| 17 | |||
| 18 | <ItemGroup> | ||
| 19 | <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" /> | ||
| 20 | <PackageReference Include="WixToolset.Core" Version="4.0.*" /> | ||
| 21 | <PackageReference Include="WixToolset.Core.Burn" Version="4.0.*" /> | ||
| 22 | <PackageReference Include="WixToolset.Core.TestPackage" Version="4.0.*" /> | ||
| 23 | <PackageReference Include="WixToolset.Core.WindowsInstaller" Version="4.0.*" /> | ||
| 24 | </ItemGroup> | ||
| 25 | |||
| 26 | <ItemGroup> | ||
| 27 | <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> | ||
| 28 | <PackageReference Include="xunit" Version="2.4.1" /> | ||
| 29 | <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="All" /> | ||
| 30 | </ItemGroup> | ||
| 31 | </Project> | ||
