From 15b1946325bf1cfd1794b997f2cbbbcfeb648e92 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 17 Feb 2021 15:46:23 -0600 Subject: Port the rest of the FailureTests from the old repo. --- src/TestData/FailureTests/BundleB/Bundle.wxs | 26 +++ src/TestData/FailureTests/BundleB/BundleB.wixproj | 16 ++ src/TestData/FailureTests/BundleC/BundleC.wixproj | 19 ++ src/TestData/FailureTests/BundleC/BundleC.wxs | 11 + src/WixTestTools/LogVerifier.cs | 252 ++++++++++++++++++++++ src/WixToolsetTest.BurnE2E/FailureTests.cs | 45 ++++ 6 files changed, 369 insertions(+) create mode 100644 src/TestData/FailureTests/BundleB/Bundle.wxs create mode 100644 src/TestData/FailureTests/BundleB/BundleB.wixproj create mode 100644 src/TestData/FailureTests/BundleC/BundleC.wixproj create mode 100644 src/TestData/FailureTests/BundleC/BundleC.wxs create mode 100644 src/WixTestTools/LogVerifier.cs diff --git a/src/TestData/FailureTests/BundleB/Bundle.wxs b/src/TestData/FailureTests/BundleB/Bundle.wxs new file mode 100644 index 00000000..ea3d029e --- /dev/null +++ b/src/TestData/FailureTests/BundleB/Bundle.wxs @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/TestData/FailureTests/BundleB/BundleB.wixproj b/src/TestData/FailureTests/BundleB/BundleB.wixproj new file mode 100644 index 00000000..6d36d120 --- /dev/null +++ b/src/TestData/FailureTests/BundleB/BundleB.wixproj @@ -0,0 +1,16 @@ + + + + Bundle + {C60B9483-CE87-4FDA-AE5A-B39A52E956E8} + + + + + + + + + + + \ No newline at end of file diff --git a/src/TestData/FailureTests/BundleC/BundleC.wixproj b/src/TestData/FailureTests/BundleC/BundleC.wixproj new file mode 100644 index 00000000..03f611fc --- /dev/null +++ b/src/TestData/FailureTests/BundleC/BundleC.wixproj @@ -0,0 +1,19 @@ + + + + Bundle + {9E9964D0-2120-4358-8136-D4A8727E0C59} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TestData/FailureTests/BundleC/BundleC.wxs b/src/TestData/FailureTests/BundleC/BundleC.wxs new file mode 100644 index 00000000..48c4ab72 --- /dev/null +++ b/src/TestData/FailureTests/BundleC/BundleC.wxs @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/WixTestTools/LogVerifier.cs b/src/WixTestTools/LogVerifier.cs new file mode 100644 index 00000000..0252a9f9 --- /dev/null +++ b/src/WixTestTools/LogVerifier.cs @@ -0,0 +1,252 @@ +// 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. + +namespace WixTestTools +{ + using System; + using System.IO; + using System.Text; + using System.Text.RegularExpressions; + using Xunit; + + /// + /// The LogVerifier can verify a log file for given regular expressions. + /// + public class LogVerifier + { + // Member Variables + private string logFile; + + /// + /// Prevent creation of LogVerifier without log file + /// + private LogVerifier() + { } + + /// + /// Constructor for log files where the exact file name is known. + /// + /// The full path to the log file + public LogVerifier(string fileName) + { + if (null == fileName) + throw new ArgumentNullException("fileName"); + + if (!File.Exists(fileName)) + throw new ArgumentException(String.Format(@"File doesn't exist:{0}", fileName), "fileName"); + + logFile = fileName; + } + + /// + /// Constructor for log files where the exact file name is known. + /// + /// The directory in which the log file is located. + /// The name of the log file. + public LogVerifier(string directory, string fileName) + : this(Path.Combine(directory, fileName)) + { } + + /// + /// Scans a log file line by line until the regex pattern is matched or eof is reached. + /// This method would be used in the case where the log file is very large, the regex doesn't + /// span multiple lines, and only one match is required. + /// + /// A regular expression + /// True if a match is found, False otherwise. + public bool LineByLine(Regex regex) + { + string line = string.Empty; + StreamReader sr = new StreamReader(logFile); + + // Read from a file stream line by line. + while ((line = sr.ReadLine()) != null) + { + if (regex.Match(line).Success) + { + sr.Close(); + sr.Dispose(); + return true; + } + } + return false; + } + + + /// + /// Scans a log file line by line until the regex pattern is matched or eof is reached. + /// This method would be used in the case where the log file is very large, the regex doesn't + /// span multiple lines, and only one match is required. + /// No RegexOptions are used and matches are case sensitive. + /// + /// A regular expression string. + /// True if a match is found, False otherwise. + public bool LineByLine(string regex) + { + return LineByLine(new Regex(regex)); + } + + + /// + /// Scans a log file for matches to the regex. + /// + /// A regular expression + /// The number of matches + public int EntireFileAtOnce(Regex regex) + { + string logFileText = this.ReadLogFile(); + return regex.Matches(logFileText).Count; + } + + /// + /// Scans a log file for matches to the regex. + /// + /// A regular expression + /// The number of matches + public bool EntireFileAtOncestr(string regex) + { + string logFileText = this.ReadLogFile(); + return logFileText.Contains(regex); + } + /// + /// Scans a log file for matches to the regex string. + /// Only the Multiline RegexOption is used and matches are case sensitive. + /// + /// A regular expression + /// The number of matches + public int EntireFileAtOnce(string regex) + { + return EntireFileAtOnce(new Regex(regex, RegexOptions.Multiline)); + } + + /// + /// Scans a log file for matches to the regex string. + /// + /// A regular expression + /// Specify whether to perform case sensitive matches + /// The number of matches + public int EntireFileAtOnce(string regex, bool ignoreCase) + { + if (!ignoreCase) + return EntireFileAtOnce(new Regex(regex, RegexOptions.Multiline)); + else + return EntireFileAtOnce(new Regex(regex, RegexOptions.Multiline | RegexOptions.IgnoreCase)); + } + + /// + /// Search through the log and Assert.Fail() if a specified string is not found. + /// + /// Search expression + /// Perform case insensitive match + public void AssertTextInLog(string regex, bool ignoreCase) + { + Assert.True(EntireFileAtOncestr(regex), + String.Format("The log does not contain a match to the regular expression \"{0}\" ", regex)); + } + + /// + /// Search through the log and Assert.Fail() if a specified string is not found. + /// + /// Search expression + /// Perform case insensitive match + public void AssertTextInLog(Regex regex, bool ignoreCase) + { + Assert.True(EntireFileAtOnce(regex) >= 1, + String.Format("The log does not contain a match to the regular expression \"{0}\" ", regex.ToString())); + } + + /// + /// Search through the log and Assert.Fail() if a specified string is not found. + /// + /// Search expression + /// Perform case insensitive match + public void AssertTextInLog(string regex) + { + AssertTextInLog(regex, true); + } + + /// + /// Search through the log and Assert.Fail() if a specified string is not found. + /// + /// Search expression + /// Perform case insensitive match + public void AssertTextInLog(Regex regex) + { + AssertTextInLog(regex, true); + } + + + /// + /// Search through the log and Assert.Fail() if a specified string is found. + /// + /// Search expression + /// Perform case insensitive match + public void AssertTextNotInLog(Regex regex, bool ignoreCase) + { + Assert.True(EntireFileAtOnce(regex) < 1, + String.Format("The log contain a match to the regular expression \"{0}\" ", regex.ToString())); + } + + /// + /// Search through the log and Assert.Fail() if a specified string is not found. + /// + /// Search expression + /// Perform case insensitive match + public void AssertTextNotInLog(string regex, bool ignoreCase) + { + Assert.False(EntireFileAtOncestr(regex), + String.Format("The log does not contain a match to the regular expression \"{0}\" ", regex)); + } + + /// + /// Checks if a meesage is in a file + /// + /// The full path to the log file + /// Search expression + /// True if the message was found, false otherwise + public static bool MessageInLogFile(string logFileName, string message) + { + LogVerifier logVerifier = new LogVerifier(logFileName); + return logVerifier.EntireFileAtOncestr(message); + } + + /// + /// Checks if a meesage is in a file + /// + /// The full path to the log file + /// Search expression (regex) + /// True if the message was found, false otherwise + public static bool MessageInLogFileRegex(string logFileName, string regexMessage) + { + LogVerifier logVerifier = new LogVerifier(logFileName); + return logVerifier.EntireFileAtOnce(regexMessage) > 0; + } + + /// + /// Read in the entire log file at once. + /// + /// Contents of log file. + private string ReadLogFile() + { + // Retry a few times. + for (int retry = 0; ; ++retry) + { + try + { + using (StreamReader sr = new StreamReader(this.logFile)) + { + return sr.ReadToEnd(); + } + } + catch // we'll catch everything a few times until we give up. + { + if (retry > 4) + { + throw; + } + + System.Threading.Thread.Sleep(1000); + } + } + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/FailureTests.cs b/src/WixToolsetTest.BurnE2E/FailureTests.cs index bc505527..a11a5eb6 100644 --- a/src/WixToolsetTest.BurnE2E/FailureTests.cs +++ b/src/WixToolsetTest.BurnE2E/FailureTests.cs @@ -63,5 +63,50 @@ namespace WixToolsetTest.BurnE2E packageA.VerifyInstalled(false); packageB.VerifyInstalled(false); } + + [Fact(Skip = "https://github.com/wixtoolset/issues/issues/5750")] + public void CanCancelExecuteWhileCaching() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var packageB = this.CreatePackageInstaller("PackageB"); + var bundleB = this.CreateBundleInstaller("BundleB"); + var testBAController = this.CreateTestBAController(); + + // Slow the caching of package B to ensure that package A starts installing and cancels. + testBAController.SetPackageCancelExecuteAtProgress("PackageA", 50); + testBAController.SetPackageSlowCache("PackageB", 2000); + + bundleB.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT); + bundleB.VerifyUnregisteredAndRemovedFromPackageCache(); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(false); + } + + /// + /// BundleC has non-vital PackageA and vital PackageB. + /// 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. + /// BundleC should be able to install successfully by ignoring the missing PackageA and installing PackageB. + /// + [Fact] + public void CanInstallWhenMissingNonVitalPackage() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var packageB = this.CreatePackageInstaller("PackageB"); + var bundleC = this.CreateBundleInstaller("BundleC"); + + var bundleCInstallLogFilePath = bundleC.Install(); + bundleC.VerifyRegisteredAndInPackageCache(); + Assert.True(LogVerifier.MessageInLogFileRegex(bundleCInstallLogFilePath, "Skipping apply of package: PackageA due to cache error: 0x80070002. Continuing...")); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(true); + + bundleC.Uninstall(); + bundleC.VerifyUnregisteredAndRemovedFromPackageCache(); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(false); + } } } -- cgit v1.2.3-55-g6feb