diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationManager.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationManager.cs
index 146ff80540..4e6b617b3e 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationManager.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ICommunicationManager.cs
@@ -105,6 +105,14 @@ public interface ICommunicationManager
/// payload to be sent
void SendMessage(string messageType, object payload);
+ ///
+ /// Writes message to the binary writer with payload
+ ///
+ /// Type of Message to be sent, for instance TestSessionStart
+ /// payload to be sent
+ /// version to be sent
+ void SendMessage(string messageType, object payload, int version);
+
///
/// Send serialized raw message
///
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/IDataSerializer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/IDataSerializer.cs
index a5ec20fe11..5704a10e14 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/IDataSerializer.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/IDataSerializer.cs
@@ -37,5 +37,14 @@ public interface IDataSerializer
/// Payload of the message
/// Raw Serialized message
string SerializePayload(string messageType, object payload);
+
+ ///
+ /// Serializes and creates a raw message given a message type and the object payload
+ ///
+ /// Message Type
+ /// Payload of the message
+ /// version to be sent
+ /// Raw Serialized message
+ string SerializePayload(string messageType, object payload, int version);
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestSender.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestSender.cs
index d5362332d0..553d9c297a 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestSender.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Interfaces/ITestRequestSender.cs
@@ -20,6 +20,11 @@ public interface ITestRequestSender : IDisposable
/// Port Number of the communication channel
int InitializeCommunication();
+ ///
+ /// Used for protocol version check with TestHost
+ ///
+ void CheckVersionWithTestHost();
+
///
/// Waits for Request Handler to be connected
///
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs
index 8123fc08d4..e749781816 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs
@@ -6,7 +6,6 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
using System.IO;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Serialization;
using Newtonsoft.Json;
@@ -20,26 +19,33 @@ public class JsonDataSerializer : IDataSerializer
{
private static JsonDataSerializer instance;
- private static JsonSerializer serializer;
+ private static JsonSerializer payloadSerializer;
+ private static JsonSerializer payloadSerializer2;
///
/// Prevents a default instance of the class from being created.
///
private JsonDataSerializer()
{
- serializer = JsonSerializer.Create(
- new JsonSerializerSettings
- {
- ContractResolver = new TestPlatformContractResolver(),
- DateFormatHandling = DateFormatHandling.IsoDateFormat,
- DateParseHandling = DateParseHandling.DateTimeOffset,
- DateTimeZoneHandling = DateTimeZoneHandling.Utc,
- TypeNameHandling = TypeNameHandling.None
- });
+ var jsonSettings = new JsonSerializerSettings
+ {
+ DateFormatHandling = DateFormatHandling.IsoDateFormat,
+ DateParseHandling = DateParseHandling.DateTimeOffset,
+ DateTimeZoneHandling = DateTimeZoneHandling.Utc,
+ TypeNameHandling = TypeNameHandling.None
+ };
+
+ payloadSerializer = JsonSerializer.Create(jsonSettings);
+ payloadSerializer2 = JsonSerializer.Create(jsonSettings);
+
+ payloadSerializer.ContractResolver = new TestPlatformContractResolver1();
+ payloadSerializer2.ContractResolver = new DefaultTestPlatformContractResolver();
+
#if DEBUG
// MemoryTraceWriter can help diagnose serialization issues. Enable it for
// debug builds only.
- serializer.TraceWriter = new MemoryTraceWriter();
+ payloadSerializer.TraceWriter = new MemoryTraceWriter();
+ payloadSerializer2.TraceWriter = new MemoryTraceWriter();
#endif
}
@@ -61,7 +67,9 @@ public static JsonDataSerializer Instance
/// A instance.
public Message DeserializeMessage(string rawMessage)
{
- return JsonConvert.DeserializeObject(rawMessage);
+ // Convert to VersionedMessage
+ // Message can be deserialized to VersionedMessage where version will be 0
+ return JsonConvert.DeserializeObject(rawMessage);
}
///
@@ -74,17 +82,10 @@ public T DeserializePayload(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 (MessageType.TestMessage.Equals(message.MessageType))
- {
- retValue = message.Payload.ToObject();
- }
- else
- {
- retValue = message.Payload.ToObject(serializer);
- }
+ var versionedMessage = message as VersionedMessage;
+ var serializer = this.GetPayloadSerializer(versionedMessage?.Version);
+ retValue = message.Payload.ToObject(serializer);
return retValue;
}
@@ -92,10 +93,13 @@ public T DeserializePayload(Message message)
/// Deserialize raw JSON to an object using the default serializer.
///
/// JSON string.
+ /// Version of serializer to be used.
/// Target type to deserialize.
/// An instance of .
- public T Deserialize(string json)
+ public T Deserialize(string json, int version = 1)
{
+ var serializer = this.GetPayloadSerializer(version);
+
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
{
@@ -121,30 +125,41 @@ public string SerializeMessage(string messageType)
/// Serialized message.
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);
- }
+ var serializedPayload = JToken.FromObject(payload, payloadSerializer);
return JsonConvert.SerializeObject(new Message { MessageType = messageType, Payload = serializedPayload });
}
+ ///
+ /// Serialize a message with payload.
+ ///
+ /// Type of the message.
+ /// Payload for the message.
+ /// Version for the message.
+ /// Serialized message.
+ public string SerializePayload(string messageType, object payload, int version)
+ {
+ var serializer = this.GetPayloadSerializer(version);
+ var serializedPayload = JToken.FromObject(payload, serializer);
+
+ var message = version == 1 ?
+ new Message { MessageType = messageType, Payload = serializedPayload } :
+ new VersionedMessage { MessageType = messageType, Version = version, Payload = serializedPayload };
+
+ return JsonConvert.SerializeObject(message);
+ }
+
///
/// Serialize an object to JSON using default serialization settings.
///
/// Type of object to serialize.
/// Instance of the object to serialize.
+ /// Version to be stamped.
/// JSON string.
- public string Serialize(T data)
+ public string Serialize(T data, int version = 1)
{
+ var serializer = this.GetPayloadSerializer(version);
+
using (var stringWriter = new StringWriter())
using (var jsonWriter = new JsonTextWriter(stringWriter))
{
@@ -153,5 +168,10 @@ public string Serialize(T data)
return stringWriter.ToString();
}
}
+
+ private JsonSerializer GetPayloadSerializer(int? version)
+ {
+ return version == 2 ? payloadSerializer2 : payloadSerializer;
+ }
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/Message.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/Message.cs
index 1a98a1e1a4..2ea60f3454 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/Message.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/Message.cs
@@ -6,6 +6,9 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+ ///
+ /// Construct used for communication
+ ///
public class Message
{
///
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs
index 7b731afacf..e20d4fa303 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs
@@ -38,6 +38,11 @@ public static class MessageType
///
public const string VersionCheck = "ProtocolVersion";
+ ///
+ /// Protocol Error
+ ///
+ public const string ProtocolError = "ProtocolError";
+
///
/// The session start.
///
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/VersionedMessage.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/VersionedMessage.cs
new file mode 100644
index 0000000000..a48e59cdae
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/VersionedMessage.cs
@@ -0,0 +1,17 @@
+// 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.VisualStudio.TestPlatform.CommunicationUtilities
+{
+ ///
+ /// Construct with version used for communication
+ /// Introduced in 15.1.0 version and default message protocol v2 onwards.
+ ///
+ public class VersionedMessage : Message
+ {
+ ///
+ /// Gets or sets the version of the message
+ ///
+ public int Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.Designer.cs
index e5584b81e8..e2d5c5a448 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.Designer.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.Designer.cs
@@ -107,5 +107,27 @@ internal static string DataCollectorUriForLogMessage
return ResourceManager.GetString("DataCollectorUriForLogMessage", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Unexpected message received. Expected MessageType : {0} Actual MessageType: {1}.
+ ///
+ internal static string UnexpectedMessage
+ {
+ get
+ {
+ return ResourceManager.GetString("UnexpectedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Protocol version check failed. Make sure test runner and host are compatible..
+ ///
+ internal static string VersionCheckFailed
+ {
+ get
+ {
+ return ResourceManager.GetString("VersionCheckFailed", resourceCulture);
+ }
+ }
}
}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.resx b/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.resx
index 13411e6979..92ba88351c 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.resx
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Resources/Resources.resx
@@ -132,4 +132,10 @@
Unable to communicate with test host process.
+
+ Unexpected message received. Expected MessageType : {0} Actual MessageType: {1}
+
+
+ Protocol version check failed. Make sure test runner and host are compatible.
+
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestPlatformContractResolver.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/DefaultTestPlatformContractResolver.cs
similarity index 96%
rename from src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestPlatformContractResolver.cs
rename to src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/DefaultTestPlatformContractResolver.cs
index 30be74678e..3b8e3c526f 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestPlatformContractResolver.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/DefaultTestPlatformContractResolver.cs
@@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Serializati
///
/// JSON contract resolver for mapping test platform types.
///
- public class TestPlatformContractResolver : DefaultContractResolver
+ public class DefaultTestPlatformContractResolver : DefaultContractResolver
{
///
protected override JsonContract CreateContract(Type objectType)
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestCaseConverter.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestCaseConverter.cs
new file mode 100644
index 0000000000..03027c3393
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestCaseConverter.cs
@@ -0,0 +1,51 @@
+// 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.VisualStudio.TestPlatform.CommunicationUtilities.Serialization
+{
+ using System;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ using Newtonsoft.Json;
+
+ ///
+ /// Converter used by v1 protocol serializer to serialize TestCase object to and from v1 json
+ ///
+ public class TestCaseConverter : JsonConverter
+ {
+ ///
+ public override bool CanRead => false;
+
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(TestCase) == objectType;
+ }
+
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ // We do not need this as SetPropetyValue inside StoreKvpList will
+ // set the properties as expected.
+ throw new NotImplementedException();
+ }
+
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ // P2 to P1
+ var testCase = value as TestCase;
+ var properties = testCase.GetProperties();
+
+ writer.WriteStartObject();
+ writer.WritePropertyName("Properties");
+ writer.WriteStartArray();
+ foreach (var property in properties)
+ {
+ serializer.Serialize(writer, property);
+ }
+
+ writer.WriteEndArray();
+ writer.WriteEndObject();
+ }
+ }
+}
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestPlatformContractResolver1.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestPlatformContractResolver1.cs
new file mode 100644
index 0000000000..0f50bd53f0
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestPlatformContractResolver1.cs
@@ -0,0 +1,31 @@
+// 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.VisualStudio.TestPlatform.CommunicationUtilities.Serialization
+{
+ using System;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ using Newtonsoft.Json.Serialization;
+
+ ///
+ /// JSON contract resolver for mapping test platform types for v1 serialization.
+ ///
+ public class TestPlatformContractResolver1 : DefaultTestPlatformContractResolver
+ {
+ ///
+ protected override JsonContract CreateContract(Type objectType)
+ {
+ var contract = base.CreateContract(objectType);
+ if (typeof(TestCase) == objectType)
+ {
+ contract.Converter = new TestCaseConverter();
+ }
+ else if (typeof(TestResult) == objectType)
+ {
+ contract.Converter = new TestResultConverter();
+ }
+
+ return contract;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestResultConverter.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestResultConverter.cs
new file mode 100644
index 0000000000..9824880490
--- /dev/null
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Serialization/TestResultConverter.cs
@@ -0,0 +1,58 @@
+// 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.VisualStudio.TestPlatform.CommunicationUtilities.Serialization
+{
+ using System;
+ using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+ using Newtonsoft.Json;
+
+ ///
+ /// Converter used by v1 protocol serializer to serialize TestResult object to and from v1 json
+ ///
+ public class TestResultConverter : JsonConverter
+ {
+ ///
+ public override bool CanRead => false;
+
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(TestResult) == objectType;
+ }
+
+ ///
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ // We do not need this as SetPropetyValue inside StoreKvpList will
+ // set the properties as expected.
+ throw new NotImplementedException();
+ }
+
+ ///
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ // P2 to P1
+ var testResult = value as TestResult;
+ var properties = testResult.GetProperties();
+
+ writer.WriteStartObject();
+ writer.WritePropertyName("TestCase");
+ serializer.Serialize(writer, testResult.TestCase);
+ writer.WritePropertyName("Attachments");
+ serializer.Serialize(writer, testResult.Attachments);
+ writer.WritePropertyName("Messages");
+ serializer.Serialize(writer, testResult.Messages);
+
+ writer.WritePropertyName("Properties");
+ writer.WriteStartArray();
+ foreach (var property in properties)
+ {
+ serializer.Serialize(writer, property);
+ }
+
+ writer.WriteEndArray();
+ writer.WriteEndObject();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/SocketCommunicationManager.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/SocketCommunicationManager.cs
index 393a46c4a8..8a39ca8520 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/SocketCommunicationManager.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/SocketCommunicationManager.cs
@@ -215,6 +215,18 @@ public void SendMessage(string messageType, object payload)
this.WriteAndFlushToChannel(rawMessage);
}
+ ///
+ /// Writes message to the binary writer with payload
+ ///
+ /// Type of Message to be sent, for instance TestSessionStart
+ /// payload to be sent
+ /// version to be sent
+ public void SendMessage(string messageType, object payload, int version)
+ {
+ var rawMessage = this.dataSerializer.SerializePayload(messageType, payload, version);
+ this.WriteAndFlushToChannel(rawMessage);
+ }
+
///
/// Send serialized raw message
///
diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
index 97d998b772..c0001b38e6 100644
--- a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
+++ b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs
@@ -5,16 +5,15 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities
{
using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
-
using CommonResources = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources.Resources;
///
@@ -28,6 +27,9 @@ public sealed class TestRequestSender : ITestRequestSender
private IDataSerializer dataSerializer;
+ // TODO:sasin Change the version to 2
+ private int highestNegotiatedVersion = 1;
+
///
/// Use to cancel blocking tasks associated with testhost process
///
@@ -35,11 +37,19 @@ public sealed class TestRequestSender : ITestRequestSender
private string clientExitErrorMessage;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public TestRequestSender()
: this(new SocketCommunicationManager(), JsonDataSerializer.Instance)
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Communication Manager for sending and receiving messages.
+ /// Serializer for serialization and deserialization of the messages.
internal TestRequestSender(ICommunicationManager communicationManager, IDataSerializer dataSerializer)
{
this.communicationManager = communicationManager;
@@ -75,16 +85,41 @@ public void Close()
EqtTrace.Info("Closing the connection");
}
+ ///
+ public void CheckVersionWithTestHost()
+ {
+ this.communicationManager.SendMessage(MessageType.VersionCheck, payload: this.highestNegotiatedVersion);
+
+ var message = this.communicationManager.ReceiveMessage();
+
+ if (message.MessageType == MessageType.VersionCheck)
+ {
+ var protocolVersion = this.dataSerializer.DeserializePayload(message);
+ this.highestNegotiatedVersion = protocolVersion;
+
+ EqtTrace.Info("TestRequestSender: VersionCheck Succeeded, NegotiatedVersion = {0}", this.highestNegotiatedVersion);
+ }
+ else if (message.MessageType == MessageType.ProtocolError)
+ {
+ // TODO : Payload for ProtocolError needs to finalized.
+ throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.VersionCheckFailed));
+ }
+ else
+ {
+ throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CommonResources.UnexpectedMessage, MessageType.VersionCheck, message.MessageType));
+ }
+ }
+
///
public void InitializeDiscovery(IEnumerable pathToAdditionalExtensions, bool loadOnlyWellKnownExtensions)
{
- this.communicationManager.SendMessage(MessageType.DiscoveryInitialize, pathToAdditionalExtensions);
+ this.communicationManager.SendMessage(MessageType.DiscoveryInitialize, pathToAdditionalExtensions, version: this.highestNegotiatedVersion);
}
///
public void InitializeExecution(IEnumerable pathToAdditionalExtensions, bool loadOnlyWellKnownExtensions)
{
- this.communicationManager.SendMessage(MessageType.ExecutionInitialize, pathToAdditionalExtensions);
+ this.communicationManager.SendMessage(MessageType.ExecutionInitialize, pathToAdditionalExtensions, version: this.highestNegotiatedVersion);
}
///
@@ -92,7 +127,7 @@ public void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEve
{
try
{
- this.communicationManager.SendMessage(MessageType.StartDiscovery, discoveryCriteria);
+ this.communicationManager.SendMessage(MessageType.StartDiscovery, discoveryCriteria, version: this.highestNegotiatedVersion);
var isDiscoveryComplete = false;
@@ -179,11 +214,18 @@ public void SendTestRunCancel()
this.communicationManager.SendMessage(MessageType.CancelTestRun);
}
+ ///
+ /// Send the Abort test run message
+ ///
public void SendTestRunAbort()
{
this.communicationManager.SendMessage(MessageType.AbortTestRun);
}
+ ///
+ /// Handles exit of the client process.
+ ///
+ /// Standard Error.
public void OnClientProcessExit(string stdError)
{
this.clientExitErrorMessage = stdError;
@@ -197,7 +239,7 @@ private void StartTestRunAndListenAndReportTestResults(
{
try
{
- this.communicationManager.SendMessage(messageType, payload);
+ this.communicationManager.SendMessage(messageType, payload, version: this.highestNegotiatedVersion);
// This needs to happen asynchronously.
Task.Run(() => this.ListenAndReportTestResults(eventHandler));
@@ -256,7 +298,8 @@ private void ListenAndReportTestResults(ITestRunEventsHandler testRunEventsHandl
this.communicationManager.SendMessage(
MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback,
- processId);
+ processId,
+ version: this.highestNegotiatedVersion);
}
}
catch (IOException exception)
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs
index 962bc6e432..c10e88f367 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs
@@ -18,6 +18,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client
using Microsoft.VisualStudio.TestPlatform.Utilities;
using CrossPlatEngineResources = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources.Resources;
+ using System.Reflection;
+ using System.Linq;
///
/// Base class for any operations that the client needs to drive through the engine.
@@ -30,7 +32,7 @@ public abstract class ProxyOperationManager
private bool initialized;
private string testHostProcessStdError;
-
+ private readonly string versionCheckPropertyName = "IsVersionCheckRequired";
#region Constructors
///
@@ -114,22 +116,37 @@ public virtual void SetupChannel(IEnumerable sources)
// Increase connection timeout when debugging is enabled.
connTimeout = 5 * this.connectionTimeout;
}
-
- this.initialized = true;
- }
- // Wait for a timeout for the client to connect.
- if (!this.RequestSender.WaitForRequestHandlerConnection(connTimeout))
- {
- var errorMsg = CrossPlatEngineResources.InitializationFailed;
+ // Wait for a timeout for the client to connect.
+ if (!this.RequestSender.WaitForRequestHandlerConnection(connTimeout))
+ {
+ var errorMsg = CrossPlatEngineResources.InitializationFailed;
+
+ if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError.ToString()))
+ {
+ // Testhost failed with error
+ errorMsg = string.Format(CrossPlatEngineResources.TestHostExitedWithError, this.testHostProcessStdError);
+ }
- if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError))
+ throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, errorMsg));
+ }
+
+ // Handling special case for dotnet core projects with older test hosts
+ // Older test hosts are not aware of protocol version check
+ // Hence we should not be sending VersionCheck message to these test hosts
+ bool checkRequired = true;
+ var property = this.testHostManager.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, versionCheckPropertyName, StringComparison.OrdinalIgnoreCase));
+ if (property != null)
+ {
+ checkRequired = (bool)property.GetValue(this.testHostManager);
+ }
+
+ if (checkRequired)
{
- // Testhost failed with error
- errorMsg = string.Format(CrossPlatEngineResources.TestHostExitedWithError, this.testHostProcessStdError);
+ this.RequestSender.CheckVersionWithTestHost();
}
- throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, errorMsg));
+ this.initialized = true;
}
}
diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs
index 4ee68c11bd..77a13a5f46 100644
--- a/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs
+++ b/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs
@@ -31,6 +31,12 @@ public class TestRequestHandler : IDisposable, ITestRequestHandler
private Action onAckMessageRecieved;
+ private int highestSupportedVersion = 2;
+
+ // Set default to 1, if protocol version check does not happen
+ // that implies runner is using version 1
+ private int protocolVersion = 1;
+
///
/// The timeout for the client to connect to the server.
///
@@ -80,8 +86,21 @@ public void ProcessRequests(ITestHostManagerFactory testHostManagerFactory)
do
{
var message = this.communicationManager.ReceiveMessage();
+
switch (message.MessageType)
{
+ case MessageType.VersionCheck:
+ var version = this.dataSerializer.DeserializePayload(message);
+ this.protocolVersion = Math.Min(version, highestSupportedVersion);
+ this.communicationManager.SendMessage(MessageType.VersionCheck, this.protocolVersion);
+
+ // Can only do this after InitializeCommunication because TestHost cannot "Send Log" unless communications are initialized
+ if (!string.IsNullOrEmpty(EqtTrace.LogFile))
+ {
+ this.SendLog(TestMessageLevel.Informational, string.Format("Logging TestHost Diagnostics in file: {0}", EqtTrace.LogFile));
+ }
+ break;
+
case MessageType.DiscoveryInitialize:
{
EqtTrace.Info("Discovery Session Initialize.");
@@ -212,20 +231,20 @@ public void Close()
///
public void SendTestCases(IEnumerable discoveredTestCases)
{
- this.communicationManager.SendMessage(MessageType.TestCasesFound, discoveredTestCases);
+ this.communicationManager.SendMessage(MessageType.TestCasesFound, discoveredTestCases, this.protocolVersion);
}
///
public void SendTestRunStatistics(TestRunChangedEventArgs testRunChangedArgs)
{
- this.communicationManager.SendMessage(MessageType.TestRunStatsChange, testRunChangedArgs);
+ this.communicationManager.SendMessage(MessageType.TestRunStatsChange, testRunChangedArgs, this.protocolVersion);
}
///
public void SendLog(TestMessageLevel messageLevel, string message)
{
- var testMessagePayload = new TestMessagePayload { MessageLevel = messageLevel, Message = message };
- this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload);
+ var testMessagePayload = new TestMessagePayload {MessageLevel = messageLevel,Message = message};
+ this.communicationManager.SendMessage(MessageType.TestMessage, testMessagePayload, this.protocolVersion);
}
///
@@ -243,7 +262,7 @@ public void SendExecutionComplete(
ExecutorUris = executorUris
};
- this.communicationManager.SendMessage(MessageType.ExecutionComplete, payload);
+ this.communicationManager.SendMessage(MessageType.ExecutionComplete, payload, this.protocolVersion);
}
///
@@ -256,7 +275,7 @@ public void DiscoveryComplete(long totalTests, IEnumerable lastChunk,
IsAborted = isAborted
};
- this.communicationManager.SendMessage(MessageType.DiscoveryComplete, discoveryCompletePayload);
+ this.communicationManager.SendMessage(MessageType.DiscoveryComplete, discoveryCompletePayload, this.protocolVersion);
}
///
@@ -270,7 +289,7 @@ public int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessSta
waitHandle.Set();
};
- this.communicationManager.SendMessage(MessageType.LaunchAdapterProcessWithDebuggerAttached, testProcessStartInfo);
+ this.communicationManager.SendMessage(MessageType.LaunchAdapterProcessWithDebuggerAttached, testProcessStartInfo, this.protocolVersion);
waitHandle.WaitOne();
this.onAckMessageRecieved = null;
diff --git a/src/Microsoft.TestPlatform.ObjectModel/TestCase.cs b/src/Microsoft.TestPlatform.ObjectModel/TestCase.cs
index ba550f482f..ce70de97b8 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/TestCase.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/TestCase.cs
@@ -77,7 +77,7 @@ public Object LocalExtensionData
///
/// Gets or sets the id of the test case.
///
- [IgnoreDataMember]
+ [DataMember]
public Guid Id
{
get
@@ -106,7 +106,7 @@ public Guid Id
///
/// Gets or sets the fully qualified name of the test case.
///
- [IgnoreDataMember]
+ [DataMember]
public string FullyQualifiedName
{
get
@@ -126,7 +126,7 @@ public string FullyQualifiedName
///
/// Gets or sets the display name of the test case.
///
- [IgnoreDataMember]
+ [DataMember]
public string DisplayName
{
get
@@ -143,7 +143,7 @@ public string DisplayName
///
/// Gets or sets the Uri of the Executor to use for running this test.
///
- [IgnoreDataMember]
+ [DataMember]
public Uri ExecutorUri
{
get
@@ -160,7 +160,7 @@ public Uri ExecutorUri
///
/// Gets the test container source from which the test is discovered.
///
- [IgnoreDataMember]
+ [DataMember]
public string Source
{
get
@@ -180,7 +180,7 @@ private set
///
/// Gets or sets the source code file path of the test.
///
- [IgnoreDataMember]
+ [DataMember]
public string CodeFilePath
{
get
@@ -197,7 +197,7 @@ public string CodeFilePath
///
/// Gets or sets the line number of the test.
///
- [IgnoreDataMember]
+ [DataMember]
public int LineNumber
{
get
@@ -244,6 +244,55 @@ private Guid GetTestId()
}
#endregion
+
+ #region Protected Methods
+
+ ///
+ /// Return TestProperty's value
+ ///
+ ///
+ protected override object ProtectedGetPropertyValue(TestProperty property, object defaultValue)
+ {
+ ValidateArg.NotNull(property, "property");
+
+ if (this.localStore.TryGetValue(property, out var value))
+ {
+ return value;
+ }
+
+ return base.ProtectedGetPropertyValue(property, defaultValue);
+ }
+
+ ///
+ /// Set TestProperty's value
+ ///
+ protected override void ProtectedSetPropertyValue(TestProperty property, object value)
+ {
+ ValidateArg.NotNull(property, "property");
+
+ switch (property.Id)
+ {
+ case "TestCase.Id":
+ case "TestCase.ExecutorUri":
+ case "TestCase.FullyQualifiedName":
+ case "TestCase.DisplayName":
+ case "TestCase.Source":
+ case "TestCase.CodeFilePath":
+ case "TestCase.LineNumber":
+ if (property.ValidateValueCallback == null || property.ValidateValueCallback(value))
+ {
+ this.localStore[property] = value;
+ }
+ else
+ {
+ throw new ArgumentException(property.Label);
+ }
+ return;
+ }
+ base.ProtectedSetPropertyValue(property, value);
+ }
+
+ #endregion
}
///
diff --git a/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs b/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs
index 2c954da006..423eb9e523 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/TestObject.cs
@@ -28,6 +28,11 @@ public abstract class TestObject
///
private readonly Dictionary store;
+ ///
+ /// The store for all the local properties registered.
+ ///
+ protected readonly Dictionary localStore;
+
///
/// Property used for Json (de)serialization of store dictionary. Serialization of dictionaries
/// by default doesn't provide the required object representation. List of KeyValuePair on the
@@ -64,13 +69,23 @@ private List> StoreKeyValuePairs
}
}
+ ///
+ /// Returns the list of testproperties associated with the test object
+ ///
+ ///
+ public IEnumerable> GetProperties()
+ {
+ return this.localStore.Concat(this.store);
+ }
+
#endregion Fields
#region Constructors
protected TestObject()
{
- this.store = new Dictionary();
+ this.store = new Dictionary();
+ this.localStore = new Dictionary();
}
[OnSerializing]
@@ -104,7 +119,7 @@ public void CacheLazyValuesOnSerializing(StreamingContext context)
///
public IEnumerable Properties
{
- get { return this.store.Keys; }
+ get { return this.localStore.Keys.Concat(this.store.Keys); }
}
///
@@ -124,7 +139,7 @@ public object GetPropertyValue(TestProperty property)
defaultValue = Activator.CreateInstance(valueType);
}
- return this.PrivateGetPropertyValue(property, defaultValue);
+ return this.ProtectedGetPropertyValue(property, defaultValue);
}
///
@@ -168,7 +183,7 @@ public void SetPropertyValue(TestProperty property, LazyPropertyValue valu
/// value to be set
public void SetPropertyValue(TestProperty property, object value)
{
- this.PrivateSetPropertyValue(property, value);
+ this.ProtectedSetPropertyValue(property, value);
}
///
@@ -186,7 +201,6 @@ public void RemovePropertyValue(TestProperty property)
}
}
-
///
/// Returns TestProperty's value
///
@@ -196,7 +210,7 @@ public T GetPropertyValue(TestProperty property, T defaultValue, CultureInfo
ValidateArg.NotNull(property, "property");
ValidateArg.NotNull(culture, "culture");
- object objValue = this.PrivateGetPropertyValue(property, defaultValue);
+ object objValue = this.ProtectedGetPropertyValue(property, defaultValue);
return ConvertPropertyTo(property, culture, objValue);
}
@@ -211,7 +225,7 @@ public void SetPropertyValue(TestProperty property, T value, CultureInfo cult
object objValue = ConvertPropertyFrom(property, culture, value);
- this.PrivateSetPropertyValue(property, objValue);
+ this.ProtectedSetPropertyValue(property, objValue);
}
///
@@ -224,7 +238,7 @@ public void SetPropertyValue(TestProperty property, LazyPropertyValue valu
object objValue = ConvertPropertyFrom(property, culture, value);
- this.PrivateSetPropertyValue(property, objValue);
+ this.ProtectedSetPropertyValue(property, objValue);
}
#endregion Property Values
@@ -235,12 +249,12 @@ public void SetPropertyValue(TestProperty property, LazyPropertyValue valu
/// Return TestProperty's value
///
///
- private object PrivateGetPropertyValue(TestProperty property, object defaultValue)
+ protected virtual object ProtectedGetPropertyValue(TestProperty property, object defaultValue)
{
ValidateArg.NotNull(property, "property");
object value;
- if (!this.store.TryGetValue(property, out value))
+ if (!this.store.TryGetValue(property, out value) )
{
value = defaultValue;
}
@@ -251,7 +265,7 @@ private object PrivateGetPropertyValue(TestProperty property, object defaultValu
///
/// Set TestProperty's value
///
- private void PrivateSetPropertyValue(TestProperty property, object value)
+ protected virtual void ProtectedSetPropertyValue(TestProperty property, object value)
{
ValidateArg.NotNull(property, "property");
diff --git a/src/Microsoft.TestPlatform.ObjectModel/TestResult.cs b/src/Microsoft.TestPlatform.ObjectModel/TestResult.cs
index 12a7ac6ca8..2c0271c7d3 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/TestResult.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/TestResult.cs
@@ -61,7 +61,7 @@ public TestResult(TestCase testCase)
///
/// Gets or sets the outcome of a test case.
///
- [IgnoreDataMember]
+ [DataMember]
public TestOutcome Outcome
{
get
@@ -78,7 +78,7 @@ public TestOutcome Outcome
///
/// Gets or sets the exception message.
///
- [IgnoreDataMember]
+ [DataMember]
public string ErrorMessage
{
get
@@ -95,7 +95,7 @@ public string ErrorMessage
///
/// Gets or sets the exception stack trace.
///
- [IgnoreDataMember]
+ [DataMember]
public string ErrorStackTrace
{
get
@@ -112,7 +112,7 @@ public string ErrorStackTrace
///
/// Gets or sets the TestResult Display name. Used for Data Driven Test (i.e. Data Driven Test. E.g. InlineData in xUnit)
///
- [IgnoreDataMember]
+ [DataMember]
public string DisplayName
{
get
@@ -139,7 +139,7 @@ public Collection Messages
///
/// Gets or sets test result ComputerName.
///
- [IgnoreDataMember]
+ [DataMember]
public string ComputerName
{
get
@@ -156,7 +156,7 @@ public string ComputerName
///
/// Gets or sets the test result Duration.
///
- [IgnoreDataMember]
+ [DataMember]
public TimeSpan Duration
{
get
@@ -173,7 +173,7 @@ public TimeSpan Duration
///
/// Gets or sets the test result StartTime.
///
- [IgnoreDataMember]
+ [DataMember]
public DateTimeOffset StartTime
{
get
@@ -190,7 +190,7 @@ public DateTimeOffset StartTime
///
/// Gets or sets test result EndTime.
///
- [IgnoreDataMember]
+ [DataMember]
public DateTimeOffset EndTime
{
get
@@ -207,7 +207,7 @@ public DateTimeOffset EndTime
#endregion
#region Methods
-
+
///
public override string ToString()
@@ -266,6 +266,56 @@ public override string ToString()
}
#endregion
+
+ #region Protected Methods
+
+ ///
+ /// Return TestProperty's value
+ ///
+ ///
+ protected override object ProtectedGetPropertyValue(TestProperty property, object defaultValue)
+ {
+ ValidateArg.NotNull(property, "property");
+
+ if (this.localStore.TryGetValue(property, out var value))
+ {
+ return value;
+ }
+
+ return base.ProtectedGetPropertyValue(property, defaultValue);
+ }
+
+ ///
+ /// Set TestProperty's value
+ ///
+ protected override void ProtectedSetPropertyValue(TestProperty property, object value)
+ {
+ ValidateArg.NotNull(property, "property");
+
+ switch (property.Id)
+ {
+ case "TestResult.DisplayName":
+ case "TestResult.ComputerName":
+ case "TestResult.Outcome":
+ case "TestResult.Duration":
+ case "TestResult.StartTime":
+ case "TestResult.EndTime":
+ case "TestResult.ErrorMessage":
+ case "TestResult.ErrorStackTrace":
+ if (property.ValidateValueCallback == null || property.ValidateValueCallback(value))
+ {
+ this.localStore[property] = value;
+ }
+ else
+ {
+ throw new ArgumentException(property.Label);
+ }
+ return;
+ }
+ base.ProtectedSetPropertyValue(property, value);
+ }
+
+ #endregion
}
///
@@ -339,7 +389,7 @@ public static class TestResultProperties
public static readonly TestProperty DisplayName = TestProperty.Register("TestResult.DisplayName", "TestResult Display Name", typeof(string), TestPropertyAttributes.Hidden, typeof(TestResult));
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
- public static readonly TestProperty ComputerName = TestProperty.Register("TestResult.ComputerName", "Computer Name", string.Empty, string.Empty, typeof(string), ValidateComputerName, TestPropertyAttributes.None, typeof(TestResult));
+ public static readonly TestProperty ComputerName = TestProperty.Register("TestResult.ComputerName", "Computer Name", typeof(string), TestPropertyAttributes.None, typeof(TestResult));
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly TestProperty Outcome = TestProperty.Register("TestResult.Outcome", "Outcome", string.Empty, string.Empty, typeof(TestOutcome), ValidateOutcome, TestPropertyAttributes.None, typeof(TestResult));
@@ -363,7 +413,7 @@ public static class TestResultProperties
public static readonly TestProperty DisplayName = TestProperty.Register("TestResult.DisplayName", Resources.Resources.TestResultPropertyDisplayNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestResult));
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
- public static readonly TestProperty ComputerName = TestProperty.Register("TestResult.ComputerName", Resources.Resources.TestResultPropertyComputerNameLabel, string.Empty, string.Empty, typeof(string), ValidateComputerName, TestPropertyAttributes.None, typeof(TestResult));
+ public static readonly TestProperty ComputerName = TestProperty.Register("TestResult.ComputerName", Resources.Resources.TestResultPropertyComputerNameLabel, typeof(string), TestPropertyAttributes.None, typeof(TestResult));
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly TestProperty Outcome = TestProperty.Register("TestResult.Outcome", Resources.Resources.TestResultPropertyOutcomeLabel, string.Empty, string.Empty, typeof(TestOutcome), ValidateOutcome, TestPropertyAttributes.None, typeof(TestResult));
@@ -383,12 +433,7 @@ public static class TestResultProperties
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly TestProperty ErrorStackTrace = TestProperty.Register("TestResult.ErrorStackTrace", Resources.Resources.TestResultPropertyErrorStackTraceLabel, typeof(string), typeof(TestResult));
#endif
-
- private static bool ValidateComputerName(object value)
- {
- return !string.IsNullOrWhiteSpace((string)value);
- }
-
+
private static bool ValidateOutcome(object value)
{
return (TestOutcome)value <= TestOutcome.NotFound && (TestOutcome)value >= TestOutcome.None;
diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs
index c6f41f6e46..927a0a8c25 100644
--- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs
+++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs
@@ -62,6 +62,8 @@ public class DotnetTestHostManager : ITestRuntimeProvider
private bool hostExitedEventRaised;
+ private string hostPackageVersion = "15.0.0";
+
///
/// Initializes a new instance of the class.
///
@@ -102,6 +104,11 @@ internal DotnetTestHostManager(
///
public bool Shared => false;
+ ///
+ /// Gets a value indicating whether the test host supports protocol version check
+ ///
+ internal virtual bool IsVersionCheckRequired => !this.hostPackageVersion.StartsWith("15.0.0");
+
///
/// Gets or sets the error length for runtime error stream.
///
@@ -175,7 +182,7 @@ public void SetCustomLauncher(ITestHostLauncher customLauncher)
}
///
- public async Task LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo)
+ public virtual async Task LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo)
{
return await Task.Run(() => this.LaunchHost(testHostStartInfo), this.GetCancellationTokenSource().Token);
}
@@ -371,6 +378,7 @@ private string GetTestHostPath(string runtimeConfigDevPath, string depsFilePath,
}
testHostPath = Path.Combine(testhostPackage.Path, testHostPath);
+ this.hostPackageVersion = testhostPackage.Version;
EqtTrace.Verbose("DotnetTestHostmanager: Relative path of testhost.dll with respect to package folder is {0}", testHostPath);
}
}
diff --git a/src/testhost.x86/DefaultEngineInvoker.cs b/src/testhost.x86/DefaultEngineInvoker.cs
index 06871a8212..2afe20d589 100644
--- a/src/testhost.x86/DefaultEngineInvoker.cs
+++ b/src/testhost.x86/DefaultEngineInvoker.cs
@@ -74,12 +74,6 @@ public void Invoke(IDictionary argsDictionary)
EqtTrace.Info("DefaultEngineInvoker: Initialize communication on port number: '{0}'", portNumber);
requestHandler.InitializeCommunication(portNumber);
- // Can only do this after InitializeCommunication because TestHost cannot "Send Log" unless communications are initialized
- if (!string.IsNullOrEmpty(EqtTrace.LogFile))
- {
- requestHandler.SendLog(TestMessageLevel.Informational, string.Format("Logging TestHost Diagnostics in file: {0}", EqtTrace.LogFile));
- }
-
// Initialize DataCollection Communication if data collection port is provided.
var dcPort = CommandLineArgumentsHelper.GetIntArgFromDict(argsDictionary, DataCollectionPortArgument);
if (dcPort > 0)
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketCommunicationManagerTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketCommunicationManagerTests.cs
index 8e6088e93e..a922d6aeb4 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketCommunicationManagerTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.PlatformTests/SocketCommunicationManagerTests.cs
@@ -22,6 +22,8 @@ public class SocketCommunicationManagerTests : IDisposable
private const string TestDiscoveryStartMessageWithDummyPayload = "{\"MessageType\":\"TestDiscovery.Start\",\"Payload\":\"Dummy Payload\"}";
+ private const string TestDiscoveryStartMessageWithVersionAndPayload = "{\"Version\":2,\"MessageType\":\"TestDiscovery.Start\",\"Payload\":\"Dummy Payload\"}";
+
private const string DummyPayload = "Dummy Payload";
private readonly SocketCommunicationManager communicationManager;
@@ -187,6 +189,16 @@ public async Task SendMessageWithPayloadShouldSerializeAndSendThePayload()
Assert.AreEqual(TestDiscoveryStartMessageWithDummyPayload, this.ReadFromStream(client.GetStream()));
}
+ [TestMethod]
+ public async Task SendMessageWithPayloadShouldSerializeAndSendThePayloadWithVersionStamped()
+ {
+ var client = await this.StartServerAndWaitForConnection();
+
+ this.communicationManager.SendMessage(MessageType.StartDiscovery, DummyPayload, 2);
+
+ Assert.AreEqual(TestDiscoveryStartMessageWithVersionAndPayload, this.ReadFromStream(client.GetStream()));
+ }
+
[TestMethod]
public async Task SendMessageWithRawMessageShouldNotSerializeThePayload()
{
@@ -217,12 +229,13 @@ public async Task ReceiveMessageShouldReceiveDeserializedMessage()
public async Task ReceiveMessageAsyncShouldReceiveDeserializedMessage()
{
var client = await this.StartServerAndWaitForConnection();
- this.WriteToStream(client.GetStream(), TestDiscoveryStartMessageWithDummyPayload);
+ this.WriteToStream(client.GetStream(), TestDiscoveryStartMessageWithVersionAndPayload);
var message = await this.communicationManager.ReceiveMessageAsync(CancellationToken.None);
-
- Assert.AreEqual(MessageType.StartDiscovery, message.MessageType);
- Assert.AreEqual(DummyPayload, message.Payload);
+ var versionedMessage = message as VersionedMessage;
+ Assert.AreEqual(MessageType.StartDiscovery, versionedMessage.MessageType);
+ Assert.AreEqual(DummyPayload, versionedMessage.Payload);
+ Assert.AreEqual(2, versionedMessage.Version);
}
[TestMethod]
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventHandlerTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventHandlerTests.cs
index b4903358a7..4095a6fe8e 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventHandlerTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventHandlerTests.cs
@@ -124,7 +124,8 @@ public void ProcessRequestsShouldProcessAfterTestCaseCompleteEvent()
{
var message = new Message();
message.MessageType = MessageType.DataCollectionTestEnd;
- message.Payload = JToken.FromObject(new TestResultEventArgs(new VisualStudio.TestPlatform.ObjectModel.TestResult(new TestCase())));
+ var testCase = new TestCase("hello", new Uri("world://how"), "1.dll");
+ message.Payload = JToken.FromObject(new TestResultEventArgs(new VisualStudio.TestPlatform.ObjectModel.TestResult(testCase)));
this.mockCommunicationManager.SetupSequence(x => x.ReceiveMessage()).Returns(message).Returns(new Message() { MessageType = MessageType.SessionEnd, Payload = "false" });
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventSenderTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventSenderTests.cs
index 719e30e9f8..1f219fb612 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventSenderTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/DataCollectionTestCaseEventSenderTests.cs
@@ -23,6 +23,7 @@ public class DataCollectionTestCaseEventSenderTests
{
private DataCollectionTestCaseEventSender dataCollectionTestCaseEventSender;
private Mock mockCommunicationManager;
+ private TestCase testCase = new TestCase("hello", new Uri("world://how"), "1.dll");
public DataCollectionTestCaseEventSenderTests()
{
@@ -90,8 +91,7 @@ public void CloseShouldThrowExceptionIfThrownByCommunicationManager()
[TestMethod]
public void SendTestCaseStartShouldSendMessageThroughCommunicationManager()
{
- var testCase = new TestCase();
- var testcaseStartEventArgs = new TestCaseStartEventArgs(testCase);
+ var testcaseStartEventArgs = new TestCaseStartEventArgs(this.testCase);
this.dataCollectionTestCaseEventSender.SendTestCaseStart(testcaseStartEventArgs);
this.mockCommunicationManager.Verify(x => x.SendMessage(MessageType.DataCollectionTestStart, testcaseStartEventArgs), Times.Once);
@@ -100,8 +100,7 @@ public void SendTestCaseStartShouldSendMessageThroughCommunicationManager()
[TestMethod]
public void SendTestCaseStartShouldThrowExceptionIfThrownByCommunicationManager()
{
- var testCase = new TestCase();
- var testcaseStartEventArgs = new TestCaseStartEventArgs(testCase);
+ var testcaseStartEventArgs = new TestCaseStartEventArgs(this.testCase);
this.mockCommunicationManager.Setup(x => x.SendMessage(MessageType.DataCollectionTestStart, testcaseStartEventArgs)).Throws();
Assert.ThrowsException(() =>
@@ -126,7 +125,6 @@ public void SendTestCaseEndShouldReturnAttachments()
[TestMethod]
public void SendTestCaseCompletedShouldThrowExceptionIfThrownByCommunicationManager()
{
- var testCase = new TestCase();
var testCaseEndEventArgs = new TestCaseEndEventArgs();
this.mockCommunicationManager.Setup(x => x.SendMessage(MessageType.DataCollectionTestEnd, It.IsAny())).Throws();
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestCaseSerializationTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestCaseSerializationTests.cs
index b50a7a42fb..f28f1f0dda 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestCaseSerializationTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestCaseSerializationTests.cs
@@ -28,6 +28,8 @@ public class TestCaseSerializationTests
Traits = { new Trait("Priority", "0"), new Trait("Category", "unit") }
};
+ #region v1 Tests
+
[TestMethod]
public void TestCaseJsonShouldContainAllPropertiesOnSerialization()
{
@@ -36,6 +38,7 @@ public void TestCaseJsonShouldContainAllPropertiesOnSerialization()
// Use raw deserialization to validate basic properties
dynamic data = JObject.Parse(json);
dynamic properties = data["Properties"];
+
Assert.AreEqual("TestCase.FullyQualifiedName", properties[0]["Key"]["Id"].Value);
Assert.AreEqual("sampleTestClass.sampleTestCase", properties[0]["Value"].Value);
Assert.AreEqual("TestCase.ExecutorUri", properties[1]["Key"]["Id"].Value);
@@ -53,7 +56,7 @@ public void TestCaseJsonShouldContainAllPropertiesOnSerialization()
// Traits require special handling with TestPlatformContract resolver. It should be null without it.
Assert.AreEqual("TestObject.Traits", properties[7]["Key"]["Id"].Value);
- Assert.IsNull(properties[7]["Key"]["Value"]);
+ Assert.IsNotNull(properties[7]["Value"]);
}
[TestMethod]
@@ -120,29 +123,118 @@ public void TestCaseObjectShouldSerializeTraitsWithSpecialCharacters()
Assert.AreEqual("[{\"Key\":\"t\",\"Value\":\"SDJDDHW>,:&^%//\\\\\\\\\\\\\\\\\"}]", properties[3]["Value"].ToString(Formatting.None));
}
+ #endregion
+
+ #region v2 Tests
+
+ [TestMethod]
+ public void TestCaseJsonShouldContainAllPropertiesOnSerializationV2()
+ {
+ var json = Serialize(testCase, 2);
+
+ // Use raw deserialization to validate basic properties
+ dynamic data = JObject.Parse(json);
+ dynamic properties = data["Properties"];
+
+ // Traits require special handling with TestPlatformContract resolver. It should be null without it.
+ Assert.AreEqual("TestObject.Traits", properties[0]["Key"]["Id"].Value);
+ Assert.IsNotNull(properties[0]["Value"]);
+
+ Assert.AreEqual("be78d6fc-61b0-4882-9d07-40d796fd96ce", data["Id"].Value);
+ Assert.AreEqual("sampleTestClass.sampleTestCase", data["FullyQualifiedName"].Value);
+ Assert.AreEqual("sampleTestCase", data["DisplayName"].Value);
+ Assert.AreEqual("sampleTest.dll", data["Source"].Value);
+ Assert.AreEqual("executor://sampleTestExecutor", data["ExecutorUri"].Value);
+ Assert.AreEqual("/user/src/testFile.cs", data["CodeFilePath"].Value);
+ Assert.AreEqual(999, data["LineNumber"].Value);
+ }
+
+ [TestMethod]
+ public void TestCaseObjectShouldContainAllPropertiesOnDeserializationV2()
+ {
+ var json = "{\"Id\": \"be78d6fc-61b0-4882-9d07-40d796fd96ce\",\"FullyQualifiedName\": \"sampleTestClass.sampleTestCase\",\"DisplayName\": \"sampleTestCase\",\"ExecutorUri\": \"executor://sampleTestExecutor\",\"Source\": \"sampleTest.dll\",\"CodeFilePath\": \"/user/src/testFile.cs\", \"LineNumber\": 999,"
+ + "\"Properties\": [{ \"Key\": { \"Id\": \"TestObject.Traits\", \"Label\": \"Traits\", \"Category\": \"\", \"Description\": \"\", \"Attributes\": 5, \"ValueType\": \"System.Collections.Generic.KeyValuePair`2[[System.String],[System.String]][]\"}, \"Value\": [{\"Key\": \"Priority\",\"Value\": \"0\"}, {\"Key\": \"Category\",\"Value\": \"unit\"}]}]}";
+
+ var test = Deserialize(json, 2);
+
+ Assert.AreEqual(testCase.CodeFilePath, test.CodeFilePath);
+ Assert.AreEqual(testCase.DisplayName, test.DisplayName);
+ Assert.AreEqual(testCase.ExecutorUri, test.ExecutorUri);
+ Assert.AreEqual(testCase.FullyQualifiedName, test.FullyQualifiedName);
+ Assert.AreEqual(testCase.LineNumber, test.LineNumber);
+ Assert.AreEqual(testCase.Source, test.Source);
+ Assert.AreEqual(testCase.Traits.First().Name, test.Traits.First().Name);
+ Assert.AreEqual(testCase.Id, test.Id);
+ }
+
[TestMethod]
- public void TestCaseObjectShouldDeserializeTraitsWithSpecialCharacters()
+ public void TestCaseObjectShouldSerializeTraitsWithSpecialCharactersV2()
+ {
+ var test = new TestCase("a.b", new Uri("uri://x"), @"/tmp/a.b.dll");
+ test.Traits.Add("t", @"SDJDDHW>,:&^%//\\\\");
+
+ var json = Serialize(test, 2);
+
+ // Use raw deserialization to validate basic properties
+ dynamic data = JObject.Parse(json);
+ dynamic properties = data["Properties"];
+ Assert.AreEqual(@"TestObject.Traits", properties[0]["Key"]["Id"].Value);
+ Assert.AreEqual("[{\"Key\":\"t\",\"Value\":\"SDJDDHW>,:&^%//\\\\\\\\\\\\\\\\\"}]", properties[0]["Value"].ToString(Formatting.None));
+ }
+
+ [TestMethod]
+ public void TestCaseObjectShouldSerializeWindowsPathWithEscapingV2()
+ {
+ var test = new TestCase("a.b", new Uri("uri://x"), @"C:\Test\TestAssembly.dll");
+
+ var json = Serialize(test, 2);
+
+ // Use raw deserialization to validate basic properties
+ dynamic data = JObject.Parse(json);
+ Assert.AreEqual(@"C:\Test\TestAssembly.dll", data["Source"].Value);
+ }
+
+ [TestMethod]
+ public void TestCaseObjectShouldDeserializeEscapedWindowsPathV2()
+ {
+ var json = "{\"Id\":\"4e35ed85-a5e8-946e-fb14-0d3de2304e74\",\"FullyQualifiedName\":\"a.b\",\"DisplayName\":\"a.b\",\"ExecutorUri\":\"uri://x\",\"Source\":\"C:\\\\Test\\\\TestAssembly.dll\",\"CodeFilePath\":null,\"LineNumber\":-1,\"Properties\":[]}";
+
+ var test = Deserialize(json, 2);
+
+ Assert.AreEqual(@"C:\Test\TestAssembly.dll", test.Source);
+ }
+
+ #endregion
+
+ #region Common Tests
+
+ [TestMethod]
+ [DataRow(1)]
+ [DataRow(2)]
+ public void TestCaseObjectShouldDeserializeTraitsWithSpecialCharacters(int version)
{
var json = "{\"Properties\":[{\"Key\":{\"Id\":\"TestCase.FullyQualifiedName\",\"Label\":\"FullyQualifiedName\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"a.b\"},"
+ "{\"Key\":{\"Id\":\"TestCase.ExecutorUri\",\"Label\":\"Executor Uri\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.Uri\"},\"Value\":\"uri://x\"},"
+ "{\"Key\":{\"Id\":\"TestCase.Source\",\"Label\":\"Source\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"/tmp/a.b.dll\"},"
+ "{\"Key\":{\"Id\":\"TestObject.Traits\",\"Label\":\"Traits\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":5,\"ValueType\":\"System.Collections.Generic.KeyValuePair`2[[System.String],[System.String]][]\"},\"Value\":[{\"Key\":\"t\",\"Value\":\"SDJDDHW>,:&^%//\\\\\\\\\\\\\\\\\"}]}]}";
- var test = Deserialize(json);
+ var test = Deserialize(json, version);
var traits = test.Traits.ToArray();
Assert.AreEqual(1, traits.Length);
Assert.AreEqual(@"SDJDDHW>,:&^%//\\\\", traits[0].Value);
}
- private static string Serialize(T data)
+ #endregion
+
+ private static string Serialize(T data, int version = 1)
{
- return JsonDataSerializer.Instance.Serialize(data);
+ return JsonDataSerializer.Instance.Serialize(data, version);
}
- private static T Deserialize(string json)
+ private static T Deserialize(string json, int version = 1)
{
- return JsonDataSerializer.Instance.Deserialize(json);
+ return JsonDataSerializer.Instance.Deserialize(json, version);
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestResultSerializationTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestResultSerializationTests.cs
index ca69dff452..bcc185ed90 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestResultSerializationTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/Serialization/TestResultSerializationTests.cs
@@ -21,6 +21,7 @@ public class TestResultSerializationTests
new Uri("executor://sampleTestExecutor"),
"sampleTest.dll");
+ private static DateTimeOffset startTime = new DateTimeOffset(new DateTime(2007, 3, 10, 0, 0, 0, DateTimeKind.Utc));
private static TestResult testResult = new TestResult(testCase)
{
// Attachments = ?
@@ -31,10 +32,12 @@ public class TestResultSerializationTests
DisplayName = "sampleTestResult",
ComputerName = "sampleComputerName",
Duration = TimeSpan.MaxValue,
- StartTime = DateTimeOffset.MinValue,
+ StartTime = startTime,
EndTime = DateTimeOffset.MaxValue
};
+ #region v1 tests
+
[TestMethod]
public void TestResultJsonShouldContainAllPropertiesOnSerialization()
{
@@ -58,7 +61,7 @@ public void TestResultJsonShouldContainAllPropertiesOnSerialization()
// By default json.net converts DateTimes to current time zone
Assert.AreEqual("TestResult.StartTime", properties[6]["Key"]["Id"].Value);
- Assert.AreEqual(DateTimeOffset.MinValue.Year, ((DateTimeOffset)properties[6]["Value"].Value).Year);
+ Assert.AreEqual(startTime.Year, ((DateTimeOffset)properties[6]["Value"].Value).Year);
Assert.AreEqual("TestResult.EndTime", properties[7]["Key"]["Id"].Value);
Assert.AreEqual(DateTimeOffset.MaxValue.Year, ((DateTimeOffset)properties[7]["Value"].Value).Year);
}
@@ -66,7 +69,7 @@ public void TestResultJsonShouldContainAllPropertiesOnSerialization()
[TestMethod]
public void TestResultObjectShouldContainAllPropertiesOnDeserialization()
{
- var json = "{\"TestCase\":{\"Properties\":[{\"Key\":{\"Id\":\"TestCase.FullyQualifiedName\",\"Label\":\"FullyQualifiedName\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"sampleTestClass.sampleTestCase\"},{\"Key\":{\"Id\":\"TestCase.ExecutorUri\",\"Label\":\"Executor Uri\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.Uri\"},\"Value\":\"executor://sampleTestExecutor\"},{\"Key\":{\"Id\":\"TestCase.Source\",\"Label\":\"Source\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleTest.dll\"}]},\"Attachments\":[],\"Messages\":[],\"Properties\":[{\"Key\":{\"Id\":\"TestResult.Outcome\",\"Label\":\"Outcome\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome\"},\"Value\":1},{\"Key\":{\"Id\":\"TestResult.ErrorMessage\",\"Label\":\"Error Message\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleError\"},{\"Key\":{\"Id\":\"TestResult.ErrorStackTrace\",\"Label\":\"Error Stack Trace\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleStackTrace\"},{\"Key\":{\"Id\":\"TestResult.DisplayName\",\"Label\":\"TestResult Display Name\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"sampleTestResult\"},{\"Key\":{\"Id\":\"TestResult.ComputerName\",\"Label\":\"Computer Name\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleComputerName\"},{\"Key\":{\"Id\":\"TestResult.Duration\",\"Label\":\"Duration\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.TimeSpan\"},\"Value\":\"10675199.02:48:05.4775807\"},{\"Key\":{\"Id\":\"TestResult.StartTime\",\"Label\":\"Start Time\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.DateTimeOffset\"},\"Value\":\"0001-01-01T00:00:00+00:00\"},{\"Key\":{\"Id\":\"TestResult.EndTime\",\"Label\":\"End Time\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.DateTimeOffset\"},\"Value\":\"9999-12-31T23:59:59.9999999+00:00\"}]}";
+ var json = "{\"TestCase\":{\"Properties\":[{\"Key\":{\"Id\":\"TestCase.FullyQualifiedName\",\"Label\":\"FullyQualifiedName\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"sampleTestClass.sampleTestCase\"},{\"Key\":{\"Id\":\"TestCase.ExecutorUri\",\"Label\":\"Executor Uri\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.Uri\"},\"Value\":\"executor://sampleTestExecutor\"},{\"Key\":{\"Id\":\"TestCase.Source\",\"Label\":\"Source\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleTest.dll\"}]},\"Attachments\":[],\"Messages\":[],\"Properties\":[{\"Key\":{\"Id\":\"TestResult.Outcome\",\"Label\":\"Outcome\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"Microsoft.VisualStudio.TestPlatform.ObjectModel.TestOutcome\"},\"Value\":1},{\"Key\":{\"Id\":\"TestResult.ErrorMessage\",\"Label\":\"Error Message\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleError\"},{\"Key\":{\"Id\":\"TestResult.ErrorStackTrace\",\"Label\":\"Error Stack Trace\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleStackTrace\"},{\"Key\":{\"Id\":\"TestResult.DisplayName\",\"Label\":\"TestResult Display Name\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"sampleTestResult\"},{\"Key\":{\"Id\":\"TestResult.ComputerName\",\"Label\":\"Computer Name\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleComputerName\"},{\"Key\":{\"Id\":\"TestResult.Duration\",\"Label\":\"Duration\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.TimeSpan\"},\"Value\":\"10675199.02:48:05.4775807\"},{\"Key\":{\"Id\":\"TestResult.StartTime\",\"Label\":\"Start Time\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.DateTimeOffset\"},\"Value\":\"2007-03-10T00:00:00+00:00\"},{\"Key\":{\"Id\":\"TestResult.EndTime\",\"Label\":\"End Time\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.DateTimeOffset\"},\"Value\":\"9999-12-31T23:59:59.9999999+00:00\"}]}";
var test = Deserialize(json);
@@ -108,14 +111,85 @@ public void TestResultObjectShouldDeserializeAttachments()
Assert.AreEqual("sampleAttachment", result.Attachments[0].DisplayName);
}
- private static string Serialize(T data)
+ #endregion
+
+ #region v2 Tests
+
+ [TestMethod]
+ public void TestResultJsonShouldContainAllPropertiesOnSerializationV2()
+ {
+ var json = Serialize(testResult, 2);
+
+ // Use raw deserialization to validate basic properties
+ dynamic data = JObject.Parse(json);
+
+ Assert.AreEqual(1, data["Outcome"].Value);
+ Assert.AreEqual("sampleError", data["ErrorMessage"].Value);
+ Assert.AreEqual("sampleStackTrace", data["ErrorStackTrace"].Value);
+ Assert.AreEqual("sampleTestResult", data["DisplayName"].Value);
+ Assert.AreEqual("sampleComputerName", data["ComputerName"].Value);
+ Assert.AreEqual("10675199.02:48:05.4775807", data["Duration"].Value);
+
+ // By default json.net converts DateTimes to current time zone
+ Assert.AreEqual(startTime.Year, ((DateTimeOffset)data["StartTime"].Value).Year);
+ Assert.AreEqual(DateTimeOffset.MaxValue.Year, ((DateTimeOffset)data["EndTime"].Value).Year);
+ }
+
+ [TestMethod]
+ public void TestResultObjectShouldContainAllPropertiesOnDeserializationV2()
+ {
+ var json = "{\"TestCase\":{\"Id\":\"28e7a7ed-8fb9-05b7-5e90-4a8c52f32b5b\",\"FullyQualifiedName\":\"sampleTestClass.sampleTestCase\",\"DisplayName\":\"sampleTestClass.sampleTestCase\",\"ExecutorUri\":\"executor://sampleTestExecutor\",\"Source\":\"sampleTest.dll\",\"CodeFilePath\":null,\"LineNumber\":-1,\"Properties\":[]},\"Attachments\":[],\"Outcome\":1,\"ErrorMessage\":\"sampleError\",\"ErrorStackTrace\":\"sampleStackTrace\",\"DisplayName\":\"sampleTestResult\",\"Messages\":[],\"ComputerName\":\"sampleComputerName\",\"Duration\":\"10675199.02:48:05.4775807\",\"StartTime\":\"2007-03-10T00:00:00+00:00\",\"EndTime\":\"9999-12-31T23:59:59.9999999+00:00\",\"Properties\":[]}";
+
+ var test = Deserialize(json, 2);
+
+ Assert.AreEqual(testResult.TestCase.Id, test.TestCase.Id);
+ Assert.AreEqual(testResult.Attachments.Count, test.Attachments.Count);
+ Assert.AreEqual(testResult.Messages.Count, test.Messages.Count);
+
+ Assert.AreEqual(testResult.ComputerName, test.ComputerName);
+ Assert.AreEqual(testResult.DisplayName, test.DisplayName);
+ Assert.AreEqual(testResult.Duration, test.Duration);
+ Assert.AreEqual(testResult.EndTime, test.EndTime);
+ Assert.AreEqual(testResult.ErrorMessage, test.ErrorMessage);
+ Assert.AreEqual(testResult.ErrorStackTrace, test.ErrorStackTrace);
+ Assert.AreEqual(testResult.Outcome, test.Outcome);
+ Assert.AreEqual(testResult.StartTime, test.StartTime);
+ }
+
+ [TestMethod]
+ public void TestResultObjectShouldSerializeAttachmentsV2()
+ {
+ var result = new TestResult(testCase);
+ result.Attachments.Add(new AttachmentSet(new Uri("http://dummyUri"), "sampleAttachment"));
+ var expectedJson = "{\"TestCase\":{\"Properties\":[{\"Key\":{\"Id\":\"TestCase.FullyQualifiedName\",\"Label\":\"FullyQualifiedName\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"sampleTestClass.sampleTestCase\"},{\"Key\":{\"Id\":\"TestCase.ExecutorUri\",\"Label\":\"Executor Uri\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.Uri\"},\"Value\":\"executor://sampleTestExecutor\"},{\"Key\":{\"Id\":\"TestCase.Source\",\"Label\":\"Source\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleTest.dll\"}]},\"Attachments\":[{\"Uri\":\"http://dummyUri\",\"DisplayName\":\"sampleAttachment\",\"Attachments\":[]}],\"Messages\":[],\"Properties\":[]}";
+
+ var json = Serialize(result);
+
+ Assert.AreEqual(expectedJson, json);
+ }
+
+ [TestMethod]
+ public void TestResultObjectShouldDeserializeAttachmentsV2()
+ {
+ var json = "{\"TestCase\":{\"Properties\":[{\"Key\":{\"Id\":\"TestCase.FullyQualifiedName\",\"Label\":\"FullyQualifiedName\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.String\"},\"Value\":\"sampleTestClass.sampleTestCase\"},{\"Key\":{\"Id\":\"TestCase.ExecutorUri\",\"Label\":\"Executor Uri\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.Uri\"},\"Value\":\"executor://sampleTestExecutor\"},{\"Key\":{\"Id\":\"TestCase.Source\",\"Label\":\"Source\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":0,\"ValueType\":\"System.String\"},\"Value\":\"sampleTest.dll\"},{\"Key\":{\"Id\":\"TestCase.Id\",\"Label\":\"Id\",\"Category\":\"\",\"Description\":\"\",\"Attributes\":1,\"ValueType\":\"System.Guid\"},\"Value\":\"28e7a7ed-8fb9-05b7-5e90-4a8c52f32b5b\"}]},\"Attachments\":[{\"Uri\":\"http://dummyUri\",\"DisplayName\":\"sampleAttachment\",\"Attachments\":[]}],\"Messages\":[],\"Properties\":[]}";
+
+ var result = Deserialize(json);
+
+ Assert.AreEqual(1, result.Attachments.Count);
+ Assert.AreEqual(new Uri("http://dummyUri"), result.Attachments[0].Uri);
+ Assert.AreEqual("sampleAttachment", result.Attachments[0].DisplayName);
+ }
+
+ #endregion
+
+ private static string Serialize(T data, int version = 1)
{
- return JsonDataSerializer.Instance.Serialize(data);
+ return JsonDataSerializer.Instance.Serialize(data, version);
}
- private static T Deserialize(string json)
+ private static T Deserialize(string json, int version = 1)
{
- return JsonDataSerializer.Instance.Deserialize(json);
+ return JsonDataSerializer.Instance.Deserialize(json, version);
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
index acdd9edb9e..45ea11e6c6 100644
--- a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
+++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/TestRequestSenderTests.cs
@@ -26,6 +26,7 @@ public class TestRequestSenderTests
private ITestRequestSender testRequestSender;
private Mock mockCommunicationManager;
private Mock mockDataSerializer;
+ private int version = 1;
[TestInitialize]
public void TestInit()
@@ -71,13 +72,44 @@ public void DisposeShouldCallStopServerOnCommunicationManager()
this.mockCommunicationManager.Verify(mc => mc.StopServer(), Times.Once);
}
+ [TestMethod]
+ public void VersionCheckWithTestHostShouldCheckVersionIfVersionCheckPassesReturnTrue()
+ {
+ var message = new Message() { MessageType = MessageType.VersionCheck, Payload = this.version };
+ this.mockCommunicationManager.Setup(mc => mc.ReceiveMessage()).Returns(message);
+
+ this.testRequestSender.CheckVersionWithTestHost();
+
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.VersionCheck, this.version), Times.Once);
+ }
+
+ [TestMethod]
+ public void VersionCheckWithTestHostShouldBeAbleToReceiveProtocolErrorAndThrowException()
+ {
+ var message = new Message() { MessageType = MessageType.ProtocolError, Payload = this.version };
+ this.mockCommunicationManager.Setup(mc => mc.ReceiveMessage()).Returns(message);
+
+ var ex = Assert.ThrowsException(() => this.testRequestSender.CheckVersionWithTestHost());
+ Assert.AreEqual("Protocol version check failed. Make sure test runner and host are compatible.", ex.Message);
+ }
+
+ [TestMethod]
+ public void VersionCheckWithTestHostForInvalidMessageShouldThrowException()
+ {
+ var message = new Message() { MessageType = MessageType.TestCasesFound, Payload = null };
+ this.mockCommunicationManager.Setup(mc => mc.ReceiveMessage()).Returns(message);
+
+ var ex = Assert.ThrowsException(() => this.testRequestSender.CheckVersionWithTestHost());
+ Assert.AreEqual("Unexpected message received. Expected MessageType : ProtocolVersion Actual MessageType: TestDiscovery.TestFound", ex.Message);
+ }
+
[TestMethod]
public void InitializeDiscoveryShouldSendCommunicationMessageWithCorrectParameters()
{
var paths = new List() { "Hello", "World" };
this.testRequestSender.InitializeDiscovery(paths, false);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.DiscoveryInitialize, paths), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.DiscoveryInitialize, paths, this.version), Times.Once);
}
[TestMethod]
@@ -86,7 +118,7 @@ public void InitializeExecutionShouldSendCommunicationMessageWithCorrectParamete
var paths = new List() { "Hello", "World" };
this.testRequestSender.InitializeExecution(paths, true);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.ExecutionInitialize, paths), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.ExecutionInitialize, paths, this.version), Times.Once);
}
[TestMethod]
@@ -120,7 +152,7 @@ public void DiscoverTestsShouldCallHandleDiscoveredTestsOnTestCaseEvent()
this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.version), Times.Once);
this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Exactly(2));
mockHandler.Verify(mh => mh.HandleDiscoveredTests(testCases), Times.Once);
mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
@@ -157,7 +189,7 @@ public void DiscoverTestsShouldCallHandleLogMessageOnTestMessage()
this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.version), Times.Once);
this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Exactly(2));
mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, rawMessage), Times.Once);
mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
@@ -185,7 +217,7 @@ public void DiscoverTestsShouldCallHandleDiscoveryCompleteOnDiscoveryCompletion(
this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.version), Times.Once);
this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Once);
mockHandler.Verify(mh => mh.HandleDiscoveryComplete(1, null, false), Times.Once);
mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Once);
@@ -199,7 +231,7 @@ public void DiscoverTestsShouldHandleExceptionOnSendMessage()
var mockHandler = new Mock();
var discoveryCriteria = new DiscoveryCriteria(sources, 100, settingsXml);
var exception = new Exception();
- this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.StartDiscovery, discoveryCriteria))
+ this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.StartDiscovery, discoveryCriteria, this.version))
.Throws(exception);
this.testRequestSender.DiscoverTests(discoveryCriteria, mockHandler.Object);
@@ -277,7 +309,7 @@ public void StartTestRunWithSourcesShouldCallHandleTestRunStatsChange()
waitHandle.WaitOne();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria, this.version), Times.Once);
// One for run stats and another for runcomplete
this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(rawMessage), Times.Exactly(2));
@@ -318,7 +350,7 @@ public void StartTestRunWithTestsShouldCallHandleTestRunStatsChange()
this.testRequestSender.StartTestRun(runCriteria, mockHandler.Object);
waitHandle.WaitOne();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithTests, runCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithTests, runCriteria, this.version), Times.Once);
mockHandler.Verify(mh => mh.HandleTestRunStatsChange(testRunChangedArgs), Times.Once);
mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.AtLeastOnce);
}
@@ -353,7 +385,7 @@ public void StartTestRunShouldCallHandleLogMessageOnTestMessage()
this.testRequestSender.StartTestRun(runCriteria, mockHandler.Object);
waitHandle.WaitOne();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria, this.version), Times.Once);
this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(It.IsAny()), Times.Exactly(2));
mockHandler.Verify(mh => mh.HandleLogMessage(payload.MessageLevel, payload.Message), Times.Once);
mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.AtLeastOnce);
@@ -398,12 +430,12 @@ public void StartTestRunShouldCallLaunchProcessWithDebuggerAndWaitForCallback()
waitHandle.WaitOne();
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria), Times.Once);
+ this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.StartTestExecutionWithSources, runCriteria, this.version), Times.Once);
this.mockDataSerializer.Verify(ds => ds.DeserializeMessage(It.IsAny()), Times.Exactly(2));
mockHandler.Verify(mh => mh.LaunchProcessWithDebuggerAttached(payload), Times.Once);
mockHandler.Verify(mh => mh.HandleRawMessage(rawMessage), Times.Exactly(2));
- this.mockCommunicationManager.Verify(mc => mc.SendMessage(MessageType.LaunchAdapterProcessWithDebuggerAttachedCallback, It.IsAny