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
}