aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/TestData/FailureTests/BundleA/BundleA.wixproj19
-rw-r--r--src/TestData/FailureTests/BundleA/BundleA.wxs11
-rw-r--r--src/TestData/FailureTests/PackageA/PackageA.wixproj9
-rw-r--r--src/TestData/FailureTests/PackageB/PackageB.wixproj9
-rw-r--r--src/WixToolsetTest.BurnE2E/BundleInstaller.cs77
-rw-r--r--src/WixToolsetTest.BurnE2E/BundleRegistration.cs145
-rw-r--r--src/WixToolsetTest.BurnE2E/BundleVerifier.cs78
-rw-r--r--src/WixToolsetTest.BurnE2E/BurnE2ETests.cs7
-rw-r--r--src/WixToolsetTest.BurnE2E/FailureTests.cs48
-rw-r--r--src/WixToolsetTest.BurnE2E/MsiUtilities.cs27
-rw-r--r--src/WixToolsetTest.BurnE2E/PackageInstaller.cs10
-rw-r--r--src/WixToolsetTest.BurnE2E/PackageVerifier.cs50
-rw-r--r--src/WixToolsetTest.BurnE2E/TestBAController.cs144
-rw-r--r--src/WixToolsetTest.BurnE2E/WixToolsetTest.BurnE2E.csproj1
14 files changed, 551 insertions, 84 deletions
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 @@
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 <PropertyGroup>
4 <OutputType>Bundle</OutputType>
5 <UpgradeCode>{FE5197EB-E324-411E-B3AC-760E566E1000}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
12 <ProjectReference Include="..\PackageB\PackageB.wixproj" />
13 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 </ItemGroup>
15 <ItemGroup>
16 <PackageReference Include="WixToolset.Bal.wixext" Version="4.0.80" />
17 <PackageReference Include="WixToolset.NetFx.wixext" Version="4.0.57" />
18 </ItemGroup>
19</Project> \ 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 @@
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<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" />
8 <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" />
9 </PackageGroup>
10 </Fragment>
11</Wix>
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 @@
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 <PropertyGroup>
4 <UpgradeCode>{82FB39B2-56FA-4631-AA03-8B8D3215E6AE}</UpgradeCode>
5 </PropertyGroup>
6 <ItemGroup>
7 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
8 </ItemGroup>
9</Project> \ 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 @@
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 <PropertyGroup>
4 <UpgradeCode>{94160B95-81DD-4DAB-AE2D-246A9E3A108E}</UpgradeCode>
5 </PropertyGroup>
6 <ItemGroup>
7 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
8 </ItemGroup>
9</Project> \ 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
4{ 4{
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
7 using System.Linq;
8 using System.Text; 7 using System.Text;
9 using Microsoft.Win32;
10 using WixToolset.Data;
11 using WixToolset.Data.Symbols;
12 using Xunit;
13 8
14 public class BundleInstaller : IDisposable 9 public partial class BundleInstaller : IDisposable
15 { 10 {
16 public const string BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
17 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = "BundleCachePath";
18 public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn";
19 public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache";
20
21 public BundleInstaller(WixTestContext testContext, string name) 11 public BundleInstaller(WixTestContext testContext, string name)
22 { 12 {
23 this.Bundle = Path.Combine(testContext.TestDataFolder, $"{name}.exe"); 13 this.Bundle = Path.Combine(testContext.TestDataFolder, $"{name}.exe");
@@ -28,10 +18,6 @@ namespace WixToolsetTest.BurnE2E
28 18
29 public string Bundle { get; } 19 public string Bundle { get; }
30 20
31 public string BundlePdb { get; }
32
33 private WixBundleSymbol BundleSymbol { get; set; }
34
35 public string TestGroupName { get; } 21 public string TestGroupName { get; }
36 22
37 public string TestName { get; } 23 public string TestName { get; }
@@ -146,67 +132,6 @@ namespace WixToolsetTest.BurnE2E
146 return logFile; 132 return logFile;
147 } 133 }
148 134
149 private WixBundleSymbol GetBundleSymbol()
150 {
151 if (this.BundleSymbol == null)
152 {
153 using var wixOutput = WixOutput.Read(this.BundlePdb);
154 var intermediate = Intermediate.Load(wixOutput);
155 var section = intermediate.Sections.Single();
156 this.BundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
157 }
158
159 return this.BundleSymbol;
160 }
161
162 public string GetExpectedCachedBundlePath()
163 {
164 var bundleSymbol = this.GetBundleSymbol();
165
166 using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH);
167 var redirectedCachePath = policyKey?.GetValue("PackageCache") as string;
168 var cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME);
169 return Path.Combine(cachePath, bundleSymbol.BundleId, Path.GetFileName(this.Bundle));
170 }
171
172 public string VerifyRegisteredAndInPackageCache()
173 {
174 var bundleSymbol = this.GetBundleSymbol();
175 var bundleId = bundleSymbol.BundleId;
176 var registrationKeyPath = $"{BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY}\\{bundleId}";
177
178 using var registrationKey = Registry.LocalMachine.OpenSubKey(registrationKeyPath);
179 Assert.NotNull(registrationKey);
180
181 var cachePathValue = registrationKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH);
182 Assert.NotNull(cachePathValue);
183 var cachePath = Assert.IsType<string>(cachePathValue);
184 Assert.True(File.Exists(cachePath));
185
186 var expectedCachePath = this.GetExpectedCachedBundlePath();
187 Assert.Equal(expectedCachePath, cachePath, StringComparer.OrdinalIgnoreCase);
188
189 return cachePath;
190 }
191
192 public void VerifyUnregisteredAndRemovedFromPackageCache()
193 {
194 var cachedBundlePath = this.GetExpectedCachedBundlePath();
195 this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath);
196 }
197
198 public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath)
199 {
200 var bundleSymbol = this.GetBundleSymbol();
201 var bundleId = bundleSymbol.BundleId;
202 var registrationKeyPath = $"{BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY}\\{bundleId}";
203
204 using var registrationKey = Registry.LocalMachine.OpenSubKey(registrationKeyPath);
205 Assert.Null(registrationKey);
206
207 Assert.False(File.Exists(cachedBundlePath));
208 }
209
210 public void Dispose() 135 public void Dispose()
211 { 136 {
212 string[] args = { "-burn.ignoredependencies=ALL" }; 137 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 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using System;
6 using Microsoft.Win32;
7
8 public class BundleRegistration
9 {
10 public const string BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
11 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH = "BundleCachePath";
12 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE = "BundleAddonCode";
13 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE = "BundleDetectCode";
14 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE = "BundlePatchCode";
15 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = "BundleUpgradeCode";
16 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME = "DisplayName";
17 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION = "BundleVersion";
18 public const string BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = "EngineVersion";
19 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = "BundleProviderKey";
20 public const string BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = "BundleTag";
21 public const string REGISTRY_REBOOT_PENDING_FORMAT = "{0}.RebootRequired";
22 public const string REGISTRY_BUNDLE_INSTALLED = "Installed";
23 public const string REGISTRY_BUNDLE_DISPLAY_ICON = "DisplayIcon";
24 public const string REGISTRY_BUNDLE_DISPLAY_VERSION = "DisplayVersion";
25 public const string REGISTRY_BUNDLE_ESTIMATED_SIZE = "EstimatedSize";
26 public const string REGISTRY_BUNDLE_PUBLISHER = "Publisher";
27 public const string REGISTRY_BUNDLE_HELP_LINK = "HelpLink";
28 public const string REGISTRY_BUNDLE_HELP_TELEPHONE = "HelpTelephone";
29 public const string REGISTRY_BUNDLE_URL_INFO_ABOUT = "URLInfoAbout";
30 public const string REGISTRY_BUNDLE_URL_UPDATE_INFO = "URLUpdateInfo";
31 public const string REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = "ParentDisplayName";
32 public const string REGISTRY_BUNDLE_PARENT_KEY_NAME = "ParentKeyName";
33 public const string REGISTRY_BUNDLE_COMMENTS = "Comments";
34 public const string REGISTRY_BUNDLE_CONTACT = "Contact";
35 public const string REGISTRY_BUNDLE_NO_MODIFY = "NoModify";
36 public const string REGISTRY_BUNDLE_MODIFY_PATH = "ModifyPath";
37 public const string REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = "NoElevateOnModify";
38 public const string REGISTRY_BUNDLE_NO_REMOVE = "NoRemove";
39 public const string REGISTRY_BUNDLE_SYSTEM_COMPONENT = "SystemComponent";
40 public const string REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = "QuietUninstallString";
41 public const string REGISTRY_BUNDLE_UNINSTALL_STRING = "UninstallString";
42 public const string REGISTRY_BUNDLE_RESUME_COMMAND_LINE = "BundleResumeCommandLine";
43 public const string REGISTRY_BUNDLE_VERSION_MAJOR = "VersionMajor";
44 public const string REGISTRY_BUNDLE_VERSION_MINOR = "VersionMinor";
45
46 public string[] AddonCodes { get; set; }
47
48 public string CachePath { get; set; }
49
50 public string DisplayName { get; set; }
51
52 public string[] DetectCodes { get; set; }
53
54 public string EngineVersion { get; set; }
55
56 public int? EstimatedSize { get; set; }
57
58 public int? Installed { get; set; }
59
60 public string ModifyPath { get; set; }
61
62 public string[] PatchCodes { get; set; }
63
64 public string ProviderKey { get; set; }
65
66 public string Publisher { get; set; }
67
68 public string QuietUninstallString { get; set; }
69
70 public string QuietUninstallCommand { get; set; }
71
72 public string QuietUninstallCommandArguments { get; set; }
73
74 public string Tag { get; set; }
75
76 public string UninstallCommand { get; set; }
77
78 public string UninstallCommandArguments { get; set; }
79
80 public string UninstallString { get; set; }
81
82 public string[] UpgradeCodes { get; set; }
83
84 public string UrlInfoAbout { get; set; }
85
86 public string UrlUpdateInfo { get; set; }
87
88 public string Version { get; set; }
89
90 public static bool TryGetPerMachineBundleRegistrationById(string bundleId, out BundleRegistration registration)
91 {
92 var registrationKeyPath = $"{BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY}\\{bundleId}";
93 using var registrationKey = Registry.LocalMachine.OpenSubKey(registrationKeyPath);
94 var success = registrationKey != null;
95 registration = success ? GetBundleRegistration(registrationKey) : null;
96 return success;
97 }
98
99 private static BundleRegistration GetBundleRegistration(RegistryKey idKey)
100 {
101 var registration = new BundleRegistration();
102
103 registration.AddonCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE) as string[];
104 registration.CachePath = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH) as string;
105 registration.DetectCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE) as string[];
106 registration.PatchCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE) as string[];
107 registration.ProviderKey = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY) as string;
108 registration.Tag = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_TAG) as string;
109 registration.UpgradeCodes = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE) as string[];
110 registration.Version = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION) as string;
111 registration.DisplayName = idKey.GetValue(BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME) as string;
112 registration.EngineVersion = idKey.GetValue(BURN_REGISTRATION_REGISTRY_ENGINE_VERSION) as string;
113 registration.EstimatedSize = idKey.GetValue(REGISTRY_BUNDLE_ESTIMATED_SIZE) as int?;
114 registration.Installed = idKey.GetValue(REGISTRY_BUNDLE_INSTALLED) as int?;
115 registration.ModifyPath = idKey.GetValue(REGISTRY_BUNDLE_MODIFY_PATH) as string;
116 registration.Publisher = idKey.GetValue(REGISTRY_BUNDLE_PUBLISHER) as string;
117 registration.UrlInfoAbout = idKey.GetValue(REGISTRY_BUNDLE_URL_INFO_ABOUT) as string;
118 registration.UrlUpdateInfo = idKey.GetValue(REGISTRY_BUNDLE_URL_UPDATE_INFO) as string;
119
120 registration.QuietUninstallString = idKey.GetValue(REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING) as string;
121 if (!String.IsNullOrEmpty(registration.QuietUninstallString))
122 {
123 var closeQuote = registration.QuietUninstallString.IndexOf("\"", 1);
124 if (closeQuote > 0)
125 {
126 registration.QuietUninstallCommand = registration.QuietUninstallString.Substring(1, closeQuote - 1).Trim();
127 registration.QuietUninstallCommandArguments = registration.QuietUninstallString.Substring(closeQuote + 1).Trim();
128 }
129 }
130
131 registration.UninstallString = idKey.GetValue(REGISTRY_BUNDLE_UNINSTALL_STRING) as string;
132 if (!String.IsNullOrEmpty(registration.UninstallString))
133 {
134 var closeQuote = registration.UninstallString.IndexOf("\"", 1);
135 if (closeQuote > 0)
136 {
137 registration.UninstallCommand = registration.UninstallString.Substring(1, closeQuote - 1).Trim();
138 registration.UninstallCommandArguments = registration.UninstallString.Substring(closeQuote + 1).Trim();
139 }
140 }
141
142 return registration;
143 }
144 }
145}
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 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using System;
6 using System.IO;
7 using System.Linq;
8 using System.Text;
9 using Microsoft.Win32;
10 using WixToolset.Data;
11 using WixToolset.Data.Symbols;
12 using Xunit;
13
14 public partial class BundleInstaller
15 {
16 public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn";
17 public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache";
18
19 public string BundlePdb { get; }
20
21 private WixBundleSymbol BundleSymbol { get; set; }
22
23 private WixBundleSymbol GetBundleSymbol()
24 {
25 if (this.BundleSymbol == null)
26 {
27 using var wixOutput = WixOutput.Read(this.BundlePdb);
28 var intermediate = Intermediate.Load(wixOutput);
29 var section = intermediate.Sections.Single();
30 this.BundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
31 }
32
33 return this.BundleSymbol;
34 }
35
36 public string GetExpectedCachedBundlePath()
37 {
38 var bundleSymbol = this.GetBundleSymbol();
39
40 using var policyKey = Registry.LocalMachine.OpenSubKey(FULL_BURN_POLICY_REGISTRY_PATH);
41 var redirectedCachePath = policyKey?.GetValue("PackageCache") as string;
42 var cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME);
43 return Path.Combine(cachePath, bundleSymbol.BundleId, Path.GetFileName(this.Bundle));
44 }
45
46 public bool TryGetPerMachineRegistration(out BundleRegistration registration)
47 {
48 var bundleSymbol = this.GetBundleSymbol();
49 var bundleId = bundleSymbol.BundleId;
50 return BundleRegistration.TryGetPerMachineBundleRegistrationById(bundleId, out registration);
51 }
52
53 public string VerifyRegisteredAndInPackageCache()
54 {
55 Assert.True(this.TryGetPerMachineRegistration(out var registration));
56
57 Assert.NotNull(registration.CachePath);
58 Assert.True(File.Exists(registration.CachePath));
59
60 var expectedCachePath = this.GetExpectedCachedBundlePath();
61 Assert.Equal(expectedCachePath, registration.CachePath, StringComparer.OrdinalIgnoreCase);
62
63 return registration.CachePath;
64 }
65
66 public void VerifyUnregisteredAndRemovedFromPackageCache()
67 {
68 var cachedBundlePath = this.GetExpectedCachedBundlePath();
69 this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath);
70 }
71
72 public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath)
73 {
74 Assert.False(this.TryGetPerMachineRegistration(out _));
75 Assert.False(File.Exists(cachedBundlePath));
76 }
77 }
78}
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
28 return installer; 28 return installer;
29 } 29 }
30 30
31 protected TestBAController CreateTestBAController()
32 {
33 var controller = new TestBAController(this.TestContext);
34 this.Installers.Enqueue(controller);
35 return controller;
36 }
37
31 public void Dispose() 38 public void Dispose()
32 { 39 {
33 while (this.Installers.TryDequeue(out var installer)) 40 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 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using Xunit;
6 using Xunit.Abstractions;
7
8 public class FailureTests : BurnE2ETests
9 {
10 public FailureTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
11
12 [Fact]
13 public void CanCancelMsiPackageVeryEarly()
14 {
15 var packageA = this.CreatePackageInstaller("PackageA");
16 var packageB = this.CreatePackageInstaller("PackageB");
17 var bundleA = this.CreateBundleInstaller("BundleA");
18 var testBAController = this.CreateTestBAController();
19
20 // Cancel package B right away.
21 testBAController.SetPackageCancelExecuteAtProgress("PackageB", 1);
22
23 bundleA.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
24 bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
25
26 packageA.VerifyInstalled(false);
27 packageB.VerifyInstalled(false);
28 }
29
30 [Fact]
31 public void CanCancelMsiPackageVeryLate()
32 {
33 var packageA = this.CreatePackageInstaller("PackageA");
34 var packageB = this.CreatePackageInstaller("PackageB");
35 var bundleA = this.CreateBundleInstaller("BundleA");
36 var testBAController = this.CreateTestBAController();
37
38 // Cancel package B at the last moment possible.
39 testBAController.SetPackageCancelExecuteAtProgress("PackageB", 100);
40
41 bundleA.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_USEREXIT);
42 bundleA.VerifyUnregisteredAndRemovedFromPackageCache();
43
44 packageA.VerifyInstalled(false);
45 packageB.VerifyInstalled(false);
46 }
47 }
48}
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 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using WixToolset.Dtf.WindowsInstaller;
6
7 public class MsiUtilities
8 {
9 /// <summary>
10 /// Return true if it finds the given productcode in system otherwise it returns false
11 /// </summary>
12 /// <param name="prodCode"></param>
13 /// <returns></returns>
14 public static bool IsProductInstalled(string prodCode)
15 {
16 //look in all user's products (both per-machine and per-user)
17 foreach (ProductInstallation product in ProductInstallation.GetProducts(null, "s-1-1-0", UserContexts.All))
18 {
19 if (product.ProductCode == prodCode)
20 {
21 return true;
22 }
23 }
24 return false;
25 }
26 }
27}
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
6 using System.IO; 6 using System.IO;
7 using static WixToolsetTest.BurnE2E.MSIExec; 7 using static WixToolsetTest.BurnE2E.MSIExec;
8 8
9 public class PackageInstaller : IDisposable 9 public partial class PackageInstaller : IDisposable
10 { 10 {
11 public PackageInstaller(WixTestContext testContext, string name) 11 public PackageInstaller(WixTestContext testContext, string name)
12 { 12 {
13 this.Package = Path.Combine(testContext.TestDataFolder, $"{name}.msi"); 13 this.Package = Path.Combine(testContext.TestDataFolder, $"{name}.msi");
14 this.PackagePdb = Path.Combine(testContext.TestDataFolder, $"{name}.wixpdb");
14 this.PackageName = name; 15 this.PackageName = name;
15 this.TestContext = testContext; 16 this.TestContext = testContext;
16 } 17 }
17 18
18 public string Package { get; } 19 public string Package { get; }
19 20
20 private string PackageName { get; }
21
22 private WixTestContext TestContext { get; } 21 private WixTestContext TestContext { get; }
23 22
24 public string TestGroupName => this.TestContext.TestGroupName; 23 public string TestGroupName => this.TestContext.TestGroupName;
25 24
26 public string TestName => this.TestContext.TestName; 25 public string TestName => this.TestContext.TestName;
27 26
28 public string GetInstalledFilePath(string filename)
29 {
30 return this.TestContext.GetTestInstallFolder(Path.Combine(this.PackageName, filename));
31 }
32
33 /// <summary> 27 /// <summary>
34 /// Installs a .msi file 28 /// Installs a .msi file
35 /// </summary> 29 /// </summary>
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 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using System;
6 using System.IO;
7 using System.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.WindowsInstaller;
10 using WixToolset.Data.WindowsInstaller.Rows;
11 using Xunit;
12
13 public partial class PackageInstaller
14 {
15 private string PackageName { get; }
16
17 public string PackagePdb { get; }
18
19 private WindowsInstallerData WiData { get; set; }
20
21 public string GetInstalledFilePath(string filename)
22 {
23 return this.TestContext.GetTestInstallFolder(Path.Combine(this.PackageName, filename));
24 }
25
26 private WindowsInstallerData GetWindowsInstallerData()
27 {
28 if (this.WiData == null)
29 {
30 using var wixOutput = WixOutput.Read(this.PackagePdb);
31 this.WiData = WindowsInstallerData.Load(wixOutput);
32 }
33
34 return this.WiData;
35 }
36
37 public string GetProperty(string name)
38 {
39 var wiData = this.GetWindowsInstallerData();
40 var row = wiData.Tables["Property"].Rows.Cast<PropertyRow>().Single(r => r.Property == name);
41 return row.Value;
42 }
43
44 public void VerifyInstalled(bool installed)
45 {
46 var productCode = this.GetProperty("ProductCode");
47 Assert.Equal(installed, MsiUtilities.IsProductInstalled(productCode));
48 }
49 }
50}
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 @@
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
3namespace WixToolsetTest.BurnE2E
4{
5 using System;
6 using Microsoft.Win32;
7 using WixToolset.Mba.Core;
8
9 public class TestBAController : IDisposable
10 {
11 private const string BaseRegKeyPath = @"Software\WOW6432Node\WiX\Tests";
12
13 public TestBAController(WixTestContext testContext)
14 {
15 this.TestGroupName = testContext.TestGroupName;
16 this.TestBaseRegKeyPath = String.Format(@"{0}\TestBAControl\{1}", BaseRegKeyPath, this.TestGroupName);
17 }
18
19 private string TestBaseRegKeyPath { get; }
20
21 public string TestGroupName { get; }
22
23 /// <summary>
24 /// Sets a test value in the registry to communicate with the TestBA.
25 /// </summary>
26 /// <param name="name">Name of the value to set.</param>
27 /// <param name="value">Value to set. If this is null, the value is removed.</param>
28 public void SetBurnTestValue(string name, string value)
29 {
30 using (var testKey = Registry.LocalMachine.CreateSubKey(this.TestBaseRegKeyPath))
31 {
32 if (String.IsNullOrEmpty(value))
33 {
34 testKey.DeleteValue(name, false);
35 }
36 else
37 {
38 testKey.SetValue(name, value);
39 }
40 }
41 }
42
43 /// <summary>
44 /// Slows the cache progress of a package.
45 /// </summary>
46 /// <param name="packageId">Package identity.</param>
47 /// <param name="delay">Sets or removes the delay on a package being cached.</param>
48 public void SetPackageSlowCache(string packageId, int? delay)
49 {
50 this.SetPackageState(packageId, "SlowCache", delay.HasValue ? delay.ToString() : null);
51 }
52
53 /// <summary>
54 /// Cancels the cache of a package at a particular progress point.
55 /// </summary>
56 /// <param name="packageId">Package identity.</param>
57 /// <param name="cancelPoint">Sets or removes the cancel progress on a package being cached.</param>
58 public void SetPackageCancelCacheAtProgress(string packageId, int? cancelPoint)
59 {
60 this.SetPackageState(packageId, "CancelCacheAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null);
61 }
62
63 /// <summary>
64 /// Slows the execute progress of a package.
65 /// </summary>
66 /// <param name="packageId">Package identity.</param>
67 /// <param name="delay">Sets or removes the delay on a package being executed.</param>
68 public void SetPackageSlowExecute(string packageId, int? delay)
69 {
70 this.SetPackageState(packageId, "SlowExecute", delay.HasValue ? delay.ToString() : null);
71 }
72
73 /// <summary>
74 /// Cancels the execute of a package at a particular progress point.
75 /// </summary>
76 /// <param name="packageId">Package identity.</param>
77 /// <param name="cancelPoint">Sets or removes the cancel progress on a package being executed.</param>
78 public void SetPackageCancelExecuteAtProgress(string packageId, int? cancelPoint)
79 {
80 this.SetPackageState(packageId, "CancelExecuteAtProgress", cancelPoint.HasValue ? cancelPoint.ToString() : null);
81 }
82
83 /// <summary>
84 /// Sets the requested state for a package that the TestBA will return to the engine during plan.
85 /// </summary>
86 /// <param name="packageId">Package identity.</param>
87 /// <param name="state">State to request.</param>
88 public void SetPackageRequestedState(string packageId, RequestState state)
89 {
90 this.SetPackageState(packageId, "Requested", state.ToString());
91 }
92
93 /// <summary>
94 /// Sets the requested state for a package that the TestBA will return to the engine during plan.
95 /// </summary>
96 /// <param name="packageId">Package identity.</param>
97 /// <param name="state">State to request.</param>
98 public void SetPackageFeatureState(string packageId, string featureId, FeatureState state)
99 {
100 this.SetPackageState(packageId, String.Concat(featureId, "Requested"), state.ToString());
101 }
102
103 /// <summary>
104 /// Sets the number of times to re-run the Detect phase.
105 /// </summary>
106 /// <param name="state">Number of times to run Detect (after the first, normal, Detect).</param>
107 public void SetRedetectCount(int redetectCount)
108 {
109 this.SetPackageState(null, "RedetectCount", redetectCount.ToString());
110 }
111
112 /// <summary>
113 /// Resets the state for a package that the TestBA will return to the engine during plan.
114 /// </summary>
115 /// <param name="packageId">Package identity.</param>
116 public void ResetPackageStates(string packageId)
117 {
118 var key = String.Format(@"{0}\{1}", this.TestBaseRegKeyPath, packageId ?? String.Empty);
119 Registry.LocalMachine.DeleteSubKey(key);
120 }
121
122 private void SetPackageState(string packageId, string name, string value)
123 {
124 var key = String.Format(@"{0}\{1}", this.TestBaseRegKeyPath, packageId ?? String.Empty);
125 using (var packageKey = Registry.LocalMachine.CreateSubKey(key))
126 {
127 if (String.IsNullOrEmpty(value))
128 {
129 packageKey.DeleteValue(name, false);
130 }
131 else
132 {
133 packageKey.SetValue(name, value);
134 }
135 }
136 }
137
138 public void Dispose()
139 {
140 Registry.LocalMachine.DeleteSubKeyTree($@"{BaseRegKeyPath}\{this.TestGroupName}", false);
141 Registry.LocalMachine.DeleteSubKeyTree($@"{BaseRegKeyPath}\TestBAControl", false);
142 }
143 }
144}
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 @@
17 <PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" /> 17 <PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
18 <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.48" /> 18 <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.48" />
19 <PackageReference Include="WixToolset.Data" Version="4.0.177" /> 19 <PackageReference Include="WixToolset.Data" Version="4.0.177" />
20 <PackageReference Include="WixToolset.Mba.Core" Version="4.0.45" />
20 </ItemGroup> 21 </ItemGroup>
21 22
22 <ItemGroup> 23 <ItemGroup>