From edccb203c421d2bd820062024088c6698424d9ee Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Wed, 4 Feb 2026 20:47:04 -0500 Subject: Support dual-purpose packages in Burn. Fixes https://github.com/wixtoolset/issues/issues/8958 --- src/test/burn/WixTestTools/ArpEntryVerifier.cs | 16 ++- src/test/burn/WixTestTools/BundleInstaller.cs | 5 +- src/test/burn/WixTestTools/BundleRegistration.cs | 13 +-- src/test/burn/WixTestTools/BundleVerifier.cs | 119 ++++++++++++++------- .../burn/WixTestTools/GenericArpRegistration.cs | 18 ++-- src/test/burn/WixTestTools/PackageVerifier.cs | 26 ++++- src/test/burn/WixTestTools/TestTool.cs | 14 ++- src/test/burn/WixTestTools/WixTestContext.cs | 4 + 8 files changed, 152 insertions(+), 63 deletions(-) (limited to 'src/test/burn/WixTestTools') diff --git a/src/test/burn/WixTestTools/ArpEntryVerifier.cs b/src/test/burn/WixTestTools/ArpEntryVerifier.cs index b3c70337..4e43062a 100644 --- a/src/test/burn/WixTestTools/ArpEntryVerifier.cs +++ b/src/test/burn/WixTestTools/ArpEntryVerifier.cs @@ -8,17 +8,25 @@ namespace WixTestTools { public bool TryGetRegistration(out GenericArpRegistration registration) { - bool success = !this.PerMachine ? GenericArpRegistration.TryGetPerUserRegistrationById(this.ArpId, out registration) - : GenericArpRegistration.TryGetPerMachineRegistrationById(this.ArpId, this.X64, out registration); + var success = this.PerMachine + ? GenericArpRegistration.TryGetPerMachineRegistrationById(this.ArpId, this.X64, this.TestContext.TestOutputHelper, out registration) + : GenericArpRegistration.TryGetPerUserRegistrationById(this.ArpId, this.TestContext.TestOutputHelper, out registration); return success; } public void VerifyRegistered(bool registered) { - bool success = this.TryGetRegistration(out _); + var success = this.TryGetRegistration(out _); - Assert.Equal(registered, success); + if (registered) + { + Assert.True(success); + } + else + { + Assert.False(success); + } } } } diff --git a/src/test/burn/WixTestTools/BundleInstaller.cs b/src/test/burn/WixTestTools/BundleInstaller.cs index 0f2cfa8f..93602661 100644 --- a/src/test/burn/WixTestTools/BundleInstaller.cs +++ b/src/test/burn/WixTestTools/BundleInstaller.cs @@ -189,7 +189,8 @@ namespace WixTestTools /// Path to the generated log file. private string RunBundleWithArguments(int expectedExitCode, MSIExec.MSIExecMode mode, string[] arguments, bool assertOnError = true, string bundlePath = null, string layoutDirectory = null) { - TestTool bundle = new TestTool(bundlePath ?? this.Bundle); + var exePath = bundlePath ?? this.Bundle; + var bundle = new TestTool(exePath); var sb = new StringBuilder(); // Be sure to run silent. @@ -230,6 +231,8 @@ namespace WixTestTools // Set the arguments. bundle.Arguments = sb.ToString(); + this.TestContext.TestOutputHelper.WriteLine($"Launching '{exePath}' with arguments '{bundle.Arguments}'..."); + // Run the tool and assert the expected code. bundle.ExpectedExitCode = expectedExitCode; bundle.AlternateExitCode = this.AlternateExitCode; diff --git a/src/test/burn/WixTestTools/BundleRegistration.cs b/src/test/burn/WixTestTools/BundleRegistration.cs index 524d4616..57581510 100644 --- a/src/test/burn/WixTestTools/BundleRegistration.cs +++ b/src/test/burn/WixTestTools/BundleRegistration.cs @@ -4,6 +4,7 @@ namespace WixTestTools { using System; using Microsoft.Win32; + using Xunit.Abstractions; public class BundleRegistration : GenericArpRegistration { @@ -36,19 +37,19 @@ namespace WixTestTools public string[] UpgradeCodes { get; set; } - public static bool TryGetPerMachineBundleRegistrationById(string id, bool x64, out BundleRegistration registration) + public static bool TryGetPerMachineBundleRegistrationById(string id, bool x64, ITestOutputHelper testOutputHelper, out BundleRegistration registration) { - return TryGetRegistrationById(id, x64, false, out registration); + return TryGetRegistrationById(id, x64, false, testOutputHelper, out registration); } - public static bool TryGetPerUserBundleRegistrationById(string id, out BundleRegistration registration) + public static bool TryGetPerUserBundleRegistrationById(string id, ITestOutputHelper testOutputHelper, out BundleRegistration registration) { - return TryGetRegistrationById(id, true, true, out registration); + return TryGetRegistrationById(id, true, true, testOutputHelper, out registration); } - private static bool TryGetRegistrationById(string id, bool x64, bool perUser, out BundleRegistration registration) + private static bool TryGetRegistrationById(string id, bool x64, bool perUser, ITestOutputHelper testOutputHelper, out BundleRegistration registration) { - registration = GetGenericArpRegistration(id, x64, perUser, key => GetBundleRegistration(key)); + registration = GetGenericArpRegistration(id, x64, perUser, testOutputHelper, key => GetBundleRegistration(key)); return registration != null; } diff --git a/src/test/burn/WixTestTools/BundleVerifier.cs b/src/test/burn/WixTestTools/BundleVerifier.cs index b6181047..7bfba687 100644 --- a/src/test/burn/WixTestTools/BundleVerifier.cs +++ b/src/test/burn/WixTestTools/BundleVerifier.cs @@ -5,8 +5,6 @@ namespace WixTestTools using System; using System.IO; using System.Linq; - using System.Text; - using System.Xml.Linq; using Microsoft.Win32; using WixInternal.TestSupport; using WixToolset.Data; @@ -56,43 +54,64 @@ namespace WixTestTools { var bundleSymbol = this.GetBundleSymbol(); var x64 = bundleSymbol.Platform != Platform.X86; + return x64 ? FULL_BURN_POLICY_REGISTRY_PATH : FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE; } - public string GetPackageCachePathForCacheId(string cacheId, bool perMachine) + public string GetPackageCachePathForCacheId(string cacheId, WixBundleScopeType? scope, bool? plannedPerMachine = null) { string cachePath; - if (perMachine) + + if (scope == WixBundleScopeType.PerMachine) { - using var policyKey = Registry.LocalMachine.OpenSubKey(this.GetFullBurnPolicyRegistryPath()); - var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; - cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); + cachePath = GetPerMachineCacheRoot(); + } + else if (scope == WixBundleScopeType.PerUser) + { + cachePath = GetPerUserCacheRoot(); } else { - cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), PACKAGE_CACHE_FOLDER_NAME); + cachePath = plannedPerMachine.Value ? GetPerMachineCacheRoot() : GetPerUserCacheRoot(); } + return Path.Combine(cachePath, cacheId); + + string GetPerMachineCacheRoot() + { + using var policyKey = Registry.LocalMachine.OpenSubKey(this.GetFullBurnPolicyRegistryPath()); + var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; + return redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); + } + + string GetPerUserCacheRoot() + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), PACKAGE_CACHE_FOLDER_NAME); + } } - public string GetExpectedCachedBundlePath() + public string GetExpectedCachedBundlePath(bool? plannedPerMachine = null) { var bundleSymbol = this.GetBundleSymbol(); - var cachePath = this.GetPackageCachePathForCacheId(bundleSymbol.BundleCode, bundleSymbol.PerMachine); + var cachePath = this.GetPackageCachePathForCacheId(bundleSymbol.BundleCode, bundleSymbol.Scope, plannedPerMachine); + return Path.Combine(cachePath, Path.GetFileName(this.Bundle)); } - public string ManuallyCache() + public string ManuallyCache(bool? plannedPerMachine = null) { - var expectedCachePath = this.GetExpectedCachedBundlePath(); + var expectedCachePath = this.GetExpectedCachedBundlePath(plannedPerMachine); + Directory.CreateDirectory(Path.GetDirectoryName(expectedCachePath)); File.Copy(this.Bundle, expectedCachePath); + return expectedCachePath; } - public void ManuallyUncache() + public void ManuallyUncache(bool? plannedPerMachine = null) { - var expectedCachePath = this.GetExpectedCachedBundlePath(); + var expectedCachePath = this.GetExpectedCachedBundlePath(plannedPerMachine); + File.Delete(expectedCachePath); } @@ -103,38 +122,51 @@ namespace WixTestTools var section = intermediate.Sections.Single(); var packageSymbol = section.Symbols.OfType().SingleOrDefault(p => p.Id.Id == packageId); var exePackageSymbol = section.Symbols.OfType().SingleOrDefault(p => p.Id.Id == packageId); + if (packageSymbol == null || exePackageSymbol == null || exePackageSymbol.DetectionType != WixBundleExePackageDetectionType.Arp) { + this.TestContext.TestOutputHelper.WriteLine($"Missing config for ExePackage {packageId}"); + arpId = null; arpVersion = null; arpWin64 = false; perMachine = false; + return false; } arpId = exePackageSymbol.ArpId; arpVersion = exePackageSymbol.ArpDisplayVersion; arpWin64 = exePackageSymbol.ArpWin64; - perMachine = packageSymbol.PerMachine == true; + perMachine = packageSymbol.Scope == WixBundleScopeType.PerMachine; + + this.TestContext.TestOutputHelper.WriteLine($"Config for ExePackage {packageId}: arpId={arpId}, arpVersion={arpVersion}, arpWin64={arpWin64}, perMachine={perMachine}"); + return true; } - public bool TryGetRegistration(out BundleRegistration registration) + public bool TryGetRegistration(bool? plannedPerMachine, out BundleRegistration registration) { var bundleSymbol = this.GetBundleSymbol(); var x64 = bundleSymbol.Platform != Platform.X86; var bundleCode = bundleSymbol.BundleCode; - if (bundleSymbol.PerMachine) + + if (bundleSymbol.Scope == WixBundleScopeType.PerMachine) + { + return BundleRegistration.TryGetPerMachineBundleRegistrationById(bundleCode, x64, this.TestContext.TestOutputHelper, out registration); + } + else if (bundleSymbol.Scope == WixBundleScopeType.PerUser) { - return BundleRegistration.TryGetPerMachineBundleRegistrationById(bundleCode, x64, out registration); + return BundleRegistration.TryGetPerUserBundleRegistrationById(bundleCode, this.TestContext.TestOutputHelper, out registration); } else { - return BundleRegistration.TryGetPerUserBundleRegistrationById(bundleCode, out registration); + return plannedPerMachine.Value ? BundleRegistration.TryGetPerMachineBundleRegistrationById(bundleCode, x64, this.TestContext.TestOutputHelper, out registration) + : BundleRegistration.TryGetPerUserBundleRegistrationById(bundleCode, this.TestContext.TestOutputHelper, out registration); } } - public bool TryGetUpdateRegistration(out BundleUpdateRegistration registration) + public bool TryGetUpdateRegistration(bool? plannedPerMachine, out BundleUpdateRegistration registration) { var bundleSymbol = this.GetBundleSymbol(); var x64 = bundleSymbol.Platform != Platform.X86; @@ -144,84 +176,93 @@ namespace WixTestTools var productFamily = updateRegistrationSymbol.ProductFamily; var name = updateRegistrationSymbol.Name; - - if (bundleSymbol.PerMachine) + if (bundleSymbol.Scope == WixBundleScopeType.PerMachine) { return BundleUpdateRegistration.TryGetPerMachineBundleUpdateRegistration(manufacturer, productFamily, name, x64, out registration); } - else + else if (bundleSymbol.Scope == WixBundleScopeType.PerUser) { return BundleUpdateRegistration.TryGetPerUserBundleUpdateRegistration(manufacturer, productFamily, name, out registration); } + else + { + return plannedPerMachine.Value ? BundleUpdateRegistration.TryGetPerMachineBundleUpdateRegistration(manufacturer, productFamily, name, x64, out registration) + : BundleUpdateRegistration.TryGetPerUserBundleUpdateRegistration(manufacturer, productFamily, name, out registration); + } } - public BundleRegistration VerifyRegisteredAndInPackageCache(int? expectedSystemComponent = null) + public BundleRegistration VerifyRegisteredAndInPackageCache(int? expectedSystemComponent = null, bool? plannedPerMachine = null) { - Assert.True(this.TryGetRegistration(out var registration)); + Assert.True(this.TryGetRegistration(plannedPerMachine, out var registration)); Assert.Equal(expectedSystemComponent, registration.SystemComponent); Assert.NotNull(registration.CachePath); Assert.True(File.Exists(registration.CachePath)); - var expectedCachePath = this.GetExpectedCachedBundlePath(); + var expectedCachePath = this.GetExpectedCachedBundlePath(plannedPerMachine); WixAssert.StringEqual(expectedCachePath, registration.CachePath, true); return registration; } - public void VerifyUnregisteredAndRemovedFromPackageCache() + public void VerifyUnregisteredAndRemovedFromPackageCache(bool? plannedPerMachine = null) { - var cachedBundlePath = this.GetExpectedCachedBundlePath(); - this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath); + var cachedBundlePath = this.GetExpectedCachedBundlePath(plannedPerMachine); + + this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath, plannedPerMachine); } - public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath) + public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath, bool? plannedPerMachine = null) { - Assert.False(this.TryGetRegistration(out _), $"Bundle cached at '{cachedBundlePath}' should not still be registered."); + Assert.False(this.TryGetRegistration(plannedPerMachine, out _), $"Bundle cached at '{cachedBundlePath}' should not still be registered."); Assert.False(File.Exists(cachedBundlePath), $"Cached bundle should have been removed from package cache at '{cachedBundlePath}'."); } - public void RemovePackageFromCache(string packageId) + public void RemovePackageFromCache(string packageId, bool? plannedPerMachine = null) { using var wixOutput = WixOutput.Read(this.BundlePdb); var intermediate = Intermediate.Load(wixOutput); var section = intermediate.Sections.Single(); var packageSymbol = section.Symbols.OfType().Single(p => p.Id.Id == packageId); - var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == true); + var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.Scope, plannedPerMachine); + if (Directory.Exists(cachePath)) { Directory.Delete(cachePath, true); } } - public string GetPackageEntryPointCachePath(string packageId) + public string GetPackageEntryPointCachePath(string packageId, bool? plannedPerMachine = null) { using var wixOutput = WixOutput.Read(this.BundlePdb); var intermediate = Intermediate.Load(wixOutput); var section = intermediate.Sections.Single(); var packageSymbol = section.Symbols.OfType().Single(p => p.Id.Id == packageId); var packagePayloadSymbol = section.Symbols.OfType().Single(p => p.Id.Id == packageSymbol.PayloadRef); - var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == true); + var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.Scope, plannedPerMachine); + return Path.Combine(cachePath, packagePayloadSymbol.Name); } - public void VerifyPackageIsCached(string packageId, bool cached = true) + public void VerifyPackageIsCached(string packageId, bool cached = true, bool? plannedPerMachine = null) { - var entryPointCachePath = this.GetPackageEntryPointCachePath(packageId); + var entryPointCachePath = this.GetPackageEntryPointCachePath(packageId, plannedPerMachine); + Assert.Equal(cached, File.Exists(entryPointCachePath)); } - public void VerifyPackageProviderRemoved(string packageId) + public void VerifyPackageProviderRemoved(string packageId, bool? plannedPerMachine = null) { using var wixOutput = WixOutput.Read(this.BundlePdb); var intermediate = Intermediate.Load(wixOutput); var section = intermediate.Sections.Single(); var packageSymbol = section.Symbols.OfType().Single(p => p.Id.Id == packageId); var providerSymbol = section.Symbols.OfType().Single(p => p.ParentRef == packageId); - var registryRoot = packageSymbol.PerMachine == true ? Registry.LocalMachine : Registry.CurrentUser; + var registryRoot = plannedPerMachine.HasValue ? (plannedPerMachine.Value ? Registry.LocalMachine : Registry.CurrentUser) : packageSymbol.Scope == WixBundleScopeType.PerMachine ? Registry.LocalMachine : Registry.CurrentUser; var subkeyPath = Path.Combine(DependencyRegistryRoot, providerSymbol.ProviderKey); using var registryKey = registryRoot.OpenSubKey(subkeyPath); + if (registryKey != null) { WixAssert.StringEqual(null, subkeyPath); diff --git a/src/test/burn/WixTestTools/GenericArpRegistration.cs b/src/test/burn/WixTestTools/GenericArpRegistration.cs index dfddd9a3..f3590fa4 100644 --- a/src/test/burn/WixTestTools/GenericArpRegistration.cs +++ b/src/test/burn/WixTestTools/GenericArpRegistration.cs @@ -4,6 +4,8 @@ namespace WixTestTools { using System; using Microsoft.Win32; + using Xunit; + using Xunit.Abstractions; public class GenericArpRegistration { @@ -69,23 +71,23 @@ namespace WixTestTools public string UrlUpdateInfo { get; set; } - public static bool TryGetPerMachineRegistrationById(string id, bool x64, out GenericArpRegistration registration) + public static bool TryGetPerMachineRegistrationById(string id, bool x64, ITestOutputHelper testOutputHelper, out GenericArpRegistration registration) { - return TryGetRegistrationById(id, x64, false, out registration); + return TryGetRegistrationById(id, x64, perUser: false, testOutputHelper, out registration); } - public static bool TryGetPerUserRegistrationById(string id, out GenericArpRegistration registration) + public static bool TryGetPerUserRegistrationById(string id, ITestOutputHelper testOutputHelper, out GenericArpRegistration registration) { - return TryGetRegistrationById(id, true, true, out registration); + return TryGetRegistrationById(id, x64: true, perUser: true, testOutputHelper, out registration); } - private static bool TryGetRegistrationById(string id, bool x64, bool perUser, out GenericArpRegistration registration) + private static bool TryGetRegistrationById(string id, bool x64, bool perUser, ITestOutputHelper testOutputHelper, out GenericArpRegistration registration) { - registration = GetGenericArpRegistration(id, x64, perUser, key => new GenericArpRegistration()); + registration = GetGenericArpRegistration(id, x64, perUser, testOutputHelper, key => new GenericArpRegistration()); return registration != null; } - protected static T GetGenericArpRegistration(string id, bool x64, bool perUser, Func fnCreate) + protected static T GetGenericArpRegistration(string id, bool x64, bool perUser, ITestOutputHelper testOutputHelper, Func fnCreate) where T : GenericArpRegistration { var baseKey = perUser ? Registry.CurrentUser : Registry.LocalMachine; @@ -95,6 +97,8 @@ namespace WixTestTools if (idKey == null) { + testOutputHelper.WriteLine($"Failed to open key {baseKey} {registrationKeyPath} (per-user {perUser})."); + return null; } diff --git a/src/test/burn/WixTestTools/PackageVerifier.cs b/src/test/burn/WixTestTools/PackageVerifier.cs index 76bc17ab..f7c7b2e6 100644 --- a/src/test/burn/WixTestTools/PackageVerifier.cs +++ b/src/test/burn/WixTestTools/PackageVerifier.cs @@ -82,14 +82,32 @@ namespace WixTestTools return MsiUtilities.IsProductInstalledWithVersion(productCode, prodVersion); } - public void VerifyInstalled(bool installed) + public void VerifyInstalled(bool installed = true) { - Assert.Equal(installed, this.IsInstalled()); + var isInstalled = this.IsInstalled(); + + if (installed) + { + Assert.True(isInstalled); + } + else + { + Assert.False(isInstalled); + } } - public void VerifyInstalledWithVersion(bool installed) + public void VerifyInstalledWithVersion(bool installed = true) { - Assert.Equal(installed, this.IsInstalledWithVersion()); + var isInstalled = this.IsInstalledWithVersion(); + + if (installed) + { + Assert.True(isInstalled); + } + else + { + Assert.False(isInstalled); + } } public void DeleteTestRegistryValue(string name) diff --git a/src/test/burn/WixTestTools/TestTool.cs b/src/test/burn/WixTestTools/TestTool.cs index b6d18ac9..6c9ad1d7 100644 --- a/src/test/burn/WixTestTools/TestTool.cs +++ b/src/test/burn/WixTestTools/TestTool.cs @@ -26,7 +26,7 @@ namespace WixTestTools public TestTool(string toolFile) : base(toolFile) { - this.PrintOutputToConsole = true; + this.PrintOutputToConsoleOnError = true; } /// @@ -74,6 +74,11 @@ namespace WixTestTools /// public bool PrintOutputToConsole { get; set; } + /// + /// Print output from the tool execution to the console + /// + public bool PrintOutputToConsoleOnError { get; set; } + /// /// The working directory of the tool /// @@ -123,7 +128,12 @@ namespace WixTestTools if (assertOnError && 0 < this.Errors.Count) { - if (this.PrintOutputToConsole) + if (this.PrintOutputToConsoleOnError) + { + Console.WriteLine(FormatResult(result)); + } + + if (this.PrintOutputToConsole || this.PrintOutputToConsoleOnError) { this.PrintErrors(); } diff --git a/src/test/burn/WixTestTools/WixTestContext.cs b/src/test/burn/WixTestTools/WixTestContext.cs index a87a56fa..b30d5739 100644 --- a/src/test/burn/WixTestTools/WixTestContext.cs +++ b/src/test/burn/WixTestTools/WixTestContext.cs @@ -16,6 +16,8 @@ namespace WixTestTools public WixTestContext(ITestOutputHelper testOutputHelper) { + this.TestOutputHelper = testOutputHelper; + var test = GetTest(testOutputHelper); var splitClassName = test.TestCase.TestMethod.TestClass.Class.Name.Split('.'); @@ -34,6 +36,8 @@ namespace WixTestTools public string TestName { get; } + public ITestOutputHelper TestOutputHelper { get; } + /// /// Gets the test install directory for the current test. /// -- cgit v1.2.3-55-g6feb