Skip to content

Commit 57d2d00

Browse files
authored
Fixed a serialization issue with DataRows. (#847)
1 parent 1c62432 commit 57d2d00

File tree

15 files changed

+323
-109
lines changed

15 files changed

+323
-109
lines changed

src/Adapter/MSTest.CoreAdapter/Discovery/AssemblyEnumerator.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery
99
using System.Globalization;
1010
using System.Linq;
1111
using System.Reflection;
12+
using System.Runtime.Serialization;
1213
using System.Security;
1314
using System.Text;
1415

@@ -249,7 +250,8 @@ private IEnumerable<UnitTestElement> DiscoverTestsInType(string assemblyFileName
249250

250251
private bool DynamicDataAttached(IDictionary<string, object> sourceLevelParameters, Assembly assembly, UnitTestElement test, List<UnitTestElement> tests)
251252
{
252-
// It should always be `true`, but any part of the chain is obsolete; it might not contain those. Since we depend on those properties, if they don't exist, we bail out early.
253+
// It should always be `true`, but if any part of the chain is obsolete; it might not contain those.
254+
// Since we depend on those properties, if they don't exist, we bail out early.
253255
if (!test.TestMethod.HasManagedMethodAndTypeProperties)
254256
{
255257
return false;
@@ -320,7 +322,7 @@ private bool ProcessDataSourceTests(UnitTestElement test, TestMethodInfo testMet
320322
var discoveredTest = test.Clone();
321323
discoveredTest.DisplayName = displayName;
322324
discoveredTest.TestMethod.DataType = DynamicDataType.DataSourceAttribute;
323-
discoveredTest.TestMethod.Data = new[] { (object)rowIndex };
325+
discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(new[] { (object)rowIndex });
324326
tests.Add(discoveredTest);
325327
}
326328

@@ -359,17 +361,37 @@ private bool ProcessTestDataSourceTests(UnitTestElement test, MethodInfo methodI
359361
foreach (var dataSource in testDataSources)
360362
{
361363
var data = dataSource.GetData(methodInfo);
364+
var discoveredTests = new List<UnitTestElement>();
365+
var serializationFailed = false;
362366

363367
foreach (var d in data)
364368
{
365369
var discoveredTest = test.Clone();
366370
discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, d);
367371

368-
discoveredTest.TestMethod.DataType = DynamicDataType.ITestDataSource;
369-
discoveredTest.TestMethod.Data = d;
372+
try
373+
{
374+
discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(d);
375+
discoveredTest.TestMethod.DataType = DynamicDataType.ITestDataSource;
376+
}
377+
catch (SerializationException)
378+
{
379+
serializationFailed = true;
380+
break;
381+
}
370382

371-
tests.Add(discoveredTest);
383+
discoveredTests.Add(discoveredTest);
372384
}
385+
386+
// Serialization failed for the type, bail out.
387+
if (serializationFailed)
388+
{
389+
tests.Add(test);
390+
391+
break;
392+
}
393+
394+
tests.AddRange(discoveredTests);
373395
}
374396

375397
return true;

src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution
1313
using System.Linq;
1414
using System.Threading;
1515
using System.Threading.Tasks;
16+
1617
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
1718
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
1819
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
@@ -375,18 +376,14 @@ private void ExecuteTestsWithTestRunner(
375376

376377
var startTime = DateTimeOffset.Now;
377378

378-
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
379-
"Executing test {0}",
380-
unitTestElement.TestMethod.Name);
379+
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executing test {0}", unitTestElement.TestMethod.Name);
381380

382381
// Run single test passing test context properties to it.
383382
var tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest);
384383
var testContextProperties = this.GetTestContextProperties(tcmProperties, sourceLevelParameters);
385384
var unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties);
386385

387-
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
388-
"Executed test {0}",
389-
unitTestElement.TestMethod.Name);
386+
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed test {0}", unitTestElement.TestMethod.Name);
390387

391388
var endTime = DateTimeOffset.Now;
392389

src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,9 @@ internal class TestMethodRunner
6464
/// <param name="captureDebugTraces">
6565
/// The capture debug traces.
6666
/// </param>
67-
public TestMethodRunner(
68-
TestMethodInfo testMethodInfo,
69-
TestMethod testMethod,
70-
ITestContext testContext,
71-
bool captureDebugTraces)
67+
public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, ITestContext testContext, bool captureDebugTraces)
7268
: this(testMethodInfo, testMethod, testContext, captureDebugTraces, ReflectHelper.Instance)
7369
{
74-
Debug.Assert(testMethodInfo != null, "testMethodInfo should not be null");
75-
Debug.Assert(testMethod != null, "testMethod should not be null");
76-
Debug.Assert(testContext != null, "testContext should not be null");
77-
78-
this.testMethodInfo = testMethodInfo;
79-
this.test = testMethod;
80-
this.testContext = testContext;
81-
this.captureDebugTraces = captureDebugTraces;
8270
}
8371

8472
/// <summary>
@@ -99,12 +87,7 @@ public TestMethodRunner(
9987
/// <param name="reflectHelper">
10088
/// The reflect Helper object.
10189
/// </param>
102-
public TestMethodRunner(
103-
TestMethodInfo testMethodInfo,
104-
TestMethod testMethod,
105-
ITestContext testContext,
106-
bool captureDebugTraces,
107-
ReflectHelper reflectHelper)
90+
public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, ITestContext testContext, bool captureDebugTraces, ReflectHelper reflectHelper)
10891
{
10992
Debug.Assert(testMethodInfo != null, "testMethodInfo should not be null");
11093
Debug.Assert(testMethod != null, "testMethod should not be null");
@@ -223,7 +206,8 @@ internal UnitTestResult[] RunTestMethod()
223206
{
224207
if (this.test.DataType == DynamicDataType.ITestDataSource)
225208
{
226-
var testResults = this.ExecuteTestWithDataSource(null, this.test.Data);
209+
var data = DataSerializationHelper.Deserialize(this.test.SerializedData, this.testMethodInfo.Parent.Parent.Assembly);
210+
var testResults = this.ExecuteTestWithDataSource(null, data);
227211
results.AddRange(testResults);
228212
}
229213
else if (this.ExecuteDataSourceBasedTests(results))
@@ -232,7 +216,7 @@ internal UnitTestResult[] RunTestMethod()
232216
}
233217
else
234218
{
235-
var testResults = this.ExecuteTest();
219+
var testResults = this.ExecuteTest(this.testMethodInfo);
236220

237221
foreach (var testResult in testResults)
238222
{
@@ -358,7 +342,7 @@ private UTF.TestResult[] ExecuteTestWithDataSource(UTF.ITestDataSource testDataS
358342
var stopwatch = Stopwatch.StartNew();
359343

360344
this.testMethodInfo.SetArguments(data);
361-
var testResults = this.ExecuteTest();
345+
var testResults = this.ExecuteTest(this.testMethodInfo);
362346
stopwatch.Stop();
363347

364348
var hasDisplayName = !string.IsNullOrWhiteSpace(this.test.DisplayName);
@@ -387,13 +371,21 @@ private UTF.TestResult[] ExecuteTestWithDataSource(UTF.ITestDataSource testDataS
387371

388372
private UTF.TestResult[] ExecuteTestWithDataRow(object dataRow, int rowIndex)
389373
{
390-
var stopwatch = Stopwatch.StartNew();
391-
392-
this.testContext.SetDataRow(dataRow);
393-
var testResults = this.ExecuteTest();
394-
stopwatch.Stop();
395-
396374
var displayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, this.test.DisplayName, rowIndex);
375+
Stopwatch stopwatch = null;
376+
377+
UTF.TestResult[] testResults = null;
378+
try
379+
{
380+
stopwatch = Stopwatch.StartNew();
381+
this.testContext.SetDataRow(dataRow);
382+
testResults = this.ExecuteTest(this.testMethodInfo);
383+
}
384+
finally
385+
{
386+
stopwatch?.Stop();
387+
this.testContext.SetDataRow(null);
388+
}
397389

398390
foreach (var testResult in testResults)
399391
{
@@ -405,11 +397,11 @@ private UTF.TestResult[] ExecuteTestWithDataRow(object dataRow, int rowIndex)
405397
return testResults;
406398
}
407399

408-
private UTF.TestResult[] ExecuteTest()
400+
private UTF.TestResult[] ExecuteTest(TestMethodInfo testMethodInfo)
409401
{
410402
try
411403
{
412-
return this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo);
404+
return this.testMethodInfo.TestMethodOptions.Executor.Execute(testMethodInfo);
413405
}
414406
catch (Exception ex)
415407
{

src/Adapter/MSTest.CoreAdapter/Execution/UnitTestRunner.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary<strin
9494
return new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.NotRunnable, testMethodInfo.NotRunnableReason) };
9595
}
9696

97-
return new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces).Execute();
97+
var testMethodRunner = new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces);
98+
return testMethodRunner.Execute();
9899
}
99100
}
100101
catch (TypeInspectionException ex)

src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string
8282
var data = testCase.GetPropertyValue<string[]>(Constants.TestDynamicDataProperty, null);
8383

8484
testMethod.DataType = dataType;
85-
testMethod.Data = Helpers.DataSerializationHelper.Deserialize(data);
85+
testMethod.SerializedData = data;
8686
}
8787

8888
testMethod.DisplayName = testCase.DisplayName;

src/Adapter/MSTest.CoreAdapter/Helpers/DataSerializationHelper.cs

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,23 @@
33

44
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers
55
{
6+
using System;
7+
using System.Collections.Generic;
68
using System.IO;
9+
using System.Linq;
10+
using System.Reflection;
711
using System.Runtime.Serialization.Json;
812
using System.Text;
913

1014
internal static class DataSerializationHelper
1115
{
16+
private static readonly Dictionary<Type, DataContractJsonSerializer> SerializerCache = new Dictionary<Type, DataContractJsonSerializer>();
17+
private static readonly DataContractJsonSerializerSettings SerializerSettings = new DataContractJsonSerializerSettings()
18+
{
19+
UseSimpleDictionaryFormat = true,
20+
EmitTypeInformation = System.Runtime.Serialization.EmitTypeInformation.Always
21+
};
22+
1223
/// <summary>
1324
/// Serializes the date in such a way that won't throw exceptions during deserialization in Test Platform.
1425
/// The result can be deserialized using <see cref="Deserialize(string[])"/> method.
@@ -22,23 +33,33 @@ public static string[] Serialize(object[] data)
2233
return null;
2334
}
2435

25-
var serializer = GetSerializer();
26-
var serializedData = new string[data.Length];
36+
var serializedData = new string[data.Length * 2];
2737

2838
for (int i = 0; i < data.Length; i++)
2939
{
40+
var typeIndex = i * 2;
41+
var dataIndex = typeIndex + 1;
42+
3043
if (data[i] == null)
3144
{
32-
serializedData[i] = null;
45+
serializedData[typeIndex] = null;
46+
serializedData[dataIndex] = null;
3347
continue;
3448
}
3549

50+
var type = data[i].GetType();
51+
var typeName = type.FullName;
52+
53+
serializedData[typeIndex] = typeName;
54+
55+
var serializer = GetSerializer(type);
56+
3657
using (var memoryStream = new MemoryStream())
3758
{
3859
serializer.WriteObject(memoryStream, data[i]);
3960
var serializerData = memoryStream.ToArray();
4061

41-
serializedData[i] = Encoding.UTF8.GetString(serializerData, 0, serializerData.Length);
62+
serializedData[dataIndex] = Encoding.UTF8.GetString(serializerData, 0, serializerData.Length);
4263
}
4364
}
4465

@@ -49,26 +70,33 @@ public static string[] Serialize(object[] data)
4970
/// Deserialzes the data serialzed by <see cref="Serialize(object[])" /> method.
5071
/// </summary>
5172
/// <param name="serializedData">Serialized data array to deserialize.</param>
73+
/// <param name="assemblies">Assemblies that serialized types defined in.</param>
5274
/// <returns>Deserialized array.</returns>
53-
public static object[] Deserialize(string[] serializedData)
75+
public static object[] Deserialize(string[] serializedData, params Assembly[] assemblies)
5476
{
55-
if (serializedData == null)
77+
if (serializedData == null || serializedData.Length % 2 != 0)
5678
{
5779
return null;
5880
}
5981

60-
var serializer = GetSerializer();
61-
var data = new object[serializedData.Length];
82+
var length = serializedData.Length / 2;
83+
var data = new object[length];
6284

63-
for (int i = 0; i < serializedData.Length; i++)
85+
for (int i = 0; i < length; i++)
6486
{
87+
var typeIndex = i * 2;
88+
var dataIndex = typeIndex + 1;
89+
6590
if (serializedData[i] == null)
6691
{
6792
data[i] = null;
6893
continue;
6994
}
7095

71-
var serialzedDataBytes = Encoding.UTF8.GetBytes(serializedData[i]);
96+
var typeName = serializedData[typeIndex];
97+
var serializer = GetSerializer(typeName, assemblies);
98+
99+
var serialzedDataBytes = Encoding.UTF8.GetBytes(serializedData[dataIndex]);
72100
using (var memoryStream = new MemoryStream(serialzedDataBytes))
73101
{
74102
data[i] = serializer.ReadObject(memoryStream);
@@ -78,17 +106,43 @@ public static object[] Deserialize(string[] serializedData)
78106
return data;
79107
}
80108

81-
private static DataContractJsonSerializer GetSerializer()
109+
private static DataContractJsonSerializer GetSerializer(string typeName, Assembly[] assemblies)
82110
{
83-
var settings = new DataContractJsonSerializerSettings()
111+
var serializer = SerializerCache.SingleOrDefault(i => i.Key.FullName == typeName);
112+
if (serializer.Value != null)
113+
{
114+
return serializer.Value;
115+
}
116+
117+
var type = Type.GetType(typeName);
118+
if (type != null)
84119
{
85-
UseSimpleDictionaryFormat = true,
86-
EmitTypeInformation = System.Runtime.Serialization.EmitTypeInformation.Always
87-
};
120+
return GetSerializer(type);
121+
}
122+
123+
if (assemblies != null)
124+
{
125+
foreach (var assembly in assemblies)
126+
{
127+
type = assembly.GetType(typeName);
128+
if (type != null)
129+
{
130+
return GetSerializer(type);
131+
}
132+
}
133+
}
88134

89-
var serializer = new DataContractJsonSerializer(typeof(object), settings);
135+
return GetSerializer(typeof(object));
136+
}
137+
138+
private static DataContractJsonSerializer GetSerializer(Type type)
139+
{
140+
if (SerializerCache.ContainsKey(type))
141+
{
142+
return SerializerCache[type];
143+
}
90144

91-
return serializer;
145+
return SerializerCache[type] = new DataContractJsonSerializer(type, SerializerSettings);
92146
}
93147
}
94148
}

src/Adapter/MSTest.CoreAdapter/ObjectModel/TestMethod.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ public string DeclaringClassFullName
144144
internal DynamicDataType DataType { get; set; }
145145

146146
/// <summary>
147-
/// Gets or sets indices of dynamic data
147+
/// Gets or sets the serialized data
148148
/// </summary>
149-
internal object[] Data { get; set; }
149+
internal string[] SerializedData { get; set; }
150150

151151
/// <summary>
152152
/// Gets or sets the test group set during discovery

src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ internal TestCase ToTestCase()
214214
// Store resolved data if any
215215
if (this.TestMethod.DataType != DynamicDataType.None)
216216
{
217-
var data = Helpers.DataSerializationHelper.Serialize(this.TestMethod.Data);
217+
var data = this.TestMethod.SerializedData;
218218

219219
testCase.SetPropertyValue(TestAdapter.Constants.TestDynamicDataTypeProperty, (int)this.TestMethod.DataType);
220220
testCase.SetPropertyValue(TestAdapter.Constants.TestDynamicDataProperty, data);

0 commit comments

Comments
 (0)