diff options
author | Rob Mensching <rob@firegiant.com> | 2022-10-25 07:01:26 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-10-31 10:57:14 -0700 |
commit | 217182f85fa737d20fb5846f395cabfa599bf1c6 (patch) | |
tree | 5930f91922aa79f34d3add6ab0a78b236057f079 /src | |
parent | 2cb28477721baaadba96a01c0763d5361d7a3d94 (diff) | |
download | wix-217182f85fa737d20fb5846f395cabfa599bf1c6.tar.gz wix-217182f85fa737d20fb5846f395cabfa599bf1c6.tar.bz2 wix-217182f85fa737d20fb5846f395cabfa599bf1c6.zip |
Update converted source line numbers when conversion is saved
The conversion process has a good chance of changing the shape of
.wxs source file. Therefore, the old line numbers may be wrong and
should be replaced with the line numbers from the converted file
when it is saved.
Fixes 6969
Diffstat (limited to 'src')
5 files changed, 272 insertions, 61 deletions
diff --git a/src/wix/WixToolset.Converters/HashSetExtensions.cs b/src/wix/WixToolset.Converters/HashSetExtensions.cs new file mode 100644 index 00000000..49b0d4ac --- /dev/null +++ b/src/wix/WixToolset.Converters/HashSetExtensions.cs | |||
@@ -0,0 +1,17 @@ | |||
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.Collections.Generic; | ||
6 | |||
7 | internal static class HashSetExtensions | ||
8 | { | ||
9 | public static void AddRange<T>(this HashSet<T> set, IEnumerable<T> values) | ||
10 | { | ||
11 | foreach (var value in values) | ||
12 | { | ||
13 | set.Add(value); | ||
14 | } | ||
15 | } | ||
16 | } | ||
17 | } | ||
diff --git a/src/wix/WixToolset.Converters/WixConverter.cs b/src/wix/WixToolset.Converters/WixConverter.cs index de81c876..cf77681c 100644 --- a/src/wix/WixToolset.Converters/WixConverter.cs +++ b/src/wix/WixToolset.Converters/WixConverter.cs | |||
@@ -11,6 +11,7 @@ namespace WixToolset.Converters | |||
11 | using System.Text.RegularExpressions; | 11 | using System.Text.RegularExpressions; |
12 | using System.Xml; | 12 | using System.Xml; |
13 | using System.Xml.Linq; | 13 | using System.Xml.Linq; |
14 | using System.Xml.XPath; | ||
14 | using WixToolset.Data; | 15 | using WixToolset.Data; |
15 | using WixToolset.Data.WindowsInstaller; | 16 | using WixToolset.Data.WindowsInstaller; |
16 | using WixToolset.Extensibility.Services; | 17 | using WixToolset.Extensibility.Services; |
@@ -272,6 +273,8 @@ namespace WixToolset.Converters | |||
272 | { WixConverter.WixLocalizationElementWithoutNamespaceName, this.ConvertWixLocalizationElementWithoutNamespace }, | 273 | { WixConverter.WixLocalizationElementWithoutNamespaceName, this.ConvertWixLocalizationElementWithoutNamespace }, |
273 | }; | 274 | }; |
274 | 275 | ||
276 | this.ConversionMessages = new List<Message>(); | ||
277 | |||
275 | this.Messaging = messaging; | 278 | this.Messaging = messaging; |
276 | 279 | ||
277 | this.IndentationAmount = indentationAmount; | 280 | this.IndentationAmount = indentationAmount; |
@@ -285,7 +288,7 @@ namespace WixToolset.Converters | |||
285 | 288 | ||
286 | private CustomTableTarget CustomTableSetting { get; } | 289 | private CustomTableTarget CustomTableSetting { get; } |
287 | 290 | ||
288 | private int Messages { get; set; } | 291 | private List<Message> ConversionMessages { get; } |
289 | 292 | ||
290 | private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } | 293 | private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; } |
291 | 294 | ||
@@ -308,54 +311,35 @@ namespace WixToolset.Converters | |||
308 | /// </summary> | 311 | /// </summary> |
309 | /// <param name="sourceFile">The file to convert.</param> | 312 | /// <param name="sourceFile">The file to convert.</param> |
310 | /// <param name="saveConvertedFile">Option to save the converted Messages that are found.</param> | 313 | /// <param name="saveConvertedFile">Option to save the converted Messages that are found.</param> |
311 | /// <returns>The number of Messages found.</returns> | 314 | /// <returns>The number of conversions found.</returns> |
312 | public int ConvertFile(string sourceFile, bool saveConvertedFile) | 315 | public int ConvertFile(string sourceFile, bool saveConvertedFile) |
313 | { | 316 | { |
314 | var document = this.OpenSourceFile(sourceFile); | 317 | var savedDocument = false; |
315 | 318 | ||
316 | if (document is null) | 319 | if (this.TryOpenSourceFile(sourceFile, out var document)) |
317 | { | 320 | { |
318 | return 1; | 321 | this.Convert(document); |
319 | } | ||
320 | |||
321 | this.ConvertDocument(document); | ||
322 | 322 | ||
323 | // Fix Messages if requested and necessary. | 323 | // Fix Messages if requested and necessary. |
324 | if (saveConvertedFile && 0 < this.Messages) | 324 | if (saveConvertedFile && 0 < this.ConversionMessages.Count) |
325 | { | 325 | { |
326 | this.SaveDocument(document); | 326 | savedDocument = this.SaveDocument(document); |
327 | } | ||
327 | } | 328 | } |
328 | 329 | ||
329 | return this.Messages; | 330 | return this.ReportMessages(document, savedDocument); |
330 | } | 331 | } |
331 | 332 | ||
332 | /// <summary> | 333 | /// <summary> |
333 | /// Convert a document. | 334 | /// Convert a document. |
334 | /// </summary> | 335 | /// </summary> |
335 | /// <param name="document">The document to convert.</param> | 336 | /// <param name="document">The document to convert.</param> |
336 | /// <returns>The number of Messages found.</returns> | 337 | /// <returns>The number of conversions found.</returns> |
337 | public int ConvertDocument(XDocument document) | 338 | public int ConvertDocument(XDocument document) |
338 | { | 339 | { |
339 | // Reset the instance info. | 340 | this.Convert(document); |
340 | this.Messages = 0; | ||
341 | this.SourceVersion = 0; | ||
342 | this.Operation = ConvertOperation.Convert; | ||
343 | |||
344 | // Remove the declaration. | ||
345 | if (null != document.Declaration | ||
346 | && this.OnInformation(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | ||
347 | { | ||
348 | document.Declaration = null; | ||
349 | TrimLeadingText(document); | ||
350 | } | ||
351 | |||
352 | this.XRoot = document.Root; | ||
353 | |||
354 | // Start converting the nodes at the top. | ||
355 | this.ConvertNodes(document.Nodes(), 0); | ||
356 | this.RemoveUnusedNamespaces(document.Root); | ||
357 | 341 | ||
358 | return this.Messages; | 342 | return this.ReportMessages(document, false); |
359 | } | 343 | } |
360 | 344 | ||
361 | /// <summary> | 345 | /// <summary> |
@@ -366,22 +350,20 @@ namespace WixToolset.Converters | |||
366 | /// <returns>The number of Messages found.</returns> | 350 | /// <returns>The number of Messages found.</returns> |
367 | public int FormatFile(string sourceFile, bool saveConvertedFile) | 351 | public int FormatFile(string sourceFile, bool saveConvertedFile) |
368 | { | 352 | { |
369 | var document = this.OpenSourceFile(sourceFile); | 353 | var savedDocument = false; |
370 | 354 | ||
371 | if (document is null) | 355 | if (this.TryOpenSourceFile(sourceFile, out var document)) |
372 | { | 356 | { |
373 | return 1; | 357 | this.FormatDocument(document); |
374 | } | ||
375 | |||
376 | this.FormatDocument(document); | ||
377 | 358 | ||
378 | // Fix Messages if requested and necessary. | 359 | // Fix Messages if requested and necessary. |
379 | if (saveConvertedFile && 0 < this.Messages) | 360 | if (saveConvertedFile && 0 < this.ConversionMessages.Count) |
380 | { | 361 | { |
381 | this.SaveDocument(document); | 362 | savedDocument = this.SaveDocument(document); |
363 | } | ||
382 | } | 364 | } |
383 | 365 | ||
384 | return this.Messages; | 366 | return this.ReportMessages(document, savedDocument); |
385 | } | 367 | } |
386 | 368 | ||
387 | /// <summary> | 369 | /// <summary> |
@@ -391,43 +373,73 @@ namespace WixToolset.Converters | |||
391 | /// <returns>The number of Messages found.</returns> | 373 | /// <returns>The number of Messages found.</returns> |
392 | public int FormatDocument(XDocument document) | 374 | public int FormatDocument(XDocument document) |
393 | { | 375 | { |
376 | this.Format(document); | ||
377 | |||
378 | return this.ReportMessages(document, false); | ||
379 | } | ||
380 | |||
381 | private void Convert(XDocument document) | ||
382 | { | ||
394 | // Reset the instance info. | 383 | // Reset the instance info. |
395 | this.Messages = 0; | 384 | this.ConversionMessages.Clear(); |
396 | this.SourceVersion = 0; | 385 | this.SourceVersion = 0; |
397 | this.Operation = ConvertOperation.Format; | 386 | this.Operation = ConvertOperation.Convert; |
398 | 387 | ||
399 | // Remove the declaration. | 388 | // Remove the declaration. |
400 | if (null != document.Declaration | 389 | if (null != document.Declaration |
401 | && this.OnInformation(ConverterTestType.DeclarationPresent, null, "This file contains an XML declaration on the first line.")) | 390 | && this.OnInformation(ConverterTestType.DeclarationPresent, document, "This file contains an XML declaration on the first line.")) |
402 | { | 391 | { |
403 | document.Declaration = null; | 392 | document.Declaration = null; |
404 | TrimLeadingText(document); | 393 | TrimLeadingText(document); |
405 | } | 394 | } |
406 | 395 | ||
396 | this.XRoot = document.Root; | ||
397 | |||
407 | // Start converting the nodes at the top. | 398 | // Start converting the nodes at the top. |
408 | this.ConvertNodes(document.Nodes(), 0); | 399 | this.ConvertNodes(document.Nodes(), 0); |
409 | this.RemoveUnusedNamespaces(document.Root); | 400 | this.RemoveUnusedNamespaces(document.Root); |
401 | } | ||
402 | |||
403 | private void Format(XDocument document) | ||
404 | { | ||
405 | // Reset the instance info. | ||
406 | this.ConversionMessages.Clear(); | ||
407 | this.SourceVersion = 0; | ||
408 | this.Operation = ConvertOperation.Format; | ||
410 | 409 | ||
411 | return this.Messages; | 410 | // Remove the declaration. |
411 | if (null != document.Declaration | ||
412 | && this.OnInformation(ConverterTestType.DeclarationPresent, document, "This file contains an XML declaration on the first line.")) | ||
413 | { | ||
414 | document.Declaration = null; | ||
415 | TrimLeadingText(document); | ||
416 | } | ||
417 | |||
418 | this.XRoot = document.Root; | ||
419 | |||
420 | // Start converting the nodes at the top. | ||
421 | this.ConvertNodes(document.Nodes(), 0); | ||
422 | this.RemoveUnusedNamespaces(document.Root); | ||
412 | } | 423 | } |
413 | 424 | ||
414 | private XDocument OpenSourceFile(string sourceFile) | 425 | private bool TryOpenSourceFile(string sourceFile, out XDocument document) |
415 | { | 426 | { |
416 | this.SourceFile = sourceFile; | 427 | this.SourceFile = sourceFile; |
417 | 428 | ||
418 | try | 429 | try |
419 | { | 430 | { |
420 | return XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | 431 | document = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); |
421 | } | 432 | } |
422 | catch (XmlException e) | 433 | catch (XmlException e) |
423 | { | 434 | { |
424 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); | 435 | this.OnError(ConverterTestType.XmlException, null, "The xml is invalid. Detail: '{0}'", e.Message); |
436 | document = null; | ||
425 | } | 437 | } |
426 | 438 | ||
427 | return null; | 439 | return document != null; |
428 | } | 440 | } |
429 | 441 | ||
430 | private void SaveDocument(XDocument document) | 442 | private bool SaveDocument(XDocument document) |
431 | { | 443 | { |
432 | var ignoreDeclarationError = this.IgnoreErrors.Contains(ConverterTestType.DeclarationPresent); | 444 | var ignoreDeclarationError = this.IgnoreErrors.Contains(ConverterTestType.DeclarationPresent); |
433 | 445 | ||
@@ -437,11 +449,15 @@ namespace WixToolset.Converters | |||
437 | { | 449 | { |
438 | document.Save(writer); | 450 | document.Save(writer); |
439 | } | 451 | } |
452 | |||
453 | return true; | ||
440 | } | 454 | } |
441 | catch (UnauthorizedAccessException) | 455 | catch (UnauthorizedAccessException) |
442 | { | 456 | { |
443 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); | 457 | this.OnError(ConverterTestType.UnauthorizedAccessException, null, "Could not write to file."); |
444 | } | 458 | } |
459 | |||
460 | return false; | ||
445 | } | 461 | } |
446 | 462 | ||
447 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) | 463 | private void ConvertNodes(IEnumerable<XNode> nodes, int level) |
@@ -2129,6 +2145,14 @@ namespace WixToolset.Converters | |||
2129 | convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); | 2145 | convertedAttribute = new XAttribute(ns.GetName(attribute.Name.LocalName), attribute.Value); |
2130 | } | 2146 | } |
2131 | 2147 | ||
2148 | if (convertedAttribute != attribute) | ||
2149 | { | ||
2150 | foreach (var message in attribute.Annotations<Message>()) | ||
2151 | { | ||
2152 | convertedAttribute.AddAnnotation(message); | ||
2153 | } | ||
2154 | } | ||
2155 | |||
2132 | element.Add(convertedAttribute); | 2156 | element.Add(convertedAttribute); |
2133 | } | 2157 | } |
2134 | } | 2158 | } |
@@ -2214,6 +2238,110 @@ namespace WixToolset.Converters | |||
2214 | } | 2238 | } |
2215 | } | 2239 | } |
2216 | 2240 | ||
2241 | private int ReportMessages(XDocument document, bool saved) | ||
2242 | { | ||
2243 | var conversionCount = this.ConversionMessages.Count; | ||
2244 | |||
2245 | // If the converted/formatted document was saved, update the source line numbers | ||
2246 | // in the messages since they will possibly be wrong. | ||
2247 | if (saved && conversionCount > 0) | ||
2248 | { | ||
2249 | var fixedupMessages = new HashSet<Message>(); | ||
2250 | |||
2251 | // Load the converted document so we can look up new line numbers for | ||
2252 | // messages. | ||
2253 | var convertedDocument = XDocument.Load(this.SourceFile, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | ||
2254 | var convertedNavigator = convertedDocument.CreateNavigator(); | ||
2255 | |||
2256 | // Look through all nodes in the document and try to fix up their line numbers. | ||
2257 | foreach (var elements in document.Descendants()) | ||
2258 | { | ||
2259 | var fixedup = this.FixupMessageLineNumbers(elements, convertedNavigator); | ||
2260 | fixedupMessages.AddRange(fixedup); | ||
2261 | |||
2262 | // Attributes are not considered nodes so they must be enumerated independently. | ||
2263 | if (elements is XElement element) | ||
2264 | { | ||
2265 | foreach (var attribute in element.Attributes()) | ||
2266 | { | ||
2267 | fixedup = this.FixupMessageLineNumbers(attribute, convertedNavigator); | ||
2268 | fixedupMessages.AddRange(fixedup); | ||
2269 | } | ||
2270 | } | ||
2271 | } | ||
2272 | |||
2273 | // For any messages that couldn't be fixed up, remove their line numbers since they point at lines | ||
2274 | // that are possibly wrong after the conversion. | ||
2275 | for (var i = 0; i < this.ConversionMessages.Count; ++i) | ||
2276 | { | ||
2277 | var message = this.ConversionMessages[i]; | ||
2278 | |||
2279 | if (!fixedupMessages.Contains(message)) | ||
2280 | { | ||
2281 | this.ConversionMessages[i] = new Message(new SourceLineNumber(this.SourceFile), message.Level, message.Id, message.ResourceNameOrFormat, message.MessageArgs); | ||
2282 | } | ||
2283 | } | ||
2284 | } | ||
2285 | |||
2286 | foreach (var message in this.ConversionMessages) | ||
2287 | { | ||
2288 | this.Messaging.Write(message); | ||
2289 | } | ||
2290 | |||
2291 | return conversionCount; | ||
2292 | } | ||
2293 | |||
2294 | private IReadOnlyCollection<Message> FixupMessageLineNumbers(XObject obj, XPathNavigator savedDocument) | ||
2295 | { | ||
2296 | var messages = obj.Annotations<Message>().ToList(); | ||
2297 | |||
2298 | if (messages.Count == 0) | ||
2299 | { | ||
2300 | return Array.Empty<Message>(); | ||
2301 | } | ||
2302 | |||
2303 | // We can't fix up line numbers based on attributes but we can fix up using their parent element, so do so. | ||
2304 | if (obj is XAttribute attribute) | ||
2305 | { | ||
2306 | obj = attribute.Parent; | ||
2307 | } | ||
2308 | |||
2309 | var fixedupMessages = new List<Message>(); | ||
2310 | |||
2311 | var modifiedLineNumber = this.GetModifiedNodeLineNumber((XElement)obj, savedDocument); | ||
2312 | |||
2313 | foreach (var message in messages) | ||
2314 | { | ||
2315 | var fixedupMessage = message; | ||
2316 | |||
2317 | // If the line number wasn't modified, the existing message's source line nuber is correct | ||
2318 | // so skip creating a new one. | ||
2319 | if (modifiedLineNumber != null) | ||
2320 | { | ||
2321 | var index = this.ConversionMessages.IndexOf(message); | ||
2322 | |||
2323 | fixedupMessage = new Message(modifiedLineNumber, message.Level, message.Id, message.ResourceNameOrFormat, message.MessageArgs); | ||
2324 | |||
2325 | this.ConversionMessages[index] = fixedupMessage; | ||
2326 | } | ||
2327 | |||
2328 | fixedupMessages.Add(fixedupMessage); | ||
2329 | } | ||
2330 | |||
2331 | return fixedupMessages; | ||
2332 | } | ||
2333 | |||
2334 | private SourceLineNumber GetModifiedNodeLineNumber(XElement element, XPathNavigator savedDocument) | ||
2335 | { | ||
2336 | var xpathToOld = CalculateXPath(element); | ||
2337 | |||
2338 | var newNode = savedDocument.SelectSingleNode(xpathToOld); | ||
2339 | var newLineNumber = (newNode as IXmlLineInfo).LineNumber; | ||
2340 | |||
2341 | var oldLineNumber = (element as IXmlLineInfo)?.LineNumber; | ||
2342 | return (oldLineNumber != newLineNumber) ? new SourceLineNumber(this.SourceFile, newLineNumber) : null; | ||
2343 | } | ||
2344 | |||
2217 | /// <summary> | 2345 | /// <summary> |
2218 | /// Output an error message to the console. | 2346 | /// Output an error message to the console. |
2219 | /// </summary> | 2347 | /// </summary> |
@@ -2250,10 +2378,8 @@ namespace WixToolset.Converters | |||
2250 | return false; | 2378 | return false; |
2251 | } | 2379 | } |
2252 | 2380 | ||
2253 | // Increase the message count. | 2381 | var sourceLine = SourceLineNumberForXmlLineInfo(this.SourceFile, node); |
2254 | this.Messages++; | ||
2255 | 2382 | ||
2256 | var sourceLine = (null == node) ? new SourceLineNumber(this.SourceFile ?? "wix.exe") : new SourceLineNumber(this.SourceFile, ((IXmlLineInfo)node).LineNumber); | ||
2257 | var prefix = String.Empty; | 2383 | var prefix = String.Empty; |
2258 | if (level == MessageLevel.Information) | 2384 | if (level == MessageLevel.Information) |
2259 | { | 2385 | { |
@@ -2268,11 +2394,34 @@ namespace WixToolset.Converters | |||
2268 | 2394 | ||
2269 | var msg = new Message(sourceLine, level, (int)converterTestType, format, args); | 2395 | var msg = new Message(sourceLine, level, (int)converterTestType, format, args); |
2270 | 2396 | ||
2271 | this.Messaging.Write(msg); | 2397 | // Add the message as a node annotation so it could be possible to remap source line numbers |
2398 | // to their new locations after converting/formatting (since lines of code could be moved). | ||
2399 | node?.AddAnnotation(msg); | ||
2400 | this.ConversionMessages.Add(msg); | ||
2272 | 2401 | ||
2273 | return true; | 2402 | return true; |
2274 | } | 2403 | } |
2275 | 2404 | ||
2405 | private static string CalculateXPath(XElement element) | ||
2406 | { | ||
2407 | var builder = new StringBuilder(); | ||
2408 | |||
2409 | while (element != null) | ||
2410 | { | ||
2411 | var index = element.Parent?.Elements().TakeWhile(e => e != element).Count() ?? 0; | ||
2412 | builder.Insert(0, $"/*[{index + 1}]"); | ||
2413 | |||
2414 | element = element.Parent; | ||
2415 | } | ||
2416 | |||
2417 | return builder.ToString(); | ||
2418 | } | ||
2419 | |||
2420 | private static SourceLineNumber SourceLineNumberForXmlLineInfo(string sourceFile, IXmlLineInfo lineInfo) | ||
2421 | { | ||
2422 | return (lineInfo?.HasLineInfo() == true) ? new SourceLineNumber(sourceFile, lineInfo.LineNumber) : new SourceLineNumber(sourceFile ?? "wix.exe"); | ||
2423 | } | ||
2424 | |||
2276 | /// <summary> | 2425 | /// <summary> |
2277 | /// Return an identifier based on passed file/directory name | 2426 | /// Return an identifier based on passed file/directory name |
2278 | /// </summary> | 2427 | /// </summary> |
@@ -2338,7 +2487,7 @@ namespace WixToolset.Converters | |||
2338 | comments = initialComments; | 2487 | comments = initialComments; |
2339 | var nonCommentNodes = new List<XNode>(); | 2488 | var nonCommentNodes = new List<XNode>(); |
2340 | 2489 | ||
2341 | foreach(var node in nodes) | 2490 | foreach (var node in nodes) |
2342 | { | 2491 | { |
2343 | if (XmlNodeType.Comment == node.NodeType) | 2492 | if (XmlNodeType.Comment == node.NodeType) |
2344 | { | 2493 | { |
diff --git a/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs b/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs index d186931b..cd6849d3 100644 --- a/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs +++ b/src/wix/test/WixToolsetTest.Converters/ConverterFixture.cs | |||
@@ -3,6 +3,8 @@ | |||
3 | namespace WixToolsetTest.Converters | 3 | namespace WixToolsetTest.Converters |
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.IO; | ||
7 | using System.Linq; | ||
6 | using System.Xml.Linq; | 8 | using System.Xml.Linq; |
7 | using WixBuildTools.TestSupport; | 9 | using WixBuildTools.TestSupport; |
8 | using WixToolset.Converters; | 10 | using WixToolset.Converters; |
@@ -62,7 +64,7 @@ namespace WixToolsetTest.Converters | |||
62 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); | 64 | var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); |
63 | 65 | ||
64 | var messaging = new MockMessaging(); | 66 | var messaging = new MockMessaging(); |
65 | var converter = new WixConverter(messaging, 2, ignoreErrors: new[] { "DeclarationPresent" } ); | 67 | var converter = new WixConverter(messaging, 2, ignoreErrors: new[] { "DeclarationPresent" }); |
66 | 68 | ||
67 | var errors = converter.ConvertDocument(document); | 69 | var errors = converter.ConvertDocument(document); |
68 | 70 | ||
@@ -103,6 +105,40 @@ namespace WixToolsetTest.Converters | |||
103 | } | 105 | } |
104 | 106 | ||
105 | [Fact] | 107 | [Fact] |
108 | public void CanConvertMainNamespaceFromDisk() | ||
109 | { | ||
110 | var dataFolder = TestData.Get("TestData", "FixDeclarationAndNamespace"); | ||
111 | |||
112 | var expected = new[] | ||
113 | { | ||
114 | "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", | ||
115 | " <Fragment />", | ||
116 | "</Wix>", | ||
117 | }; | ||
118 | |||
119 | using (var fs = new TestDataFolderFileSystem()) | ||
120 | { | ||
121 | fs.Initialize(dataFolder); | ||
122 | var path = Path.Combine(fs.BaseFolder, "FixDeclarationAndNamespace.wxs"); | ||
123 | |||
124 | var messaging = new MockMessaging(); | ||
125 | var converter = new WixConverter(messaging, 2, null, null); | ||
126 | |||
127 | var errors = converter.ConvertFile(path, true); | ||
128 | |||
129 | var messages = messaging.Messages.Select(m => $"{Path.GetFileName(m.SourceLineNumbers.FileName)}({m.SourceLineNumbers.LineNumber}) {m.ToString()}").ToArray(); | ||
130 | WixAssert.CompareLineByLine(new[] | ||
131 | { | ||
132 | "FixDeclarationAndNamespace.wxs() [Converted] This file contains an XML declaration on the first line. (DeclarationPresent)", | ||
133 | "FixDeclarationAndNamespace.wxs(1) [Converted] The namespace 'http://schemas.microsoft.com/wix/2006/wi' is out of date. It must be 'http://wixtoolset.org/schemas/v4/wxs'. (XmlnsValueWrong)" | ||
134 | }, messages); | ||
135 | |||
136 | var actual = File.ReadAllLines(path); | ||
137 | WixAssert.CompareLineByLine(expected, actual); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | [Fact] | ||
106 | public void CanConvertNamedMainNamespace() | 142 | public void CanConvertNamedMainNamespace() |
107 | { | 143 | { |
108 | var parse = String.Join(Environment.NewLine, | 144 | var parse = String.Join(Environment.NewLine, |
diff --git a/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs b/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs index a8908df7..fdebc6b0 100644 --- a/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs +++ b/src/wix/test/WixToolsetTest.Converters/ConverterIntegrationFixture.cs | |||
@@ -85,9 +85,14 @@ namespace WixToolsetTest.Converters | |||
85 | 85 | ||
86 | var messaging = new MockMessaging(); | 86 | var messaging = new MockMessaging(); |
87 | var converter = new WixConverter(messaging, 4); | 87 | var converter = new WixConverter(messaging, 4); |
88 | var errors = converter.ConvertFile(targetFile, true); | 88 | var convertedCount = converter.ConvertFile(targetFile, true); |
89 | 89 | ||
90 | Assert.Single(messaging.Messages.Where(m => m.Id == 5/*WixConverter.ConverterTestType.UnauthorizedAccessException*/)); | 90 | var errors = messaging.Messages.Where(m => m.Level == WixToolset.Data.MessageLevel.Error).Select(m => m.ToString()).ToArray(); |
91 | WixAssert.CompareLineByLine(new[] | ||
92 | { | ||
93 | "Could not write to file. (UnauthorizedAccessException)" | ||
94 | }, errors); | ||
95 | Assert.Equal(10, convertedCount); | ||
91 | } | 96 | } |
92 | } | 97 | } |
93 | 98 | ||
diff --git a/src/wix/test/WixToolsetTest.Converters/TestData/FixDeclarationAndNamespace/FixDeclarationAndNamespace.wxs b/src/wix/test/WixToolsetTest.Converters/TestData/FixDeclarationAndNamespace/FixDeclarationAndNamespace.wxs new file mode 100644 index 00000000..a8923337 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/TestData/FixDeclarationAndNamespace/FixDeclarationAndNamespace.wxs | |||
@@ -0,0 +1,4 @@ | |||
1 | <?xml version='1.0' encoding='utf-8'?> | ||
2 | <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'> | ||
3 | <Fragment /> | ||
4 | </Wix> | ||