| 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 WixTestTools |
| 4 | { |
| 5 | using System; |
| 6 | using System.IO; |
| 7 | using System.Linq; |
| 8 | using System.Text; |
| 9 | using Microsoft.Win32; |
| 10 | using WixInternal.TestSupport; |
| 11 | using WixToolset.Data; |
| 12 | using WixToolset.Data.Symbols; |
| 13 | using Xunit; |
| 14 | |
| 15 | public partial class BundleInstaller |
| 16 | { |
| 17 | public const string DependencyRegistryRoot = "Software\\Classes\\Installer\\Dependencies"; |
| 18 | public const string FULL_BURN_POLICY_REGISTRY_PATH = "SOFTWARE\\Policies\\WiX\\Burn"; |
| 19 | public const string FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE = "SOFTWARE\\WOW6432Node\\Policies\\WiX\\Burn"; |
| 20 | public const string PACKAGE_CACHE_FOLDER_NAME = "Package Cache"; |
| 21 | |
| 22 | public string BundlePdb { get; } |
| 23 | |
| 24 | private WixBundleSymbol BundleSymbol { get; set; } |
| 25 | |
| 26 | private WixBundleSymbol GetBundleSymbol() |
| 27 | { |
| 28 | if (this.BundleSymbol == null) |
| 29 | { |
| 30 | using var wixOutput = WixOutput.Read(this.BundlePdb); |
| 31 | var intermediate = Intermediate.Load(wixOutput); |
| 32 | var section = intermediate.Sections.Single(); |
| 33 | this.BundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single(); |
| 34 | } |
| 35 | |
| 36 | return this.BundleSymbol; |
| 37 | } |
| 38 | |
| 39 | public string GetFullBurnPolicyRegistryPath() |
| 40 | { |
| 41 | var bundleSymbol = this.GetBundleSymbol(); |
| 42 | var x64 = bundleSymbol.Platform != Platform.X86; |
| 43 | return x64 ? FULL_BURN_POLICY_REGISTRY_PATH : FULL_BURN_POLICY_REGISTRY_PATH_WOW6432NODE; |
| 44 | } |
| 45 | |
| 46 | public string GetPackageCachePathForCacheId(string cacheId, bool perMachine) |
| 47 | { |
| 48 | string cachePath; |
| 49 | if (perMachine) |
| 50 | { |
| 51 | using var policyKey = Registry.LocalMachine.OpenSubKey(this.GetFullBurnPolicyRegistryPath()); |
| 52 | var redirectedCachePath = policyKey?.GetValue("PackageCache") as string; |
| 53 | cachePath = redirectedCachePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), PACKAGE_CACHE_FOLDER_NAME); |
| 54 | } |
| 55 | else |
| 56 | { |
| 57 | cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), PACKAGE_CACHE_FOLDER_NAME); |
| 58 | } |
| 59 | return Path.Combine(cachePath, cacheId); |
| 60 | } |
| 61 | |
| 62 | public string GetExpectedCachedBundlePath() |
| 63 | { |
| 64 | var bundleSymbol = this.GetBundleSymbol(); |
| 65 | var cachePath = this.GetPackageCachePathForCacheId(bundleSymbol.BundleId, bundleSymbol.PerMachine); |
| 66 | return Path.Combine(cachePath, Path.GetFileName(this.Bundle)); |
| 67 | } |
| 68 | |
| 69 | public string ManuallyCache() |
| 70 | { |
| 71 | var expectedCachePath = this.GetExpectedCachedBundlePath(); |
| 72 | Directory.CreateDirectory(Path.GetDirectoryName(expectedCachePath)); |
| 73 | File.Copy(this.Bundle, expectedCachePath); |
| 74 | return expectedCachePath; |
| 75 | } |
| 76 | |
| 77 | public void ManuallyUncache() |
| 78 | { |
| 79 | var expectedCachePath = this.GetExpectedCachedBundlePath(); |
| 80 | File.Delete(expectedCachePath); |
| 81 | } |
| 82 | |
| 83 | public bool TryGetArpEntryExePackageConfiguration(string packageId, out string arpId, out string arpVersion, out bool arpWin64, out bool perMachine) |
| 84 | { |
| 85 | using var wixOutput = WixOutput.Read(this.BundlePdb); |
| 86 | var intermediate = Intermediate.Load(wixOutput); |
| 87 | var section = intermediate.Sections.Single(); |
| 88 | var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().SingleOrDefault(p => p.Id.Id == packageId); |
| 89 | var exePackageSymbol = section.Symbols.OfType<WixBundleExePackageSymbol>().SingleOrDefault(p => p.Id.Id == packageId); |
| 90 | if (packageSymbol == null || exePackageSymbol == null || exePackageSymbol.DetectionType != WixBundleExePackageDetectionType.Arp) |
| 91 | { |
| 92 | arpId = null; |
| 93 | arpVersion = null; |
| 94 | arpWin64 = false; |
| 95 | perMachine = false; |
| 96 | return false; |
| 97 | } |
| 98 | |
| 99 | arpId = exePackageSymbol.ArpId; |
| 100 | arpVersion = exePackageSymbol.ArpDisplayVersion; |
| 101 | arpWin64 = exePackageSymbol.ArpWin64; |
| 102 | perMachine = packageSymbol.PerMachine == true; |
| 103 | return true; |
| 104 | } |
| 105 | |
| 106 | public bool TryGetRegistration(out BundleRegistration registration) |
| 107 | { |
| 108 | var bundleSymbol = this.GetBundleSymbol(); |
| 109 | var x64 = bundleSymbol.Platform != Platform.X86; |
| 110 | var bundleId = bundleSymbol.BundleId; |
| 111 | if (bundleSymbol.PerMachine) |
| 112 | { |
| 113 | return BundleRegistration.TryGetPerMachineBundleRegistrationById(bundleId, x64, out registration); |
| 114 | } |
| 115 | else |
| 116 | { |
| 117 | return BundleRegistration.TryGetPerUserBundleRegistrationById(bundleId, out registration); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | public BundleRegistration VerifyRegisteredAndInPackageCache(int? expectedSystemComponent = null) |
| 122 | { |
| 123 | Assert.True(this.TryGetRegistration(out var registration)); |
| 124 | |
| 125 | Assert.Equal(expectedSystemComponent, registration.SystemComponent); |
| 126 | |
| 127 | Assert.NotNull(registration.CachePath); |
| 128 | Assert.True(File.Exists(registration.CachePath)); |
| 129 | |
| 130 | var expectedCachePath = this.GetExpectedCachedBundlePath(); |
| 131 | WixAssert.StringEqual(expectedCachePath, registration.CachePath, true); |
| 132 | |
| 133 | return registration; |
| 134 | } |
| 135 | |
| 136 | public void VerifyUnregisteredAndRemovedFromPackageCache() |
| 137 | { |
| 138 | var cachedBundlePath = this.GetExpectedCachedBundlePath(); |
| 139 | this.VerifyUnregisteredAndRemovedFromPackageCache(cachedBundlePath); |
| 140 | } |
| 141 | |
| 142 | public void VerifyUnregisteredAndRemovedFromPackageCache(string cachedBundlePath) |
| 143 | { |
| 144 | Assert.False(this.TryGetRegistration(out _)); |
| 145 | Assert.False(File.Exists(cachedBundlePath)); |
| 146 | } |
| 147 | |
| 148 | public void RemovePackageFromCache(string packageId) |
| 149 | { |
| 150 | using var wixOutput = WixOutput.Read(this.BundlePdb); |
| 151 | var intermediate = Intermediate.Load(wixOutput); |
| 152 | var section = intermediate.Sections.Single(); |
| 153 | var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId); |
| 154 | var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == true); |
| 155 | if (Directory.Exists(cachePath)) |
| 156 | { |
| 157 | Directory.Delete(cachePath, true); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | public string GetPackageEntryPointCachePath(string packageId) |
| 162 | { |
| 163 | using var wixOutput = WixOutput.Read(this.BundlePdb); |
| 164 | var intermediate = Intermediate.Load(wixOutput); |
| 165 | var section = intermediate.Sections.Single(); |
| 166 | var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId); |
| 167 | var packagePayloadSymbol = section.Symbols.OfType<WixBundlePayloadSymbol>().Single(p => p.Id.Id == packageSymbol.PayloadRef); |
| 168 | var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == true); |
| 169 | return Path.Combine(cachePath, packagePayloadSymbol.Name); |
| 170 | } |
| 171 | |
| 172 | public void VerifyPackageIsCached(string packageId, bool cached = true) |
| 173 | { |
| 174 | var entryPointCachePath = this.GetPackageEntryPointCachePath(packageId); |
| 175 | Assert.Equal(cached, File.Exists(entryPointCachePath)); |
| 176 | } |
| 177 | |
| 178 | public void VerifyPackageProviderRemoved(string packageId) |
| 179 | { |
| 180 | using var wixOutput = WixOutput.Read(this.BundlePdb); |
| 181 | var intermediate = Intermediate.Load(wixOutput); |
| 182 | var section = intermediate.Sections.Single(); |
| 183 | var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId); |
| 184 | var providerSymbol = section.Symbols.OfType<WixDependencyProviderSymbol>().Single(p => p.ParentRef == packageId); |
| 185 | var registryRoot = packageSymbol.PerMachine == true ? Registry.LocalMachine : Registry.CurrentUser; |
| 186 | var subkeyPath = Path.Combine(DependencyRegistryRoot, providerSymbol.ProviderKey); |
| 187 | using var registryKey = registryRoot.OpenSubKey(subkeyPath); |
| 188 | if (registryKey != null) |
| 189 | { |
| 190 | WixAssert.StringEqual(null, subkeyPath); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | public void VerifyExeTestRegistryRootDeleted(string name, bool x64 = false) |
| 195 | { |
| 196 | using var testRegistryRoot = this.TestContext.GetTestRegistryRoot(x64, name); |
| 197 | if (testRegistryRoot != null) |
| 198 | { |
| 199 | var actualValue = testRegistryRoot.GetValue("Version") as string; |
| 200 | Assert.Null(actualValue); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | public void VerifyExeTestRegistryValue(string name, string expectedValue, bool x64 = false) |
| 205 | { |
| 206 | using (var root = this.TestContext.GetTestRegistryRoot(x64, name)) |
| 207 | { |
| 208 | Assert.NotNull(root); |
| 209 | var actualValue = root.GetValue("Version") as string; |
| 210 | Assert.Equal(expectedValue, actualValue); |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | } |