diff --git a/eng/Subsets.props b/eng/Subsets.props index 60c14971d5bdb..ad61be8b48102 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -489,6 +489,7 @@ + diff --git a/src/installer/tests/Assets/Projects/HelloWorld/HelloWorld.csproj b/src/installer/tests/Assets/Projects/HelloWorld/HelloWorld.csproj new file mode 100644 index 0000000000000..e71444bd29bbf --- /dev/null +++ b/src/installer/tests/Assets/Projects/HelloWorld/HelloWorld.csproj @@ -0,0 +1,8 @@ + + + + $(NetCoreAppCurrent) + Exe + + + diff --git a/src/installer/tests/Assets/Projects/HelloWorld/Program.cs b/src/installer/tests/Assets/Projects/HelloWorld/Program.cs new file mode 100644 index 0000000000000..b206495c013c9 --- /dev/null +++ b/src/installer/tests/Assets/Projects/HelloWorld/Program.cs @@ -0,0 +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.Runtime.InteropServices; + +namespace HelloWorld +{ + public static class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + Console.WriteLine(string.Join(Environment.NewLine, args)); + Console.WriteLine(RuntimeInformation.FrameworkDescription); + } + } +} diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs index fb290084af26c..7e062c658a98a 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundleAndRun.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.CoreSetup.Test; using BundleTests.Helpers; using System.Runtime.InteropServices; +using System.Text; namespace Microsoft.NET.HostModel.Tests { @@ -120,6 +121,27 @@ public void TestWithRelativePathsDirSeparator() BundleRun(fixture, publishDir); } + [Fact] + public void TestWithAdditionalContentAfterBundleMetadata() + { + var fixture = sharedTestState.TestFixture.Copy(); + string singleFile = BundleHelper.BundleApp(fixture); + + using (var file = File.OpenWrite(singleFile)) + { + file.Position = file.Length; + var blob = Encoding.UTF8.GetBytes("Mock signature at the end of the bundle"); + file.Write(blob, 0, blob.Length); + } + + Command.Create(singleFile) + .CaptureStdErr() + .CaptureStdOut() + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello World!"); + } + public class SharedTestState : IDisposable { public TestProjectFixture TestFixture { get; set; } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs index c2df90c84c186..7a9129da2bf08 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs @@ -1,17 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using BundleTests.Helpers; -using FluentAssertions; -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.Text; +using FluentAssertions; +using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.NET.HostModel.Bundle; using Xunit; namespace Microsoft.NET.HostModel.Tests @@ -25,80 +22,69 @@ public BundlerConsistencyTests(SharedTestState fixture) sharedTestState = fixture; } + private static string BundlerHostName = Binaries.GetExeFileNameForCurrentPlatform(SharedTestState.AppName); + private Bundler CreateBundlerInstance(BundleOptions bundleOptions = BundleOptions.None, Version version = null) + => new Bundler(BundlerHostName, SharedFramework.CalculateUniqueTestDirectory($"{sharedTestState.App.Location}-bundle"), bundleOptions, targetFrameworkVersion: version); + [Fact] public void EnableCompression_Before60_Fails() { // compression must be off when targeting pre-6.0 Assert.Throws(() => - new Bundler("appName", RepoDirectoriesProvider.Default.TestAssetsOutput, BundleOptions.EnableCompression, targetFrameworkVersion: new Version(5, 0))); + CreateBundlerInstance(BundleOptions.EnableCompression, new Version(5, 0))); } - [Fact] - public void TestWithEmptySpecFails() + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void InvalidFileSpec_Fails(string invalidSpecPath) { - var fixture = sharedTestState.TestFixture.Copy(); + FileSpec invalidSourcePath = new FileSpec(invalidSpecPath, BundlerHostName); + Assert.False(invalidSourcePath.IsValid()); - var hostName = BundleHelper.GetHostName(fixture); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); - - FileSpec[][] invalidSpecs = - { - new FileSpec[] {new FileSpec(hostName, null) }, - new FileSpec[] {new FileSpec(hostName, "") }, - new FileSpec[] {new FileSpec(hostName, " ") } - }; + FileSpec invalidBundlePath = new FileSpec(BundlerHostName, invalidSpecPath); + Assert.False(invalidBundlePath.IsValid()); - foreach (var invalidSpec in invalidSpecs) - { - Assert.Throws(() => bundler.GenerateBundle(invalidSpec)); - } + Bundler bundler = CreateBundlerInstance(); + Assert.Throws(() => bundler.GenerateBundle(new[] { invalidSourcePath })); + Assert.Throws(() => bundler.GenerateBundle(new[] { invalidBundlePath })); } [Fact] - public void TestWithoutSpecifyingHostFails() + public void NoHostInFileSpecs_Fails() { - var fixture = sharedTestState.TestFixture.Copy(); + var appName = Path.GetFileNameWithoutExtension(BundlerHostName); - var hostName = BundleHelper.GetHostName(fixture); - var appName = Path.GetFileNameWithoutExtension(hostName); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - // Generate a file specification without the apphost - var fileSpecs = new List(); - string[] files = { $"{appName}.dll", $"{appName}.deps.json", $"{appName}.runtimeconfig.json" }; - Array.ForEach(files, x => fileSpecs.Add(new FileSpec(x, x))); - - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + // File specification without the apphost + var fileSpecs = new FileSpec[] + { + new FileSpec($"{appName}.dll", $"{appName}.dll"), + new FileSpec($"{appName}.deps.json", $"{appName}.deps.json"), + new FileSpec($"{appName}.runtimeconfig.json", $"{appName}.runtimeconfig.json") + }; + Bundler bundler = CreateBundlerInstance(); Assert.Throws(() => bundler.GenerateBundle(fileSpecs)); } [Fact] - public void TestWithExactDuplicateEntriesPasses() + public void ExactDuplicateEntries() { - var fixture = sharedTestState.TestFixture.Copy(); - - var hostName = BundleHelper.GetHostName(fixture); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - // Generate a file specification with duplicate entries - var fileSpecs = new List(); - fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); - string appPath = BundleHelper.GetAppPath(fixture); - fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); - fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); - string systemLibPath = Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"); - fileSpecs.Add(new FileSpec(systemLibPath, "rel/system.repeat.dll")); - fileSpecs.Add(new FileSpec(systemLibPath, "rel/system.repeat.dll")); - - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + string appPath = sharedTestState.App.AppDll; + string systemLibPath = sharedTestState.SystemDll; + + // File specification with duplicate entries with matching source paths + var fileSpecs = new FileSpec[] + { + new FileSpec(Binaries.AppHost.FilePath, BundlerHostName), + new FileSpec(appPath, "rel/app.repeat.dll"), + new FileSpec(appPath, "rel/app.repeat.dll"), + new FileSpec(systemLibPath, "rel/system.repeat.dll"), + new FileSpec(systemLibPath, "rel/system.repeat.dll") + }; + + Bundler bundler = CreateBundlerInstance(); bundler.GenerateBundle(fileSpecs); // Exact duplicates are not duplicated in the bundle @@ -107,45 +93,35 @@ public void TestWithExactDuplicateEntriesPasses() } [Fact] - public void TestWithDuplicateEntriesFails() + public void DuplicateBundleRelativePath_Fails() { - var fixture = sharedTestState.TestFixture.Copy(); - - var hostName = BundleHelper.GetHostName(fixture); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - // Generate a file specification with duplicate entries - var fileSpecs = new List(); - fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); - fileSpecs.Add(new FileSpec(BundleHelper.GetAppPath(fixture), "rel/app.repeat")); - fileSpecs.Add(new FileSpec(Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"), "rel/app.repeat")); + // File specification with duplicate entries with different source paths + var fileSpecs = new FileSpec[] + { + new FileSpec(Binaries.AppHost.FilePath, BundlerHostName), + new FileSpec(sharedTestState.App.AppDll, "rel/app.repeat"), + new FileSpec(sharedTestState.SystemDll, "rel/app.repeat"), + }; - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + Bundler bundler = CreateBundlerInstance(); Assert.Throws(() => bundler.GenerateBundle(fileSpecs)) .Message .Should().Contain("rel/app.repeat") - .And.Contain(BundleHelper.GetAppPath(fixture)); + .And.Contain(sharedTestState.App.AppDll); } [Fact] - public void TestWithCaseSensitiveDuplicateEntriesPasses() + public void CaseSensitiveBundleRelativePath() { - var fixture = sharedTestState.TestFixture.Copy(); - - var hostName = BundleHelper.GetHostName(fixture); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - // Generate a file specification with duplicate entries - var fileSpecs = new List(); - fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); - fileSpecs.Add(new FileSpec(BundleHelper.GetAppPath(fixture), "rel/app.repeat.dll")); - fileSpecs.Add(new FileSpec(Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"), "rel/app.Repeat.dll")); + // File specification with entries with bundle paths differing only in casing + var fileSpecs = new FileSpec[] + { + new FileSpec(Binaries.AppHost.FilePath, BundlerHostName), + new FileSpec(sharedTestState.App.AppDll, "rel/app.repeat.dll"), + new FileSpec(sharedTestState.SystemDll, "rel/app.Repeat.dll"), + }; - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + Bundler bundler = CreateBundlerInstance(); bundler.GenerateBundle(fileSpecs); bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals("rel/app.repeat.dll")).Single().Type.Should().Be(FileType.Assembly); @@ -154,27 +130,21 @@ public void TestWithCaseSensitiveDuplicateEntriesPasses() private (string bundleFileName, string bundleId) CreateSampleBundle(bool bundleMultipleFiles) { - var fixture = sharedTestState.TestFixture.Copy(); - - var hostName = BundleHelper.GetHostName(fixture); - var bundleDir = Directory.CreateDirectory( - Path.Combine(BundleHelper.GetBundleDir(fixture).FullName, Path.GetRandomFileName())); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - var fileSpecs = new List(); - fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); + var fileSpecs = new List() + { + new FileSpec(Binaries.AppHost.FilePath, BundlerHostName) + }; if (bundleMultipleFiles) { - fileSpecs.Add(new FileSpec(BundleHelper.GetAppPath(fixture), "rel/app.repeat.dll")); + fileSpecs.Add(new FileSpec(sharedTestState.App.AppDll, "rel/app.repeat.dll")); } - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + Bundler bundler = CreateBundlerInstance(); return (bundler.GenerateBundle(fileSpecs), bundler.BundleManifest.BundleID); } [Fact] - public void TestWithIdenticalBundlesShouldBeBinaryEqualPasses() + public void IdenticalBundles_BinaryEqual() { var firstBundle = CreateSampleBundle(true); byte[] firstBundleContent = File.ReadAllBytes(firstBundle.bundleFileName); @@ -188,7 +158,7 @@ public void TestWithIdenticalBundlesShouldBeBinaryEqualPasses() } [Fact] - public void TestWithUniqueBundlesShouldHaveUniqueBundleIdsPasses() + public void UniqueBundles_UniqueBundleIds() { string firstBundle = CreateSampleBundle(true).bundleId; string secondBundle = CreateSampleBundle(false).bundleId; @@ -197,26 +167,21 @@ public void TestWithUniqueBundlesShouldHaveUniqueBundleIdsPasses() } [Fact] - public void TestWithMultipleDuplicateEntriesFails() + public void MultipleDuplicateBundleRelativePath_Fails() { - var fixture = sharedTestState.TestFixture.Copy(); - - var hostName = BundleHelper.GetHostName(fixture); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - // Generate a file specification with duplicate entries - var fileSpecs = new List(); - fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); - string appPath = BundleHelper.GetAppPath(fixture); - fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); - fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); - string systemLibPath = Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"); - fileSpecs.Add(new FileSpec(appPath, "rel/system.repeat.dll")); - fileSpecs.Add(new FileSpec(systemLibPath, "rel/system.repeat.dll")); - - Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + // File specification with a mix of duplicate entries with different/matching source paths + string appPath = sharedTestState.App.AppDll; + string systemLibPath = sharedTestState.SystemDll; + var fileSpecs = new FileSpec[] + { + new FileSpec(Binaries.AppHost.FilePath, BundlerHostName), + new FileSpec(appPath, "rel/app.repeat.dll"), + new FileSpec(appPath, "rel/app.repeat.dll"), + new FileSpec(appPath, "rel/system.repeat.dll"), + new FileSpec(systemLibPath, "rel/system.repeat.dll"), + }; + + Bundler bundler = CreateBundlerInstance(); Assert.Throws(() => bundler.GenerateBundle(fileSpecs)) .Message .Should().Contain("rel/system.repeat.dll") @@ -226,41 +191,36 @@ public void TestWithMultipleDuplicateEntriesFails() } [Fact] - public void TestBaseNameComputation() + public void BaseNameComputation() { - var fixture = sharedTestState.TestFixture.Copy(); - var publishPath = BundleHelper.GetPublishPath(fixture); - var bundleDir = BundleHelper.GetBundleDir(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - - // Rename the host from "StandaloneApp" to "Stand.Alone.App" to check that baseName computation + // Create an app with multiple periods in its name to check that baseName computation // (and consequently deps.json and runtimeconfig.json name computations) in the bundler // work correctly in the presence of "."s in the hostName. - var originalBaseName = "StandaloneApp"; - var newBaseName = "Stand.Alone.App"; - var exe = OperatingSystem.IsWindows() ? ".exe" : string.Empty; - - void rename(string extension) + using (var app = TestApp.CreateEmpty("App.With.Periods")) { - File.Move(Path.Combine(publishPath, originalBaseName + extension), Path.Combine(publishPath, newBaseName + extension)); + app.PopulateFrameworkDependent(Constants.MicrosoftNETCoreApp, RepoDirectoriesProvider.Default.MicrosoftNETCoreAppVersion); + + string hostName = Path.GetFileName(app.AppExe); + string depsJsonName = Path.GetFileName(app.DepsJson); + string runtimeConfigName = Path.GetFileName(app.RuntimeConfigJson); + FileSpec[] fileSpecs = new FileSpec[] + { + new FileSpec(Binaries.AppHost.FilePath, hostName), + new FileSpec(app.AppDll, Path.GetRelativePath(app.Location, app.AppDll)), + new FileSpec(app.DepsJson, depsJsonName), + new FileSpec(app.RuntimeConfigJson, runtimeConfigName), + }; + + var bundleDir = Directory.CreateDirectory(SharedFramework.CalculateUniqueTestDirectory(Path.Combine(app.Location, "bundle"))); + var bundler = new Bundler(hostName, bundleDir.FullName); + bundler.GenerateBundle(fileSpecs); + + + bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals(depsJsonName)).Single().Type.Should().Be(FileType.DepsJson); + bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals(runtimeConfigName)).Single().Type.Should().Be(FileType.RuntimeConfigJson); + bundleDir.Should().NotHaveFile(depsJsonName); + bundleDir.Should().NotHaveFile(runtimeConfigName); } - rename(exe); - rename(".deps.json"); - rename(".runtimeconfig.json"); - - var hostName = newBaseName + exe; - var depsJson = newBaseName + ".deps.json"; - var runtimeconfigJson = newBaseName + ".runtimeconfig.json"; - - var bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); - BundleHelper.GenerateBundle(bundler, publishPath, bundleDir.FullName); - - string[] jsonFiles = { depsJson, runtimeconfigJson }; - - bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals(depsJson)).Single().Type.Should().Be(FileType.DepsJson); - bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals(runtimeconfigJson)).Single().Type.Should().Be(FileType.RuntimeConfigJson); - bundleDir.Should().NotHaveFiles(jsonFiles); } [InlineData(BundleOptions.None)] @@ -269,126 +229,103 @@ void rename(string extension) [InlineData(BundleOptions.BundleAllContent)] [InlineData(BundleOptions.BundleSymbolFiles)] [Theory] - public void TestFilesAlwaysBundled(BundleOptions options) + public void BundleOptions_IncludedExcludedFiles(BundleOptions options) { - var fixture = sharedTestState.TestFixture.Copy(); - var bundler = BundleHelper.Bundle(fixture, options); - var bundledFiles = BundleHelper.GetBundledFiles(fixture); - - Array.ForEach(bundledFiles, file => bundler.BundleManifest.Contains(file).Should().BeTrue()); - } + TestApp app = sharedTestState.App; + string devJsonName = Path.GetFileName(app.RuntimeDevConfigJson); + string appSymbolName = $"{app.Name}.pdb"; + string otherContentName = "other.txt"; + 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)), + new FileSpec(app.RuntimeConfigJson, devJsonName), + new FileSpec(Path.Combine(app.Location, appSymbolName), appSymbolName), + new FileSpec(Binaries.CoreClr.FilePath, Binaries.CoreClr.FileName), + new FileSpec(app.RuntimeConfigJson, otherContentName), + }; - [InlineData(BundleOptions.None)] - [InlineData(BundleOptions.BundleNativeBinaries)] - [InlineData(BundleOptions.BundleOtherFiles)] - [InlineData(BundleOptions.BundleAllContent)] - [InlineData(BundleOptions.BundleSymbolFiles)] - [Theory] - public void TestFilesNeverBundled(BundleOptions options) - { - var fixture = sharedTestState.TestFixture.Copy(); - var appBaseName = BundleHelper.GetAppBaseName(fixture); - string publishPath = BundleHelper.GetPublishPath(fixture); + Bundler bundler = CreateBundlerInstance(options); + bundler.GenerateBundle(fileSpecs); - // Make up a app.runtimeconfig.dev.json file in the publish directory. - File.Copy(Path.Combine(publishPath, $"{appBaseName}.runtimeconfig.json"), - Path.Combine(publishPath, $"{appBaseName}.runtimeconfig.dev.json")); + // App's dll, .deps.json, and .runtimeconfig.json should always be bundled + Assert.True(bundler.BundleManifest.Contains(Path.GetFileName(app.AppDll))); + Assert.True(bundler.BundleManifest.Contains(Path.GetFileName(app.DepsJson))); + Assert.True(bundler.BundleManifest.Contains(Path.GetFileName(app.RuntimeConfigJson))); - var bundler = BundleHelper.Bundle(fixture, options); + // App's .runtimeconfig.dev.json is always excluded + Assert.False(bundler.BundleManifest.Contains(devJsonName)); - bundler.BundleManifest.Contains($"{appBaseName}.runtimeconfig.dev.json").Should().BeFalse(); - } + // Symbols should only be bundled if option is explicitly set + bundler.BundleManifest.Contains(appSymbolName).Should().Be(options.HasFlag(BundleOptions.BundleSymbolFiles)); - [InlineData(BundleOptions.None)] - [InlineData(BundleOptions.BundleSymbolFiles)] - [Theory] - public void TestBundlingSymbols(BundleOptions options) - { - var fixture = sharedTestState.TestFixture.Copy(); - var appBaseName = BundleHelper.GetAppBaseName(fixture); - var bundler = BundleHelper.Bundle(fixture, options); + // Native libararies should only be bundled if option is explicitly set + bundler.BundleManifest.Contains(Binaries.CoreClr.FileName).Should().Be(options.HasFlag(BundleOptions.BundleNativeBinaries)); - bundler.BundleManifest.Contains($"{appBaseName}.pdb").Should().Be(options.HasFlag(BundleOptions.BundleSymbolFiles)); + // Other files should only be bundled if option is explicitly set + bundler.BundleManifest.Contains(otherContentName).Should().Be(options.HasFlag(BundleOptions.BundleOtherFiles)); } - [InlineData(BundleOptions.None)] - [InlineData(BundleOptions.BundleNativeBinaries)] - [Theory] - public void TestBundlingNativeBinaries(BundleOptions options) + [Fact] + public void FileSizes() { - var fixture = sharedTestState.TestFixture.Copy(); - var coreclr = Path.GetFileName(fixture.TestProject.CoreClrDll); - var bundler = BundleHelper.Bundle(fixture, options); + var app = sharedTestState.App; + List fileSpecs = new List + { + 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)), + }; + fileSpecs.AddRange(SingleFileTestApp.GetRuntimeFilesToBundle()); - bundler.BundleManifest.Contains($"{coreclr}").Should().Be(options.HasFlag(BundleOptions.BundleNativeBinaries)); + Bundler bundler = CreateBundlerInstance(); + bundler.GenerateBundle(fileSpecs); + foreach (FileEntry file in bundler.BundleManifest.Files) + { + var spec = fileSpecs.Single(f => f.BundleRelativePath == file.RelativePath); + Assert.True(file.Size == new FileInfo(spec.SourcePath).Length); + } } [Fact] - public void TestFileSizes() + public void AssemblyAlignment() { - var fixture = sharedTestState.TestFixture.Copy(); - var bundler = BundleHelper.Bundle(fixture); - var publishPath = BundleHelper.GetPublishPath(fixture); + var app = sharedTestState.App; + List fileSpecs = new List + { + new FileSpec(Binaries.AppHost.FilePath, BundlerHostName), + new FileSpec(app.AppDll, Path.GetRelativePath(app.Location, app.AppDll)), + }; + fileSpecs.AddRange(SingleFileTestApp.GetRuntimeFilesToBundle()); - bundler.BundleManifest.Files.ForEach(file => - Assert.True(file.Size == new FileInfo(Path.Combine(publishPath, file.RelativePath)).Length)); - } + Bundler bundler = CreateBundlerInstance(); + bundler.GenerateBundle(fileSpecs); - [Fact] - public void TestAssemblyAlignment() - { - var fixture = sharedTestState.TestFixture.Copy(); - var bundler = BundleHelper.Bundle(fixture); - var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); - var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); - var alignment = (targetOS == OSPlatform.Linux && targetArch == Architecture.Arm64) ? 4096 : 16; + var alignment = OperatingSystem.IsLinux() && RuntimeInformation.OSArchitecture == Architecture.Arm64 ? 4096 : 16; bundler.BundleManifest.Files.ForEach(file => Assert.True((file.Type != FileType.Assembly) || (file.Offset % alignment == 0))); } - [Fact] - public void TestWithAdditionalContentAfterBundleMetadata() - { - var fixture = sharedTestState.TestFixture.Copy(); - string singleFile = BundleHelper.BundleApp(fixture); - - using (var file = File.OpenWrite(singleFile)) - { - file.Position = file.Length; - var blob = Encoding.UTF8.GetBytes("Mock signature at the end of the bundle"); - file.Write(blob, 0, blob.Length); - } - - Command.Create(singleFile) - .CaptureStdErr() - .CaptureStdOut() - .Execute() - .Should() - .Pass() - .And - .HaveStdOutContaining("Hello World!"); - } - public class SharedTestState : IDisposable { - public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } + public const string AppName = "HelloWorld"; + public TestApp App { get; } + public string SystemDll { get; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - - TestFixture = new TestProjectFixture("StandaloneApp", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid) - .PublishProject(runtime: TestFixture.CurrentRid, - selfContained: true, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + App = TestApp.CreateFromBuiltAssets(AppName); + + var builtDotNet = new DotNet.Cli.Build.DotNetCli(RepoDirectoriesProvider.Default.BuiltDotnet); + SystemDll = Path.Combine(builtDotNet.GreatestVersionSharedFxPath, "System.dll"); } public void Dispose() { - TestFixture.Dispose(); + App.Dispose(); } } } diff --git a/src/installer/tests/TestUtils/Binaries.cs b/src/installer/tests/TestUtils/Binaries.cs index dc9d109e7def5..9299a76481bed 100644 --- a/src/installer/tests/TestUtils/Binaries.cs +++ b/src/installer/tests/TestUtils/Binaries.cs @@ -42,7 +42,7 @@ public static class AppHost public static class CoreClr { public static string FileName = GetSharedLibraryFileNameForCurrentPlatform("coreclr"); - public static string FilePath = Path.Combine(RepoDirectoriesProvider.Default.HostArtifacts, FileName); + public static string FilePath = Path.Combine(new DotNetCli(RepoDirectoriesProvider.Default.BuiltDotnet).GreatestVersionSharedFxPath, FileName); public static string MockName = GetSharedLibraryFileNameForCurrentPlatform("mockcoreclr"); public static string MockPath = Path.Combine(RepoDirectoriesProvider.Default.HostTestArtifacts, MockName); diff --git a/src/installer/tests/TestUtils/SingleFileTestApp.cs b/src/installer/tests/TestUtils/SingleFileTestApp.cs index eeeeb8122fba9..356cdb5d5ccea 100644 --- a/src/installer/tests/TestUtils/SingleFileTestApp.cs +++ b/src/installer/tests/TestUtils/SingleFileTestApp.cs @@ -60,6 +60,19 @@ private static SingleFileTestApp Create(string appName, bool selfContained) }; } + public static IReadOnlyList GetRuntimeFilesToBundle() + { + var runtimeAssemblies = Binaries.GetRuntimeFiles().Assemblies; + List fileSpecs = new List(); + foreach (var asset in runtimeAssemblies) + { + fileSpecs.Add(new FileSpec(asset, Path.GetFileName(asset))); + } + + fileSpecs.Sort((a, b) => string.CompareOrdinal(a.BundleRelativePath, b.BundleRelativePath)); + return fileSpecs; + } + public string Bundle(BundleOptions options, Version? bundleVersion = null) { string bundleDirectory = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(Location, "bundle")); @@ -81,11 +94,7 @@ public string Bundle(BundleOptions options, Version? bundleVersion = null) // If this is a self-contained app, add the runtime assemblies to the bundle if (selfContained) { - var runtimeAssemblies = Binaries.GetRuntimeFiles().Assemblies; - foreach (var asset in runtimeAssemblies) - { - fileSpecs.Add(new FileSpec(asset, Path.GetFileName(asset))); - } + fileSpecs.AddRange(GetRuntimeFilesToBundle()); } // Sort the file specs to keep the bundle construction deterministic. diff --git a/src/installer/tests/TestUtils/TestApp.cs b/src/installer/tests/TestUtils/TestApp.cs index f3df7c9f4514c..487903bf41963 100644 --- a/src/installer/tests/TestUtils/TestApp.cs +++ b/src/installer/tests/TestUtils/TestApp.cs @@ -50,6 +50,15 @@ public static TestApp CreateEmpty(string name) }; } + public static TestApp CreateFromBuiltAssets(string appName) + { + TestApp app = CreateEmpty(appName); + TestArtifact.CopyRecursive( + Path.Combine(RepoDirectoriesProvider.Default.TestAssetsOutput, appName), + app.Location); + return app; + } + public void PopulateFrameworkDependent(string fxName, string fxVersion, Action customizer = null) { var builder = NetCoreAppBuilder.PortableForNETCoreApp(this);