Skip to content

Switch BundleExtractToSpecificPath and BundleAndRun to using built test assets #92024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using System.Threading;
using BundleTests.Helpers;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.CoreSetup.Test;
using Microsoft.NET.HostModel.Bundle;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Xunit;

namespace AppHost.Bundle.Tests
{
public class BundleExtractToSpecificPath : BundleTestBase, IClassFixture<BundleExtractToSpecificPath.SharedTestState>
public class BundleExtractToSpecificPath : IClassFixture<BundleExtractToSpecificPath.SharedTestState>
{
private SharedTestState sharedTestState;

Expand All @@ -25,52 +22,48 @@ public BundleExtractToSpecificPath(SharedTestState fixture)
}

[Fact]
private void Bundle_Extraction_To_Specific_Path_Succeeds()
private void AbsolutePath()
{
var fixture = sharedTestState.TestFixture.Copy();
var hostName = BundleHelper.GetHostName(fixture);

// Publish the bundle
BundleOptions options = BundleOptions.BundleNativeBinaries;
Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options);
SingleFileTestApp app = sharedTestState.SelfContainedApp;
var bundledApp = sharedTestState.BundledApp;

// Verify expected files in the bundle directory
var bundleDir = BundleHelper.GetBundleDir(fixture);
bundleDir.Should().HaveFile(hostName);
bundleDir.Should().NotHaveFiles(BundleHelper.GetBundledFiles(fixture));
var bundleDir = Directory.GetParent(bundledApp.Path);
bundleDir.Should().OnlyHaveFiles(new[]
{
Binaries.GetExeFileNameForCurrentPlatform(app.Name),
$"{app.Name}.pdb"
});

// Create a directory for extraction.
var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture);
extractBaseDir.Should().NotHaveDirectory(BundleHelper.GetAppBaseName(fixture));
// Directory for extraction.
string extractBaseDir = app.GetNewExtractionRootPath();

// Run the bundled app for the first time, and extract files to
// $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id
Command.Create(singleFile)
Command.Create(bundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName)
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");

var extractDir = BundleHelper.GetExtractionDir(fixture, bundler);
extractDir.Should().HaveFiles(BundleHelper.GetExtractedFiles(fixture, BundleOptions.BundleNativeBinaries));
extractDir.Should().NotHaveFiles(BundleHelper.GetFilesNeverExtracted(fixture));
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

var extractDir = app.GetExtractionDir(extractBaseDir, bundledApp.Manifest);
extractDir.Should().OnlyHaveFiles(BundleHelper.GetExtractedFiles(bundledApp.Manifest, bundledApp.Options));
}

[InlineData("./foo", BundleOptions.BundleAllContent)]
[InlineData("../foo", BundleOptions.BundleAllContent)]
[InlineData("foo", BundleOptions.BundleAllContent)]
[InlineData("foo/bar", BundleOptions.BundleAllContent)]
[InlineData("foo\\bar", BundleOptions.BundleAllContent)]
[InlineData("./foo", BundleOptions.BundleNativeBinaries)]
[InlineData("../foo", BundleOptions.BundleNativeBinaries)]
[InlineData("foo", BundleOptions.BundleNativeBinaries)]
[InlineData("foo/bar", BundleOptions.BundleNativeBinaries)]
[InlineData("foo\\bar", BundleOptions.BundleNativeBinaries)]
[Theory]
private void Bundle_Extraction_To_Relative_Path_Succeeds(string relativePath, BundleOptions bundleOptions)
private void RelativePath(string relativePath, BundleOptions bundleOptions)
{
// As we don't modify user defined environment variables, we will not convert
// any forward slashes to the standard Windows dir separator ('\'), thus
Expand All @@ -84,56 +77,47 @@ private void Bundle_Extraction_To_Relative_Path_Succeeds(string relativePath, Bu
if (relativePath == "foo\\bar" && !OperatingSystem.IsWindows())
return;

var fixture = sharedTestState.TestFixture.Copy();
var bundler = BundleSelfContainedApp(fixture, out var singleFile, bundleOptions);
Manifest manifest;
string singleFile = sharedTestState.SelfContainedApp.Bundle(bundleOptions, out manifest);

// Run the bundled app (extract files to <path>)
var cmd = Command.Create(singleFile);
cmd.WorkingDirectory(Path.GetDirectoryName(singleFile))
Command.Create(singleFile)
.WorkingDirectory(Path.GetDirectoryName(singleFile))
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, relativePath)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");

var extractedFiles = BundleHelper.GetExtractedFiles(fixture, bundleOptions);
var extractedDir = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(singleFile),
relativePath,
fixture.TestProject.ProjectName,
bundler.BundleManifest.BundleID));

extractedDir.Should().HaveFiles(extractedFiles);
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

using (TestArtifact extractionRoot = new TestArtifact(Path.Combine(Path.GetDirectoryName(singleFile), relativePath)))
{
var extractedDir = sharedTestState.SelfContainedApp.GetExtractionDir(extractionRoot.Location, manifest);
var extractedFiles = BundleHelper.GetExtractedFiles(manifest, bundleOptions);
extractedDir.Should().OnlyHaveFiles(extractedFiles);
}
}

[Fact]
private void Bundle_extraction_is_reused()
private void ExtractionDirectoryReused()
{
var fixture = sharedTestState.TestFixture.Copy();

// Publish the bundle
BundleOptions options = BundleOptions.BundleNativeBinaries;
Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options);
SingleFileTestApp app = sharedTestState.SelfContainedApp;
var bundledApp = sharedTestState.BundledApp;

// Create a directory for extraction.
var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture);
// Directory for extraction.
string extractBaseDir = app.GetNewExtractionRootPath();

// Run the bunded app for the first time, and extract files to
// $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id
Command.Create(singleFile)
Command.Create(bundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName)
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");

var extractDir = BundleHelper.GetExtractionDir(fixture, bundler);
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

var extractDir = app.GetExtractionDir(extractBaseDir, bundledApp.Manifest);
extractDir.Refresh();
DateTime firstWriteTime = extractDir.LastWriteTimeUtc;

Expand All @@ -143,81 +127,71 @@ private void Bundle_extraction_is_reused()
}

// Run the bundled app again (reuse extracted files)
Command.Create(singleFile)
Command.Create(bundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName)
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

extractDir.Should().NotBeModifiedAfter(firstWriteTime);
}

[Fact]
private void Bundle_extraction_can_recover_missing_files()
private void RecoverMissingFiles()
{
var fixture = sharedTestState.TestFixture.Copy();
var hostName = BundleHelper.GetHostName(fixture);
var appName = Path.GetFileNameWithoutExtension(hostName);
SingleFileTestApp app = sharedTestState.SelfContainedApp;
var bundledApp = sharedTestState.BundledApp;

// Publish the bundle
BundleOptions options = BundleOptions.BundleNativeBinaries;
Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options);

// Create a directory for extraction.
var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture);
// Directory for extraction.
string extractBaseDir = app.GetNewExtractionRootPath();

// Run the bunded app for the first time, and extract files to
// $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id
Command.Create(singleFile)
Command.Create(bundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName)
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

// Remove the extracted files, but keep the extraction directory
var extractDir = BundleHelper.GetExtractionDir(fixture, bundler);
var extractedFiles = BundleHelper.GetExtractedFiles(fixture, BundleOptions.BundleNativeBinaries);
var extractDir = app.GetExtractionDir(extractBaseDir, bundledApp.Manifest);
var extractedFiles = BundleHelper.GetExtractedFiles(bundledApp.Manifest, bundledApp.Options);

Array.ForEach(extractedFiles, file => File.Delete(Path.Combine(extractDir.FullName, file)));
foreach (string file in extractedFiles)
File.Delete(Path.Combine(extractDir.FullName, file));

extractDir.Should().Exist();
extractDir.Should().NotHaveFiles(extractedFiles);

// Run the bundled app again (recover deleted files)
Command.Create(singleFile)
Command.Create(bundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName)
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

extractDir.Should().HaveFiles(extractedFiles);
extractDir.Should().OnlyHaveFiles(extractedFiles);
}

[Fact]
private void Bundle_extraction_to_nonexisting_default()
private void NonexistentDefault_Fails()
{
string nonExistentPath = Path.Combine(
sharedTestState.DefaultBundledAppFixture.TestProject.OutputDirectory,
Path.GetDirectoryName(sharedTestState.BundledApp.Path),
"nonexistent");

string defaultExpansionEnvVariable = OperatingSystem.IsWindows() ? "TMP" : "HOME";
string expectedErrorMessagePart = OperatingSystem.IsWindows() ?
$"Failed to determine default extraction location. Check if 'TMP'" :
$"Default extraction directory [{nonExistentPath}] either doesn't exist or is not accessible for read/write.";

Command.Create(sharedTestState.DefaultBundledAppExecutablePath)
Command.Create(sharedTestState.BundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(defaultExpansionEnvVariable, nonExistentPath)
Expand All @@ -227,56 +201,51 @@ private void Bundle_extraction_to_nonexisting_default()

[Fact]
[SkipOnPlatform(TestPlatforms.Windows, "On Windows the default extraction path is determined by calling GetTempPath which looks at multiple places and can't really be undefined.")]
private void Bundle_extraction_fallsback_to_getpwuid_when_HOME_env_var_is_undefined()
private void UndefinedHOME_getpwuidFallback()
{
string home = Environment.GetEnvironmentVariable("HOME");
// suppose we are testing on a system where HOME is not set, use System.Environment (which also fallsback to getpwuid)
if (string.IsNullOrEmpty(home))
{
// HOME is not set. Use System.Environment (which also fallsback to getpwuid)
home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
}

DirectoryInfo sharedExtractDirInfo = BundleHelper.GetExtractionDir(sharedTestState.TestFixture, sharedTestState.DefaultBundledAppBundler);
string sharedExtractDir = sharedExtractDirInfo.FullName;
string extractDirSubPath = sharedExtractDir.Substring(sharedExtractDir.LastIndexOf("extract/") + "extract/".Length);
string realExtractDir = Path.Combine(home, ".net", extractDirSubPath);
var expectedExtractDir = new DirectoryInfo(realExtractDir);

Command.Create(sharedTestState.DefaultBundledAppExecutablePath)
var bundledApp = sharedTestState.BundledApp;
Command.Create(bundledApp.Path)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable("HOME", null)
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello World");
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

var extractedFiles = BundleHelper.GetExtractedFiles(sharedTestState.TestFixture, BundleOptions.BundleNativeBinaries);
DirectoryInfo expectedExtractDir = sharedTestState.SelfContainedApp.GetExtractionDir(Path.Combine(home, ".net"), bundledApp.Manifest);
var extractedFiles = BundleHelper.GetExtractedFiles(bundledApp.Manifest, bundledApp.Options);
expectedExtractDir.Should().HaveFiles(extractedFiles);
}

public class SharedTestState : SharedTestStateBase, IDisposable
public class SharedTestState : IDisposable
{
public TestProjectFixture TestFixture { get; }
public (string Path, Manifest Manifest, BundleOptions Options) BundledApp{ get; }

public TestProjectFixture DefaultBundledAppFixture { get; }
public string DefaultBundledAppExecutablePath { get; }
public Bundler DefaultBundledAppBundler { get; }
public SingleFileTestApp SelfContainedApp { get; }

public SharedTestState()
{
TestFixture = PreparePublishedSelfContainedTestProject("StandaloneApp");
SelfContainedApp = SingleFileTestApp.CreateSelfContained("HelloWorld");

// Copy over mockcoreclr so that the app will have a native binary
File.Copy(Binaries.CoreClr.MockPath, Path.Combine(SelfContainedApp.NonBundledLocation, Binaries.CoreClr.MockName));

DefaultBundledAppFixture = TestFixture.Copy();
DefaultBundledAppBundler = BundleSelfContainedApp(DefaultBundledAppFixture, out var singleFile, BundleOptions.BundleNativeBinaries);
DefaultBundledAppExecutablePath = singleFile;
// Create a bundled app that can be used by multiple tests
BundleOptions options = BundleOptions.BundleNativeBinaries;
string bundlePath = SelfContainedApp.Bundle(options, out Manifest manifest);
BundledApp = (bundlePath, manifest, options);
}

public void Dispose()
{
DefaultBundledAppFixture.Dispose();
TestFixture.Dispose();
SelfContainedApp.Dispose();
}
}
}
Expand Down
Loading