@joebigelow / wix / commits / d1050931

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