// 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 WixTestTools; using WixToolset.BootstrapperApplicationApi; using Xunit; using Xunit.Abstractions; public class SlipstreamTests : BurnE2ETests { public SlipstreamTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } private const string V090 = "0.9.0.0"; private const string V100 = "1.0.0.0"; private const string V101 = "1.0.1.0"; [RuntimeFact] public void CanInstallBundleWithSlipstreamedPatchThenRemoveIt() { var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleA = this.CreateBundleInstaller("BundleA"); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); bundleA.Install(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); bundleA.Uninstall(); bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } /// <summary> /// BundleA installs PackageA with slipstreamed PatchA. /// BundleOnlyPatchA is installed which contains PatchA (which should be a no-op). /// BundleOnlyPatchA in uninstalled which should do nothing since BundleA has a dependency on it. /// Bundle is installed which should remove everything. /// </summary> [RuntimeFact] public void ReferenceCountsSlipstreamedPatch() { var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleOnlyPatchA = this.CreateBundleInstaller("BundleOnlyPatchA"); var bundleA = this.CreateBundleInstaller("BundleA"); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); bundleA.Install(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); bundleOnlyPatchA.Install(); bundleOnlyPatchA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); bundleOnlyPatchA.Uninstall(); bundleOnlyPatchA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); bundleA.Uninstall(); bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact(Skip = "https://github.com/wixtoolset/issues/issues/6350")] public void CanInstallBundleWithSlipstreamedPatchThenRepairIt() { this.InstallBundleWithSlipstreamedPatchThenRepairIt(false); } [RuntimeFact(Skip = "https://github.com/wixtoolset/issues/issues/6350")] public void CanInstallReversedBundleWithSlipstreamedPatchThenRepairIt() { this.InstallBundleWithSlipstreamedPatchThenRepairIt(true); } private void InstallBundleWithSlipstreamedPatchThenRepairIt(bool isReversed) { var bundleName = isReversed ? "BundleAReverse" : "BundleA"; var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleA = this.CreateBundleInstaller(bundleName); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); bundleA.Install(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); // Delete the installed file and registry key so we have something to repair. File.Delete(packageAv1SourceCodeInstalled); packageAv1.DeleteTestRegistryValue(testRegistryValue); bundleA.Repair(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); bundleA.Uninstall(); bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void CanInstallSlipstreamedPatchThroughForcedRepair() { this.InstallSlipstreamedPatchThroughForcedRepair(false); } [RuntimeFact] public void CanInstallSlipstreamedPatchThroughReversedForcedRepair() { this.InstallSlipstreamedPatchThroughForcedRepair(true); } private void InstallSlipstreamedPatchThroughForcedRepair(bool isReversed) { var bundleName = isReversed ? "BundleAReverse" : "BundleA"; var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleA = this.CreateBundleInstaller(bundleName); var bundleOnlyA = this.CreateBundleInstaller("BundleOnlyA"); var testBAController = this.CreateTestBAController(); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); bundleOnlyA.Install(); bundleOnlyA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V100); // Delete the installed file and registry key so we have something to repair. File.Delete(packageAv1SourceCodeInstalled); packageAv1.DeleteTestRegistryValue(testRegistryValue); testBAController.SetPackageRequestedState("PackageA", RequestState.Repair); testBAController.SetPackageRequestedState("PatchA", RequestState.Repair); bundleA.Install(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); testBAController.ResetPackageStates("PackageA"); testBAController.ResetPackageStates("PatchA"); bundleA.Uninstall(); bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V100); bundleOnlyA.Uninstall(); bundleOnlyA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void CanUninstallSlipstreamedPatchAlone() { var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleA = this.CreateBundleInstaller("BundleA"); var testBAController = this.CreateTestBAController(); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); bundleA.Install(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); testBAController.SetPackageRequestedState("PatchA", RequestState.Absent); bundleA.Modify(); bundleA.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V100); bundleA.Uninstall(); bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void CanModifyToUninstallPackageWithSlipstreamedPatch() { var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var packageBv1 = this.CreatePackageInstaller("PackageBv1"); var bundleB = this.CreateBundleInstaller("BundleB"); var testBAController = this.CreateTestBAController(); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); var packageBv1SourceCodeInstalled = packageBv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); Assert.False(File.Exists(packageBv1SourceCodeInstalled), $"PackageBv1 payload should not be there on test start: {packageBv1SourceCodeInstalled}"); bundleB.Install(); bundleB.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); Assert.True(File.Exists(packageBv1SourceCodeInstalled), String.Concat("Should have found PackageBv1 payload installed at: ", packageBv1SourceCodeInstalled)); testBAController.SetPackageRequestedState("PackageA", RequestState.Absent); testBAController.SetPackageRequestedState("PatchA", RequestState.Absent); bundleB.Modify(); bundleB.VerifyRegisteredAndInPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should have been removed by modify from: {packageAv1SourceCodeInstalled}"); testBAController.ResetPackageStates("PackageA"); testBAController.ResetPackageStates("PatchA"); bundleB.Uninstall(); bundleB.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageBv1SourceCodeInstalled), String.Concat("PackageBv1 payload should have been removed by uninstall from: ", packageBv1SourceCodeInstalled)); packageBv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void UninstallsPackageWithSlipstreamedPatchDuringRollback() { var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var packageBv1 = this.CreatePackageInstaller("PackageBv1"); var bundleB = this.CreateBundleInstaller("BundleB"); var testBAController = this.CreateTestBAController(); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); var packageBv1SourceCodeInstalled = packageBv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); Assert.False(File.Exists(packageBv1SourceCodeInstalled), $"PackageBv1 payload should not be there on test start: {packageBv1SourceCodeInstalled}"); testBAController.SetPackageCancelExecuteAtProgress("PackageB", 50); bundleB.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT); bundleB.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should have been removed by rollback from: {packageAv1SourceCodeInstalled}"); Assert.False(File.Exists(packageBv1SourceCodeInstalled), String.Concat("PackageBv1 payload should not have been installed from: ", packageBv1SourceCodeInstalled)); packageBv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void CanAutomaticallyPredetermineSlipstreamPatchesAtBuildTime() { var testRegistryValueA = "PackageA"; var testRegistryValueA2 = "PackageA2"; var testRegistryValueB = "PackageB"; var testRegistryValueB2 = "PackageB2"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var packageBv1 = this.CreatePackageInstaller("PackageBv1"); var bundleC = this.CreateBundleInstaller("BundleC"); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); var packageBv1SourceCodeInstalled = packageBv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); Assert.False(File.Exists(packageBv1SourceCodeInstalled), $"PackageBv1 payload should not be there on test start: {packageBv1SourceCodeInstalled}"); bundleC.Install(); bundleC.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); // Product A should've slipstreamed both patches. packageAv1.VerifyTestRegistryValue(testRegistryValueA, V101); packageAv1.VerifyTestRegistryValue(testRegistryValueA2, V101); // Product B should've only slipstreamed patch AB2. packageBv1.VerifyTestRegistryValue(testRegistryValueB, V100); packageBv1.VerifyTestRegistryValue(testRegistryValueB2, V101); bundleC.Uninstall(); bundleC.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); Assert.False(File.Exists(packageBv1SourceCodeInstalled), String.Concat("PackageBv1 payload should have been removed by uninstall from: ", packageBv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void CanInstallSlipstreamedPatchWithPackageDuringMajorUpgrade() { var testRegistryValue = "PackageA"; var packageAv0 = this.CreatePackageInstaller("PackageAv0_9_0"); var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleA = this.CreateBundleInstaller("BundleA"); packageAv1.VerifyInstalled(false); packageAv0.InstallProduct(); packageAv0.VerifyInstalled(true); packageAv1.VerifyTestRegistryValue(testRegistryValue, V090); bundleA.Install(); bundleA.VerifyRegisteredAndInPackageCache(); packageAv0.VerifyInstalled(false); packageAv1.VerifyInstalled(true); packageAv1.VerifyTestRegistryValue(testRegistryValue, V101); bundleA.Uninstall(); bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); packageAv1.VerifyInstalled(false); packageAv1.VerifyTestRegistryRootDeleted(); } [RuntimeFact] public void RespectsSlipstreamedPatchInstallCondition() { var testRegistryValue = "PackageA"; var packageAv1 = this.CreatePackageInstaller("PackageAv1"); var bundleD = this.CreateBundleInstaller("BundleD"); var packageAv1SourceCodeInstalled = packageAv1.GetInstalledFilePath("Package.wxs"); Assert.False(File.Exists(packageAv1SourceCodeInstalled), $"PackageAv1 payload should not be there on test start: {packageAv1SourceCodeInstalled}"); bundleD.Install(); bundleD.VerifyRegisteredAndInPackageCache(); Assert.True(File.Exists(packageAv1SourceCodeInstalled), String.Concat("Should have found PackageAv1 payload installed at: ", packageAv1SourceCodeInstalled)); // The patch was not supposed to be installed. packageAv1.VerifyTestRegistryValue(testRegistryValue, V100); bundleD.Uninstall(); bundleD.VerifyUnregisteredAndRemovedFromPackageCache(); Assert.False(File.Exists(packageAv1SourceCodeInstalled), String.Concat("PackageAv1 payload should have been removed by uninstall from: ", packageAv1SourceCodeInstalled)); packageAv1.VerifyTestRegistryRootDeleted(); } } }