From 81721d622e7ba2983d8e8b484edee26dbe507228 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 19 Jan 2021 12:49:52 -0600 Subject: Port CancelEarly and CancelLate failure tests from old repo. --- src/TestData/FailureTests/BundleA/BundleA.wixproj | 19 +++ src/TestData/FailureTests/BundleA/BundleA.wxs | 11 ++ .../FailureTests/PackageA/PackageA.wixproj | 9 ++ .../FailureTests/PackageB/PackageB.wixproj | 9 ++ src/WixToolsetTest.BurnE2E/BundleInstaller.cs | 77 +---------- src/WixToolsetTest.BurnE2E/BundleRegistration.cs | 145 +++++++++++++++++++++ src/WixToolsetTest.BurnE2E/BundleVerifier.cs | 78 +++++++++++ src/WixToolsetTest.BurnE2E/BurnE2ETests.cs | 7 + src/WixToolsetTest.BurnE2E/FailureTests.cs | 48 +++++++ src/WixToolsetTest.BurnE2E/MsiUtilities.cs | 27 ++++ src/WixToolsetTest.BurnE2E/PackageInstaller.cs | 10 +- src/WixToolsetTest.BurnE2E/PackageVerifier.cs | 50 +++++++ src/WixToolsetTest.BurnE2E/TestBAController.cs | 144 ++++++++++++++++++++ .../WixToolsetTest.BurnE2E.csproj | 1 + 14 files changed, 551 insertions(+), 84 deletions(-) create mode 100644 src/TestData/FailureTests/BundleA/BundleA.wixproj create mode 100644 src/TestData/FailureTests/BundleA/BundleA.wxs create mode 100644 src/TestData/FailureTests/PackageA/PackageA.wixproj create mode 100644 src/TestData/FailureTests/PackageB/PackageB.wixproj create mode 100644 src/WixToolsetTest.BurnE2E/BundleRegistration.cs create mode 100644 src/WixToolsetTest.BurnE2E/BundleVerifier.cs create mode 100644 src/WixToolsetTest.BurnE2E/FailureTests.cs create mode 100644 src/WixToolsetTest.BurnE2E/MsiUtilities.cs create mode 100644 src/WixToolsetTest.BurnE2E/PackageVerifier.cs create mode 100644 src/WixToolsetTest.BurnE2E/TestBAController.cs diff --git a/src/TestData/FailureTests/BundleA/BundleA.wixproj b/src/TestData/FailureTests/BundleA/BundleA.wixproj new file mode 100644 index 00000000..85dc0101 --- /dev/null +++ b/src/TestData/FailureTests/BundleA/BundleA.wixproj @@ -0,0 +1,19 @@ + + + + Bundle + {FE5197EB-E324-411E-B3AC-760E566E1000} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TestData/FailureTests/BundleA/BundleA.wxs b/src/TestData/FailureTests/BundleA/BundleA.wxs new file mode 100644 index 00000000..95e714ec --- /dev/null +++ b/src/TestData/FailureTests/BundleA/BundleA.wxs @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/TestData/FailureTests/PackageA/PackageA.wixproj b/src/TestData/FailureTests/PackageA/PackageA.wixproj new file mode 100644 index 00000000..3b0e74e2 --- /dev/null +++ b/src/TestData/FailureTests/PackageA/PackageA.wixproj @@ -0,0 +1,9 @@ + + + + {82FB39B2-56FA-4631-AA03-8B8D3215E6AE} + + + + + \ No newline at end of file diff --git a/src/TestData/FailureTests/PackageB/PackageB.wixproj b/src/TestData/FailureTests/PackageB/PackageB.wixproj new file mode 100644 index 00000000..96f7a031 --- /dev/null +++ b/src/TestData/FailureTests/PackageB/PackageB.wixproj @@ -0,0 +1,9 @@ + + + + {94160B95-81DD-4DAB-AE2D-246A9E3A108E} + + + + + \ No newline at end of file diff --git a/src/WixToolsetTest.BurnE2E/BundleInstaller.cs b/src/WixToolsetTest.BurnE2E/BundleInstaller.cs index 5ce993f3..923618b9 100644 --- a/src/WixToolsetTest.BurnE2E/BundleInstaller.cs +++ b/src/WixToolsetTest.BurnE2E/BundleInstaller.cs @@ -4,20 +4,10 @@ namespace WixToolsetTest.BurnE2E { using System; using System.IO; - using System.Linq; using System.Text; - using Microsoft.Win32; - using WixToolset.Data; - using WixToolset.Data.Symbols; - using Xunit; - public class BundleInstaller : IDisposable + public partial class BundleInstaller : IDisposable { - public const string BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; - public const string BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = "BundleCachePath"; - public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn"; - public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache"; - public BundleInstaller(WixTestContext testContext, string name) { this.Bundle = Path.Combine(testContext.TestDataFolder, $"{name}.exe"); @@ -28,10 +18,6 @@ namespace WixToolsetTest.BurnE2E public string Bundle { get; } - public string BundlePdb { get; } - - private WixBundleSymbol BundleSymbol { get; set; } - public string TestGroupName { get; } public string TestName { get; } @@ -146,67 +132,6 @@ namespace WixToolsetTest.BurnE2E return logFile; } - private WixBundleSymbol GetBundleSymbol() - { - if (this.BundleSymbol == null) - { - using var wixOutput = WixOutput.Read(this.BundlePdb); - var intermediate = Intermediate.Load(wixOutput); - var section = intermediate.Sections.Single(); - this.BundleSymbol = section.Symbols.OfType().Single(); - } - - return this.BundleSymbol; - } - - public string GetExpectedCachedBundlePath() - { - var bundleSymbol = this.GetBundleSymbol(); - - using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH); - var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; - var cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); - return Path.Combine(cachePath, bundleSymbol.BundleId, Path.GetFileName(this.Bundle)); - } - - public string VerifyRegisteredAndInPackageCache() - { - var bundleSymbol = this.GetBundleSymbol(); - var bundleId = bundleSymbol.BundleId; - var registrationKeyPath = $"{BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY}\\{bundleId}"; - - using var registrationKey = Registry.LocalMachine.OpenSubKey(registrationKeyPath); - Assert.NotNull(registrationKey); - - var cachePathValue = registrationKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); - Assert.NotNull(cachePathValue); - var cachePath = Assert.IsType(cachePathValue); - Assert.True(File.Exists(cachePath)); - - var expectedCachePath = this.GetExpectedCachedBundlePath(); - Assert.Equal(expectedCachePath, cachePath, StringComparer.OrdinalIgnoreCase); - - return cachePath; - } - - public void VerifyUnregisteredAndRemovedFromPackageCache() - { - var cachedBundlePath = this.GetExpectedCachedBundlePath(); - this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath); - } - - public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath) - { - var bundleSymbol = this.GetBundleSymbol(); - var bundleId = bundleSymbol.BundleId; - var registrationKeyPath = $"{BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY}\\{bundleId}"; - - using var registrationKey = Registry.LocalMachine.OpenSubKey(registrationKeyPath); - Assert.Null(registrationKey); - - Assert.False(File.Exists(cachedBundlePath)); - } - public void Dispose() { string[] args = { "-burn.ignoredependencies=ALL" }; diff --git a/src/WixToolsetTest.BurnE2E/BundleRegistration.cs b/src/WixToolsetTest.BurnE2E/BundleRegistration.cs new file mode 100644 index 00000000..6d20a0b2 --- /dev/null +++ b/src/WixToolsetTest.BurnE2E/BundleRegistration.cs @@ -0,0 +1,145 @@ +// 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 WixToolsetTest.BurnE2E +{ + using System; + using Microsoft.Win32; + + public class BundleRegistration + { + public const string BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = "BundleCachePath"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = "BundleAddonCode"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = "BundleDetectCode"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = "BundlePatchCode"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = "BundleUpgradeCode"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = "DisplayName"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = "BundleVersion"; + public const string BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = "EngineVersion"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = "BundleProviderKey"; + public const string BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = "BundleTag"; + public const string REGISTRY_REBOOT_PENDING_FORMAT = "{0}.RebootRequired"; + public const string REGISTRY_BUNDLE_INSTALLED = "Installed"; + public const string REGISTRY_BUNDLE_DISPLAY_ICON = "DisplayIcon"; + public const string REGISTRY_BUNDLE_DISPLAY_VERSION = "DisplayVersion"; + public const string REGISTRY_BUNDLE_ESTIMATED_SIZE = "EstimatedSize"; + public const string REGISTRY_BUNDLE_PUBLISHER = "Publisher"; + public const string REGISTRY_BUNDLE_HELP_LINK = "HelpLink"; + public const string REGISTRY_BUNDLE_HELP_TELEPHONE = "HelpTelephone"; + public const string REGISTRY_BUNDLE_URL_INFO_ABOUT = "URLInfoAbout"; + public const string REGISTRY_BUNDLE_URL_UPDATE_INFO = "URLUpdateInfo"; + public const string REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = "ParentDisplayName"; + public const string REGISTRY_BUNDLE_PARENT_KEY_NAME = "ParentKeyName"; + public const string REGISTRY_BUNDLE_COMMENTS = "Comments"; + public const string REGISTRY_BUNDLE_CONTACT = "Contact"; + public const string REGISTRY_BUNDLE_NO_MODIFY = "NoModify"; + public const string REGISTRY_BUNDLE_MODIFY_PATH = "ModifyPath"; + public const string REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = "NoElevateOnModify"; + public const string REGISTRY_BUNDLE_NO_REMOVE = "NoRemove"; + public const string REGISTRY_BUNDLE_SYSTEM_COMPONENT = "SystemComponent"; + public const string REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = "QuietUninstallString"; + public const string REGISTRY_BUNDLE_UNINSTALL_STRING = "UninstallString"; + public const string REGISTRY_BUNDLE_RESUME_COMMAND_LINE = "BundleResumeCommandLine"; + public const string REGISTRY_BUNDLE_VERSION_MAJOR = "VersionMajor"; + public const string REGISTRY_BUNDLE_VERSION_MINOR = "VersionMinor"; + + public string[] AddonCodes { get; set; } + + public string CachePath { get; set; } + + public string DisplayName { get; set; } + + public string[] DetectCodes { get; set; } + + public string EngineVersion { get; set; } + + public int? EstimatedSize { get; set; } + + public int? Installed { get; set; } + + public string ModifyPath { get; set; } + + public string[] PatchCodes { get; set; } + + public string ProviderKey { get; set; } + + public string Publisher { get; set; } + + public string QuietUninstallString { get; set; } + + public string QuietUninstallCommand { get; set; } + + public string QuietUninstallCommandArguments { get; set; } + + public string Tag { get; set; } + + public string UninstallCommand { get; set; } + + public string UninstallCommandArguments { get; set; } + + public string UninstallString { get; set; } + + public string[] UpgradeCodes { get; set; } + + public string UrlInfoAbout { get; set; } + + public string UrlUpdateInfo { get; set; } + + public string Version { get; set; } + + public static bool TryGetPerMachineBundleRegistrationById(string bundleId, out BundleRegistration registration) + { + var registrationKeyPath = $"{BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY}\\{bundleId}"; + using var registrationKey = Registry.LocalMachine.OpenSubKey(registrationKeyPath); + var success = registrationKey != null; + registration = success ? GetBundleRegistration(registrationKey) : null; + return success; + } + + private static BundleRegistration GetBundleRegistration(RegistryKey idKey) + { + var registration = new BundleRegistration(); + + registration.AddonCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE) as string[]; + registration.CachePath = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH) as string; + registration.DetectCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE) as string[]; + registration.PatchCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE) as string[]; + registration.ProviderKey = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY) as string; + registration.Tag = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_TAG) as string; + registration.UpgradeCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE) as string[]; + registration.Version = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION) as string; + registration.DisplayName = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME) as string; + registration.EngineVersion = idKey.GetValue(BURN_REGISTRATION_REGISTRY_ENGINE_VERSION) as string; + registration.EstimatedSize = idKey.GetValue(REGISTRY_BUNDLE_ESTIMATED_SIZE) as int?; + registration.Installed = idKey.GetValue(REGISTRY_BUNDLE_INSTALLED) as int?; + registration.ModifyPath = idKey.GetValue(REGISTRY_BUNDLE_MODIFY_PATH) as string; + registration.Publisher = idKey.GetValue(REGISTRY_BUNDLE_PUBLISHER) as string; + registration.UrlInfoAbout = idKey.GetValue(REGISTRY_BUNDLE_URL_INFO_ABOUT) as string; + registration.UrlUpdateInfo = idKey.GetValue(REGISTRY_BUNDLE_URL_UPDATE_INFO) as string; + + registration.QuietUninstallString = idKey.GetValue(REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING) as string; + if (!String.IsNullOrEmpty(registration.QuietUninstallString)) + { + var closeQuote = registration.QuietUninstallString.IndexOf("\"", 1); + if (closeQuote > 0) + { + registration.QuietUninstallCommand = registration.QuietUninstallString.Substring(1, closeQuote - 1).Trim(); + registration.QuietUninstallCommandArguments = registration.QuietUninstallString.Substring(closeQuote + 1).Trim(); + } + } + + registration.UninstallString = idKey.GetValue(REGISTRY_BUNDLE_UNINSTALL_STRING) as string; + if (!String.IsNullOrEmpty(registration.UninstallString)) + { + var closeQuote = registration.UninstallString.IndexOf("\"", 1); + if (closeQuote > 0) + { + registration.UninstallCommand = registration.UninstallString.Substring(1, closeQuote - 1).Trim(); + registration.UninstallCommandArguments = registration.UninstallString.Substring(closeQuote + 1).Trim(); + } + } + + return registration; + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/BundleVerifier.cs b/src/WixToolsetTest.BurnE2E/BundleVerifier.cs new file mode 100644 index 00000000..94d51890 --- /dev/null +++ b/src/WixToolsetTest.BurnE2E/BundleVerifier.cs @@ -0,0 +1,78 @@ +// 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 WixToolsetTest.BurnE2E +{ + using System; + using System.IO; + using System.Linq; + using System.Text; + using Microsoft.Win32; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using Xunit; + + public partial class BundleInstaller + { + public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn"; + public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache"; + + public string BundlePdb { get; } + + private WixBundleSymbol BundleSymbol { get; set; } + + private WixBundleSymbol GetBundleSymbol() + { + if (this.BundleSymbol == null) + { + using var wixOutput = WixOutput.Read(this.BundlePdb); + var intermediate = Intermediate.Load(wixOutput); + var section = intermediate.Sections.Single(); + this.BundleSymbol = section.Symbols.OfType().Single(); + } + + return this.BundleSymbol; + } + + public string GetExpectedCachedBundlePath() + { + var bundleSymbol = this.GetBundleSymbol(); + + using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH); + var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; + var cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); + return Path.Combine(cachePath, bundleSymbol.BundleId, Path.GetFileName(this.Bundle)); + } + + public bool TryGetPerMachineRegistration(out BundleRegistration registration) + { + var bundleSymbol = this.GetBundleSymbol(); + var bundleId = bundleSymbol.BundleId; + return BundleRegistration.TryGetPerMachineBundleRegistrationById(bundleId, out registration); + } + + public string VerifyRegisteredAndInPackageCache() + { + Assert.True(this.TryGetPerMachineRegistration(out var registration)); + + Assert.NotNull(registration.CachePath); + Assert.True(File.Exists(registration.CachePath)); + + var expectedCachePath = this.GetExpectedCachedBundlePath(); + Assert.Equal(expectedCachePath, registration.CachePath, StringComparer.OrdinalIgnoreCase); + + return registration.CachePath; + } + + public void VerifyUnregisteredAndRemovedFromPackageCache() + { + var cachedBundlePath = this.GetExpectedCachedBundlePath(); + this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath); + } + + public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath) + { + Assert.False(this.TryGetPerMachineRegistration(out _)); + Assert.False(File.Exists(cachedBundlePath)); + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/BurnE2ETests.cs b/src/WixToolsetTest.BurnE2E/BurnE2ETests.cs index 55353277..8d1cca23 100644 --- a/src/WixToolsetTest.BurnE2E/BurnE2ETests.cs +++ b/src/WixToolsetTest.BurnE2E/BurnE2ETests.cs @@ -28,6 +28,13 @@ namespace WixToolsetTest.BurnE2E return installer; } + protected TestBAController CreateTestBAController() + { + var controller = new TestBAController(this.TestContext); + this.Installers.Enqueue(controller); + return controller; + } + public void Dispose() { while (this.Installers.TryDequeue(out var installer)) diff --git a/src/WixToolsetTest.BurnE2E/FailureTests.cs b/src/WixToolsetTest.BurnE2E/FailureTests.cs new file mode 100644 index 00000000..773c9dd0 --- /dev/null +++ b/src/WixToolsetTest.BurnE2E/FailureTests.cs @@ -0,0 +1,48 @@ +// 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 WixToolsetTest.BurnE2E +{ + using Xunit; + using Xunit.Abstractions; + + public class FailureTests : BurnE2ETests + { + public FailureTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } + + [Fact] + public void CanCancelMsiPackageVeryEarly() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var packageB = this.CreatePackageInstaller("PackageB"); + var bundleA = this.CreateBundleInstaller("BundleA"); + var testBAController = this.CreateTestBAController(); + + // Cancel package B right away. + testBAController.SetPackageCancelExecuteAtProgress("PackageB", 1); + + bundleA.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT); + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(false); + } + + [Fact] + public void CanCancelMsiPackageVeryLate() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var packageB = this.CreatePackageInstaller("PackageB"); + var bundleA = this.CreateBundleInstaller("BundleA"); + var testBAController = this.CreateTestBAController(); + + // Cancel package B at the last moment possible. + testBAController.SetPackageCancelExecuteAtProgress("PackageB", 100); + + bundleA.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT); + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(false); + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/MsiUtilities.cs b/src/WixToolsetTest.BurnE2E/MsiUtilities.cs new file mode 100644 index 00000000..1a9f0925 --- /dev/null +++ b/src/WixToolsetTest.BurnE2E/MsiUtilities.cs @@ -0,0 +1,27 @@ +// 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 WixToolsetTest.BurnE2E +{ + using WixToolset.Dtf.WindowsInstaller; + + public class MsiUtilities + { + /// + /// Return true if it finds the given productcode in system otherwise it returns false + /// + /// + /// + public static bool IsProductInstalled(string prodCode) + { + //look in all user's products (both per-machine and per-user) + foreach (ProductInstallation product in ProductInstallation.GetProducts(null, "s-1-1-0", UserContexts.All)) + { + if (product.ProductCode == prodCode) + { + return true; + } + } + return false; + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/PackageInstaller.cs b/src/WixToolsetTest.BurnE2E/PackageInstaller.cs index e49d010d..95d6cd90 100644 --- a/src/WixToolsetTest.BurnE2E/PackageInstaller.cs +++ b/src/WixToolsetTest.BurnE2E/PackageInstaller.cs @@ -6,30 +6,24 @@ namespace WixToolsetTest.BurnE2E using System.IO; using static WixToolsetTest.BurnE2E.MSIExec; - public class PackageInstaller : IDisposable + public partial class PackageInstaller : IDisposable { public PackageInstaller(WixTestContext testContext, string name) { this.Package = Path.Combine(testContext.TestDataFolder, $"{name}.msi"); + this.PackagePdb = Path.Combine(testContext.TestDataFolder, $"{name}.wixpdb"); this.PackageName = name; this.TestContext = testContext; } public string Package { get; } - private string PackageName { get; } - private WixTestContext TestContext { get; } public string TestGroupName => this.TestContext.TestGroupName; public string TestName => this.TestContext.TestName; - public string GetInstalledFilePath(string filename) - { - return this.TestContext.GetTestInstallFolder(Path.Combine(this.PackageName, filename)); - } - /// /// Installs a .msi file /// diff --git a/src/WixToolsetTest.BurnE2E/PackageVerifier.cs b/src/WixToolsetTest.BurnE2E/PackageVerifier.cs new file mode 100644 index 00000000..7b4bbfef --- /dev/null +++ b/src/WixToolsetTest.BurnE2E/PackageVerifier.cs @@ -0,0 +1,50 @@ +// 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 WixToolsetTest.BurnE2E +{ + using System; + using System.IO; + using System.Linq; + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; + using Xunit; + + public partial class PackageInstaller + { + private string PackageName { get; } + + public string PackagePdb { get; } + + private WindowsInstallerData WiData { get; set; } + + public string GetInstalledFilePath(string filename) + { + return this.TestContext.GetTestInstallFolder(Path.Combine(this.PackageName, filename)); + } + + private WindowsInstallerData GetWindowsInstallerData() + { + if (this.WiData == null) + { + using var wixOutput = WixOutput.Read(this.PackagePdb); + this.WiData = WindowsInstallerData.Load(wixOutput); + } + + return this.WiData; + } + + public string GetProperty(string name) + { + var wiData = this.GetWindowsInstallerData(); + var row = wiData.Tables["Property"].Rows.Cast().Single(r => r.Property == name); + return row.Value; + } + + public void VerifyInstalled(bool installed) + { + var productCode = this.GetProperty("ProductCode"); + Assert.Equal(installed, MsiUtilities.IsProductInstalled(productCode)); + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/TestBAController.cs b/src/WixToolsetTest.BurnE2E/TestBAController.cs new file mode 100644 index 00000000..6ae9a9dd --- /dev/null +++ b/src/WixToolsetTest.BurnE2E/TestBAController.cs @@ -0,0 +1,144 @@ +// 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 WixToolsetTest.BurnE2E +{ + using System; + using Microsoft.Win32; + using WixToolset.Mba.Core; + + public class TestBAController : IDisposable + { + private const string BaseRegKeyPath = @"Software\WOW6432Node\WiX\Tests"; + + public TestBAController(WixTestContext testContext) + { + this.TestGroupName = testContext.TestGroupName; + this.TestBaseRegKeyPath = String.Format(@"{0}\TestBAControl\{1}", BaseRegKeyPath, this.TestGroupName); + } + + private string TestBaseRegKeyPath { get; } + + public string TestGroupName { get; } + + /// + /// Sets a test value in the registry to communicate with the TestBA. + /// + /// Name of the value to set. + /// Value to set. If this is null, the value is removed. + public void SetBurnTestValue(string name, string value) + { + using (var testKey = Registry.LocalMachine.CreateSubKey(this.TestBaseRegKeyPath)) + { + if (String.IsNullOrEmpty(value)) + { + testKey.DeleteValue(name, false); + } + else + { + testKey.SetValue(name, value); + } + } + } + + /// + /// Slows the cache progress of a package. + /// + /// Package identity. + /// Sets or removes the delay on a package being cached. + public void SetPackageSlowCache(string packageId, int? delay) + { + this.SetPackageState(packageId, "SlowCache", delay.HasValue ? delay.ToString() : null); + } + + /// + /// Cancels the cache of a package at a particular progress point. + /// + /// Package identity. + /// Sets or removes the cancel progress on a package being cached. + public void SetPackageCancelCacheAtProgress(string packageId, int? cancelPoint) + { + this.SetPackageState(packageId, "CancelCacheAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null); + } + + /// + /// Slows the execute progress of a package. + /// + /// Package identity. + /// Sets or removes the delay on a package being executed. + public void SetPackageSlowExecute(string packageId, int? delay) + { + this.SetPackageState(packageId, "SlowExecute", delay.HasValue ? delay.ToString() : null); + } + + /// + /// Cancels the execute of a package at a particular progress point. + /// + /// Package identity. + /// Sets or removes the cancel progress on a package being executed. + public void SetPackageCancelExecuteAtProgress(string packageId, int? cancelPoint) + { + this.SetPackageState(packageId, "CancelExecuteAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null); + } + + /// + /// Sets the requested state for a package that the TestBA will return to the engine during plan. + /// + /// Package identity. + /// State to request. + public void SetPackageRequestedState(string packageId, RequestState state) + { + this.SetPackageState(packageId, "Requested", state.ToString()); + } + + /// + /// Sets the requested state for a package that the TestBA will return to the engine during plan. + /// + /// Package identity. + /// State to request. + public void SetPackageFeatureState(string packageId, string featureId, FeatureState state) + { + this.SetPackageState(packageId, String.Concat(featureId, "Requested"), state.ToString()); + } + + /// + /// Sets the number of times to re-run the Detect phase. + /// + /// Number of times to run Detect (after the first, normal, Detect). + public void SetRedetectCount(int redetectCount) + { + this.SetPackageState(null, "RedetectCount", redetectCount.ToString()); + } + + /// + /// Resets the state for a package that the TestBA will return to the engine during plan. + /// + /// Package identity. + public void ResetPackageStates(string packageId) + { + var key = String.Format(@"{0}\{1}", this.TestBaseRegKeyPath, packageId ?? String.Empty); + Registry.LocalMachine.DeleteSubKey(key); + } + + private void SetPackageState(string packageId, string name, string value) + { + var key = String.Format(@"{0}\{1}", this.TestBaseRegKeyPath, packageId ?? String.Empty); + using (var packageKey = Registry.LocalMachine.CreateSubKey(key)) + { + if (String.IsNullOrEmpty(value)) + { + packageKey.DeleteValue(name, false); + } + else + { + packageKey.SetValue(name, value); + } + } + } + + public void Dispose() + { + Registry.LocalMachine.DeleteSubKeyTree($@"{BaseRegKeyPath}\{this.TestGroupName}", false); + Registry.LocalMachine.DeleteSubKeyTree($@"{BaseRegKeyPath}\TestBAControl", false); + } + } +} diff --git a/src/WixToolsetTest.BurnE2E/WixToolsetTest.BurnE2E.csproj b/src/WixToolsetTest.BurnE2E/WixToolsetTest.BurnE2E.csproj index 10962dcf..e08f5ce6 100644 --- a/src/WixToolsetTest.BurnE2E/WixToolsetTest.BurnE2E.csproj +++ b/src/WixToolsetTest.BurnE2E/WixToolsetTest.BurnE2E.csproj @@ -17,6 +17,7 @@ + -- cgit v1.2.3-55-g6feb