// 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. namespace WixToolsetTest.BurnE2E { using System; using System.Collections.Generic; using System.IO; using WixBuildTools.TestSupport; using WixTestTools; using WixToolset.Mba.Core; using Xunit; using Xunit.Abstractions; public class CacheTests : BurnE2ETests { public CacheTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } private bool Is5GBFileAvailable() { // Recreate the 5GB payload to avoid having to copy it to the VM to run the tests. const long FiveGB = 5_368_709_120; const long OneGB = 1_073_741_824; var targetFilePath = Path.Combine(this.TestContext.TestDataFolder, "fivegb.file"); // If the drive has less than 5GB (for the test file) plus 1GB (for working space), then // skip the test. var drive = new DriveInfo(targetFilePath.Substring(0, 1)); if (drive.AvailableFreeSpace < FiveGB + OneGB) { Console.WriteLine($"Skipping {this.TestContext.TestName} because there is not enough disk space available to run the test."); return false; } if (!File.Exists(targetFilePath)) { var testTool = new TestTool(Path.Combine(TestData.Get(), "win-x86", "TestExe.exe")) { Arguments = "/lf \"" + targetFilePath + $"|{FiveGB}\"", ExpectedExitCode = 0, }; testTool.Run(true); } return true; } [Fact] public void CanCache5GBFile() { if (!this.Is5GBFileAvailable()) { return; } var packageA = this.CreatePackageInstaller("PackageA"); var bundleC = this.CreateBundleInstaller("BundleC"); packageA.VerifyInstalled(false); bundleC.Install(); bundleC.VerifyRegisteredAndInPackageCache(); packageA.VerifyInstalled(true); } private string Cache5GBFileFromDownload(bool disableRangeRequests) { if (!this.Is5GBFileAvailable()) { return null; } var packageA = this.CreatePackageInstaller("PackageA"); var bundleC = this.CreateBundleInstaller("BundleC"); var webServer = this.CreateWebServer(); webServer.AddFiles(new Dictionary { { "/BundleC/fivegb.file", Path.Combine(this.TestContext.TestDataFolder, "fivegb.file") }, { "/BundleC/PackageA.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageA.msi") }, }); webServer.DisableRangeRequests = disableRangeRequests; webServer.Start(); using var dfs = new DisposableFileSystem(); var separateDirectory = dfs.GetFolder(true); // Manually copy bundle to separate directory and then run from there so the non-compressed payloads have to be resolved. var bundleCFileInfo = new FileInfo(bundleC.Bundle); var bundleCCopiedPath = Path.Combine(separateDirectory, bundleCFileInfo.Name); bundleCFileInfo.CopyTo(bundleCCopiedPath); packageA.VerifyInstalled(false); var installLogPath = bundleC.Install(bundleCCopiedPath); bundleC.VerifyRegisteredAndInPackageCache(); packageA.VerifyInstalled(true); return installLogPath; } [Fact] public void CanCache5GBFileFromDownloadWithRangeRequestSupport() { var logPath = this.Cache5GBFileFromDownload(false); if (logPath == null) { return; } Assert.False(LogVerifier.MessageInLogFile(logPath, "Range request not supported for URL: http://localhost:9999/e2e/BundleC/fivegb.file")); Assert.False(LogVerifier.MessageInLogFile(logPath, "Content-Length not returned for URL: http://localhost:9999/e2e/BundleC/fivegb.file")); } [Fact] public void CanCache5GBFileFromDownloadWithoutRangeRequestSupport() { var logPath = this.Cache5GBFileFromDownload(true); if (logPath == null) { return; } Assert.True(LogVerifier.MessageInLogFile(logPath, "Range request not supported for URL: http://localhost:9999/e2e/BundleC/fivegb.file")); Assert.False(LogVerifier.MessageInLogFile(logPath, "Content-Length not returned for URL: http://localhost:9999/e2e/BundleC/fivegb.file")); } [Fact] public void CanDownloadPayloadsFromMissingAttachedContainer() { var packageA = this.CreatePackageInstaller("PackageA"); var packageB = this.CreatePackageInstaller("PackageB"); var bundleA = this.CreateBundleInstaller("BundleA"); var testBAController = this.CreateTestBAController(); var webServer = this.CreateWebServer(); webServer.AddFiles(new Dictionary { { "/BundleA/PackageA.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageA.msi") }, { "/BundleA/PackageB.msi", Path.Combine(this.TestContext.TestDataFolder, "PackageB.msi") }, }); webServer.DisableHeadResponses = true; webServer.Start(); // Don't install PackageB initially so it will be installed when run from the package cache. testBAController.SetPackageRequestedState("PackageB", RequestState.Absent); packageA.VerifyInstalled(false); packageB.VerifyInstalled(false); // Manually copy bundle to separate directory, install from there, and then delete it // so that when run from the package cache, it can't find the attached container. using (var dfs = new DisposableFileSystem()) { var tempDirectory = dfs.GetFolder(true); var bundleAFileInfo = new FileInfo(bundleA.Bundle); var bundleACopiedPath = Path.Combine(tempDirectory, bundleAFileInfo.Name); bundleAFileInfo.CopyTo(bundleACopiedPath); bundleA.Install(bundleACopiedPath); } var bundlePackageCachePath = bundleA.VerifyRegisteredAndInPackageCache(); packageA.VerifyInstalled(true); packageB.VerifyInstalled(false); testBAController.SetPackageRequestedState("PackageB", RequestState.Present); var modifyLogPath = bundleA.Modify(bundlePackageCachePath); bundleA.VerifyRegisteredAndInPackageCache(); packageA.VerifyInstalled(true); packageB.VerifyInstalled(true); Assert.True(LogVerifier.MessageInLogFile(modifyLogPath, "Ignoring failure to get size and time for URL: http://localhost:9999/e2e/BundleA/PackageB.msi (error 0x80070002)")); } [Fact] public void CanFindAttachedContainerFromRenamedBundle() { var packageA = this.CreatePackageInstaller("PackageA"); var packageB = this.CreatePackageInstaller("PackageB"); var bundleB = this.CreateBundleInstaller("BundleB"); var testBAController = this.CreateTestBAController(); // Don't install PackageB initially so it will be installed when run from the package cache. testBAController.SetPackageRequestedState("PackageB", RequestState.Absent); packageA.VerifyInstalled(false); packageB.VerifyInstalled(false); // Manually copy bundle to separate directory with new name and install from there // so that when run from the package cache, it has to get the attached container from the renamed bundle. using (var dfs = new DisposableFileSystem()) { var tempDirectory = dfs.GetFolder(true); var bundleBFileInfo = new FileInfo(bundleB.Bundle); var bundleBCopiedPath = Path.Combine(tempDirectory, "RenamedBundle.exe"); bundleBFileInfo.CopyTo(bundleBCopiedPath); bundleB.Install(bundleBCopiedPath); var bundlePackageCachePath = bundleB.VerifyRegisteredAndInPackageCache(); packageA.VerifyInstalled(true); packageB.VerifyInstalled(false); testBAController.SetPackageRequestedState("PackageB", RequestState.Present); bundleB.Modify(bundlePackageCachePath); bundleB.VerifyRegisteredAndInPackageCache(); packageA.VerifyInstalled(true); packageB.VerifyInstalled(true); } } } }