diff --git a/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/AppWithCorruptedLaunchSettings.csproj b/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/AppWithCorruptedLaunchSettings.csproj
new file mode 100644
index 000000000..45a048433
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/AppWithCorruptedLaunchSettings.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+ Exe
+ netcoreapp2.0
+ win7-x64;win7-x86;osx.10.12-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;ubuntu.16.10-x64;centos.7-x64;rhel.7-x64;debian.8-x64;fedora.24-x64;opensuse.42.1-x64
+
+
diff --git a/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/Program.cs b/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/Program.cs
new file mode 100644
index 000000000..042e99826
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/Program.cs
@@ -0,0 +1,26 @@
+// 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;
+
+namespace MSBuildTestApp
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ if (args.Length > 0)
+ {
+ Console.WriteLine("echo args:" + string.Join(";", args));
+ }
+ string message = Environment.GetEnvironmentVariable("Message");
+
+ if (string.IsNullOrEmpty(message))
+ {
+ message = "(NO MESSAGE)";
+ }
+
+ Console.WriteLine(message);
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/Properties/launchSettings.json b/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/Properties/launchSettings.json
new file mode 100644
index 000000000..4307944ab
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithCorruptedLaunchSettings/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:49850/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "First": {
+ "commandName": "Project",
+ "environmentVariables": [ ]
+ },
+ "Second": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "Message": "Second"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestAssets/TestProjects/AppWithLaunchSettings/AppWithLaunchSettings.csproj b/TestAssets/TestProjects/AppWithLaunchSettings/AppWithLaunchSettings.csproj
new file mode 100644
index 000000000..45a048433
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithLaunchSettings/AppWithLaunchSettings.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+ Exe
+ netcoreapp2.0
+ win7-x64;win7-x86;osx.10.12-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;ubuntu.16.10-x64;centos.7-x64;rhel.7-x64;debian.8-x64;fedora.24-x64;opensuse.42.1-x64
+
+
diff --git a/TestAssets/TestProjects/AppWithLaunchSettings/Program.cs b/TestAssets/TestProjects/AppWithLaunchSettings/Program.cs
new file mode 100644
index 000000000..042e99826
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithLaunchSettings/Program.cs
@@ -0,0 +1,26 @@
+// 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;
+
+namespace MSBuildTestApp
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ if (args.Length > 0)
+ {
+ Console.WriteLine("echo args:" + string.Join(";", args));
+ }
+ string message = Environment.GetEnvironmentVariable("Message");
+
+ if (string.IsNullOrEmpty(message))
+ {
+ message = "(NO MESSAGE)";
+ }
+
+ Console.WriteLine(message);
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/AppWithLaunchSettings/Properties/launchSettings.json b/TestAssets/TestProjects/AppWithLaunchSettings/Properties/launchSettings.json
new file mode 100644
index 000000000..615a345f2
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithLaunchSettings/Properties/launchSettings.json
@@ -0,0 +1,31 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:49850/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "First": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "Message": "First"
+ }
+ },
+ "Second": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "Message": "Second"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/AppWithLaunchSettingsNoDefault.csproj b/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/AppWithLaunchSettingsNoDefault.csproj
new file mode 100644
index 000000000..45a048433
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/AppWithLaunchSettingsNoDefault.csproj
@@ -0,0 +1,9 @@
+
+
+
+
+ Exe
+ netcoreapp2.0
+ win7-x64;win7-x86;osx.10.12-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;ubuntu.16.10-x64;centos.7-x64;rhel.7-x64;debian.8-x64;fedora.24-x64;opensuse.42.1-x64
+
+
diff --git a/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/Program.cs b/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/Program.cs
new file mode 100644
index 000000000..042e99826
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/Program.cs
@@ -0,0 +1,26 @@
+// 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;
+
+namespace MSBuildTestApp
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ if (args.Length > 0)
+ {
+ Console.WriteLine("echo args:" + string.Join(";", args));
+ }
+ string message = Environment.GetEnvironmentVariable("Message");
+
+ if (string.IsNullOrEmpty(message))
+ {
+ message = "(NO MESSAGE)";
+ }
+
+ Console.WriteLine(message);
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/Properties/launchSettings.json b/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/Properties/launchSettings.json
new file mode 100644
index 000000000..010893fd5
--- /dev/null
+++ b/TestAssets/TestProjects/AppWithLaunchSettingsNoDefault/Properties/launchSettings.json
@@ -0,0 +1,19 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:49850/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-run/LaunchSettings/ILaunchSettingsProvider.cs b/src/dotnet/commands/dotnet-run/LaunchSettings/ILaunchSettingsProvider.cs
new file mode 100644
index 000000000..64840872e
--- /dev/null
+++ b/src/dotnet/commands/dotnet-run/LaunchSettings/ILaunchSettingsProvider.cs
@@ -0,0 +1,13 @@
+using Microsoft.DotNet.Cli.Utils;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.DotNet.Tools.Run.LaunchSettings
+{
+ public interface ILaunchSettingsProvider
+ {
+ string CommandName { get; }
+
+ LaunchSettingsApplyResult TryApplySettings(JObject document, JObject model, ref ICommand command);
+ }
+
+}
diff --git a/src/dotnet/commands/dotnet-run/LaunchSettings/LaunchSettingsApplyResult.cs b/src/dotnet/commands/dotnet-run/LaunchSettings/LaunchSettingsApplyResult.cs
new file mode 100644
index 000000000..04b9dca3b
--- /dev/null
+++ b/src/dotnet/commands/dotnet-run/LaunchSettings/LaunchSettingsApplyResult.cs
@@ -0,0 +1,18 @@
+namespace Microsoft.DotNet.Tools.Run.LaunchSettings
+{
+ public class LaunchSettingsApplyResult
+ {
+ public LaunchSettingsApplyResult(bool success, string failureReason, string runAfterLaunch = null)
+ {
+ Success = success;
+ FailureReason = failureReason;
+ RunAfterLaunch = runAfterLaunch;
+ }
+
+ public bool Success { get; }
+
+ public string FailureReason { get; }
+
+ public string RunAfterLaunch { get; }
+ }
+}
diff --git a/src/dotnet/commands/dotnet-run/LaunchSettings/LaunchSettingsManager.cs b/src/dotnet/commands/dotnet-run/LaunchSettings/LaunchSettingsManager.cs
new file mode 100644
index 000000000..0fbb7f777
--- /dev/null
+++ b/src/dotnet/commands/dotnet-run/LaunchSettings/LaunchSettingsManager.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.DotNet.Cli.Utils;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.DotNet.Tools.Run.LaunchSettings
+{
+ internal class LaunchSettingsManager
+ {
+ private const string ProfilesKey = "profiles";
+ private const string CommandNameKey = "commandName";
+ private const string DefaultProfileCommandName = "Project";
+ private static IReadOnlyDictionary _providers;
+
+ static LaunchSettingsManager()
+ {
+ _providers = new Dictionary
+ {
+ { ProjectLaunchSettingsProvider.CommandNameValue, new ProjectLaunchSettingsProvider() }
+ };
+ }
+
+ public static LaunchSettingsApplyResult TryApplyLaunchSettings(string launchSettingsJsonContents, ref ICommand command, string profileName = null)
+ {
+ try
+ {
+ var model = JObject.Parse(launchSettingsJsonContents);
+ var profilesObject = model[ProfilesKey] as JObject;
+
+ if (profilesObject == null)
+ {
+ return new LaunchSettingsApplyResult(false, LocalizableStrings.LaunchProfilesCollectionIsNotAJsonObject);
+ }
+
+ JObject profileObject;
+ if (profileName == null)
+ {
+ profileObject = profilesObject
+ .Properties()
+ .FirstOrDefault(IsDefaultProfileType)?.Value as JObject;
+ }
+ else
+ {
+ profileObject = profilesObject[profileName] as JObject;
+
+ if (profileObject == null)
+ {
+ return new LaunchSettingsApplyResult(false, LocalizableStrings.LaunchProfileIsNotAJsonObject);
+ }
+ }
+
+ if (profileObject == null)
+ {
+ foreach (var prop in profilesObject.Properties())
+ {
+ var profile = prop.Value as JObject;
+
+ if (profile != null)
+ {
+ var cmdName = profile[CommandNameKey]?.Value();
+ if (_providers.ContainsKey(cmdName))
+ {
+ profileObject = profile;
+ break;
+ }
+ }
+ }
+ }
+
+ var commandName = profileObject?[CommandNameKey]?.Value();
+
+ if (profileObject == null)
+ {
+ return new LaunchSettingsApplyResult(false, LocalizableStrings.UsableLaunchProfileCannotBeLocated);
+ }
+
+ if (!TryLocateHandler(commandName, out ILaunchSettingsProvider provider))
+ {
+ return new LaunchSettingsApplyResult(false, string.Format(LocalizableStrings.LaunchProfileHandlerCannotBeLocated, commandName));
+ }
+
+ return provider.TryApplySettings(model, profileObject, ref command);
+ }
+ catch (JsonException ex)
+ {
+ return new LaunchSettingsApplyResult(false, string.Format(LocalizableStrings.DeserializationExceptionMessage, ex.Message));
+ }
+ }
+
+ private static bool TryLocateHandler(string commandName, out ILaunchSettingsProvider provider)
+ {
+ return _providers.TryGetValue(commandName, out provider);
+ }
+
+ private static bool IsDefaultProfileType(JProperty profileProperty)
+ {
+ JObject profile = profileProperty.Value as JObject;
+ var commandName = profile?[CommandNameKey]?.Value();
+ return string.Equals(commandName, DefaultProfileCommandName, StringComparison.Ordinal);
+ }
+ }
+}
diff --git a/src/dotnet/commands/dotnet-run/LaunchSettings/ProjectLaunchSettingsProvider.cs b/src/dotnet/commands/dotnet-run/LaunchSettings/ProjectLaunchSettingsProvider.cs
new file mode 100644
index 000000000..780870494
--- /dev/null
+++ b/src/dotnet/commands/dotnet-run/LaunchSettings/ProjectLaunchSettingsProvider.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.DotNet.Cli.Utils;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.DotNet.Tools.Run.LaunchSettings
+{
+ public class ProjectLaunchSettingsProvider : ILaunchSettingsProvider
+ {
+ public static readonly string CommandNameValue = "Project";
+
+ public string CommandName => CommandNameValue;
+
+ public LaunchSettingsApplyResult TryApplySettings(JObject document, JObject model, ref ICommand command)
+ {
+ var config = model.ToObject();
+
+ //For now, ignore everything but the environment variables section
+
+ foreach (var entry in config.EnvironmentVariables)
+ {
+ string value = Environment.ExpandEnvironmentVariables(entry.Value);
+ //NOTE: MSBuild variables are not expanded like they are in VS
+ command.EnvironmentVariable(entry.Key, value);
+ }
+
+ if (!string.IsNullOrEmpty(config.ApplicationUrl))
+ {
+ command.EnvironmentVariable("ASPNETCORE_URLS", config.ApplicationUrl);
+ }
+
+ return new LaunchSettingsApplyResult(true, null, config.LaunchUrl);
+ }
+
+ private class ProjectLaunchSettingsModel
+ {
+ public ProjectLaunchSettingsModel()
+ {
+ EnvironmentVariables = new Dictionary();
+ }
+
+ public string CommandLineArgs { get; set; }
+
+ public bool LaunchBrowser { get; set; }
+
+ public string LaunchUrl { get; set; }
+
+ public string ApplicationUrl { get; set; }
+
+ public Dictionary EnvironmentVariables { get; }
+ }
+ }
+}
diff --git a/src/dotnet/commands/dotnet-run/LocalizableStrings.cs b/src/dotnet/commands/dotnet-run/LocalizableStrings.cs
index 4cdfd5217..bf700eece 100644
--- a/src/dotnet/commands/dotnet-run/LocalizableStrings.cs
+++ b/src/dotnet/commands/dotnet-run/LocalizableStrings.cs
@@ -17,6 +17,10 @@ namespace Microsoft.DotNet.Tools.Run
public const string CommandOptionProjectDescription = "The path to the project file to run (defaults to the current directory if there is only one project).";
+ public const string CommandOptionLaunchProfileDescription = "The name of the launch profile (if any) to use when launching the application.";
+
+ public const string CommandOptionNoLaunchProfileDescription = "Do not attempt to use launchSettings.json to configure the application.";
+
public const string RunCommandException = "The build failed. Please fix the build errors and run again.";
public const string RunCommandExceptionUnableToRunSpecifyFramework = "Unable to run your project\nYour project targets multiple frameworks. Please specify which framework to run using '{0}'.";
@@ -28,5 +32,25 @@ namespace Microsoft.DotNet.Tools.Run
public const string RunCommandExceptionMultipleProjects = "Specify which project file to use because {0} contains more than one project file.";
public const string RunCommandAdditionalArgsHelpText = "Arguments passed to the application that is being run.";
+
+ public const string RunCommandExceptionCouldNotLocateALaunchSettingsFile = "The specified launch profile could not be located.";
+
+ public const string RunCommandExceptionCouldNotApplyLaunchSettings = "The launch profile \"{0}\" could not be applied.\n{1}";
+
+ public const string DefaultLaunchProfileDisplayName = "(Default)";
+
+ public const string UsingLaunchSettingsFromMessage = "Using launch settings from {0}...";
+
+ public const string LaunchProfileIsNotAJsonObject = "Launch profile is not a JSON object.";
+
+ public const string LaunchProfileHandlerCannotBeLocated = "The launch profile type '{0}' is not supported.";
+
+ public const string UsableLaunchProfileCannotBeLocated = "A usable launch profile could not be located.";
+
+ public const string UnexpectedExceptionProcessingLaunchSettings = "An unexpected exception occurred while processing launch settings:\n{0}";
+
+ public const string LaunchProfilesCollectionIsNotAJsonObject = "The 'profiles' property of the launch settings document is not a JSON object.";
+
+ public const string DeserializationExceptionMessage = "An error was encountered when reading launchSettings.json.\n{0}";
}
}
diff --git a/src/dotnet/commands/dotnet-run/RunCommand.cs b/src/dotnet/commands/dotnet-run/RunCommand.cs
index 5a60765ee..3401f1ac1 100644
--- a/src/dotnet/commands/dotnet-run/RunCommand.cs
+++ b/src/dotnet/commands/dotnet-run/RunCommand.cs
@@ -8,6 +8,7 @@ using System.Linq;
using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.MSBuild;
+using Microsoft.DotNet.Tools.Run.LaunchSettings;
namespace Microsoft.DotNet.Tools.Run
{
@@ -22,6 +23,10 @@ namespace Microsoft.DotNet.Tools.Run
private List _args;
private bool ShouldBuild => !NoBuild;
+ public string LaunchProfile { get; private set; }
+ public bool NoLaunchProfile { get; private set; }
+ private bool UseLaunchProfile => !NoLaunchProfile;
+
public int Start()
{
Initialize();
@@ -32,6 +37,12 @@ namespace Microsoft.DotNet.Tools.Run
}
ICommand runCommand = GetRunCommand();
+ int launchSettingsApplicationResult = ApplyLaunchProfileSettingsIfNeeded(ref runCommand);
+
+ if (launchSettingsApplicationResult != 0)
+ {
+ return launchSettingsApplicationResult;
+ }
return runCommand
.Execute()
@@ -42,12 +53,16 @@ namespace Microsoft.DotNet.Tools.Run
string framework,
bool noBuild,
string project,
+ string launchProfile,
+ bool noLaunchProfile,
IReadOnlyCollection args)
{
Configuration = configuration;
Framework = framework;
NoBuild = noBuild;
Project = project;
+ LaunchProfile = launchProfile;
+ NoLaunchProfile = noLaunchProfile;
Args = args;
}
@@ -55,6 +70,8 @@ namespace Microsoft.DotNet.Tools.Run
string framework = null,
bool? noBuild = null,
string project = null,
+ string launchProfile = null,
+ bool? noLaunchProfile = null,
IReadOnlyCollection args = null)
{
return new RunCommand(
@@ -62,10 +79,50 @@ namespace Microsoft.DotNet.Tools.Run
framework ?? this.Framework,
noBuild ?? this.NoBuild,
project ?? this.Project,
+ launchProfile ?? this.LaunchProfile,
+ noLaunchProfile ?? this.NoLaunchProfile,
args ?? this.Args
);
}
+ private int ApplyLaunchProfileSettingsIfNeeded(ref ICommand runCommand)
+ {
+ if (UseLaunchProfile)
+ {
+ var buildPathContainer = File.Exists(Project) ? Path.GetDirectoryName(Project) : Project;
+ var launchSettingsPath = Path.Combine(buildPathContainer, "Properties", "launchSettings.json");
+ if (File.Exists(launchSettingsPath))
+ {
+ Reporter.Output.WriteLine(string.Format(LocalizableStrings.UsingLaunchSettingsFromMessage, launchSettingsPath));
+ string profileName = string.IsNullOrEmpty(LaunchProfile) ? LocalizableStrings.DefaultLaunchProfileDisplayName : LaunchProfile;
+
+ try
+ {
+ var launchSettingsFileContents = File.ReadAllText(launchSettingsPath);
+ var applyResult = LaunchSettingsManager.TryApplyLaunchSettings(launchSettingsFileContents, ref runCommand, LaunchProfile);
+ if (!applyResult.Success)
+ {
+ //Error that the launch profile couldn't be applied
+ Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName, applyResult.FailureReason).Bold().Red());
+ }
+ }
+ catch (IOException ex)
+ {
+ Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName).Bold().Red());
+ Reporter.Error.WriteLine(ex.Message.Bold().Red());
+ return -1;
+ }
+ }
+ else if (!string.IsNullOrEmpty(LaunchProfile))
+ {
+ //Error that the launch profile couldn't be found
+ Reporter.Error.WriteLine(LocalizableStrings.RunCommandExceptionCouldNotLocateALaunchSettingsFile.Bold().Red());
+ }
+ }
+
+ return 0;
+ }
+
private void EnsureProjectIsBuilt()
{
List buildArgs = new List();
diff --git a/src/dotnet/commands/dotnet-run/RunCommandParser.cs b/src/dotnet/commands/dotnet-run/RunCommandParser.cs
index 54f439d79..ec0839c88 100644
--- a/src/dotnet/commands/dotnet-run/RunCommandParser.cs
+++ b/src/dotnet/commands/dotnet-run/RunCommandParser.cs
@@ -21,6 +21,8 @@ namespace Microsoft.DotNet.Cli
framework: o.SingleArgumentOrDefault("--framework"),
noBuild: o.HasOption("--no-build"),
project: o.SingleArgumentOrDefault("--project"),
+ launchProfile: o.SingleArgumentOrDefault("--launch-profile"),
+ noLaunchProfile: o.HasOption("--no-launch-profile"),
args: o.Arguments
)),
options: new[]
@@ -32,6 +34,14 @@ namespace Microsoft.DotNet.Cli
"-p|--project",
LocalizableStrings.CommandOptionProjectDescription,
Accept.ExactlyOneArgument()),
+ Create.Option(
+ "--launch-profile",
+ LocalizableStrings.CommandOptionLaunchProfileDescription,
+ Accept.ExactlyOneArgument()),
+ Create.Option(
+ "--no-launch-profile",
+ LocalizableStrings.CommandOptionNoLaunchProfileDescription,
+ Accept.NoArguments()),
Create.Option(
"--no-build",
LocalizableStrings.CommandOptionNoBuildDescription,
diff --git a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs b/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs
index 8ce1730be..234f890e9 100644
--- a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs
+++ b/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.IO;
+using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
@@ -200,5 +201,267 @@ namespace Microsoft.DotNet.Cli.Run.Tests
.Pass()
.And.HaveStdOutContaining("echo args:foo;bar;baz");
}
+
+ [Fact]
+ public void ItGivesAnErrorWhenAttemptingToUseALaunchProfileThatDoesNotExistWhenThereIsNoLaunchSettingsFile()
+ {
+ var testAppName = "MSBuildTestApp";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput("--launch-profile test")
+ .Should().Pass()
+ .And.HaveStdOutContaining("Hello World!")
+ .And.HaveStdErrContaining("The specified launch profile could not be located.");
+ }
+
+ [Fact]
+ public void ItUsesLaunchProfileOfTheSpecifiedName()
+ {
+ var testAppName = "AppWithLaunchSettings";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ var cmd = new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput("--launch-profile Second");
+
+ cmd.Should().Pass()
+ .And.HaveStdOutContaining("Second");
+
+ cmd.StdErr.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ItDefaultsToTheFirstUsableLaunchProfile()
+ {
+ var testAppName = "AppWithLaunchSettings";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ var cmd = new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput();
+
+ cmd.Should().Pass()
+ .And.HaveStdOutContaining("First");
+
+ cmd.StdErr.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ItGivesAnErrorWhenTheLaunchProfileNotFound()
+ {
+ var testAppName = "AppWithLaunchSettings";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput("--launch-profile Third")
+ .Should().Pass()
+ .And.HaveStdOutContaining("(NO MESSAGE)")
+ .And.HaveStdErrContaining("The launch profile \"Third\" could not be applied.");
+ }
+
+ [Fact]
+ public void ItGivesAnErrorWhenTheLaunchProfileCanNotBeHandled()
+ {
+ var testAppName = "AppWithLaunchSettings";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput("--launch-profile \"IIS Express\"")
+ .Should().Pass()
+ .And.HaveStdOutContaining("(NO MESSAGE)")
+ .And.HaveStdErrContaining("The launch profile \"IIS Express\" could not be applied.");
+ }
+
+ [Fact]
+ public void ItSkipsLaunchProfilesWhenTheSwitchIsSupplied()
+ {
+ var testAppName = "AppWithLaunchSettings";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ var cmd = new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput("--no-launch-profile");
+
+ cmd.Should().Pass()
+ .And.HaveStdOutContaining("(NO MESSAGE)");
+
+ cmd.StdErr.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ItSkipsLaunchProfilesWhenTheSwitchIsSuppliedWithoutErrorWhenThereAreNoLaunchSettings()
+ {
+ var testAppName = "MSBuildTestApp";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ var cmd = new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput("--no-launch-profile");
+
+ cmd.Should().Pass()
+ .And.HaveStdOutContaining("Hello World!");
+
+ cmd.StdErr.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ItSkipsLaunchProfilesWhenThereIsNoUsableDefault()
+ {
+ var testAppName = "AppWithLaunchSettingsNoDefault";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ var cmd = new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput();
+
+ cmd.Should().Pass()
+ .And.HaveStdOutContaining("(NO MESSAGE)")
+ .And.HaveStdErrContaining("The launch profile \"(Default)\" could not be applied.");
+ }
+
+ [Fact]
+ public void ItPrintsAnErrorWhenLaunchSettingsAreCorrupted()
+ {
+ var testAppName = "AppWithCorruptedLaunchSettings";
+ var testInstance = TestAssets.Get(testAppName)
+ .CreateInstance()
+ .WithSourceFiles();
+
+ var testProjectDirectory = testInstance.Root.FullName;
+
+ new RestoreCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute("/p:SkipInvalidConfigurations=true")
+ .Should().Pass();
+
+ new BuildCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .Execute()
+ .Should().Pass();
+
+ var cmd = new RunCommand()
+ .WithWorkingDirectory(testProjectDirectory)
+ .ExecuteWithCapturedOutput();
+
+ cmd.Should().Pass()
+ .And.HaveStdOutContaining("(NO MESSAGE)")
+ .And.HaveStdErrContaining("The launch profile \"(Default)\" could not be applied.");
+ }
}
}