diff options
| author | Rob Mensching <rob@firegiant.com> | 2019-05-15 16:21:14 -0700 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2019-10-20 01:06:43 -0700 |
| commit | 70d459c39d1716b2d26ffcbd2c4037d646b9ec37 (patch) | |
| tree | 2c04b4940d64b99bea85cf00823a66697d344301 /src | |
| parent | 14fdc9113bdc7270fb33e06990081988eb099ba9 (diff) | |
| download | wix-70d459c39d1716b2d26ffcbd2c4037d646b9ec37.tar.gz wix-70d459c39d1716b2d26ffcbd2c4037d646b9ec37.tar.bz2 wix-70d459c39d1716b2d26ffcbd2c4037d646b9ec37.zip | |
Extract common converters code to Converters repo and use that instead
Diffstat (limited to 'src')
| -rw-r--r-- | src/test/WixToolsetTest.WixCop/ConverterFixture.cs | 554 | ||||
| -rw-r--r-- | src/wixcop/CommandLine/ConvertCommand.cs | 5 | ||||
| -rw-r--r-- | src/wixcop/Converter.cs | 629 | ||||
| -rw-r--r-- | src/wixcop/WixCop.csproj | 2 |
4 files changed, 4 insertions, 1186 deletions
diff --git a/src/test/WixToolsetTest.WixCop/ConverterFixture.cs b/src/test/WixToolsetTest.WixCop/ConverterFixture.cs deleted file mode 100644 index ceac07d6..00000000 --- a/src/test/WixToolsetTest.WixCop/ConverterFixture.cs +++ /dev/null | |||
| @@ -1,554 +0,0 @@ | |||
| 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.WixCop | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using System.Text; | ||
| 8 | using System.Xml.Linq; | ||
| 9 | using WixToolset.Data; | ||
| 10 | using WixToolset.Extensibility; | ||
| 11 | using WixToolset.Extensibility.Services; | ||
| 12 | using WixToolset.Tools.WixCop; | ||
| 13 | using Xunit; | ||
| 14 | |||
| 15 | public class ConverterFixture | ||
| 16 | { | ||
| 17 | private static readonly XNamespace Wix4Namespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
| 18 | |||
| 19 | [Fact] | ||
| 20 | public void EnsuresDeclaration() | ||
| 21 | { | ||
| 22 | var parse = String.Join(Environment.NewLine, | ||
| 23 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 24 | " <Fragment />", | ||
| 25 | "</Wix>"); | ||
| 26 | |||
| 27 | var expected = String.Join(Environment.NewLine, | ||
| 28 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 29 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 30 | " <Fragment />", | ||
| 31 | "</Wix>"); | ||
| 32 | |||
| 33 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 34 | |||
| 35 | var messaging = new DummyMessaging(); | ||
| 36 | var converter = new Converter(messaging, 2, null, null); | ||
| 37 | |||
| 38 | var errors = converter.ConvertDocument(document); | ||
| 39 | |||
| 40 | var actual = UnformattedDocumentString(document); | ||
| 41 | |||
| 42 | Assert.Equal(1, errors); | ||
| 43 | Assert.Equal(expected, actual); | ||
| 44 | } | ||
| 45 | |||
| 46 | [Fact] | ||
| 47 | public void EnsuresUtf8Declaration() | ||
| 48 | { | ||
| 49 | var parse = String.Join(Environment.NewLine, | ||
| 50 | "<?xml version='1.0'?>", | ||
| 51 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 52 | " <Fragment />", | ||
| 53 | "</Wix>"); | ||
| 54 | |||
| 55 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 56 | |||
| 57 | var messaging = new DummyMessaging(); | ||
| 58 | var converter = new Converter(messaging, 4, null, null); | ||
| 59 | |||
| 60 | var errors = converter.ConvertDocument(document); | ||
| 61 | |||
| 62 | Assert.Equal(1, errors); | ||
| 63 | Assert.Equal("1.0", document.Declaration.Version); | ||
| 64 | Assert.Equal("utf-8", document.Declaration.Encoding); | ||
| 65 | } | ||
| 66 | |||
| 67 | [Fact] | ||
| 68 | public void CanFixWhitespace() | ||
| 69 | { | ||
| 70 | var parse = String.Join(Environment.NewLine, | ||
| 71 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 72 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 73 | " <Fragment>", | ||
| 74 | " <Property Id='Prop'", | ||
| 75 | " Value='Val'>", | ||
| 76 | " </Property>", | ||
| 77 | " </Fragment>", | ||
| 78 | "</Wix>"); | ||
| 79 | |||
| 80 | var expected = String.Join(Environment.NewLine, | ||
| 81 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 82 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 83 | " <Fragment>", | ||
| 84 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 85 | " </Fragment>", | ||
| 86 | "</Wix>"); | ||
| 87 | |||
| 88 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 89 | |||
| 90 | var messaging = new DummyMessaging(); | ||
| 91 | var converter = new Converter(messaging, 4, null, null); | ||
| 92 | |||
| 93 | var errors = converter.ConvertDocument(document); | ||
| 94 | |||
| 95 | var actual = UnformattedDocumentString(document); | ||
| 96 | |||
| 97 | Assert.Equal(expected, actual); | ||
| 98 | Assert.Equal(4, errors); | ||
| 99 | } | ||
| 100 | |||
| 101 | [Fact] | ||
| 102 | public void CanPreserveNewLines() | ||
| 103 | { | ||
| 104 | var parse = String.Join(Environment.NewLine, | ||
| 105 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 106 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 107 | " <Fragment>", | ||
| 108 | "", | ||
| 109 | " <Property Id='Prop' Value='Val' />", | ||
| 110 | "", | ||
| 111 | " </Fragment>", | ||
| 112 | "</Wix>"); | ||
| 113 | |||
| 114 | var expected = String.Join(Environment.NewLine, | ||
| 115 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 116 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 117 | " <Fragment>", | ||
| 118 | "", | ||
| 119 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 120 | "", | ||
| 121 | " </Fragment>", | ||
| 122 | "</Wix>"); | ||
| 123 | |||
| 124 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 125 | |||
| 126 | var messaging = new DummyMessaging(); | ||
| 127 | var converter = new Converter(messaging, 4, null, null); | ||
| 128 | |||
| 129 | var conversions = converter.ConvertDocument(document); | ||
| 130 | |||
| 131 | var actual = UnformattedDocumentString(document); | ||
| 132 | |||
| 133 | Assert.Equal(expected, actual); | ||
| 134 | Assert.Equal(3, conversions); | ||
| 135 | } | ||
| 136 | |||
| 137 | [Fact] | ||
| 138 | public void CanConvertWithNewLineAtEndOfFile() | ||
| 139 | { | ||
| 140 | var parse = String.Join(Environment.NewLine, | ||
| 141 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 142 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 143 | " <Fragment>", | ||
| 144 | "", | ||
| 145 | " <Property Id='Prop' Value='Val' />", | ||
| 146 | "", | ||
| 147 | " </Fragment>", | ||
| 148 | "</Wix>", | ||
| 149 | ""); | ||
| 150 | |||
| 151 | var expected = String.Join(Environment.NewLine, | ||
| 152 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 153 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 154 | " <Fragment>", | ||
| 155 | "", | ||
| 156 | " <Property Id=\"Prop\" Value=\"Val\" />", | ||
| 157 | "", | ||
| 158 | " </Fragment>", | ||
| 159 | "</Wix>", | ||
| 160 | ""); | ||
| 161 | |||
| 162 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 163 | |||
| 164 | var messaging = new DummyMessaging(); | ||
| 165 | var converter = new Converter(messaging, 4, null, null); | ||
| 166 | |||
| 167 | var conversions = converter.ConvertDocument(document); | ||
| 168 | |||
| 169 | var actual = UnformattedDocumentString(document); | ||
| 170 | |||
| 171 | Assert.Equal(expected, actual); | ||
| 172 | Assert.Equal(3, conversions); | ||
| 173 | } | ||
| 174 | |||
| 175 | [Fact] | ||
| 176 | public void CanFixCdataWhitespace() | ||
| 177 | { | ||
| 178 | var parse = String.Join(Environment.NewLine, | ||
| 179 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 180 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 181 | " <Fragment>", | ||
| 182 | " <Property Id='Prop'>", | ||
| 183 | " <![CDATA[1<2]]>", | ||
| 184 | " </Property>", | ||
| 185 | " </Fragment>", | ||
| 186 | "</Wix>"); | ||
| 187 | |||
| 188 | var expected = String.Join(Environment.NewLine, | ||
| 189 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 190 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 191 | " <Fragment>", | ||
| 192 | " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>", | ||
| 193 | " </Fragment>", | ||
| 194 | "</Wix>"); | ||
| 195 | |||
| 196 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 197 | |||
| 198 | var messaging = new DummyMessaging(); | ||
| 199 | var converter = new Converter(messaging, 2, null, null); | ||
| 200 | |||
| 201 | var errors = converter.ConvertDocument(document); | ||
| 202 | |||
| 203 | var actual = UnformattedDocumentString(document); | ||
| 204 | |||
| 205 | Assert.Equal(expected, actual); | ||
| 206 | Assert.Equal(2, errors); | ||
| 207 | } | ||
| 208 | |||
| 209 | [Fact] | ||
| 210 | public void CanFixCdataWithWhitespace() | ||
| 211 | { | ||
| 212 | var parse = String.Join(Environment.NewLine, | ||
| 213 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 214 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 215 | " <Fragment>", | ||
| 216 | " <Property Id='Prop'>", | ||
| 217 | " <![CDATA[", | ||
| 218 | " 1<2", | ||
| 219 | " ]]>", | ||
| 220 | " </Property>", | ||
| 221 | " </Fragment>", | ||
| 222 | "</Wix>"); | ||
| 223 | |||
| 224 | var expected = String.Join(Environment.NewLine, | ||
| 225 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 226 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 227 | " <Fragment>", | ||
| 228 | " <Property Id=\"Prop\"><![CDATA[1<2]]></Property>", | ||
| 229 | " </Fragment>", | ||
| 230 | "</Wix>"); | ||
| 231 | |||
| 232 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 233 | |||
| 234 | var messaging = new DummyMessaging(); | ||
| 235 | var converter = new Converter(messaging, 2, null, null); | ||
| 236 | |||
| 237 | var errors = converter.ConvertDocument(document); | ||
| 238 | |||
| 239 | var actual = UnformattedDocumentString(document); | ||
| 240 | |||
| 241 | Assert.Equal(expected, actual); | ||
| 242 | Assert.Equal(2, errors); | ||
| 243 | } | ||
| 244 | |||
| 245 | [Fact] | ||
| 246 | public void CanConvertMainNamespace() | ||
| 247 | { | ||
| 248 | var parse = String.Join(Environment.NewLine, | ||
| 249 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 250 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 251 | " <Fragment />", | ||
| 252 | "</Wix>"); | ||
| 253 | |||
| 254 | var expected = String.Join(Environment.NewLine, | ||
| 255 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 256 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 257 | " <Fragment />", | ||
| 258 | "</Wix>"); | ||
| 259 | |||
| 260 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 261 | |||
| 262 | var messaging = new DummyMessaging(); | ||
| 263 | var converter = new Converter(messaging, 2, null, null); | ||
| 264 | |||
| 265 | var errors = converter.ConvertDocument(document); | ||
| 266 | |||
| 267 | var actual = UnformattedDocumentString(document); | ||
| 268 | |||
| 269 | Assert.Equal(1, errors); | ||
| 270 | //Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 271 | Assert.Equal(expected, actual); | ||
| 272 | } | ||
| 273 | |||
| 274 | [Fact] | ||
| 275 | public void CanConvertNamedMainNamespace() | ||
| 276 | { | ||
| 277 | var parse = String.Join(Environment.NewLine, | ||
| 278 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 279 | "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi'>", | ||
| 280 | " <w:Fragment />", | ||
| 281 | "</w:Wix>"); | ||
| 282 | |||
| 283 | var expected = String.Join(Environment.NewLine, | ||
| 284 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 285 | "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 286 | " <w:Fragment />", | ||
| 287 | "</w:Wix>"); | ||
| 288 | |||
| 289 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 290 | |||
| 291 | var messaging = new DummyMessaging(); | ||
| 292 | var converter = new Converter(messaging, 2, null, null); | ||
| 293 | |||
| 294 | var errors = converter.ConvertDocument(document); | ||
| 295 | |||
| 296 | var actual = UnformattedDocumentString(document); | ||
| 297 | |||
| 298 | Assert.Equal(1, errors); | ||
| 299 | Assert.Equal(expected, actual); | ||
| 300 | Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); | ||
| 301 | } | ||
| 302 | |||
| 303 | [Fact] | ||
| 304 | public void CanConvertNonWixDefaultNamespace() | ||
| 305 | { | ||
| 306 | var parse = String.Join(Environment.NewLine, | ||
| 307 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 308 | "<w:Wix xmlns:w='http://schemas.microsoft.com/wix/2006/wi' xmlns='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 309 | " <w:Fragment>", | ||
| 310 | " <Test />", | ||
| 311 | " </w:Fragment>", | ||
| 312 | "</w:Wix>"); | ||
| 313 | |||
| 314 | var expected = String.Join(Environment.NewLine, | ||
| 315 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 316 | "<w:Wix xmlns:w=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 317 | " <w:Fragment>", | ||
| 318 | " <Test />", | ||
| 319 | " </w:Fragment>", | ||
| 320 | "</w:Wix>"); | ||
| 321 | |||
| 322 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 323 | |||
| 324 | var messaging = new DummyMessaging(); | ||
| 325 | var converter = new Converter(messaging, 2, null, null); | ||
| 326 | |||
| 327 | var errors = converter.ConvertDocument(document); | ||
| 328 | |||
| 329 | var actual = UnformattedDocumentString(document); | ||
| 330 | |||
| 331 | Assert.Equal(expected, actual); | ||
| 332 | Assert.Equal(2, errors); | ||
| 333 | Assert.Equal(Wix4Namespace, document.Root.GetNamespaceOfPrefix("w")); | ||
| 334 | Assert.Equal("http://wixtoolset.org/schemas/v4/wxs/util", document.Root.GetDefaultNamespace()); | ||
| 335 | } | ||
| 336 | |||
| 337 | [Fact] | ||
| 338 | public void CanConvertExtensionNamespace() | ||
| 339 | { | ||
| 340 | var parse = String.Join(Environment.NewLine, | ||
| 341 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 342 | "<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension'>", | ||
| 343 | " <Fragment />", | ||
| 344 | "</Wix>"); | ||
| 345 | |||
| 346 | var expected = String.Join(Environment.NewLine, | ||
| 347 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 348 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:util=\"http://wixtoolset.org/schemas/v4/wxs/util\">", | ||
| 349 | " <Fragment />", | ||
| 350 | "</Wix>"); | ||
| 351 | |||
| 352 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 353 | |||
| 354 | var messaging = new DummyMessaging(); | ||
| 355 | var converter = new Converter(messaging, 2, null, null); | ||
| 356 | |||
| 357 | var errors = converter.ConvertDocument(document); | ||
| 358 | |||
| 359 | var actual = UnformattedDocumentString(document); | ||
| 360 | |||
| 361 | Assert.Equal(2, errors); | ||
| 362 | Assert.Equal(expected, actual); | ||
| 363 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 364 | } | ||
| 365 | |||
| 366 | [Fact] | ||
| 367 | public void CanConvertMissingNamespace() | ||
| 368 | { | ||
| 369 | var parse = String.Join(Environment.NewLine, | ||
| 370 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 371 | "<Wix>", | ||
| 372 | " <Fragment />", | ||
| 373 | "</Wix>"); | ||
| 374 | |||
| 375 | var expected = String.Join(Environment.NewLine, | ||
| 376 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 377 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 378 | " <Fragment />", | ||
| 379 | "</Wix>"); | ||
| 380 | |||
| 381 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 382 | |||
| 383 | var messaging = new DummyMessaging(); | ||
| 384 | var converter = new Converter(messaging, 2, null, null); | ||
| 385 | |||
| 386 | var errors = converter.ConvertDocument(document); | ||
| 387 | |||
| 388 | var actual = UnformattedDocumentString(document); | ||
| 389 | |||
| 390 | Assert.Equal(1, errors); | ||
| 391 | Assert.Equal(expected, actual); | ||
| 392 | Assert.Equal(Wix4Namespace, document.Root.GetDefaultNamespace()); | ||
| 393 | } | ||
| 394 | |||
| 395 | [Fact] | ||
| 396 | public void CanConvertAnonymousFile() | ||
| 397 | { | ||
| 398 | var parse = String.Join(Environment.NewLine, | ||
| 399 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 400 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 401 | " <File Source='path\\to\\foo.txt' />", | ||
| 402 | "</Wix>"); | ||
| 403 | |||
| 404 | var expected = String.Join(Environment.NewLine, | ||
| 405 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 406 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 407 | " <File Id=\"foo.txt\" Source=\"path\\to\\foo.txt\" />", | ||
| 408 | "</Wix>"); | ||
| 409 | |||
| 410 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 411 | |||
| 412 | var messaging = new DummyMessaging(); | ||
| 413 | var converter = new Converter(messaging, 2, null, null); | ||
| 414 | |||
| 415 | var errors = converter.ConvertDocument(document); | ||
| 416 | |||
| 417 | var actual = UnformattedDocumentString(document); | ||
| 418 | |||
| 419 | Assert.Equal(1, errors); | ||
| 420 | Assert.Equal(expected, actual); | ||
| 421 | } | ||
| 422 | |||
| 423 | [Fact] | ||
| 424 | public void CanConvertShortNameDirectoryWithoutName() | ||
| 425 | { | ||
| 426 | var parse = String.Join(Environment.NewLine, | ||
| 427 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 428 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 429 | " <Directory ShortName='iamshort' />", | ||
| 430 | "</Wix>"); | ||
| 431 | |||
| 432 | var expected = String.Join(Environment.NewLine, | ||
| 433 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 434 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 435 | " <Directory Name=\"iamshort\" />", | ||
| 436 | "</Wix>"); | ||
| 437 | |||
| 438 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 439 | |||
| 440 | var messaging = new DummyMessaging(); | ||
| 441 | var converter = new Converter(messaging, 2, null, null); | ||
| 442 | |||
| 443 | var errors = converter.ConvertDocument(document); | ||
| 444 | |||
| 445 | var actual = UnformattedDocumentString(document); | ||
| 446 | |||
| 447 | Assert.Equal(1, errors); | ||
| 448 | Assert.Equal(expected, actual); | ||
| 449 | } | ||
| 450 | |||
| 451 | [Fact] | ||
| 452 | public void CanConvertSuppressSignatureValidationNo() | ||
| 453 | { | ||
| 454 | var parse = String.Join(Environment.NewLine, | ||
| 455 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 456 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 457 | " <MsiPackage SuppressSignatureValidation='no' />", | ||
| 458 | "</Wix>"); | ||
| 459 | |||
| 460 | var expected = String.Join(Environment.NewLine, | ||
| 461 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 462 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 463 | " <MsiPackage EnableSignatureValidation=\"yes\" />", | ||
| 464 | "</Wix>"); | ||
| 465 | |||
| 466 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 467 | |||
| 468 | var messaging = new DummyMessaging(); | ||
| 469 | var converter = new Converter(messaging, 2, null, null); | ||
| 470 | |||
| 471 | var errors = converter.ConvertDocument(document); | ||
| 472 | |||
| 473 | var actual = UnformattedDocumentString(document); | ||
| 474 | |||
| 475 | Assert.Equal(1, errors); | ||
| 476 | Assert.Equal(expected, actual); | ||
| 477 | } | ||
| 478 | |||
| 479 | [Fact] | ||
| 480 | public void CanConvertSuppressSignatureValidationYes() | ||
| 481 | { | ||
| 482 | var parse = String.Join(Environment.NewLine, | ||
| 483 | "<?xml version='1.0' encoding='utf-8'?>", | ||
| 484 | "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", | ||
| 485 | " <Payload SuppressSignatureValidation='yes' />", | ||
| 486 | "</Wix>"); | ||
| 487 | |||
| 488 | var expected = String.Join(Environment.NewLine, | ||
| 489 | "<?xml version=\"1.0\" encoding=\"utf-16\"?>", | ||
| 490 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
| 491 | " <Payload />", | ||
| 492 | "</Wix>"); | ||
| 493 | |||
| 494 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 495 | |||
| 496 | var messaging = new DummyMessaging(); | ||
| 497 | var converter = new Converter(messaging, 2, null, null); | ||
| 498 | |||
| 499 | var errors = converter.ConvertDocument(document); | ||
| 500 | |||
| 501 | var actual = UnformattedDocumentString(document); | ||
| 502 | |||
| 503 | Assert.Equal(1, errors); | ||
| 504 | Assert.Equal(expected, actual); | ||
| 505 | } | ||
| 506 | |||
| 507 | private static string UnformattedDocumentString(XDocument document) | ||
| 508 | { | ||
| 509 | var sb = new StringBuilder(); | ||
| 510 | |||
| 511 | using (var writer = new StringWriter(sb)) | ||
| 512 | { | ||
| 513 | document.Save(writer, SaveOptions.DisableFormatting); | ||
| 514 | } | ||
| 515 | |||
| 516 | return sb.ToString(); | ||
| 517 | } | ||
| 518 | |||
| 519 | private class DummyMessaging : IMessaging | ||
| 520 | { | ||
| 521 | public bool EncounteredError { get; set; } | ||
| 522 | |||
| 523 | public int LastErrorNumber { get; set; } | ||
| 524 | |||
| 525 | public bool ShowVerboseMessages { get; set; } | ||
| 526 | |||
| 527 | public bool SuppressAllWarnings { get; set; } | ||
| 528 | |||
| 529 | public bool WarningsAsError { get; set; } | ||
| 530 | |||
| 531 | public void ElevateWarningMessage(int warningNumber) | ||
| 532 | { | ||
| 533 | } | ||
| 534 | |||
| 535 | public string FormatMessage(Message message) => String.Empty; | ||
| 536 | |||
| 537 | public void SetListener(IMessageListener listener) | ||
| 538 | { | ||
| 539 | } | ||
| 540 | |||
| 541 | public void SuppressWarningMessage(int warningNumber) | ||
| 542 | { | ||
| 543 | } | ||
| 544 | |||
| 545 | public void Write(Message message) | ||
| 546 | { | ||
| 547 | } | ||
| 548 | |||
| 549 | public void Write(string message, bool verbose = false) | ||
| 550 | { | ||
| 551 | } | ||
| 552 | } | ||
| 553 | } | ||
| 554 | } | ||
diff --git a/src/wixcop/CommandLine/ConvertCommand.cs b/src/wixcop/CommandLine/ConvertCommand.cs index c65652ab..a9a110fe 100644 --- a/src/wixcop/CommandLine/ConvertCommand.cs +++ b/src/wixcop/CommandLine/ConvertCommand.cs | |||
| @@ -6,6 +6,7 @@ namespace WixToolset.Tools.WixCop.CommandLine | |||
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.IO; | 7 | using System.IO; |
| 8 | using System.Xml; | 8 | using System.Xml; |
| 9 | using WixToolset.Converters; | ||
| 9 | using WixToolset.Extensibility.Data; | 10 | using WixToolset.Extensibility.Data; |
| 10 | using WixToolset.Extensibility.Services; | 11 | using WixToolset.Extensibility.Services; |
| 11 | 12 | ||
| @@ -75,7 +76,7 @@ namespace WixToolset.Tools.WixCop.CommandLine | |||
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | var messaging = this.ServiceProvider.GetService<IMessaging>(); | 78 | var messaging = this.ServiceProvider.GetService<IMessaging>(); |
| 78 | var converter = new Converter(messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); | 79 | var converter = new Wix3Converter(messaging, this.IndentationAmount, this.ErrorsAsWarnings, this.IgnoreErrors); |
| 79 | 80 | ||
| 80 | var errors = this.InspectSubDirectories(converter, Path.GetFullPath(".")); | 81 | var errors = this.InspectSubDirectories(converter, Path.GetFullPath(".")); |
| 81 | 82 | ||
| @@ -131,7 +132,7 @@ namespace WixToolset.Tools.WixCop.CommandLine | |||
| 131 | /// </summary> | 132 | /// </summary> |
| 132 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> | 133 | /// <param name="directory">The directory whose sub-directories will be inspected.</param> |
| 133 | /// <returns>The number of errors that were found.</returns> | 134 | /// <returns>The number of errors that were found.</returns> |
| 134 | private int InspectSubDirectories(Converter converter, string directory) | 135 | private int InspectSubDirectories(Wix3Converter converter, string directory) |
| 135 | { | 136 | { |
| 136 | var errors = 0; | 137 | var errors = 0; |
| 137 | 138 | ||
diff --git a/src/wixcop/Converter.cs b/src/wixcop/Converter.cs deleted file mode 100644 index 37016c19..00000000 --- a/src/wixcop/Converter.cs +++ /dev/null | |||
| @@ -1,629 +0,0 @@ | |||
| 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.Tools.WixCop | ||
| 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.Xml; | ||
| 12 | using System.Xml.Linq; | ||
| 13 | using WixToolset.Data; | ||
| 14 | using WixToolset.Extensibility.Services; | ||
| 15 | using WixToolset.Tools.Core; | ||
| 16 | |||
| 17 | /// <summary> | ||
| 18 | /// WiX source code converter. | ||
| 19 | /// </summary> | ||
| 20 | public class Converter | ||
| 21 | { | ||
| 22 | private const char XDocumentNewLine = '\n'; // XDocument normalizes "\r\n" to just "\n". | ||
| 23 | private static readonly XNamespace WixNamespace = "http://wixtoolset.org/schemas/v4/wxs"; | ||
| 24 | |||
| 25 | private static readonly XName DirectoryElementName = WixNamespace + "Directory"; | ||
| 26 | private static readonly XName FileElementName = WixNamespace + "File"; | ||
| 27 | private static readonly XName ExePackageElementName = WixNamespace + "ExePackage"; | ||
| 28 | private static readonly XName MsiPackageElementName = WixNamespace + "MsiPackage"; | ||
| 29 | private static readonly XName MspPackageElementName = WixNamespace + "MspPackage"; | ||
| 30 | private static readonly XName MsuPackageElementName = WixNamespace + "MsuPackage"; | ||
| 31 | private static readonly XName PayloadElementName = WixNamespace + "Payload"; | ||
| 32 | private static readonly XName CustomActionElementName = WixNamespace + "CustomAction"; | ||
| 33 | private static readonly XName PropertyElementName = WixNamespace + "Property"; | ||
| 34 | private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix"; | ||
| 35 | |||
| 36 | private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>() | ||
| 37 | { | ||
| 38 | { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" }, | ||
| 39 | { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" }, | ||
| 40 | { "http://schemas.microsoft.com/wix/DependencyExtension", "http://wixtoolset.org/schemas/v4/wxs/dependency" }, | ||
| 41 | { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" }, | ||
| 42 | { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" }, | ||
| 43 | { "http://schemas.microsoft.com/wix/GamingExtension", "http://wixtoolset.org/schemas/v4/wxs/gaming" }, | ||
| 44 | { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" }, | ||
| 45 | { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" }, | ||
| 46 | { "http://schemas.microsoft.com/wix/NetFxExtension", "http://wixtoolset.org/schemas/v4/wxs/netfx" }, | ||
| 47 | { "http://schemas.microsoft.com/wix/PSExtension", "http://wixtoolset.org/schemas/v4/wxs/powershell" }, | ||
| 48 | { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" }, | ||
| 49 | { "http://schemas.microsoft.com/wix/TagExtension", "http://wixtoolset.org/schemas/v4/wxs/tag" }, | ||
| 50 | { "http://schemas.microsoft.com/wix/UtilExtension", "http://wixtoolset.org/schemas/v4/wxs/util" }, | ||
| 51 | { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" }, | ||
| 52 | { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" }, | ||
| 53 | { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" }, | ||
| 54 | { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" }, | ||
| 55 | { "http://schemas.microsoft.com/wix/2006/localization", "http://wixtoolset.org/schemas/v4/wxl" }, | ||
| 56 | { "http://schemas.microsoft.com/wix/2006/libraries", "http://wixtoolset.org/schemas/v4/wixlib" }, | ||
| 57 | { "http://schemas.microsoft.com/wix/2006/objects", "http://wixtoolset.org/schemas/v4/wixobj" }, | ||
| 58 | { "http://schemas.microsoft.com/wix/2006/outputs", "http://wixtoolset.org/schemas/v4/wixout" }, | ||
| 59 | { "http://schemas.microsoft.com/wix/2007/pdbs", "http://wixtoolset.org/schemas/v4/wixpdb" }, | ||
| 60 | { "http://schemas.microsoft.com/wix/2003/04/actions", "http://wixtoolset.org/schemas/v4/wi/actions" }, | ||
| 61 | { "http://schemas.microsoft.com/wix/2006/tables", "http://wixtoolset.org/schemas/v4/wi/tables" }, | ||
| 62 | { "http://schemas.microsoft.com/wix/2006/WixUnit", "http://wixtoolset.org/schemas/v4/wixunit" }, | ||
| 63 | }; | ||
| 64 | |||
| 65 | private readonly Dictionary<XName, Action<XElement>> ConvertElementMapping; | ||
| 66 | |||
| 67 | /// <summary> | ||
| 68 | /// Instantiate a new Converter class. | ||
| 69 | /// </summary> | ||
| 70 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 71 | /// <param name="errorsAsWarnings">Test errors to display as warnings.</param> | ||
| 72 | /// <param name="ignoreErrors">Test errors to ignore.</param> | ||
| 73 | public Converter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null) | ||
| 74 | { | ||
| 75 | this.ConvertElementMapping = new Dictionary<XName, Action<XElement>> | ||
| 76 | { | ||
| 77 | { Converter.DirectoryElementName, this.ConvertDirectoryElement }, | ||
| 78 | { Converter.FileElementName, this.ConvertFileElement }, | ||
| 79 | { Converter.ExePackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 80 | { Converter.MsiPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 81 | { Converter.MspPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 82 | { Converter.MsuPackageElementName, this.ConvertSuppressSignatureValidation }, | ||
| 83 | { Converter.PayloadElementName, this.ConvertSuppressSignatureValidation }, | ||
| 84 | { Converter.CustomActionElementName, this.ConvertCustomActionElement }, | ||
| 85 | { Converter.PropertyElementName, this.ConvertPropertyElement }, | ||
| 86 | { Converter.WixElementWithoutNamespaceName, this.ConvertWixElementWithoutNamespace }, | ||
| 87 | }; | ||
| 88 | |||
| 89 | this.Messaging = messaging; | ||
| 90 | |||
| 91 | this.IndentationAmount = indentationAmount; | ||
| 92 | |||
| 93 | this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings)); | ||
| 94 | |||
| 95 | this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors)); | ||
| 96 | } | ||
| 97 | |||
| 98 | private int Errors { get; set; } | ||
| 99 | |||
| 100 | private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } | ||
| 101 | |||
| 102 | private HashSet<ConverterTestType> IgnoreErrors { get; set; } | ||
| 103 | |||
| 104 | private IMessaging Messaging { get; } | ||
| 105 | |||
| 106 | private int IndentationAmount { get; set; } | ||
| 107 | |||
| 108 | private string SourceFile { get; set; } | ||
| 109 | |||
| 110 | /// <summary> | ||
| 111 | /// Convert a file. | ||
| 112 | /// </summary> | ||
| 113 | /// <param name="sourceFile">The file to convert.</param> | ||
| 114 | /// <param name="saveConvertedFile">Option to save the converted errors that are found.</param> | ||
| 115 | /// <returns>The number of errors found.</returns> | ||
| 116 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | ||
| 117 | { | ||
| 118 | XDocument document; | ||
| 119 | |||
| 120 | // Set the instance info. | ||
| 121 | this.Errors = 0; | ||
| 122 | this.SourceFile = sourceFile; | ||
| 123 | |||
| 124 | try | ||
| 125 | { | ||
| 126 | document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
| 127 | } | ||
| 128 | catch (XmlException e) | ||
| 129 | { | ||
| 130 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | ||
| 131 | |||
| 132 | return this.Errors; | ||
| 133 | } | ||
| 134 | |||
| 135 | this.ConvertDocument(document); | ||
| 136 | |||
| 137 | // Fix errors if requested and necessary. | ||
| 138 | if (saveConvertedFile && 0 < this.Errors) | ||
| 139 | { | ||
| 140 | try | ||
| 141 | { | ||
| 142 | using (var writer = File.CreateText(this.SourceFile)) | ||
| 143 | { | ||
| 144 | document.Save(writer, SaveOptions.DisableFormatting | SaveOptions.OmitDuplicateNamespaces); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | catch (UnauthorizedAccessException) | ||
| 148 | { | ||
| 149 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | return this.Errors; | ||
| 154 | } | ||
| 155 | |||
| 156 | /// <summary> | ||
| 157 | /// Convert a document. | ||
| 158 | /// </summary> | ||
| 159 | /// <param name="document">The document to convert.</param> | ||
| 160 | /// <returns>The number of errors found.</returns> | ||
| 161 | public int ConvertDocument(XDocument document) | ||
| 162 | { | ||
| 163 | var declaration = document.Declaration; | ||
| 164 | |||
| 165 | // Convert the declaration. | ||
| 166 | if (null != declaration) | ||
| 167 | { | ||
| 168 | if (!String.Equals("utf-8", declaration.Encoding, StringComparison.OrdinalIgnoreCase)) | ||
| 169 | { | ||
| 170 | if (this.OnError(ConverterTestType.DeclarationEncodingWrong, document.Root, "The XML declaration encoding is not properly set to 'utf-8'.")) | ||
| 171 | { | ||
| 172 | declaration.Encoding = "utf-8"; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
| 176 | else // missing declaration | ||
| 177 | { | ||
| 178 | if (this.OnError(ConverterTestType.DeclarationMissing, null, "This file is missing an XML declaration on the first line.")) | ||
| 179 | { | ||
| 180 | document.Declaration = new XDeclaration("1.0", "utf-8", null); | ||
| 181 | document.Root.AddBeforeSelf(new XText(XDocumentNewLine.ToString())); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | // Start converting the nodes at the top. | ||
| 186 | this.ConvertNodes(document.Nodes(), 0); | ||
| 187 | |||
| 188 | return this.Errors; | ||
| 189 | } | ||
| 190 | |||
| 191 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) | ||
| 192 | { | ||
| 193 | // Note we operate on a copy of the node list since we may | ||
| 194 | // remove some whitespace nodes during this processing. | ||
| 195 | foreach (var node in nodes.ToList()) | ||
| 196 | { | ||
| 197 | if (node is XText text) | ||
| 198 | { | ||
| 199 | if (!String.IsNullOrWhiteSpace(text.Value)) | ||
| 200 | { | ||
| 201 | text.Value = text.Value.Trim(); | ||
| 202 | } | ||
| 203 | else if (node.NextNode is XCData cdata) | ||
| 204 | { | ||
| 205 | this.EnsurePrecedingWhitespaceRemoved(text, node, ConverterTestType.WhitespacePrecedingNodeWrong); | ||
| 206 | } | ||
| 207 | else if (node.NextNode is XElement element) | ||
| 208 | { | ||
| 209 | this.EnsurePrecedingWhitespaceCorrect(text, node, level, ConverterTestType.WhitespacePrecedingNodeWrong); | ||
| 210 | } | ||
| 211 | else if (node.NextNode is null) // this is the space before the close element | ||
| 212 | { | ||
| 213 | if (node.PreviousNode is null || node.PreviousNode is XCData) | ||
| 214 | { | ||
| 215 | this.EnsurePrecedingWhitespaceRemoved(text, node.Parent, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 216 | } | ||
| 217 | else if (level == 0) // root element's close tag | ||
| 218 | { | ||
| 219 | this.EnsurePrecedingWhitespaceCorrect(text, node, 0, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 220 | } | ||
| 221 | else | ||
| 222 | { | ||
| 223 | this.EnsurePrecedingWhitespaceCorrect(text, node, level - 1, ConverterTestType.WhitespacePrecedingEndElementWrong); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | else if (node is XElement element) | ||
| 228 | { | ||
| 229 | this.ConvertElement(element); | ||
| 230 | |||
| 231 | this.ConvertNodes(element.Nodes(), level + 1); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | private void EnsurePrecedingWhitespaceCorrect(XText whitespace, XNode node, int level, ConverterTestType testType) | ||
| 237 | { | ||
| 238 | if (!Converter.LeadingWhitespaceValid(this.IndentationAmount, level, whitespace.Value)) | ||
| 239 | { | ||
| 240 | var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; | ||
| 241 | |||
| 242 | if (this.OnError(testType, node, message)) | ||
| 243 | { | ||
| 244 | Converter.FixupWhitespace(this.IndentationAmount, level, whitespace); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | private void EnsurePrecedingWhitespaceRemoved(XText whitespace, XNode node, ConverterTestType testType) | ||
| 250 | { | ||
| 251 | if (!String.IsNullOrEmpty(whitespace.Value)) | ||
| 252 | { | ||
| 253 | var message = testType == ConverterTestType.WhitespacePrecedingEndElementWrong ? "The whitespace preceding this end element is incorrect." : "The whitespace preceding this node is incorrect."; | ||
| 254 | |||
| 255 | if (this.OnError(testType, node, message)) | ||
| 256 | { | ||
| 257 | whitespace.Remove(); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | private void ConvertElement(XElement element) | ||
| 263 | { | ||
| 264 | // Gather any deprecated namespaces, then update this element tree based on those deprecations. | ||
| 265 | var deprecatedToUpdatedNamespaces = new Dictionary<XNamespace, XNamespace>(); | ||
| 266 | |||
| 267 | foreach (var declaration in element.Attributes().Where(a => a.IsNamespaceDeclaration)) | ||
| 268 | { | ||
| 269 | if (Converter.OldToNewNamespaceMapping.TryGetValue(declaration.Value, out var ns)) | ||
| 270 | { | ||
| 271 | if (this.OnError(ConverterTestType.XmlnsValueWrong, declaration, "The namespace '{0}' is out of date. It must be '{1}'.", declaration.Value, ns.NamespaceName)) | ||
| 272 | { | ||
| 273 | deprecatedToUpdatedNamespaces.Add(declaration.Value, ns); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | if (deprecatedToUpdatedNamespaces.Any()) | ||
| 279 | { | ||
| 280 | Converter.UpdateElementsWithDeprecatedNamespaces(element.DescendantsAndSelf(), deprecatedToUpdatedNamespaces); | ||
| 281 | } | ||
| 282 | |||
| 283 | // Apply any specialized conversion actions. | ||
| 284 | if (this.ConvertElementMapping.TryGetValue(element.Name, out var convert)) | ||
| 285 | { | ||
| 286 | convert(element); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | private void ConvertDirectoryElement(XElement element) | ||
| 291 | { | ||
| 292 | if (null == element.Attribute("Name")) | ||
| 293 | { | ||
| 294 | var attribute = element.Attribute("ShortName"); | ||
| 295 | if (null != attribute) | ||
| 296 | { | ||
| 297 | var shortName = attribute.Value; | ||
| 298 | if (this.OnError(ConverterTestType.AssignDirectoryNameFromShortName, element, "The directory ShortName attribute is being renamed to Name since Name wasn't specified for value '{0}'", shortName)) | ||
| 299 | { | ||
| 300 | element.Add(new XAttribute("Name", shortName)); | ||
| 301 | attribute.Remove(); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | private void ConvertFileElement(XElement element) | ||
| 308 | { | ||
| 309 | if (null == element.Attribute("Id")) | ||
| 310 | { | ||
| 311 | var attribute = element.Attribute("Name"); | ||
| 312 | |||
| 313 | if (null == attribute) | ||
| 314 | { | ||
| 315 | attribute = element.Attribute("Source"); | ||
| 316 | } | ||
| 317 | |||
| 318 | if (null != attribute) | ||
| 319 | { | ||
| 320 | var name = Path.GetFileName(attribute.Value); | ||
| 321 | |||
| 322 | if (this.OnError(ConverterTestType.AssignAnonymousFileId, element, "The file id is being updated to '{0}' to ensure it remains the same as the default", name)) | ||
| 323 | { | ||
| 324 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
| 325 | element.RemoveAttributes(); | ||
| 326 | element.Add(new XAttribute("Id", ToolsCommon.GetIdentifierFromName(name))); | ||
| 327 | element.Add(attributes); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | private void ConvertSuppressSignatureValidation(XElement element) | ||
| 334 | { | ||
| 335 | var suppressSignatureValidation = element.Attribute("SuppressSignatureValidation"); | ||
| 336 | |||
| 337 | if (null != suppressSignatureValidation) | ||
| 338 | { | ||
| 339 | if (this.OnError(ConverterTestType.SuppressSignatureValidationDeprecated, element, "The chain package element contains deprecated '{0}' attribute. Use the 'EnableSignatureValidation' attribute instead.", suppressSignatureValidation)) | ||
| 340 | { | ||
| 341 | if ("no" == suppressSignatureValidation.Value) | ||
| 342 | { | ||
| 343 | element.Add(new XAttribute("EnableSignatureValidation", "yes")); | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | suppressSignatureValidation.Remove(); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | private void ConvertCustomActionElement(XElement xCustomAction) | ||
| 352 | { | ||
| 353 | var xBinaryKey = xCustomAction.Attribute("BinaryKey"); | ||
| 354 | |||
| 355 | if (xBinaryKey?.Value == "WixCA") | ||
| 356 | { | ||
| 357 | if (this.OnError(ConverterTestType.WixCABinaryIdRenamed, xCustomAction, "The WixCA custom action DLL Binary table id has been renamed. Use the id 'UtilCA' instead.")) | ||
| 358 | { | ||
| 359 | xBinaryKey.Value = "UtilCA"; | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | var xDllEntry = xCustomAction.Attribute("DllEntry"); | ||
| 364 | |||
| 365 | if (xDllEntry?.Value == "CAQuietExec" || xDllEntry?.Value == "CAQuietExec64") | ||
| 366 | { | ||
| 367 | if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The CAQuietExec and CAQuietExec64 custom action ids have been renamed. Use the ids 'WixQuietExec' and 'WixQuietExec64' instead.")) | ||
| 368 | { | ||
| 369 | xDllEntry.Value = xDllEntry.Value.Replace("CAQuietExec", "WixQuietExec"); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | var xProperty = xCustomAction.Attribute("Property"); | ||
| 374 | |||
| 375 | if (xProperty?.Value == "QtExecCmdLine" || xProperty?.Value == "QtExec64CmdLine") | ||
| 376 | { | ||
| 377 | if (this.OnError(ConverterTestType.QuietExecCustomActionsRenamed, xCustomAction, "The QtExecCmdLine and QtExec64CmdLine property ids have been renamed. Use the ids 'WixQuietExecCmdLine' and 'WixQuietExec64CmdLine' instead.")) | ||
| 378 | { | ||
| 379 | xProperty.Value = xProperty.Value.Replace("QtExec", "WixQuietExec"); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | private void ConvertPropertyElement(XElement xProperty) | ||
| 385 | { | ||
| 386 | var xId = xProperty.Attribute("Id"); | ||
| 387 | |||
| 388 | if (xId.Value == "QtExecCmdTimeout") | ||
| 389 | { | ||
| 390 | this.OnError(ConverterTestType.QtExecCmdTimeoutAmbiguous, xProperty, "QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout."); | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | /// <summary> | ||
| 395 | /// Converts a Wix element. | ||
| 396 | /// </summary> | ||
| 397 | /// <param name="element">The Wix element to convert.</param> | ||
| 398 | /// <returns>The converted element.</returns> | ||
| 399 | private void ConvertWixElementWithoutNamespace(XElement element) | ||
| 400 | { | ||
| 401 | if (this.OnError(ConverterTestType.XmlnsMissing, element, "The xmlns attribute is missing. It must be present with a value of '{0}'.", WixNamespace.NamespaceName)) | ||
| 402 | { | ||
| 403 | element.Name = WixNamespace.GetName(element.Name.LocalName); | ||
| 404 | |||
| 405 | element.Add(new XAttribute("xmlns", WixNamespace.NamespaceName)); // set the default namespace. | ||
| 406 | |||
| 407 | foreach (var elementWithoutNamespace in element.Elements().Where(e => XNamespace.None == e.Name.Namespace)) | ||
| 408 | { | ||
| 409 | elementWithoutNamespace.Name = WixNamespace.GetName(elementWithoutNamespace.Name.LocalName); | ||
| 410 | } | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | private IEnumerable<ConverterTestType> YieldConverterTypes(IEnumerable<string> types) | ||
| 415 | { | ||
| 416 | if (null != types) | ||
| 417 | { | ||
| 418 | foreach (var type in types) | ||
| 419 | { | ||
| 420 | |||
| 421 | if (Enum.TryParse<ConverterTestType>(type, true, out var itt)) | ||
| 422 | { | ||
| 423 | yield return itt; | ||
| 424 | } | ||
| 425 | else // not a known ConverterTestType | ||
| 426 | { | ||
| 427 | this.OnError(ConverterTestType.ConverterTestTypeUnknown, null, "Unknown error type: '{0}'.", type); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | private static void UpdateElementsWithDeprecatedNamespaces(IEnumerable<XElement> elements, Dictionary<XNamespace, XNamespace> deprecatedToUpdatedNamespaces) | ||
| 434 | { | ||
| 435 | foreach (var element in elements) | ||
| 436 | { | ||
| 437 | |||
| 438 | if (deprecatedToUpdatedNamespaces.TryGetValue(element.Name.Namespace, out var ns)) | ||
| 439 | { | ||
| 440 | element.Name = ns.GetName(element.Name.LocalName); | ||
| 441 | } | ||
| 442 | |||
| 443 | // Remove all the attributes and add them back to with their namespace updated (as necessary). | ||
| 444 | IEnumerable<XAttribute> attributes = element.Attributes().ToList(); | ||
| 445 | element.RemoveAttributes(); | ||
| 446 | |||
| 447 | foreach (var attribute in attributes) | ||
| 448 | { | ||
| 449 | var convertedAttribute = attribute; | ||
| 450 | |||
| 451 | if (attribute.IsNamespaceDeclaration) | ||
| 452 | { | ||
| 453 | if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Value, out ns)) | ||
| 454 | { | ||
| 455 | convertedAttribute = ("xmlns" == attribute.Name.LocalName) ? new XAttribute(attribute.Name.LocalName, ns.NamespaceName) : new XAttribute(XNamespace.Xmlns + attribute.Name.LocalName, ns.NamespaceName); | ||
| 456 | } | ||
| 457 | } | ||
| 458 | else if (deprecatedToUpdatedNamespaces.TryGetValue(attribute.Name.Namespace, out ns)) | ||
| 459 | { | ||
| 460 | convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); | ||
| 461 | } | ||
| 462 | |||
| 463 | element.Add(convertedAttribute); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | } | ||
| 467 | |||
| 468 | /// <summary> | ||
| 469 | /// Determine if the whitespace preceding a node is appropriate for its depth level. | ||
| 470 | /// </summary> | ||
| 471 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 472 | /// <param name="level">The depth level that should match this whitespace.</param> | ||
| 473 | /// <param name="whitespace">The whitespace to validate.</param> | ||
| 474 | /// <returns>true if the whitespace is legal; false otherwise.</returns> | ||
| 475 | private static bool LeadingWhitespaceValid(int indentationAmount, int level, string whitespace) | ||
| 476 | { | ||
| 477 | // Strip off leading newlines; there can be an arbitrary number of these. | ||
| 478 | whitespace = whitespace.TrimStart(XDocumentNewLine); | ||
| 479 | |||
| 480 | var indentation = new string(' ', level * indentationAmount); | ||
| 481 | |||
| 482 | return whitespace == indentation; | ||
| 483 | } | ||
| 484 | |||
| 485 | /// <summary> | ||
| 486 | /// Fix the whitespace in a whitespace node. | ||
| 487 | /// </summary> | ||
| 488 | /// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param> | ||
| 489 | /// <param name="level">The depth level of the desired whitespace.</param> | ||
| 490 | /// <param name="whitespace">The whitespace node to fix.</param> | ||
| 491 | private static void FixupWhitespace(int indentationAmount, int level, XText whitespace) | ||
| 492 | { | ||
| 493 | var value = new StringBuilder(whitespace.Value.Length); | ||
| 494 | |||
| 495 | // Keep any previous preceeding new lines. | ||
| 496 | var newlines = whitespace.Value.TakeWhile(c => c == XDocumentNewLine).Count(); | ||
| 497 | |||
| 498 | // Ensure there is always at least one new line before the indentation. | ||
| 499 | value.Append(XDocumentNewLine, newlines == 0 ? 1 : newlines); | ||
| 500 | |||
| 501 | whitespace.Value = value.Append(' ', level * indentationAmount).ToString(); | ||
| 502 | } | ||
| 503 | |||
| 504 | /// <summary> | ||
| 505 | /// Output an error message to the console. | ||
| 506 | /// </summary> | ||
| 507 | /// <param name="converterTestType">The type of converter test.</param> | ||
| 508 | /// <param name="node">The node that caused the error.</param> | ||
| 509 | /// <param name="message">Detailed error message.</param> | ||
| 510 | /// <param name="args">Additional formatted string arguments.</param> | ||
| 511 | /// <returns>Returns true indicating that action should be taken on this error, and false if it should be ignored.</returns> | ||
| 512 | private bool OnError(ConverterTestType converterTestType, XObject node, string message, params object[] args) | ||
| 513 | { | ||
| 514 | if (this.IgnoreErrors.Contains(converterTestType)) // ignore the error | ||
| 515 | { | ||
| 516 | return false; | ||
| 517 | } | ||
| 518 | |||
| 519 | // Increase the error count. | ||
| 520 | this.Errors++; | ||
| 521 | |||
| 522 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wixcop.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | ||
| 523 | var warning = this.ErrorsAsWarnings.Contains(converterTestType); | ||
| 524 | var display = String.Format(CultureInfo.CurrentCulture, message, args); | ||
| 525 | |||
| 526 | var msg = new Message(sourceLine, warning ? MessageLevel.Warning : MessageLevel.Error, (int)converterTestType, "{0} ({1})", display, converterTestType.ToString()); | ||
| 527 | |||
| 528 | this.Messaging.Write(msg); | ||
| 529 | |||
| 530 | return true; | ||
| 531 | } | ||
| 532 | |||
| 533 | /// <summary> | ||
| 534 | /// Converter test types. These are used to condition error messages down to warnings. | ||
| 535 | /// </summary> | ||
| 536 | private enum ConverterTestType | ||
| 537 | { | ||
| 538 | /// <summary> | ||
| 539 | /// Internal-only: displayed when a string cannot be converted to an ConverterTestType. | ||
| 540 | /// </summary> | ||
| 541 | ConverterTestTypeUnknown, | ||
| 542 | |||
| 543 | /// <summary> | ||
| 544 | /// Displayed when an XML loading exception has occurred. | ||
| 545 | /// </summary> | ||
| 546 | XmlException, | ||
| 547 | |||
| 548 | /// <summary> | ||
| 549 | /// Displayed when a file cannot be accessed; typically when trying to save back a fixed file. | ||
| 550 | /// </summary> | ||
| 551 | UnauthorizedAccessException, | ||
| 552 | |||
| 553 | /// <summary> | ||
| 554 | /// Displayed when the encoding attribute in the XML declaration is not 'UTF-8'. | ||
| 555 | /// </summary> | ||
| 556 | DeclarationEncodingWrong, | ||
| 557 | |||
| 558 | /// <summary> | ||
| 559 | /// Displayed when the XML declaration is missing from the source file. | ||
| 560 | /// </summary> | ||
| 561 | DeclarationMissing, | ||
| 562 | |||
| 563 | /// <summary> | ||
| 564 | /// Displayed when the whitespace preceding a CDATA node is wrong. | ||
| 565 | /// </summary> | ||
| 566 | WhitespacePrecedingCDATAWrong, | ||
| 567 | |||
| 568 | /// <summary> | ||
| 569 | /// Displayed when the whitespace preceding a node is wrong. | ||
| 570 | /// </summary> | ||
| 571 | WhitespacePrecedingNodeWrong, | ||
| 572 | |||
| 573 | /// <summary> | ||
| 574 | /// Displayed when an element is not empty as it should be. | ||
| 575 | /// </summary> | ||
| 576 | NotEmptyElement, | ||
| 577 | |||
| 578 | /// <summary> | ||
| 579 | /// Displayed when the whitespace following a CDATA node is wrong. | ||
| 580 | /// </summary> | ||
| 581 | WhitespaceFollowingCDATAWrong, | ||
| 582 | |||
| 583 | /// <summary> | ||
| 584 | /// Displayed when the whitespace preceding an end element is wrong. | ||
| 585 | /// </summary> | ||
| 586 | WhitespacePrecedingEndElementWrong, | ||
| 587 | |||
| 588 | /// <summary> | ||
| 589 | /// Displayed when the xmlns attribute is missing from the document element. | ||
| 590 | /// </summary> | ||
| 591 | XmlnsMissing, | ||
| 592 | |||
| 593 | /// <summary> | ||
| 594 | /// Displayed when the xmlns attribute on the document element is wrong. | ||
| 595 | /// </summary> | ||
| 596 | XmlnsValueWrong, | ||
| 597 | |||
| 598 | /// <summary> | ||
| 599 | /// Assign an identifier to a File element when on Id attribute is specified. | ||
| 600 | /// </summary> | ||
| 601 | AssignAnonymousFileId, | ||
| 602 | |||
| 603 | /// <summary> | ||
| 604 | /// SuppressSignatureValidation attribute is deprecated and replaced with EnableSignatureValidation. | ||
| 605 | /// </summary> | ||
| 606 | SuppressSignatureValidationDeprecated, | ||
| 607 | |||
| 608 | /// <summary> | ||
| 609 | /// WixCA Binary/@Id has been renamed to UtilCA. | ||
| 610 | /// </summary> | ||
| 611 | WixCABinaryIdRenamed, | ||
| 612 | |||
| 613 | /// <summary> | ||
| 614 | /// QtExec custom actions have been renamed. | ||
| 615 | /// </summary> | ||
| 616 | QuietExecCustomActionsRenamed, | ||
| 617 | |||
| 618 | /// <summary> | ||
| 619 | /// QtExecCmdTimeout was previously used for both CAQuietExec and CAQuietExec64. For WixQuietExec, use WixQuietExecCmdTimeout. For WixQuietExec64, use WixQuietExec64CmdTimeout. | ||
| 620 | /// </summary> | ||
| 621 | QtExecCmdTimeoutAmbiguous, | ||
| 622 | |||
| 623 | /// <summary> | ||
| 624 | /// Directory/@ShortName may only be specified with Directory/@Name. | ||
| 625 | /// </summary> | ||
| 626 | AssignDirectoryNameFromShortName, | ||
| 627 | } | ||
| 628 | } | ||
| 629 | } | ||
diff --git a/src/wixcop/WixCop.csproj b/src/wixcop/WixCop.csproj index 7a17caf1..4c0d9a16 100644 --- a/src/wixcop/WixCop.csproj +++ b/src/wixcop/WixCop.csproj | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | </ItemGroup> | 21 | </ItemGroup> |
| 22 | 22 | ||
| 23 | <ItemGroup> | 23 | <ItemGroup> |
| 24 | <PackageReference Include="WixToolset.Core" Version="4.0.*" /> | 24 | <PackageReference Include="WixToolset.Converters" Version="4.0.*" /> |
| 25 | </ItemGroup> | 25 | </ItemGroup> |
| 26 | 26 | ||
| 27 | <ItemGroup> | 27 | <ItemGroup> |
