Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In-process vstest.console #3728

Merged
merged 28 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed public api
  • Loading branch information
Codrin Poienaru committed Jul 14, 2022
commit 7bf52fe80952cfb284c2aca477f5a9ece9514ae3
28 changes: 28 additions & 0 deletions playground/MSTest1/MSTest1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TestPlatformRoot Condition="$(TestPlatformRoot) == ''">..\..\</TestPlatformRoot>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<Import Project="$(TestPlatformRoot)scripts/build/TestPlatform.Settings.targets" />

<PropertyGroup>
<TargetFrameworks>$(TargetFrameworks);net472;net5.0</TargetFrameworks>
<Prefer32Bit>false</Prefer32Bit>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) AND '$(OS)' != 'Windows_NT' ">
<Reference Include="System" />
<Reference Include="System.Runtime" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<Import Project="$(TestPlatformRoot)scripts\build\TestPlatform.targets" />
</Project>
68 changes: 7 additions & 61 deletions playground/MSTest1/UnitTest1.cs
Original file line number Diff line number Diff line change
@@ -1,70 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

// This dll is a TestProject as well as a test adapter.We use it as a baseline for performance.
// It does no actual discovery or run.It just returns as many results as we ask it to by TEST_COUNT env variable.
// It has almost no overhead, so all the overhead we see is coming from TestPlatform.
using Microsoft.VisualStudio.TestTools.UnitTesting;

using System.Data;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MSTest1;

using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;

namespace PerfyPassing
[TestClass]
public class UnitTest1
{
[ExtensionUri(Id)]
[DefaultExecutorUri(Id)]
public class Perfy : ITestDiscoverer, ITestExecutor
[TestMethod]
public void TestMethod1()
{
static Perfy()
{
// No meaning to the number, it is just easy to find when it breaks. Better than returning 1000 or 0 which are both
// less suspicious. It could throw, but that is bad for interactive debugging.
Count = int.TryParse(Environment.GetEnvironmentVariable("TEST_COUNT") ?? "10000", out var count) ? count : 356;
}

public const string Id = "executor://perfy.testadapter";
public static readonly Uri Uri = new Uri(Id);

public static int Count { get; }

public void Cancel()
{
// noop
}

public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle)
{
var sources = tests.Select(t => t.Source).Distinct().ToList();
RunTests(sources, runContext, frameworkHandle);
}

public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrameworkHandle frameworkHandle)
{
var location = typeof(Perfy).Assembly.Location;
for (var i = 0; i < Count; i++)
{
var tc = new TestCase($"Test{i}", Uri, location);
frameworkHandle.RecordResult(new TestResult(tc)
{
Outcome = TestOutcome.Passed
});
}
}

public void DiscoverTests(IEnumerable<string> _, IDiscoveryContext _2,
IMessageLogger _3, ITestCaseDiscoverySink discoverySink)
{
var location = typeof(Perfy).Assembly.Location;
for (var i = 0; i < Count; i++)
{
var tc = new TestCase($"Test{i}", Uri, location);
discoverySink.SendTestCase(tc);
}
}
// Thread.Sleep(1000);
}
}
1 change: 0 additions & 1 deletion playground/TestPlatform.Playground/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ internal class EnvironmentVariables
["VSTEST_RUNNER_DEBUG_ATTACHVS"] = "0",
["VSTEST_HOST_DEBUG_ATTACHVS"] = "0",
["VSTEST_DATACOLLECTOR_DEBUG_ATTACHVS"] = "0",
["TEST_COUNT"] = "100000"
};

}
150 changes: 80 additions & 70 deletions playground/TestPlatform.Playground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,11 @@
using System.Threading;

using Microsoft.TestPlatform.VsTestConsole.TranslationLayer;
using Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CommandLine;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;

#nullable disable

namespace TestPlatform.Playground;

internal class Program
Expand All @@ -37,87 +33,101 @@ static void Main(string[] args)
// sub-processes. It won't stop at entry-point automatically, don't forget to set your breakpoints, or remove VSTEST_DEBUG_NOBP
// from the environment variables of this project.

EnvironmentVariables.Variables.ToList().ForEach(p => Environment.SetEnvironmentVariable(p.Key, p.Value));
var thisAssemblyPath = Assembly.GetEntryAssembly().Location;
var here = Path.GetDirectoryName(thisAssemblyPath);
var playground = Path.GetFullPath(Path.Combine(here, "..", "..", "..", ".."));

var discoverySettings = $@"
<RunSettings>
<RunConfiguration>
<InIsolation>true</InIsolation>
<MaxCpuCount>10</MaxCpuCount>
<DisableAppDomain>False</DisableAppDomain>
<BatchSize>10</BatchSize>
</RunConfiguration>
</RunSettings>
";

var sourceSettings = @"
<RunSettings>
<RunConfiguration>
<InIsolation>true</InIsolation>
<MaxCpuCount>10</MaxCpuCount>
<BatchSize>10</BatchSize>
</RunConfiguration>
</RunSettings>
";
var console = Path.Combine(here, "vstest.console", "vstest.console.exe");

var maxCpuCount = Environment.GetEnvironmentVariable("VSTEST_MAX_CPU_COUNT") ?? "0";
var sourceSettings = $$$"""
<RunSettings>
<RunConfiguration>

<!-- <MaxCpuCount>1</MaxCpuCount> -->
<!-- <TargetPlatform>x86</TargetPlatform> -->
<!-- <TargetFrameworkVersion>net472</TargetFrameworkVersion> -->

<!-- The settings below are what VS sends by default. -->
<CollectSourceInformation>False</CollectSourceInformation>
<DesignMode>True</DesignMode>
</RunConfiguration>
<BoostTestInternalSettings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<VSProcessId>999999</VSProcessId>
</BoostTestInternalSettings>
<GoogleTestAdapterSettings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SolutionSettings>
<Settings />
</SolutionSettings>
<ProjectSettings />
</GoogleTestAdapterSettings>
</RunSettings>
""";

var sources = new[] {
Path.Combine(playground, "MSTest1", "bin", "Debug", "net472", "MSTest1.dll"),
Path.Combine(playground, "MSTest1", "bin", "Debug", "net5.0", "MSTest1.dll"),
};

//// console mode
//var settingsFile = Path.GetTempFileName();
//try
//{
// File.WriteAllText(settingsFile, sourceSettings);
// var process = Process.Start(console, string.Join(" ", sources) + " --settings:" + settingsFile + " --listtests");
// var cmd = console + "\n\n" + string.Join(" ", sources) + " --settings:" + settingsFile + " --listtests";
// var swc = Stopwatch.StartNew();
// process.WaitForExit();
// if (process.ExitCode != 0)
// {
// throw new Exception($"Process failed with {process.ExitCode}");
// }
// Console.WriteLine($"Done in {swc.ElapsedMilliseconds} ms");
//}
//finally
//{
// try { File.Delete(settingsFile); } catch { }
//}
// console mode
var settingsFile = Path.GetTempFileName();
try
{
File.WriteAllText(settingsFile, sourceSettings);
var processStartInfo = new ProcessStartInfo
{
FileName = console,
Arguments = $"{string.Join(" ", sources)} --settings:{settingsFile} --listtests",
UseShellExecute = false,
};
EnvironmentVariables.Variables.ToList().ForEach(processStartInfo.Environment.Add);
var process = Process.Start(processStartInfo);
process.WaitForExit();
if (process.ExitCode != 0)
{
throw new Exception($"Process failed with {process.ExitCode}");
}
}
finally
{
try { File.Delete(settingsFile); } catch { }
}

// design mode
var dotnetExe = @"C:\Program Files\dotnet\dotnet.exe";
var consoleDll = Path.Combine(here, "vstest.console", "vstest.console.dll");
var consoleOptions = new ConsoleParameters
{
//LogFilePath = Path.Combine(here, "logs", "log.txt"),
//TraceLevel = TraceLevel.Off,
EnvironmentVariables = EnvironmentVariables.Variables,
LogFilePath = Path.Combine(here, "logs", "log.txt"),
TraceLevel = TraceLevel.Off,
};
var options = new TestPlatformOptions { CollectMetrics = true };
// var r = new VsTestConsoleWrapper(consoleDll, dotnetExe, consoleOptions);
// r.StartSession();
IVsTestConsoleWrapper r = new InProcessVsTestConsoleWrapper(consoleOptions);

var options = new TestPlatformOptions
{
CollectMetrics = true,
};
var r = new VsTestConsoleWrapper(console, consoleOptions);
var sessionHandler = new TestSessionHandler();
#pragma warning disable CS0618 // Type or member is obsolete
// r.StartTestSession(sources, sourceSettings, sessionHandler);
//// TestSessions
// r.StartTestSession(sources, sourceSettings, sessionHandler);
#pragma warning restore CS0618 // Type or member is obsolete
var discoveryHandler = new PlaygroundTestDiscoveryHandler();
var sw = Stopwatch.StartNew();
r.DiscoverTests(sources, discoverySettings, options, sessionHandler.TestSessionInfo, discoveryHandler);
var dd = sw.ElapsedMilliseconds;
Console.WriteLine($"Discovery done in {sw.ElapsedMilliseconds} ms");
sw.Restart();
// Discovery
r.DiscoverTests(sources, sourceSettings, options, sessionHandler.TestSessionInfo, discoveryHandler);
var discoveryDuration = sw.ElapsedMilliseconds;
Console.WriteLine($"Discovery done in {discoveryDuration} ms");
sw.Restart();
// Run with test cases and custom testhost launcher
r.RunTestsWithCustomTestHost(discoveryHandler.TestCases, sourceSettings, options, sessionHandler.TestSessionInfo, new TestRunHandler(), new DebuggerTestHostLauncher());
//// Run with test cases and without custom testhost launcher
//r.RunTests(discoveryHandler.TestCases, sourceSettings, options, sessionHandler.TestSessionInfo, new TestRunHandler());
//// Run with sources and custom testhost launcher
//r.RunTestsWithCustomTestHost(sources, sourceSettings, options, sessionHandler.TestSessionInfo, new TestRunHandler(), new DebuggerTestHostLauncher());
//// Run with sources
//r.RunTests(sources, sourceSettings, options, sessionHandler.TestSessionInfo, new TestRunHandler());
var rd = sw.ElapsedMilliseconds;
Console.WriteLine($"Discovery: {dd} ms, Run: {rd} ms, Total: {dd + rd} ms");
Console.WriteLine($"Discovery: {discoveryDuration} ms, Run: {rd} ms, Total: {discoveryDuration + rd} ms");
Console.WriteLine($"Settings:\n{sourceSettings}");
}

public class PlaygroundTestDiscoveryHandler : ITestDiscoveryEventsHandler, ITestDiscoveryEventsHandler2
Expand All @@ -128,25 +138,25 @@ public class PlaygroundTestDiscoveryHandler : ITestDiscoveryEventsHandler, ITest

public void HandleDiscoveredTests(IEnumerable<TestCase>? discoveredTestCases)
{
//Console.WriteLine($"[DISCOVERY.PROGRESS]");
//Console.WriteLine(WriteTests(discoveredTestCases));
Console.WriteLine($"[DISCOVERY.PROGRESS]");
Console.WriteLine(WriteTests(discoveredTestCases));
_testCasesCount += discoveredTestCases.Count();
if (discoveredTestCases != null) { TestCases.AddRange(discoveredTestCases); }
}

public void HandleDiscoveryComplete(long totalTests, IEnumerable<TestCase>? lastChunk, bool isAborted)
{
Console.WriteLine($"[DISCOVERY.COMPLETE] aborted? {isAborted}, tests count: {totalTests}");
//Console.WriteLine("Last chunk:");
//Console.WriteLine(WriteTests(lastChunk));
Console.WriteLine("Last chunk:");
Console.WriteLine(WriteTests(lastChunk));
if (lastChunk != null) { TestCases.AddRange(lastChunk); }
}

public void HandleDiscoveryComplete(DiscoveryCompleteEventArgs discoveryCompleteEventArgs, IEnumerable<TestCase>? lastChunk)
{
Console.WriteLine($"[DISCOVERY.COMPLETE] aborted? {discoveryCompleteEventArgs.IsAborted}, tests count: {discoveryCompleteEventArgs.TotalCount}, discovered count: {_testCasesCount}");
//Console.WriteLine("Last chunk:");
//Console.WriteLine(WriteTests(lastChunk));
Console.WriteLine("Last chunk:");
Console.WriteLine(WriteTests(lastChunk));
Console.WriteLine("Fully discovered:");
Console.WriteLine(WriteSources(discoveryCompleteEventArgs.FullyDiscoveredSources));
Console.WriteLine("Partially discovered:");
Expand All @@ -165,7 +175,7 @@ public void HandleLogMessage(TestMessageLevel level, string? message)

public void HandleRawMessage(string rawMessage)
{
//Console.WriteLine($"[DISCOVERY.MESSAGE] {rawMessage}");
Console.WriteLine($"[DISCOVERY.MESSAGE] {rawMessage}");
}

private static string WriteTests(IEnumerable<TestCase>? testCases)
Expand Down Expand Up @@ -193,19 +203,19 @@ public void HandleLogMessage(TestMessageLevel level, string? message)

public void HandleRawMessage(string rawMessage)
{
//Console.WriteLine($"[RUN.MESSAGE]: {rawMessage}");
Console.WriteLine($"[RUN.MESSAGE]: {rawMessage}");
}

public void HandleTestRunComplete(TestRunCompleteEventArgs testRunCompleteArgs, TestRunChangedEventArgs? lastChunkArgs, ICollection<AttachmentSet>? runContextAttachments, ICollection<string>? executorUris)
{
Console.WriteLine($"[RUN.COMPLETE]: err: {testRunCompleteArgs.Error}, lastChunk:");
//Console.WriteLine(WriteTests(lastChunkArgs?.NewTestResults));
Console.WriteLine(WriteTests(lastChunkArgs?.NewTestResults));
}

public void HandleTestRunStatsChange(TestRunChangedEventArgs? testRunChangedArgs)
{
//Console.WriteLine($"[RUN.PROGRESS]");
//Console.WriteLine(WriteTests(testRunChangedArgs.NewTestResults));
Console.WriteLine($"[RUN.PROGRESS]");
Console.WriteLine(WriteTests(testRunChangedArgs?.NewTestResults));
}

public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
Expand Down
Loading