From 124fef398a26bc8e139e889a2345602d2478590c Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 3 Aug 2022 14:55:23 -0500 Subject: Add ability to skip a local path candidate if it failed verification. Fixes 6818 --- src/test/burn/TestBA/TestBA.cs | 51 ++++++++++++ .../TestData/LayoutTests/BundleB/BundleB.wixproj | 17 ++++ .../burn/TestData/LayoutTests/BundleB/BundleB.wxs | 10 +++ .../burn/WixToolsetTest.BurnE2E/LayoutTests.cs | 93 ++++++++++++++++++++++ .../Utilities/TestBAController.cs | 53 +++++++++++- 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj create mode 100644 src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs (limited to 'src/test') diff --git a/src/test/burn/TestBA/TestBA.cs b/src/test/burn/TestBA/TestBA.cs index 7e3d2623..e73c907e 100644 --- a/src/test/burn/TestBA/TestBA.cs +++ b/src/test/burn/TestBA/TestBA.cs @@ -155,6 +155,9 @@ namespace WixToolset.Test.BA this.quitAfterDetect = false; } + this.ImportContainerSources(); + this.ImportPayloadSources(); + this.wait.WaitOne(); if (this.action == LaunchAction.Help) @@ -657,6 +660,54 @@ namespace WixToolset.Test.BA this.Engine.Log(LogLevel.Standard, String.Concat("TESTBA", relation, ": ", message)); } + private void ImportContainerSources() + { + string testName = this.Engine.GetVariableString("TestGroupName"); + using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}\container", testName))) + { + if (testKey == null) + { + return; + } + + foreach (var containerId in testKey.GetSubKeyNames()) + { + using (RegistryKey subkey = testKey.OpenSubKey(containerId)) + { + string initialSource = subkey == null ? null : subkey.GetValue("InitialLocalSource") as string; + if (initialSource != null) + { + this.Engine.SetLocalSource(containerId, null, initialSource); + } + } + } + } + } + + private void ImportPayloadSources() + { + string testName = this.Engine.GetVariableString("TestGroupName"); + using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}\payload", testName))) + { + if (testKey == null) + { + return; + } + + foreach (var payloadId in testKey.GetSubKeyNames()) + { + using (RegistryKey subkey = testKey.OpenSubKey(payloadId)) + { + string initialSource = subkey == null ? null : subkey.GetValue("InitialLocalSource") as string; + if (initialSource != null) + { + this.Engine.SetLocalSource(null, payloadId, initialSource); + } + } + } + } + } + private List ReadVerifyArguments() { string testName = this.Engine.GetVariableString("TestGroupName"); diff --git a/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj new file mode 100644 index 00000000..2ce22ea1 --- /dev/null +++ b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wixproj @@ -0,0 +1,17 @@ + + + + hyperlinkLicense + Bundle + {CBF22B7D-C6C0-408A-9F29-81FE8610559C} + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs new file mode 100644 index 00000000..fb6eadb9 --- /dev/null +++ b/src/test/burn/TestData/LayoutTests/BundleB/BundleB.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs index 47a42750..14c3cd5d 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/LayoutTests.cs @@ -65,5 +65,98 @@ namespace WixToolsetTest.BurnE2E Assert.True(File.Exists(Path.Combine(layoutDirectory, "packages.cab"))); Assert.True(File.Exists(Path.Combine(layoutDirectory, "BundleA.wxs"))); } + + [RuntimeFact] + public void CanSkipOverCorruptLocalFileForDownloadableFile() + { + var bundleB = this.CreateBundleInstaller("BundleB"); + var webServer = this.CreateWebServer(); + + webServer.AddFiles(new Dictionary + { + { "/BundleB/PackageA.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageA.msi") }, + }); + webServer.Start(); + + using var dfs = new DisposableFileSystem(); + var baseDirectory = dfs.GetFolder(true); + var sourceDirectory = Path.Combine(baseDirectory, "source"); + Directory.CreateDirectory(sourceDirectory); + var layoutDirectory = Path.Combine(baseDirectory, "layout"); + Directory.CreateDirectory(layoutDirectory); + + // Manually copy bundle to empty directory and then run from there so it can't find the uncorrupted file. + var bundleBFileInfo = new FileInfo(bundleB.Bundle); + var bundleBCopiedPath = Path.Combine(sourceDirectory, bundleBFileInfo.Name); + bundleBFileInfo.CopyTo(bundleBCopiedPath); + + // Copy a corrupted version of PackageA.msi next to the bundle. + var packageAFileInfo = new FileInfo(Path.Combine(bundleBFileInfo.DirectoryName, "PackageA.msi")); + var packageACorruptedFileInfo = new FileInfo(Path.Combine(sourceDirectory, packageAFileInfo.Name)); + packageAFileInfo.CopyTo(packageACorruptedFileInfo.FullName); + SubtlyCorruptFile(packageACorruptedFileInfo); + + var layoutLogPath = bundleB.Layout(bundleBCopiedPath, layoutDirectory); + bundleB.VerifyUnregisteredAndRemovedFromPackageCache(); + + Assert.True(File.Exists(Path.Combine(layoutDirectory, bundleBFileInfo.Name))); + Assert.True(File.Exists(Path.Combine(layoutDirectory, packageAFileInfo.Name))); + Assert.True(LogVerifier.MessageInLogFile(layoutLogPath, "Verification failed on payload group item: PackageA")); + } + + [RuntimeFact] + public void CanSkipOverCorruptLocalFileForOtherLocalFile() + { + var bundleA = this.CreateBundleInstaller("BundleA"); + var testBAController = this.CreateTestBAController(); + + using var dfs = new DisposableFileSystem(); + var baseDirectory = dfs.GetFolder(true); + var sourceDirectory = Path.Combine(baseDirectory, "source"); + Directory.CreateDirectory(sourceDirectory); + var layoutDirectory = Path.Combine(baseDirectory, "layout"); + Directory.CreateDirectory(layoutDirectory); + + // Copy a corrupted version of packages.cab and BundleA.wxs. + var bundleAFileInfo = new FileInfo(bundleA.Bundle); + var packagesCabFileInfo = new FileInfo(Path.Combine(bundleAFileInfo.DirectoryName, "packages.cab")); + var packagesCabCorruptedFileInfo = new FileInfo(Path.Combine(sourceDirectory, packagesCabFileInfo.Name)); + packagesCabFileInfo.CopyTo(packagesCabCorruptedFileInfo.FullName); + SubtlyCorruptFile(packagesCabCorruptedFileInfo); + + var layoutOnlyPayloadFileInfo = new FileInfo(Path.Combine(bundleAFileInfo.DirectoryName, "BundleA.wxs")); + var layoutOnlyPayloadCorruptedFileInfo = new FileInfo(Path.Combine(sourceDirectory, layoutOnlyPayloadFileInfo.Name)); + layoutOnlyPayloadFileInfo.CopyTo(layoutOnlyPayloadCorruptedFileInfo.FullName); + SubtlyCorruptFile(layoutOnlyPayloadCorruptedFileInfo); + + // Set the source to absolute path so the engine tries the corrupted files first. + testBAController.SetContainerInitialLocalSource("PackagesContainer", packagesCabCorruptedFileInfo.FullName); + testBAController.SetPayloadInitialLocalSource("LayoutOnlyPayload", layoutOnlyPayloadCorruptedFileInfo.FullName); + + var layoutLogPath = bundleA.Layout(layoutDirectory); + bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); + + Assert.True(File.Exists(Path.Combine(layoutDirectory, bundleAFileInfo.Name))); + Assert.True(File.Exists(Path.Combine(layoutDirectory, packagesCabFileInfo.Name))); + Assert.True(File.Exists(Path.Combine(layoutDirectory, "BundleA.wxs"))); + Assert.True(LogVerifier.MessageInLogFile(layoutLogPath, "Verification failed on container: PackagesContainer")); + Assert.True(LogVerifier.MessageInLogFile(layoutLogPath, "Verification failed on payload group item: LayoutOnlyPayload")); + } + + private static void SubtlyCorruptFile(FileInfo fileInfo) + { + using (var v = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite)) + { + // Change one byte of information in the middle of the file to corrupt it. + // Hopefully this ensures that these tests will continue to work even if optimizations are added later, + // such as checking the file header, product code, or product version during acquisition. + var bytePosition = v.Length / 2; + v.Position = bytePosition; + var byteValue = v.ReadByte(); + byteValue = byteValue == 0 ? 1 : 0; + v.Position = bytePosition; + v.WriteByte((byte)byteValue); + } + } } } diff --git a/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs b/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs index 3f9d76b8..115f3b54 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/Utilities/TestBAController.cs @@ -182,6 +182,26 @@ namespace WixToolsetTest.BurnE2E this.SetPackageState(packageId, "ProcessCancelAction", action.ToString()); } + /// + /// At startup, set the payload's source path to the given path. + /// + /// + /// + public void SetPayloadInitialLocalSource(string payloadId, string sourcePath) + { + this.SetPayloadState(payloadId, "InitialLocalSource", sourcePath); + } + + /// + /// At startup, set the container's source path to the given path. + /// + /// + /// + public void SetContainerInitialLocalSource(string containerId, string sourcePath) + { + this.SetContainerState(containerId, "InitialLocalSource", sourcePath); + } + /// /// Sets the number of times to re-run the Detect phase. /// @@ -204,7 +224,6 @@ namespace WixToolsetTest.BurnE2E public void SetVerifyArguments(string verifyArguments) { this.SetBurnTestValue("VerifyArguments", verifyArguments); - } private void SetPackageState(string packageId, string name, string value) @@ -223,6 +242,38 @@ namespace WixToolsetTest.BurnE2E } } + private void SetContainerState(string containerId, string name, string value) + { + var key = String.Format(@"{0}\container\{1}", this.TestBaseRegKeyPath, containerId); + using (var containerKey = Registry.LocalMachine.CreateSubKey(key)) + { + if (String.IsNullOrEmpty(value)) + { + containerKey.DeleteValue(name, false); + } + else + { + containerKey.SetValue(name, value); + } + } + } + + private void SetPayloadState(string payloadId, string name, string value) + { + var key = String.Format(@"{0}\payload\{1}", this.TestBaseRegKeyPath, payloadId ?? String.Empty); + using (var payloadKey = Registry.LocalMachine.CreateSubKey(key)) + { + if (String.IsNullOrEmpty(value)) + { + payloadKey.DeleteValue(name, false); + } + else + { + payloadKey.SetValue(name, value); + } + } + } + public void Dispose() { Registry.LocalMachine.DeleteSubKeyTree($@"{this.BaseRegKeyPath}\{this.TestGroupName}", false); -- cgit v1.2.3-55-g6feb