// 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.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using WixInternal.TestSupport; using Xunit; public class TestTool : ExternalExecutable { /// /// Constructor for a TestTool /// public TestTool() : this(null) { } /// /// Constructor for a TestTool /// /// The full path to the tool. Eg. c:\bin\candle.exe public TestTool(string toolFile) : base(toolFile) { this.PrintOutputToConsole = true; } /// /// The alternate expected exit code of the tool /// public int? AlternateExitCode { get; set; } /// /// The arguments to pass to the tool /// public virtual string Arguments { get; set; } /// /// Stores the errors that occurred when a run was checked against its expected results /// public List Errors { get; set; } /// /// A list of Regex's that are expected to match stderr /// public List ExpectedErrorRegexs { get; set; } = new List(); /// /// The expected error strings to stderr /// public List ExpectedErrorStrings { get; set; } = new List(); /// /// The expected exit code of the tool /// public int? ExpectedExitCode { get; set; } /// /// A list of Regex's that are expected to match stdout /// public List ExpectedOutputRegexs { get; set; } = new List(); /// /// The expected output strings to stdout /// public List ExpectedOutputStrings { get; set; } = new List(); /// /// Print output from the tool execution to the console /// public bool PrintOutputToConsole { get; set; } /// /// The working directory of the tool /// public string WorkingDirectory { get; set; } /// /// Print the errors from the last run /// public void PrintErrors() { if (null != this.Errors) { Console.WriteLine("Errors:"); foreach (string error in this.Errors) { Console.WriteLine(error); } } } /// /// Run the tool /// /// The results of the run public ExternalExecutableResult Run() { return this.Run(true); } /// /// Run the tool /// /// Throw an exception if the expected results don't match the actual results /// Thrown when the expected results don't match the actual results /// The results of the run public virtual ExternalExecutableResult Run(bool assertOnError) { var result = this.Run(this.Arguments, workingDirectory: this.WorkingDirectory ?? String.Empty); if (this.PrintOutputToConsole) { Console.WriteLine(FormatResult(result)); } this.Errors = this.CheckResult(result); if (assertOnError && 0 < this.Errors.Count) { if (this.PrintOutputToConsole) { this.PrintErrors(); } WixAssert.StringCollectionEmpty(this.Errors); } return result; } /// /// Checks that the result from a run matches the expected results /// /// A result from a run /// A list of errors public virtual List CheckResult(ExternalExecutableResult result) { List errors = new List(); // Verify that the expected return code matched the actual return code if (null != this.ExpectedExitCode && this.ExpectedExitCode != result.ExitCode && (null == this.AlternateExitCode || this.AlternateExitCode != result.ExitCode)) { errors.Add(String.Format("Expected exit code {0} did not match actual exit code {1}", this.ExpectedExitCode, result.ExitCode)); } var standardErrorString = String.Join(Environment.NewLine, result.StandardError); // Verify that the expected error string are in stderr if (null != this.ExpectedErrorStrings) { foreach (string expectedString in this.ExpectedErrorStrings) { if (!standardErrorString.Contains(expectedString)) { errors.Add(String.Format("The text '{0}' was not found in stderr", expectedString)); } } } var standardOutputString = String.Join(Environment.NewLine, result.StandardOutput); // Verify that the expected output string are in stdout if (null != this.ExpectedOutputStrings) { foreach (string expectedString in this.ExpectedOutputStrings) { if (!standardOutputString.Contains(expectedString)) { errors.Add(String.Format("The text '{0}' was not found in stdout", expectedString)); } } } // Verify that the expected regular expressions match stderr if (null != this.ExpectedOutputRegexs) { foreach (Regex expectedRegex in this.ExpectedOutputRegexs) { if (!expectedRegex.IsMatch(standardOutputString)) { errors.Add(String.Format("Regex {0} did not match stdout", expectedRegex.ToString())); } } } // Verify that the expected regular expressions match stdout if (null != this.ExpectedErrorRegexs) { foreach (Regex expectedRegex in this.ExpectedErrorRegexs) { if (!expectedRegex.IsMatch(standardErrorString)) { errors.Add(String.Format("Regex {0} did not match stderr", expectedRegex.ToString())); } } } return errors; } /// /// Clears all of the expected results and resets them to the default values /// public virtual void SetDefaultExpectedResults() { this.ExpectedErrorRegexs = new List(); this.ExpectedErrorStrings = new List(); this.ExpectedExitCode = null; this.ExpectedOutputRegexs = new List(); this.ExpectedOutputStrings = new List(); } /// /// Returns a string with data contained in the result. /// /// A string private static string FormatResult(ExternalExecutableResult result) { var returnValue = new StringBuilder(); returnValue.AppendLine(); returnValue.AppendLine("----------------"); returnValue.AppendLine("Tool run result:"); returnValue.AppendLine("----------------"); returnValue.AppendLine("Command:"); returnValue.AppendLine($"\"{result.FileName}\" {result.Arguments}"); returnValue.AppendLine(); returnValue.AppendLine("Standard Output:"); foreach (var line in result.StandardOutput ?? new string[0]) { returnValue.AppendLine(line); } returnValue.AppendLine("Standard Error:"); foreach (var line in result.StandardError ?? new string[0]) { returnValue.AppendLine(line); } returnValue.AppendLine("Exit Code:"); returnValue.AppendLine(Convert.ToString(result.ExitCode)); returnValue.AppendLine("----------------"); return returnValue.ToString(); } } }