@joebigelow / wix / commits / 15b19463

Port the rest of the FailureTests from the old repo.

Sean Hall committed Feb 17, 2021 at 15:46 UTC 15b1946325bf1cfd1794b997f2cbbbcfeb648e92
6 files changed +369
src/TestData/FailureTests/BundleB/Bundle.wxs new
+26
@@ -0,0 +1,26 @@
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 +<?ifndef Version?>
4 +<?define Version = 1.0.0.0?>
5 +<?endif?>
6 +
7 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
8 + <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="yes">
9 + <Log Prefix="~$(var.TestGroupName)_$(var.BundleName)" />
10 +
11 + <Variable Name="TestGroupName" Value="$(var.TestGroupName)" />
12 +
13 + <!-- ParallelCache="yes" should be the only thing different from the template -->
14 + <Chain ParallelCache="yes">
15 + <PackageGroupRef Id="TestBA" />
16 +
17 + <PackageGroupRef Id="BundlePackages" />
18 + </Chain>
19 + </Bundle>
20 + <Fragment>
21 + <PackageGroup Id="BundlePackages">
22 + <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" />
23 + <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" />
24 + </PackageGroup>
25 + </Fragment>
26 +</Wix>
src/TestData/FailureTests/BundleB/BundleB.wixproj new
+16
@@ -0,0 +1,16 @@
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 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <UpgradeCode>{C60B9483-CE87-4FDA-AE5A-B39A52E956E8}</UpgradeCode>
6 + </PropertyGroup>
7 + <ItemGroup>
8 + <ProjectReference Include="..\PackageA\PackageA.wixproj" />
9 + <ProjectReference Include="..\PackageB\PackageB.wixproj" />
10 + <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
11 + </ItemGroup>
12 + <ItemGroup>
13 + <PackageReference Include="WixToolset.Bal.wixext" Version="4.0.83" />
14 + <PackageReference Include="WixToolset.NetFx.wixext" Version="4.0.61" />
15 + </ItemGroup>
16 +</Project>
\ No newline at end of file
src/TestData/FailureTests/BundleC/BundleC.wixproj new
+19
@@ -0,0 +1,19 @@
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 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <UpgradeCode>{9E9964D0-2120-4358-8136-D4A8727E0C59}</UpgradeCode>
6 + </PropertyGroup>
7 + <ItemGroup>
8 + <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
9 + </ItemGroup>
10 + <ItemGroup>
11 + <ProjectReference Include="..\PackageA\PackageA.wixproj" />
12 + <ProjectReference Include="..\PackageB\PackageB.wixproj" />
13 + <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 + </ItemGroup>
15 + <ItemGroup>
16 + <PackageReference Include="WixToolset.Bal.wixext" Version="4.0.83" />
17 + <PackageReference Include="WixToolset.NetFx.wixext" Version="4.0.61" />
18 + </ItemGroup>
19 +</Project>
\ No newline at end of file
src/TestData/FailureTests/BundleC/BundleC.wxs new
+11
@@ -0,0 +1,11 @@
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 +
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 + <Fragment>
6 + <PackageGroup Id="BundlePackages">
7 + <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" Name="MissingNonVital.msi" Vital="no" Compressed="no" />
8 + <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" />
9 + </PackageGroup>
10 + </Fragment>
11 +</Wix>
src/WixTestTools/LogVerifier.cs new
+252
@@ -0,0 +1,252 @@
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 WixTestTools
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Text;
8 + using System.Text.RegularExpressions;
9 + using Xunit;
10 +
11 + /// <summary>
12 + /// The LogVerifier can verify a log file for given regular expressions.
13 + /// </summary>
14 + public class LogVerifier
15 + {
16 + // Member Variables
17 + private string logFile;
18 +
19 + /// <summary>
20 + /// Prevent creation of LogVerifier without log file
21 + /// </summary>
22 + private LogVerifier()
23 + { }
24 +
25 + /// <summary>
26 + /// Constructor for log files where the exact file name is known.
27 + /// </summary>
28 + /// <param name="fileName">The full path to the log file</param>
29 + public LogVerifier(string fileName)
30 + {
31 + if (null == fileName)
32 + throw new ArgumentNullException("fileName");
33 +
34 + if (!File.Exists(fileName))
35 + throw new ArgumentException(String.Format(@"File doesn't exist:{0}", fileName), "fileName");
36 +
37 + logFile = fileName;
38 + }
39 +
40 + /// <summary>
41 + /// Constructor for log files where the exact file name is known.
42 + /// </summary>
43 + /// <param name="directory">The directory in which the log file is located.</param>
44 + /// <param name="fileName">The name of the log file.</param>
45 + public LogVerifier(string directory, string fileName)
46 + : this(Path.Combine(directory, fileName))
47 + { }
48 +
49 + /// <summary>
50 + /// Scans a log file line by line until the regex pattern is matched or eof is reached.
51 + /// This method would be used in the case where the log file is very large, the regex doesn't
52 + /// span multiple lines, and only one match is required.
53 + /// </summary>
54 + /// <param name="regex">A regular expression</param>
55 + /// <returns>True if a match is found, False otherwise.</returns>
56 + public bool LineByLine(Regex regex)
57 + {
58 + string line = string.Empty;
59 + StreamReader sr = new StreamReader(logFile);
60 +
61 + // Read from a file stream line by line.
62 + while ((line = sr.ReadLine()) != null)
63 + {
64 + if (regex.Match(line).Success)
65 + {
66 + sr.Close();
67 + sr.Dispose();
68 + return true;
69 + }
70 + }
71 + return false;
72 + }
73 +
74 +
75 + /// <summary>
76 + /// Scans a log file line by line until the regex pattern is matched or eof is reached.
77 + /// This method would be used in the case where the log file is very large, the regex doesn't
78 + /// span multiple lines, and only one match is required.
79 + /// No RegexOptions are used and matches are case sensitive.
80 + /// </summary>
81 + /// <param name="regex">A regular expression string.</param>
82 + /// <returns>True if a match is found, False otherwise.</returns>
83 + public bool LineByLine(string regex)
84 + {
85 + return LineByLine(new Regex(regex));
86 + }
87 +
88 +
89 + /// <summary>
90 + /// Scans a log file for matches to the regex.
91 + /// </summary>
92 + /// <param name="regex">A regular expression</param>
93 + /// <returns>The number of matches</returns>
94 + public int EntireFileAtOnce(Regex regex)
95 + {
96 + string logFileText = this.ReadLogFile();
97 + return regex.Matches(logFileText).Count;
98 + }
99 +
100 + /// <summary>
101 + /// Scans a log file for matches to the regex.
102 + /// </summary>
103 + /// <param name="regex">A regular expression</param>
104 + /// <returns>The number of matches</returns>
105 + public bool EntireFileAtOncestr(string regex)
106 + {
107 + string logFileText = this.ReadLogFile();
108 + return logFileText.Contains(regex);
109 + }
110 + /// <summary>
111 + /// Scans a log file for matches to the regex string.
112 + /// Only the Multiline RegexOption is used and matches are case sensitive.
113 + /// </summary>
114 + /// <param name="regex">A regular expression</param>
115 + /// <returns>The number of matches</returns>
116 + public int EntireFileAtOnce(string regex)
117 + {
118 + return EntireFileAtOnce(new Regex(regex, RegexOptions.Multiline));
119 + }
120 +
121 + /// <summary>
122 + /// Scans a log file for matches to the regex string.
123 + /// </summary>
124 + /// <param name="regex">A regular expression</param>
125 + /// <param name="ignoreCase">Specify whether to perform case sensitive matches</param>
126 + /// <returns>The number of matches</returns>
127 + public int EntireFileAtOnce(string regex, bool ignoreCase)
128 + {
129 + if (!ignoreCase)
130 + return EntireFileAtOnce(new Regex(regex, RegexOptions.Multiline));
131 + else
132 + return EntireFileAtOnce(new Regex(regex, RegexOptions.Multiline | RegexOptions.IgnoreCase));
133 + }
134 +
135 + /// <summary>
136 + /// Search through the log and Assert.Fail() if a specified string is not found.
137 + /// </summary>
138 + /// <param name="regex">Search expression</param>
139 + /// <param name="ignoreCase">Perform case insensitive match</param>
140 + public void AssertTextInLog(string regex, bool ignoreCase)
141 + {
142 + Assert.True(EntireFileAtOncestr(regex),
143 + String.Format("The log does not contain a match to the regular expression \"{0}\" ", regex));
144 + }
145 +
146 + /// <summary>
147 + /// Search through the log and Assert.Fail() if a specified string is not found.
148 + /// </summary>
149 + /// <param name="regex">Search expression</param>
150 + /// <param name="ignoreCase">Perform case insensitive match</param>
151 + public void AssertTextInLog(Regex regex, bool ignoreCase)
152 + {
153 + Assert.True(EntireFileAtOnce(regex) >= 1,
154 + String.Format("The log does not contain a match to the regular expression \"{0}\" ", regex.ToString()));
155 + }
156 +
157 + /// <summary>
158 + /// Search through the log and Assert.Fail() if a specified string is not found.
159 + /// </summary>
160 + /// <param name="regex">Search expression</param>
161 + /// <param name="ignoreCase">Perform case insensitive match</param>
162 + public void AssertTextInLog(string regex)
163 + {
164 + AssertTextInLog(regex, true);
165 + }
166 +
167 + /// <summary>
168 + /// Search through the log and Assert.Fail() if a specified string is not found.
169 + /// </summary>
170 + /// <param name="regex">Search expression</param>
171 + /// <param name="ignoreCase">Perform case insensitive match</param>
172 + public void AssertTextInLog(Regex regex)
173 + {
174 + AssertTextInLog(regex, true);
175 + }
176 +
177 +
178 + /// <summary>
179 + /// Search through the log and Assert.Fail() if a specified string is found.
180 + /// </summary>
181 + /// <param name="regex">Search expression</param>
182 + /// <param name="ignoreCase">Perform case insensitive match</param>
183 + public void AssertTextNotInLog(Regex regex, bool ignoreCase)
184 + {
185 + Assert.True(EntireFileAtOnce(regex) < 1,
186 + String.Format("The log contain a match to the regular expression \"{0}\" ", regex.ToString()));
187 + }
188 +
189 + /// <summary>
190 + /// Search through the log and Assert.Fail() if a specified string is not found.
191 + /// </summary>
192 + /// <param name="regex">Search expression</param>
193 + /// <param name="ignoreCase">Perform case insensitive match</param>
194 + public void AssertTextNotInLog(string regex, bool ignoreCase)
195 + {
196 + Assert.False(EntireFileAtOncestr(regex),
197 + String.Format("The log does not contain a match to the regular expression \"{0}\" ", regex));
198 + }
199 +
200 + /// <summary>
201 + /// Checks if a meesage is in a file
202 + /// </summary>
203 + /// <param name="logFileName">The full path to the log file</param>
204 + /// <param name="message">Search expression</param>
205 + /// <returns>True if the message was found, false otherwise</returns>
206 + public static bool MessageInLogFile(string logFileName, string message)
207 + {
208 + LogVerifier logVerifier = new LogVerifier(logFileName);
209 + return logVerifier.EntireFileAtOncestr(message);
210 + }
211 +
212 + /// <summary>
213 + /// Checks if a meesage is in a file
214 + /// </summary>
215 + /// <param name="logFileName">The full path to the log file</param>
216 + /// <param name="message">Search expression (regex)</param>
217 + /// <returns>True if the message was found, false otherwise</returns>
218 + public static bool MessageInLogFileRegex(string logFileName, string regexMessage)
219 + {
220 + LogVerifier logVerifier = new LogVerifier(logFileName);
221 + return logVerifier.EntireFileAtOnce(regexMessage) > 0;
222 + }
223 +
224 + /// <summary>
225 + /// Read in the entire log file at once.
226 + /// </summary>
227 + /// <returns>Contents of log file.</returns>
228 + private string ReadLogFile()
229 + {
230 + // Retry a few times.
231 + for (int retry = 0; ; ++retry)
232 + {
233 + try
234 + {
235 + using (StreamReader sr = new StreamReader(this.logFile))
236 + {
237 + return sr.ReadToEnd();
238 + }
239 + }
240 + catch // we'll catch everything a few times until we give up.
241 + {
242 + if (retry > 4)
243 + {
244 + throw;
245 + }
246 +
247 + System.Threading.Thread.Sleep(1000);
248 + }
249 + }
250 + }
251 + }
252 +}
src/WixToolsetTest.BurnE2E/FailureTests.cs
+45
@@ -63,5 +63,50 @@ namespace WixToolsetTest.BurnE2E
63 packageA.VerifyInstalled(false);
64 packageB.VerifyInstalled(false);
65 }
66 +
67 + [Fact(Skip = "https://github.com/wixtoolset/issues/issues/5750")]
68 + public void CanCancelExecuteWhileCaching()
69 + {
70 + var packageA = this.CreatePackageInstaller("PackageA");
71 + var packageB = this.CreatePackageInstaller("PackageB");
72 + var bundleB = this.CreateBundleInstaller("BundleB");
73 + var testBAController = this.CreateTestBAController();
74 +
75 + // Slow the caching of package B to ensure that package A starts installing and cancels.
76 + testBAController.SetPackageCancelExecuteAtProgress("PackageA", 50);
77 + testBAController.SetPackageSlowCache("PackageB", 2000);
78 +
79 + bundleB.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
80 + bundleB.VerifyUnregisteredAndRemovedFromPackageCache();
81 +
82 + packageA.VerifyInstalled(false);
83 + packageB.VerifyInstalled(false);
84 + }
85 +
86 + /// <summary>
87 + /// BundleC has non-vital PackageA and vital PackageB.
88 + /// PackageA is not compressed in the bundle and has a Name different from the source file. The Name points to a file that does not exist.
89 + /// BundleC should be able to install successfully by ignoring the missing PackageA and installing PackageB.
90 + /// </summary>
91 + [Fact]
92 + public void CanInstallWhenMissingNonVitalPackage()
93 + {
94 + var packageA = this.CreatePackageInstaller("PackageA");
95 + var packageB = this.CreatePackageInstaller("PackageB");
96 + var bundleC = this.CreateBundleInstaller("BundleC");
97 +
98 + var bundleCInstallLogFilePath = bundleC.Install();
99 + bundleC.VerifyRegisteredAndInPackageCache();
100 + Assert.True(LogVerifier.MessageInLogFileRegex(bundleCInstallLogFilePath, "Skipping apply of package: PackageA due to cache error: 0x80070002. Continuing..."));
101 +
102 + packageA.VerifyInstalled(false);
103 + packageB.VerifyInstalled(true);
104 +
105 + bundleC.Uninstall();
106 + bundleC.VerifyUnregisteredAndRemovedFromPackageCache();
107 +
108 + packageA.VerifyInstalled(false);
109 + packageB.VerifyInstalled(false);
110 + }
111 }
112 }