Skip to content

Commit

Permalink
Fix setting of json serializer to ignore self referencing loop (#796)
Browse files Browse the repository at this point in the history
* Dont pick package from fallbackPackageFolders as we want all the packages in vstest\package folder

* Issue:
1) #706
2) #618

Fix:
Don’t serialize the type if that have self-referencing loop

* Address PR comment.

* Log the exception in EqtTrace thrown from OnDiscoveryAbort and OnTestRunAbort
  • Loading branch information
Faizan2304 authored May 11, 2017
1 parent a657e1e commit 333752b
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace Microsoft.TestPlatform.Protocol
{
using System.IO;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
Expand All @@ -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.
Expand Down Expand Up @@ -68,7 +69,7 @@ public Message DeserializeMessage(string rawMessage)
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))
Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ClassWithSelfReferencingLoop>(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;
}
}
}
}

0 comments on commit 333752b

Please sign in to comment.