@joebigelow / wix / commits / 42964da2

Port EnableFeatureSelection tests from v3 repo, add a new one, fix them

Fixes 4616

Sean Hall committed Aug 22, 2022 at 15:11 UTC 42964da2a12df3b6e7d658a8037f17be003b1947
13 files changed +288 -5
src/burn/engine/msiengine.cpp
+9 -5
@@ -448,12 +448,15 @@ extern "C" HRESULT MsiEngineDetectPackage(
448 VERUTIL_VERSION* pVersion = NULL;
449 UINT uLcid = 0;
450 BOOL fPerMachine = FALSE;
451 + BOOL fDetectFeatures = FALSE;
452
453 // detect self by product code
454 // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED?
455 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
456 if (SUCCEEDED(hr))
457 {
458 + fDetectFeatures = TRUE;
459 +
460 hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion);
461 ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode);
462
@@ -661,7 +664,7 @@ extern "C" HRESULT MsiEngineDetectPackage(
664 BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i];
665
666 // Try to detect features state if the product is present on the machine.
664 - if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT <= pPackage->currentState)
667 + if (fDetectFeatures)
668 {
669 hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState);
670 ExitOnFailure(hr, "Failed to query feature state.");
@@ -864,8 +867,9 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage(
867
868 if (pPackage->Msi.cFeatures)
869 {
867 - // If the package is present and we're repairing it.
868 - BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested);
870 + // If the package is present and we're repairing it, or we're doing a minor update.
871 + BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) ||
872 + BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE == pPackage->Msi.operation;
873
874 // plan features
875 for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i)
@@ -1282,10 +1286,10 @@ extern "C" HRESULT MsiEngineExecutePackage(
1286 hr = ConcatPatchProperty(pCache, pPackage, fRollback, &sczObfuscatedProperties);
1287 ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string.");
1288
1285 - hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, !pPackage->Msi.cFeatures, &sczProperties);
1289 + hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczProperties);
1290 ExitOnFailure(hr, "Failed to add action property to argument string.");
1291
1288 - hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, !pPackage->Msi.cFeatures, &sczObfuscatedProperties);
1292 + hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczObfuscatedProperties);
1293 ExitOnFailure(hr, "Failed to add action property to obfuscated argument string.");
1294
1295 LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L"");
src/test/burn/TestBA/TestBA.cs
+2
@@ -320,6 +320,8 @@ namespace WixToolset.Test.BA
320 {
321 args.State = state;
322 }
323 +
324 + this.Log("OnPlanMsiFeature() - id: {0}, defaultState: {1}, requestedState: {2}", args.PackageId, args.RecommendedState, args.State);
325 }
326
327 protected override void OnPlanComplete(PlanCompleteEventArgs args)
src/test/burn/TestData/FeatureTests/BundleAv1/BundleA.props new
+11
@@ -0,0 +1,11 @@
1 +<!-- 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. -->
2 +<Project>
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <BundleName>BundleA</BundleName>
6 + <UpgradeCode>{0A6EDDC7-236F-4367-97EF-EFB0F0B478DE}</UpgradeCode>
7 + </PropertyGroup>
8 + <ItemGroup>
9 + <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 + </ItemGroup>
11 +</Project>
src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wixproj new
+16
@@ -0,0 +1,16 @@
1 +<!-- 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. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="BundleA.props" />
4 + <PropertyGroup>
5 + <Version>1.0.0.0</Version>
6 + </PropertyGroup>
7 + <ItemGroup>
8 + <ProjectReference Include="..\PackageAv1\PackageAv1.wixproj" />
9 + <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
10 + </ItemGroup>
11 + <ItemGroup>
12 + <PackageReference Include="WixToolset.Bal.wixext" />
13 + <PackageReference Include="WixToolset.NetFx.wixext" />
14 + <PackageReference Include="WixToolset.Util.wixext" />
15 + </ItemGroup>
16 +</Project>
\ No newline at end of file
src/test/burn/TestData/FeatureTests/BundleAv1/BundleAv1.wxs new
+9
@@ -0,0 +1,9 @@
1 +<!-- 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. -->
2 +
3 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
4 + <Fragment>
5 + <PackageGroup Id="BundlePackages">
6 + <MsiPackage Id="PackageA" SourceFile="$(var.PackageAv1.TargetPath)" EnableFeatureSelection="yes" />
7 + </PackageGroup>
8 + </Fragment>
9 +</Wix>
src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wixproj new
+16
@@ -0,0 +1,16 @@
1 +<!-- 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. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="..\BundleAv1\BundleA.props" />
4 + <PropertyGroup>
5 + <Version>1.0.1.0</Version>
6 + </PropertyGroup>
7 + <ItemGroup>
8 + <ProjectReference Include="..\PackageAv1_0_1\PackageAv1_0_1.wixproj" />
9 + <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
10 + </ItemGroup>
11 + <ItemGroup>
12 + <PackageReference Include="WixToolset.Bal.wixext" />
13 + <PackageReference Include="WixToolset.NetFx.wixext" />
14 + <PackageReference Include="WixToolset.Util.wixext" />
15 + </ItemGroup>
16 +</Project>
\ No newline at end of file
src/test/burn/TestData/FeatureTests/BundleAv1_0_1/BundleAv1_0_1.wxs new
+9
@@ -0,0 +1,9 @@
1 +<!-- 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. -->
2 +
3 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
4 + <Fragment>
5 + <PackageGroup Id="BundlePackages">
6 + <MsiPackage Id="PackageA" Name="PackageAv1.msi" SourceFile="$(var.PackageAv1_0_1.TargetPath)" EnableFeatureSelection="yes" />
7 + </PackageGroup>
8 + </Fragment>
9 +</Wix>
src/test/burn/TestData/FeatureTests/PackageAv1/Package.wxs new
+64
@@ -0,0 +1,64 @@
1 +<!-- 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. -->
2 +
3 +
4 +
5 +<?ifndef Version?>
6 +<?define Version = 1.0.0.0?>
7 +<?endif?>
8 +
9 +<?ifndef ProductCode?>
10 +<?define ProductCode = *?>
11 +<?endif?>
12 +
13 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
14 + <Package Name="~$(var.TestGroupName) - $(var.PackageName)" Language="1033" Version="$(var.Version)" Manufacturer="WixToolset" UpgradeCode="$(var.UpgradeCode)" Compressed="yes" ProductCode="$(var.ProductCode)">
15 + <MediaTemplate EmbedCab="yes" />
16 + <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
17 +
18 + <PropertyRef Id="TestVersion" />
19 +
20 + <Feature Id="Complete" Level="1">
21 + <ComponentRef Id="RegistryComponent"/>
22 +
23 + <!-- Not installed by default. -->
24 + <Feature Id="Test" Level="100">
25 + <ComponentRef Id="FileComponent"/>
26 + <ComponentRef Id="RegistryComponent2"/>
27 + </Feature>
28 + </Feature>
29 + </Package>
30 +
31 + <Fragment>
32 + <StandardDirectory Id="ProgramFiles6432Folder">
33 + <Directory Id="WixDir" Name="~Test WiX">
34 + <Directory Id="TestDir" Name="$(var.TestGroupName)">
35 + <Directory Id="INSTALLFOLDER" Name="$(var.PackageName)" />
36 + </Directory>
37 + </Directory>
38 + </StandardDirectory>
39 + </Fragment>
40 +
41 + <Fragment>
42 + <Component Id="FileComponent" Guid="{771E78AA-850D-4E08-AA6D-3F31E30ED140}" Directory="INSTALLFOLDER">
43 + <File Source="$(sys.SOURCEFILEPATH)" />
44 + <File Name="notkeypath.file" Source="$(sys.SOURCEFILEPATH)" />
45 + </Component>
46 + </Fragment>
47 +
48 + <Fragment>
49 + <Component Id="RegistryComponent" Directory="INSTALLFOLDER">
50 + <RegistryValue Root="HKLM" Key="Software\WiX\Tests\$(var.TestGroupName)" Name="$(var.PackageName)" Value="!(bind.Property.TestVersion)" Type="string" />
51 + <Provides Key="WiX.$(var.TestGroupName).A,v1.0" />
52 + </Component>
53 + </Fragment>
54 +
55 + <Fragment>
56 + <Component Id="RegistryComponent2" Directory="INSTALLFOLDER">
57 + <RegistryValue Root="HKLM" Key="Software\WiX\Tests\$(var.TestGroupName)" Name="$(var.PackageName)2" Value="!(bind.Property.TestVersion)" Type="string" />
58 + </Component>
59 + </Fragment>
60 +
61 + <Fragment>
62 + <Property Id="TestVersion" Value="$(var.Version)" />
63 + </Fragment>
64 +</Wix>
src/test/burn/TestData/FeatureTests/PackageAv1/PackageA.props new
+7
@@ -0,0 +1,7 @@
1 +<!-- 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. -->
2 +<Project>
3 + <PropertyGroup>
4 + <PackageName>PackageA</PackageName>
5 + <UpgradeCode>{6E3370C1-12DE-4E1C-8AE6-C15113FCE22B}</UpgradeCode>
6 + </PropertyGroup>
7 +</Project>
\ No newline at end of file
src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.props new
+7
@@ -0,0 +1,7 @@
1 +<!-- 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. -->
2 +<Project>
3 + <Import Project="PackageA.props" />
4 + <PropertyGroup>
5 + <ProductCode>{DD17AA83-3B58-4C97-859A-78F228D0B680}</ProductCode>
6 + </PropertyGroup>
7 +</Project>
\ No newline at end of file
src/test/burn/TestData/FeatureTests/PackageAv1/PackageAv1.wixproj new
+4
@@ -0,0 +1,4 @@
1 +<!-- 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. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="PackageAv1.props" />
4 +</Project>
\ No newline at end of file
src/test/burn/TestData/FeatureTests/PackageAv1_0_1/PackageAv1_0_1.wixproj new
+10
@@ -0,0 +1,10 @@
1 +<!-- 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. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="..\PackageAv1\PackageAv1.props" />
4 + <PropertyGroup>
5 + <Version>1.0.1.0</Version>
6 + </PropertyGroup>
7 + <ItemGroup>
8 + <Compile Include="..\PackageAv1\Package.wxs" Link="Package.wxs" />
9 + </ItemGroup>
10 +</Project>
\ No newline at end of file
src/test/burn/WixToolsetTest.BurnE2E/FeatureTests.cs new
+124
@@ -0,0 +1,124 @@
1 +// 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.
2 +
3 +namespace WixToolsetTest.BurnE2E
4 +{
5 + using System;
6 + using System.IO;
7 + using WixTestTools;
8 + using WixToolset.Mba.Core;
9 + using Xunit;
10 + using Xunit.Abstractions;
11 +
12 + public class FeatureTests : BurnE2ETests
13 + {
14 + public FeatureTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
15 +
16 + [RuntimeFact]
17 + public void CanControlFeatureSelectionDuringInstallAndModify()
18 + {
19 + var packageA = this.CreatePackageInstaller("PackageAv1");
20 + var bundleA = this.CreateBundleInstaller("BundleAv1");
21 + var testBAController = this.CreateTestBAController();
22 +
23 + // Install the bundle without the optional feature present
24 + testBAController.SetPackageFeatureState("PackageA", "Complete", FeatureState.Local);
25 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Absent);
26 + bundleA.Install();
27 + bundleA.VerifyRegisteredAndInPackageCache();
28 +
29 + string packageSourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
30 + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Should not have found Package A payload installed at: ", packageSourceCodeInstalled));
31 + packageA.VerifyTestRegistryValue("PackageA", "1.0.0.0");
32 +
33 + // Now turn on the feature.
34 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local);
35 +
36 + bundleA.Modify();
37 + bundleA.VerifyRegisteredAndInPackageCache();
38 + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A payload installed at: ", packageSourceCodeInstalled));
39 +
40 + // Turn the feature back off.
41 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Absent);
42 + bundleA.Modify();
43 + bundleA.VerifyRegisteredAndInPackageCache();
44 + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Should have removed Package A payload from: ", packageSourceCodeInstalled));
45 +
46 + // Uninstall everything.
47 + testBAController.ResetPackageStates("PackageA");
48 + bundleA.Uninstall();
49 + bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
50 + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Package A payload should have been removed by uninstall from: ", packageSourceCodeInstalled));
51 + packageA.VerifyTestRegistryRootDeleted();
52 + }
53 +
54 + [RuntimeFact]
55 + public void CanControlFeatureSelectionDuringRepair()
56 + {
57 + var packageA = this.CreatePackageInstaller("PackageAv1");
58 + var bundleA = this.CreateBundleInstaller("BundleAv1");
59 + var testBAController = this.CreateTestBAController();
60 +
61 + // Install the bundle with the optional feature present
62 + testBAController.SetPackageFeatureState("PackageA", "Complete", FeatureState.Local);
63 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local);
64 + bundleA.Install();
65 + bundleA.VerifyRegisteredAndInPackageCache();
66 +
67 + string packageSourceCodeInstalled = packageA.GetInstalledFilePath("Package.wxs");
68 + string packageNotKeyPathFile = packageA.GetInstalledFilePath("notkeypath.file");
69 +
70 + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A keyfile installed at: ", packageSourceCodeInstalled));
71 + Assert.True(File.Exists(packageNotKeyPathFile), String.Concat("Should have found Package A non-keyfile installed at: ", packageNotKeyPathFile));
72 +
73 + // Delete the non-keypath source file.
74 + File.Delete(packageNotKeyPathFile);
75 +
76 + // Now repair without repairing the feature to verify the non-keyfile doesn't come back.
77 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Unknown);
78 + bundleA.Repair();
79 + bundleA.VerifyRegisteredAndInPackageCache();
80 + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A keyfile installed at: ", packageSourceCodeInstalled));
81 + Assert.False(File.Exists(packageNotKeyPathFile), String.Concat("Should have not found Package A non-keyfile installed at: ", packageNotKeyPathFile));
82 +
83 + // Now repair and include the feature this time.
84 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local);
85 + bundleA.Repair();
86 + bundleA.VerifyRegisteredAndInPackageCache();
87 + Assert.True(File.Exists(packageSourceCodeInstalled), String.Concat("Should have found Package A keyfile installed at: ", packageSourceCodeInstalled));
88 + Assert.True(File.Exists(packageNotKeyPathFile), String.Concat("Should have repaired Package A non-keyfile installed at: ", packageNotKeyPathFile));
89 +
90 + // Uninstall everything.
91 + testBAController.ResetPackageStates("PackageA");
92 + bundleA.Uninstall();
93 + bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
94 + Assert.False(File.Exists(packageSourceCodeInstalled), String.Concat("Package A payload should have been removed by uninstall from: ", packageSourceCodeInstalled));
95 + packageA.VerifyTestRegistryRootDeleted();
96 + }
97 +
98 + [RuntimeFact]
99 + public void CanControlFeatureSelectionDuringMinorUpgrade()
100 + {
101 + var packageAv1 = this.CreatePackageInstaller("PackageAv1");
102 + var packageAv1_0_1 = this.CreatePackageInstaller("PackageAv1_0_1");
103 + var bundleAv1 = this.CreateBundleInstaller("BundleAv1");
104 + var bundleAv1_0_1 = this.CreateBundleInstaller("BundleAv1_0_1");
105 + var testBAController = this.CreateTestBAController();
106 +
107 + // Install v1 with the optional feature turned on.
108 + testBAController.SetPackageFeatureState("PackageA", "Complete", FeatureState.Local);
109 + testBAController.SetPackageFeatureState("PackageA", "Test", FeatureState.Local);
110 + bundleAv1.Install();
111 + bundleAv1.VerifyRegisteredAndInPackageCache();
112 +
113 + packageAv1.VerifyInstalledWithVersion(true);
114 + packageAv1.VerifyTestRegistryValue("PackageA", "1.0.0.0");
115 +
116 + // Install v1.0.1 with the optional feature still turned on.
117 + bundleAv1_0_1.Install();
118 + bundleAv1_0_1.VerifyRegisteredAndInPackageCache();
119 +
120 + packageAv1_0_1.VerifyInstalledWithVersion(true);
121 + packageAv1_0_1.VerifyTestRegistryValue("PackageA", "1.0.1.0");
122 + }
123 + }
124 +}