Add conversion for BootstrapperApplicationData to BundleCustomData.
#6176
Sean Hall committed
Mar 12, 2021 at 17:11 UTC
d1050931018d6e8dd06189202492df666dfbb594
3 files changed
+337
-19
src/WixToolset.Converters/FixupCommandBase.cs
+18
@@ -29,6 +29,8 @@ namespace WixToolset.Converters
29
30
protected bool ShowHelp { get; set; }
31
32
+ protected CustomTableTarget CustomTableSetting { get; set; }
33
+
34
protected bool DryRun { get; set; }
35
36
protected HashSet<string> ErrorsAsWarnings { get; }
@@ -68,6 +70,22 @@ namespace WixToolset.Converters
70
this.StopParsing = true;
71
return true;
72
73
+ case "-custom-table":
74
+ var customTableSetting = parser.GetNextArgumentOrError(argument);
75
+ switch (customTableSetting)
76
+ {
77
+ case "bundle":
78
+ this.CustomTableSetting = CustomTableTarget.Bundle;
79
+ break;
80
+ case "msi":
81
+ this.CustomTableSetting = CustomTableTarget.Msi;
82
+ break;
83
+ default:
84
+ parser.ReportErrorArgument(argument);
85
+ break;
86
+ }
87
+ return true;
88
+
89
case "n":
90
case "-dry-run":
91
this.DryRun = true;
src/WixToolset.Converters/WixConverter.cs
+147
-13
@@ -14,6 +14,27 @@ namespace WixToolset.Converters
14
using WixToolset.Data;
15
using WixToolset.Extensibility.Services;
16
17
+ /// <summary>
18
+ /// How to convert CustomTable elements.
19
+ /// </summary>
20
+ public enum CustomTableTarget
21
+ {
22
+ /// <summary>
23
+ /// Ambiguous elements will be left alone.
24
+ /// </summary>
25
+ Unknown,
26
+
27
+ /// <summary>
28
+ /// Use CustomTable, CustomTableRef, and Unreal.
29
+ /// </summary>
30
+ Msi,
31
+
32
+ /// <summary>
33
+ /// Use BundleCustomData and BundleCustomDataRef.
34
+ /// </summary>
35
+ Bundle,
36
+ }
37
+
38
/// <summary>
39
/// WiX source code converter.
40
/// </summary>
@@ -45,19 +66,25 @@ namespace WixToolset.Converters
66
private static readonly XName BootstrapperApplicationDllElementName = WixNamespace + "BootstrapperApplicationDll";
67
private static readonly XName BootstrapperApplicationRefElementName = WixNamespace + "BootstrapperApplicationRef";
68
private static readonly XName ApprovedExeForElevationElementName = WixNamespace + "ApprovedExeForElevation";
48
- private static readonly XName EmbeddedChainerElementName = WixNamespace + "EmbeddedChainer";
69
+ private static readonly XName BundleAttributeElementName = WixNamespace + "BundleAttribute";
70
+ private static readonly XName BundleAttributeDefinitionElementName = WixNamespace + "BundleAttributeDefinition";
71
+ private static readonly XName BundleCustomDataElementName = WixNamespace + "BundleCustomData";
72
+ private static readonly XName BundleCustomDataRefElementName = WixNamespace + "BundleCustomDataRef";
73
+ private static readonly XName BundleElementElementName = WixNamespace + "BundleElement";
74
+ private static readonly XName CustomTableElementName = WixNamespace + "CustomTable";
75
+ private static readonly XName CustomTableRefElementName = WixNamespace + "CustomTableRef";
76
private static readonly XName CatalogElementName = WixNamespace + "Catalog";
77
private static readonly XName ColumnElementName = WixNamespace + "Column";
78
private static readonly XName ComponentElementName = WixNamespace + "Component";
79
private static readonly XName ControlElementName = WixNamespace + "Control";
80
private static readonly XName ConditionElementName = WixNamespace + "Condition";
81
private static readonly XName CreateFolderElementName = WixNamespace + "CreateFolder";
55
- private static readonly XName CustomTableElementName = WixNamespace + "CustomTable";
82
private static readonly XName DataElementName = WixNamespace + "Data";
83
private static readonly XName OldProvidesElementName = WixDependencyNamespace + "Provides";
84
private static readonly XName OldRequiresElementName = WixDependencyNamespace + "Requires";
85
private static readonly XName OldRequiresRefElementName = WixDependencyNamespace + "RequiresRef";
86
private static readonly XName DirectoryElementName = WixNamespace + "Directory";
87
+ private static readonly XName EmbeddedChainerElementName = WixNamespace + "EmbeddedChainer";
88
private static readonly XName ErrorElementName = WixNamespace + "Error";
89
private static readonly XName FeatureElementName = WixNamespace + "Feature";
90
private static readonly XName FileElementName = WixNamespace + "File";
@@ -85,6 +112,7 @@ namespace WixToolset.Converters
112
private static readonly XName RemotePayloadElementName = WixNamespace + "RemotePayload";
113
private static readonly XName RegistrySearchElementName = WixNamespace + "RegistrySearch";
114
private static readonly XName RequiredPrivilegeElementName = WixNamespace + "RequiredPrivilege";
115
+ private static readonly XName RowElementName = WixNamespace + "Row";
116
private static readonly XName ServiceArgumentElementName = WixNamespace + "ServiceArgument";
117
private static readonly XName SetDirectoryElementName = WixNamespace + "SetDirectory";
118
private static readonly XName SetPropertyElementName = WixNamespace + "SetProperty";
@@ -156,7 +184,8 @@ namespace WixToolset.Converters
184
/// <param name="indentationAmount">Indentation value to use when validating leading whitespace.</param>
185
/// <param name="errorsAsWarnings">Test errors to display as warnings.</param>
186
/// <param name="ignoreErrors">Test errors to ignore.</param>
159
- public WixConverter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null)
187
+ /// <param name="customTableTarget">How to convert CustomTable elements.</param>
188
+ public WixConverter(IMessaging messaging, int indentationAmount, IEnumerable<string> errorsAsWarnings = null, IEnumerable<string> ignoreErrors = null, CustomTableTarget customTableTarget = CustomTableTarget.Unknown)
189
{
190
this.ConvertElementMapping = new Dictionary<XName, Action<XElement>>
191
{
@@ -170,9 +199,10 @@ namespace WixToolset.Converters
199
{ WixConverter.ApprovedExeForElevationElementName, this.ConvertApprovedExeForElevationElement },
200
{ WixConverter.CatalogElementName, this.ConvertCatalogElement },
201
{ WixConverter.ColumnElementName, this.ConvertColumnElement },
173
- { WixConverter.CustomTableElementName, this.ConvertCustomTableElement },
174
- { WixConverter.ControlElementName, this.ConvertControlElement },
202
{ WixConverter.ComponentElementName, this.ConvertComponentElement },
203
+ { WixConverter.ControlElementName, this.ConvertControlElement },
204
+ { WixConverter.CustomActionElementName, this.ConvertCustomActionElement },
205
+ { WixConverter.CustomTableElementName, this.ConvertCustomTableElement },
206
{ WixConverter.DataElementName, this.ConvertDataElement },
207
{ WixConverter.DirectoryElementName, this.ConvertDirectoryElement },
208
{ WixConverter.FeatureElementName, this.ConvertFeatureElement },
@@ -198,7 +228,6 @@ namespace WixToolset.Converters
228
{ WixConverter.RegistrySearchElementName, this.ConvertRegistrySearchElement },
229
{ WixConverter.RemotePayloadElementName, this.ConvertRemotePayloadElement },
230
{ WixConverter.RequiredPrivilegeElementName, this.ConvertRequiredPrivilegeElement },
201
- { WixConverter.CustomActionElementName, this.ConvertCustomActionElement },
231
{ WixConverter.ServiceArgumentElementName, this.ConvertServiceArgumentElement },
232
{ WixConverter.SetDirectoryElementName, this.ConvertSetDirectoryElement },
233
{ WixConverter.SetPropertyElementName, this.ConvertSetPropertyElement },
@@ -224,8 +253,12 @@ namespace WixToolset.Converters
253
this.ErrorsAsWarnings = new HashSet<ConverterTestType>(this.YieldConverterTypes(errorsAsWarnings));
254
255
this.IgnoreErrors = new HashSet<ConverterTestType>(this.YieldConverterTypes(ignoreErrors));
256
+
257
+ this.CustomTableSetting = customTableTarget;
258
}
259
260
+ private CustomTableTarget CustomTableSetting { get; }
261
+
262
private int Errors { get; set; }
263
264
private HashSet<ConverterTestType> ErrorsAsWarnings { get; set; }
@@ -717,11 +750,104 @@ namespace WixToolset.Converters
750
private void ConvertCustomTableElement(XElement element)
751
{
752
var bootstrapperApplicationData = element.Attribute("BootstrapperApplicationData");
720
- if (bootstrapperApplicationData != null
721
- && this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name))
753
+ if (bootstrapperApplicationData?.Value == "no")
754
+ {
755
+ if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name))
756
+ {
757
+ bootstrapperApplicationData.Remove();
758
+ }
759
+ }
760
+ else
761
+ {
762
+ if (element.Elements(ColumnElementName).Any() || bootstrapperApplicationData != null)
763
+ {
764
+ // Table definition
765
+ if (bootstrapperApplicationData != null)
766
+ {
767
+ switch (this.CustomTableSetting)
768
+ {
769
+ case CustomTableTarget.Bundle:
770
+ if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'BundleCustomData' element for Bundles.", bootstrapperApplicationData.Name))
771
+ {
772
+ element.Name = WixConverter.BundleCustomDataElementName;
773
+ bootstrapperApplicationData.Remove();
774
+ this.ConvertCustomTableElementToBundle(element);
775
+ }
776
+ break;
777
+ case CustomTableTarget.Msi:
778
+ if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name))
779
+ {
780
+ element.Add(new XAttribute("Unreal", bootstrapperApplicationData.Value));
781
+ bootstrapperApplicationData.Remove();
782
+ }
783
+ break;
784
+ default:
785
+ this.OnError(ConverterTestType.CustomTableNotAlwaysConvertable, element, "The CustomTable element contains deprecated '{0}' attribute so can't be converted. Use the 'Unreal' attribute for MSI. Use the 'BundleCustomData' element for Bundles. Use the --custom-table argument to force conversion to 'msi' or 'bundle'", bootstrapperApplicationData.Name);
786
+ break;
787
+ }
788
+ }
789
+ }
790
+ else
791
+ {
792
+ // Table ref
793
+ switch (this.CustomTableSetting)
794
+ {
795
+ case CustomTableTarget.Bundle:
796
+ if (this.OnError(ConverterTestType.CustomTableRef, element, "CustomTable elements that don't contain the table definition are now BundleCustomDataRef for Bundles."))
797
+ {
798
+ element.Name = WixConverter.BundleCustomDataRefElementName;
799
+ this.ConvertCustomTableElementToBundle(element);
800
+ }
801
+ break;
802
+ case CustomTableTarget.Msi:
803
+ if (this.OnError(ConverterTestType.CustomTableRef, element, "CustomTable elements that don't contain the table definition are now CustomTableRef for MSI."))
804
+ {
805
+ element.Name = WixConverter.CustomTableRefElementName;
806
+ }
807
+ break;
808
+ default:
809
+ this.OnError(ConverterTestType.CustomTableNotAlwaysConvertable, element, "The CustomTable element contains no 'Column' elements so can't be converted. Use the 'CustomTableRef' element for MSI. Use the 'BundleCustomDataRef' element for Bundles. Use the --custom-table argument to force conversion to 'msi' or 'bundle'");
810
+ break;
811
+ }
812
+ }
813
+ }
814
+ }
815
+
816
+ private void ConvertCustomTableElementToBundle(XElement element)
817
+ {
818
+ foreach (var xColumn in element.Elements(ColumnElementName))
819
{
723
- element.Add(new XAttribute("Unreal", bootstrapperApplicationData.Value));
724
- bootstrapperApplicationData.Remove();
820
+ xColumn.Name = WixConverter.BundleAttributeDefinitionElementName;
821
+
822
+ foreach (var xAttribute in xColumn.Attributes().ToList())
823
+ {
824
+ if (xAttribute.Name.LocalName != "Id" &&
825
+ (xAttribute.Name.Namespace == WixConverter.Wix3Namespace ||
826
+ xAttribute.Name.Namespace == WixConverter.WixNamespace ||
827
+ String.IsNullOrEmpty(xAttribute.Name.Namespace.NamespaceName)))
828
+ {
829
+ xAttribute.Remove();
830
+ }
831
+ }
832
+ }
833
+
834
+ foreach (var xRow in element.Elements(RowElementName))
835
+ {
836
+ xRow.Name = WixConverter.BundleElementElementName;
837
+
838
+ foreach (var xData in xRow.Elements(DataElementName))
839
+ {
840
+ xData.Name = WixConverter.BundleAttributeElementName;
841
+
842
+ var xColumn = xData.Attribute("Column");
843
+ if (xColumn != null)
844
+ {
845
+ xData.Add(new XAttribute("Id", xColumn.Value));
846
+ xColumn.Remove();
847
+ }
848
+
849
+ this.ConvertInnerTextToAttribute(xData, "Value");
850
+ }
851
}
852
}
853
@@ -1165,8 +1291,6 @@ namespace WixToolset.Converters
1291
1292
private void ConvertRequiredPrivilegeElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Name");
1293
1168
- private void ConvertRowElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value");
1169
-
1294
private void ConvertDataElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value");
1295
1296
private void ConvertSequenceElement(XElement element)
@@ -1854,7 +1978,7 @@ namespace WixToolset.Converters
1978
AssignDirectoryNameFromShortName,
1979
1980
/// <summary>
1857
- /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal.
1981
+ /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal for MSI. Use BundleCustomData element for Bundles.
1982
/// </summary>
1983
BootstrapperApplicationDataDeprecated,
1984
@@ -2022,6 +2146,16 @@ namespace WixToolset.Converters
2146
/// The hash algorithm used for bundles changed from SHA1 to SHA512.
2147
/// </summary>
2148
BurnHashAlgorithmChanged,
2149
+
2150
+ /// <summary>
2151
+ /// CustomTable elements can't always be converted.
2152
+ /// </summary>
2153
+ CustomTableNotAlwaysConvertable,
2154
+
2155
+ /// <summary>
2156
+ /// CustomTable elements that don't contain the table definition are now CustomTableRef.
2157
+ /// </summary>
2158
+ CustomTableRef,
2159
}
2160
}
2161
}
src/test/WixToolsetTest.Converters/CustomTableFixture.cs
+172
-6
@@ -83,7 +83,7 @@ namespace WixToolsetTest.Converters
83
var converter = new WixConverter(messaging, 2, null, null);
84
85
var errors = converter.ConvertDocument(document);
86
- Assert.Equal(3, errors);
86
+ Assert.Equal(4, errors);
87
88
var actualLines = UnformattedDocumentLines(document);
89
WixAssert.CompareLineByLine(expected, actualLines);
@@ -126,7 +126,7 @@ namespace WixToolsetTest.Converters
126
var converter = new WixConverter(messaging, 2, null, null);
127
128
var errors = converter.ConvertDocument(document);
129
- Assert.Equal(2, errors);
129
+ Assert.Equal(3, errors);
130
131
var actualLines = UnformattedDocumentLines(document);
132
WixAssert.CompareLineByLine(expected, actualLines);
@@ -162,14 +162,154 @@ namespace WixToolsetTest.Converters
162
var converter = new WixConverter(messaging, 2, null, null);
163
164
var errors = converter.ConvertDocument(document);
165
- Assert.Equal(2, errors);
165
+ Assert.Equal(3, errors);
166
167
var actualLines = UnformattedDocumentLines(document);
168
WixAssert.CompareLineByLine(expected, actualLines);
169
}
170
171
[Fact]
172
- public void CanConvertCustomTableBootstrapperApplicationData()
172
+ public void CanConvertBundleCustomTableBootstrapperApplicationData()
173
+ {
174
+ var parse = String.Join(Environment.NewLine,
175
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
176
+ " <CustomTable Id='FgAppx' BootstrapperApplicationData='yes'>",
177
+ " <Column Id='Column1' PrimaryKey='yes' Type='string' Width='0' Category='text' Description='The first custom column.' />",
178
+ " <Row>",
179
+ " <Data Column='Column1'>Row1</Data>",
180
+ " </Row>",
181
+ " </CustomTable>",
182
+ "</Wix>");
183
+
184
+ var expected = String.Join(Environment.NewLine,
185
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
186
+ " <BundleCustomData Id=\"FgAppx\">",
187
+ " <BundleAttributeDefinition Id=\"Column1\" />",
188
+ " <BundleElement>",
189
+ " <BundleAttribute Id=\"Column1\" Value=\"Row1\" />",
190
+ " </BundleElement>",
191
+ " </BundleCustomData>",
192
+ "</Wix>");
193
+
194
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
195
+
196
+ var messaging = new MockMessaging();
197
+ var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Bundle);
198
+
199
+ var errors = converter.ConvertDocument(document);
200
+
201
+ var actual = UnformattedDocumentString(document);
202
+
203
+ Assert.Equal(2, errors);
204
+ Assert.Equal(expected, actual);
205
+ }
206
+
207
+ [Fact]
208
+ public void CanConvertBundleCustomTableRef()
209
+ {
210
+ var parse = String.Join(Environment.NewLine,
211
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
212
+ " <CustomTable Id='FgAppx'>",
213
+ " <Row>",
214
+ " <Data Column='Column1'>Row1</Data>",
215
+ " </Row>",
216
+ " </CustomTable>",
217
+ "</Wix>");
218
+
219
+ var expected = String.Join(Environment.NewLine,
220
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
221
+ " <BundleCustomDataRef Id=\"FgAppx\">",
222
+ " <BundleElement>",
223
+ " <BundleAttribute Id=\"Column1\" Value=\"Row1\" />",
224
+ " </BundleElement>",
225
+ " </BundleCustomDataRef>",
226
+ "</Wix>");
227
+
228
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
229
+
230
+ var messaging = new MockMessaging();
231
+ var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Bundle);
232
+
233
+ var errors = converter.ConvertDocument(document);
234
+
235
+ var actual = UnformattedDocumentString(document);
236
+
237
+ Assert.Equal(2, errors);
238
+ Assert.Equal(expected, actual);
239
+ }
240
+
241
+ [Fact]
242
+ public void CanConvertMsiCustomTableBootstrapperApplicationData()
243
+ {
244
+ var parse = String.Join(Environment.NewLine,
245
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
246
+ " <CustomTable Id='FgAppx' BootstrapperApplicationData='yes'>",
247
+ " <Column Id='Column1' PrimaryKey='yes' Type='string' Width='0' Category='text' Description='The first custom column.' />",
248
+ " <Row>",
249
+ " <Data Column='Column1'>Row1</Data>",
250
+ " </Row>",
251
+ " </CustomTable>",
252
+ "</Wix>");
253
+
254
+ var expected = String.Join(Environment.NewLine,
255
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
256
+ " <CustomTable Id=\"FgAppx\" Unreal=\"yes\">",
257
+ " <Column Id=\"Column1\" PrimaryKey=\"yes\" Type=\"string\" Width=\"0\" Category=\"text\" Description=\"The first custom column.\" />",
258
+ " <Row>",
259
+ " <Data Column=\"Column1\" Value=\"Row1\" />",
260
+ " </Row>",
261
+ " </CustomTable>",
262
+ "</Wix>");
263
+
264
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
265
+
266
+ var messaging = new MockMessaging();
267
+ var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Msi);
268
+
269
+ var errors = converter.ConvertDocument(document);
270
+
271
+ var actual = UnformattedDocumentString(document);
272
+
273
+ Assert.Equal(2, errors);
274
+ Assert.Equal(expected, actual);
275
+ }
276
+
277
+ [Fact]
278
+ public void CanConvertMsiCustomTableRef()
279
+ {
280
+ var parse = String.Join(Environment.NewLine,
281
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
282
+ " <CustomTable Id='FgAppx'>",
283
+ " <Row>",
284
+ " <Data Column='Column1'>Row1</Data>",
285
+ " </Row>",
286
+ " </CustomTable>",
287
+ "</Wix>");
288
+
289
+ var expected = String.Join(Environment.NewLine,
290
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
291
+ " <CustomTableRef Id=\"FgAppx\">",
292
+ " <Row>",
293
+ " <Data Column=\"Column1\" Value=\"Row1\" />",
294
+ " </Row>",
295
+ " </CustomTableRef>",
296
+ "</Wix>");
297
+
298
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
299
+
300
+ var messaging = new MockMessaging();
301
+ var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Msi);
302
+
303
+ var errors = converter.ConvertDocument(document);
304
+
305
+ var actual = UnformattedDocumentString(document);
306
+
307
+ Assert.Equal(2, errors);
308
+ Assert.Equal(expected, actual);
309
+ }
310
+
311
+ [Fact]
312
+ public void CanDetectAmbiguousCustomTableBootstrapperApplicationData()
313
{
314
var parse = String.Join(Environment.NewLine,
315
"<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
@@ -178,13 +318,39 @@ namespace WixToolsetTest.Converters
318
319
var expected = String.Join(Environment.NewLine,
320
"<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
181
- " <CustomTable Id=\"FgAppx\" Unreal=\"yes\" />",
321
+ " <CustomTable Id=\"FgAppx\" BootstrapperApplicationData=\"yes\" />",
322
"</Wix>");
323
324
var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
325
326
var messaging = new MockMessaging();
187
- var converter = new WixConverter(messaging, 2, null, null);
327
+ var converter = new WixConverter(messaging, 2);
328
+
329
+ var errors = converter.ConvertDocument(document);
330
+
331
+ var actual = UnformattedDocumentString(document);
332
+
333
+ Assert.Equal(1, errors);
334
+ Assert.Equal(expected, actual);
335
+ }
336
+
337
+ [Fact]
338
+ public void CanRemoveBootstrapperApplicationDataFromRealCustomTable()
339
+ {
340
+ var parse = String.Join(Environment.NewLine,
341
+ "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>",
342
+ " <CustomTable Id='FgAppx' BootstrapperApplicationData='no' />",
343
+ "</Wix>");
344
+
345
+ var expected = String.Join(Environment.NewLine,
346
+ "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">",
347
+ " <CustomTable Id=\"FgAppx\" />",
348
+ "</Wix>");
349
+
350
+ var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
351
+
352
+ var messaging = new MockMessaging();
353
+ var converter = new WixConverter(messaging, 2);
354
355
var errors = converter.ConvertDocument(document);
356