Merge pull request #344 from piotrpMSFT/piotrpMSFT/issue342/dotnet-test

dotnet-test command
fixes issue #344 and #359 
@brthor, note issue #359. Powershell was interpreting particular Env Var names as patterns due to use of -Path. Changing this to -LiteralPath was required since use of patterns was unintended.
This commit is contained in:
Piotr Puszkiewicz 2015-11-30 22:19:52 -08:00
commit d63bd8e50d
71 changed files with 3690 additions and 45 deletions

View file

@ -51,6 +51,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.ProjectMod
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Runtime", "src\Microsoft.DotNet.Runtime\Microsoft.DotNet.Runtime.xproj", "{DB29F219-DC92-4AF7-A2EE-E89FFBB3F5F0}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Testing.Abstractions", "src\Microsoft.Extensions.Testing.Abstractions\Microsoft.Extensions.Testing.Abstractions.xproj", "{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Tools.Test", "src\Microsoft.DotNet.Tools.Test\Microsoft.DotNet.Tools.Test.xproj", "{F003F228-2AE2-4E9D-877B-93EB773B5061}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -355,6 +359,38 @@ Global
{DB29F219-DC92-4AF7-A2EE-E89FFBB3F5F0}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{DB29F219-DC92-4AF7-A2EE-E89FFBB3F5F0}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{DB29F219-DC92-4AF7-A2EE-E89FFBB3F5F0}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Debug|x64.ActiveCfg = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Debug|x64.Build.0 = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Release|Any CPU.Build.0 = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Release|x64.ActiveCfg = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.Release|x64.Build.0 = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Debug|x64.ActiveCfg = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Debug|x64.Build.0 = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Release|Any CPU.Build.0 = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Release|x64.ActiveCfg = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.Release|x64.Build.0 = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{F003F228-2AE2-4E9D-877B-93EB773B5061}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -380,5 +416,7 @@ Global
{0F480791-4BA0-44E3-8CC4-C71656664175} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{BD7833F8-3209-4682-BF75-B4BCA883E279} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{DB29F219-DC92-4AF7-A2EE-E89FFBB3F5F0} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{F003F228-2AE2-4E9D-877B-93EB773B5061} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
EndGlobalSection
EndGlobal

View file

@ -40,12 +40,13 @@ compilers, package managers and other utilities that developers need.",
"bin/dotnet" : "usr/bin/dotnet",
"bin/dotnet-compile" : "usr/bin/dotnet-compile",
"bin/dotnet-compile-csc" : "usr/bin/dotnet-compile-csc",
"bin/dotnet-compile-native" : "/usr/bin/dotnet-compile-native",
"bin/dotnet-init":"usr/bin/dotnet-init",
"bin/dotnet-publish" : "usr/bin/dotnet-publish",
"bin/dotnet-repl" : "usr/bin/dotnet-repl",
"bin/dotnet-repl-csi" : "usr/bin/dotnet-repl-csi",
"bin/dotnet-restore" : "usr/bin/dotnet-restore",
"bin/dotnet-compile-native" : "/usr/bin/dotnet-compile-native",
"bin/resgen" : "usr/bin/resgen",
"bin/dotnet-init":"usr/bin/dotnet-init"
"bin/dotnet-test" : "usr/bin/dotnet-test",
"bin/resgen" : "usr/bin/resgen"
}
}

View file

@ -15,14 +15,15 @@ $Projects = @(
"Microsoft.DotNet.Cli",
"Microsoft.DotNet.Tools.Compiler",
"Microsoft.DotNet.Tools.Compiler.Csc",
"Microsoft.DotNet.Tools.Compiler.Native",
"Microsoft.DotNet.Tools.Init",
"Microsoft.DotNet.Tools.Pack",
"Microsoft.DotNet.Tools.Publish",
"Microsoft.DotNet.Tools.Repl",
"Microsoft.DotNet.Tools.Repl.Csi",
"Microsoft.DotNet.Tools.Resgen",
"Microsoft.DotNet.Tools.Run",
"Microsoft.DotNet.Tools.Init",
"Microsoft.DotNet.Tools.Compiler.Native"
"Microsoft.DotNet.Tools.Test"
)
$BinariesForCoreHost = @(

View file

@ -27,14 +27,15 @@ PROJECTS=( \
Microsoft.DotNet.Cli \
Microsoft.DotNet.Tools.Compiler \
Microsoft.DotNet.Tools.Compiler.Csc \
Microsoft.DotNet.Tools.Publish \
Microsoft.DotNet.Tools.Compiler.Native \
Microsoft.DotNet.Tools.Init \
Microsoft.DotNet.Tools.Pack \
Microsoft.DotNet.Tools.Publish \
Microsoft.DotNet.Tools.Repl \
Microsoft.DotNet.Tools.Repl.Csi \
Microsoft.DotNet.Tools.Resgen \
Microsoft.DotNet.Tools.Run \
Microsoft.DotNet.Tools.Init \
Microsoft.DotNet.Tools.Compiler.Native \
Microsoft.DotNet.Tools.Test \
)
BINARIES_FOR_COREHOST=( \

View file

@ -25,7 +25,7 @@ pushd "$env:VS140COMNTOOLS\..\..\VC"
cmd /c "vcvarsall.bat x64&set" |
foreach {
if ($_ -match "=") {
$v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])"
$v = $_.split("="); set-item -force -literalpath "ENV:\$($v[0])" -value "$($v[1])"
}
}
popd

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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;

View file

@ -201,6 +201,9 @@ namespace Microsoft.DotNet.Cli.Utils
Reporter.Verbose.WriteLine($"> {FormatProcessInfo(_process.StartInfo)}".White());
#endif
_process.Start();
Reporter.Verbose.WriteLine($"Process ID: {_process.Id}");
_process.BeginOutputReadLine();
_process.BeginErrorReadLine();

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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.
namespace Microsoft.DotNet.Cli.Utils.CommandParsing
{

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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.
namespace Microsoft.DotNet.Cli.Utils.CommandParsing
{

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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 System.Linq;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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.
namespace Microsoft.DotNet.Cli.Utils.CommandParsing
{

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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;

View file

@ -1,5 +1,5 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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.
namespace Microsoft.DotNet.Cli.Utils.CommandParsing
{

View file

@ -1,17 +0,0 @@
using System.Linq;
namespace System.Collections.Generic
{
internal static class DictionaryExtensions
{
public static IEnumerable<V> GetOrEmpty<K, V>(this IDictionary<K, IEnumerable<V>> self, K key)
{
IEnumerable<V> val;
if (!self.TryGetValue(key, out val))
{
return Enumerable.Empty<V>();
}
return val;
}
}
}

View file

@ -76,6 +76,8 @@ namespace Microsoft.DotNet.ProjectModel
public string CompilerName { get; set; }
public string TestRunner { get; set; }
public ProjectFilesCollection Files { get; set; }
public IDictionary<string, string> Commands { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

View file

@ -99,5 +99,28 @@ namespace Microsoft.DotNet.ProjectModel
.Build();
}
}
public string GetAssemblyPath(string buildConfiguration)
{
return Path.Combine(
GetOutputDirectoryPath(buildConfiguration),
ProjectFile.Name + FileNameSuffixes.DotNet.DynamicLib);
}
public string GetPdbPath(string buildConfiguration)
{
return Path.Combine(
GetOutputDirectoryPath(buildConfiguration),
ProjectFile.Name + FileNameSuffixes.DotNet.ProgramDatabase);
}
private string GetOutputDirectoryPath(string buildConfiguration)
{
return Path.Combine(
ProjectDirectory,
DirectoryNames.Bin,
buildConfiguration,
TargetFramework.GetShortFolderName(),
ProjectModel.RuntimeIdentifier.Current);
}
}
}

View file

@ -140,6 +140,7 @@ namespace Microsoft.DotNet.ProjectModel
project.LicenseUrl = rawProject.ValueAsString("licenseUrl");
project.IconUrl = rawProject.ValueAsString("iconUrl");
project.CompilerName = rawProject.ValueAsString("compilerName");
project.TestRunner = rawProject.ValueAsString("testRunner");
project.Authors = rawProject.ValueAsStringArray("authors") ?? Array.Empty<string>();
project.Owners = rawProject.ValueAsStringArray("owners") ?? Array.Empty<string>();

View file

@ -1,10 +1,22 @@
// 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.
namespace System.Collections.Generic
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.DotNet.ProjectModel.Utilities
{
internal static class DictionaryExtensions
public static class DictionaryExtensions
{
public static IEnumerable<V> GetOrEmpty<K, V>(this IDictionary<K, IEnumerable<V>> self, K key)
{
IEnumerable<V> val;
return !self.TryGetValue(key, out val)
? Enumerable.Empty<V>()
: val;
}
public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> factory)
{
lock (dictionary)

View file

@ -14,6 +14,7 @@ using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using NuGet.Frameworks;
using Microsoft.DotNet.ProjectModel.Utilities;
namespace Microsoft.DotNet.Tools.Compiler
{

View file

@ -51,7 +51,7 @@ namespace Microsoft.DotNet.Tools.Compiler
var configValue = configuration.Value() ?? Cli.Utils.Constants.DefaultConfiguration;
var outputValue = output.Value();
return BuildPackage(path, configValue, outputValue, intermediateOutput.Value()) ? 0 : 1;
return BuildPackage(path, configValue, outputValue, intermediateOutput.Value()) ? 1 : 0;
});
try

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>f003f228-2ae2-4e9d-877b-93eb773b5061</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,305 @@
// 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.DotNet.Cli.Utils;
using Microsoft.Dnx.Runtime.Common.CommandLine;
using Microsoft.DotNet.ProjectModel;
using Microsoft.Extensions.Testing.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Tools.Test
{
public class Program
{
public static int Main(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
var app = new CommandLineApplication(false)
{
Name = "dotnet test",
FullName = ".NET Test Driver",
Description = "Test Driver for the .NET Platform"
};
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 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;
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);
}
else
{
return RunConsole(projectContext, app, testRunner);
}
}
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)
{
var commandArgs = new List<string> {projectContext.GetAssemblyPath(Constants.DefaultConfiguration)};
commandArgs.AddRange(app.RemainingArguments);
return Command.Create($"{GetCommandName(testRunner)}", commandArgs, projectContext.TargetFramework)
.ForwardStdErr()
.ForwardStdOut()
.Execute()
.ExitCode;
}
private static int RunDesignTime(int port, ProjectContext projectContext, string testRunner)
{
Console.WriteLine("Listening on port {0}", port);
using (var channel = ReportingChannel.ListenOn(port))
{
Console.WriteLine("Client accepted {0}", channel.Socket.LocalEndPoint);
HandleDesignTimeMessages(projectContext, testRunner, channel);
return 0;
}
}
private static void HandleDesignTimeMessages(ProjectContext projectContext, string testRunner, ReportingChannel channel)
{
try
{
var message = channel.ReadQueue.Take();
if (message.MessageType == "ProtocolVersion")
{
HandleProtocolVersionMessage(message, channel);
// Take the next message, which should be the command to execute.
message = channel.ReadQueue.Take();
}
if (message.MessageType == "TestDiscovery.Start")
{
HandleTestDiscoveryStartMessage(testRunner, channel, projectContext);
}
else if (message.MessageType == "TestExecution.Start")
{
HandleTestExecutionStartMessage(testRunner, message, channel, projectContext);
}
else
{
HandleUnknownMessage(message, channel);
}
}
catch (Exception ex)
{
channel.SendError(ex);
}
}
private static void HandleProtocolVersionMessage(Message message, ReportingChannel channel)
{
var version = message.Payload?.ToObject<ProtocolVersionMessage>().Version;
var supportedVersion = 1;
TestHostTracing.Source.TraceInformation(
"[ReportingChannel]: Requested Version: {0} - Using Version: {1}",
version,
supportedVersion);
channel.Send(new Message()
{
MessageType = "ProtocolVersion",
Payload = JToken.FromObject(new ProtocolVersionMessage()
{
Version = supportedVersion,
}),
});
}
private static void HandleTestDiscoveryStartMessage(string testRunner, ReportingChannel channel, ProjectContext projectContext)
{
TestHostTracing.Source.TraceInformation("Starting Discovery");
var commandArgs = new List<string> { projectContext.GetAssemblyPath(Constants.DefaultConfiguration) };
commandArgs.AddRange(new[]
{
"--list",
"--designtime"
});
ExecuteRunnerCommand(testRunner, channel, commandArgs);
channel.Send(new Message()
{
MessageType = "TestDiscovery.Response",
});
TestHostTracing.Source.TraceInformation("Completed Discovery");
}
private static void HandleTestExecutionStartMessage(string testRunner, Message message, ReportingChannel channel, ProjectContext projectContext)
{
TestHostTracing.Source.TraceInformation("Starting Execution");
var commandArgs = new List<string> { projectContext.GetAssemblyPath(Constants.DefaultConfiguration) };
commandArgs.AddRange(new[]
{
"--designtime"
});
var tests = message.Payload?.ToObject<RunTestsMessage>().Tests;
if (tests != null)
{
foreach (var test in tests)
{
commandArgs.Add("--test");
commandArgs.Add(test);
}
}
ExecuteRunnerCommand(testRunner, channel, commandArgs);
channel.Send(new Message()
{
MessageType = "TestExecution.Response",
});
TestHostTracing.Source.TraceInformation("Completed Execution");
}
private static void HandleUnknownMessage(Message message, ReportingChannel channel)
{
var error = string.Format("Unexpected message type: '{0}'.", message.MessageType);
TestHostTracing.Source.TraceEvent(TraceEventType.Error, 0, error);
channel.SendError(error);
throw new InvalidOperationException(error);
}
private static void ExecuteRunnerCommand(string testRunner, ReportingChannel channel, List<string> commandArgs)
{
var result = Command.Create(GetCommandName(testRunner), commandArgs, new NuGetFramework("DNXCore", Version.Parse("5.0")))
.OnOutputLine(line =>
{
try
{
channel.Send(JsonConvert.DeserializeObject<Message>(line));
}
catch
{
TestHostTracing.Source.TraceInformation(line);
}
})
.Execute();
if (result.ExitCode != 0)
{
channel.SendError($"{GetCommandName(testRunner)} returned '{result.ExitCode}'.");
}
}
private static string GetCommandName(string testRunner)
{
return $"dotnet-test-{testRunner}";
}
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.");
}
}
private static IEnumerable<ProjectContext> CreateProjectContexts(string projectPath)
{
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.");
}
return ProjectContext.CreateContextForEachFramework(projectPath);
}
}
}

View file

@ -0,0 +1,8 @@
// 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.Reflection;
using System.Resources;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]

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.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.Extensions.Testing.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.Tools.Test
{
public class ReportingChannel : IDisposable
{
public static ReportingChannel ListenOn(int port)
{
// This fixes the mono incompatibility but ties it to ipv4 connections
using (var listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, port));
listenSocket.Listen(10);
var socket = listenSocket.Accept();
return new ReportingChannel(socket);
}
}
private readonly BinaryWriter _writer;
private readonly BinaryReader _reader;
private readonly ManualResetEventSlim _ackWaitHandle;
private ReportingChannel(Socket socket)
{
Socket = socket;
var stream = new NetworkStream(Socket);
_writer = new BinaryWriter(stream);
_reader = new BinaryReader(stream);
_ackWaitHandle = new ManualResetEventSlim();
ReadQueue = new BlockingCollection<Message>(boundedCapacity: 1);
// Read incoming messages on the background thread
new Thread(ReadMessages) { IsBackground = true }.Start();
}
public BlockingCollection<Message> ReadQueue { get; }
public Socket Socket { get; private set; }
public void Send(Message message)
{
lock (_writer)
{
try
{
TestHostTracing.Source.TraceEvent(
TraceEventType.Verbose,
0,
"[ReportingChannel]: Send({0})",
message);
_writer.Write(JsonConvert.SerializeObject(message));
}
catch (Exception ex)
{
TestHostTracing.Source.TraceEvent(
TraceEventType.Error,
0,
"[ReportingChannel]: Error sending {0}",
ex);
throw;
}
}
}
public void SendError(string error)
{
Send(new Message()
{
MessageType = "Error",
Payload = JToken.FromObject(new ErrorMessage()
{
Message = error,
}),
});
}
public void SendError(Exception ex)
{
SendError(ex.ToString());
}
private void ReadMessages()
{
while (true)
{
try
{
var message = JsonConvert.DeserializeObject<Message>(_reader.ReadString());
ReadQueue.Add(message);
if (string.Equals(message.MessageType, "TestHost.Acknowledge"))
{
_ackWaitHandle.Set();
ReadQueue.CompleteAdding();
break;
}
}
catch (Exception ex)
{
TestHostTracing.Source.TraceEvent(
TraceEventType.Error,
0,
"[ReportingChannel]: Waiting for message failed {0}",
ex);
throw;
}
}
}
public void Dispose()
{
// Wait for a graceful disconnect - drain the queue until we get an 'ACK'
Message message;
while (ReadQueue.TryTake(out message, millisecondsTimeout: 1))
{
}
if (_ackWaitHandle.Wait(TimeSpan.FromSeconds(10)))
{
TestHostTracing.Source.TraceInformation("[ReportingChannel]: Received for ack from test host");
}
else
{
TestHostTracing.Source.TraceEvent(
TraceEventType.Error,
0,
"[ReportingChannel]: Timed out waiting for ack from test host");
}
Socket.Dispose();
}
}
}

View file

@ -0,0 +1,24 @@
// 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;
namespace Microsoft.DotNet.Tools.Test
{
public static class TestHostTracing
{
public static readonly string TracingEnvironmentVariable = "DOTNET_TEST_TRACE";
public static readonly TraceSource Source;
static TestHostTracing()
{
Source = Environment.GetEnvironmentVariable(TracingEnvironmentVariable) == "1"
? new TraceSource("dotnet-test", SourceLevels.Verbose)
: new TraceSource("dotnet-test", SourceLevels.Warning);
Source.Listeners.Add(new TextWriterTraceListener(Console.Error));
}
}
}

View file

@ -0,0 +1,63 @@
{
"name": "dotnet-test",
"description": "Test host for discovering and running unit tests at design time, such as in Visual Studio.",
"version": "1.0.0-*",
"repository": {
"type": "git",
"url": "git://github.com/dotnet/cli"
},
"compilationOptions": {
"warningsAsErrors": true,
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.ConsoleHost": "1.0.0-beta-23409",
"Microsoft.NETCore.TestHost": "1.0.0-beta-23409",
"Microsoft.Extensions.Compilation.Abstractions": "1.0.0-*",
"Microsoft.DotNet.Cli.Utils": {
"type": "build",
"version": "1.0.0-*"
},
"Microsoft.Dnx.Runtime.CommandParsing.Sources": {
"version": "1.0.0-*",
"type": "build"
},
"Microsoft.Dnx.Runtime.Sources": {
"version": "1.0.0-*",
"type": "build"
},
"Microsoft.Extensions.Testing.Abstractions": {
"version": "1.0.0-*",
"type": "build"
},
"Microsoft.Extensions.CommandLineUtils.Sources": {
"version": "1.0.0-*",
"type": "build"
},
"Microsoft.Extensions.Logging": "1.0.0-*",
"Newtonsoft.Json": "7.0.1"
},
"frameworks": {
"dnxcore50": {
"dependencies": {
"System.Console": "4.0.0-beta-*",
"System.Diagnostics.Process": "4.1.0-beta-*",
"System.Diagnostics.TextWriterTraceListener": "4.0.0-beta-*",
"System.Diagnostics.TraceSource": "4.0.0-beta-*",
"System.Dynamic.Runtime": "4.0.11-beta-*",
"System.Net.Primitives": "4.0.11-beta-*",
"System.Net.Sockets": "4.1.0-beta-*",
"System.Runtime": "4.0.21-rc2-*",
"System.Reflection.Extensions": "4.0.1-beta-*",
"System.Reflection.TypeExtensions": "4.0.1-beta-*",
"System.Threading.Thread": "4.0.0-beta-*"
}
}
},
"scripts": {
"postcompile": [
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"",
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\""
]
}
}

View file

@ -0,0 +1,19 @@
// 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.
namespace dia2
{
public enum DataKind
{
DataIsUnknown,
DataIsLocal,
DataIsStaticLocal,
DataIsParam,
DataIsObjectPtr,
DataIsFileStatic,
DataIsGlobal,
DataIsMember,
DataIsStaticMember,
DataIsConstant
}
}

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 System.Runtime.InteropServices;
namespace dia2
{
[ComImport]
[Guid("E6756135-1E65-4D17-8576-610761398C3C")]
public class DiaDataSource
{
}
}

View file

@ -0,0 +1,35 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("79F1BB5F-B66E-48E5-B6A9-1545C323CA3D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaDataSource
{
[DispId(1)]
string lastError
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
void loadDataFromPdb([MarshalAs(UnmanagedType.LPWStr)] [In] string pdbPath);
void loadAndValidateDataFromPdb([MarshalAs(UnmanagedType.LPWStr)] [In] string pdbPath, [In] ref Guid pcsig70, [In] uint sig, [In] uint age);
void loadDataForExe([MarshalAs(UnmanagedType.LPWStr)] [In] string executable, [MarshalAs(UnmanagedType.LPWStr)] [In] string searchPath, [MarshalAs(UnmanagedType.IUnknown)] [In] object pCallback);
void loadDataFromIStream([MarshalAs(UnmanagedType.Interface)] [In] IStream pIStream);
void openSession([MarshalAs(UnmanagedType.Interface)] out IDiaSession ppSession);
void loadDataFromCodeViewInfo([MarshalAs(UnmanagedType.LPWStr)] [In] string executable, [MarshalAs(UnmanagedType.LPWStr)] [In] string searchPath, [In] uint cbCvInfo, [In] ref byte pbCvInfo, [MarshalAs(UnmanagedType.IUnknown)] [In] object pCallback);
void loadDataFromMiscInfo([MarshalAs(UnmanagedType.LPWStr)] [In] string executable, [MarshalAs(UnmanagedType.LPWStr)] [In] string searchPath, [In] uint timeStampExe, [In] uint timeStampDbg, [In] uint sizeOfExe, [In] uint cbMiscInfo, [In] ref byte pbMiscInfo, [MarshalAs(UnmanagedType.IUnknown)] [In] object pCallback);
}
}

View file

@ -0,0 +1,40 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("486943E8-D187-4A6B-A3C4-291259FFF60D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumDebugStreamData
{
[DispId(1)]
int count
{
get;
}
[DispId(2)]
string name
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
IEnumerator GetEnumerator();
void Item([In] uint index, [In] uint cbData, out uint pcbData, out byte pbData);
void Next([In] uint celt, [In] uint cbData, out uint pcbData, out byte pbData, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumDebugStreamData ppenum);
}
}

View file

@ -0,0 +1,35 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("08CBB41E-47A6-4F87-92F1-1C9C87CED044"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumDebugStreams
{
[DispId(1)]
int count
{
get;
}
[return: MarshalAs(UnmanagedType.Interface)]
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaEnumDebugStreamData Item([In] object index);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumDebugStreamData rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumDebugStreams ppenum);
}
}

View file

@ -0,0 +1,34 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("D5612573-6925-4468-8883-98CDEC8C384A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumInjectedSources
{
[DispId(1)]
int count
{
get;
}
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaInjectedSource Item([In] uint index);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaInjectedSource rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumInjectedSources ppenum);
}
}

View file

@ -0,0 +1,34 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("1C7FF653-51F7-457E-8419-B20F57EF7E4D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumInputAssemblyFiles
{
[DispId(1)]
int count
{
get;
}
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaInputAssemblyFile Item([In] uint index);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaInputAssemblyFile rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumInputAssemblyFiles ppenum);
}
}

View file

@ -0,0 +1,34 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("FE30E878-54AC-44F1-81BA-39DE940F6052"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumLineNumbers
{
[DispId(1)]
int count
{
get;
}
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaLineNumber Item([In] uint index);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaLineNumber rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppenum);
}
}

View file

@ -0,0 +1,34 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("10F3DBD9-664F-4469-B808-9471C7A50538"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumSourceFiles
{
[DispId(1)]
int count
{
get;
}
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaSourceFile Item([In] uint index);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaSourceFile rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumSourceFiles ppenum);
}
}

View file

@ -0,0 +1,34 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("CAB72C48-443B-48F5-9B0B-42F0820AB29A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumSymbols
{
[DispId(1)]
int count
{
get;
}
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaSymbol Item([In] uint index);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppenum);
}
}

View file

@ -0,0 +1,28 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("624B7D9C-24EA-4421-9D06-3B577471C1FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumSymbolsByAddr
{
[return: MarshalAs(UnmanagedType.Interface)]
IDiaSymbol symbolByAddr([In] uint isect, [In] uint offset);
[return: MarshalAs(UnmanagedType.Interface)]
IDiaSymbol symbolByRVA([In] uint relativeVirtualAddress);
[return: MarshalAs(UnmanagedType.Interface)]
IDiaSymbol symbolByVA([In] ulong virtualAddress);
void Next([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol rgelt, out uint pceltFetched);
void Prev([In] uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol rgelt, out uint pceltFetched);
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbolsByAddr ppenum);
}
}

View file

@ -0,0 +1,34 @@
// 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;
using System.Reflection;
using System.Runtime.InteropServices;
namespace dia2
{
[DefaultMember("Item"), Guid("C65C2B0A-1150-4D7A-AFCC-E05BF3DEE81E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaEnumTables
{
[DispId(1)]
int count
{
get;
}
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.Interface)]
IDiaTable Item([In] object index);
void Next(uint celt, [MarshalAs(UnmanagedType.Interface)] out IDiaTable rgelt, ref uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IDiaEnumTables ppenum);
}
}

View file

@ -0,0 +1,54 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("AE605CDC-8105-4A23-B710-3259F1E26112"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaInjectedSource
{
[DispId(1)]
uint crc
{
get;
}
[DispId(2)]
ulong length
{
get;
}
[DispId(3)]
string fileName
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
[DispId(4)]
string objectFileName
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
[DispId(5)]
string virtualFilename
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
[DispId(6)]
uint sourceCompression
{
get;
}
void get_source([In] uint cbData, out uint pcbData, out byte pbData);
}
}

View file

@ -0,0 +1,46 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("3BFE56B0-390C-4863-9430-1F3D083B7684"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaInputAssemblyFile
{
[DispId(1)]
uint uniqueId
{
get;
}
[DispId(2)]
uint index
{
get;
}
[DispId(3)]
uint timeStamp
{
get;
}
[DispId(4)]
int pdbAvailableAtILMerge
{
get;
}
[DispId(5)]
string fileName
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
void get_version([In] uint cbData, out uint pcbData, out byte pbData);
}
}

View file

@ -0,0 +1,99 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("B388EB14-BE4D-421D-A8A1-6CF7AB057086"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaLineNumber
{
[DispId(1)]
IDiaSymbol compiland
{
[return: MarshalAs(UnmanagedType.Interface)]
get;
}
[DispId(2)]
IDiaSourceFile sourceFile
{
[return: MarshalAs(UnmanagedType.Interface)]
get;
}
[DispId(3)]
uint lineNumber
{
get;
}
[DispId(4)]
uint lineNumberEnd
{
get;
}
[DispId(5)]
uint columnNumber
{
get;
}
[DispId(6)]
uint columnNumberEnd
{
get;
}
[DispId(7)]
uint addressSection
{
get;
}
[DispId(8)]
uint addressOffset
{
get;
}
[DispId(9)]
uint relativeVirtualAddress
{
get;
}
[DispId(10)]
ulong virtualAddress
{
get;
}
[DispId(11)]
uint length
{
get;
}
[DispId(12)]
uint sourceFileId
{
get;
}
[DispId(13)]
int statement
{
get;
}
[DispId(14)]
uint compilandId
{
get;
}
}
}

View file

@ -0,0 +1,130 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("2F609EE1-D1C8-4E24-8288-3326BADCD211"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaSession
{
[DispId(1)]
ulong loadAddress
{
get;
set;
}
[DispId(2)]
IDiaSymbol globalScope
{
[return: MarshalAs(UnmanagedType.Interface)]
get;
}
void getEnumTables([MarshalAs(UnmanagedType.Interface)] out IDiaEnumTables ppEnumTables);
void getSymbolsByAddr([MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbolsByAddr ppEnumbyAddr);
void findChildren([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findChildrenEx([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findChildrenExByAddr([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [In] uint isect, [In] uint offset, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findChildrenExByVA([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [In] ulong va, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findChildrenExByRVA([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [In] uint rva, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findSymbolByAddr([In] uint isect, [In] uint offset, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol);
void findSymbolByRVA([In] uint rva, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol);
void findSymbolByVA([In] ulong va, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol);
void findSymbolByToken([In] uint token, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol);
void symsAreEquiv([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol symbolA, [MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol symbolB);
void symbolById([In] uint id, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol);
void findSymbolByRVAEx([In] uint rva, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol, out int displacement);
void findSymbolByVAEx([In] ulong va, [In] SymTagEnum symTag, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol ppSymbol, out int displacement);
void findFile([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol pCompiland, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSourceFiles ppResult);
void findFileById([In] uint uniqueId, [MarshalAs(UnmanagedType.Interface)] out IDiaSourceFile ppResult);
void findLines([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol compiland, [MarshalAs(UnmanagedType.Interface)] [In] IDiaSourceFile file, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findLinesByAddr([In] uint seg, [In] uint offset, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findLinesByRVA([In] uint rva, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findLinesByVA([In] ulong va, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findLinesByLinenum([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol compiland, [MarshalAs(UnmanagedType.Interface)] [In] IDiaSourceFile file, [In] uint linenum, [In] uint column, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInjectedSource([MarshalAs(UnmanagedType.LPWStr)] [In] string srcFile, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumInjectedSources ppResult);
void getEnumDebugStreams([MarshalAs(UnmanagedType.Interface)] out IDiaEnumDebugStreams ppEnumDebugStreams);
void findInlineFramesByAddr([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] uint isect, [In] uint offset, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findInlineFramesByRVA([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] uint rva, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findInlineFramesByVA([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] ulong va, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void findInlineeLines([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInlineeLinesByAddr([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] uint isect, [In] uint offset, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInlineeLinesByRVA([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] uint rva, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInlineeLinesByVA([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol parent, [In] ulong va, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInlineeLinesByLinenum([MarshalAs(UnmanagedType.Interface)] [In] IDiaSymbol compiland, [MarshalAs(UnmanagedType.Interface)] [In] IDiaSourceFile file, [In] uint linenum, [In] uint column, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInlineesByName([MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint option, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void addressForVA([In] ulong va, out uint pISect, out uint pOffset);
void addressForRVA([In] uint rva, out uint pISect, out uint pOffset);
void findILOffsetsByAddr([In] uint isect, [In] uint offset, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findILOffsetsByRVA([In] uint rva, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findILOffsetsByVA([In] ulong va, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult);
void findInputAssemblyFiles([MarshalAs(UnmanagedType.Interface)] out IDiaEnumInputAssemblyFiles ppResult);
void findInputAssembly([In] uint index, [MarshalAs(UnmanagedType.Interface)] out IDiaInputAssemblyFile ppResult);
void findInputAssemblyById([In] uint uniqueId, [MarshalAs(UnmanagedType.Interface)] out IDiaInputAssemblyFile ppResult);
void getFuncMDTokenMapSize(out uint pcb);
void getFuncMDTokenMap([In] uint cb, out uint pcb, out byte pb);
void getTypeMDTokenMapSize(out uint pcb);
void getTypeMDTokenMap([In] uint cb, out uint pcb, out byte pb);
void getNumberOfFunctionFragments_VA([In] ulong vaFunc, [In] uint cbFunc, out uint pNumFragments);
void getNumberOfFunctionFragments_RVA([In] uint rvaFunc, [In] uint cbFunc, out uint pNumFragments);
void getFunctionFragments_VA([In] ulong vaFunc, [In] uint cbFunc, [In] uint cFragments, out ulong pVaFragment, out uint pLenFragment);
void getFunctionFragments_RVA([In] uint rvaFunc, [In] uint cbFunc, [In] uint cFragments, out uint pRvaFragment, out uint pLenFragment);
void getExports([MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
void getHeapAllocationSites([MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult);
}
}

View file

@ -0,0 +1,42 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("A2EF5353-F5A8-4EB3-90D2-CB526ACB3CDD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaSourceFile
{
[DispId(2)]
uint uniqueId
{
get;
}
[DispId(3)]
string fileName
{
[return: MarshalAs(UnmanagedType.BStr)]
get;
}
[DispId(4)]
uint checksumType
{
get;
}
[DispId(5)]
IDiaEnumSymbols compilands
{
[return: MarshalAs(UnmanagedType.Interface)]
get;
}
void get_checksum([In] uint cbData, out uint pcbData, out byte pbData);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
// 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;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace dia2
{
[TypeIdentifier]
[CompilerGenerated]
[DefaultMember("Item"), Guid("4A59FB77-ABAC-469B-A30B-9ECC85BFEF14"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IDiaTable : IEnumUnknown
{
string name { get; }
[DispId(2)]
int count
{
get;
}
new void RemoteNext([In] uint celt, [MarshalAs(UnmanagedType.IUnknown)] out object rgelt, out uint pceltFetched);
new void Skip([In] uint celt);
new void Reset();
new void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumUnknown ppenum);
[return: MarshalAs(UnmanagedType.IUnknown, MarshalType = "System.Runtime.InteropServices.CustomMarshalers.EnumeratorToEnumVariantMarshaler")]
IEnumerator GetEnumerator();
[return: MarshalAs(UnmanagedType.IUnknown)]
object Item([In] uint index);
}
}

View file

@ -0,0 +1,20 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("00000100-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IEnumUnknown
{
void RemoteNext([In] uint celt, [MarshalAs(UnmanagedType.IUnknown)] out object rgelt, out uint pceltFetched);
void Skip([In] uint celt);
void Reset();
void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumUnknown ppenum);
}
}

View file

@ -0,0 +1,17 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISequentialStream
{
void RemoteRead([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, out uint pcbRead);
void RemoteWrite([In] ref byte pv, [In] uint cb, out uint pcbWritten);
}
}

View file

@ -0,0 +1,34 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IStream : ISequentialStream
{
new void RemoteRead([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, out uint pcbRead);
new void RemoteWrite([In] ref byte pv, [In] uint cb, out uint pcbWritten);
void RemoteSeek([In] _LARGE_INTEGER dlibMove, [In] uint dwOrigin, out _ULARGE_INTEGER plibNewPosition);
void SetSize([In] _ULARGE_INTEGER libNewSize);
void RemoteCopyTo([MarshalAs(UnmanagedType.Interface)] [In] IStream pstm, [In] _ULARGE_INTEGER cb, out _ULARGE_INTEGER pcbRead, out _ULARGE_INTEGER pcbWritten);
void Commit([In] uint grfCommitFlags);
void Revert();
void LockRegion([In] _ULARGE_INTEGER libOffset, [In] _ULARGE_INTEGER cb, [In] uint dwLockType);
void UnlockRegion([In] _ULARGE_INTEGER libOffset, [In] _ULARGE_INTEGER cb, [In] uint dwLockType);
void Stat(out tagSTATSTG pstatstg, [In] uint grfStatFlag);
void Clone([MarshalAs(UnmanagedType.Interface)] out IStream ppstm);
}
}

View file

@ -0,0 +1,80 @@
// 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 System.Runtime.InteropServices;
namespace dia2
{
public class StreamWrapper : IStream
{
private Stream _stream;
public StreamWrapper(Stream stream)
{
_stream = stream;
}
public void RemoteRead(byte[] pv, int cb, out uint pcbRead)
{
pcbRead = (uint)_stream.Read(pv, 0, cb);
}
public void Stat(out tagSTATSTG pstatstg, [In]uint grfStatFlag)
{
pstatstg = new tagSTATSTG();
pstatstg.cbSize.QuadPart = (ulong)_stream.Length;
}
public void RemoteSeek([In]_LARGE_INTEGER dlibMove, [In]uint dwOrigin, out _ULARGE_INTEGER plibNewPosition)
{
plibNewPosition.QuadPart = (ulong)_stream.Seek(dlibMove.QuadPart, (SeekOrigin)dwOrigin);
}
public void RemoteRead(byte[] pv, [In]uint cb, out uint pcbRead)
{
pcbRead = (uint)_stream.Read(pv, offset: 0, count: (int)cb);
}
public void SetSize([In]_ULARGE_INTEGER libNewSize)
{
throw new NotImplementedException();
}
public void RemoteCopyTo([In, MarshalAs(UnmanagedType.Interface)]IStream pstm, [In]_ULARGE_INTEGER cb, out _ULARGE_INTEGER pcbRead, out _ULARGE_INTEGER pcbWritten)
{
throw new NotImplementedException();
}
public void Commit([In]uint grfCommitFlags)
{
throw new NotImplementedException();
}
public void Revert()
{
throw new NotImplementedException();
}
public void LockRegion([In]_ULARGE_INTEGER libOffset, [In]_ULARGE_INTEGER cb, [In]uint dwLockType)
{
throw new NotImplementedException();
}
public void UnlockRegion([In]_ULARGE_INTEGER libOffset, [In]_ULARGE_INTEGER cb, [In]uint dwLockType)
{
throw new NotImplementedException();
}
public void Clone([MarshalAs(UnmanagedType.Interface)]out IStream ppstm)
{
throw new NotImplementedException();
}
public void RemoteWrite([In]ref byte pv, [In]uint cb, out uint pcbWritten)
{
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,44 @@
// 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.Runtime.InteropServices;
namespace dia2
{
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct _LARGE_INTEGER
{
public long QuadPart;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct _ULARGE_INTEGER
{
public ulong QuadPart;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct tagSTATSTG
{
[MarshalAs(UnmanagedType.LPWStr)]
public string pwcsName;
public uint type;
public _ULARGE_INTEGER cbSize;
public _FILETIME mtime;
public _FILETIME ctime;
public _FILETIME atime;
public uint grfMode;
public uint grfLocksSupported;
public Guid clsid;
public uint grfStateBits;
public uint reserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct _FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
}
}

View file

@ -0,0 +1,51 @@
// 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.
namespace dia2
{
public enum SymTagEnum
{
SymTagNull,
SymTagExe,
SymTagCompiland,
SymTagCompilandDetails,
SymTagCompilandEnv,
SymTagFunction,
SymTagBlock,
SymTagData,
SymTagAnnotation,
SymTagLabel,
SymTagPublicSymbol,
SymTagUDT,
SymTagEnum,
SymTagFunctionType,
SymTagPointerType,
SymTagArrayType,
SymTagBaseType,
SymTagTypedef,
SymTagBaseClass,
SymTagFriend,
SymTagFunctionArgType,
SymTagFuncDebugStart,
SymTagFuncDebugEnd,
SymTagUsingNamespace,
SymTagVTableShape,
SymTagVTable,
SymTagCustom,
SymTagThunk,
SymTagCustomType,
SymTagManagedType,
SymTagDimension,
SymTagCallSite,
SymTagInlineSite,
SymTagBaseInterface,
SymTagVectorType,
SymTagMatrixType,
SymTagHLSLType,
SymTagCaller,
SymTagCallee,
SymTagExport,
SymTagHeapAllocationSite,
SymTagMax
}
}

View file

@ -0,0 +1,12 @@
// 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.Reflection;
namespace Microsoft.Extensions.Testing.Abstractions
{
public interface ISourceInformationProvider
{
SourceInformation GetSourceInformation(MethodInfo method);
}
}

View file

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.Extensions.Testing.Abstractions
{
public interface ITestDiscoverySink
{
void SendTestFound(Test test);
}
}

View file

@ -0,0 +1,12 @@
// 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.
namespace Microsoft.Extensions.Testing.Abstractions
{
public interface ITestExecutionSink
{
void SendTestStarted(Test test);
void SendTestResult(TestResult testResult);
}
}

View file

@ -0,0 +1,25 @@
// 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 Newtonsoft.Json;
namespace Microsoft.Extensions.Testing.Abstractions
{
class LineDelimitedJsonStream
{
private readonly StreamWriter _stream;
public LineDelimitedJsonStream(Stream stream)
{
_stream = new StreamWriter(stream);
}
public void Send(object @object)
{
_stream.WriteLine(JsonConvert.SerializeObject(@object));
_stream.Flush();
}
}
}

View file

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.DotNet.Tools.Test
{
public class ErrorMessage
{
public string Message { get; set; }
}
}

View file

@ -0,0 +1,20 @@
// 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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.Testing.Abstractions
{
public class Message
{
public string MessageType { get; set; }
public JToken Payload { get; set; }
public override string ToString()
{
return "(" + MessageType + ") -> " + (Payload == null ? "null" : Payload.ToString(Formatting.Indented));
}
}
}

View file

@ -0,0 +1,10 @@
// 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.
namespace Microsoft.DotNet.Tools.Test
{
public class ProtocolVersionMessage
{
public int Version { get; set; }
}
}

View file

@ -0,0 +1,12 @@
// 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;
namespace Microsoft.Extensions.Testing.Abstractions
{
public class RunTestsMessage
{
public List<string> Tests { get; set; }
}
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>dcdfe282-03de-4dbc-b90c-cc3ce3ec8162</ProjectGuid>
<RootNamespace>Microsoft.Extensions.Testing.Abstractions</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,8 @@
// 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.Reflection;
using System.Resources;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]

View file

@ -0,0 +1,18 @@
// 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.
namespace Microsoft.Extensions.Testing.Abstractions
{
public class SourceInformation
{
public SourceInformation(string filename, int lineNumber)
{
Filename = filename;
LineNumber = lineNumber;
}
public string Filename { get; }
public int LineNumber { get; }
}
}

View file

@ -0,0 +1,315 @@
// 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.Reflection;
using System.Runtime.CompilerServices;
using dia2;
using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.Testing.Abstractions
{
public class SourceInformationProvider : ISourceInformationProvider
{
//private readonly IMetadataProjectReference _project;
private readonly string _pdbPath;
private readonly ILogger _logger;
private bool? _isInitialized;
private IDiaDataSource _diaDataSource;
private IDiaSession _diaSession;
private AssemblyData _assemblyData;
public SourceInformationProvider(
string pdbPath,
ILogger logger)
{
if (String.IsNullOrWhiteSpace(pdbPath) ||
!File.Exists(pdbPath))
{
throw new ArgumentException($"The file '{pdbPath}' does not exist.", nameof(pdbPath));
}
_pdbPath = pdbPath;
_logger = logger;
}
public SourceInformation GetSourceInformation(MethodInfo method)
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
if (!EnsureInitialized())
{
// Unable to load DIA or we had a failure reading the symbols.
return null;
}
Debug.Assert(_isInitialized == true);
Debug.Assert(_diaSession != null);
Debug.Assert(_assemblyData != null);
// We need a MethodInfo so we can deal with cases where no user code shows up for provided
// method and class name. In particular:
//
// 1) inherited test methods (method.DeclaringType)
// 2) async test methods (see StateMachineAttribute).
//
// Note that this doesn't deal gracefully with overloaded methods. Symbol APIs don't provide
// a way to match overloads. We'd really need MetadataTokens to do this correctly (missing in
// CoreCLR).
method = ResolveBestMethodInfo(method);
var className = method.DeclaringType.FullName;
var methodName = method.Name;
// The DIA code doesn't include a + for nested classes, just a dot.
var symbolId = FindMethodSymbolId(className.Replace('+', '.'), methodName);
if (symbolId == null)
{
// No matching method in the symbol.
return null;
}
try
{
return GetSourceInformation(symbolId.Value);
}
catch (Exception ex)
{
_logger.LogWarning("Failed to access source information in symbol.", ex);
return null;
}
}
private MethodInfo ResolveBestMethodInfo(MethodInfo method)
{
Debug.Assert(_isInitialized == true);
// If a method has a StateMachineAttribute, then all of the user code will show up
// in the symbols associated with the compiler-generated code. So, we need to look
// for the 'MoveNext' on the generated type and resolve symbols for that.
var attribute = method.GetCustomAttribute<StateMachineAttribute>();
if (attribute?.StateMachineType == null)
{
return method;
}
return attribute.StateMachineType.GetMethod(
"MoveNext",
BindingFlags.Instance | BindingFlags.NonPublic);
}
private uint? FindMethodSymbolId(string className, string methodName)
{
Debug.Assert(_isInitialized == true);
ClassData classData;
if (_assemblyData.Classes.TryGetValue(className, out classData))
{
MethodData methodData;
if (classData.Methods.TryGetValue(methodName, out methodData))
{
return methodData.SymbolId;
}
}
return null;
}
private SourceInformation GetSourceInformation(uint symbolId)
{
Debug.Assert(_isInitialized == true);
string filename = null;
int? lineNumber = null;
IDiaSymbol diaSymbol;
_diaSession.symbolById(symbolId, out diaSymbol);
if (diaSymbol == null)
{
// Doesn't seem like this should happen, since DIA gave us the id.
return null;
}
IDiaEnumLineNumbers diaLineNumbers;
_diaSession.findLinesByAddr(
diaSymbol.addressSection,
diaSymbol.addressOffset,
(uint)diaSymbol.length,
out diaLineNumbers);
// Resist the user to use foreach here. It doesn't work well with these APIs.
IDiaLineNumber diaLineNumber;
var lineNumbersFetched = 0u;
diaLineNumbers.Next(1u, out diaLineNumber, out lineNumbersFetched);
while (lineNumbersFetched == 1 && diaLineNumber != null)
{
if (filename == null)
{
var diaFile = diaLineNumber.sourceFile;
if (diaFile != null)
{
filename = diaFile.fileName;
}
}
if (diaLineNumber.lineNumber != 16707566u)
{
// We'll see multiple line numbers for the same method, but we just want the first one.
lineNumber = Math.Min(lineNumber ?? Int32.MaxValue, (int)diaLineNumber.lineNumber);
}
diaLineNumbers.Next(1u, out diaLineNumber, out lineNumbersFetched);
}
if (filename == null || lineNumber == null)
{
return null;
}
else
{
return new SourceInformation(filename, lineNumber.Value);
}
}
private bool EnsureInitialized()
{
if (_isInitialized.HasValue)
{
return _isInitialized.Value;
}
try
{
_diaDataSource = (IDiaDataSource)new DiaDataSource();
_isInitialized = true;
}
catch (Exception ex)
{
_logger.LogWarning("Failed to create DIA DataSource. No source information will be available.", ex);
_isInitialized = false;
return _isInitialized.Value;
}
// We have a project, and we successfully loaded DIA, so let's capture the symbols
// and create a session.
try
{
_diaDataSource.loadDataFromPdb(_pdbPath);
_diaDataSource.openSession(out _diaSession);
}
catch (Exception ex)
{
_logger.LogWarning("Failed to load symbols. No source information will be available.", ex);
_isInitialized = false;
return _isInitialized.Value;
}
try
{
_assemblyData = FetchSymbolData(_diaSession);
}
catch (Exception ex)
{
_logger.LogWarning("Failed to read symbols. No source information will be available.", ex);
_isInitialized = false;
return _isInitialized.Value;
}
_isInitialized = true;
return _isInitialized.Value;
}
// Builds a lookup table of class+method name.
//
// It's easier to build it at once by enumerating, once we have the table, we
// can use the symbolIds to look up the sources when we need them.
private static AssemblyData FetchSymbolData(IDiaSession session)
{
// This will be a *flat* enumerator of all classes.
//
// A nested class will not contain a '+' in it's name, just a '.' separating the parent class name from
// the child class name.
IDiaEnumSymbols diaClasses;
session.findChildren(
session.globalScope, // Search at the top-level.
SymTagEnum.SymTagCompiland, // Just find classes.
name: null, // Don't filter by name.
compareFlags: 0u, // doesn't matter because name is null.
ppResult: out diaClasses);
var assemblyData = new AssemblyData();
// Resist the urge to use foreach here. It doesn't work well with these APIs.
var classesFetched = 0u;
IDiaSymbol diaClass;
diaClasses.Next(1u, out diaClass, out classesFetched);
while (classesFetched == 1 && diaClass != null)
{
var classData = new ClassData()
{
Name = diaClass.name,
SymbolId = diaClass.symIndexId,
};
assemblyData.Classes.Add(diaClass.name, classData);
IDiaEnumSymbols diaMethods;
session.findChildren(
diaClass,
SymTagEnum.SymTagFunction,
name: null, // Don't filter by name.
compareFlags: 0u, // doesn't matter because name is null.
ppResult: out diaMethods);
// Resist the urge to use foreach here. It doesn't work well with these APIs.
var methodsFetched = 0u;
IDiaSymbol diaMethod;
diaMethods.Next(1u, out diaMethod, out methodsFetched);
while (methodsFetched == 1 && diaMethod != null)
{
classData.Methods[diaMethod.name] = new MethodData()
{
Name = diaMethod.name,
SymbolId = diaMethod.symIndexId,
};
diaMethods.Next(1u, out diaMethod, out methodsFetched);
}
diaClasses.Next(1u, out diaClass, out classesFetched);
}
return assemblyData;
}
private class AssemblyData
{
public IDictionary<string, ClassData> Classes { get; } = new Dictionary<string, ClassData>();
}
private class ClassData
{
public string Name { get; set; }
public uint SymbolId { get; set; }
public IDictionary<string, MethodData> Methods { get; } = new Dictionary<string, MethodData>();
}
private class MethodData
{
public string Name { get; set; }
public uint SymbolId { get; set; }
}
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.IO;
using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.Testing.Abstractions
{
public class StreamingTestDiscoverySink : ITestDiscoverySink
{
private readonly LineDelimitedJsonStream _stream;
public StreamingTestDiscoverySink(Stream stream)
{
_stream = new LineDelimitedJsonStream(stream);
}
public void SendTestFound(Test test)
{
if (test == null)
{
throw new ArgumentNullException(nameof(test));
}
_stream.Send(new Message
{
MessageType = "TestDiscovery.TestFound",
Payload = JToken.FromObject(test),
});
}
}
}

View file

@ -0,0 +1,72 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.Testing.Abstractions
{
public class StreamingTestExecutionSink : ITestExecutionSink
{
private readonly LineDelimitedJsonStream _stream;
private readonly ConcurrentDictionary<string, TestState> _runningTests;
public StreamingTestExecutionSink(Stream stream)
{
_stream = new LineDelimitedJsonStream(stream);
_runningTests = new ConcurrentDictionary<string, TestState>();
}
public void SendTestStarted(Test test)
{
if (test == null)
{
throw new ArgumentNullException(nameof(test));
}
if (test.FullyQualifiedName != null)
{
var state = new TestState() { StartTime = DateTimeOffset.Now, };
_runningTests.TryAdd(test.FullyQualifiedName, state);
}
_stream.Send(new Message
{
MessageType = "TestExecution.TestStarted",
Payload = JToken.FromObject(test),
});
}
public void SendTestResult(TestResult testResult)
{
if (testResult == null)
{
throw new ArgumentNullException(nameof(testResult));
}
if (testResult.StartTime == default(DateTimeOffset) && testResult.Test.FullyQualifiedName != null)
{
TestState state;
_runningTests.TryRemove(testResult.Test.FullyQualifiedName, out state);
testResult.StartTime = state.StartTime;
}
if (testResult.EndTime == default(DateTimeOffset))
{
testResult.EndTime = DateTimeOffset.Now;
}
_stream.Send(new Message
{
MessageType = "TestExecution.TestResult",
Payload = JToken.FromObject(testResult),
});
}
private class TestState
{
public DateTimeOffset StartTime { get; set; }
}
}
}

View file

@ -0,0 +1,28 @@
// 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;
namespace Microsoft.Extensions.Testing.Abstractions
{
public class Test
{
public Test()
{
Properties = new Dictionary<string, object>(StringComparer.Ordinal);
}
public string CodeFilePath { get; set; }
public string DisplayName { get; set; }
public string FullyQualifiedName { get; set; }
public Guid? Id { get; set; }
public int? LineNumber { get; set; }
public IDictionary<string, object> Properties { get; private set; }
}
}

View file

@ -0,0 +1,18 @@
// 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.Extensions.Logging;
namespace Microsoft.Extensions.Testing.Abstractions
{
public abstract class TestHostServices
{
public abstract ITestDiscoverySink TestDiscoverySink { get; }
public abstract ITestExecutionSink TestExecutionSink { get; }
public abstract ISourceInformationProvider SourceInformationProvider { get; }
public abstract ILoggerFactory LoggerFactory { get; }
}
}

View file

@ -0,0 +1,14 @@
// 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.
namespace Microsoft.Extensions.Testing.Abstractions
{
public enum TestOutcome
{
None,
Passed,
Failed,
Skipped,
NotFound
}
}

View file

@ -0,0 +1,42 @@
// 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.ObjectModel;
namespace Microsoft.Extensions.Testing.Abstractions
{
public sealed class TestResult
{
public TestResult(Test test)
{
if (test == null)
{
throw new ArgumentNullException(nameof(test));
}
Test = test;
Messages = new Collection<string>();
}
public Test Test { get; private set; }
public TestOutcome Outcome { get; set; }
public string ErrorMessage { get; set; }
public string ErrorStackTrace { get; set; }
public string DisplayName { get; set; }
public Collection<string> Messages { get; private set; }
public string ComputerName { get; set; }
public TimeSpan Duration { get; set; }
public DateTimeOffset StartTime { get; set; }
public DateTimeOffset EndTime { get; set; }
}
}

View file

@ -0,0 +1,38 @@
{
"description": "Abstractions for test runners to communicate to a tool, such as Visual Studio.",
"version": "1.0.0-*",
"repository": {
"type": "git",
"url": "git://github.com/dotnet/cli"
},
"compilationOptions": {
"warningsAsErrors": true
},
"dependencies": {
"Newtonsoft.Json": "7.0.1",
"Microsoft.DotNet.ProjectModel": {
"version": "1.0.0",
"type": "build"
},
"Microsoft.Extensions.Compilation.Abstractions": "1.0.0-*",
"Microsoft.Extensions.Logging.Abstractions": "1.0.0-*",
"System.Runtime.Serialization.Primitives": "4.0.11-beta-*"
},
"frameworks": {
"dnxcore50": {
"dependencies": {
"System.Collections": "4.0.11-rc2-*",
"System.Reflection": "4.1.0-beta-*",
"System.Resources.ResourceManager": "4.0.1-beta-*",
"System.Runtime": "4.0.21-rc2-*",
"System.Runtime.Extensions": "4.0.11-rc2-*"
}
}
},
"scripts": {
"postcompile": [
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"",
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\""
]
}
}