From 0c911d93a81ae3bd2f5ba211cb25649042cdac61 Mon Sep 17 00:00:00 2001 From: "Ronald M. Martin" Date: Thu, 5 Oct 2023 16:46:23 -0400 Subject: [PATCH] Add tests to simulate issue 7739. Generalize the issue to apply to most situations in which inner text is used in Wix 3 to represent condidtions. These all sufferfrom the mishandling of whitespace othere than the space character gets converted into an attribute value. The XML parser generally treats all white space the same and reduces contiguous white space to a single space character. All condition expressions are reduced to single line expressions. The user is free to reformat his conveerted code by inserting white space characters for the optimum readability readability. The ne --- src/wix/WixToolset.Converters/WixConverter.cs | 51 +++- .../ConditionFixture.cs | 239 +++++++++++++++++- 2 files changed, 283 insertions(+), 7 deletions(-) diff --git a/src/wix/WixToolset.Converters/WixConverter.cs b/src/wix/WixToolset.Converters/WixConverter.cs index f3675ecc6..143000c8c 100644 --- a/src/wix/WixToolset.Converters/WixConverter.cs +++ b/src/wix/WixToolset.Converters/WixConverter.cs @@ -1080,7 +1080,7 @@ private void ConvertComponentElement(XElement element) var xCondition = element.Element(ConditionElementName); if (xCondition != null) { - if (TryGetInnerText(xCondition, out var text, out var comments) && + if (TryGetNormalizedInnerText(xCondition, out var text, out var comments) && this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", xCondition.Name.LocalName)) { using (var lab = new ConversionLab(element)) @@ -1192,7 +1192,7 @@ private void ConvertFeatureElement(XElement element) { var level = xCondition.Attribute("Level")?.Value; if (!String.IsNullOrEmpty(level) && - TryGetInnerText(xCondition, out var text, out var comments) && + TryGetNormalizedInnerText(xCondition, out var text, out var comments) && this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Level' element instead.", xCondition.Name.LocalName)) { using (var lab = new ConversionLab(xCondition)) @@ -1237,7 +1237,7 @@ private void ConvertLaunchConditionElement(XElement element) var message = element.Attribute("Message")?.Value; if (!String.IsNullOrEmpty(message) && - TryGetInnerText(element, out var text, out var comments) && + TryGetNormalizedInnerText(element, out var text, out var comments) && this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Launch' element instead.", element.Name.LocalName)) { using (var lab = new ConversionLab(element)) @@ -1290,7 +1290,7 @@ private void ConvertPermissionExElement(XElement element) var xCondition = element.Element(ConditionElementName); if (xCondition != null) { - if (TryGetInnerText(xCondition, out var text, out var comments) && + if (TryGetNormalizedInnerText(xCondition, out var text, out var comments) && this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", xCondition.Name.LocalName)) { using (var lab = new ConversionLab(xCondition)) @@ -1669,7 +1669,7 @@ private void ConvertCustomActionRefElement(XElement element) private void ConvertPublishElement(XElement element) { - if (TryGetInnerText(element, out var text, out var comments) && + if (TryGetNormalizedInnerText(element, out var text, out var comments) && this.OnInformation(ConverterTestType.InnerTextDeprecated, element, "Using {0} element text is deprecated. Use the 'Condition' attribute instead.", element.Name.LocalName)) { using (var lab = new ConversionLab(element)) @@ -2144,7 +2144,7 @@ private void ConvertWixLocalizationUIElement(XElement element) private void ConvertInnerTextToAttribute(XElement element, string attributeName) { - if (TryGetInnerText(element, out var text, out var comments)) + if (TryGetNormalizedInnerText(element, out var text, out var comments)) { // If the target attribute already exists, error if we have anything more than whitespace. var attribute = element.Attribute(attributeName); @@ -2854,6 +2854,45 @@ private static void RenameElementToStandardDirectory(XElement element) } } + // If TryGetInnerText succeeds, normalize the string value it returns by replacing all \r's \n's with spaces, + // then replacing all multiple spacws with single spaces. This is suitable for use in an XText representing a condition. + private static bool TryGetNormalizedInnerText(XElement element, out string value, out List comments) + { + var found = TryGetInnerText(element, out var localValue, out comments); + + if (found && null != localValue) + + { + var whiteSpace = false; + var sb = new StringBuilder(); + + foreach (var c in localValue) + { + if ('\u0020' == c || '\u000a' == c || '\u000d' == c) + { + if (!whiteSpace) + { + whiteSpace = true; + sb.Append('\u0020'); + } + } + else + { + whiteSpace = false; + sb.Append(c); + } + } + + value = sb.ToString(); + } + else + { + value = localValue; + } + + return found; + } + private static bool TryGetInnerText(XElement element, out string value, out List comments) { return TryGetInnerText(element, out value, out comments, new List()); diff --git a/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs b/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs index e3563c60c..b293d627c 100644 --- a/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs +++ b/src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs @@ -305,6 +305,78 @@ public void FixComponentConditionWithComment() WixAssert.CompareLineByLine(expected, actualLines); } + [Fact] + public void FixMultilineComponentCondition() + { + var parse = String.Join(Environment.NewLine, + "", + "", + " ", + " ", + " 1<2", + " ", + " ", + ""); + + var expected = new[] + { + "", + " ", + " ", + " ", + "" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(3, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(expected, actualLines); + } + + [Fact] + public void FixMultilineComponentConditionWithComment() + { + var parse = String.Join(Environment.NewLine, + "", + "", + " ", + " ", + " ", + " ", + " 1<2 OR ", + " 4<3", + " ", + " ", + " ", + ""); + var expected = new[] + { + "", + " ", + " ", + " ", + " ", + "" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(3, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(expected, actualLines); + } + [Fact] public void FixEmptyCondition() { @@ -480,6 +552,90 @@ public void FixFeatureConditionWithComment() WixAssert.CompareLineByLine(expected, actualLines); } + [Fact] + public void FixMultilineFeatureCondition() + { + var parse = String.Join(Environment.NewLine, + "", + "", + " ", + " ", + " ", + " (NOT Installed AND NOT PERUSER) OR", + " (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)", + " ", + " ", + " ", + " ", + ""); + + var expected = new[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(3, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(expected, actualLines); + } + + [Fact] + public void FixMultilineFeatureConditionWithComment() + { + var parse = String.Join(Environment.NewLine, + "", + "", + " ", + " ", + " ", + " ", + " (NOT Installed AND NOT PERUSER) OR", + " (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)", + " ", + " ", + " ", + " ", + ""); + + var expected = new[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(3, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(expected, actualLines); + } + [Fact] public void FixLaunchConditionInFragment() { @@ -519,7 +675,7 @@ public void FixLaunchConditionInFragment() } [Fact] - public void FixLaunchConditionWithComment() + public void FixLaunchConditionInFragmentWithComment() { var parse = String.Join(Environment.NewLine, "", @@ -558,6 +714,87 @@ public void FixLaunchConditionWithComment() WixAssert.CompareLineByLine(expected, actualLines); } + [Fact] + public void FixMultilineLaunchConditionInFragment() + { + var parse = String.Join(Environment.NewLine, + "", + "", + " ", + " ", + " 1<2 OR ", + " 4<3", + " ", + " ", + " 1=2", + " ", + " ", + ""); + + var expected = new[] + { + "", + " ", + " ", + " ", + " ", + "" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(4, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(expected, actualLines); + } + + [Fact] + public void FixMultilineLaunchConditionInFragmentWithComment() + { + var parse = String.Join(Environment.NewLine, + "", + "", + " ", + " ", + " ", + " 1<2 OR", + " 4<3", + " ", + " ", + " 1=2", + " ", + " ", + ""); + + var expected = new[] + { + "", + " ", + " ", + " ", + " ", + " ", + " ", + "" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(4, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(expected, actualLines); + } + [Fact] public void FixLaunchConditionInProduct() {