diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2022-06-24 12:28:27 -0500 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2022-06-27 11:14:21 -0500 |
commit | eb53852d7ae6838e54525eb57df1d8ce8a722f9b (patch) | |
tree | 7fa05bd6df1bce2e20d87c5fbacc1c658dc000aa /src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs | |
parent | 6ee12a64cb75097a238e60d4fd0ea542e8312214 (diff) | |
download | wix-eb53852d7ae6838e54525eb57df1d8ce8a722f9b.tar.gz wix-eb53852d7ae6838e54525eb57df1d8ce8a722f9b.tar.bz2 wix-eb53852d7ae6838e54525eb57df1d8ce8a722f9b.zip |
Add longPathAware to Burn manifest to support long paths.
Fixes 3455
Diffstat (limited to 'src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs')
-rw-r--r-- | src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs new file mode 100644 index 00000000..ba793d7a --- /dev/null +++ b/src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs | |||
@@ -0,0 +1,298 @@ | |||
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.ComponentModel; | ||
7 | using System.IO; | ||
8 | using System.Runtime.InteropServices; | ||
9 | using Microsoft.Win32; | ||
10 | using WixBuildTools.TestSupport; | ||
11 | using WixTestTools; | ||
12 | using WixToolset.Mba.Core; | ||
13 | using Xunit; | ||
14 | using Xunit.Abstractions; | ||
15 | |||
16 | public class LongPathTests : BurnE2ETests | ||
17 | { | ||
18 | public LongPathTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } | ||
19 | |||
20 | [RuntimeFact] | ||
21 | public void CanInstallAndUninstallSimpleBundle_x86_wixstdba() | ||
22 | { | ||
23 | this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleA"); | ||
24 | } | ||
25 | |||
26 | [RuntimeFact] | ||
27 | public void CanInstallAndUninstallSimpleBundle_x86_testba() | ||
28 | { | ||
29 | this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleB"); | ||
30 | } | ||
31 | |||
32 | [RuntimeFact] | ||
33 | public void CanInstallAndUninstallSimpleBundle_x86_dnctestba() | ||
34 | { | ||
35 | this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleC"); | ||
36 | } | ||
37 | |||
38 | [RuntimeFact] | ||
39 | public void CanInstallAndUninstallSimpleBundle_x86_wixba() | ||
40 | { | ||
41 | this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleD"); | ||
42 | } | ||
43 | |||
44 | [RuntimeFact] | ||
45 | public void CanInstallAndUninstallSimpleBundle_x64_wixstdba() | ||
46 | { | ||
47 | this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleA_x64"); | ||
48 | } | ||
49 | |||
50 | [RuntimeFact] | ||
51 | public void CanInstallAndUninstallSimplePerUserBundle_x64_wixstdba() | ||
52 | { | ||
53 | this.CanInstallAndUninstallSimpleBundle("PackageApu_x64", "BundleApu_x64", "PackagePerUser.wxs", unchecked((int)0xc0000005)); | ||
54 | } | ||
55 | |||
56 | [RuntimeFact] | ||
57 | public void CanInstallAndUninstallSimpleBundle_x64_testba() | ||
58 | { | ||
59 | this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleB_x64"); | ||
60 | } | ||
61 | |||
62 | [RuntimeFact] | ||
63 | public void CanInstallAndUninstallSimpleBundle_x64_dnctestba() | ||
64 | { | ||
65 | this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleC_x64"); | ||
66 | } | ||
67 | |||
68 | [RuntimeFact] | ||
69 | public void CanInstallAndUninstallSimpleBundle_x64_dncwixba() | ||
70 | { | ||
71 | this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleD_x64"); | ||
72 | } | ||
73 | |||
74 | private void CanInstallAndUninstallSimpleBundle(string packageName, string bundleName, string fileName = "Package.wxs", int? alternateExitCode = null) | ||
75 | { | ||
76 | var package = this.CreatePackageInstaller(Path.Combine("..", "BasicFunctionalityTests", packageName)); | ||
77 | |||
78 | var bundle = this.CreateBundleInstaller(Path.Combine("..", "BasicFunctionalityTests", bundleName)); | ||
79 | bundle.AlternateExitCode = alternateExitCode; | ||
80 | |||
81 | using var dfs = new DisposableFileSystem(); | ||
82 | var baseFolder = GetLongPath(dfs.GetFolder()); | ||
83 | |||
84 | var packageSourceCodeInstalled = package.GetInstalledFilePath(fileName); | ||
85 | |||
86 | // Source file should *not* be installed | ||
87 | Assert.False(File.Exists(packageSourceCodeInstalled), $"{packageName} payload should not be there on test start: {packageSourceCodeInstalled}"); | ||
88 | |||
89 | var bundleFileInfo = new FileInfo(bundle.Bundle); | ||
90 | var bundleCopiedPath = Path.Combine(baseFolder, bundleFileInfo.Name); | ||
91 | bundleFileInfo.CopyTo(bundleCopiedPath); | ||
92 | |||
93 | bundle.Install(bundleCopiedPath); | ||
94 | bundle.VerifyRegisteredAndInPackageCache(); | ||
95 | |||
96 | // Source file should be installed | ||
97 | Assert.True(File.Exists(packageSourceCodeInstalled), $"Should have found {packageName} payload installed at: {packageSourceCodeInstalled}"); | ||
98 | |||
99 | if (alternateExitCode == bundle.LastExitCode) | ||
100 | { | ||
101 | WixAssert.Skip($"Install exited with {bundle.LastExitCode}"); | ||
102 | } | ||
103 | |||
104 | bundle.Uninstall(bundleCopiedPath); | ||
105 | |||
106 | // Source file should *not* be installed | ||
107 | Assert.False(File.Exists(packageSourceCodeInstalled), $"{packageName} payload should have been removed by uninstall from: {packageSourceCodeInstalled}"); | ||
108 | |||
109 | bundle.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
110 | |||
111 | if (alternateExitCode == bundle.LastExitCode) | ||
112 | { | ||
113 | WixAssert.Skip($"Uninstall exited with {bundle.LastExitCode}"); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | [RuntimeFact] | ||
118 | public void CanLayoutNonCompressedBundleToLongPath() | ||
119 | { | ||
120 | var nonCompressedBundle = this.CreateBundleInstaller("NonCompressedBundle"); | ||
121 | var testBAController = this.CreateTestBAController(); | ||
122 | |||
123 | testBAController.SetPackageRequestedState("NetFx48Web", RequestState.None); | ||
124 | |||
125 | using var dfs = new DisposableFileSystem(); | ||
126 | var layoutDirectory = GetLongPath(dfs.GetFolder()); | ||
127 | |||
128 | nonCompressedBundle.Layout(layoutDirectory); | ||
129 | nonCompressedBundle.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
130 | |||
131 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "NonCompressedBundle.exe"))); | ||
132 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "PackageA.msi"))); | ||
133 | Assert.True(File.Exists(Path.Combine(layoutDirectory, "1a.cab"))); | ||
134 | Assert.False(File.Exists(Path.Combine(layoutDirectory, @"redist\ndp48-web.exe"))); | ||
135 | } | ||
136 | |||
137 | [RuntimeFact] | ||
138 | public void CanInstallNonCompressedBundleWithLongTempPath() | ||
139 | { | ||
140 | this.InstallNonCompressedBundle(longTemp: true, useOriginalTempForLog: true); | ||
141 | } | ||
142 | |||
143 | [RuntimeFact] | ||
144 | public void CannotInstallNonCompressedBundleWithLongPackageCachePath() | ||
145 | { | ||
146 | var installLogPath = this.InstallNonCompressedBundle((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, longPackageCache: true); | ||
147 | Assert.True(LogVerifier.MessageInLogFile(installLogPath, @"Error 0x80070643: Failed to install MSI package")); | ||
148 | } | ||
149 | |||
150 | [RuntimeFact] | ||
151 | public void CannotInstallNonCompressedBundleWithLongWorkingPath() | ||
152 | { | ||
153 | var installLogPath = this.InstallNonCompressedBundle((int)MSIExec.MSIExecReturnCode.ERROR_FILENAME_EXCED_RANGE | unchecked((int)0x80070000), longWorkingPath: true); | ||
154 | Assert.True(LogVerifier.MessageInLogFile(installLogPath, @"Error 0x800700ce: Failed to load BA DLL")); | ||
155 | } | ||
156 | |||
157 | public string InstallNonCompressedBundle(int expectedExitCode = 0, bool longTemp = false, bool useOriginalTempForLog = false, bool longWorkingPath = false, bool longPackageCache = false, int? alternateExitCode = null) | ||
158 | { | ||
159 | var deletePolicyKey = false; | ||
160 | string originalEngineWorkingDirectoryValue = null; | ||
161 | string originalPackageCacheValue = null; | ||
162 | var originalTemp = Environment.GetEnvironmentVariable("TMP"); | ||
163 | var packageA = this.CreatePackageInstaller("PackageA"); | ||
164 | var nonCompressedBundle = this.CreateBundleInstaller("NonCompressedBundle"); | ||
165 | var policyPath = nonCompressedBundle.GetFullBurnPolicyRegistryPath(); | ||
166 | string installLogPath = null; | ||
167 | |||
168 | try | ||
169 | { | ||
170 | using var dfs = new DisposableFileSystem(); | ||
171 | var originalBaseFolder = dfs.GetFolder(); | ||
172 | var baseFolder = GetLongPath(originalBaseFolder); | ||
173 | var sourceFolder = Path.Combine(baseFolder, "source"); | ||
174 | var workingFolder = Path.Combine(baseFolder, "working"); | ||
175 | var tempFolder = Path.Combine(originalBaseFolder, new string('d', 260 - originalBaseFolder.Length - 2)); | ||
176 | var packageCacheFolder = Path.Combine(baseFolder, "package cache"); | ||
177 | |||
178 | var copyResult = TestDataFolderFileSystem.RobocopyFolder(this.TestContext.TestDataFolder, sourceFolder); | ||
179 | Assert.True(copyResult.ExitCode >= 0 && copyResult.ExitCode < 8, $"Exit code: {copyResult.ExitCode}\r\nOutput: {String.Join("\r\n", copyResult.StandardOutput)}\r\nError: {String.Join("\r\n", copyResult.StandardError)}"); | ||
180 | |||
181 | var bundleFileInfo = new FileInfo(nonCompressedBundle.Bundle); | ||
182 | var bundleCopiedPath = Path.Combine(sourceFolder, bundleFileInfo.Name); | ||
183 | |||
184 | var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true); | ||
185 | if (policyKey == null) | ||
186 | { | ||
187 | policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true); | ||
188 | deletePolicyKey = true; | ||
189 | } | ||
190 | |||
191 | using (policyKey) | ||
192 | { | ||
193 | originalEngineWorkingDirectoryValue = policyKey.GetValue("EngineWorkingDirectory") as string; | ||
194 | originalPackageCacheValue = policyKey.GetValue("PackageCache") as string; | ||
195 | |||
196 | if (longWorkingPath) | ||
197 | { | ||
198 | policyKey.SetValue("EngineWorkingDirectory", workingFolder); | ||
199 | } | ||
200 | |||
201 | if (longPackageCache) | ||
202 | { | ||
203 | policyKey.SetValue("PackageCache", packageCacheFolder); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if (longTemp) | ||
208 | { | ||
209 | Environment.SetEnvironmentVariable("TMP", tempFolder); | ||
210 | |||
211 | if (useOriginalTempForLog) | ||
212 | { | ||
213 | nonCompressedBundle.LogDirectory = originalTemp; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | try | ||
218 | { | ||
219 | nonCompressedBundle.AlternateExitCode = alternateExitCode; | ||
220 | installLogPath = nonCompressedBundle.Install(bundleCopiedPath, expectedExitCode); | ||
221 | |||
222 | if (alternateExitCode == nonCompressedBundle.LastExitCode) | ||
223 | { | ||
224 | WixAssert.Skip($"Install exited with {nonCompressedBundle.LastExitCode}"); | ||
225 | } | ||
226 | } | ||
227 | finally | ||
228 | { | ||
229 | TestDataFolderFileSystem.RobocopyFolder(tempFolder, originalTemp); | ||
230 | } | ||
231 | |||
232 | installLogPath = Path.Combine(originalTemp, Path.GetFileName(installLogPath)); | ||
233 | |||
234 | if (expectedExitCode == 0) | ||
235 | { | ||
236 | var registration = nonCompressedBundle.VerifyRegisteredAndInPackageCache(); | ||
237 | packageA.VerifyInstalled(true); | ||
238 | |||
239 | nonCompressedBundle.Uninstall(registration.CachePath); | ||
240 | |||
241 | if (alternateExitCode == nonCompressedBundle.LastExitCode) | ||
242 | { | ||
243 | WixAssert.Skip($"Uninstall exited with {nonCompressedBundle.LastExitCode}"); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | nonCompressedBundle.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
248 | packageA.VerifyInstalled(false); | ||
249 | |||
250 | return installLogPath; | ||
251 | } | ||
252 | finally | ||
253 | { | ||
254 | Environment.SetEnvironmentVariable("TMP", originalTemp); | ||
255 | |||
256 | if (deletePolicyKey) | ||
257 | { | ||
258 | Registry.LocalMachine.DeleteSubKeyTree(policyPath); | ||
259 | } | ||
260 | else | ||
261 | { | ||
262 | using (var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true)) | ||
263 | { | ||
264 | policyKey?.SetValue("EngineWorkingDirectory", originalEngineWorkingDirectoryValue); | ||
265 | policyKey?.SetValue("PackageCache", originalPackageCacheValue); | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | } | ||
270 | |||
271 | private static string GetLongPath(string baseFolder) | ||
272 | { | ||
273 | Directory.CreateDirectory(baseFolder); | ||
274 | |||
275 | // Try to create a directory that is longer than MAX_PATH but without the \\?\ prefix to detect OS support for long paths. | ||
276 | // Need to PInvoke CreateDirectoryW directly because .NET methods will append the \\?\ prefix. | ||
277 | foreach (var c in new char[] { 'a', 'b', 'c' }) | ||
278 | { | ||
279 | baseFolder = Path.Combine(baseFolder, new string(c, 100)); | ||
280 | if (!CreateDirectoryW(baseFolder, IntPtr.Zero)) | ||
281 | { | ||
282 | int lastError = Marshal.GetLastWin32Error(); | ||
283 | if (lastError == 206) | ||
284 | { | ||
285 | WixAssert.Skip($"MAX_PATH is being enforced ({baseFolder})"); | ||
286 | } | ||
287 | throw new Win32Exception(lastError); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | return baseFolder; | ||
292 | } | ||
293 | |||
294 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] | ||
295 | [return: MarshalAs(UnmanagedType.Bool)] | ||
296 | private extern static bool CreateDirectoryW(string lpPathName, IntPtr lpSecurityAttributes); | ||
297 | } | ||
298 | } | ||