Added extra params (base-build-path, runtime, framework) and refactored parameter parsing into its own class with tests.

Changed program.cs to use the new dotnettestparams.

Hooked up the new parameters to the ProjectDependencyCommandResolver
This commit is contained in:
Livar Cunha 2016-03-11 15:30:37 -08:00
parent 778e63a5a6
commit 45abe6d52b
10 changed files with 554 additions and 182 deletions

View file

@ -22,5 +22,7 @@ namespace Microsoft.DotNet.Cli.Utils
public string Configuration { get; set; }
public IEnumerable<string> InferredExtensions { get; set; }
public string BuildBasePath { get; set; }
}
}

View file

@ -39,7 +39,7 @@ namespace Microsoft.DotNet.Cli.Utils
public CommandSpec Resolve(CommandResolverArguments commandResolverArguments)
{
if (commandResolverArguments.Framework == null
if (commandResolverArguments.Framework == null
|| commandResolverArguments.ProjectDirectory == null
|| commandResolverArguments.Configuration == null
|| commandResolverArguments.CommandName == null)
@ -53,7 +53,8 @@ namespace Microsoft.DotNet.Cli.Utils
commandResolverArguments.Configuration,
commandResolverArguments.CommandName,
commandResolverArguments.CommandArguments.OrEmptyIfNull(),
commandResolverArguments.OutputPath);
commandResolverArguments.OutputPath,
commandResolverArguments.BuildBasePath);
}
private CommandSpec ResolveFromProjectDependencies(
@ -62,23 +63,25 @@ namespace Microsoft.DotNet.Cli.Utils
string configuration,
string commandName,
IEnumerable<string> commandArguments,
string outputPath)
string outputPath,
string buildBasePath)
{
var allowedExtensions = GetAllowedCommandExtensionsFromEnvironment(_environment);
var projectContext = GetProjectContextFromDirectory(
projectDirectory,
projectDirectory,
framework);
if (projectContext == null)
{
{
return null;
}
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.DepsJson;
var depsFilePath =
projectContext.GetOutputPaths(configuration, buildBasePath, outputPath).RuntimeFiles.Deps;
var dependencyLibraries = GetAllDependencyLibraries(projectContext);
return ResolveFromDependencyLibraries(
dependencyLibraries,
depsFilePath,

View file

@ -11,17 +11,20 @@ namespace Microsoft.DotNet.Cli.Utils
private readonly NuGetFramework _nugetFramework;
private readonly string _configuration;
private readonly string _outputPath;
private readonly string _buildBasePath;
private readonly string _projectDirectory;
public ProjectDependenciesCommandFactory(
NuGetFramework nugetFramework,
string configuration,
NuGetFramework nugetFramework,
string configuration,
string outputPath,
string buildBasePath,
string projectDirectory)
{
_nugetFramework = nugetFramework;
_configuration = configuration;
_outputPath = outputPath;
_buildBasePath = buildBasePath;
_projectDirectory = projectDirectory;
}
@ -42,11 +45,12 @@ namespace Microsoft.DotNet.Cli.Utils
}
var commandSpec = FindProjectDependencyCommands(
commandName,
args,
configuration,
framework,
commandName,
args,
configuration,
framework,
_outputPath,
_buildBasePath,
_projectDirectory);
return Command.Create(commandSpec);
@ -58,6 +62,7 @@ namespace Microsoft.DotNet.Cli.Utils
string configuration,
NuGetFramework framework,
string outputPath,
string buildBasePath,
string projectDirectory)
{
var commandResolverArguments = new CommandResolverArguments
@ -67,6 +72,7 @@ namespace Microsoft.DotNet.Cli.Utils
Framework = framework,
Configuration = configuration,
OutputPath = outputPath,
BuildBasePath = buildBasePath,
ProjectDirectory = projectDirectory
};

View file

@ -0,0 +1,39 @@
// 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.IO;
using Microsoft.DotNet.ProjectModel;
namespace Microsoft.DotNet.Tools.Test
{
public class AssemblyUnderTest
{
private readonly ProjectContext _projectContext;
private readonly DotnetTestParams _dotentTestParams;
public AssemblyUnderTest(ProjectContext projectContext, DotnetTestParams dotentTestParams)
{
_projectContext = projectContext;
_dotentTestParams = dotentTestParams;
}
public string Path
{
get
{
var assemblyUnderTest = _projectContext.GetOutputPaths(
_dotentTestParams.Config,
outputPath: _dotentTestParams.Output).CompilationFiles.Assembly;
if (!string.IsNullOrEmpty(_dotentTestParams.Output))
{
assemblyUnderTest = _projectContext.GetOutputPaths(
_dotentTestParams.Config,
outputPath: _dotentTestParams.Output).RuntimeFiles.Assembly;
}
return assemblyUnderTest;
}
}
}
}

View file

@ -0,0 +1,50 @@
// 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.Collections.Generic;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
namespace Microsoft.DotNet.Tools.Test
{
public class ConsoleTestRunner : IDotnetTestRunner
{
public int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
{
var commandFactory =
new ProjectDependenciesCommandFactory(
projectContext.TargetFramework,
dotnetTestParams.Config,
dotnetTestParams.Output,
dotnetTestParams.BuildBasePath,
projectContext.ProjectDirectory);
return commandFactory.Create(
GetCommandName(projectContext.ProjectFile.TestRunner),
GetCommandArgs(projectContext, dotnetTestParams),
projectContext.TargetFramework,
dotnetTestParams.Config)
.ForwardStdErr()
.ForwardStdOut()
.Execute()
.ExitCode;
}
private IEnumerable<string> GetCommandArgs(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
{
var commandArgs = new List<string>
{
new AssemblyUnderTest(projectContext, dotnetTestParams).Path
};
commandArgs.AddRange(dotnetTestParams.RemainingArguments);
return commandArgs;
}
private static string GetCommandName(string testRunner)
{
return $"dotnet-test-{testRunner}";
}
}
}

View file

@ -0,0 +1,69 @@
// 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 Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
namespace Microsoft.DotNet.Tools.Test
{
public class DesignTimeRunner : IDotnetTestRunner
{
public int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams)
{
Console.WriteLine("Listening on port {0}", dotnetTestParams.Port.Value);
HandleDesignTimeMessages(projectContext, dotnetTestParams);
return 0;
}
private static void HandleDesignTimeMessages(
ProjectContext projectContext,
DotnetTestParams dotnetTestParams)
{
var reportingChannelFactory = new ReportingChannelFactory();
var adapterChannel = reportingChannelFactory.CreateAdapterChannel(dotnetTestParams.Port.Value);
try
{
var pathToAssemblyUnderTest = new AssemblyUnderTest(projectContext, dotnetTestParams).Path;
var messages = new TestMessagesCollection();
using (var dotnetTest = new DotnetTest(messages, pathToAssemblyUnderTest))
{
var commandFactory =
new ProjectDependenciesCommandFactory(
projectContext.TargetFramework,
dotnetTestParams.Config,
dotnetTestParams.Output,
dotnetTestParams.BuildBasePath,
projectContext.ProjectDirectory);
var testRunnerFactory =
new TestRunnerFactory(GetCommandName(projectContext.ProjectFile.TestRunner), commandFactory);
dotnetTest
.AddNonSpecificMessageHandlers(messages, adapterChannel)
.AddTestDiscoveryMessageHandlers(adapterChannel, reportingChannelFactory, testRunnerFactory)
.AddTestRunMessageHandlers(adapterChannel, reportingChannelFactory, testRunnerFactory)
.AddTestRunnnersMessageHandlers(adapterChannel, reportingChannelFactory);
dotnetTest.StartListeningTo(adapterChannel);
adapterChannel.Accept();
dotnetTest.StartHandlingMessages();
}
}
catch (Exception ex)
{
adapterChannel.SendError(ex);
}
}
private static string GetCommandName(string testRunner)
{
return $"test-{testRunner}";
}
}
}

View file

@ -0,0 +1,150 @@
// 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Dnx.Runtime.Common.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using NuGet.Frameworks;
using static System.Int32;
namespace Microsoft.DotNet.Tools.Test
{
public class DotnetTestParams
{
private readonly CommandLineApplication _app;
private CommandOption _outputOption;
private CommandOption _buildBasePath;
private CommandOption _frameworkOption;
private CommandOption _runtimeOption;
private CommandOption _configurationOption;
private CommandOption _portOption;
private CommandOption _parentProcessIdOption;
private CommandArgument _projectPath;
public int? Port { get; set; }
public int? ParentProcessId { get; set; }
public string Runtime { get; set; }
public string Config { get; set; }
public string BuildBasePath { get; set; }
public string Output { get; set; }
public string ProjectPath { get; set; }
public NuGetFramework Framework { get; set; }
public List<string> RemainingArguments { get; set; }
public DotnetTestParams()
{
_app = new CommandLineApplication(false)
{
Name = "dotnet test",
FullName = ".NET Test Driver",
Description = "Test Driver for the .NET Platform"
};
AddDotnetTestParameters();
}
public void Parse(string[] args)
{
_app.OnExecute(() =>
{
// Locate the project and get the name and full path
ProjectPath = _projectPath.Value;
if (string.IsNullOrEmpty(ProjectPath))
{
ProjectPath = Directory.GetCurrentDirectory();
}
if (_parentProcessIdOption.HasValue())
{
int processId;
if (!TryParse(_parentProcessIdOption.Value(), out processId))
{
throw new InvalidOperationException(
$"Invalid process id '{_parentProcessIdOption.Value()}'. Process id must be an integer.");
}
ParentProcessId = processId;
}
if (_portOption.HasValue())
{
int port;
if (!TryParse(_portOption.Value(), out port))
{
throw new InvalidOperationException($"{_portOption.Value()} is not a valid port number.");
}
Port = port;
}
if (_frameworkOption.HasValue())
{
Framework = NuGetFramework.Parse(_frameworkOption.Value());
}
Output = _outputOption.Value();
BuildBasePath = _buildBasePath.Value();
Config = _configurationOption.Value() ?? Constants.DefaultConfiguration;
Runtime = _runtimeOption.Value();
RemainingArguments = _app.RemainingArguments;
return 0;
});
_app.Execute(args);
}
private void AddDotnetTestParameters()
{
_app.HelpOption("-?|-h|--help");
_parentProcessIdOption = _app.Option(
"--parentProcessId",
"Used by IDEs to specify their process ID. Test will exit if the parent process does.",
CommandOptionType.SingleValue);
_portOption = _app.Option(
"--port",
"Used by IDEs to specify a port number to listen for a connection.",
CommandOptionType.SingleValue);
_configurationOption = _app.Option(
"-c|--configuration <CONFIGURATION>",
"Configuration under which to build",
CommandOptionType.SingleValue);
_outputOption = _app.Option(
"-o|--output <OUTPUT_DIR>",
"Directory in which to find the binaries to be run",
CommandOptionType.SingleValue);
_buildBasePath = _app.Option(
"-b|--build-base-path <OUTPUT_DIR>",
"Directory in which to find temporary outputs",
CommandOptionType.SingleValue);
_frameworkOption = _app.Option(
"-f|--framework <FRAMEWORK>",
"Looks for test binaries for a specific framework",
CommandOptionType.SingleValue);
_runtimeOption = _app.Option(
"-r|--runtime <RUNTIME_IDENTIFIER>",
"Look for test binaries for a for the specified runtime",
CommandOptionType.SingleValue);
_projectPath = _app.Argument(
"<PROJECT>",
"The project to test, defaults to the current directory. Can be a path to a project.json or a project directory.");
}
}
}

View file

@ -0,0 +1,13 @@
// 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 Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.Tools.Test
{
public interface IDotnetTestRunner
{
int RunTests(ProjectContext projectContext, DotnetTestParams dotnetTestParams);
}
}

View file

@ -18,187 +18,44 @@ namespace Microsoft.DotNet.Tools.Test
{
DebugHelper.HandleDebugSwitch(ref args);
var app = new CommandLineApplication(false)
{
Name = "dotnet test",
FullName = ".NET Test Driver",
Description = "Test Driver for the .NET Platform"
};
var dotnetTestParams = new DotnetTestParams();
app.HelpOption("-?|-h|--help");
var parentProcessIdOption = app.Option("--parentProcessId", "Used by IDEs to specify their process ID. Test will exit if the parent process does.", CommandOptionType.SingleValue);
var portOption = app.Option("--port", "Used by IDEs to specify a port number to listen for a connection.", CommandOptionType.SingleValue);
var configurationOption = app.Option("-c|--configuration <CONFIGURATION>", "Configuration under which to build", CommandOptionType.SingleValue);
var output = app.Option("-o|--output <OUTPUT_DIR>", "Directory in which to find the binaries to be run", CommandOptionType.SingleValue);
var projectPath = app.Argument("<PROJECT>", "The project to test, defaults to the current directory. Can be a path to a project.json or a project directory.");
app.OnExecute(() =>
{
try
{
// Register for parent process's exit event
if (parentProcessIdOption.HasValue())
{
int processId;
if (!Int32.TryParse(parentProcessIdOption.Value(), out processId))
{
throw new InvalidOperationException($"Invalid process id '{parentProcessIdOption.Value()}'. Process id must be an integer.");
}
RegisterForParentProcessExit(processId);
}
var projectContexts = CreateProjectContexts(projectPath.Value);
var projectContext = projectContexts.First();
var testRunner = projectContext.ProjectFile.TestRunner;
var configuration = configurationOption.Value() ?? Constants.DefaultConfiguration;
var outputPath = output.Value();
if (portOption.HasValue())
{
int port;
if (!Int32.TryParse(portOption.Value(), out port))
{
throw new InvalidOperationException($"{portOption.Value()} is not a valid port number.");
}
return RunDesignTime(port, projectContext, testRunner, configuration, outputPath);
}
else
{
return RunConsole(projectContext, app, testRunner, configuration, outputPath);
}
}
catch (InvalidOperationException ex)
{
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, ex.ToString());
return -1;
}
catch (Exception ex)
{
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, ex.ToString());
return -2;
}
});
return app.Execute(args);
}
private static int RunConsole(
ProjectContext projectContext,
CommandLineApplication app,
string testRunner,
string configuration,
string outputPath)
{
var commandArgs = new List<string> { GetAssemblyUnderTest(projectContext, configuration, outputPath) };
commandArgs.AddRange(app.RemainingArguments);
var commandFactory =
new ProjectDependenciesCommandFactory(
projectContext.TargetFramework,
configuration,
outputPath,
projectContext.ProjectDirectory);
return commandFactory.Create(
$"dotnet-{GetCommandName(testRunner)}",
commandArgs,
projectContext.TargetFramework,
configuration)
.ForwardStdErr()
.ForwardStdOut()
.Execute()
.ExitCode;
}
private static string GetAssemblyUnderTest(ProjectContext projectContext, string configuration, string outputPath)
{
var assemblyUnderTest =
projectContext.GetOutputPaths(configuration, outputPath: outputPath).CompilationFiles.Assembly;
if (!string.IsNullOrEmpty(outputPath))
{
assemblyUnderTest =
projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.Assembly;
}
return assemblyUnderTest;
}
private static int RunDesignTime(
int port,
ProjectContext
projectContext,
string testRunner,
string configuration,
string outputPath)
{
Console.WriteLine("Listening on port {0}", port);
HandleDesignTimeMessages(projectContext, testRunner, port, configuration, outputPath);
return 0;
}
private static void HandleDesignTimeMessages(
ProjectContext projectContext,
string testRunner,
int port,
string configuration,
string outputPath)
{
var reportingChannelFactory = new ReportingChannelFactory();
var adapterChannel = reportingChannelFactory.CreateAdapterChannel(port);
dotnetTestParams.Parse(args);
try
{
var assemblyUnderTest = GetAssemblyUnderTest(projectContext, configuration, outputPath);
var messages = new TestMessagesCollection();
using (var dotnetTest = new DotnetTest(messages, assemblyUnderTest))
// Register for parent process's exit event
if (dotnetTestParams.ParentProcessId.HasValue)
{
var commandFactory =
new ProjectDependenciesCommandFactory(
projectContext.TargetFramework,
configuration,
outputPath,
projectContext.ProjectDirectory);
var testRunnerFactory = new TestRunnerFactory(GetCommandName(testRunner), commandFactory);
dotnetTest
.AddNonSpecificMessageHandlers(messages, adapterChannel)
.AddTestDiscoveryMessageHandlers(adapterChannel, reportingChannelFactory, testRunnerFactory)
.AddTestRunMessageHandlers(adapterChannel, reportingChannelFactory, testRunnerFactory)
.AddTestRunnnersMessageHandlers(adapterChannel, reportingChannelFactory);
dotnetTest.StartListeningTo(adapterChannel);
adapterChannel.Connect();
dotnetTest.StartHandlingMessages();
RegisterForParentProcessExit(dotnetTestParams.ParentProcessId.Value);
}
var projectContexts = CreateProjectContexts(dotnetTestParams.ProjectPath);
var projectContext = projectContexts.First();
var testRunner = projectContext.ProjectFile.TestRunner;
IDotnetTestRunner dotnetTestRunner = new ConsoleTestRunner();
if (dotnetTestParams.Port.HasValue)
{
dotnetTestRunner = new DesignTimeRunner();
}
return dotnetTestRunner.RunTests(projectContext, dotnetTestParams);
}
catch (InvalidOperationException ex)
{
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, ex.ToString());
return -1;
}
catch (Exception ex)
{
adapterChannel.SendError(ex);
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, ex.ToString());
return -2;
}
}
private static string GetCommandName(string testRunner)
{
return $"test-{testRunner}";
}
private static void RegisterForParentProcessExit(int id)
{
var parentProcess = Process.GetProcesses().FirstOrDefault(p => p.Id == id);

View file

@ -0,0 +1,183 @@
// 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 FluentAssertions;
using Microsoft.DotNet.Tools.Test;
using Xunit;
namespace Microsoft.Dotnet.Tools.Test.Tests
{
public class GivenThatWeWantToParseArgumentsForDotnetTest
{
private const string ProjectJson = "project.json";
private const string Framework = "netstandardapp1.5";
private const string Output = "some output";
private const string BuildBasePath = "some build base path";
private const string Config = "some config";
private const string Runtime = "some runtime";
private const int ParentProcessId = 1010;
private const int Port = 2314;
private DotnetTestParams _dotnetTestFullParams;
private DotnetTestParams _emptyDotnetTestParams;
public GivenThatWeWantToParseArgumentsForDotnetTest()
{
_dotnetTestFullParams = new DotnetTestParams();
_emptyDotnetTestParams = new DotnetTestParams();
_dotnetTestFullParams.Parse(new[]
{
ProjectJson,
"--parentProcessId", ParentProcessId.ToString(),
"--port", Port.ToString(),
"--framework", Framework,
"--output", Output,
"--build-base-path", BuildBasePath,
"--configuration", Config,
"--runtime", Runtime,
"--additional-parameters", "additional-parameter-value"
});
_emptyDotnetTestParams.Parse(new string[] { });
}
[Fact]
public void It_sets_the_project_path_current_folder_if_one_is_not_passed_in()
{
_emptyDotnetTestParams.ProjectPath.Should().Be(Directory.GetCurrentDirectory());
}
[Fact]
public void It_sets_the_project_path_to_the_passed_value()
{
_dotnetTestFullParams.ProjectPath.Should().Be(ProjectJson);
}
[Fact]
public void It_throws_InvalidOperationException_if_an_invalid_parent_process_id_is_passed_to_it()
{
var dotnetTestParams = new DotnetTestParams();
const string invalidParentProcessId = "daddy";
Action action = () => dotnetTestParams.Parse(new [] { "--parentProcessId", invalidParentProcessId });
action
.ShouldThrow<InvalidOperationException>()
.WithMessage($"Invalid process id '{invalidParentProcessId}'. Process id must be an integer.");
}
[Fact]
public void It_converts_the_parent_process_id_to_int_when_a_valid_one_is_passed()
{
_dotnetTestFullParams.ParentProcessId.Should().Be(ParentProcessId);
}
[Fact]
public void It_does_not_set_parent_process_id_when_one_is_not_passed()
{
_emptyDotnetTestParams.ParentProcessId.Should().NotHaveValue();
}
[Fact]
public void It_throws_InvalidOperationException_if_an_invalid_port_is_passed_to_it()
{
var dotnetTestParams = new DotnetTestParams();
const string invalidPort = "door";
Action action = () => dotnetTestParams.Parse(new[] { "--port", invalidPort });
action
.ShouldThrow<InvalidOperationException>()
.WithMessage($"{invalidPort} is not a valid port number.");
}
[Fact]
public void It_converts_the_port_to_int_when_a_valid_one_is_passed()
{
_dotnetTestFullParams.Port.Should().Be(Port);
}
[Fact]
public void It_does_not_set_port_when_one_is_not_passed()
{
_emptyDotnetTestParams.Port.Should().NotHaveValue();
}
[Fact]
public void It_converts_the_framework_to_NugetFramework()
{
_dotnetTestFullParams.Framework.DotNetFrameworkName.Should().Be(".NETStandardApp,Version=v1.5");
}
[Fact]
public void It_does_not_set_framework_when_one_is_not_passed()
{
_emptyDotnetTestParams.Framework.Should().BeNull();
}
[Fact]
public void It_sets_the_framework_to_unsupported_when_an_invalid_framework_is_passed_in()
{
var dotnetTestParams = new DotnetTestParams();
dotnetTestParams.Parse(new[] { "--framework", "farm work" });
dotnetTestParams.Framework.DotNetFrameworkName.Should().Be("Unsupported,Version=v0.0");
}
[Fact]
public void It_sets_Output_when_one_is_passed_in()
{
_dotnetTestFullParams.Output.Should().Be(Output);
}
[Fact]
public void It_leaves_Output_null_when_one_is_not_passed_in()
{
_emptyDotnetTestParams.Output.Should().BeNull();
}
[Fact]
public void It_sets_BuildBasePath_when_one_is_passed_in()
{
_dotnetTestFullParams.BuildBasePath.Should().Be(BuildBasePath);
}
[Fact]
public void It_leaves_BuildBasePath_null_when_one_is_not_passed_in()
{
_emptyDotnetTestParams.BuildBasePath.Should().BeNull();
}
[Fact]
public void It_sets_Config_to_passed_in_value()
{
_dotnetTestFullParams.Config.Should().Be(Config);
}
[Fact]
public void It_sets_Config_to_Debug_when_one_is_not_passed_in()
{
_emptyDotnetTestParams.Config.Should().Be("Debug");
}
[Fact]
public void It_sets_Runtime_when_one_is_passed_in()
{
_dotnetTestFullParams.Runtime.Should().Be(Runtime);
}
[Fact]
public void It_leaves_Runtime_null_when_one_is_not_passed_in()
{
_emptyDotnetTestParams.Runtime.Should().BeNull();
}
[Fact]
public void It_sets_any_remaining_params_to_RemainingArguments()
{
_dotnetTestFullParams.RemainingArguments.ShouldBeEquivalentTo(
new [] { "--additional-parameters", "additional-parameter-value" });
}
}
}