2016-10-04 10:07:36 +00:00
|
|
|
|
// 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 Microsoft.DotNet.Cli.CommandLine;
|
|
|
|
|
using Microsoft.DotNet.Cli.Utils;
|
|
|
|
|
using Microsoft.DotNet.Tools.MSBuild;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
2016-10-28 01:46:43 +00:00
|
|
|
|
namespace Microsoft.DotNet.Tools.Test
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-28 01:46:43 +00:00
|
|
|
|
public class TestCommand
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
|
|
|
|
public static int Run(string[] args)
|
|
|
|
|
{
|
|
|
|
|
DebugHelper.HandleDebugSwitch(ref args);
|
|
|
|
|
|
|
|
|
|
var cmd = new CommandLineApplication(throwOnUnexpectedArg: false)
|
|
|
|
|
{
|
2016-10-28 01:46:43 +00:00
|
|
|
|
Name = "dotnet test",
|
2016-12-02 01:15:25 +00:00
|
|
|
|
FullName = LocalizableStrings.AppFullName,
|
|
|
|
|
Description = LocalizableStrings.AppDescription
|
2016-10-04 10:07:36 +00:00
|
|
|
|
};
|
|
|
|
|
|
2016-11-24 08:48:54 +00:00
|
|
|
|
cmd.AllowArgumentSeparator = true;
|
|
|
|
|
cmd.ArgumentSeparatorHelpText = HelpMessageStrings.MSBuildAdditionalArgsHelpText;
|
2016-10-04 10:07:36 +00:00
|
|
|
|
cmd.HelpOption("-h|--help");
|
|
|
|
|
|
|
|
|
|
var argRoot = cmd.Argument(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"<{LocalizableStrings.CmdArgProject}>",
|
|
|
|
|
LocalizableStrings.CmdArgDescription,
|
2016-10-04 10:07:36 +00:00
|
|
|
|
multipleValues: false);
|
|
|
|
|
|
|
|
|
|
var settingOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-s|--settings <{LocalizableStrings.CmdSettingsFile}>",
|
|
|
|
|
LocalizableStrings.CmdSettingsDescription,
|
2016-10-04 10:07:36 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
var listTestsOption = cmd.Option(
|
|
|
|
|
"-lt|--listTests",
|
2016-12-02 01:15:25 +00:00
|
|
|
|
LocalizableStrings.CmdListTestsDescription,
|
2016-10-05 23:03:54 +00:00
|
|
|
|
CommandOptionType.NoValue);
|
|
|
|
|
|
|
|
|
|
var testCaseFilterOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-tcf|--testCaseFilter <{LocalizableStrings.CmdTestCaseFilterExpression}>",
|
|
|
|
|
LocalizableStrings.CmdTestCaseFilterDescription,
|
2016-10-04 10:07:36 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
|
|
|
|
|
|
|
|
|
var testAdapterPathOption = cmd.Option(
|
2016-10-12 09:50:07 +00:00
|
|
|
|
"-tap|--testAdapterPath",
|
2016-12-02 01:15:25 +00:00
|
|
|
|
LocalizableStrings.CmdTestAdapterPathDescription,
|
2016-10-04 10:07:36 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
|
|
|
|
|
|
|
|
|
var loggerOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-l|--logger <{LocalizableStrings.CmdLoggerOption}>",
|
|
|
|
|
LocalizableStrings.CmdLoggerDescription,
|
2016-10-05 23:03:54 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
2016-10-04 10:07:36 +00:00
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
var configurationOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-c|--configuration <{LocalizableStrings.CmdConfiguration}>",
|
|
|
|
|
LocalizableStrings.CmdConfigDescription,
|
2016-10-05 23:03:54 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
2016-10-04 10:07:36 +00:00
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
var frameworkOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-f|--framework <{LocalizableStrings.CmdFramework}>",
|
|
|
|
|
LocalizableStrings.CmdFrameworkDescription,
|
2016-10-04 10:07:36 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
var outputOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-o|--output <{LocalizableStrings.CmdOutputDir}>",
|
|
|
|
|
LocalizableStrings.CmdOutputDescription,
|
2016-10-04 10:07:36 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
|
|
|
|
|
2016-10-28 14:45:38 +00:00
|
|
|
|
var diagOption = cmd.Option(
|
2016-12-02 01:15:25 +00:00
|
|
|
|
$"-d|--diag <{LocalizableStrings.CmdPathToLogFile}>",
|
|
|
|
|
LocalizableStrings.CmdPathTologFileDescription,
|
2016-10-28 14:45:38 +00:00
|
|
|
|
CommandOptionType.SingleValue);
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
var noBuildtOption = cmd.Option(
|
|
|
|
|
"--noBuild",
|
2016-12-02 01:15:25 +00:00
|
|
|
|
LocalizableStrings.CmdNoBuildDescription,
|
2016-10-05 23:03:54 +00:00
|
|
|
|
CommandOptionType.NoValue);
|
|
|
|
|
|
2016-11-15 19:56:39 +00:00
|
|
|
|
CommandOption verbosityOption = MSBuildForwardingApp.AddVerbosityOption(cmd);
|
|
|
|
|
|
2016-10-04 10:07:36 +00:00
|
|
|
|
cmd.OnExecute(() =>
|
|
|
|
|
{
|
|
|
|
|
var msbuildArgs = new List<string>()
|
|
|
|
|
{
|
|
|
|
|
"/t:VSTest"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
msbuildArgs.Add("/nologo");
|
|
|
|
|
|
|
|
|
|
if (settingOption.HasValue())
|
|
|
|
|
{
|
|
|
|
|
msbuildArgs.Add($"/p:VSTestSetting={settingOption.Value()}");
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (listTestsOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:VSTestListTests=true");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (testCaseFilterOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:VSTestTestCaseFilter={testCaseFilterOption.Value()}");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (testAdapterPathOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:VSTestTestAdapterPath={testAdapterPathOption.Value()}");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (loggerOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:VSTestLogger={string.Join(";", loggerOption.Values)}");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (configurationOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:Configuration={configurationOption.Value()}");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (frameworkOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:TargetFramework={frameworkOption.Value()}");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (outputOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:OutputPath={outputOption.Value()}");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-28 14:45:38 +00:00
|
|
|
|
if (diagOption.HasValue())
|
|
|
|
|
{
|
|
|
|
|
msbuildArgs.Add($"/p:VSTestDiag={diagOption.Value()}");
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (noBuildtOption.HasValue())
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
2016-10-05 23:03:54 +00:00
|
|
|
|
msbuildArgs.Add($"/p:VSTestNoBuild=true");
|
2016-10-04 10:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-15 19:56:39 +00:00
|
|
|
|
if (verbosityOption.HasValue())
|
|
|
|
|
{
|
|
|
|
|
msbuildArgs.Add($"/verbosity:{verbosityOption.Value()}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
msbuildArgs.Add("/verbosity:quiet");
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-04 10:07:36 +00:00
|
|
|
|
string defaultproject = GetSingleTestProjectToRunTestIfNotProvided(argRoot.Value, cmd.RemainingArguments);
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrEmpty(defaultproject))
|
|
|
|
|
{
|
|
|
|
|
msbuildArgs.Add(defaultproject);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
if (!string.IsNullOrEmpty(argRoot.Value))
|
|
|
|
|
{
|
|
|
|
|
msbuildArgs.Add(argRoot.Value);
|
|
|
|
|
}
|
2016-10-04 10:07:36 +00:00
|
|
|
|
|
|
|
|
|
// Add remaining arguments that the parser did not understand,
|
|
|
|
|
msbuildArgs.AddRange(cmd.RemainingArguments);
|
|
|
|
|
|
|
|
|
|
return new MSBuildForwardingApp(msbuildArgs).Execute();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return cmd.Execute(args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetSingleTestProjectToRunTestIfNotProvided(string args, List<string> remainingArguments)
|
|
|
|
|
{
|
|
|
|
|
string result = string.Empty;
|
2016-10-05 23:03:54 +00:00
|
|
|
|
int projectFound = NumberOfTestProjectInRemainingArgs(remainingArguments) + NumberOfTestProjectInArgsRoot(args);
|
2016-10-04 10:07:36 +00:00
|
|
|
|
|
|
|
|
|
if (projectFound > 1)
|
|
|
|
|
{
|
|
|
|
|
throw new GracefulException(
|
|
|
|
|
$"Specify a single project file to run tests from.");
|
|
|
|
|
}
|
|
|
|
|
else if (projectFound == 0)
|
|
|
|
|
{
|
|
|
|
|
result = GetDefaultTestProject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 23:03:54 +00:00
|
|
|
|
private static int NumberOfTestProjectInArgsRoot(string args)
|
2016-10-04 10:07:36 +00:00
|
|
|
|
{
|
|
|
|
|
Regex pattern = new Regex(@"^.*\..*proj$");
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(args))
|
|
|
|
|
{
|
|
|
|
|
return pattern.IsMatch(args) ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int NumberOfTestProjectInRemainingArgs(List<string> remainingArguments)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
if (remainingArguments.Count != 0)
|
|
|
|
|
{
|
|
|
|
|
Regex pattern = new Regex(@"^.*\..*proj$");
|
|
|
|
|
|
|
|
|
|
foreach (var x in remainingArguments)
|
|
|
|
|
{
|
|
|
|
|
if (pattern.IsMatch(x))
|
|
|
|
|
{
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetDefaultTestProject()
|
|
|
|
|
{
|
|
|
|
|
string directory = Directory.GetCurrentDirectory();
|
|
|
|
|
string[] projectFiles = Directory.GetFiles(directory, "*.*proj");
|
|
|
|
|
|
|
|
|
|
if (projectFiles.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
throw new GracefulException(
|
|
|
|
|
$"Couldn't find a project to run test from. Ensure a project exists in {directory}." + Environment.NewLine +
|
|
|
|
|
"Or pass the path to the project");
|
|
|
|
|
}
|
|
|
|
|
else if (projectFiles.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
throw new GracefulException(
|
|
|
|
|
$"Specify which project file to use because this '{directory}' contains more than one project file.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return projectFiles[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|