// 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.IO; using System.Text; public partial class BundleInstaller : IDisposable { public BundleInstaller(WixTestContext testContext, string name) { this.Bundle = Path.Combine(testContext.TestDataFolder, $"{name}.exe"); this.BundlePdb = Path.Combine(testContext.TestDataFolder, $"{name}.wixpdb"); this.TestContext = testContext; this.TestGroupName = testContext.TestGroupName; this.TestName = testContext.TestName; } public string Bundle { get; } private WixTestContext TestContext { get; } public string TestGroupName { get; } public string TestName { get; } public int? AlternateExitCode { get; set; } public string LogDirectory { get; set; } public int? LastExitCode { get; set; } /// /// Runs the bundle asking for help. /// /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Help(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { var newArgumentList = new List(); newArgumentList.Add("-help"); newArgumentList.AddRange(arguments); return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Custom, newArgumentList.ToArray()); } /// /// Installs the bundle with optional arguments. /// /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Install(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Install, arguments); } /// /// Installs the bundle with optional arguments. /// /// This should be the bundle in the package cache. /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Install(string bundlePath, int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Install, arguments, bundlePath: bundlePath); } /// /// Calls Layout for the bundle with optional arguments. /// /// The destination directory. /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Layout(string layoutDirectory, int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.AdministrativeInstall, arguments, layoutDirectory: layoutDirectory); } /// /// Calls Layout for the bundle with optional arguments. /// /// Path to the bundle to run. /// The destination directory. /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Layout(string bundlePath, string layoutDirectory, int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.AdministrativeInstall, arguments, bundlePath: bundlePath, layoutDirectory: layoutDirectory); } /// /// Modify the bundle with optional arguments. /// /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Modify(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Modify, arguments); } /// /// Modify the bundle with optional arguments. /// /// This should be the bundle in the package cache. /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Modify(string bundlePath, int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Modify, arguments, bundlePath: bundlePath); } /// /// Repairs the bundle with optional arguments. /// /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Repair(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Repair, arguments); } /// /// Uninstalls the bundle with optional arguments. /// /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Uninstall(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Uninstall, arguments); } /// /// Uninstalls the bundle at the given path with optional arguments. /// /// This should be the bundle in the package cache. /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string Uninstall(string bundlePath, int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Uninstall, arguments, bundlePath: bundlePath); } /// /// Uninstalls the bundle unsafely with optional arguments. /// /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string UnsafeUninstall(int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { var newArgumentList = new List(); newArgumentList.Add("-unsafeuninstall"); newArgumentList.AddRange(arguments); return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Custom, newArgumentList.ToArray()); } /// /// Uninstalls the bundle unsafely at the given path with optional arguments. /// /// This should be the bundle in the package cache. /// Expected exit code, defaults to success. /// Optional arguments to pass to the tool. /// Path to the generated log file. public string UnsafeUninstall(string bundlePath, int expectedExitCode = (int)MSIExec.MSIExecReturnCode.SUCCESS, params string[] arguments) { var newArgumentList = new List(); newArgumentList.Add("-unsafeuninstall"); newArgumentList.AddRange(arguments); return this.RunBundleWithArguments(expectedExitCode, MSIExec.MSIExecMode.Custom, newArgumentList.ToArray(), bundlePath: bundlePath); } /// /// Executes the bundle with optional arguments. /// /// Expected exit code. /// Install mode. /// Optional arguments to pass to the tool. /// Path to the generated log file. private string RunBundleWithArguments(int expectedExitCode, MSIExec.MSIExecMode mode, string[] arguments, bool assertOnError = true, string bundlePath = null, string layoutDirectory = null) { TestTool bundle = new TestTool(bundlePath ?? this.Bundle); var sb = new StringBuilder(); // Be sure to run silent. sb.Append(" -quiet"); // Generate the log file name. string logFile = Path.Combine(this.LogDirectory ?? Path.GetTempPath(), String.Format("{0}_{1}_{2:yyyyMMddhhmmss}_{4}_{3}.log", this.TestGroupName, this.TestName, DateTime.UtcNow, Path.GetFileNameWithoutExtension(this.Bundle), mode)); sb.AppendFormat(" -log \"{0}\"", logFile); // Set operation. switch (mode) { case MSIExec.MSIExecMode.AdministrativeInstall: sb.Append($" -layout \"{layoutDirectory}\""); break; case MSIExec.MSIExecMode.Modify: sb.Append(" -modify"); break; case MSIExec.MSIExecMode.Repair: sb.Append(" -repair"); break; case MSIExec.MSIExecMode.Cleanup: case MSIExec.MSIExecMode.Uninstall: sb.Append(" -uninstall"); break; } // Add additional arguments. if (null != arguments) { sb.Append(" "); sb.Append(String.Join(" ", arguments)); } // Set the arguments. bundle.Arguments = sb.ToString(); // Run the tool and assert the expected code. bundle.ExpectedExitCode = expectedExitCode; bundle.AlternateExitCode = this.AlternateExitCode; var result = bundle.Run(assertOnError); this.LastExitCode = result.ExitCode; // Return the log file name. return logFile; } public void Dispose() { string[] args = { "-burn.ignoredependencies=ALL" }; this.RunBundleWithArguments((int)MSIExec.MSIExecReturnCode.SUCCESS, MSIExec.MSIExecMode.Cleanup, args, assertOnError: false); } } }