Skip to content

Commit

Permalink
Add tests to simulate issue 7739.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
cpuwzd committed Oct 8, 2023
1 parent 0014af6 commit 0c911d9
Show file tree
Hide file tree
Showing 2 changed files with 283 additions and 7 deletions.
51 changes: 45 additions & 6 deletions src/wix/WixToolset.Converters/WixConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<XNode> 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<XNode> comments)
{
return TryGetInnerText(element, out value, out comments, new List<XNode>());
Expand Down
239 changes: 238 additions & 1 deletion src/wix/test/WixToolsetTest.Converters/ConditionFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,78 @@ public void FixComponentConditionWithComment()
WixAssert.CompareLineByLine(expected, actualLines);
}

[Fact]
public void FixMultilineComponentCondition()
{
var parse = String.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
" <Fragment>",
" <Component Id='Comp1' Directory='ApplicationFolder'>",
" <Condition>1&lt;2</Condition>",
" </Component>",
" </Fragment>",
"</Wix>");

var expected = new[]
{
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
" <Fragment>",
" <Component Id=\"Comp1\" Directory=\"ApplicationFolder\" Condition=\"1&lt;2\" />",
" </Fragment>",
"</Wix>"
};

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,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
" <Fragment>",
" <Component Id='Comp1' Directory='ApplicationFolder'>",
" <Condition>",
" <!-- Comment -->",
" 1&lt;2 OR ",
" 4&lt;3",
" </Condition>",
" </Component>",
" </Fragment>",
"</Wix>");
var expected = new[]
{
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
" <Fragment>",
" <!-- Comment -->",
" <Component Id=\"Comp1\" Directory=\"ApplicationFolder\" Condition=\"1&lt;2 OR 4&lt;3\" />",
" </Fragment>",
"</Wix>"
};

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()
{
Expand Down Expand Up @@ -480,6 +552,90 @@ public void FixFeatureConditionWithComment()
WixAssert.CompareLineByLine(expected, actualLines);
}

[Fact]
public void FixMultilineFeatureCondition()
{
var parse = String.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
" <Fragment>",
" <Feature Id='ShellExtensionFeature' Level='0' InstallDefault='local' >",
" <Condition Level='1'>",
" (NOT Installed AND NOT PERUSER) OR",
" (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)",
" </Condition>",
" <ComponentGroupRef Id='ShellExtComponents' />",
" </Feature>",
" </Fragment>",
"</Wix>");

var expected = new[]
{
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
" <Fragment>",
" <Feature Id=\"ShellExtensionFeature\" Level=\"0\" InstallDefault=\"local\">",
" <Level Value=\"1\" Condition=\"(NOT Installed AND NOT PERUSER) OR (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)\" />",
" <ComponentGroupRef Id=\"ShellExtComponents\" />",
" </Feature>",
" </Fragment>",
"</Wix>"
};

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,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
" <Fragment>",
" <Feature Id='ShellExtensionFeature' Level='0' InstallDefault='local' >",
" <Condition Level='1'>",
" <!-- Comment -->",
" (NOT Installed AND NOT PERUSER) OR",
" (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)",
" </Condition>",
" <ComponentGroupRef Id='ShellExtComponents' />",
" </Feature>",
" </Fragment>",
"</Wix>");

var expected = new[]
{
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
" <Fragment>",
" <Feature Id=\"ShellExtensionFeature\" Level=\"0\" InstallDefault=\"local\">",
" <!-- Comment -->",
" <Level Value=\"1\" Condition=\"(NOT Installed AND NOT PERUSER) OR (Installed AND ZOS_SHELL_EXTENSION_INSTALLLED)\" />",
" <ComponentGroupRef Id=\"ShellExtComponents\" />",
" </Feature>",
" </Fragment>",
"</Wix>"
};

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()
{
Expand Down Expand Up @@ -519,7 +675,7 @@ public void FixLaunchConditionInFragment()
}

[Fact]
public void FixLaunchConditionWithComment()
public void FixLaunchConditionInFragmentWithComment()
{
var parse = String.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
Expand Down Expand Up @@ -558,6 +714,87 @@ public void FixLaunchConditionWithComment()
WixAssert.CompareLineByLine(expected, actualLines);
}

[Fact]
public void FixMultilineLaunchConditionInFragment()
{
var parse = String.Join(Environment.NewLine,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
" <Fragment>",
" <Condition Message='Stop the install'>",
" 1&lt;2 OR ",
" 4&lt;3",
" </Condition>",
" <Condition Message='Do not stop'>",
" 1=2",
" </Condition>",
" </Fragment>",
"</Wix>");

var expected = new[]
{
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
" <Fragment>",
" <Launch Condition=\"1&lt;2 OR 4&lt;3\" Message=\"Stop the install\" />",
" <Launch Condition=\"1=2\" Message=\"Do not stop\" />",
" </Fragment>",
"</Wix>"
};

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,
"<?xml version=\"1.0\" encoding=\"utf-16\"?>",
"<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>",
" <Fragment>",
" <Condition Message='Stop the install'>",
" <!-- Comment 1 -->",
" 1&lt;2 OR",
" 4&lt;3",
" </Condition>",
" <Condition Message='Do not stop'>",
" <!-- Comment 2 -->1=2",
" </Condition>",
" </Fragment>",
"</Wix>");

var expected = new[]
{
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
" <Fragment>",
" <!-- Comment 1 -->",
" <Launch Condition=\"1&lt;2 OR 4&lt;3\" Message=\"Stop the install\" />",
" <!-- Comment 2 -->",
" <Launch Condition=\"1=2\" Message=\"Do not stop\" />",
" </Fragment>",
"</Wix>"
};

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()
{
Expand Down

0 comments on commit 0c911d9

Please sign in to comment.