diff --git a/Microsoft.DotNet.Cli.sln b/Microsoft.DotNet.Cli.sln index cb91c90cf..2a6e50489 100644 --- a/Microsoft.DotNet.Cli.sln +++ b/Microsoft.DotNet.Cli.sln @@ -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 diff --git a/packaging/debian/debian_config.json b/packaging/debian/debian_config.json index bc0fd97b2..3f41586ff 100644 --- a/packaging/debian/debian_config.json +++ b/packaging/debian/debian_config.json @@ -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" } } diff --git a/scripts/build/build-stage.ps1 b/scripts/build/build-stage.ps1 index d9ecd885f..1b74562ee 100644 --- a/scripts/build/build-stage.ps1 +++ b/scripts/build/build-stage.ps1 @@ -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 = @( diff --git a/scripts/build/build-stage.sh b/scripts/build/build-stage.sh index 1d2a83aa3..018879ed6 100755 --- a/scripts/build/build-stage.sh +++ b/scripts/build/build-stage.sh @@ -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=( \ diff --git a/scripts/test/e2e-test.ps1 b/scripts/test/e2e-test.ps1 index aa6503004..2b73e99e6 100644 --- a/scripts/test/e2e-test.ps1 +++ b/scripts/test/e2e-test.ps1 @@ -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 diff --git a/src/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs b/src/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs index 513d548d9..53afc3483 100644 --- a/src/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs +++ b/src/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs @@ -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; diff --git a/src/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Microsoft.DotNet.Cli.Utils/Command.cs index 2a7933173..ff88e3184 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Command.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Command.cs @@ -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(); diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs index fa066a7f5..a068f162e 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Chain.cs @@ -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 { diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs index 5b6e06bd2..554859333 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/CommandGrammar.cs @@ -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; diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs index b8868b80e..12c65f460 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Cursor.cs @@ -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 { diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs index 6c2961fb7..1d36c8aa6 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Grammar.cs @@ -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; diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs index b1643f33a..66769c5e4 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Parser.cs @@ -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 { diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs index e9bc9ef16..7237f0372 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/ParserExtensions.cs @@ -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; diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs index 4a6adab07..3caf6bdad 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandParsing/Result.cs @@ -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 { diff --git a/src/Microsoft.DotNet.Cli.Utils/DictionaryExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/DictionaryExtensions.cs deleted file mode 100644 index 380cfa658..000000000 --- a/src/Microsoft.DotNet.Cli.Utils/DictionaryExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Linq; - -namespace System.Collections.Generic -{ - internal static class DictionaryExtensions - { - public static IEnumerable GetOrEmpty(this IDictionary> self, K key) - { - IEnumerable val; - if (!self.TryGetValue(key, out val)) - { - return Enumerable.Empty(); - } - return val; - } - } -} diff --git a/src/Microsoft.DotNet.ProjectModel/Project.cs b/src/Microsoft.DotNet.ProjectModel/Project.cs index 635aed22b..60bfa6187 100644 --- a/src/Microsoft.DotNet.ProjectModel/Project.cs +++ b/src/Microsoft.DotNet.ProjectModel/Project.cs @@ -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 Commands { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs index 83993dd48..8166c9b92 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs @@ -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); + } } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs index 92b877966..b97b47492 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs @@ -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(); project.Owners = rawProject.ValueAsStringArray("owners") ?? Array.Empty(); diff --git a/src/Microsoft.DotNet.ProjectModel/Utilities/DictionaryExtensions.cs b/src/Microsoft.DotNet.ProjectModel/Utilities/DictionaryExtensions.cs index 0fc4fbad2..65cd560e6 100644 --- a/src/Microsoft.DotNet.ProjectModel/Utilities/DictionaryExtensions.cs +++ b/src/Microsoft.DotNet.ProjectModel/Utilities/DictionaryExtensions.cs @@ -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 GetOrEmpty(this IDictionary> self, K key) + { + IEnumerable val; + return !self.TryGetValue(key, out val) + ? Enumerable.Empty() + : val; + } + public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) { lock (dictionary) diff --git a/src/Microsoft.DotNet.Tools.Compiler/Program.cs b/src/Microsoft.DotNet.Tools.Compiler/Program.cs index 5ae14ddf1..407421f33 100644 --- a/src/Microsoft.DotNet.Tools.Compiler/Program.cs +++ b/src/Microsoft.DotNet.Tools.Compiler/Program.cs @@ -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 { diff --git a/src/Microsoft.DotNet.Tools.Pack/Program.cs b/src/Microsoft.DotNet.Tools.Pack/Program.cs index 838b7b9b8..3136461c0 100644 --- a/src/Microsoft.DotNet.Tools.Pack/Program.cs +++ b/src/Microsoft.DotNet.Tools.Pack/Program.cs @@ -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 diff --git a/src/Microsoft.DotNet.Tools.Test/Microsoft.DotNet.Tools.Test.xproj b/src/Microsoft.DotNet.Tools.Test/Microsoft.DotNet.Tools.Test.xproj new file mode 100644 index 000000000..7f173a43e --- /dev/null +++ b/src/Microsoft.DotNet.Tools.Test/Microsoft.DotNet.Tools.Test.xproj @@ -0,0 +1,17 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + f003f228-2ae2-4e9d-877b-93eb773b5061 + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.DotNet.Tools.Test/Program.cs b/src/Microsoft.DotNet.Tools.Test/Program.cs new file mode 100644 index 000000000..0a253c47d --- /dev/null +++ b/src/Microsoft.DotNet.Tools.Test/Program.cs @@ -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("", "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 {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().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 { 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 { projectContext.GetAssemblyPath(Constants.DefaultConfiguration) }; + + commandArgs.AddRange(new[] + { + "--designtime" + }); + + var tests = message.Payload?.ToObject().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 commandArgs) + { + var result = Command.Create(GetCommandName(testRunner), commandArgs, new NuGetFramework("DNXCore", Version.Parse("5.0"))) + .OnOutputLine(line => + { + try + { + channel.Send(JsonConvert.DeserializeObject(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 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); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Tools.Test/Properties/AssemblyInfo.cs b/src/Microsoft.DotNet.Tools.Test/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..cdbb8074c --- /dev/null +++ b/src/Microsoft.DotNet.Tools.Test/Properties/AssemblyInfo.cs @@ -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")] \ No newline at end of file diff --git a/src/Microsoft.DotNet.Tools.Test/ReportingChannel.cs b/src/Microsoft.DotNet.Tools.Test/ReportingChannel.cs new file mode 100644 index 000000000..cf2f2d33c --- /dev/null +++ b/src/Microsoft.DotNet.Tools.Test/ReportingChannel.cs @@ -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(boundedCapacity: 1); + + // Read incoming messages on the background thread + new Thread(ReadMessages) { IsBackground = true }.Start(); + } + + public BlockingCollection 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(_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(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Tools.Test/TestHostTracing.cs b/src/Microsoft.DotNet.Tools.Test/TestHostTracing.cs new file mode 100644 index 000000000..809c3f70b --- /dev/null +++ b/src/Microsoft.DotNet.Tools.Test/TestHostTracing.cs @@ -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)); + } + } +} diff --git a/src/Microsoft.DotNet.Tools.Test/project.json b/src/Microsoft.DotNet.Tools.Test/project.json new file mode 100644 index 000000000..e1e584fa2 --- /dev/null +++ b/src/Microsoft.DotNet.Tools.Test/project.json @@ -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\"" + ] + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/DataKind.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/DataKind.cs new file mode 100644 index 000000000..13b8d7c39 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/DataKind.cs @@ -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 + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/DiaDataSource.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/DiaDataSource.cs new file mode 100644 index 000000000..942d605ea --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/DiaDataSource.cs @@ -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 + { + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaDataSource.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaDataSource.cs new file mode 100644 index 000000000..99669b8c1 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaDataSource.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumDebugStreamData.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumDebugStreamData.cs new file mode 100644 index 000000000..70e7c59c2 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumDebugStreamData.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumDebugStreams.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumDebugStreams.cs new file mode 100644 index 000000000..2b415c2d7 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumDebugStreams.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumInjectedSources.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumInjectedSources.cs new file mode 100644 index 000000000..3d1b2214f --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumInjectedSources.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumInputAssemblyFiles.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumInputAssemblyFiles.cs new file mode 100644 index 000000000..bf5fc842c --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumInputAssemblyFiles.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumLineNumbers.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumLineNumbers.cs new file mode 100644 index 000000000..ebcc07838 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumLineNumbers.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSourceFiles.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSourceFiles.cs new file mode 100644 index 000000000..139d13f19 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSourceFiles.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSymbols.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSymbols.cs new file mode 100644 index 000000000..189188426 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSymbols.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSymbolsByAddr.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSymbolsByAddr.cs new file mode 100644 index 000000000..dc588e938 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumSymbolsByAddr.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumTables.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumTables.cs new file mode 100644 index 000000000..0293437bd --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaEnumTables.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaInjectedSource.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaInjectedSource.cs new file mode 100644 index 000000000..cc97f3b55 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaInjectedSource.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaInputAssemblyFile.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaInputAssemblyFile.cs new file mode 100644 index 000000000..dc2022151 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaInputAssemblyFile.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaLineNumber.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaLineNumber.cs new file mode 100644 index 000000000..447e0411e --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaLineNumber.cs @@ -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; + } + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSession.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSession.cs new file mode 100644 index 000000000..34d3175ff --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSession.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSourceFile.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSourceFile.cs new file mode 100644 index 000000000..c03d7d869 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSourceFile.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSymbol.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSymbol.cs new file mode 100644 index 000000000..b8a3c3e6b --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaSymbol.cs @@ -0,0 +1,1269 @@ +// 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.Reflection; +using System.Runtime.InteropServices; + +namespace dia2 +{ + [DefaultMember("symIndexId"), Guid("CB787B2F-BD6C-4635-BA52-933126BD2DCD"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport] + public interface IDiaSymbol + { + [DispId(0)] + uint symIndexId + { + + get; + } + [DispId(1)] + uint symTag + { + + get; + } + [DispId(2)] + string name + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(3)] + IDiaSymbol lexicalParent + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(4)] + IDiaSymbol classParent + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(5)] + IDiaSymbol type + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(6)] + uint dataKind + { + + get; + } + [DispId(7)] + uint locationType + { + + get; + } + [DispId(8)] + uint addressSection + { + + get; + } + [DispId(9)] + uint addressOffset + { + + get; + } + [DispId(10)] + uint relativeVirtualAddress + { + + get; + } + [DispId(11)] + ulong virtualAddress + { + + get; + } + [DispId(12)] + uint registerId + { + + get; + } + [DispId(13)] + int offset + { + + get; + } + [DispId(14)] + ulong length + { + + get; + } + [DispId(15)] + uint slot + { + + get; + } + [DispId(16)] + int volatileType + { + + get; + } + [DispId(17)] + int constType + { + + get; + } + [DispId(18)] + int unalignedType + { + + get; + } + [DispId(19)] + uint access + { + + get; + } + [DispId(20)] + string libraryName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(21)] + uint platform + { + + get; + } + [DispId(22)] + uint language + { + + get; + } + [DispId(23)] + int editAndContinueEnabled + { + + get; + } + [DispId(24)] + uint frontEndMajor + { + + get; + } + [DispId(25)] + uint frontEndMinor + { + + get; + } + [DispId(26)] + uint frontEndBuild + { + + get; + } + [DispId(27)] + uint backEndMajor + { + + get; + } + [DispId(28)] + uint backEndMinor + { + + get; + } + [DispId(29)] + uint backEndBuild + { + + get; + } + [DispId(30)] + string sourceFileName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(31)] + string unused + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(32)] + uint thunkOrdinal + { + + get; + } + [DispId(33)] + int thisAdjust + { + + get; + } + [DispId(34)] + uint virtualBaseOffset + { + + get; + } + [DispId(35)] + int @virtual + { + + get; + } + [DispId(36)] + int intro + { + + get; + } + [DispId(37)] + int pure + { + + get; + } + [DispId(38)] + uint callingConvention + { + + get; + } + [DispId(39)] + object value + { + + get; + } + [DispId(40)] + uint baseType + { + + get; + } + [DispId(41)] + uint token + { + + get; + } + [DispId(42)] + uint timeStamp + { + + get; + } + [DispId(43)] + Guid guid + { + + get; + } + [DispId(44)] + string symbolsFileName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(46)] + int reference + { + + get; + } + [DispId(47)] + uint count + { + + get; + } + [DispId(49)] + uint bitPosition + { + + get; + } + [DispId(50)] + IDiaSymbol arrayIndexType + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(51)] + int packed + { + + get; + } + [DispId(52)] + int constructor + { + + get; + } + [DispId(53)] + int overloadedOperator + { + + get; + } + [DispId(54)] + int nested + { + + get; + } + [DispId(55)] + int hasNestedTypes + { + + get; + } + [DispId(56)] + int hasAssignmentOperator + { + + get; + } + [DispId(57)] + int hasCastOperator + { + + get; + } + [DispId(58)] + int scoped + { + + get; + } + [DispId(59)] + int virtualBaseClass + { + + get; + } + [DispId(60)] + int indirectVirtualBaseClass + { + + get; + } + [DispId(61)] + int virtualBasePointerOffset + { + + get; + } + [DispId(62)] + IDiaSymbol virtualTableShape + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(64)] + uint lexicalParentId + { + + get; + } + [DispId(65)] + uint classParentId + { + + get; + } + [DispId(66)] + uint typeId + { + + get; + } + [DispId(67)] + uint arrayIndexTypeId + { + + get; + } + [DispId(68)] + uint virtualTableShapeId + { + + get; + } + [DispId(69)] + int code + { + + get; + } + [DispId(70)] + int function + { + + get; + } + [DispId(71)] + int managed + { + + get; + } + [DispId(72)] + int msil + { + + get; + } + [DispId(73)] + uint virtualBaseDispIndex + { + + get; + } + [DispId(74)] + string undecoratedName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(75)] + uint age + { + + get; + } + [DispId(76)] + uint signature + { + + get; + } + [DispId(77)] + int compilerGenerated + { + + get; + } + [DispId(78)] + int addressTaken + { + + get; + } + [DispId(79)] + uint rank + { + + get; + } + [DispId(80)] + IDiaSymbol lowerBound + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(81)] + IDiaSymbol upperBound + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(82)] + uint lowerBoundId + { + + get; + } + [DispId(83)] + uint upperBoundId + { + + get; + } + [DispId(84)] + uint targetSection + { + + get; + } + [DispId(85)] + uint targetOffset + { + + get; + } + [DispId(86)] + uint targetRelativeVirtualAddress + { + + get; + } + [DispId(87)] + ulong targetVirtualAddress + { + + get; + } + [DispId(88)] + uint machineType + { + + get; + } + [DispId(89)] + uint oemId + { + + get; + } + [DispId(90)] + uint oemSymbolId + { + + get; + } + [DispId(91)] + IDiaSymbol objectPointerType + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(92)] + uint udtKind + { + + get; + } + [DispId(93)] + int noReturn + { + + get; + } + [DispId(94)] + int customCallingConvention + { + + get; + } + [DispId(95)] + int noInline + { + + get; + } + [DispId(96)] + int optimizedCodeDebugInfo + { + + get; + } + [DispId(97)] + int notReached + { + + get; + } + [DispId(98)] + int interruptReturn + { + + get; + } + [DispId(99)] + int farReturn + { + + get; + } + [DispId(100)] + int isStatic + { + + get; + } + [DispId(101)] + int hasDebugInfo + { + + get; + } + [DispId(102)] + int isLTCG + { + + get; + } + [DispId(103)] + int isDataAligned + { + + get; + } + [DispId(104)] + int hasSecurityChecks + { + + get; + } + [DispId(105)] + string compilerName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(106)] + int hasAlloca + { + + get; + } + [DispId(107)] + int hasSetJump + { + + get; + } + [DispId(108)] + int hasLongJump + { + + get; + } + [DispId(109)] + int hasInlAsm + { + + get; + } + [DispId(110)] + int hasEH + { + + get; + } + [DispId(111)] + int hasSEH + { + + get; + } + [DispId(112)] + int hasEHa + { + + get; + } + [DispId(113)] + int isNaked + { + + get; + } + [DispId(114)] + int isAggregated + { + + get; + } + [DispId(115)] + int isSplitted + { + + get; + } + [DispId(116)] + IDiaSymbol container + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(117)] + int inlSpec + { + + get; + } + [DispId(118)] + int noStackOrdering + { + + get; + } + [DispId(119)] + IDiaSymbol virtualBaseTableType + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(120)] + int hasManagedCode + { + + get; + } + [DispId(121)] + int isHotpatchable + { + + get; + } + [DispId(122)] + int isCVTCIL + { + + get; + } + [DispId(123)] + int isMSILNetmodule + { + + get; + } + [DispId(124)] + int isCTypes + { + + get; + } + [DispId(125)] + int isStripped + { + + get; + } + [DispId(126)] + uint frontEndQFE + { + + get; + } + [DispId(127)] + uint backEndQFE + { + + get; + } + [DispId(128)] + int wasInlined + { + + get; + } + [DispId(129)] + int strictGSCheck + { + + get; + } + [DispId(130)] + int isCxxReturnUdt + { + + get; + } + [DispId(131)] + int isConstructorVirtualBase + { + + get; + } + [DispId(132)] + int RValueReference + { + + get; + } + [DispId(133)] + IDiaSymbol unmodifiedType + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(134)] + int framePointerPresent + { + + get; + } + [DispId(135)] + int isSafeBuffers + { + + get; + } + [DispId(136)] + int intrinsic + { + + get; + } + [DispId(137)] + int @sealed + { + + get; + } + [DispId(138)] + int hfaFloat + { + + get; + } + [DispId(139)] + int hfaDouble + { + + get; + } + [DispId(140)] + uint liveRangeStartAddressSection + { + + get; + } + [DispId(141)] + uint liveRangeStartAddressOffset + { + + get; + } + [DispId(142)] + uint liveRangeStartRelativeVirtualAddress + { + + get; + } + [DispId(143)] + uint countLiveRanges + { + + get; + } + [DispId(144)] + ulong liveRangeLength + { + + get; + } + [DispId(145)] + uint offsetInUdt + { + + get; + } + [DispId(146)] + uint paramBasePointerRegisterId + { + + get; + } + [DispId(147)] + uint localBasePointerRegisterId + { + + get; + } + [DispId(148)] + int isLocationControlFlowDependent + { + + get; + } + [DispId(149)] + uint stride + { + + get; + } + [DispId(150)] + uint numberOfRows + { + + get; + } + [DispId(151)] + uint numberOfColumns + { + + get; + } + [DispId(152)] + int isMatrixRowMajor + { + + get; + } + [DispId(153)] + int isReturnValue + { + + get; + } + [DispId(154)] + int isOptimizedAway + { + + get; + } + [DispId(155)] + uint builtInKind + { + + get; + } + [DispId(156)] + uint registerType + { + + get; + } + [DispId(157)] + uint baseDataSlot + { + + get; + } + [DispId(158)] + uint baseDataOffset + { + + get; + } + [DispId(159)] + uint textureSlot + { + + get; + } + [DispId(160)] + uint samplerSlot + { + + get; + } + [DispId(161)] + uint uavSlot + { + + get; + } + [DispId(162)] + uint sizeInUdt + { + + get; + } + [DispId(163)] + uint memorySpaceKind + { + + get; + } + [DispId(164)] + uint unmodifiedTypeId + { + + get; + } + [DispId(165)] + uint subTypeId + { + + get; + } + [DispId(166)] + IDiaSymbol subType + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(167)] + uint numberOfModifiers + { + + get; + } + [DispId(168)] + uint numberOfRegisterIndices + { + + get; + } + [DispId(169)] + int isHLSLData + { + + get; + } + [DispId(170)] + int isPointerToDataMember + { + + get; + } + [DispId(171)] + int isPointerToMemberFunction + { + + get; + } + [DispId(172)] + int isSingleInheritance + { + + get; + } + [DispId(173)] + int isMultipleInheritance + { + + get; + } + [DispId(174)] + int isVirtualInheritance + { + + get; + } + [DispId(175)] + int restrictedType + { + + get; + } + [DispId(176)] + int isPointerBasedOnSymbolValue + { + + get; + } + [DispId(177)] + IDiaSymbol baseSymbol + { + + [return: MarshalAs(UnmanagedType.Interface)] + get; + } + [DispId(178)] + uint baseSymbolId + { + + get; + } + [DispId(179)] + string objectFileName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(184)] + int isSdl + { + + get; + } + [DispId(185)] + int isWinRTPointer + { + + get; + } + [DispId(186)] + int isRefUdt + { + + get; + } + [DispId(187)] + int isValueUdt + { + + get; + } + [DispId(188)] + int isInterfaceUdt + { + + get; + } + [DispId(189)] + int isPGO + { + + get; + } + [DispId(190)] + int hasValidPGOCounts + { + + get; + } + [DispId(191)] + int isOptimizedForSpeed + { + + get; + } + [DispId(192)] + uint PGOEntryCount + { + + get; + } + [DispId(193)] + uint PGOEdgeCount + { + + get; + } + [DispId(194)] + ulong PGODynamicInstructionCount + { + + get; + } + [DispId(195)] + uint staticSize + { + + get; + } + [DispId(196)] + uint finalLiveStaticSize + { + + get; + } + [DispId(197)] + string phaseName + { + + [return: MarshalAs(UnmanagedType.BStr)] + get; + } + [DispId(198)] + int hasControlFlowCheck + { + + get; + } + [DispId(199)] + int constantExport + { + + get; + } + [DispId(200)] + int dataExport + { + + get; + } + [DispId(201)] + int privateExport + { + + get; + } + [DispId(202)] + int noNameExport + { + + get; + } + [DispId(203)] + int exportHasExplicitlyAssignedOrdinal + { + + get; + } + [DispId(204)] + int exportIsForwarder + { + + get; + } + [DispId(205)] + uint ordinal + { + + get; + } + + void get_dataBytes([In] uint cbData, out uint pcbData, out byte pbData); + + void findChildren([In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void findChildrenEx([In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void findChildrenExByAddr([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([In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [In] ulong va, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void findChildrenExByRVA([In] SymTagEnum symTag, [MarshalAs(UnmanagedType.LPWStr)] [In] string name, [In] uint compareFlags, [In] uint rva, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void get_types([In] uint cTypes, out uint pcTypes, [MarshalAs(UnmanagedType.Interface)] out IDiaSymbol pTypes); + + void get_typeIds([In] uint cTypeIds, out uint pcTypeIds, out uint pdwTypeIds); + + void get_undecoratedNameEx([In] uint undecorateOptions, [MarshalAs(UnmanagedType.BStr)] out string name); + + void get_numericProperties([In] uint cnt, out uint pcnt, out uint pProperties); + + void get_modifierValues([In] uint cnt, out uint pcnt, out ushort pModifiers); + + void findInlineFramesByAddr([In] uint isect, [In] uint offset, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void findInlineFramesByRVA([In] uint rva, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void findInlineFramesByVA([In] ulong va, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumSymbols ppResult); + + void findInlineeLines([MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult); + + void findInlineeLinesByAddr([In] uint isect, [In] uint offset, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult); + + void findInlineeLinesByRVA([In] uint rva, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult); + + void findInlineeLinesByVA([In] ulong va, [In] uint length, [MarshalAs(UnmanagedType.Interface)] out IDiaEnumLineNumbers ppResult); + + void getSrcLineOnTypeDefn([MarshalAs(UnmanagedType.Interface)] out IDiaLineNumber ppResult); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaTable.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaTable.cs new file mode 100644 index 000000000..e42d52760 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IDiaTable.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IEnumUnknown.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IEnumUnknown.cs new file mode 100644 index 000000000..f4976e5d5 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IEnumUnknown.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/ISequentialStream.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/ISequentialStream.cs new file mode 100644 index 000000000..f5baae399 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/ISequentialStream.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/IStream.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IStream.cs new file mode 100644 index 000000000..dc9e41920 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/IStream.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/StreamWrapper.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/StreamWrapper.cs new file mode 100644 index 000000000..6fcf6a3ba --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/StreamWrapper.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/Structs.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/Structs.cs new file mode 100644 index 000000000..a53012f36 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/Structs.cs @@ -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; + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/DIA/SymTagEnum.cs b/src/Microsoft.Extensions.Testing.Abstractions/DIA/SymTagEnum.cs new file mode 100644 index 000000000..b1103077b --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/DIA/SymTagEnum.cs @@ -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 + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/ISourceInformationProvider.cs b/src/Microsoft.Extensions.Testing.Abstractions/ISourceInformationProvider.cs new file mode 100644 index 000000000..08c38fca8 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/ISourceInformationProvider.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/ITestDiscoverySink.cs b/src/Microsoft.Extensions.Testing.Abstractions/ITestDiscoverySink.cs new file mode 100644 index 000000000..0529c1a3b --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/ITestDiscoverySink.cs @@ -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); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/ITestExecutionSink.cs b/src/Microsoft.Extensions.Testing.Abstractions/ITestExecutionSink.cs new file mode 100644 index 000000000..0557a2287 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/ITestExecutionSink.cs @@ -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); + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/LineDelimitedJsonStream.cs b/src/Microsoft.Extensions.Testing.Abstractions/LineDelimitedJsonStream.cs new file mode 100644 index 000000000..c7ef529d8 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/LineDelimitedJsonStream.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Messages/ErrorMessage.cs b/src/Microsoft.Extensions.Testing.Abstractions/Messages/ErrorMessage.cs new file mode 100644 index 000000000..254889ef8 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Messages/ErrorMessage.cs @@ -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; } + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Messages/Message.cs b/src/Microsoft.Extensions.Testing.Abstractions/Messages/Message.cs new file mode 100644 index 000000000..5a40f7470 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Messages/Message.cs @@ -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)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Messages/ProtocolVersionMessage.cs b/src/Microsoft.Extensions.Testing.Abstractions/Messages/ProtocolVersionMessage.cs new file mode 100644 index 000000000..3aa6e78fc --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Messages/ProtocolVersionMessage.cs @@ -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; } + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Messages/RunTestsMessage.cs b/src/Microsoft.Extensions.Testing.Abstractions/Messages/RunTestsMessage.cs new file mode 100644 index 000000000..403cb033c --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Messages/RunTestsMessage.cs @@ -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 Tests { get; set; } + } +} diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj b/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj new file mode 100644 index 000000000..fa5e79f26 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Microsoft.Extensions.Testing.Abstractions.xproj @@ -0,0 +1,18 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + dcdfe282-03de-4dbc-b90c-cc3ce3ec8162 + Microsoft.Extensions.Testing.Abstractions + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Properties/AssemblyInfo.cs b/src/Microsoft.Extensions.Testing.Abstractions/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..cdbb8074c --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Properties/AssemblyInfo.cs @@ -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")] \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/SourceInformation.cs b/src/Microsoft.Extensions.Testing.Abstractions/SourceInformation.cs new file mode 100644 index 000000000..3245b291e --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/SourceInformation.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/SourceInformationProvider.cs b/src/Microsoft.Extensions.Testing.Abstractions/SourceInformationProvider.cs new file mode 100644 index 000000000..a2a5e31a7 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/SourceInformationProvider.cs @@ -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(); + 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 Classes { get; } = new Dictionary(); + } + + private class ClassData + { + public string Name { get; set; } + + public uint SymbolId { get; set; } + + public IDictionary Methods { get; } = new Dictionary(); + } + + private class MethodData + { + public string Name { get; set; } + + public uint SymbolId { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/StreamingTestDiscoverySink.cs b/src/Microsoft.Extensions.Testing.Abstractions/StreamingTestDiscoverySink.cs new file mode 100644 index 000000000..713ce446b --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/StreamingTestDiscoverySink.cs @@ -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), + }); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/StreamingTestExecutionSink.cs b/src/Microsoft.Extensions.Testing.Abstractions/StreamingTestExecutionSink.cs new file mode 100644 index 000000000..240ca788d --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/StreamingTestExecutionSink.cs @@ -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 _runningTests; + + + public StreamingTestExecutionSink(Stream stream) + { + _stream = new LineDelimitedJsonStream(stream); + _runningTests = new ConcurrentDictionary(); + } + + 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; } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/Test.cs b/src/Microsoft.Extensions.Testing.Abstractions/Test.cs new file mode 100644 index 000000000..06622eda5 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/Test.cs @@ -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(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 Properties { get; private set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/TestHostServices.cs b/src/Microsoft.Extensions.Testing.Abstractions/TestHostServices.cs new file mode 100644 index 000000000..73c6d134a --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/TestHostServices.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/TestOutcome.cs b/src/Microsoft.Extensions.Testing.Abstractions/TestOutcome.cs new file mode 100644 index 000000000..a3124a1d6 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/TestOutcome.cs @@ -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 + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/TestResult.cs b/src/Microsoft.Extensions.Testing.Abstractions/TestResult.cs new file mode 100644 index 000000000..c91cdeeb0 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/TestResult.cs @@ -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(); + } + + 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 Messages { get; private set; } + + public string ComputerName { get; set; } + + public TimeSpan Duration { get; set; } + + public DateTimeOffset StartTime { get; set; } + + public DateTimeOffset EndTime { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Testing.Abstractions/project.json b/src/Microsoft.Extensions.Testing.Abstractions/project.json new file mode 100644 index 000000000..232e683e7 --- /dev/null +++ b/src/Microsoft.Extensions.Testing.Abstractions/project.json @@ -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\"" + ] + } +} \ No newline at end of file