From c8b602e595bced83dad206ce40189634432f1f07 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 22 Jan 2021 15:59:13 -0600 Subject: Add test that cancels an MSI package in the OnProgress for the package. This makes it cancel after the MSI install completed but before Burn considers the package complete, which needs to cause Burn to roll it back (#3089). Also add more logging in TestBA when it does non-standard stuff. --- src/Utilities/TestBA/TestBA.cs | 71 +++++++++++++++++++++++++- src/WixToolsetTest.BurnE2E/FailureTests.cs | 18 +++++++ src/WixToolsetTest.BurnE2E/TestBAController.cs | 20 ++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/src/Utilities/TestBA/TestBA.cs b/src/Utilities/TestBA/TestBA.cs index e3305d33..2a4bd581 100644 --- a/src/Utilities/TestBA/TestBA.cs +++ b/src/Utilities/TestBA/TestBA.cs @@ -29,6 +29,8 @@ namespace WixToolset.Test.BA private int cancelCacheAtProgress; private int sleepDuringExecute; private int cancelExecuteAtProgress; + private string cancelExecuteActionName; + private int cancelOnProgressAtProgress; private int retryExecuteFilesInUse; private IBootstrapperCommand Command { get; } @@ -227,24 +229,34 @@ namespace WixToolset.Test.BA { this.sleepDuringCache = 0; } + else + { + this.Log(" SlowCache: {0}", this.sleepDuringCache); + } string cancelCache = this.ReadPackageAction(args.PackageId, "CancelCacheAtProgress"); if (String.IsNullOrEmpty(cancelCache) || !Int32.TryParse(cancelCache, out this.cancelCacheAtProgress)) { this.cancelCacheAtProgress = -1; } + else + { + this.Log(" CancelCacheAtProgress: {0}", this.cancelCacheAtProgress); + } } protected override void OnCacheAcquireProgress(CacheAcquireProgressEventArgs args) { this.Log("OnCacheAcquireProgress() - container/package: {0}, payload: {1}, progress: {2}, total: {3}, overall progress: {4}%", args.PackageOrContainerId, args.PayloadId, args.Progress, args.Total, args.OverallPercentage); - if (this.cancelCacheAtProgress > 0 && this.cancelCacheAtProgress <= args.Progress) + if (this.cancelCacheAtProgress >= 0 && this.cancelCacheAtProgress <= args.Progress) { args.Cancel = true; + this.Log("OnCacheAcquireProgress(cancel)"); } else if (this.sleepDuringCache > 0) { + this.Log("OnCacheAcquireProgress(sleep {0})", this.sleepDuringCache); Thread.Sleep(this.sleepDuringCache); } } @@ -258,18 +270,46 @@ namespace WixToolset.Test.BA { this.sleepDuringExecute = 0; } + else + { + this.Log(" SlowExecute: {0}", this.sleepDuringExecute); + } string cancelExecute = this.ReadPackageAction(args.PackageId, "CancelExecuteAtProgress"); if (String.IsNullOrEmpty(cancelExecute) || !Int32.TryParse(cancelExecute, out this.cancelExecuteAtProgress)) { this.cancelExecuteAtProgress = -1; } + else + { + this.Log(" CancelExecuteAtProgress: {0}", this.cancelExecuteAtProgress); + } + + this.cancelExecuteActionName = this.ReadPackageAction(args.PackageId, "CancelExecuteAtActionStart"); + if (!String.IsNullOrEmpty(this.cancelExecuteActionName)) + { + this.Log(" CancelExecuteAtActionState: {0}", this.cancelExecuteActionName); + } + + string cancelOnProgressAtProgress = this.ReadPackageAction(args.PackageId, "CancelOnProgressAtProgress"); + if (String.IsNullOrEmpty(cancelOnProgressAtProgress) || !Int32.TryParse(cancelOnProgressAtProgress, out this.cancelOnProgressAtProgress)) + { + this.cancelOnProgressAtProgress = -1; + } + else + { + this.Log(" CancelOnProgressAtProgress: {0}", this.cancelOnProgressAtProgress); + } string retryBeforeCancel = this.ReadPackageAction(args.PackageId, "RetryExecuteFilesInUse"); if (String.IsNullOrEmpty(retryBeforeCancel) || !Int32.TryParse(retryBeforeCancel, out this.retryExecuteFilesInUse)) { this.retryExecuteFilesInUse = 0; } + else + { + this.Log(" RetryExecuteFilesInUse: {0}", this.retryExecuteFilesInUse); + } } protected override void OnExecuteFilesInUse(ExecuteFilesInUseEventArgs args) @@ -287,16 +327,30 @@ namespace WixToolset.Test.BA } } + protected override void OnExecuteMsiMessage(ExecuteMsiMessageEventArgs args) + { + this.Log("OnExecuteMsiMessage() - MessageType: {0}, Message: {1}, Data: '{2}'", args.MessageType, args.Message, String.Join("','", args.Data.ToArray())); + + if (!String.IsNullOrEmpty(this.cancelExecuteActionName) && args.MessageType == InstallMessage.ActionStart && + args.Data.Count > 0 && args.Data[0] == this.cancelExecuteActionName) + { + this.Log("OnExecuteMsiMessage(cancelNextProgress)"); + this.cancelExecuteAtProgress = 0; + } + } + protected override void OnExecuteProgress(ExecuteProgressEventArgs args) { this.Log("OnExecuteProgress() - package: {0}, progress: {1}%, overall progress: {2}%", args.PackageId, args.ProgressPercentage, args.OverallPercentage); - if (this.cancelExecuteAtProgress > 0 && this.cancelExecuteAtProgress <= args.ProgressPercentage) + if (this.cancelExecuteAtProgress >= 0 && this.cancelExecuteAtProgress <= args.ProgressPercentage) { args.Cancel = true; + this.Log("OnExecuteProgress(cancel)"); } else if (this.sleepDuringExecute > 0) { + this.Log("OnExecuteProgress(sleep {0})", this.sleepDuringExecute); Thread.Sleep(this.sleepDuringExecute); } } @@ -313,6 +367,12 @@ namespace WixToolset.Test.BA { this.Engine.SendEmbeddedProgress(args.ProgressPercentage, args.OverallPercentage); } + + if (this.cancelOnProgressAtProgress >= 0 && this.cancelOnProgressAtProgress <= args.OverallPercentage) + { + args.Cancel = true; + this.Log("OnProgress(cancel)"); + } } protected override void OnResolveSource(ResolveSourceEventArgs args) @@ -323,6 +383,13 @@ namespace WixToolset.Test.BA } } + protected override void OnApplyBegin(ApplyBeginEventArgs args) + { + this.cancelOnProgressAtProgress = -1; + this.cancelExecuteAtProgress = -1; + this.cancelCacheAtProgress = -1; + } + protected override void OnApplyComplete(ApplyCompleteEventArgs args) { // Output what the privileges are now. diff --git a/src/WixToolsetTest.BurnE2E/FailureTests.cs b/src/WixToolsetTest.BurnE2E/FailureTests.cs index 773c9dd0..ba6e5ba4 100644 --- a/src/WixToolsetTest.BurnE2E/FailureTests.cs +++ b/src/WixToolsetTest.BurnE2E/FailureTests.cs @@ -44,5 +44,23 @@ namespace WixToolsetTest.BurnE2E packageA.VerifyInstalled(false); packageB.VerifyInstalled(false); } + + [Fact] + public void CanCancelMsiPackageInOnProgress() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var packageB = this.CreatePackageInstaller("PackageB"); + var bundleA = this.CreateBundleInstaller("BundleA"); + var testBAController = this.CreateTestBAController(); + + // Cancel package B during its OnProgress message. + testBAController.SetPackageCancelOnProgressAtProgress("PackageB", 100); + + bundleA.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT); + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(false); + } } } diff --git a/src/WixToolsetTest.BurnE2E/TestBAController.cs b/src/WixToolsetTest.BurnE2E/TestBAController.cs index 6ae9a9dd..54a81b46 100644 --- a/src/WixToolsetTest.BurnE2E/TestBAController.cs +++ b/src/WixToolsetTest.BurnE2E/TestBAController.cs @@ -80,6 +80,26 @@ namespace WixToolsetTest.BurnE2E this.SetPackageState(packageId, "CancelExecuteAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null); } + /// + /// Cancels the execute of a package at the next progess after the specified MSI action start. + /// + /// Package identity. + /// Sets or removes the cancel progress on a package being executed. + public void SetPackageCancelExecuteAtActionStart(string packageId, string actionName) + { + this.SetPackageState(packageId, "CancelExecuteAtActionStart", actionName); + } + + /// + /// Cancels the execute of a package at a particular OnProgress point. + /// + /// Package identity. + /// Sets or removes the cancel OnProgress point on a package being executed. + public void SetPackageCancelOnProgressAtProgress(string packageId, int? cancelPoint) + { + this.SetPackageState(packageId, "CancelOnProgressAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null); + } + /// /// Sets the requested state for a package that the TestBA will return to the engine during plan. /// -- cgit v1.2.3-55-g6feb