2015-11-30 16:24:03 -08: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.Diagnostics;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using Microsoft.DotNet.Cli.Utils;
|
2016-04-28 16:30:32 -07:00
|
|
|
using Microsoft.DotNet.InternalAbstractions;
|
2015-11-30 16:24:03 -08:00
|
|
|
using Microsoft.DotNet.ProjectModel;
|
|
|
|
|
|
|
|
namespace Microsoft.DotNet.Tools.Test
|
|
|
|
{
|
2016-01-30 21:47:50 -08:00
|
|
|
public class TestCommand
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
2016-03-25 20:17:15 -07:00
|
|
|
private readonly IDotnetTestRunnerFactory _dotnetTestRunnerFactory;
|
|
|
|
|
|
|
|
public TestCommand(IDotnetTestRunnerFactory testRunnerFactory)
|
|
|
|
{
|
|
|
|
_dotnetTestRunnerFactory = testRunnerFactory;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int DoRun(string[] args)
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
|
|
|
DebugHelper.HandleDebugSwitch(ref args);
|
|
|
|
|
2016-03-11 15:30:37 -08:00
|
|
|
var dotnetTestParams = new DotnetTestParams();
|
2015-11-30 16:24:03 -08:00
|
|
|
|
2016-03-11 15:30:37 -08:00
|
|
|
dotnetTestParams.Parse(args);
|
2016-01-26 06:39:13 -08:00
|
|
|
|
2016-03-11 15:30:37 -08:00
|
|
|
try
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
2016-03-25 20:17:15 -07:00
|
|
|
if (dotnetTestParams.Help)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-11 15:30:37 -08:00
|
|
|
// Register for parent process's exit event
|
|
|
|
if (dotnetTestParams.ParentProcessId.HasValue)
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
2016-03-11 15:30:37 -08:00
|
|
|
RegisterForParentProcessExit(dotnetTestParams.ParentProcessId.Value);
|
|
|
|
}
|
2016-01-26 06:39:13 -08:00
|
|
|
|
2016-04-14 15:59:11 -07:00
|
|
|
var projectPath = GetProjectPath(dotnetTestParams.ProjectPath);
|
|
|
|
var runtimeIdentifiers = !string.IsNullOrEmpty(dotnetTestParams.Runtime) ?
|
|
|
|
new[] { dotnetTestParams.Runtime } :
|
2016-04-28 16:30:32 -07:00
|
|
|
RuntimeEnvironmentRidExtensions.GetAllCandidateRuntimeIdentifiers();
|
2016-04-14 15:59:11 -07:00
|
|
|
var exitCode = 0;
|
|
|
|
|
2016-04-22 15:01:56 -07:00
|
|
|
// Create a workspace
|
2016-05-02 11:32:24 -07:00
|
|
|
var workspace = new BuildWorkspace(ProjectReaderSettings.ReadFromEnvironment());
|
2016-04-22 15:01:56 -07:00
|
|
|
|
2016-04-14 15:59:11 -07:00
|
|
|
if (dotnetTestParams.Framework != null)
|
|
|
|
{
|
2016-05-02 11:32:24 -07:00
|
|
|
var projectContext = workspace.GetProjectContext(projectPath, dotnetTestParams.Framework);
|
|
|
|
if (projectContext == null)
|
|
|
|
{
|
|
|
|
Reporter.Error.WriteLine($"Project '{projectPath}' does not support framework: {dotnetTestParams.UnparsedFramework}");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
projectContext = workspace.GetRuntimeContext(projectContext, runtimeIdentifiers);
|
|
|
|
|
|
|
|
exitCode = RunTest(projectContext, dotnetTestParams, workspace);
|
2016-04-14 15:59:11 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var summary = new Summary();
|
2016-04-29 15:46:16 -05:00
|
|
|
var projectContexts = workspace.GetProjectContextCollection(projectPath)
|
|
|
|
.EnsureValid(projectPath)
|
|
|
|
.FrameworkOnlyContexts
|
2016-04-14 15:59:11 -07:00
|
|
|
.Select(c => workspace.GetRuntimeContext(c, runtimeIdentifiers))
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
// Execute for all TFMs the project targets.
|
|
|
|
foreach (var projectContext in projectContexts)
|
|
|
|
{
|
2016-05-02 11:32:24 -07:00
|
|
|
var result = RunTest(projectContext, dotnetTestParams, workspace);
|
2016-04-14 15:59:11 -07:00
|
|
|
if (result == 0)
|
|
|
|
{
|
|
|
|
summary.Passed++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
summary.Failed++;
|
|
|
|
if (exitCode == 0)
|
|
|
|
{
|
|
|
|
// If tests fail in more than one TFM, we'll have it use the result of the first one
|
|
|
|
// as the exit code.
|
|
|
|
exitCode = result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
summary.Print();
|
|
|
|
}
|
2016-03-01 21:15:07 -08:00
|
|
|
|
2016-04-14 15:59:11 -07:00
|
|
|
return exitCode;
|
2016-03-01 21:15:07 -08:00
|
|
|
}
|
2016-03-11 15:30:37 -08:00
|
|
|
catch (InvalidOperationException ex)
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
2016-03-11 15:30:37 -08:00
|
|
|
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, ex.ToString());
|
|
|
|
return -1;
|
2015-11-30 16:24:03 -08:00
|
|
|
}
|
2016-04-29 15:46:16 -05:00
|
|
|
catch (Exception ex) when (!(ex is GracefulException))
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
2016-03-11 15:30:37 -08:00
|
|
|
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, ex.ToString());
|
|
|
|
return -2;
|
2015-11-30 16:24:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-25 20:17:15 -07:00
|
|
|
public static int Run(string[] args)
|
|
|
|
{
|
|
|
|
var testCommand = new TestCommand(new DotnetTestRunnerFactory());
|
|
|
|
|
|
|
|
return testCommand.DoRun(args);
|
|
|
|
}
|
|
|
|
|
2015-11-30 16:24:03 -08:00
|
|
|
private static void RegisterForParentProcessExit(int id)
|
|
|
|
{
|
|
|
|
var parentProcess = Process.GetProcesses().FirstOrDefault(p => p.Id == id);
|
|
|
|
|
|
|
|
if (parentProcess != null)
|
|
|
|
{
|
|
|
|
parentProcess.EnableRaisingEvents = true;
|
|
|
|
parentProcess.Exited += (sender, eventArgs) =>
|
|
|
|
{
|
|
|
|
TestHostTracing.Source.TraceEvent(
|
|
|
|
TraceEventType.Information,
|
|
|
|
0,
|
|
|
|
"Killing the current process as parent process has exited.");
|
|
|
|
|
|
|
|
Process.GetCurrentProcess().Kill();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TestHostTracing.Source.TraceEvent(
|
|
|
|
TraceEventType.Information,
|
|
|
|
0,
|
|
|
|
"Failed to register for parent process's exit event. " +
|
|
|
|
$"Parent process with id '{id}' was not found.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-02 11:32:24 -07:00
|
|
|
private int RunTest(ProjectContext projectContext, DotnetTestParams dotnetTestParams, BuildWorkspace workspace)
|
2016-04-14 15:59:11 -07:00
|
|
|
{
|
|
|
|
var testRunner = projectContext.ProjectFile.TestRunner;
|
|
|
|
var dotnetTestRunner = _dotnetTestRunnerFactory.Create(dotnetTestParams.Port);
|
2016-05-02 11:32:24 -07:00
|
|
|
return dotnetTestRunner.RunTests(projectContext, dotnetTestParams, workspace);
|
2016-04-14 15:59:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private static string GetProjectPath(string projectPath)
|
2015-11-30 16:24:03 -08:00
|
|
|
{
|
|
|
|
projectPath = projectPath ?? Directory.GetCurrentDirectory();
|
|
|
|
|
|
|
|
if (!projectPath.EndsWith(Project.FileName))
|
|
|
|
{
|
|
|
|
projectPath = Path.Combine(projectPath, Project.FileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!File.Exists(projectPath))
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException($"{projectPath} does not exist.");
|
|
|
|
}
|
|
|
|
|
2016-04-14 15:59:11 -07:00
|
|
|
return projectPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Summary
|
|
|
|
{
|
|
|
|
public int Passed { get; set; }
|
2016-04-18 15:28:01 -07:00
|
|
|
|
2016-04-14 15:59:11 -07:00
|
|
|
public int Failed { get; set; }
|
|
|
|
|
|
|
|
public int Total => Passed + Failed;
|
|
|
|
|
|
|
|
public void Print()
|
|
|
|
{
|
|
|
|
var summaryMessage = $"SUMMARY: Total: {Total} targets, Passed: {Passed}, Failed: {Failed}.";
|
|
|
|
if (Failed > 0)
|
|
|
|
{
|
|
|
|
Reporter.Error.WriteLine(summaryMessage.Red());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Reporter.Output.WriteLine(summaryMessage);
|
|
|
|
}
|
|
|
|
}
|
2015-11-30 16:24:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|