diff --git a/src/Cli/dotnet/Commands/Test/MTP/MicrosoftTestingPlatformTestCommand.cs b/src/Cli/dotnet/Commands/Test/MTP/MicrosoftTestingPlatformTestCommand.cs index 28035134421b..fcc7ed0849ce 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/MicrosoftTestingPlatformTestCommand.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/MicrosoftTestingPlatformTestCommand.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Immutable; using System.CommandLine; using System.Diagnostics.CodeAnalysis; using Microsoft.DotNet.Cli.Commands.Test.Terminal; @@ -41,7 +42,10 @@ private int RunInternal(ParseResult parseResult, bool isHelp) ValidationUtility.ValidateSolutionOrProjectOrDirectoryOrModulesArePassedCorrectly(parseResult); int degreeOfParallelism = GetDegreeOfParallelism(parseResult); - var testOptions = new TestOptions(IsHelp: isHelp, IsDiscovery: parseResult.HasOption(MicrosoftTestingPlatformOptions.ListTestsOption)); + var testOptions = new TestOptions( + IsHelp: isHelp, + IsDiscovery: parseResult.HasOption(MicrosoftTestingPlatformOptions.ListTestsOption), + EnvironmentVariables: parseResult.GetValue(CommonOptions.EnvOption) ?? ImmutableDictionary.Empty); InitializeOutput(degreeOfParallelism, parseResult, testOptions); diff --git a/src/Cli/dotnet/Commands/Test/MTP/Options.cs b/src/Cli/dotnet/Commands/Test/MTP/Options.cs index 21277304d1b9..cb9e29e3e4e3 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Options.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Options.cs @@ -3,7 +3,7 @@ namespace Microsoft.DotNet.Cli.Commands.Test; -internal record TestOptions(bool IsHelp, bool IsDiscovery); +internal record TestOptions(bool IsHelp, bool IsDiscovery, IReadOnlyDictionary EnvironmentVariables); internal record PathOptions(string? ProjectPath, string? SolutionPath, string? ResultsDirectoryPath, string? ConfigFilePath, string? DiagnosticOutputDirectoryPath); diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs index 109e9e3b4259..df4a5fff6260 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs @@ -140,6 +140,12 @@ private ProcessStartInfo CreateProcessStartInfo() processStartInfo.Environment[entry.Key] = value; } + // Env variables specified on command line override those specified in launch profile: + foreach (var (name, value) in TestOptions.EnvironmentVariables) + { + processStartInfo.Environment[name] = value; + } + if (!_buildOptions.NoLaunchProfileArguments && !string.IsNullOrEmpty(Module.LaunchSettings.CommandLineArgs)) { diff --git a/src/Cli/dotnet/Commands/Test/TestCommandParser.cs b/src/Cli/dotnet/Commands/Test/TestCommandParser.cs index 229db64e3d75..844e7292dd31 100644 --- a/src/Cli/dotnet/Commands/Test/TestCommandParser.cs +++ b/src/Cli/dotnet/Commands/Test/TestCommandParser.cs @@ -246,6 +246,7 @@ private static Command GetTestingPlatformCliCommand() command.Options.Add(MicrosoftTestingPlatformOptions.MaxParallelTestModulesOption); command.Options.Add(MicrosoftTestingPlatformOptions.MinimumExpectedTestsOption); command.Options.Add(CommonOptions.ArchitectureOption); + command.Options.Add(CommonOptions.EnvOption); command.Options.Add(CommonOptions.PropertiesOption); command.Options.Add(MicrosoftTestingPlatformOptions.ConfigurationOption); command.Options.Add(MicrosoftTestingPlatformOptions.FrameworkOption); diff --git a/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/Program.cs b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/Program.cs new file mode 100644 index 000000000000..07ba26000ce4 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/Program.cs @@ -0,0 +1,47 @@ +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + +using var testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +public class DummyTestAdapter : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestAdapter); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestAdapter); + + public string Description => nameof(DummyTestAdapter); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { + typeof(TestNodeUpdateMessage) + }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + var envVariableValue = Environment.GetEnvironmentVariable("DUMMY_TEST_ENV_VAR") ?? "NOT SET"; + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + Uid = "Test1", + DisplayName = "Test1", + Properties = new PropertyBag(new FailedTestNodeStateProperty($"DUMMY_TEST_ENV_VAR is '{envVariableValue}'")), + })); + + context.Complete(); + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/Properties/launchSettings.json b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/Properties/launchSettings.json new file mode 100644 index 000000000000..8c5035e3252d --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "ConsoleApp25": { + "commandName": "Project", + "environmentVariables": { + "DUMMY_TEST_ENV_VAR": "FROM_LAUNCHSETTINGS" + } + } + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/TestProjectShowingEnvVariable.csproj b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/TestProjectShowingEnvVariable.csproj new file mode 100644 index 000000000000..1ba237591546 --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/TestProjectShowingEnvVariable.csproj @@ -0,0 +1,18 @@ + + + + + $(CurrentTargetFramework) + Exe + + enable + enable + + false + true + + + + + + diff --git a/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/global.json b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/global.json new file mode 100644 index 000000000000..9009caf0ba8f --- /dev/null +++ b/test/TestAssets/TestProjects/TestProjectShowingEnvVariable/global.json @@ -0,0 +1,5 @@ +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} diff --git a/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTests.cs b/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTests.cs index 25735f54c6aa..29ca6c1a0825 100644 --- a/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTests.cs +++ b/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTests.cs @@ -476,5 +476,35 @@ at Microsoft.DotNet.Cli.Commands.Test.TestApplicationActionQueue.Read(BuildOptio result.StdOut.Contains("Test run completed with non-success exit code: 1 (see: https://aka.ms/testingplatform/exitcodes)"); } } + + [Theory] + [InlineData(TestingConstants.Debug)] + [InlineData(TestingConstants.Release)] + public void RunTestProjectWithEnvVariable(string configuration) + { + TestAsset testInstance = _testAssetsManager.CopyTestAsset("TestProjectShowingEnvVariable", Guid.NewGuid().ToString()) + .WithSource(); + + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: false) + .WithWorkingDirectory(testInstance.Path) + .Execute( + MicrosoftTestingPlatformOptions.ConfigurationOption.Name, configuration, + CommonOptions.EnvOption.Name, "DUMMY_TEST_ENV_VAR=ENV_VAR_CMD_LINE"); + + if (!TestContext.IsLocalized()) + { + result.StdOut + .Should().Contain("Using launch settings from") + .And.Contain($"Properties{Path.DirectorySeparatorChar}launchSettings.json...") + .And.Contain("Test run summary: Failed!") + .And.Contain("total: 1") + .And.Contain("succeeded: 0") + .And.Contain("failed: 1") + .And.Contain("skipped: 0") + .And.Contain("DUMMY_TEST_ENV_VAR is 'ENV_VAR_CMD_LINE'"); + } + + result.ExitCode.Should().Be(ExitCodes.AtLeastOneTestFailed); + } } }