From 42964da2a12df3b6e7d658a8037f17be003b1947 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Mon, 22 Aug 2022 15:11:15 -0500 Subject: Port EnableFeatureSelection tests from v3 repo, add a new one, fix them Fixes 4616 --- src/burn/engine/msiengine.cpp | 14 ++- src/test/burn/TestBA/TestBA.cs | 2 + .../TestData/FeatureTests/BundleAv1/BundleA.props | 11 ++ .../FeatureTests/BundleAv1/BundleAv1.wixproj | 16 +++ .../TestData/FeatureTests/BundleAv1/BundleAv1.wxs | 9 ++ .../BundleAv1_0_1/BundleAv1_0_1.wixproj | 16 +++ .../FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wxs | 9 ++ .../TestData/FeatureTests/PackageAv1/Package.wxs | 64 +++++++++++ .../FeatureTests/PackageAv1/PackageA.props | 7 ++ .../FeatureTests/PackageAv1/PackageAv1.props | 7 ++ .../FeatureTests/PackageAv1/PackageAv1.wixproj | 4 + .../PackageAv1_0_1/PackageAv1_0_1.wixproj | 10 ++ .../burn/WixToolsetTest.BurnE2E/FeatureTests.cs | 124 +++++++++++++++++++++ 13 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 src/test/burn/TestData/FeatureTests/BundleAv1/BundleA.props create mode 100644 src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wixproj create mode 100644 src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wxs create mode 100644 src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wixproj create mode 100644 src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wxs create mode 100644 src/test/burn/TestData/FeatureTests/PackageAv1/Package.wxs create mode 100644 src/test/burn/TestData/FeatureTests/PackageAv1/PackageA.props create mode 100644 src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.props create mode 100644 src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.wixproj create mode 100644 src/test/burn/TestData/FeatureTests/PackageAv1_0_1/PackageAv1_0_1.wixproj create mode 100644 src/test/burn/WixToolsetTest.BurnE2E/FeatureTests.cs (limited to 'src') diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp index edbf4d45..2c1b0d4f 100644 --- a/src/burn/engine/msiengine.cpp +++ b/src/burn/engine/msiengine.cpp @@ -448,12 +448,15 @@ extern "C" HRESULT MsiEngineDetectPackage( VERUTIL_VERSION* pVersion = NULL; UINT uLcid = 0; BOOL fPerMachine = FALSE; + BOOL fDetectFeatures = FALSE; // detect self by product code // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED? hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (SUCCEEDED(hr)) { + fDetectFeatures = TRUE; + hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); @@ -661,7 +664,7 @@ extern "C" HRESULT MsiEngineDetectPackage( BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; // Try to detect features state if the product is present on the machine. - if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState) + if (fDetectFeatures) { hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState); ExitOnFailure(hr, "Failed to query feature state."); @@ -864,8 +867,9 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( if (pPackage->Msi.cFeatures) { - // If the package is present and we're repairing it. - BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested); + // If the package is present and we're repairing it, or we're doing a minor update. + BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) || + BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE == pPackage->Msi.operation; // plan features for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) @@ -1282,10 +1286,10 @@ extern "C" HRESULT MsiEngineExecutePackage( hr = ConcatPatchProperty(pCache, pPackage, fRollback, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); - hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, !pPackage->Msi.cFeatures, &sczProperties); + hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczProperties); ExitOnFailure(hr, "Failed to add action property to argument string."); - hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, !pPackage->Msi.cFeatures, &sczObfuscatedProperties); + hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); diff --git a/src/test/burn/TestBA/TestBA.cs b/src/test/burn/TestBA/TestBA.cs index e73c907e..087219af 100644 --- a/src/test/burn/TestBA/TestBA.cs +++ b/src/test/burn/TestBA/TestBA.cs @@ -320,6 +320,8 @@ namespace WixToolset.Test.BA { args.State = state; } + + this.Log("OnPlanMsiFeature() - id: {0}, defaultState: {1}, requestedState: {2}", args.PackageId, args.RecommendedState, args.State); } protected override void OnPlanComplete(PlanCompleteEventArgs args) diff --git a/src/test/burn/TestData/FeatureTests/BundleAv1/BundleA.props b/src/test/burn/TestData/FeatureTests/BundleAv1/BundleA.props new file mode 100644 index 00000000..dcfcbc82 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/BundleAv1/BundleA.props @@ -0,0 +1,11 @@ + + + + Bundle + BundleA + {0A6EDDC7-236F-4367-97EF-EFB0F0B478DE} + + + + + diff --git a/src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wixproj b/src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wixproj new file mode 100644 index 00000000..8a5c9594 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wixproj @@ -0,0 +1,16 @@ + + + + + 1.0.0.0 + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wxs b/src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wxs new file mode 100644 index 00000000..0fa01755 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wxs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wixproj b/src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wixproj new file mode 100644 index 00000000..4c6a0993 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wixproj @@ -0,0 +1,16 @@ + + + + + 1.0.1.0 + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wxs b/src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wxs new file mode 100644 index 00000000..68802a47 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wxs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/test/burn/TestData/FeatureTests/PackageAv1/Package.wxs b/src/test/burn/TestData/FeatureTests/PackageAv1/Package.wxs new file mode 100644 index 00000000..20ce503a --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/PackageAv1/Package.wxs @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/FeatureTests/PackageAv1/PackageA.props b/src/test/burn/TestData/FeatureTests/PackageAv1/PackageA.props new file mode 100644 index 00000000..a7c721c1 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/PackageAv1/PackageA.props @@ -0,0 +1,7 @@ + + + + PackageA + {6E3370C1-12DE-4E1C-8AE6-C15113FCE22B} + + \ No newline at end of file diff --git a/src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.props b/src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.props new file mode 100644 index 00000000..0b0bb2c4 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.props @@ -0,0 +1,7 @@ + + + + + {DD17AA83-3B58-4C97-859A-78F228D0B680} + + \ No newline at end of file diff --git a/src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.wixproj b/src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.wixproj new file mode 100644 index 00000000..e85be384 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.wixproj @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/FeatureTests/PackageAv1_0_1/PackageAv1_0_1.wixproj b/src/test/burn/TestData/FeatureTests/PackageAv1_0_1/PackageAv1_0_1.wixproj new file mode 100644 index 00000000..74d94a41 --- /dev/null +++ b/src/test/burn/TestData/FeatureTests/PackageAv1_0_1/PackageAv1_0_1.wixproj @@ -0,0 +1,10 @@ + + + + + 1.0.1.0 + + + + + \ No newline at end of file diff --git a/src/test/burn/WixToolsetTest.BurnE2E/FeatureTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/FeatureTests.cs new file mode 100644 index 00000000..b3d0dfe1 --- /dev/null +++ b/src/test/burn/WixToolsetTest.BurnE2E/FeatureTests.cs @@ -0,0 +1,124 @@ +// 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.Mba.Core; + using Xunit; + using Xunit.Abstractions; + + public class FeatureTests : BurnE2ETests + { + public FeatureTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } + + [RuntimeFact] + public void CanControlFeatureSelectionDuringInstallAndModify() + { + var packageA = this.CreatePackageInstaller("PackageAv1"); + var bundleA = this.CreateBundleInstaller("BundleAv1"); + var testBAController = this.CreateTestBAController(); + + // Install the bundle without the optional feature present + testBAController.SetPackageFeatureState("PackageA", "Complete", FeatureState.Local); + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Absent); + bundleA.Install(); + bundleA.VerifyRegisteredAndInPackageCache(); + + string packageSourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs"); + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Should not have found Package A payload installed at: ", packageSourceCodeInstalled)); + packageA.VerifyTestRegistryValue("PackageA", "1.0.0.0"); + + // Now turn on the feature. + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local); + + bundleA.Modify(); + bundleA.VerifyRegisteredAndInPackageCache(); + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageSourceCodeInstalled)); + + // Turn the feature back off. + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Absent); + bundleA.Modify(); + bundleA.VerifyRegisteredAndInPackageCache(); + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Should have removed Package A payload from: ", packageSourceCodeInstalled)); + + // Uninstall everything. + testBAController.ResetPackageStates("PackageA"); + bundleA.Uninstall(); + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Package A payload should have been removed by uninstall from: ", packageSourceCodeInstalled)); + packageA.VerifyTestRegistryRootDeleted(); + } + + [RuntimeFact] + public void CanControlFeatureSelectionDuringRepair() + { + var packageA = this.CreatePackageInstaller("PackageAv1"); + var bundleA = this.CreateBundleInstaller("BundleAv1"); + var testBAController = this.CreateTestBAController(); + + // Install the bundle with the optional feature present + testBAController.SetPackageFeatureState("PackageA", "Complete", FeatureState.Local); + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local); + bundleA.Install(); + bundleA.VerifyRegisteredAndInPackageCache(); + + string packageSourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs"); + string packageNotKeyPathFile = packageA.GetInstalledFilePath("notkeypath.file"); + + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A keyfile installed at: ", packageSourceCodeInstalled)); + Assert.True(File.Exists(packageNotKeyPathFile), String.Concat("Should have found Package A non-keyfile installed at: ", packageNotKeyPathFile)); + + // Delete the non-keypath source file. + File.Delete(packageNotKeyPathFile); + + // Now repair without repairing the feature to verify the non-keyfile doesn't come back. + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Unknown); + bundleA.Repair(); + bundleA.VerifyRegisteredAndInPackageCache(); + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A keyfile installed at: ", packageSourceCodeInstalled)); + Assert.False(File.Exists(packageNotKeyPathFile), String.Concat("Should have not found Package A non-keyfile installed at: ", packageNotKeyPathFile)); + + // Now repair and include the feature this time. + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local); + bundleA.Repair(); + bundleA.VerifyRegisteredAndInPackageCache(); + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A keyfile installed at: ", packageSourceCodeInstalled)); + Assert.True(File.Exists(packageNotKeyPathFile), String.Concat("Should have repaired Package A non-keyfile installed at: ", packageNotKeyPathFile)); + + // Uninstall everything. + testBAController.ResetPackageStates("PackageA"); + bundleA.Uninstall(); + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Package A payload should have been removed by uninstall from: ", packageSourceCodeInstalled)); + packageA.VerifyTestRegistryRootDeleted(); + } + + [RuntimeFact] + public void CanControlFeatureSelectionDuringMinorUpgrade() + { + var packageAv1 = this.CreatePackageInstaller("PackageAv1"); + var packageAv1_0_1 = this.CreatePackageInstaller("PackageAv1_0_1"); + var bundleAv1 = this.CreateBundleInstaller("BundleAv1"); + var bundleAv1_0_1 = this.CreateBundleInstaller("BundleAv1_0_1"); + var testBAController = this.CreateTestBAController(); + + // Install v1 with the optional feature turned on. + testBAController.SetPackageFeatureState("PackageA", "Complete", FeatureState.Local); + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local); + bundleAv1.Install(); + bundleAv1.VerifyRegisteredAndInPackageCache(); + + packageAv1.VerifyInstalledWithVersion(true); + packageAv1.VerifyTestRegistryValue("PackageA", "1.0.0.0"); + + // Install v1.0.1 with the optional feature still turned on. + bundleAv1_0_1.Install(); + bundleAv1_0_1.VerifyRegisteredAndInPackageCache(); + + packageAv1_0_1.VerifyInstalledWithVersion(true); + packageAv1_0_1.VerifyTestRegistryValue("PackageA", "1.0.1.0"); + } + } +} -- cgit v1.2.3-55-g6feb