Skip to content

Commit

Permalink
Performance Tests Automation API (#63)
Browse files Browse the repository at this point in the history
1. Performance automation using the Microsoft.Diagnostics.Tracing.TraceEvent API
2. PerformanceTestBase created to write the Performance tests
3. Sample Performance tests.
  • Loading branch information
navin22 authored and codito committed Sep 23, 2016
1 parent 4f34968 commit 2bbf394
Show file tree
Hide file tree
Showing 11 changed files with 679 additions and 2 deletions.
10 changes: 10 additions & 0 deletions TestPlatform.sln
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SimpleDataCollector", "test
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SimpleTestProject", "test\TestAssets\SimpleTestProject\SimpleTestProject.xproj", "{0CC51637-B665-47B0-A093-042D31785928}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Performance", "Performance", "{44DABCFB-7AA0-4682-B7F7-067E0ABA1D14}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.TestPlatform.PerformanceTests", "test\Performance\Microsoft.TestPlatform.PerformanceTests\Microsoft.TestPlatform.PerformanceTests.xproj", "{12D59CED-9916-4C3F-AF82-12E019757FD2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -233,6 +237,10 @@ Global
{0CC51637-B665-47B0-A093-042D31785928}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CC51637-B665-47B0-A093-042D31785928}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CC51637-B665-47B0-A093-042D31785928}.Release|Any CPU.Build.0 = Release|Any CPU
{12D59CED-9916-4C3F-AF82-12E019757FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12D59CED-9916-4C3F-AF82-12E019757FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12D59CED-9916-4C3F-AF82-12E019757FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12D59CED-9916-4C3F-AF82-12E019757FD2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -275,5 +283,7 @@ Global
{50D7D355-08F6-4DFD-AEAA-9BCE41C94C18} = {463031A2-7F16-4E38-9944-1F5161D04933}
{D7F7A9F5-5646-44E7-990C-500844E9272E} = {50D7D355-08F6-4DFD-AEAA-9BCE41C94C18}
{0CC51637-B665-47B0-A093-042D31785928} = {50D7D355-08F6-4DFD-AEAA-9BCE41C94C18}
{44DABCFB-7AA0-4682-B7F7-067E0ABA1D14} = {463031A2-7F16-4E38-9944-1F5161D04933}
{12D59CED-9916-4C3F-AF82-12E019757FD2} = {44DABCFB-7AA0-4682-B7F7-067E0ABA1D14}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ private TimeSpan RunTestsInternal()
totalTests);

stopwatch.Stop();
this.testPlatformEventSource.ExecutionStop(totalTests);
this.testPlatformEventSource.ExecutionStop(this.testRunCache.TotalExecutedTests);
this.BeforeRaisingTestRunComplete(exceptionsHitDuringRunTests);
return stopwatch.Elapsed;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.TestPlatform.TestUtilities.PerfInstrumentation
{
/// <summary>
/// The constants.
/// </summary>
public class Constants
{
/// <summary>
/// The discovery task.
/// </summary>
public const string DiscoveryTask = "Discovery";

/// <summary>
/// The execution task.
/// </summary>
public const string ExecutionTask = "Execution";

/// <summary>
/// The execution request task.
/// </summary>
public const string ExecutionRequestTask = "ExecutionRequest";

/// <summary>
/// The discovery request task.
/// </summary>
public const string DiscoveryRequestTask = "DiscoveryRequest";

/// <summary>
/// The test host task.
/// </summary>
public const string TestHostTask = "TestHost";

/// <summary>
/// The vs test console task.
/// </summary>
public const string VsTestConsoleTask = "VsTestConsole";

/// <summary>
/// The adapter search task.
/// </summary>
public const string AdapterSearchTask = "AdapterSearch";

/// <summary>
/// The adapter execution task.
/// </summary>
public const string AdapterExecutionTask = "AdapterExecution";

/// <summary>
/// The adapter discovery task.
/// </summary>
public const string AdapterDiscoveryTask = "AdapterDiscovery";

#region PayLoad Property Names

/// <summary>
/// The execution uri property.
/// </summary>
public const string ExecutionUriProperty = "executorUri";

/// <summary>
/// The number of tests property.
/// </summary>
public const string NumberOfTestsProperty = "numberOfTests";
#endregion

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.TestPlatform.TestUtilities.PerfInstrumentation
{
using System.Collections.Generic;

#if NET46
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
using System;
using System.Linq;
#endif

/// <summary>
/// The perf analyzer.
/// </summary>
public class PerfAnalyzer
{
/// <summary>
/// The etw session provider name.
/// </summary>
private const string ETWSessionProviderName = "TestPlatform";

#if NET46
private string perfDataFileName;
private TraceEventSession traceEventSession;
private Dictionary<string, List<TestPlatformTask>> testPlatformTaskMap;
#endif

/// <summary>
/// Initializes a new instance of the <see cref="PerfAnalyzer"/> class.
/// </summary>
public PerfAnalyzer()
{
#if NET46
this.perfDataFileName = "TestPlatformEventsData.etl";
this.testPlatformTaskMap = new Dictionary<string, List<TestPlatformTask>>();
this.traceEventSession = new TraceEventSession("TestPlatofrmSession", this.perfDataFileName);
#endif
}

/// <summary>
/// The enable provider.
/// </summary>
public void EnableProvider()
{
#if NET46
this.traceEventSession.StopOnDispose = true;
this.traceEventSession.EnableProvider(ETWSessionProviderName);
#endif
}

/// <summary>
/// The disable provider.
/// </summary>
public void DisableProvider()
{
#if NET46
this.traceEventSession.Dispose();
#endif
}

/// <summary>
/// The analyze events data.
/// </summary>
public void AnalyzeEventsData()
{
#if NET46
using (var source = new ETWTraceEventSource(this.perfDataFileName))
{
// Open the file
var parser = new DynamicTraceEventParser(source);
parser.All += delegate(TraceEvent data)
{
try
{
if (data.ProviderName.Equals("TestPlatform") && !data.EventName.Equals("ManifestData"))
{
Console.WriteLine("Received Event : {0}", data.ToString());
var key = data.ProcessID + "_" + data.ThreadID.ToString() + "_" + data.TaskName;

if (!testPlatformTaskMap.ContainsKey(key))
{
var list = new List<TestPlatformTask> { CreateTestPlatformTask(data) };
testPlatformTaskMap.Add(key, list);
}
else
{
if (data.Opcode == TraceEventOpcode.Start)
{
testPlatformTaskMap[key].Add(CreateTestPlatformTask(data));
}
else
{
UpdateTask(testPlatformTaskMap[key].Last(), data);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
};
source.Process(); // Read the file, processing the callbacks.
}
#endif
}

/// <summary>
/// The get elapsed time by task name.
/// </summary>
/// <param name="taskName">
/// The task name.
/// </param>
/// <returns>
/// The <see cref="double"/>.
/// </returns>
public double GetElapsedTimeByTaskName(string taskName)
{
var timeTaken = 0.0;
#if NET46
var key = GetEventKey(taskName);

if (key != null)
{
var task = testPlatformTaskMap[key].First();
timeTaken = task.EventStopped - task.EventStarted;
}
#endif
return timeTaken;
}

/// <summary>
/// The get event data by task name.
/// </summary>
/// <param name="taskName">
/// The task name.
/// </param>
/// <returns>
/// The <see cref="IDictionary"/>.
/// </returns>
public IDictionary<string, string> GetEventDataByTaskName(string taskName)
{
IDictionary<string, string> properties = new Dictionary<string, string>();
#if NET46
var key = GetEventKey(taskName);

if(key != null)
{
properties = testPlatformTaskMap[key].First().PayLoadProperties;
}
#endif
return properties;
}

public double GetAdapterExecutionTime(string executorUri)
{
var timeTaken = 0.0;
#if NET46
var key = GetEventKey(Constants.AdapterExecutionTask);

if(key != null)
{
var task = testPlatformTaskMap[key].FirstOrDefault(t => t.PayLoadProperties["executorUri"].Equals(executorUri));
timeTaken = task.EventStopped - task.EventStarted;
}
#endif
return timeTaken;
}

public long GetAdapterExecutedTests(string executorUri)
{
long totalTestsExecuted = 0;
#if NET46
var key = GetEventKey(Constants.AdapterExecutionTask);

if (key != null)
{
var task = testPlatformTaskMap[key].FirstOrDefault(t => t.PayLoadProperties["executorUri"].Equals(executorUri));
long.TryParse(task.PayLoadProperties["numberOfTests"].ToString(), out totalTestsExecuted);
}
#endif
return totalTestsExecuted;
}

#if NET46

private string GetEventKey(string taskName)
{
string key = null;

key = testPlatformTaskMap.Keys.FirstOrDefault(k => k.Split('_')[2].Equals(taskName));

return key;
}

private static TestPlatformTask CreateTestPlatformTask(TraceEvent data)
{
var task = new TestPlatformTask(data.TaskName, data.TimeStampRelativeMSec);
task.PayLoadProperties = GetPayloadProperties(data);
return task;
}

private static void UpdateTask(TestPlatformTask task, TraceEvent data)
{
task.EventStopped = data.TimeStampRelativeMSec;
var payLoadProperties = GetPayloadProperties(data);

//Merging dictionaries look for better way
foreach (var k in payLoadProperties.Keys)
{
if (!task.PayLoadProperties.ContainsKey(k))
{
task.PayLoadProperties.Add(k, payLoadProperties[k]);
}
}
}

private static IDictionary<string, string> GetPayloadProperties(TraceEvent data)
{
var payLoadProperties = new Dictionary<string, string>();

foreach (var payLoad in data.PayloadNames)
{
var payLoadData = data.PayloadByName(payLoad).ToString();
if (!payLoadProperties.ContainsKey(payLoad))
{
payLoadProperties.Add(payLoad, payLoadData);
}
else
{
payLoadProperties[payLoad] = payLoadData;
}
}

return payLoadProperties;
}
#endif
}
}
Loading

0 comments on commit 2bbf394

Please sign in to comment.