diff --git a/samples/Microsoft.TestPlatform.Protocol/Communication/JsonDataSerializer.cs b/samples/Microsoft.TestPlatform.Protocol/Communication/JsonDataSerializer.cs index b5800254a5..08c0dc0135 100644 --- a/samples/Microsoft.TestPlatform.Protocol/Communication/JsonDataSerializer.cs +++ b/samples/Microsoft.TestPlatform.Protocol/Communication/JsonDataSerializer.cs @@ -4,7 +4,7 @@ namespace Microsoft.TestPlatform.Protocol { using System.IO; - + using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; @@ -25,12 +25,13 @@ private JsonDataSerializer() { serializer = JsonSerializer.Create( new JsonSerializerSettings - { - DateFormatHandling = DateFormatHandling.IsoDateFormat, - DateParseHandling = DateParseHandling.DateTimeOffset, - DateTimeZoneHandling = DateTimeZoneHandling.Utc, - TypeNameHandling = TypeNameHandling.None - }); + { + DateFormatHandling = DateFormatHandling.IsoDateFormat, + DateParseHandling = DateParseHandling.DateTimeOffset, + DateTimeZoneHandling = DateTimeZoneHandling.Utc, + TypeNameHandling = TypeNameHandling.None, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + }); #if DEBUG // MemoryTraceWriter can help diagnose serialization issues. Enable it for // debug builds only. @@ -68,7 +69,7 @@ public Message DeserializeMessage(string rawMessage) 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 (Microsoft.TestPlatform.Protocol.MessageType.TestMessage.Equals(message.MessageType)) @@ -117,7 +118,7 @@ public string SerializeMessage(string messageType) 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)) diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs index ae4bd2f689..2da632cd13 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs @@ -32,7 +32,8 @@ private JsonDataSerializer() DateFormatHandling = DateFormatHandling.IsoDateFormat, DateParseHandling = DateParseHandling.DateTimeOffset, DateTimeZoneHandling = DateTimeZoneHandling.Utc, - TypeNameHandling = TypeNameHandling.None + TypeNameHandling = TypeNameHandling.None, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; payloadSerializer = JsonSerializer.Create(jsonSettings); diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs index 15b4d0ec09..7af0940b57 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs @@ -333,58 +333,74 @@ private void CleanupCommunicationIfProcessExit() private void OnTestRunAbort(ITestRunEventsHandler testRunEventsHandler, Exception exception) { - EqtTrace.Error("Server: TestExecution: Aborting test run because {0}", exception); + try + { + EqtTrace.Error("Server: TestExecution: Aborting test run because {0}", exception); - var reason = string.Format(CommonResources.AbortedTestRun, exception?.Message); + var reason = string.Format(CommonResources.AbortedTestRun, exception?.Message); - // log console message to vstest console - testRunEventsHandler.HandleLogMessage(TestMessageLevel.Error, reason); + // log console message to vstest console + testRunEventsHandler.HandleLogMessage(TestMessageLevel.Error, reason); - // log console message to vstest console wrapper - var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = reason }; - var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload); - testRunEventsHandler.HandleRawMessage(rawMessage); + // log console message to vstest console wrapper + var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = reason }; + var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload); + testRunEventsHandler.HandleRawMessage(rawMessage); - // notify test run abort to vstest console wrapper. - var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero); - var payload = new TestRunCompletePayload { TestRunCompleteArgs = completeArgs }; - rawMessage = this.dataSerializer.SerializePayload(MessageType.ExecutionComplete, payload); - testRunEventsHandler.HandleRawMessage(rawMessage); + // notify test run abort to vstest console wrapper. + var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero); + var payload = new TestRunCompletePayload { TestRunCompleteArgs = completeArgs }; + rawMessage = this.dataSerializer.SerializePayload(MessageType.ExecutionComplete, payload); + testRunEventsHandler.HandleRawMessage(rawMessage); - // notify of a test run complete and bail out. - testRunEventsHandler.HandleTestRunComplete(completeArgs, null, null, null); + // notify of a test run complete and bail out. + testRunEventsHandler.HandleTestRunComplete(completeArgs, null, null, null); - this.CleanupCommunicationIfProcessExit(); + this.CleanupCommunicationIfProcessExit(); + } + catch (Exception ex) + { + EqtTrace.Error(ex); + throw ex; + } } private void OnDiscoveryAbort(ITestDiscoveryEventsHandler eventHandler, Exception exception) { - EqtTrace.Error("Server: TestExecution: Aborting test discovery because {0}", exception); + try + { + EqtTrace.Error("Server: TestExecution: Aborting test discovery because {0}", exception); - var reason = string.Format(CommonResources.AbortedTestDiscovery, exception?.Message); + var reason = string.Format(CommonResources.AbortedTestDiscovery, exception?.Message); - // Log to vstest console - eventHandler.HandleLogMessage(TestMessageLevel.Error, reason); + // Log to vstest console + eventHandler.HandleLogMessage(TestMessageLevel.Error, reason); - // Log to vs ide test output - var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = reason }; - var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload); - eventHandler.HandleRawMessage(rawMessage); + // Log to vs ide test output + var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = reason }; + var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload); + eventHandler.HandleRawMessage(rawMessage); - // Notify discovery abort to IDE test output - var payload = new DiscoveryCompletePayload() - { - IsAborted = true, - LastDiscoveredTests = null, - TotalTests = -1 - }; - rawMessage = this.dataSerializer.SerializePayload(MessageType.DiscoveryComplete, payload); - eventHandler.HandleRawMessage(rawMessage); + // Notify discovery abort to IDE test output + var payload = new DiscoveryCompletePayload() + { + IsAborted = true, + LastDiscoveredTests = null, + TotalTests = -1 + }; + rawMessage = this.dataSerializer.SerializePayload(MessageType.DiscoveryComplete, payload); + eventHandler.HandleRawMessage(rawMessage); - // Complete discovery - eventHandler.HandleDiscoveryComplete(-1, null, true); + // Complete discovery + eventHandler.HandleDiscoveryComplete(-1, null, true); - this.CleanupCommunicationIfProcessExit(); + this.CleanupCommunicationIfProcessExit(); + } + catch (Exception ex) + { + EqtTrace.Error(ex); + throw ex; + } } private string TryReceiveRawMessage() diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunCompleteEventArgs.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunCompleteEventArgs.cs index 6cfd375802..4ad82f5fb5 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunCompleteEventArgs.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Events/TestRunCompleteEventArgs.cs @@ -29,7 +29,7 @@ public TestRunCompleteEventArgs(ITestRunStatistics stats, bool isCanceled, bool this.TestRunStatistics = stats; this.IsCanceled = isCanceled; this.IsAborted = isAborted; - this.Error = null; // Passing error value as null, should be pass exception. Issue: https://github.com/Microsoft/vstest/issues/618 + this.Error = error; this.AttachmentSets = attachmentSets; this.ElapsedTimeInRunningTests = elapsedTime; } diff --git a/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/JsonDataSerializerTests.cs b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/JsonDataSerializerTests.cs new file mode 100644 index 0000000000..e227a558b6 --- /dev/null +++ b/test/Microsoft.TestPlatform.CommunicationUtilities.UnitTests/JsonDataSerializerTests.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.TestPlatform.CommunicationUtilities.UnitTests +{ + using System; + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class JsonDataSerializerTests + { + [TestMethod] + public void SerializePayloadShouldSerializeAnObjectWithSelfReferencingLoop() + { + var classWithSelfReferencingLoop = new ClassWithSelfReferencingLoop(null); + classWithSelfReferencingLoop = new ClassWithSelfReferencingLoop(classWithSelfReferencingLoop); + classWithSelfReferencingLoop.InfiniteRefernce.InfiniteRefernce = classWithSelfReferencingLoop; + + var sut = JsonDataSerializer.Instance; + + // This line should not throw exception + sut.SerializePayload("dummy", classWithSelfReferencingLoop); + } + + [TestMethod] + public void DeserializeShouldDeserializeAnObjectWhichHadSelfReferencingLoopBeforeSerialization() + { + var classWithSelfReferencingLoop = new ClassWithSelfReferencingLoop(null); + classWithSelfReferencingLoop = new ClassWithSelfReferencingLoop(classWithSelfReferencingLoop); + classWithSelfReferencingLoop.InfiniteRefernce.InfiniteRefernce = classWithSelfReferencingLoop; + + var sut = JsonDataSerializer.Instance; + + var json = sut.SerializePayload("dummy", classWithSelfReferencingLoop); + + // This line should deserialize properly + var result = sut.Deserialize(json, 1); + + Assert.AreEqual(typeof(ClassWithSelfReferencingLoop), result.GetType()); + Assert.IsNull(result.InfiniteRefernce); + } + + public class ClassWithSelfReferencingLoop + { + public ClassWithSelfReferencingLoop(ClassWithSelfReferencingLoop ir) + { + this.InfiniteRefernce = ir; + } + + public ClassWithSelfReferencingLoop InfiniteRefernce + { + get; + set; + } + } + } +}