diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/AndroidTestCommandArguments.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/AndroidTestCommandArguments.cs index 087640450..50f56e815 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/AndroidTestCommandArguments.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/AndroidTestCommandArguments.cs @@ -23,6 +23,7 @@ internal class AndroidTestCommandArguments : XHarnessCommandArguments, IAndroidA public ExpectedExitCodeArgument ExpectedExitCode { get; } = new((int)Common.CLI.ExitCode.SUCCESS); public DeviceOutputFolderArgument DeviceOutputFolder { get; } = new(); public WifiArgument Wifi { get; } = new(); + public EnvironmentalVariablesArgument EnvironmentalVariables { get; } = new(); protected override IEnumerable GetArguments() => new Argument[] { @@ -39,6 +40,7 @@ internal class AndroidTestCommandArguments : XHarnessCommandArguments, IAndroidA ExpectedExitCode, DeviceOutputFolder, Wifi, + EnvironmentalVariables, }; public override void Validate() diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/Arguments/EnvironmentalVariablesArgument.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/Arguments/EnvironmentalVariablesArgument.cs new file mode 100644 index 000000000..a9912e4a2 --- /dev/null +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Android/Arguments/EnvironmentalVariablesArgument.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.XHarness.CLI.CommandArguments.Android; + +/// +/// Environmental variables set when executing the application. +/// +internal class EnvironmentalVariablesArgument : Argument +{ + public IReadOnlyCollection<(string, string)> Value => _environmentalVariables; + + private readonly List<(string, string)> _environmentalVariables = new(); + + public EnvironmentalVariablesArgument() : base("set-env=", "Environmental variable to set for the application in format key=value. Can be used multiple times") + { + } + + public override void Action(string argumentValue) + { + var position = argumentValue.IndexOf('='); + if (position == -1) + { + throw new ArgumentException($"The set-env argument {argumentValue} must be in the key=value format"); + } + + var key = argumentValue.Substring(0, position); + var value = argumentValue.Substring(position + 1); + _environmentalVariables.Add((key, value)); + } +} diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidTestCommand.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidTestCommand.cs index 8a42e3d32..3aa4186dd 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidTestCommand.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidTestCommand.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Generic; using System.Linq; using Microsoft.DotNet.XHarness.Android; using Microsoft.DotNet.XHarness.CLI.Android; @@ -62,10 +64,17 @@ protected override ExitCode InvokeCommand(ILogger logger) runner.ClearAdbLog(); var instrumentationRunner = new InstrumentationRunner(logger, runner); + var instrumentationArguments = new Dictionary(Arguments.InstrumentationArguments.Value, StringComparer.Ordinal); + + foreach (var (name, value) in Arguments.EnvironmentalVariables.Value) + { + instrumentationArguments[$"env:{name}"] = value; + } + exitCode = instrumentationRunner.RunApkInstrumentation( Arguments.PackageName, Arguments.InstrumentationName, - Arguments.InstrumentationArguments, + instrumentationArguments, Arguments.OutputDirectory, Arguments.DeviceOutputFolder, Arguments.Timeout, diff --git a/src/Microsoft.DotNet.XHarness.InstrumentationBase.Xunit/DefaultAndroidEntryPoint.cs b/src/Microsoft.DotNet.XHarness.InstrumentationBase.Xunit/DefaultAndroidEntryPoint.cs index 5112d7270..ecc7fdab8 100644 --- a/src/Microsoft.DotNet.XHarness.InstrumentationBase.Xunit/DefaultAndroidEntryPoint.cs +++ b/src/Microsoft.DotNet.XHarness.InstrumentationBase.Xunit/DefaultAndroidEntryPoint.cs @@ -32,6 +32,8 @@ public class DefaultAndroidEntryPoint : AndroidApplicationEntryPoint private readonly string? _excludeCategoriesFile; private readonly Dictionary _parsedArguments; + private const string EnvironmentArgumentPrefix = "env:"; + public const string ResultsFileArgumentName = "results-file-name"; public const string ResultsFileArgumentPath = "results-file-path"; public const string ExcludeCategoriesDirArgumentName = "exclude-categories-dir"; @@ -48,6 +50,11 @@ public DefaultAndroidEntryPoint(string resultsPath, Dictionary o { _parsedArguments = optionalBundle; + if (ApplyEnvironmentVariables(optionalBundle)) + { + ApplicationOptions.Current = new ApplicationOptions(); + } + // use default name for test results file _parsedArguments.TryAdd(ResultsFileArgumentName, "TestResults.xml"); @@ -113,4 +120,22 @@ protected override TestRunner GetTestRunner(LogWriter logWriter) return testRunner; } + + private static bool ApplyEnvironmentVariables(IReadOnlyDictionary arguments) + { + bool applied = false; + + foreach (var pair in arguments) + { + if (pair.Key.StartsWith(EnvironmentArgumentPrefix, StringComparison.Ordinal) && + pair.Key.Length > EnvironmentArgumentPrefix.Length) + { + var envName = pair.Key.Substring(EnvironmentArgumentPrefix.Length); + Environment.SetEnvironmentVariable(envName, pair.Value); + applied = true; + } + } + + return applied; + } } diff --git a/tests/Microsoft.DotNet.XHarness.CLI.Tests/CommandArguments/ArgumentTests.cs b/tests/Microsoft.DotNet.XHarness.CLI.Tests/CommandArguments/ArgumentTests.cs index dc457ce70..69e2ea140 100644 --- a/tests/Microsoft.DotNet.XHarness.CLI.Tests/CommandArguments/ArgumentTests.cs +++ b/tests/Microsoft.DotNet.XHarness.CLI.Tests/CommandArguments/ArgumentTests.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Linq; using Microsoft.DotNet.XHarness.CLI.CommandArguments; +using Microsoft.DotNet.XHarness.CLI.CommandArguments.Android; using Microsoft.DotNet.XHarness.Common.CLI; using Xunit; @@ -195,4 +197,42 @@ public void ArgumentsAreInterpolatedWell() Assert.Equal("switch is true", $"switch is {switchArg}"); Assert.Equal("string is string-value", $"string is {stringArg}"); } + + [Fact] + public void EnvironmentalVariablesArgumentCollectsValues() + { + var argument = new EnvironmentalVariablesArgument(); + var command = UnitTestCommand.FromArgument(argument); + + var exitCode = command.Invoke(new[] + { + "--set-env=env1=val1", + "--set-env", + "env2=val2", + }); + + Assert.Equal(0, exitCode); + Assert.True(command.CommandRun); + + var values = argument.Value.ToArray(); + Assert.Equal(2, values.Length); + Assert.Equal(("env1", "val1"), values[0]); + Assert.Equal(("env2", "val2"), values[1]); + } + + [Fact] + public void EnvironmentalVariablesArgumentRequiresKeyValueFormat() + { + var argument = new EnvironmentalVariablesArgument(); + var command = UnitTestCommand.FromArgument(argument); + + var exitCode = command.Invoke(new[] + { + "--set-env", + "invalid", + }); + + Assert.Equal((int)ExitCode.INVALID_ARGUMENTS, exitCode); + Assert.False(command.CommandRun); + } }