// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.IO; using System.Diagnostics; using System.Runtime.InteropServices; using FluentAssertions; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.PlatformAbstractions; using Microsoft.DotNet.TestFramework; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; using LocalizableStrings = Microsoft.DotNet.Tools.Publish.LocalizableStrings; namespace Microsoft.DotNet.Cli.Publish.Tests { public class GivenDotnetPublishPublishesProjects : TestBase { [Fact] public void ItPublishesARunnablePortableApp() { var testAppName = "MSBuildTestApp"; var testInstance = TestAssets.Get(testAppName) .CreateInstance() .WithSourceFiles(); var testProjectDirectory = testInstance.Root.FullName; new RestoreCommand() .WithWorkingDirectory(testProjectDirectory) .Execute() .Should().Pass(); new PublishCommand() .WithWorkingDirectory(testProjectDirectory) .Execute("--framework netcoreapp2.2") .Should().Pass(); var configuration = Environment.GetEnvironmentVariable("CONFIGURATION") ?? "Debug"; var outputDll = Path.Combine(testProjectDirectory, "bin", configuration, "netcoreapp2.2", "publish", $"{testAppName}.dll"); new DotnetCommand() .ExecuteWithCapturedOutput(outputDll) .Should().Pass() .And.HaveStdOutContaining("Hello World"); } [Fact] public void ItImplicitlyRestoresAProjectWhenPublishing() { var testAppName = "MSBuildTestApp"; var testInstance = TestAssets.Get(testAppName) .CreateInstance() .WithSourceFiles(); var testProjectDirectory = testInstance.Root.FullName; new PublishCommand() .WithWorkingDirectory(testProjectDirectory) .Execute("--framework netcoreapp2.2") .Should().Pass(); } [Fact] public void ItCanPublishAMultiTFMProjectWithImplicitRestore() { var testInstance = TestAssets.Get( TestAssetKinds.DesktopTestProjects, "NETFrameworkReferenceNETStandard20") .CreateInstance() .WithSourceFiles(); string projectDirectory = Path.Combine(testInstance.Root.FullName, "MultiTFMTestApp"); new PublishCommand() .WithWorkingDirectory(projectDirectory) .Execute("--framework netcoreapp2.2") .Should().Pass(); } [Fact] public void ItDoesNotImplicitlyRestoreAProjectWhenPublishingWithTheNoRestoreOption() { var testAppName = "MSBuildTestApp"; var testInstance = TestAssets.Get(testAppName) .CreateInstance() .WithSourceFiles(); var testProjectDirectory = testInstance.Root.FullName; new PublishCommand() .WithWorkingDirectory(testProjectDirectory) .ExecuteWithCapturedOutput("--framework netcoreapp2.2 --no-restore") .Should().Fail() .And.HaveStdOutContaining("project.assets.json"); } [Theory] [InlineData(null)] [InlineData("--self-contained")] [InlineData("--self-contained=true")] public void ItPublishesSelfContainedWithRid(string args) { var testAppName = "MSBuildTestApp"; var rid = DotnetLegacyRuntimeIdentifiers.InferLegacyRestoreRuntimeIdentifier(); var outputDirectory = PublishApp(testAppName, rid, args); var outputProgram = Path.Combine(outputDirectory.FullName, $"{testAppName}{Constants.ExeSuffix}"); new TestCommand(outputProgram) .ExecuteWithCapturedOutput() .Should().Pass() .And.HaveStdOutContaining("Hello World"); } [Theory] [InlineData("--self-contained=false")] public void ItPublishesFrameworkDependentWithRid(string args) { var testAppName = "MSBuildTestApp"; var rid = DotnetLegacyRuntimeIdentifiers.InferLegacyRestoreRuntimeIdentifier(); var outputDirectory = PublishApp(testAppName, rid, args); outputDirectory.Should().OnlyHaveFiles(new[] { $"{testAppName}.dll", $"{testAppName}.pdb", $"{testAppName}.deps.json", $"{testAppName}.runtimeconfig.json", }); var outputProgram = Path.Combine(outputDirectory.FullName, $"{testAppName}{Constants.ExeSuffix}"); new DotnetCommand() .ExecuteWithCapturedOutput(Path.Combine(outputDirectory.FullName, $"{testAppName}.dll")) .Should().Pass() .And.HaveStdOutContaining("Hello World"); } [Theory] [InlineData("--self-contained=false")] [InlineData(null)] public void ItPublishesFrameworkDependentWithoutRid(string args) { var testAppName = "MSBuildTestApp"; var outputDirectory = PublishApp(testAppName, rid: null, args: args); outputDirectory.Should().OnlyHaveFiles(new[] { $"{testAppName}.dll", $"{testAppName}.pdb", $"{testAppName}.deps.json", $"{testAppName}.runtimeconfig.json", }); new DotnetCommand() .ExecuteWithCapturedOutput(Path.Combine(outputDirectory.FullName, $"{testAppName}.dll")) .Should().Pass() .And.HaveStdOutContaining("Hello World"); } private DirectoryInfo PublishApp(string testAppName, string rid, string args = null) { var testInstance = TestAssets.Get(testAppName) .CreateInstance($"PublishApp_{rid ?? "none"}_{args ?? "none"}") .WithSourceFiles() .WithRestoreFiles(); var testProjectDirectory = testInstance.Root; new PublishCommand() .WithRuntime(rid) .WithWorkingDirectory(testProjectDirectory) .Execute(args ?? "") .Should().Pass(); var configuration = Environment.GetEnvironmentVariable("CONFIGURATION") ?? "Debug"; return testProjectDirectory .GetDirectory("bin", configuration, "netcoreapp2.2", rid ?? "", "publish"); } [Fact] public void ItPublishesAppWhenRestoringToSpecificPackageDirectory() { var rootPath = TestAssets.CreateTestDirectory().FullName; var rootDir = new DirectoryInfo(rootPath); string dir = "pkgs"; string args = $"--packages {dir}"; string newArgs = $"console -o \"{rootPath}\" --no-restore"; new NewCommandShim() .WithWorkingDirectory(rootPath) .Execute(newArgs) .Should() .Pass(); new RestoreCommand() .WithWorkingDirectory(rootPath) .Execute(args) .Should() .Pass(); new PublishCommand() .WithWorkingDirectory(rootPath) .ExecuteWithCapturedOutput("--no-restore") .Should().Pass(); var configuration = Environment.GetEnvironmentVariable("CONFIGURATION") ?? "Debug"; var outputProgram = rootDir .GetDirectory("bin", configuration, "netcoreapp2.1", "publish", $"{rootDir.Name}.dll") .FullName; new TestCommand(outputProgram) .ExecuteWithCapturedOutput() .Should().Pass() .And.HaveStdOutContaining("Hello World"); } [Fact] public void ItFailsToPublishWithNoBuildIfNotPreviouslyBuilt() { var rootPath = TestAssets.CreateTestDirectory().FullName; string newArgs = $"console -o \"{rootPath}\""; new NewCommandShim() // note implicit restore here .WithWorkingDirectory(rootPath) .Execute(newArgs) .Should() .Pass(); new PublishCommand() .WithWorkingDirectory(rootPath) .ExecuteWithCapturedOutput("--no-build") .Should() .Fail() .And.HaveStdOutContaining("MSB3030"); // "Could not copy ___ because it was not found." } [Theory] [InlineData(false)] [InlineData(true)] public void ItPublishesSuccessfullyWithNoBuildIfPreviouslyBuilt(bool selfContained) { var rootPath = TestAssets.CreateTestDirectory(identifier: selfContained ? "_sc" : "").FullName; var rootDir = new DirectoryInfo(rootPath); string newArgs = $"console -o \"{rootPath}\" --no-restore"; new NewCommandShim() .WithWorkingDirectory(rootPath) .Execute(newArgs) .Should() .Pass(); var rid = selfContained ? DotnetLegacyRuntimeIdentifiers.InferLegacyRestoreRuntimeIdentifier() : ""; var ridArg = selfContained ? $"-r {rid}" : ""; new BuildCommand() .WithWorkingDirectory(rootPath) .ExecuteWithCapturedOutput(ridArg) .Should() .Pass(); new PublishCommand() .WithWorkingDirectory(rootPath) .ExecuteWithCapturedOutput($"{ridArg} --no-build") .Should() .Pass(); var configuration = Environment.GetEnvironmentVariable("CONFIGURATION") ?? "Debug"; var outputProgram = rootDir .GetDirectory("bin", configuration, "netcoreapp2.1", rid, "publish", $"{rootDir.Name}.dll") .FullName; new TestCommand(outputProgram) .ExecuteWithCapturedOutput() .Should() .Pass() .And.HaveStdOutContaining("Hello World"); } [Fact] public void ItFailsToPublishWithNoBuildIfPreviouslyBuiltWithoutRid() { var rootPath = TestAssets.CreateTestDirectory().FullName; var rootDir = new DirectoryInfo(rootPath); string newArgs = $"console -o \"{rootPath}\" --no-restore"; new NewCommandShim() .WithWorkingDirectory(rootPath) .Execute(newArgs) .Should() .Pass(); new BuildCommand() .WithWorkingDirectory(rootPath) .ExecuteWithCapturedOutput() .Should() .Pass(); new PublishCommand() .WithWorkingDirectory(rootPath) .ExecuteWithCapturedOutput("-r win-x64 --no-build") .Should() .Fail(); } } }