main
cs 701 lines 33.7 KB
Raw
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 WixToolsetTest.CoreIntegration
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.Diagnostics;
9 using System.IO;
10 using System.Linq;
11 using System.Runtime.InteropServices;
12 using System.Text;
13 using System.Text.RegularExpressions;
14 using System.Xml;
15 using System.Xml.Linq;
16 using Example.Extension;
17 using WixInternal.TestSupport;
18 using WixInternal.Core.TestPackage;
19 using WixToolset.Data;
20 using WixToolset.Data.Burn;
21 using WixToolset.Data.WindowsInstaller;
22 using WixToolset.Dtf.Compression.Cab;
23 using Xunit;
24
25 public class PatchFixture : IDisposable
26 {
27 private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd";
28 private static readonly XName TargetProductCodeName = PatchNamespace + "TargetProductCode";
29
30 private readonly DisposableFileSystem tempFileSystem;
31 private readonly string tempBaseFolder;
32 private readonly string templateSourceFolder;
33 private readonly string templateBaselinePdb;
34 private readonly string templateUpdatePdb;
35 private readonly string templateUpdateNoFilesChangedPdb;
36
37 public PatchFixture()
38 {
39 this.tempFileSystem = new DisposableFileSystem();
40 this.tempBaseFolder = this.tempFileSystem.GetFolder();
41
42 this.templateSourceFolder = TestData.Get(@"TestData", "PatchTemplatePackage");
43 var tempFolderBaseline = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "baseline");
44 var tempFolderUpdate = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "update");
45 var tempFolderUpdateNoFileChanges = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "updatewithoutfilechanges");
46
47 this.templateBaselinePdb = BuildMsi("Baseline.msi", this.templateSourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0", bindpaths: new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") });
48 this.templateUpdatePdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1", bindpaths: new[] { Path.Combine(this.templateSourceFolder, ".update-data") });
49 this.templateUpdateNoFilesChangedPdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdateNoFileChanges, "1.0.1", "1.0.1", "1.0.1", bindpaths: new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") });
50 }
51
52 public void Dispose()
53 {
54 this.tempFileSystem.Dispose();
55 }
56
57 [Fact]
58 public void CanBuildSimplePatchUsingWixpdbs()
59 {
60 var folder = TestData.Get(@"TestData", "PatchSingle");
61
62 using (var fs = new DisposableFileSystem())
63 {
64 var tempFolder = fs.GetFolder();
65
66 var baselinePath = BuildMsi("Baseline.msi", folder, tempFolder, "1.0.0", "1.0.0", "1.0.0");
67 var update1Path = BuildMsi("Update.msi", folder, tempFolder, "1.0.1", "1.0.1", "1.0.1");
68 var patchPath = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1");
69
70 var doc = GetExtractPatchXml(patchPath);
71 WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(TargetProductCodeName).Value);
72
73 var names = Query.GetSubStorageNames(patchPath);
74 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
75
76 var cab = Path.Combine(tempFolder, "foo.cab");
77 Query.ExtractStream(patchPath, "foo.cab", cab);
78
79 var files = Query.GetCabinetFiles(cab);
80 WixAssert.CompareLineByLine(new[] { "a.txt", "b.txt" }, files.Select(f => f.Name).ToArray());
81 }
82 }
83
84 [Fact]
85 public void CanBuildSimplePatchWithNewFileAndFilteringUsingWixpdbs()
86 {
87 var folder = TestData.Get(@"TestData", "PatchWithAddedFile");
88
89 using (var fs = new DisposableFileSystem())
90 {
91 var tempFolder = fs.GetFolder();
92
93 var baselinePath = BuildMsi("Baseline.msi", folder, tempFolder, "1.0.0", "1.0.0", "1.0.0");
94 var update1Path = BuildMsi("Update.msi", folder, tempFolder, "1.0.1", "1.0.1", "1.0.1", "TRUE");
95 var patchPath = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1", warningsAsErrors: false);
96
97 var doc = GetExtractPatchXml(patchPath);
98 WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(TargetProductCodeName).Value);
99
100 var names = Query.GetSubStorageNames(patchPath);
101 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
102
103 var cab = Path.Combine(tempFolder, "foo.cab");
104 Query.ExtractStream(patchPath, "foo.cab", cab);
105
106 var files = Query.GetCabinetFiles(cab);
107 WixAssert.CompareLineByLine(new[] { "c.txt" }, files.Select(f => f.Name).ToArray());
108 }
109 }
110
111 [Fact]
112 public void CanBuildSimplePatchWithFileChangesUsingMsi()
113 {
114 var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChangesUsingMsi");
115
116 using (var fs = new DisposableFileSystem())
117 {
118 var baseFolder = fs.GetFolder();
119 var tempFolderPatch = Path.Combine(baseFolder, "patch");
120
121 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) });
122
123 var doc = GetExtractPatchXml(patchPath);
124 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value);
125
126 var names = Query.GetSubStorageNames(patchPath);
127 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
128
129 var cab = Path.Combine(baseFolder, "foo.cab");
130 Query.ExtractStream(patchPath, "foo.cab", cab);
131
132 var files = Query.GetCabinetFiles(cab);
133 var file = files.Single();
134 WixAssert.StringEqual("a.txt", file.Name);
135 var contents = file.OpenText().ReadToEnd();
136 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents);
137 }
138 }
139
140 [Fact]
141 public void CanBuildSimplePatchWithFileChangesUsingWixpdb()
142 {
143 var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChanges");
144
145 using (var fs = new DisposableFileSystem())
146 {
147 var baseFolder = fs.GetFolder();
148 var tempFolderPatch = Path.Combine(baseFolder, "patch");
149
150 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) });
151
152 var doc = GetExtractPatchXml(patchPath);
153 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value);
154
155 var names = Query.GetSubStorageNames(patchPath);
156 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
157
158 var cab = Path.Combine(baseFolder, "foo.cab");
159 Query.ExtractStream(patchPath, "foo.cab", cab);
160
161 var files = Query.GetCabinetFiles(cab);
162 var file = files.Single();
163 WixAssert.StringEqual("a.txt", file.Name);
164 var contents = file.OpenText().ReadToEnd();
165 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents);
166 }
167 }
168
169 [Fact]
170 public void CanBuildSimplePatchWithFileChangesUsingWixpdbAndAlternativeUpdatedSourceFolder()
171 {
172 var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChanges");
173
174 using (var fs = new DisposableFileSystem())
175 {
176 var baseFolder = fs.GetFolder();
177 var tempFolderPatch = Path.Combine(baseFolder, "patch");
178
179 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) }, updateBindpaths: new[] { Path.Combine(this.templateSourceFolder, ".update-data-alternative") });
180
181 var doc = GetExtractPatchXml(patchPath);
182 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value);
183
184 var names = Query.GetSubStorageNames(patchPath);
185 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
186
187 var cab = Path.Combine(baseFolder, "foo.cab");
188 Query.ExtractStream(patchPath, "foo.cab", cab);
189
190 var files = Query.GetCabinetFiles(cab);
191 var file = files.Single();
192 WixAssert.StringEqual("a.txt", file.Name);
193 var contents = file.OpenText().ReadToEnd();
194 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data-alternative' folder in 'PatchTemplatePackage'.\r\n\r\nDiam quis enim lobortis scelerisque fermentum dui faucibus in ornare.\r\n", contents);
195 }
196 }
197
198 [Fact]
199 public void CanBuildPatchFromAdminImage()
200 {
201 var sourceFolder = TestData.Get(@"TestData", "PatchUsingAdminImages");
202
203 var baseFolder = this.tempFileSystem.GetFolder();
204 var tempFolderPatch = Path.Combine(baseFolder, "patch");
205 var adminBaselineFolder = Path.Combine(baseFolder, "admin-baseline");
206 var adminUpdateFolder = Path.Combine(baseFolder, "admin-update");
207
208 CreateAdminImage(this.templateBaselinePdb, adminBaselineFolder);
209 CreateAdminImage(this.templateUpdatePdb, adminUpdateFolder);
210
211 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { adminBaselineFolder, adminUpdateFolder });
212
213 var doc = GetExtractPatchXml(patchPath);
214 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value);
215
216 var names = Query.GetSubStorageNames(patchPath);
217 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
218
219 var cab = Path.Combine(baseFolder, "foo.cab");
220 Query.ExtractStream(patchPath, "foo.cab", cab);
221
222 var files = Query.GetCabinetFiles(cab);
223 var file = files.Single();
224 WixAssert.StringEqual("a.txt", file.Name);
225 var contents = file.OpenText().ReadToEnd();
226 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents);
227 }
228
229 [Fact]
230 public void CanBuildSimplePatchWithNoFileChanges()
231 {
232 var folder = TestData.Get(@"TestData", "PatchNoFileChanges");
233
234 using (var fs = new DisposableFileSystem())
235 {
236 var tempFolder = fs.GetFolder();
237
238 var patchPath = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdateNoFilesChangedPdb) }, hasNoFiles: true);
239
240 var doc = GetExtractPatchXml(patchPath);
241 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value);
242
243 var names = Query.GetSubStorageNames(patchPath);
244 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
245
246 var cab = Path.Combine(tempFolder, "foo.cab");
247 Query.ExtractStream(patchPath, "foo.cab", cab);
248
249 var files = Query.GetCabinetFiles(cab);
250 Assert.Empty(files);
251 }
252 }
253
254 [Fact]
255 public void CanBuildSimplePatchWithSoftwareTag()
256 {
257 var folder = TestData.Get(@"TestData", "PatchWithSoftwareTag");
258
259 using (var fs = new DisposableFileSystem())
260 {
261 var baseFolder = fs.GetFolder();
262 var tempFolderBaseline = Path.Combine(baseFolder, "baseline");
263 var tempFolderUpdate = Path.Combine(baseFolder, "update");
264 var tempFolderPatch = Path.Combine(baseFolder, "patch");
265
266 var baselinePath = BuildMsi("Baseline.msi", folder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0");
267 var update1Path = BuildMsi("Update.msi", folder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1");
268 var patchPath = BuildMsp("Patch1.msp", folder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePath), Path.GetDirectoryName(update1Path) });
269
270 var doc = GetExtractPatchXml(patchPath);
271 WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(TargetProductCodeName).Value);
272
273 var names = Query.GetSubStorageNames(patchPath);
274 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
275
276 var cab = Path.Combine(baseFolder, "foo.cab");
277 Query.ExtractStream(patchPath, "foo.cab", cab);
278
279 var files = Query.GetCabinetFiles(cab);
280 var file = files.Single();
281 WixAssert.StringEqual("tag1jwIT_7lT286E4Dyji95s65UuO4", file.Name);
282
283 var contents = file.OpenText().ReadToEnd();
284 contents = Regex.Replace(contents, @"msi\:package/[A-Z0-9\-]+", "msi:package/G-U-I-D");
285 WixAssert.StringEqual(String.Join(Environment.NewLine, new[]
286 {
287 "<?xml version='1.0' encoding='utf-8'?>",
288 "<SoftwareIdentity tagId='msi:package/G-U-I-D' name='~Test Package' version='1.0.1' versionScheme='multipartnumeric' xmlns='http://standards.iso.org/iso/19770/-2/2015/schema.xsd'>",
289 " <Entity name='Example Corporation' regid='regid.1995-08.com.example' role='softwareCreator tagCreator' />",
290 " <Meta persistentId='msi:upgrade/7D326855-E790-4A94-8611-5351F8321FCA' />",
291 "</SoftwareIdentity>",
292 }), contents.Replace('"', '\''));
293 }
294 }
295
296 [Fact]
297 public void CanBuildSimplePatchWithBaselineIdTooLong()
298 {
299 var folder = TestData.Get(@"TestData", "PatchBaselineIdTooLong");
300
301 using (var fs = new DisposableFileSystem())
302 {
303 var baseFolder = fs.GetFolder();
304 var tempFolderBaseline = Path.Combine(baseFolder, "baseline");
305 var tempFolderUpdate = Path.Combine(baseFolder, "update");
306 var tempFolderPatch = Path.Combine(baseFolder, "patch");
307
308 var patchPath = BuildMsp("Patch1.msp", folder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdateNoFilesChangedPdb) }, hasNoFiles: true, warningsAsErrors: false);
309
310 var doc = GetExtractPatchXml(patchPath);
311 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value);
312
313 var names = Query.GetSubStorageNames(patchPath);
314 WixAssert.CompareLineByLine(new[] { "#ThisBaseLineIdIsTooLongAndGe.1", "ThisBaseLineIdIsTooLongAndGe.1" }, names);
315 }
316 }
317
318 [Fact]
319 public void CanBuildPatchFromProductWithFilesFromWixlib()
320 {
321 var sourceFolder = TestData.Get(@"TestData", "PatchFromWixlib");
322
323 using (var fs = new DisposableFileSystem())
324 {
325 var baseFolder = fs.GetFolder();
326 var tempFolderBaseline = Path.Combine(baseFolder, "baseline");
327 var tempFolderUpdate = Path.Combine(baseFolder, "update");
328 var tempFolderPatch = Path.Combine(baseFolder, "patch");
329
330 var baselinePath = BuildMsi("Baseline.msi", sourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0");
331 var updatePath = BuildMsi("Update.msi", sourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1");
332 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePath), Path.GetDirectoryName(updatePath) }, hasNoFiles: true);
333
334 var doc = GetExtractPatchXml(patchPath);
335 WixAssert.StringEqual("{7C871EC1-1F89-4850-A6A9-D7A4C21769F6}", doc.Root.Element(TargetProductCodeName).Value);
336 }
337 }
338
339 [Fact]
340 public void CanBuildPatchFromProductWithAddedRemoveFileRows()
341 {
342 var sourceFolder = TestData.Get(@"TestData", "PatchWithRemovedRemoveFileRows");
343
344 using (var fs = new DisposableFileSystem())
345 {
346 var baseFolder = fs.GetFolder();
347 var tempFolderBaseline = Path.Combine(baseFolder, "baseline");
348 var tempFolderUpdate = Path.Combine(baseFolder, "update");
349 var tempFolderPatch = Path.Combine(baseFolder, "patch");
350
351 var baselinePath = BuildMsi("Baseline.msi", sourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0");
352 var updatePath = BuildMsi("Update.msi", sourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1");
353 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePath), Path.GetDirectoryName(updatePath) }, hasNoFiles: true);
354
355 var doc = GetExtractPatchXml(patchPath);
356 WixAssert.StringEqual("{6CB58995-A174-4A21-823E-3A114A81AB66}", doc.Root.Element(TargetProductCodeName).Value);
357 }
358 }
359
360 [Fact]
361 public void CanBuildBundleWithNonSpecificPatches()
362 {
363 var folder = TestData.Get(@"TestData", "PatchNonSpecific");
364
365 using (var fs = new DisposableFileSystem())
366 {
367 var tempFolder = fs.GetFolder();
368
369 var baselinePath = BuildMsi("Baseline.msi", Path.Combine(folder, "PackageA"), tempFolder, "1.0.0", "A", "B");
370 var updatePath = BuildMsi("Update.msi", Path.Combine(folder, "PackageA"), tempFolder, "1.0.1", "A", "B");
371 var patchAPath = BuildMsp("PatchA.msp", Path.Combine(folder, "PatchA"), tempFolder, "1.0.1", hasNoFiles: true);
372 var patchBPath = BuildMsp("PatchB.msp", Path.Combine(folder, "PatchB"), tempFolder, "1.0.1", hasNoFiles: true);
373 var patchCPath = BuildMsp("PatchC.msp", Path.Combine(folder, "PatchC"), tempFolder, "1.0.1", hasNoFiles: true);
374 var bundleAPath = BuildBundle("BundleA.exe", Path.Combine(folder, "BundleA"), tempFolder);
375 var bundleBPath = BuildBundle("BundleB.exe", Path.Combine(folder, "BundleB"), tempFolder);
376 var bundleCPath = BuildBundle("BundleC.exe", Path.Combine(folder, "BundleC"), tempFolder);
377
378 var bundleAPdb = Path.ChangeExtension(bundleAPath, ".wixpdb");
379 var bundleBPdb = Path.ChangeExtension(bundleBPath, ".wixpdb");
380 var bundleCPdb = Path.ChangeExtension(bundleCPath, ".wixpdb");
381
382 VerifyPatchTargetCodesInBurnManifest(bundleAPdb, new[]
383 {
384 "<PatchTargetCode TargetCode='{26309973-0A5E-4979-B142-98A6E064EDC0}' Product='yes' />",
385 });
386 VerifyPatchTargetCodesInBurnManifest(bundleBPdb, new[]
387 {
388 "<PatchTargetCode TargetCode='{26309973-0A5E-4979-B142-98A6E064EDC0}' Product='yes' />",
389 "<PatchTargetCode TargetCode='{32B0396A-CE36-4570-B16E-F88FA42DC409}' Product='no' />",
390 });
391 VerifyPatchTargetCodesInBurnManifest(bundleCPdb, new string[0]);
392 }
393 }
394
395 [Fact]
396 public void CanBuildBundleWithSlipstreamPatch()
397 {
398 var folder = TestData.Get(@"TestData", "PatchSingle");
399
400 using (var fs = new DisposableFileSystem())
401 {
402 var tempFolder = fs.GetFolder();
403
404 var baselinePath = BuildMsi("Baseline.msi", folder, tempFolder, "1.0.0", "1.0.0", "1.0.0");
405 var update1Path = BuildMsi("Update.msi", folder, tempFolder, "1.0.1", "1.0.1", "1.0.1");
406 var patchPath = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1");
407 var bundleAPath = BuildBundle("BundleA.exe", Path.Combine(folder, "BundleA"), tempFolder);
408 var bundleAPdb = Path.ChangeExtension(bundleAPath, ".wixpdb");
409
410 using (var wixOutput = WixOutput.Read(bundleAPdb))
411 {
412 var manifestData = wixOutput.GetData(BurnConstants.BurnManifestWixOutputStreamName);
413 var doc = new XmlDocument();
414 doc.LoadXml(manifestData);
415 var nsmgr = BundleExtractor.GetBurnNamespaceManager(doc, "w");
416 var slipstreamMspNodes = doc.SelectNodes("/w:BurnManifest/w:Chain/w:MsiPackage/w:SlipstreamMsp", nsmgr).GetTestXmlLines();
417 WixAssert.CompareLineByLine(new[]
418 {
419 "<SlipstreamMsp Id='PatchA' />",
420 }, slipstreamMspNodes);
421 }
422 }
423 }
424
425 [Fact]
426 public void CannotBuildPatchWithMissingBaseline()
427 {
428 var sourceFolder = TestData.Get(@"TestData", "PatchWithMissingBaseline");
429
430 using (var fs = new DisposableFileSystem())
431 {
432 var baseFolder = fs.GetFolder();
433 var tempFolderPatch = Path.Combine(baseFolder, "patch");
434 var templateBaselineFolder = Path.GetDirectoryName(this.templateBaselinePdb);
435 var templateUpdateFolder = Path.GetDirectoryName(this.templateUpdatePdb);
436
437 var result = BuildMspForResult("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { templateBaselineFolder, templateUpdateFolder });
438
439 var messages = result.Messages.Select(m => m.ToString().Replace(tempFolderPatch, "<tempFolderPatch>").Replace(templateBaselineFolder, "<templateBaselineFolder>").Replace(templateUpdateFolder, "<templateUpdateFolder>")).ToArray();
440 WixAssert.CompareLineByLine(new[]
441 {
442 @"Cannot find the WixPatchBaseline file 'Missing.wixpdb'. The following paths were checked: Missing.wixpdb, <tempFolderPatch>\bin\Missing.wixpdb, <templateBaselineFolder>\Missing.wixpdb, <templateUpdateFolder>\Missing.wixpdb",
443 }, messages);
444 }
445 }
446
447 [Fact]
448 public void CanBuildPatchWithFileFiltering()
449 {
450 var sourceFolder = TestData.Get(@"TestData", "PatchFamilyFileFilter");
451
452 using (var fs = new DisposableFileSystem())
453 {
454 var baseFolder = fs.GetFolder();
455 var tempFolderPatch = Path.Combine(baseFolder, "patch");
456
457 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) });
458
459 var mainTransform = ExtractBaselinePatch(patchPath, "RTM.1", baseFolder);
460 Assert.Null(mainTransform.Tables["Registry"]);
461 var fileRow = mainTransform.Tables["File"].Rows.Single();
462 Assert.Equal("a.txt", fileRow.FieldAsString(0));
463 Assert.Equal(152, fileRow.FieldAsInteger(3));
464
465 var pairedTransform = ExtractBaselinePatch(patchPath, "#RTM.1", baseFolder);
466 fileRow = mainTransform.Tables["File"].Rows.Single();
467 Assert.Equal("a.txt", fileRow.FieldAsString(0));
468 Assert.Equal(152, fileRow.FieldAsInteger(3));
469
470 var files = ExtractFilesFromPatchCabinet(patchPath, "foo.cab", baseFolder);
471 var file = files.Single();
472 var contents = file.OpenText().ReadToEnd();
473 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents);
474 }
475 }
476
477 [Fact]
478 public void CanBuildPatchWithRegistryFiltering()
479 {
480 var sourceFolder = TestData.Get(@"TestData", "PatchFamilyRegistryFilter");
481
482 using (var fs = new DisposableFileSystem())
483 {
484 var baseFolder = fs.GetFolder();
485 var tempFolderPatch = Path.Combine(baseFolder, "patch");
486
487 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) }, warningsAsErrors: false);
488
489 var mainTransform = ExtractBaselinePatch(patchPath, "RTM.1", baseFolder);
490 Assert.Null(mainTransform.Tables["File"]);
491 var row = mainTransform.Tables["Registry"].Rows.Single();
492 Assert.Equal("regWOrcuJr1c2LRNY5vB1ZXx6nPyLo", row.FieldAsString(0));
493 Assert.Equal("1.0.1", row.FieldAsString(4));
494
495 var pairedTransform = ExtractBaselinePatch(patchPath, "#RTM.1", baseFolder);
496 Assert.Null(pairedTransform.Tables["File"]);
497
498 var files = ExtractFilesFromPatchCabinet(patchPath, "foo.cab", baseFolder);
499 Assert.Empty(files);
500 }
501 }
502
503 private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB, string defineC = null, IEnumerable<string> bindpaths = null)
504 {
505 var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName));
506
507 var args = new List<string>
508 {
509 "build",
510 Path.Combine(sourceFolder, @"Package.wxs"),
511 "-d", "V=" + defineV,
512 "-d", "A=" + defineA,
513 "-d", "B=" + defineB,
514 "-d", "C=" + defineC ?? String.Empty,
515 "-bindpath", Path.Combine(sourceFolder, ".data"),
516 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
517 "-o", outputPath,
518 "-ext", ExtensionPaths.ExampleExtensionPath,
519 };
520
521 foreach (var additionaBindPath in bindpaths ?? Enumerable.Empty<string>())
522 {
523 args.Add("-bindpath");
524 args.Add(additionaBindPath);
525 }
526
527 var result = WixRunner.Execute(args.ToArray());
528
529 result.AssertSuccess();
530
531 return outputPath;
532 }
533
534 private static string BuildMst(string transformName, string baseFolder, string templateBaselinePdb, string templateUpdatePdb)
535 {
536 var outputPath = Path.Combine(baseFolder, transformName);
537
538 var args = new List<string>
539 {
540 "msi", "transform",
541 templateBaselinePdb,
542 templateUpdatePdb,
543 "-intermediateFolder", Path.Combine(baseFolder),
544 "-t", "patch",
545 "-o", outputPath,
546 };
547
548 var result = WixRunner.Execute(args.ToArray());
549
550 result.AssertSuccess();
551
552 return outputPath;
553 }
554
555 private static WixRunnerResult BuildMspForResult(string outputName, string sourceFolder, string baseFolder, string defineV, IEnumerable<string> bindpaths = null, IEnumerable<string> targetBindpaths = null, IEnumerable<string> updateBindpaths = null, bool hasNoFiles = false, bool warningsAsErrors = true)
556 {
557 var outputPath = Path.Combine(baseFolder, "bin", outputName);
558
559 var args = new List<string>
560 {
561 "build",
562 hasNoFiles ? "-sw1079" : " ",
563 Path.Combine(sourceFolder, @"Patch.wxs"),
564 "-d", "V=" + defineV,
565 "-bindpath", Path.Combine(baseFolder, "bin"),
566 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
567 "-o", outputPath
568 };
569
570 foreach (var additionalBindPath in bindpaths ?? Enumerable.Empty<string>())
571 {
572 args.Add("-bindpath");
573 args.Add(additionalBindPath);
574 }
575
576 foreach (var targetBindPath in targetBindpaths ?? Enumerable.Empty<string>())
577 {
578 args.Add("-bindpath:target");
579 args.Add(targetBindPath);
580 }
581
582 foreach (var updateBindpath in updateBindpaths ?? Enumerable.Empty<string>())
583 {
584 args.Add("-bindpath:update");
585 args.Add(updateBindpath);
586 }
587
588 var result = WixRunner.Execute(warningsAsErrors, args.ToArray());
589
590 return result;
591 }
592
593 private static string BuildMsp(string outputName, string sourceFolder, string baseFolder, string defineV, IEnumerable<string> bindpaths = null, IEnumerable<string> targetBindpaths = null, IEnumerable<string> updateBindpaths = null, bool hasNoFiles = false, bool warningsAsErrors = true)
594 {
595 var result = BuildMspForResult(outputName, sourceFolder, baseFolder, defineV, bindpaths, targetBindpaths, updateBindpaths, hasNoFiles, warningsAsErrors);
596
597 result.AssertSuccess();
598
599 return Path.Combine(baseFolder, "bin", outputName);
600 }
601
602 private static string BuildBundle(string outputName, string sourceFolder, string baseFolder)
603 {
604 var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName));
605
606 var result = WixRunner.Execute(new[]
607 {
608 "build",
609 Path.Combine(sourceFolder, @"Bundle.wxs"),
610 Path.Combine(sourceFolder, "..", "..", "BundleWithPackageGroupRef", "Bundle.wxs"),
611 "-bindpath", Path.Combine(sourceFolder, "..", "..", "SimpleBundle", "data"),
612 "-bindpath", Path.Combine(baseFolder, "bin"),
613 "-intermediateFolder", Path.Combine(baseFolder, "obj"),
614 "-o", outputPath
615 });
616
617 result.AssertSuccess();
618
619 return outputPath;
620 }
621
622 private static void CreateAdminImage(string msiPath, string targetDir)
623 {
624 var args = $"/a \"{Path.ChangeExtension(msiPath, "msi")}\" TARGETDIR=\"{targetDir}\" /qn";
625
626 var proc = Process.Start("msiexec.exe", args);
627 proc.WaitForExit(20000);
628
629 Assert.Equal(0, proc.ExitCode);
630 }
631
632 private static WindowsInstallerData DecompileMst(string transformPath, string baseFolder)
633 {
634 var outputPath = Path.ChangeExtension(transformPath, ".wixmst");
635
636 var args = new List<string>
637 {
638 "msi", "decompile",
639 transformPath,
640 "-intermediateFolder", Path.Combine(baseFolder),
641 "-o", outputPath,
642 };
643
644 var result = WixRunner.Execute(args.ToArray());
645
646 result.AssertSuccess();
647
648 return WindowsInstallerData.Load(outputPath);
649 }
650
651 private static WindowsInstallerData ExtractBaselinePatch(string patchPath, string substorageName, string baseFolder)
652 {
653 var mstPath = Path.Combine(baseFolder, substorageName, substorageName + ".mst");
654 Query.ExtractSubStorage(patchPath, substorageName, mstPath);
655
656 return DecompileMst(mstPath, baseFolder);
657 }
658
659 private static CabFileInfo[] ExtractFilesFromPatchCabinet(string patchPath, string cabinetName, string baseFolder)
660 {
661 var cab = Path.Combine(baseFolder, cabinetName);
662 Query.ExtractStream(patchPath, cabinetName, cab);
663
664 return Query.GetCabinetFiles(cab);
665 }
666
667 private static XDocument GetExtractPatchXml(string path)
668 {
669 var buffer = new StringBuilder(65535);
670 var size = buffer.Capacity;
671
672 var er = MsiExtractPatchXMLData(path, 0, buffer, ref size);
673 if (er != 0)
674 {
675 throw new Win32Exception(er);
676 }
677
678 return XDocument.Parse(buffer.ToString());
679 }
680
681 private static void VerifyPatchTargetCodesInBurnManifest(string pdbPath, string[] expected)
682 {
683 using (var wixOutput = WixOutput.Read(pdbPath))
684 {
685 var manifestData = wixOutput.GetData(BurnConstants.BurnManifestWixOutputStreamName);
686 var doc = new XmlDocument();
687 doc.LoadXml(manifestData);
688 var nsmgr = BundleExtractor.GetBurnNamespaceManager(doc, "w");
689 var patchTargetCodes = doc.SelectNodes("/w:BurnManifest/w:PatchTargetCode", nsmgr).GetTestXmlLines();
690
691 WixAssert.CompareLineByLine(expected, patchTargetCodes);
692 }
693 }
694
695 [DllImport("msi.dll", EntryPoint = "MsiExtractPatchXMLDataW", CharSet = CharSet.Unicode, ExactSpelling = true)]
696 private static extern int MsiExtractPatchXMLData(string szPatchPath, int dwReserved, StringBuilder szXMLData, ref int pcchXMLData);
697
698 [DllImport("msi.dll", EntryPoint = "MsiApplyPatchW", CharSet = CharSet.Unicode, ExactSpelling = true)]
699 private static extern int MsiApplyPatch(string szPatchPackage, string szInstallPackage, int eInstallType, string szCommandLine);
700 }
701 }