First Draft for the Protocol tool. (#306)

* First Draft for the Protocol tool.
Emits the json sent and recieved for Discovery, RunAll, RunSelected scenarios.

* Fixing the Testplatform.sln build.

* Changes as per discussion with Arun.
- Removed the runnerlocation, adapter and testhost configs.
This commit is contained in:
Sarabjot Singh 2017-01-03 18:16:03 +05:30 коммит произвёл Arun Mahapatra
Родитель 34a8665be2
Коммит 230c966318
14 изменённых файлов: 1085 добавлений и 13 удалений

Просмотреть файл

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26005.1
VisualStudioVersion = 15.0.26014.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED0C35EB-7F31-4841-A24F-8EB708FFA959}"
EndProject
@ -118,6 +118,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfTestProject", "test\Tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleDataCollector", "test\TestAssets\SimpleDataCollector\SimpleDataCollector.csproj", "{D62D754C-8F0A-406F-8BA7-E96C6FFA7C7C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Protocol", "samples\Microsoft.TestPlatform.Protocol\Microsoft.TestPlatform.Protocol.csproj", "{97DD9467-B011-4736-AAC4-2C21BF554349}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleTestProject3", "test\TestAssets\SimpleTestProject3\SimpleTestProject3.csproj", "{82E75225-FA92-4F87-909D-039D62F96BFF}"
EndProject
Global
@ -646,6 +648,18 @@ Global
{D62D754C-8F0A-406F-8BA7-E96C6FFA7C7C}.Release|x64.Build.0 = Release|Any CPU
{D62D754C-8F0A-406F-8BA7-E96C6FFA7C7C}.Release|x86.ActiveCfg = Release|Any CPU
{D62D754C-8F0A-406F-8BA7-E96C6FFA7C7C}.Release|x86.Build.0 = Release|Any CPU
{97DD9467-B011-4736-AAC4-2C21BF554349}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97DD9467-B011-4736-AAC4-2C21BF554349}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97DD9467-B011-4736-AAC4-2C21BF554349}.Debug|x64.ActiveCfg = Debug|x64
{97DD9467-B011-4736-AAC4-2C21BF554349}.Debug|x64.Build.0 = Debug|x64
{97DD9467-B011-4736-AAC4-2C21BF554349}.Debug|x86.ActiveCfg = Debug|x86
{97DD9467-B011-4736-AAC4-2C21BF554349}.Debug|x86.Build.0 = Debug|x86
{97DD9467-B011-4736-AAC4-2C21BF554349}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97DD9467-B011-4736-AAC4-2C21BF554349}.Release|Any CPU.Build.0 = Release|Any CPU
{97DD9467-B011-4736-AAC4-2C21BF554349}.Release|x64.ActiveCfg = Release|x64
{97DD9467-B011-4736-AAC4-2C21BF554349}.Release|x64.Build.0 = Release|x64
{97DD9467-B011-4736-AAC4-2C21BF554349}.Release|x86.ActiveCfg = Release|x86
{97DD9467-B011-4736-AAC4-2C21BF554349}.Release|x86.Build.0 = Release|x86
{82E75225-FA92-4F87-909D-039D62F96BFF}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{82E75225-FA92-4F87-909D-039D62F96BFF}.Debug|Any CPU.Build.0 = Release|Any CPU
{82E75225-FA92-4F87-909D-039D62F96BFF}.Debug|x64.ActiveCfg = Release|Any CPU
@ -712,6 +726,7 @@ Global
{A23E3408-D569-488E-A071-E1B3625C5F09} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A}
{57B182B8-9014-4C6D-B966-B464DE3127D5} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A}
{D62D754C-8F0A-406F-8BA7-E96C6FFA7C7C} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A}
{97DD9467-B011-4736-AAC4-2C21BF554349} = {B9AB7A3D-4F63-48D2-86C0-70F52F6509AB}
{82E75225-FA92-4F87-909D-039D62F96BFF} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,152 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
/// <summary>
/// JsonDataSerializes serializes and deserializes data using Json format
/// </summary>
public class JsonDataSerializer
{
private static JsonDataSerializer instance;
private static JsonSerializer serializer;
/// <summary>
/// Prevents a default instance of the <see cref="JsonDataSerializer"/> class from being created.
/// </summary>
private JsonDataSerializer()
{
serializer = JsonSerializer.Create(
new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateParseHandling = DateParseHandling.DateTimeOffset,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
TypeNameHandling = TypeNameHandling.None
});
#if DEBUG
// MemoryTraceWriter can help diagnose serialization issues. Enable it for
// debug builds only.
serializer.TraceWriter = new MemoryTraceWriter();
#endif
}
/// <summary>
/// Gets the JSON Serializer instance.
/// </summary>
public static JsonDataSerializer Instance
{
get
{
return instance ?? (instance = new JsonDataSerializer());
}
}
/// <summary>
/// Deserialize a <see cref="Message"/> from raw JSON text.
/// </summary>
/// <param name="rawMessage">JSON string.</param>
/// <returns>A <see cref="Message"/> instance.</returns>
public Message DeserializeMessage(string rawMessage)
{
return JsonConvert.DeserializeObject<Message>(rawMessage);
}
/// <summary>
/// Deserialize the <see cref="Message.Payload"/> for a message.
/// </summary>
/// <param name="message">A <see cref="Message"/> object.</param>
/// <typeparam name="T">Payload type.</typeparam>
/// <returns>The deserialized payload.</returns>
public T DeserializePayload<T>(Message message)
{
T retValue = default(T);
// TODO: Currently we use json serializer auto only for non-testmessage types
// CHECK: Can't we just use auto for everything
if (Microsoft.TestPlatform.Protocol.MessageType.TestMessage.Equals(message.MessageType))
{
retValue = message.Payload.ToObject<T>();
}
else
{
retValue = message.Payload.ToObject<T>(serializer);
}
return retValue;
}
/// <summary>
/// Deserialize raw JSON to an object using the default serializer.
/// </summary>
/// <param name="json">JSON string.</param>
/// <typeparam name="T">Target type to deserialize.</typeparam>
/// <returns>An instance of <see cref="T"/>.</returns>
public T Deserialize<T>(string json)
{
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<T>(jsonReader);
}
}
/// <summary>
/// Serialize an empty message.
/// </summary>
/// <param name="messageType">Type of the message.</param>
/// <returns>Serialized message.</returns>
public string SerializeMessage(string messageType)
{
return JsonConvert.SerializeObject(new Message { MessageType = messageType });
}
/// <summary>
/// Serialize a message with payload.
/// </summary>
/// <param name="messageType">Type of the message.</param>
/// <param name="payload">Payload for the message.</param>
/// <returns>Serialized message.</returns>
public string SerializePayload(string messageType, object payload)
{
JToken serializedPayload = null;
// TODO: Currently we use json serializer auto only for non-testmessage types
// CHECK: Can't we just use auto for everything
if (MessageType.TestMessage.Equals(messageType))
{
serializedPayload = JToken.FromObject(payload);
}
else
{
serializedPayload = JToken.FromObject(payload, serializer);
}
return JsonConvert.SerializeObject(new Message { MessageType = messageType, Payload = serializedPayload });
}
/// <summary>
/// Serialize an object to JSON using default serialization settings.
/// </summary>
/// <typeparam name="T">Type of object to serialize.</typeparam>
/// <param name="data">Instance of the object to serialize.</param>
/// <returns>JSON string.</returns>
public string Serialize<T>(T data)
{
using (var stringWriter = new StringWriter())
using (var jsonWriter = new JsonTextWriter(stringWriter))
{
serializer.Serialize(jsonWriter, data);
return stringWriter.ToString();
}
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Message
{
/// <summary>
/// Gets or sets the message type.
/// </summary>
public string MessageType { get; set; }
/// <summary>
/// Gets or sets the payload.
/// </summary>
public JToken Payload { get; set; }
/// <summary>
/// To string implementation.
/// </summary>
/// <returns> The <see cref="string"/>. </returns>
public override string ToString()
{
return "(" + MessageType + ") -> " + (Payload == null ? "null" : Payload.ToString(Formatting.Indented));
}
}
}

Просмотреть файл

@ -0,0 +1,142 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
/// <summary>
/// The message type.
/// </summary>
public static class MessageType
{
/// <summary>
/// The session start.
/// </summary>
public const string SessionStart = "TestSession.Start";
/// <summary>
/// The session end.
/// </summary>
public const string SessionEnd = "TestSession.Terminate";
/// <summary>
/// The is aborted.
/// </summary>
public const string SessionAbort = "TestSession.Abort";
/// <summary>
/// The session connected.
/// </summary>
public const string SessionConnected = "TestSession.Connected";
/// <summary>
/// Test Message
/// </summary>
public const string TestMessage = "TestSession.Message";
/// <summary>
/// Protocol Version
/// </summary>
public const string VersionCheck = "ProtocolVersion";
/// <summary>
/// The session start.
/// </summary>
public const string DiscoveryInitialize = "TestDiscovery.Initialize";
/// <summary>
/// The discovery started.
/// </summary>
public const string StartDiscovery = "TestDiscovery.Start";
/// <summary>
/// The test cases found.
/// </summary>
public const string TestCasesFound = "TestDiscovery.TestFound";
/// <summary>
/// The discovery complete.
/// </summary>
public const string DiscoveryComplete = "TestDiscovery.Completed";
/// <summary>
/// The session start.
/// </summary>
public const string ExecutionInitialize = "TestExecution.Initialize";
/// <summary>
/// Cancel the current test run
/// </summary>
public const string CancelTestRun = "TestExecution.Cancel";
/// <summary>
/// Cancel the current test run
/// </summary>
public const string AbortTestRun = "TestExecution.Abort";
/// <summary>
/// Start test execution.
/// </summary>
public const string StartTestExecutionWithSources = "TestExecution.StartWithSources";
/// <summary>
/// Start test execution.
/// </summary>
public const string StartTestExecutionWithTests = "TestExecution.StartWithTests";
/// <summary>
/// The test run stats change.
/// </summary>
public const string TestRunStatsChange = "TestExecution.StatsChange";
/// <summary>
/// The execution complete.
/// </summary>
public const string ExecutionComplete = "TestExecution.Completed";
/// <summary>
/// The message to get runner process startInfo for run all tests in given sources
/// </summary>
public const string GetTestRunnerProcessStartInfoForRunAll = "TestExecution.GetTestRunnerProcessStartInfoForRunAll";
/// <summary>
/// The message to get runner process startInfo for run selected tests
/// </summary>
public const string GetTestRunnerProcessStartInfoForRunSelected = "TestExecution.GetTestRunnerProcessStartInfoForRunSelected";
/// <summary>
/// CustomTestHostLaunch
/// </summary>
public const string CustomTestHostLaunch = "TestExecution.CustomTestHostLaunch";
/// <summary>
/// Custom Test Host launch callback
/// </summary>
public const string CustomTestHostLaunchCallback = "TestExecution.CustomTestHostLaunchCallback";
/// <summary>
/// Extensions Initialization
/// </summary>
public const string ExtensionsInitialize = "Extensions.Initialize";
/// <summary>
/// Start Test Run All Sources
/// </summary>
public const string TestRunAllSourcesWithDefaultHost = "TestExecution.RunAllWithDefaultHost";
/// <summary>
/// Start Test Run - Testcases
/// </summary>
public const string TestRunSelectedTestCasesDefaultHost = "TestExecution.RunSelectedWithDefaultHost";
/// <summary>
/// Launch Adapter Process With DebuggerAttached
/// </summary>
public const string LaunchAdapterProcessWithDebuggerAttached = "TestExecution.LaunchAdapterProcessWithDebuggerAttached";
/// <summary>
/// Launch Adapter Process With DebuggerAttached
/// </summary>
public const string LaunchAdapterProcessWithDebuggerAttachedCallback = "TestExecution.LaunchAdapterProcessWithDebuggerAttachedCallback";
}
}

Просмотреть файл

@ -0,0 +1,213 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Facilitates communication using sockets
/// </summary>
public class SocketCommunicationManager
{
/// <summary>
/// TCP Listener to host TCP channel and listen
/// </summary>
private TcpListener tcpListener;
/// <summary>
/// Binary Writer to write to channel stream
/// </summary>
private BinaryWriter binaryWriter;
/// <summary>
/// Binary reader to read from channel stream
/// </summary>
private BinaryReader binaryReader;
/// <summary>
/// Serializer for the data objects
/// </summary>
private JsonDataSerializer dataSerializer;
/// <summary>
/// Event used to maintain client connection state
/// </summary>
private ManualResetEvent clientConnectedEvent = new ManualResetEvent(false);
/// <summary>
/// Sync object for sending messages
/// SendMessage over socket channel is NOT thread-safe
/// </summary>
private object sendSyncObject = new object();
/// <summary>
/// Stream to use read timeout
/// </summary>
private NetworkStream stream;
private Socket socket;
/// <summary>
/// The server stream read timeout constant (in microseconds).
/// </summary>
private const int StreamReadTimeout = 1000 * 1000;
/// <summary>
/// Initializes a new instance of the <see cref="SocketCommunicationManager"/> class.
/// </summary>
public SocketCommunicationManager() : this(JsonDataSerializer.Instance)
{
}
internal SocketCommunicationManager(JsonDataSerializer dataSerializer)
{
this.dataSerializer = dataSerializer;
}
#region ServerMethods
/// <summary>
/// Host TCP Socket Server and start listening
/// </summary>
/// <returns></returns>
public int HostServer()
{
var endpoint = new IPEndPoint(IPAddress.Loopback, 0);
this.tcpListener = new TcpListener(endpoint);
this.tcpListener.Start();
var portNumber = ((IPEndPoint)this.tcpListener.LocalEndpoint).Port;
Console.WriteLine("Server started. Listening at port : {0}", portNumber);
return portNumber;
}
/// <summary>
/// Accepts client async
/// </summary>
public async Task AcceptClientAsync()
{
if (this.tcpListener != null)
{
this.clientConnectedEvent.Reset();
var client = await this.tcpListener.AcceptTcpClientAsync();
this.socket = client.Client;
this.stream = client.GetStream();
this.binaryReader = new BinaryReader(this.stream);
this.binaryWriter = new BinaryWriter(this.stream);
this.clientConnectedEvent.Set();
Console.WriteLine("Accepted Client request and set the flag");
}
}
/// <summary>
/// Waits for Client Connection
/// </summary>
/// <param name="clientConnectionTimeout">Time to Wait for the connection</param>
/// <returns>True if Client is connected, false otherwise</returns>
public bool WaitForClientConnection(int clientConnectionTimeout)
{
return this.clientConnectedEvent.WaitOne(clientConnectionTimeout);
}
/// <summary>
/// Stop Listener
/// </summary>
public void StopServer()
{
this.tcpListener?.Stop();
this.tcpListener = null;
this.binaryReader?.Dispose();
this.binaryWriter?.Dispose();
}
#endregion
/// <summary>
/// Writes message to the binary writer.
/// </summary>
/// <param name="messageType">Type of Message to be sent, for instance TestSessionStart</param>
public void SendMessage(string messageType)
{
var serializedObject = this.dataSerializer.SerializeMessage(messageType);
this.WriteAndFlushToChannel(serializedObject);
}
/// <summary>
/// Reads message from the binary reader
/// </summary>
/// <returns>Returns message read from the binary reader</returns>
public Message ReceiveMessage()
{
var rawMessage = this.ReceiveRawMessage();
return this.dataSerializer.DeserializeMessage(rawMessage);
}
/// <summary>
/// Writes message to the binary writer with payload
/// </summary>
/// <param name="messageType">Type of Message to be sent, for instance TestSessionStart</param>
/// <param name="payload">payload to be sent</param>
public void SendMessage(string messageType, object payload)
{
var rawMessage = this.dataSerializer.SerializePayload(messageType, payload);
this.WriteAndFlushToChannel(rawMessage);
}
/// <summary>
/// The send hand shake message.
/// </summary>
public void SendHandShakeMessage()
{
this.SendMessage(MessageType.SessionStart);
}
/// <summary>
/// Reads message from the binary reader
/// </summary>
/// <returns> Raw message string </returns>
public string ReceiveRawMessage()
{
var rawMessage = this.binaryReader.ReadString();
Console.WriteLine("\n=========== Receiving Message ===========");
Console.WriteLine(rawMessage);
return rawMessage;
}
/// <summary>
/// Deserializes the Message into actual TestPlatform objects
/// </summary>
/// <typeparam name="T"> The type of object to deserialize to. </typeparam>
/// <param name="message"> Message object </param>
/// <returns> TestPlatform object </returns>
public T DeserializePayload<T>(Message message)
{
return this.dataSerializer.DeserializePayload<T>(message);
}
/// <summary>
/// Writes the data on socket and flushes the buffer
/// </summary>
/// <param name="rawMessage">message to write</param>
private void WriteAndFlushToChannel(string rawMessage)
{
// Writing Message on binarywriter is not Thread-Safe
// Need to sync one by one to avoid buffer corruption
lock (this.sendSyncObject)
{
Console.WriteLine("\n=========== Sending Message ===========");
Console.WriteLine(rawMessage);
this.binaryWriter?.Write(rawMessage);
this.binaryWriter?.Flush();
}
}
}
}

Просмотреть файл

@ -0,0 +1,42 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
<AssemblyName>Microsoft.TestPlatform.Protocol</AssemblyName>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<EmbeddedResource Include="compiler\resources\**\*" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<PackageReference Include="System.Runtime.Serialization.Primitives">
<Version>4.1.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json">
<Version>8.0.3</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using System.Collections.Generic;
using System.Runtime.Serialization;
/// <summary>
/// Class used to define the DiscoveryRequestPayload sent by the Vstest.console translation layers into design mode
/// </summary>
public class DiscoveryRequestPayload
{
/// <summary>
/// Settings used for the discovery request.
/// </summary>
[DataMember]
public IEnumerable<string> Sources { get; set; }
/// <summary>
/// Settings used for the discovery request.
/// </summary>
[DataMember]
public string RunSettings { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using System.Collections.Generic;
using System.Runtime.Serialization;
/// <summary>
/// Class used to define the TestRunRequestPayload sent to Vstest.console in design mode
/// </summary>
public class TestRunRequestPayload
{
/// <summary>
/// Gets or sets the sources for the test run request.
/// </summary>
[DataMember]
public List<string> Sources { get; set; }
/// <summary>
/// Gets or sets the test cases for the test run request.
/// </summary>
[DataMember]
public dynamic TestCases { get; set; }
/// <summary>
/// Gets or sets the settings used for the test run request.
/// </summary>
[DataMember]
public string RunSettings { get; set; }
/// <summary>
/// Settings used for the Run request.
/// </summary>
[DataMember]
public bool KeepAlive { get; set; }
/// <summary>
/// Is Debugging enabled
/// </summary>
[DataMember]
public bool DebuggingEnabled { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,201 @@
//// Copyright (c) Microsoft Corporation. All rights reserved.
//// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading;
public class Program
{
private const string PORT_ARGUMENT = "/port:{0}";
private const string PARENT_PROCESSID_ARGUMENT = "/parentprocessid:{0}";
private static SocketCommunicationManager communicationManager;
private static JsonDataSerializer dataSerializer = JsonDataSerializer.Instance;
public static void Main(string[] args)
{
if(args == null || args.Length < 1)
{
Console.WriteLine("Please provide appropriate arguments. Arguments can be passed as following:");
Console.WriteLine("Microsoft.TestPlatform.Protocol.exe --testassembly:\"[assemblyPath]\" --operation:\"[RunAll|RunSelected|Discovery]\" --testadapterpath:\"[path]\"");
Console.WriteLine("or Microsoft.TestPlatform.Protocol.exe -a:\"[assemblyPath]\" -o:\"[RunAll|RunSelected|Discovery]\" -p:\"[path]\" \n");
}
var executingLocation = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
// Default values
var testAssembly = Path.Combine(executingLocation, "UnitTestProject.dll");
string testadapterPath = null;
string operation = "Discovery";
var separator = new char[] { ':' };
foreach (var arg in args)
{
if (arg.StartsWith("-p:") || arg.StartsWith("--testadapterpath:"))
{
testadapterPath = arg.Split(separator, 2)[1];
}
else if (arg.StartsWith("-a:") || arg.StartsWith("--testassembly:"))
{
testAssembly = arg.Split(separator,2)[1];
}
else if (arg.StartsWith("-o:") || arg.StartsWith("--operation:"))
{
operation = arg.Split(separator, 2)[1];
}
}
Console.WriteLine("TestAdapter Path : {0}", testadapterPath);
Console.WriteLine("TestAssembly Path : {0}", testAssembly);
Console.WriteLine("Operation : {0}", operation);
var processManager = new RunnerProcessManager();
communicationManager = new SocketCommunicationManager();
// Start the server
var port = communicationManager.HostServer();
//Start runner exe and wait for the connection
string parentProcessIdArgs = string.Format(CultureInfo.InvariantCulture, PARENT_PROCESSID_ARGUMENT, Process.GetCurrentProcess().Id);
string portArgs = string.Format(CultureInfo.InvariantCulture, PORT_ARGUMENT, port);
processManager.StartProcess(new string[2] { parentProcessIdArgs, portArgs });
communicationManager.AcceptClientAsync();
communicationManager.WaitForClientConnection(Timeout.Infinite);
HandShakeWithVsTestConsole();
//Actual operation
dynamic discoveredTestCases;
switch (operation.ToLower())
{
case "discovery":
discoveredTestCases = DiscoverTests(testadapterPath, testAssembly);
break;
case "runselected":
discoveredTestCases = DiscoverTests(testadapterPath, testAssembly);
RunSelectedTests(discoveredTestCases);
break;
case "runall":
default:
RunAllTests(new List<string>() { testAssembly });
break;
}
}
static void HandShakeWithVsTestConsole()
{
//HandShake with vstest.console
Console.WriteLine("=========== HandShake with vstest.console ==========");
var message = communicationManager.ReceiveMessage();
if (message.MessageType == MessageType.SessionConnected)
{
//Version Check
communicationManager.SendMessage(MessageType.VersionCheck);
message = communicationManager.ReceiveMessage();
if (message.MessageType == MessageType.VersionCheck)
{
var version = JsonDataSerializer.Instance.DeserializePayload<int>(message);
var success = version == 1;
Console.WriteLine("Version Success: {0}", success);
}
}
}
static dynamic DiscoverTests(string testadapterPath, string testAssembly)
{
Console.WriteLine("Starting Operation : Discovery");
// Intialize the extensions
if (testadapterPath != null)
{
communicationManager.SendMessage(MessageType.ExtensionsInitialize, new List<string>() { testadapterPath });
}
// Start Discovery
communicationManager.SendMessage(
MessageType.StartDiscovery,
new DiscoveryRequestPayload() { Sources = new List<string>() { testAssembly }, RunSettings = null });
var isDiscoveryComplete = false;
dynamic testCases = null;
while (!isDiscoveryComplete)
{
var message = communicationManager.ReceiveMessage();
if (string.Equals(MessageType.TestCasesFound, message.MessageType))
{
// Handle discovered tests here.
testCases = (JsonDataSerializer.Instance.DeserializePayload<dynamic>(message));
}
else if (string.Equals(MessageType.DiscoveryComplete, message.MessageType))
{
dynamic discoveryCompletePayload =
JsonDataSerializer.Instance.DeserializePayload<dynamic>(message);
//Handle discovery complete here
isDiscoveryComplete = true;
}
else if (string.Equals(MessageType.TestMessage, message.MessageType))
{
var testMessagePayload = dataSerializer.DeserializePayload<dynamic>(message);
// Handle messages here.
}
}
return testCases;
}
static void RunAllTests(List<string> sources)
{
Console.WriteLine("Starting Operation: RunAll");
communicationManager.SendMessage(MessageType.TestRunAllSourcesWithDefaultHost, new TestRunRequestPayload() { Sources = sources, RunSettings = null });
RecieveRunMesagesAndHandleRunComplete();
}
static void RunSelectedTests(dynamic testCases)
{
Console.WriteLine("Starting Operation: RunSelected");
communicationManager.SendMessage(MessageType.TestRunSelectedTestCasesDefaultHost, new TestRunRequestPayload() { TestCases = testCases, RunSettings = null });
RecieveRunMesagesAndHandleRunComplete();
}
static void RecieveRunMesagesAndHandleRunComplete()
{
var isTestRunComplete = false;
while (!isTestRunComplete)
{
var message = communicationManager.ReceiveMessage();
if (string.Equals(MessageType.TestRunStatsChange, message.MessageType))
{
var testRunChangedArgs = dataSerializer.DeserializePayload<dynamic>(message);
// Handle TestRunStatsChange here
}
else if (string.Equals(MessageType.ExecutionComplete, message.MessageType))
{
var testRunCompletePayload = dataSerializer.DeserializePayload<dynamic>(message);
// Handle TestRunComplete here
// Set the flag, to end the loop.
isTestRunComplete = true;
}
else if (string.Equals(MessageType.TestMessage, message.MessageType))
{
var testMessagePayload = dataSerializer.DeserializePayload<dynamic>(message);
// Handle log messages here
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Microsoft.TestPlatform.Protocol")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4ec14041-7804-4840-ae70-98babdc8b0e2")]

Просмотреть файл

@ -0,0 +1,7 @@
{
"profiles": {
"Microsoft.TestPlatform.Protocol": {
"commandName": "Project"
}
}
}

Просмотреть файл

@ -0,0 +1,125 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.TestPlatform.Protocol
{
using System;
using System.Diagnostics;
using System.IO;
/// <summary>
/// dotnet.exe process manager
/// </summary>
internal class RunnerProcessManager
{
private object syncObject = new object();
private bool vstestConsoleStarted = false;
private bool vstestConsoleExited = false;
private Process process;
public event EventHandler ProcessExited;
#region Constructor
public RunnerProcessManager()
{
}
#endregion Constructor
public bool IsProcessInitialized()
{
lock (syncObject)
{
return this.vstestConsoleStarted && !vstestConsoleExited &&
this.process != null;
}
}
/// <summary>
/// Call dotnet.exe with the parameters previously specified
/// </summary>
public void StartProcess(string[] args)
{
this.process = new Process();
process.StartInfo.FileName = GetDotnetHostFullPath();
if (args != null)
{
process.StartInfo.Arguments = args.Length < 2 ? args[0] : string.Join(" ", args);
}
process.StartInfo.Arguments = "vstest" + " " + process.StartInfo.Arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
lock (syncObject)
{
vstestConsoleExited = false;
vstestConsoleStarted = true;
}
}
public void ShutdownProcess()
{
// Ideally process should die by itself
if (IsProcessInitialized())
{
this.process.Kill();
this.process.Dispose();
this.process = null;
}
}
/// <summary>
/// Get full path for the .net host
/// </summary>
/// <returns>Full path to <c>dotnet</c> executable</returns>
/// <remarks>Debuggers require the full path of executable to launch it.</remarks>
private string GetDotnetHostFullPath()
{
char separator = ';';
var dotnetExeName = "dotnet.exe";
#if !NET46
// Use semicolon(;) as path separator for windows
// colon(:) for Linux and OSX
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
separator = ':';
dotnetExeName = "dotnet";
}
#endif
var pathString = Environment.GetEnvironmentVariable("PATH");
foreach (string path in pathString.Split(separator))
{
string exeFullPath = Path.Combine(path.Trim(), dotnetExeName);
if (File.Exists(exeFullPath))
{
return exeFullPath;
}
}
string errorMessage = String.Format("Unable to find dotnet.exe");
Console.WriteLine("Error : {0}", errorMessage);
throw new FileNotFoundException(errorMessage);
}
private void Process_Exited(object sender, EventArgs e)
{
lock (syncObject)
{
vstestConsoleExited = true;
this.ProcessExited?.Invoke(sender, e);
}
}
}
}

Просмотреть файл

@ -8,9 +8,62 @@ namespace UnitTestProject
[TestClass]
public class UnitTest
{
/// <summary>
/// The passing test.
/// </summary>
[TestMethod]
public void TestMethod1()
public void PassingTest()
{
Assert.AreEqual(2, 2);
}
/// <summary>
/// The failing test.
/// </summary>
[TestMethod]
public void FailingTest()
{
Assert.AreEqual(2, 3);
}
/// <summary>
/// The skipping test.
/// </summary>
[Ignore]
[TestMethod]
public void SkippingTest()
{
}
[TestCategory("CategoryA")]
[TestMethod]
public void TestWithTestCategory()
{
Assert.AreEqual(1, 1);
}
[Priority(0)]
[TestMethod]
public void TestWithPriority()
{
Assert.AreEqual(1, 1);
}
[TestProperty("Property1", "Value1")]
[TestProperty("Property2", "Value2")]
[TestMethod]
public void TestWithProperties()
{
Assert.AreEqual(1, 1);
}
[TestCategory("CategoryA")]
[Priority(1)]
[TestProperty("Property2", "Value2")]
[TestMethod]
public void FailingTestWithTraits()
{
Assert.Fail();
}
}
}

Просмотреть файл

@ -1,8 +1,8 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp1.0;net46</TargetFrameworks>
<OutputType Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">Exe</OutputType>
<AssemblyName>UnitTestProject</AssemblyName>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">$(PackageTargetFallback);dnxcore50;portable-net45+win8</PackageTargetFallback>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
@ -14,12 +14,10 @@
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<Compile Include="**\*.cs" />
<EmbeddedResource Include="compiler\resources\**\*" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
@ -29,28 +27,30 @@
<Version>1.6.0</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.0.4-preview</Version>
<Version>1.0.8-rc</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.1.8-rc</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk">
<Version>15.0.0-preview-20161123-03</Version>
<Version>15.0.0-preview-20161216-01</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<Reference Include="System.Runtime" />
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>