main
cs 298 lines 12.4 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.BurnE2E
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using Microsoft.Win32;
9 using WixInternal.TestSupport;
10 using WixTestTools;
11 using WixToolset.BootstrapperApplicationApi;
12 using Xunit;
13 using Xunit.Abstractions;
14
15 public class CacheTests : BurnE2ETests
16 {
17 public CacheTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
18
19 private void SkipIf5GBFileUnavailable()
20 {
21 // Recreate the 5GB payload to avoid having to copy it to the VM to run the tests.
22 const long FiveGB = 5_368_709_120;
23 const long OneGB = 1_073_741_824;
24 var targetFilePath = Path.Combine(this.TestContext.TestDataFolder, "fivegb.file");
25
26 // If the drive has less than 5GB (for the test file) plus 1GB (for working space), then
27 // skip the test.
28 var drive = new DriveInfo(targetFilePath.Substring(0, 1));
29 if (drive.AvailableFreeSpace < FiveGB + OneGB)
30 {
31 WixAssert.Skip($"Skipping {this.TestContext.TestName} because there is not enough disk space available to run the test.");
32 }
33
34 if (!File.Exists(targetFilePath))
35 {
36 var testExeTool = new TestExeTool
37 {
38 Arguments = "/lf \"" + targetFilePath + $"|{FiveGB}\"",
39 ExpectedExitCode = 0,
40 };
41 testExeTool.Run(true);
42 }
43 }
44
45 [LongRuntimeFact]
46 public void CanCache5GBFile()
47 {
48 this.SkipIf5GBFileUnavailable();
49
50 var packageA = this.CreatePackageInstaller("PackageA");
51 var bundleC = this.CreateBundleInstaller("BundleC");
52
53 packageA.VerifyInstalled(false);
54
55 bundleC.Install();
56 bundleC.VerifyRegisteredAndInPackageCache();
57
58 packageA.VerifyInstalled(true);
59 }
60
61 private string Cache5GBFileFromDownload(bool disableRangeRequests)
62 {
63 this.SkipIf5GBFileUnavailable();
64
65 var packageA = this.CreatePackageInstaller("PackageA");
66 var bundleC = this.CreateBundleInstaller("BundleC");
67 var webServer = this.CreateWebServer();
68
69 webServer.AddFiles(new Dictionary<string, string>
70 {
71 { "/BundleC/fivegb.file", Path.Combine(this.TestContext.TestDataFolder, "fivegb.file") },
72 { "/BundleC/PackageA.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageA.msi") },
73 });
74 webServer.DisableRangeRequests = disableRangeRequests;
75 webServer.Start();
76
77 using var dfs = new DisposableFileSystem();
78 var separateDirectory = dfs.GetFolder(true);
79
80 // Manually copy bundle to separate directory and then run from there so the non-compressed payloads have to be resolved.
81 var bundleCFileInfo = new FileInfo(bundleC.Bundle);
82 var bundleCCopiedPath = Path.Combine(separateDirectory, bundleCFileInfo.Name);
83 bundleCFileInfo.CopyTo(bundleCCopiedPath);
84
85 packageA.VerifyInstalled(false);
86
87 var installLogPath = bundleC.Install(bundleCCopiedPath);
88 bundleC.VerifyRegisteredAndInPackageCache();
89
90 packageA.VerifyInstalled(true);
91
92 return installLogPath;
93 }
94
95 [LongRuntimeFact]
96 public void CanCache5GBFileFromDownloadWithRangeRequestSupport()
97 {
98 var logPath = this.Cache5GBFileFromDownload(false);
99
100 Assert.False(LogVerifier.MessageInLogFile(logPath, "Range request not supported for URL: http://localhost:9999/e2e/BundleC/fivegb.file"));
101 Assert.False(LogVerifier.MessageInLogFile(logPath, "Content-Length not returned for URL: http://localhost:9999/e2e/BundleC/fivegb.file"));
102 }
103
104 [LongRuntimeFact]
105 public void CanCache5GBFileFromDownloadWithoutRangeRequestSupport()
106 {
107 var logPath = this.Cache5GBFileFromDownload(true);
108
109 Assert.True(LogVerifier.MessageInLogFile(logPath, "Range request not supported for URL: http://localhost:9999/e2e/BundleC/fivegb.file"));
110 Assert.False(LogVerifier.MessageInLogFile(logPath, "Content-Length not returned for URL: http://localhost:9999/e2e/BundleC/fivegb.file"));
111 }
112
113 [RuntimeFact]
114 public void CanDownloadPayloadsFromMissingAttachedContainer()
115 {
116 var packageA = this.CreatePackageInstaller("PackageA");
117 var packageB = this.CreatePackageInstaller("PackageB");
118 var bundleA = this.CreateBundleInstaller("BundleA");
119 var testBAController = this.CreateTestBAController();
120 var webServer = this.CreateWebServer();
121
122 webServer.AddFiles(new Dictionary<string, string>
123 {
124 { "/BundleA/PackageA.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageA.msi") },
125 { "/BundleA/PackageB.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageB.msi") },
126 });
127 webServer.DisableHeadResponses = true;
128 webServer.Start();
129
130 // Don't install PackageB initially so it will be installed when run from the package cache.
131 testBAController.SetPackageRequestedState("PackageB", RequestState.Absent);
132
133 packageA.VerifyInstalled(false);
134 packageB.VerifyInstalled(false);
135
136 // Manually copy bundle to separate directory, install from there, and then delete it
137 // so that when run from the package cache, it can't find the attached container.
138 using (var dfs = new DisposableFileSystem())
139 {
140 var tempDirectory = dfs.GetFolder(true);
141
142 var bundleAFileInfo = new FileInfo(bundleA.Bundle);
143 var bundleACopiedPath = Path.Combine(tempDirectory, bundleAFileInfo.Name);
144 bundleAFileInfo.CopyTo(bundleACopiedPath);
145
146 bundleA.Install(bundleACopiedPath);
147 }
148
149 var bundlePackageRegistration = bundleA.VerifyRegisteredAndInPackageCache();
150
151 packageA.VerifyInstalled(true);
152 packageB.VerifyInstalled(false);
153
154 testBAController.SetPackageRequestedState("PackageB", RequestState.Present);
155
156 var modifyLogPath = bundleA.Modify(bundlePackageRegistration.CachePath);
157 bundleA.VerifyRegisteredAndInPackageCache();
158
159 packageA.VerifyInstalled(true);
160 packageB.VerifyInstalled(true);
161
162 Assert.True(LogVerifier.MessageInLogFile(modifyLogPath, "Ignoring failure to get size and time for URL: http://localhost:9999/e2e/BundleA/PackageB.msi (error 0x80070002)"));
163 }
164
165 [RuntimeFact]
166 public void CanFindAttachedContainerFromRenamedBundle()
167 {
168 var packageA = this.CreatePackageInstaller("PackageA");
169 var packageB = this.CreatePackageInstaller("PackageB");
170 var bundleB = this.CreateBundleInstaller("BundleB");
171 var testBAController = this.CreateTestBAController();
172
173 // Don't install PackageB initially so it will be installed when run from the package cache.
174 testBAController.SetPackageRequestedState("PackageB", RequestState.Absent);
175
176 packageA.VerifyInstalled(false);
177 packageB.VerifyInstalled(false);
178
179 // Manually copy bundle to separate directory with new name and install from there
180 // so that when run from the package cache, it has to get the attached container from the renamed bundle.
181 using (var dfs = new DisposableFileSystem())
182 {
183 var tempDirectory = dfs.GetFolder(true);
184
185 var bundleBFileInfo = new FileInfo(bundleB.Bundle);
186 var bundleBCopiedPath = Path.Combine(tempDirectory, "RenamedBundle.exe");
187 bundleBFileInfo.CopyTo(bundleBCopiedPath);
188
189 bundleB.Install(bundleBCopiedPath);
190
191 var bundlePackageRegistration = bundleB.VerifyRegisteredAndInPackageCache();
192
193 packageA.VerifyInstalled(true);
194 packageB.VerifyInstalled(false);
195
196 testBAController.SetPackageRequestedState("PackageB", RequestState.Present);
197
198 bundleB.Modify(bundlePackageRegistration.CachePath);
199 bundleB.VerifyRegisteredAndInPackageCache();
200
201 packageA.VerifyInstalled(true);
202 packageB.VerifyInstalled(true);
203 }
204 }
205
206 [RuntimeFact]
207 public void CanHandleCachedBundleInPackageCacheInUse()
208 {
209 var packageA = this.CreatePackageInstaller("PackageA");
210 var packageB = this.CreatePackageInstaller("PackageB");
211 var bundleA = this.CreateBundleInstaller("BundleA");
212
213 bundleA.Install();
214
215 var registration = bundleA.VerifyRegisteredAndInPackageCache();
216 var cachedBundlePath = registration.CachePath;
217
218 packageA.VerifyInstalled(true);
219 packageB.VerifyInstalled(true);
220
221 // Lock the bundle file in the package cache.
222 string modifyLogPath;
223 using (FileStream lockTargetFile = new FileStream(cachedBundlePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
224 {
225 modifyLogPath = bundleA.Modify();
226 }
227
228 bundleA.VerifyRegisteredAndInPackageCache();
229 packageA.VerifyInstalled(true);
230 packageB.VerifyInstalled(true);
231
232 Assert.True(LogVerifier.MessageInLogFile(modifyLogPath, "w345: Ignoring failure to cache bundle because the file already exists, encountered error: 0x80070020. Continuing..."));
233 }
234
235 [RuntimeFact]
236 public void CanGetEngineWorkingDirectoryFromCommandLine()
237 {
238 var bundleA = this.CreateBundleInstaller("BundleA");
239 var testBAController = this.CreateTestBAController();
240
241 using (var dfs = new DisposableFileSystem())
242 {
243 var baseTempPath = dfs.GetFolder(true);
244 var logPath = bundleA.Install(0, $"-burn.engine.working.directory=\"{baseTempPath}\"");
245 Assert.True(LogVerifier.MessageInLogFileRegex(logPath, $"Caching bundle from: '{baseTempPath.Replace("\\", "\\\\")}\\\\.*\\\\.be\\\\BundleA.exe' to: 'C:\\\\ProgramData\\\\Package Cache\\\\.*\\\\BundleA.exe'"));
246 }
247 }
248
249 [RuntimeFact]
250 public void CanGetEngineWorkingDirectoryFromPolicy()
251 {
252 var deletePolicyKey = false;
253 string originalPolicyValue = null;
254
255 var bundleA = this.CreateBundleInstaller("BundleA");
256 var testBAController = this.CreateTestBAController();
257 var policyPath = bundleA.GetFullBurnPolicyRegistryPath();
258
259 try
260 {
261 using (var dfs = new DisposableFileSystem())
262 {
263 var baseTempPath = dfs.GetFolder(true);
264
265 var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true);
266 if (policyKey == null)
267 {
268 policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true);
269 deletePolicyKey = true;
270 }
271
272 using (policyKey)
273 {
274 originalPolicyValue = policyKey.GetValue("EngineWorkingDirectory") as string;
275 policyKey.SetValue("EngineWorkingDirectory", baseTempPath);
276 }
277
278 var logPath = bundleA.Install();
279 Assert.True(LogVerifier.MessageInLogFileRegex(logPath, $"Caching bundle from: '{baseTempPath.Replace("\\", "\\\\")}\\\\.*\\\\.be\\\\BundleA.exe' to: 'C:\\\\ProgramData\\\\Package Cache\\\\.*\\\\BundleA.exe'"));
280 }
281 }
282 finally
283 {
284 if (deletePolicyKey)
285 {
286 Registry.LocalMachine.DeleteSubKeyTree(policyPath);
287 }
288 else if (originalPolicyValue != null)
289 {
290 using (var policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true))
291 {
292 policyKey.SetValue("EngineWorkingDirectory", originalPolicyValue);
293 }
294 }
295 }
296 }
297 }
298 }