未验证 提交 1bd62206 编写于 作者: E Elinor Fung 提交者: GitHub

Switch `BundleExtractToSpecificPath` and `BundleAndRun` to using built test assets (#92024)

- Make `BundleExtractToSpecificPath` and `BundleAndRun` tests use built test asset project (`HelloWorld`)
  - The `BundleExtractToSpecificPath` tests explicitly include `mockcoreclr` so that it will have native binaries to bundle
- Remove redundant `BundleAndRun.TestWith*Paths*` tests
  - These were running a self-contained (not bundled) application by invoking it using relative / absolute paths, which does not seem particularly useful. There was no difference in how they bundled the application or ran the bundled application.
- Move codesign check to `BundlerConsistencyTests` - trying to separate tests targeting the `Bundler` API from tests targeting single-file activation
上级 49a0633f
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using System.Threading;
using BundleTests.Helpers; using BundleTests.Helpers;
using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.CoreSetup.Test; using Microsoft.DotNet.CoreSetup.Test;
using Microsoft.NET.HostModel.Bundle; 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; using Xunit;
namespace AppHost.Bundle.Tests namespace AppHost.Bundle.Tests
{ {
public class BundleExtractToSpecificPath : BundleTestBase, IClassFixture<BundleExtractToSpecificPath.SharedTestState> public class BundleExtractToSpecificPath : IClassFixture<BundleExtractToSpecificPath.SharedTestState>
{ {
private SharedTestState sharedTestState; private SharedTestState sharedTestState;
...@@ -25,52 +22,48 @@ public BundleExtractToSpecificPath(SharedTestState fixture) ...@@ -25,52 +22,48 @@ public BundleExtractToSpecificPath(SharedTestState fixture)
} }
[Fact] [Fact]
private void Bundle_Extraction_To_Specific_Path_Succeeds() private void AbsolutePath()
{ {
var fixture = sharedTestState.TestFixture.Copy(); SingleFileTestApp app = sharedTestState.SelfContainedApp;
var hostName = BundleHelper.GetHostName(fixture); var bundledApp = sharedTestState.BundledApp;
// Publish the bundle
BundleOptions options = BundleOptions.BundleNativeBinaries;
Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options);
// Verify expected files in the bundle directory // Verify expected files in the bundle directory
var bundleDir = BundleHelper.GetBundleDir(fixture); var bundleDir = Directory.GetParent(bundledApp.Path);
bundleDir.Should().HaveFile(hostName); bundleDir.Should().OnlyHaveFiles(new[]
bundleDir.Should().NotHaveFiles(BundleHelper.GetBundledFiles(fixture)); {
Binaries.GetExeFileNameForCurrentPlatform(app.Name),
$"{app.Name}.pdb"
});
// Create a directory for extraction. // Directory for extraction.
var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); string extractBaseDir = app.GetNewExtractionRootPath();
extractBaseDir.Should().NotHaveDirectory(BundleHelper.GetAppBaseName(fixture));
// Run the bundled app for the first time, and extract files to // Run the bundled app for the first time, and extract files to
// $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id // $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id
Command.Create(singleFile) Command.Create(bundledApp.Path)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName) .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.And
.HaveStdOutContaining("Hello World"); var extractDir = app.GetExtractionDir(extractBaseDir, bundledApp.Manifest);
extractDir.Should().OnlyHaveFiles(BundleHelper.GetExtractedFiles(bundledApp.Manifest, bundledApp.Options));
var extractDir = BundleHelper.GetExtractionDir(fixture, bundler);
extractDir.Should().HaveFiles(BundleHelper.GetExtractedFiles(fixture, BundleOptions.BundleNativeBinaries));
extractDir.Should().NotHaveFiles(BundleHelper.GetFilesNeverExtracted(fixture));
} }
[InlineData("./foo", BundleOptions.BundleAllContent)] [InlineData("./foo", BundleOptions.BundleAllContent)]
[InlineData("../foo", BundleOptions.BundleAllContent)] [InlineData("../foo", BundleOptions.BundleAllContent)]
[InlineData("foo", BundleOptions.BundleAllContent)] [InlineData("foo", BundleOptions.BundleAllContent)]
[InlineData("foo/bar", 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", BundleOptions.BundleNativeBinaries)]
[InlineData("foo", BundleOptions.BundleNativeBinaries)] [InlineData("foo", BundleOptions.BundleNativeBinaries)]
[InlineData("foo/bar", BundleOptions.BundleNativeBinaries)] [InlineData("foo/bar", BundleOptions.BundleNativeBinaries)]
[InlineData("foo\\bar", BundleOptions.BundleNativeBinaries)] [InlineData("foo\\bar", BundleOptions.BundleNativeBinaries)]
[Theory] [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 // As we don't modify user defined environment variables, we will not convert
// any forward slashes to the standard Windows dir separator ('\'), thus // any forward slashes to the standard Windows dir separator ('\'), thus
...@@ -84,56 +77,47 @@ private void Bundle_Extraction_To_Relative_Path_Succeeds(string relativePath, Bu ...@@ -84,56 +77,47 @@ private void Bundle_Extraction_To_Relative_Path_Succeeds(string relativePath, Bu
if (relativePath == "foo\\bar" && !OperatingSystem.IsWindows()) if (relativePath == "foo\\bar" && !OperatingSystem.IsWindows())
return; return;
var fixture = sharedTestState.TestFixture.Copy(); Manifest manifest;
var bundler = BundleSelfContainedApp(fixture, out var singleFile, bundleOptions); string singleFile = sharedTestState.SelfContainedApp.Bundle(bundleOptions, out manifest);
// Run the bundled app (extract files to <path>) // Run the bundled app (extract files to <path>)
var cmd = Command.Create(singleFile); Command.Create(singleFile)
cmd.WorkingDirectory(Path.GetDirectoryName(singleFile)) .WorkingDirectory(Path.GetDirectoryName(singleFile))
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, relativePath) .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, relativePath)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.And
.HaveStdOutContaining("Hello World"); using (TestArtifact extractionRoot = new TestArtifact(Path.Combine(Path.GetDirectoryName(singleFile), relativePath)))
{
var extractedFiles = BundleHelper.GetExtractedFiles(fixture, bundleOptions); var extractedDir = sharedTestState.SelfContainedApp.GetExtractionDir(extractionRoot.Location, manifest);
var extractedDir = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(singleFile), var extractedFiles = BundleHelper.GetExtractedFiles(manifest, bundleOptions);
relativePath, extractedDir.Should().OnlyHaveFiles(extractedFiles);
fixture.TestProject.ProjectName, }
bundler.BundleManifest.BundleID));
extractedDir.Should().HaveFiles(extractedFiles);
} }
[Fact] [Fact]
private void Bundle_extraction_is_reused() private void ExtractionDirectoryReused()
{ {
var fixture = sharedTestState.TestFixture.Copy(); 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. // Directory for extraction.
var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); string extractBaseDir = app.GetNewExtractionRootPath();
// Run the bunded app for the first time, and extract files to // Run the bunded app for the first time, and extract files to
// $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id // $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id
Command.Create(singleFile) Command.Create(bundledApp.Path)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName) .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.And
.HaveStdOutContaining("Hello World");
var extractDir = BundleHelper.GetExtractionDir(fixture, bundler);
var extractDir = app.GetExtractionDir(extractBaseDir, bundledApp.Manifest);
extractDir.Refresh(); extractDir.Refresh();
DateTime firstWriteTime = extractDir.LastWriteTimeUtc; DateTime firstWriteTime = extractDir.LastWriteTimeUtc;
...@@ -143,73 +127,63 @@ private void Bundle_extraction_is_reused() ...@@ -143,73 +127,63 @@ private void Bundle_extraction_is_reused()
} }
// Run the bundled app again (reuse extracted files) // Run the bundled app again (reuse extracted files)
Command.Create(singleFile) Command.Create(bundledApp.Path)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName) .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.And
.HaveStdOutContaining("Hello World");
extractDir.Should().NotBeModifiedAfter(firstWriteTime); extractDir.Should().NotBeModifiedAfter(firstWriteTime);
} }
[Fact] [Fact]
private void Bundle_extraction_can_recover_missing_files() private void RecoverMissingFiles()
{ {
var fixture = sharedTestState.TestFixture.Copy(); SingleFileTestApp app = sharedTestState.SelfContainedApp;
var hostName = BundleHelper.GetHostName(fixture); var bundledApp = sharedTestState.BundledApp;
var appName = Path.GetFileNameWithoutExtension(hostName);
// Publish the bundle // Directory for extraction.
BundleOptions options = BundleOptions.BundleNativeBinaries; string extractBaseDir = app.GetNewExtractionRootPath();
Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options);
// Create a directory for extraction.
var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture);
// Run the bunded app for the first time, and extract files to // Run the bunded app for the first time, and extract files to
// $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id // $DOTNET_BUNDLE_EXTRACT_BASE_DIR/<app>/bundle-id
Command.Create(singleFile) Command.Create(bundledApp.Path)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName) .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.And
.HaveStdOutContaining("Hello World");
// Remove the extracted files, but keep the extraction directory // Remove the extracted files, but keep the extraction directory
var extractDir = BundleHelper.GetExtractionDir(fixture, bundler); var extractDir = app.GetExtractionDir(extractBaseDir, bundledApp.Manifest);
var extractedFiles = BundleHelper.GetExtractedFiles(fixture, BundleOptions.BundleNativeBinaries); 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().Exist();
extractDir.Should().NotHaveFiles(extractedFiles); extractDir.Should().NotHaveFiles(extractedFiles);
// Run the bundled app again (recover deleted files) // Run the bundled app again (recover deleted files)
Command.Create(singleFile) Command.Create(bundledApp.Path)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir.FullName) .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractBaseDir)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.And
.HaveStdOutContaining("Hello World");
extractDir.Should().HaveFiles(extractedFiles); extractDir.Should().OnlyHaveFiles(extractedFiles);
} }
[Fact] [Fact]
private void Bundle_extraction_to_nonexisting_default() private void NonexistentDefault_Fails()
{ {
string nonExistentPath = Path.Combine( string nonExistentPath = Path.Combine(
sharedTestState.DefaultBundledAppFixture.TestProject.OutputDirectory, Path.GetDirectoryName(sharedTestState.BundledApp.Path),
"nonexistent"); "nonexistent");
string defaultExpansionEnvVariable = OperatingSystem.IsWindows() ? "TMP" : "HOME"; string defaultExpansionEnvVariable = OperatingSystem.IsWindows() ? "TMP" : "HOME";
...@@ -217,7 +191,7 @@ private void Bundle_extraction_to_nonexisting_default() ...@@ -217,7 +191,7 @@ private void Bundle_extraction_to_nonexisting_default()
$"Failed to determine default extraction location. Check if 'TMP'" : $"Failed to determine default extraction location. Check if 'TMP'" :
$"Default extraction directory [{nonExistentPath}] either doesn't exist or is not accessible for read/write."; $"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() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable(defaultExpansionEnvVariable, nonExistentPath) .EnvironmentVariable(defaultExpansionEnvVariable, nonExistentPath)
...@@ -227,56 +201,51 @@ private void Bundle_extraction_to_nonexisting_default() ...@@ -227,56 +201,51 @@ private void Bundle_extraction_to_nonexisting_default()
[Fact] [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.")] [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"); 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)) if (string.IsNullOrEmpty(home))
{ {
// HOME is not set. Use System.Environment (which also fallsback to getpwuid)
home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
} }
DirectoryInfo sharedExtractDirInfo = BundleHelper.GetExtractionDir(sharedTestState.TestFixture, sharedTestState.DefaultBundledAppBundler); var bundledApp = sharedTestState.BundledApp;
string sharedExtractDir = sharedExtractDirInfo.FullName; Command.Create(bundledApp.Path)
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)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.EnvironmentVariable("HOME", null) .EnvironmentVariable("HOME", null)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World");
.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); 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 SingleFileTestApp SelfContainedApp { get; }
public string DefaultBundledAppExecutablePath { get; }
public Bundler DefaultBundledAppBundler { get; }
public SharedTestState() 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(); // Create a bundled app that can be used by multiple tests
DefaultBundledAppBundler = BundleSelfContainedApp(DefaultBundledAppFixture, out var singleFile, BundleOptions.BundleNativeBinaries); BundleOptions options = BundleOptions.BundleNativeBinaries;
DefaultBundledAppExecutablePath = singleFile; string bundlePath = SelfContainedApp.Bundle(options, out Manifest manifest);
BundledApp = (bundlePath, manifest, options);
} }
public void Dispose() public void Dispose()
{ {
DefaultBundledAppFixture.Dispose(); SelfContainedApp.Dispose();
TestFixture.Dispose();
} }
} }
} }
......
...@@ -52,46 +52,39 @@ public static string GetAppBaseName(TestProjectFixture fixture) ...@@ -52,46 +52,39 @@ public static string GetAppBaseName(TestProjectFixture fixture)
return Path.GetFileNameWithoutExtension(GetAppName(fixture)); return Path.GetFileNameWithoutExtension(GetAppName(fixture));
} }
public static string[] GetBundledFiles(TestProjectFixture fixture) public static List<string> GetExtractedFiles(Manifest manifest, BundleOptions bundleOptions)
{ {
string appBaseName = GetAppBaseName(fixture); List<string> expected = new List<string>();
return new string[] { $"{appBaseName}.dll", $"{appBaseName}.deps.json", $"{appBaseName}.runtimeconfig.json" }; foreach (FileEntry file in manifest.Files)
}
public static string[] GetExtractedFiles(TestProjectFixture fixture, BundleOptions bundleOptions)
{
switch (bundleOptions & ~BundleOptions.EnableCompression)
{ {
case BundleOptions.None: if (!ShouldBeExtracted(file.Type, bundleOptions))
case BundleOptions.BundleOtherFiles: continue;
case BundleOptions.BundleSymbolFiles:
throw new ArgumentException($"Bundle option {bundleOptions} doesn't extract any files to disk.");
case BundleOptions.BundleAllContent: expected.Add(file.RelativePath);
return Directory.GetFiles(GetPublishPath(fixture)) }
.Select(f => Path.GetFileName(f))
.Except(GetFilesNeverExtracted(fixture)).ToArray();
case BundleOptions.BundleNativeBinaries: return expected;
return new string[] { Path.GetFileName(fixture.TestProject.CoreClrDll) };
default: static bool ShouldBeExtracted(FileType type, BundleOptions options)
throw new ArgumentException("Unsupported bundle option."); {
switch (type)
{
case FileType.Assembly:
case FileType.DepsJson:
case FileType.RuntimeConfigJson:
return options.HasFlag(BundleOptions.BundleAllContent);
case FileType.NativeBinary:
return options.HasFlag(BundleOptions.BundleNativeBinaries);
case FileType.Symbols:
return options.HasFlag(BundleOptions.BundleSymbolFiles);
case FileType.Unknown:
return options.HasFlag(BundleOptions.BundleOtherFiles);
default:
return false;
}
} }
} }
public static string[] GetFilesNeverExtracted(TestProjectFixture fixture)
{
string appBaseName = GetAppBaseName(fixture);
return new string[] { $"{appBaseName}",
$"{appBaseName}.dll",
$"{appBaseName}.exe",
$"{appBaseName}.pdb",
$"{appBaseName}.runtimeconfig.dev.json",
Path.GetFileName(fixture.TestProject.HostFxrDll),
Path.GetFileName(fixture.TestProject.HostPolicyDll) };
}
public static string GetPublishPath(TestProjectFixture fixture) public static string GetPublishPath(TestProjectFixture fixture)
{ {
return Path.Combine(fixture.TestProject.ProjectDirectory, "publish"); return Path.Combine(fixture.TestProject.ProjectDirectory, "publish");
......
...@@ -21,33 +21,21 @@ public BundleAndRun(BundleAndRun.SharedTestState fixture) ...@@ -21,33 +21,21 @@ public BundleAndRun(BundleAndRun.SharedTestState fixture)
sharedTestState = fixture; sharedTestState = fixture;
} }
private void RunTheApp(string path) private void RunTheApp(string path, bool selfContained)
{ {
Command.Create(path) Command.Create(path)
.CaptureStdErr() .CaptureStdErr()
.CaptureStdOut() .CaptureStdOut()
.DotNetRoot(selfContained ? null : RepoDirectoriesProvider.Default.BuiltDotnet)
.Execute() .Execute()
.Should() .Should().Pass()
.Pass() .And.HaveStdOutContaining("Hello World!");
.And
.HaveStdOutContaining("Hello World!");
}
private void CheckFileSigned(string path)
{
// Check if the file is signed (it should have been signed by the bundler)
Command.Create("codesign", $"-v {path}")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should()
.Pass();
} }
private string MakeUniversalBinary(string path, string rid) private string MakeUniversalBinary(string path, Architecture architecture)
{ {
string fatApp = path + ".fat"; string fatApp = path + ".fat";
string arch = BundleHelper.GetTargetArch(rid) == Architecture.Arm64 ? "arm64" : "x86_64"; string arch = architecture == Architecture.Arm64 ? "arm64" : "x86_64";
// We will create a universal binary with just one arch slice and run it. // We will create a universal binary with just one arch slice and run it.
// It is enough for testing purposes. The code that finds the releavant slice // It is enough for testing purposes. The code that finds the releavant slice
...@@ -62,70 +50,36 @@ private string MakeUniversalBinary(string path, string rid) ...@@ -62,70 +50,36 @@ private string MakeUniversalBinary(string path, string rid)
return fatApp; return fatApp;
} }
private void BundleRun(TestProjectFixture fixture, string publishPath) [Theory]
[InlineData(true)]
[InlineData(false)]
private void RunApp(bool selfContained)
{ {
var hostName = BundleHelper.GetHostName(fixture);
// Run the App normally
RunTheApp(Path.Combine(publishPath, hostName));
// Bundle to a single-file // Bundle to a single-file
string singleFile = BundleHelper.BundleApp(fixture); string singleFile = selfContained
? sharedTestState.SelfContainedApp.Bundle()
: sharedTestState.FrameworkDependentApp.Bundle();
// check that the file structure is understood by codesign // Run the bundled app
var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); RunTheApp(singleFile, selfContained);
if (targetOS == OSPlatform.OSX)
{
CheckFileSigned(singleFile);
}
// Run the extracted app if (OperatingSystem.IsMacOS())
RunTheApp(singleFile);
if (targetOS == OSPlatform.OSX)
{ {
string fatApp = MakeUniversalBinary(singleFile, fixture.CurrentRid); string fatApp = MakeUniversalBinary(singleFile, RuntimeInformation.OSArchitecture);
// Run the fat app // Run the fat app
RunTheApp(fatApp); RunTheApp(fatApp, selfContained);
} }
} }
private string RelativePath(string path) [Theory]
[InlineData(true)]
[InlineData(false)]
public void AdditionalContentAfterBundleMetadata(bool selfContained)
{ {
return Path.GetRelativePath(Directory.GetCurrentDirectory(), path) string singleFile = selfContained
.TrimEnd(Path.DirectorySeparatorChar); ? sharedTestState.SelfContainedApp.Bundle()
} : sharedTestState.FrameworkDependentApp.Bundle();
[Fact]
public void TestWithAbsolutePaths()
{
var fixture = sharedTestState.TestFixture.Copy();
string publishDir = BundleHelper.GetPublishPath(fixture);
BundleRun(fixture, publishDir);
}
[Fact]
public void TestWithRelativePaths()
{
var fixture = sharedTestState.TestFixture.Copy();
string publishDir = RelativePath(BundleHelper.GetPublishPath(fixture));
BundleRun(fixture, publishDir);
}
[Fact]
public void TestWithRelativePathsDirSeparator()
{
var fixture = sharedTestState.TestFixture.Copy();
string publishDir = RelativePath(BundleHelper.GetPublishPath(fixture)) + Path.DirectorySeparatorChar;
BundleRun(fixture, publishDir);
}
[Fact]
public void TestWithAdditionalContentAfterBundleMetadata()
{
var fixture = sharedTestState.TestFixture.Copy();
string singleFile = BundleHelper.BundleApp(fixture);
using (var file = File.OpenWrite(singleFile)) using (var file = File.OpenWrite(singleFile))
{ {
...@@ -134,35 +88,24 @@ public void TestWithAdditionalContentAfterBundleMetadata() ...@@ -134,35 +88,24 @@ public void TestWithAdditionalContentAfterBundleMetadata()
file.Write(blob, 0, blob.Length); file.Write(blob, 0, blob.Length);
} }
Command.Create(singleFile) RunTheApp(singleFile, selfContained);
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World!");
} }
public class SharedTestState : IDisposable public class SharedTestState : IDisposable
{ {
public TestProjectFixture TestFixture { get; set; } public SingleFileTestApp FrameworkDependentApp { get; }
public TestProjectFixture LegacyFixture { get; set; } public SingleFileTestApp SelfContainedApp { get; }
public RepoDirectoriesProvider RepoDirectories { get; set; }
public SharedTestState() public SharedTestState()
{ {
RepoDirectories = new RepoDirectoriesProvider(); FrameworkDependentApp = SingleFileTestApp.CreateFrameworkDependent("HelloWorld");
SelfContainedApp = SingleFileTestApp.CreateSelfContained("HelloWorld");
TestFixture = new TestProjectFixture("StandaloneApp", RepoDirectories);
TestFixture
.EnsureRestoredForRid(TestFixture.CurrentRid)
.PublishProject(runtime: TestFixture.CurrentRid,
selfContained: true,
outputDirectory: BundleHelper.GetPublishPath(TestFixture));
} }
public void Dispose() public void Dispose()
{ {
TestFixture.Dispose(); FrameworkDependentApp.Dispose();
SelfContainedApp.Dispose();
} }
} }
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FluentAssertions; using FluentAssertions;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.CoreSetup.Test; using Microsoft.DotNet.CoreSetup.Test;
using Microsoft.NET.HostModel.Bundle; using Microsoft.NET.HostModel.Bundle;
using Xunit; using Xunit;
...@@ -23,8 +24,8 @@ public BundlerConsistencyTests(SharedTestState fixture) ...@@ -23,8 +24,8 @@ public BundlerConsistencyTests(SharedTestState fixture)
} }
private static string BundlerHostName = Binaries.GetExeFileNameForCurrentPlatform(SharedTestState.AppName); private static string BundlerHostName = Binaries.GetExeFileNameForCurrentPlatform(SharedTestState.AppName);
private Bundler CreateBundlerInstance(BundleOptions bundleOptions = BundleOptions.None, Version version = null) private Bundler CreateBundlerInstance(BundleOptions bundleOptions = BundleOptions.None, Version version = null, bool macosCodesign = true)
=> new Bundler(BundlerHostName, SharedFramework.CalculateUniqueTestDirectory($"{sharedTestState.App.Location}-bundle"), bundleOptions, targetFrameworkVersion: version); => new Bundler(BundlerHostName, SharedFramework.CalculateUniqueTestDirectory($"{sharedTestState.App.Location}-bundle"), bundleOptions, targetFrameworkVersion: version, macosCodesign: macosCodesign);
[Fact] [Fact]
public void EnableCompression_Before60_Fails() public void EnableCompression_Before60_Fails()
...@@ -309,6 +310,40 @@ public void AssemblyAlignment() ...@@ -309,6 +310,40 @@ public void AssemblyAlignment()
Assert.True((file.Type != FileType.Assembly) || (file.Offset % alignment == 0))); Assert.True((file.Type != FileType.Assembly) || (file.Offset % alignment == 0)));
} }
[Theory]
[InlineData(true)]
[InlineData(false)]
[PlatformSpecific(TestPlatforms.OSX)]
public void Codesign(bool shouldCodesign)
{
TestApp app = sharedTestState.App;
FileSpec[] fileSpecs = new FileSpec[]
{
new FileSpec(Binaries.AppHost.FilePath, BundlerHostName),
new FileSpec(app.AppDll, Path.GetRelativePath(app.Location, app.AppDll)),
new FileSpec(app.DepsJson, Path.GetRelativePath(app.Location, app.DepsJson)),
new FileSpec(app.RuntimeConfigJson, Path.GetRelativePath(app.Location, app.RuntimeConfigJson)),
};
Bundler bundler = CreateBundlerInstance(macosCodesign: shouldCodesign);
string bundledApp = bundler.GenerateBundle(fileSpecs);
// Check if the file is signed
CommandResult result = Command.Create("codesign", $"-v {bundledApp}")
.CaptureStdErr()
.CaptureStdOut()
.Execute(expectedToFail: !shouldCodesign);
if (shouldCodesign)
{
result.Should().Pass();
}
else
{
result.Should().Fail();
}
}
public class SharedTestState : IDisposable public class SharedTestState : IDisposable
{ {
public const string AppName = "HelloWorld"; public const string AppName = "HelloWorld";
......
...@@ -73,7 +73,12 @@ public static IReadOnlyList<FileSpec> GetRuntimeFilesToBundle() ...@@ -73,7 +73,12 @@ public static IReadOnlyList<FileSpec> GetRuntimeFilesToBundle()
return fileSpecs; return fileSpecs;
} }
public string Bundle(BundleOptions options, Version? bundleVersion = null) public string Bundle(BundleOptions options = BundleOptions.None, Version? bundleVersion = null)
{
return Bundle(options, out _, bundleVersion);
}
public string Bundle(BundleOptions options, out Manifest manifest, Version? bundleVersion = null)
{ {
string bundleDirectory = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(Location, "bundle")); string bundleDirectory = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(Location, "bundle"));
var bundler = new Bundler( var bundler = new Bundler(
...@@ -113,9 +118,20 @@ public string Bundle(BundleOptions options, Version? bundleVersion = null) ...@@ -113,9 +118,20 @@ public string Bundle(BundleOptions options, Version? bundleVersion = null)
File.Copy(spec.SourcePath, outputFilePath, true); File.Copy(spec.SourcePath, outputFilePath, true);
} }
manifest = bundler.BundleManifest;
return singleFile; return singleFile;
} }
public string GetNewExtractionRootPath()
{
return SharedFramework.CalculateUniqueTestDirectory(Path.Combine(Location, "extract"));
}
public DirectoryInfo GetExtractionDir(string root, Manifest manifest)
{
return new DirectoryInfo(Path.Combine(root, Name, manifest.BundleID));
}
private void PopulateBuiltAppDirectory() private void PopulateBuiltAppDirectory()
{ {
// Copy the compiled app output - the app is expected to have been built as framework-dependent // Copy the compiled app output - the app is expected to have been built as framework-dependent
......
...@@ -63,7 +63,7 @@ pal::string_t& extractor_t::extraction_dir() ...@@ -63,7 +63,7 @@ pal::string_t& extractor_t::extraction_dir()
append_path(&m_extraction_dir, host_name.c_str()); append_path(&m_extraction_dir, host_name.c_str());
append_path(&m_extraction_dir, m_bundle_id.c_str()); append_path(&m_extraction_dir, m_bundle_id.c_str());
trace::info(_X("Files embedded within the bundled will be extracted to [%s] directory."), m_extraction_dir.c_str()); trace::info(_X("Files embedded within the bundle will be extracted to [%s] directory."), m_extraction_dir.c_str());
} }
return m_extraction_dir; return m_extraction_dir;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册