Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection
{
using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;

// <inheritdoc />
internal class InProcDataCollectionSink : IDataCollectionSink
{
private IDictionary<Guid, TestCaseDataCollectionData> testCaseDataCollectionDataMap;

/// <summary>
/// In process data collection sink
/// </summary>
public InProcDataCollectionSink()
{
this.testCaseDataCollectionDataMap = new Dictionary<Guid, TestCaseDataCollectionData>();
}

// <inheritdoc />
public void SendData(DataCollectionContext dataCollectionContext, string key, string value)
{
ValidateArg.NotNullOrEmpty(key, "key");
ValidateArg.NotNullOrEmpty(value, "value");
ValidateArg.NotNullOrEmpty(dataCollectionContext.TestCase.Id.ToString(), "dataCollectionContext.TestCase.Id");

var testCaseId = dataCollectionContext.TestCase.Id;
this.AddKeyValuePairToDictionary(testCaseId, key, value);
}


/// <summary>
/// Gets the data collection data stored in the in process data collection sink
/// </summary>
/// <param name="testCaseId">valid test case id</param>
/// <returns>test data collection dictionary </returns>
public IDictionary<string, string> GetDataCollectionDataSetForTestCase(Guid testCaseId)
{
TestCaseDataCollectionData testCaseDataCollection = null;

if (!this.testCaseDataCollectionDataMap.TryGetValue(testCaseId, out testCaseDataCollection))
{
if (EqtTrace.IsWarningEnabled)
{
EqtTrace.Warning("No DataCollection Data set for the test case {0}", testCaseId);
}
return new Dictionary<string, string>();
}
else
{
this.testCaseDataCollectionDataMap.Remove(testCaseId);
return testCaseDataCollection.CollectionData;
}
}

private void AddKeyValuePairToDictionary(Guid testCaseId, string key, string value)
{
if (!this.testCaseDataCollectionDataMap.ContainsKey(testCaseId))
{
var testCaseCollectionData = new TestCaseDataCollectionData();
testCaseCollectionData.AddOrUpdateData(key, value);
this.testCaseDataCollectionDataMap[testCaseId] = testCaseCollectionData;

}
else
{
this.testCaseDataCollectionDataMap[testCaseId].AddOrUpdateData(key, value);
}
}

private class TestCaseDataCollectionData
{
public TestCaseDataCollectionData()
{
this.CollectionData = new Dictionary<string, string>();
}

internal IDictionary<string, string> CollectionData { get; private set; }

internal void AddOrUpdateData(string key, string value)
{
if (!this.CollectionData.ContainsKey(key))
{
this.CollectionData[key] = value;
}
else
{
if (EqtTrace.IsWarningEnabled)
{
EqtTrace.Warning("The data for inprocdata collector with key {0} has already been set. Will be reset with new value", key);
}
this.CollectionData[key] = value;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Execution
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection
{
using System;
using System.Collections.Generic;
Expand All @@ -17,23 +17,30 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Execution
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using ObjectModel.DataCollector.InProcDataCollector;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection;

/// <summary>
/// The in process data collection extension manager.
/// </summary>
public class InProcDataCollectionExtensionManager
{
private IDictionary<string, object> inProcDataCollectors;
private IDataCollectionSink inProcDataCollectionSink;
private IDictionary<string, Tuple<string, string>> inProcDataSinkDict;

public InProcDataCollectionExtensionManager()
{
this.inProcDataCollectors = new Dictionary<string,Object>();
this.inProcDataCollectors = new Dictionary<string, Object>();
this.inProcDataCollectionSink = new InProcDataCollectionSink();
this.inProcDataSinkDict = new Dictionary<string, Tuple<string, string>>();
}

public InProcDataCollectionExtensionManager(string runsettings)
{
this.inProcDataCollectors = new Dictionary<string, Object>();
InProcDataCollectionUtilities.ReadInProcDataCollectionRunSettings(runsettings);
this.inProcDataCollectionSink = new InProcDataCollectionSink();
this.inProcDataSinkDict = new Dictionary<string, Tuple<string, string>>();
this.InitializeInProcDataCollectors();
}

Expand Down Expand Up @@ -65,9 +72,11 @@ public void InitializeInProcDataCollectors()
var type =
assembly?.GetTypes()
.FirstOrDefault(x => (x.AssemblyQualifiedName.Equals(inProcDc.AssemblyQualifiedName) && interfaceType.GetTypeInfo().IsAssignableFrom(x)));
if (type != null && !this.inProcDataCollectors.ContainsKey(type.AssemblyQualifiedName))//this.inProcDataCollectionTypes.Contains(type)
if (type != null && !this.inProcDataCollectors.ContainsKey(type.AssemblyQualifiedName))
{
this.inProcDataCollectors[type.AssemblyQualifiedName] = CreateObjectFromType(type);
var obj = CreateObjectFromType(type);
InvokeInitializeOnInProcDataCollector(obj, this.inProcDataCollectionSink);
this.inProcDataCollectors[type.AssemblyQualifiedName] = obj;
}
}
}
Expand All @@ -76,7 +85,7 @@ public void InitializeInProcDataCollectors()
{
EqtTrace.Error("Error occured while Initializing the datacollectors : {0}", ex);
}
}
}

/// <summary>
/// The trigger session start.
Expand All @@ -87,8 +96,6 @@ public virtual void TriggerTestSessionStart()
this.TriggerInProcDataCollectionMethods(Constants.TestSessionStartMethodName, testSessionStartArgs);
}



/// <summary>
/// The trigger session end.
/// </summary>
Expand All @@ -113,10 +120,20 @@ public virtual void TriggerTestCaseStart(TestCase testCase)
/// </summary>
public virtual void TriggerTestCaseEnd(TestCase testCase, TestOutcome outcome)
{
var testCaseEndArgs = new TestCaseEndArgs(testCase, outcome);
var dataCollectionContext = new DataCollectionContext(testCase);
var testCaseEndArgs = new TestCaseEndArgs(dataCollectionContext, outcome);
this.TriggerInProcDataCollectionMethods(Constants.TestCaseEndMethodName, testCaseEndArgs);
}

/// <summary>
/// Triggers the send test result method
/// </summary>
/// <param name="testResult"></param>
public virtual void TriggerUpdateTestResult(TestResult testResult)
{
this.SetInProcDataCollectionDataInTestResult(testResult);
}

/// <summary>
/// Loads the assembly into the default context based on the codebase path
/// </summary>
Expand All @@ -126,7 +143,7 @@ private Assembly LoadInProcDataCollectorExtension(string codeBase)
{
Assembly assembly = null;
try
{
{
#if NET46
assembly = Assembly.LoadFrom(codeBase);
#else
Expand Down Expand Up @@ -154,6 +171,12 @@ private static object CreateObjectFromType(Type type)
return obj;
}

private static void InvokeInitializeOnInProcDataCollector(object obj, IDataCollectionSink inProcDataCollectionSink)
{
var initializeMethodInfo = GetMethodInfoFromType(obj.GetType(), "Initialize", new Type[] { typeof(IDataCollectionSink) });
initializeMethodInfo.Invoke(obj, new object[] { inProcDataCollectionSink });
}

private static MethodInfo GetMethodInfoFromType(Type type, string funcName, Type[] argumentTypes)
{
MethodInfo methodInfo = null;
Expand All @@ -179,11 +202,11 @@ private void TriggerInProcDataCollectionMethods(string methodName, InProcDataCol
x => x.AssemblyQualifiedName.Equals(entry.Key))?
.Configuration.OuterXml;
testSessionStartArgs.Configuration = config;
methodInfo?.Invoke(entry.Value, new[] { testSessionStartArgs });
methodInfo?.Invoke(entry.Value, new object[] { testSessionStartArgs });
}
else
{
methodInfo?.Invoke(entry.Value, new[] { methodArg });
methodInfo?.Invoke(entry.Value, new object[] { methodArg });
}
}
}
Expand All @@ -192,6 +215,28 @@ private void TriggerInProcDataCollectionMethods(string methodName, InProcDataCol
EqtTrace.Error("Error occured while Triggering the {0} method : {1}", methodName, ex);
}
}

/// <summary>
/// Set the data sent via datacollection sink in the testresult property for upstream applications to read.
/// And removes the data from the dictionary.
/// </summary>
/// <param name="testResultArg"></param>
private void SetInProcDataCollectionDataInTestResult(TestResult testResult)
{
//Loops through each datacollector reads the data collection data and sets as TestResult property.
foreach (var entry in this.inProcDataCollectors)
{
var dataCollectionData =
((InProcDataCollectionSink)this.inProcDataCollectionSink).GetDataCollectionDataSetForTestCase(
testResult.TestCase.Id);

foreach (var keyValuePair in dataCollectionData)
{
var testProperty = TestProperty.Register(id: keyValuePair.Key, label: keyValuePair.Key, category: string.Empty, description: string.Empty, valueType: typeof(string), validateValueCallback: null, attributes: TestPropertyAttributes.None, owner: typeof(TestCase));
testResult.SetPropertyValue(testProperty, keyValuePair.Value);
}
}
}
}

public static class Constants
Expand All @@ -215,6 +260,5 @@ public static class Constants
/// The test case end method name.
/// </summary>
public const string TestCaseEndMethodName = "TestCaseEnd";

}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@

namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.EventHandlers
{

using System;

using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection;
#if !NET46
using System.Runtime.Loader;
#endif
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Execution;

using TestPlatform.ObjectModel;
using TestPlatform.ObjectModel.Engine;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;

/// <summary>
/// The test case events handler.
Expand All @@ -18,7 +19,6 @@ internal class TestCaseEventsHandler : ITestCaseEventsHandler
{

private InProcDataCollectionExtensionManager inProcDataCollectionExtensionManager;

private ITestCaseEventsHandler testCaseEvents;

/// <summary>
Expand Down Expand Up @@ -68,6 +68,7 @@ public void SendTestCaseEnd(TestCase testCase, TestOutcome outcome)
/// </param>
public void SendTestResult(TestResult result)
{
this.inProcDataCollectionExtensionManager.TriggerUpdateTestResult(result);
this.testCaseEvents?.SendTestResult(result);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Execution
using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.SettingsProvider;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Discovery;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.EventHandlers;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection
{
using System;

/// <summary>
/// Class representing the context in which data collection occurs.
/// </summary>
Expand Down Expand Up @@ -29,6 +31,15 @@ public class DataCollectionContext
// have public constructors. This will allow them to instantiate their
// class and pass to us for creating data collection events.

/// <summary>
/// Constructs DataCollection Context for in process data collectors
/// </summary>
/// <param name="testCase">test case to identify the context</param>
public DataCollectionContext(TestCase testCase)
{
this.TestCase = testCase;
}

/// <summary>
/// Constructs a DataCollectionContext indicating that there is a session,
/// but no executing test, in context.
Expand All @@ -55,11 +66,15 @@ protected internal DataCollectionContext(SessionId sessionId, TestExecId testExe
this.testExecId = testExecId;
this.hashCode = ComputeHashCode();
}

#endregion

#region Properties

/// <summary>
/// Gets test case.
/// </summary>
public TestCase TestCase { get; private set; }

/// <summary>
/// Identifies the session under which the data collection occurs. Will not be null.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection
{
/// <summary>
/// Class used by data collectors to send data to up-stream components
/// (agent, controller, client, etc).
/// </summary>
public interface IDataCollectionSink
{
/// <summary>
/// The send data will send the data collection data to upstream applications.
/// Key will be set as TestProperty in TestResult and value as corresponding value.
/// </summary>
/// <param name="dataCollectionContext">
/// The data collection context.
/// </param>
/// <param name="key">
/// The key should be unique for a data collector.
/// </param>
/// <param name="value">
/// The value should be a string or an object serialized into a JSON string.
/// </param>
void SendData(DataCollectionContext dataCollectionContext, string key, string value);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.InProcDataCollector
/// </summary>
public interface InProcDataCollection
{
/// <summary>
/// Initializes the In Process DataCollection with the DataCollectionSink
/// </summary>
/// <param name="dataCollectionSink">data collection sink object</param>
void Initialize(IDataCollectionSink dataCollectionSink);

/// <summary>
/// Called when test session starts
/// </summary>
Expand Down
Loading