Add stress reporting infrastructure to conditionally output stress results.

- The default reporting mechanisms do not output stress data.
- Can be overridden by configuration file `stressConfig.json` and running the tests with a `-stressconfig` option.
- Updated runner to pass through metric information.
- Updated `.gitignore` with `TestOutput`
This commit is contained in:
N. Taylor Mullen 2015-09-08 16:32:18 -07:00
Родитель 9801e861fe
Коммит c9999cd9df
13 изменённых файлов: 207 добавлений и 12 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -7,6 +7,7 @@ _ReSharper.*/
packages/
artifacts/
PublishProfiles/
TestOutput/
*.user
*.suo
*.cache

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using Xunit.Abstractions;
namespace Microsoft.AspNet.StressFramework.Collectors
{

Просмотреть файл

@ -6,13 +6,14 @@ namespace Microsoft.AspNet.StressFramework.Collectors
{
public class MetricsRecordedMessage : IMessageSinkMessage
{
private ITest Test { get; }
private IReadOnlyList<Metric> Metrics { get; }
public MetricsRecordedMessage(ITest test, IEnumerable<Metric> metrics)
{
Test = test;
Metrics = metrics.ToList().AsReadOnly();
}
public ITest Test { get; }
public IReadOnlyList<Metric> Metrics { get; }
}
}

Просмотреть файл

@ -0,0 +1,38 @@
using System;
using Microsoft.AspNet.StressFramework.Collectors;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNet.StressFramework
{
public abstract class DefaultStressTestMessageVisitor : DefaultRunnerReporterMessageHandler
{
public DefaultStressTestMessageVisitor(IRunnerLogger logger)
: base(logger)
{
}
public override bool OnMessage(IMessageSinkMessage message)
{
return DoVisit<MetricsRecordedMessage>(message, (t, m) => t.Visit(m)) &&
base.OnMessage(message);
}
protected virtual bool Visit(MetricsRecordedMessage metric)
{
return true;
}
bool DoVisit<TMessage>(IMessageSinkMessage message, Func<DefaultStressTestMessageVisitor, TMessage, bool> callback)
where TMessage : class, IMessageSinkMessage
{
var castMessage = message as TMessage;
if (castMessage != null)
{
return callback(this, castMessage);
}
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNet.StressFramework
{
public class DefaultStressTestRunnerReporter : DefaultRunnerReporter
{
public override string RunnerSwitch => "stress";
public override string Description => "doesn't output stress test results.";
public override IMessageSink CreateMessageHandler(IRunnerLogger logger)
{
return new DefaultRunnerReporterMessageHandler(new ConsoleRunnerLogger(useColors: true));
}
}
}

Просмотреть файл

@ -49,7 +49,7 @@ namespace Microsoft.AspNet.StressFramework
DisplayName = suppliedDisplayName ?? BaseDisplayName;
Iterations = iterations;
WarmupIterations = warmupIterations;
TestMethodArguments = testMethodArguments?.ToArray();
}

Просмотреть файл

@ -13,10 +13,6 @@ namespace Microsoft.AspNet.StressFramework
private readonly IMessageBus _bus;
private readonly Process _me;
private MemoryUsage _startMemory;
private MemoryUsage _endMemory;
private CpuTime _startCpu;
public IReadOnlyList<Metric> Recordings { get; }
public int Iteration { get; }

Просмотреть файл

@ -9,6 +9,7 @@ using Xunit.Abstractions;
using Xunit.Sdk;
using System.Collections.Generic;
using Microsoft.AspNet.StressFramework.Collectors;
using System.Linq;
using System.Reflection;
#if DNXCORE50 || DNX451

Просмотреть файл

@ -0,0 +1,66 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.AspNet.StressFramework.Collectors;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNet.StressFramework
{
public class StressTestRunnerCSVReporter : DefaultRunnerReporter
{
public override string RunnerSwitch => "stresscsv";
public override string Description => "output stress test results to CSV files. 1 CSV file per test.";
public override IMessageSink CreateMessageHandler(IRunnerLogger logger)
{
return new CSVRunnerReporterMessageHandler(new ConsoleRunnerLogger(useColors: true));
}
private class CSVRunnerReporterMessageHandler : DefaultStressTestMessageVisitor
{
public CSVRunnerReporterMessageHandler(IRunnerLogger logger)
: base(logger)
{
}
protected override bool Visit(MetricsRecordedMessage metricsRecordedMessage)
{
Debug.Assert(metricsRecordedMessage.Test.TestCase is StressTestCase);
WriteCSV(metricsRecordedMessage);
return true;
}
private static void WriteCSV(MetricsRecordedMessage metricsRecordedMessage)
{
var test = metricsRecordedMessage.Test;
var metrics = metricsRecordedMessage.Metrics;
var testOutputDir = Path.Combine(Directory.GetCurrentDirectory(), "TestOutput");
if (!Directory.Exists(testOutputDir))
{
Directory.CreateDirectory(testOutputDir);
}
var outputFile = Path.Combine(testOutputDir, $"{test.TestCase.DisplayName}.{test.TestCase.UniqueID}.metrics.csv");
using (var writer = new StreamWriter(new FileStream(outputFile, FileMode.Create)))
{
writer.WriteLine("Iteration,Heap,WorkingSet,PrivateBytes,ElapsedTicks");
foreach (var iteration in metrics.GroupBy(g => g.Iteration).OrderBy(g => g.Key))
{
var elapsed = (ElapsedTime)iteration.Single(m => m.Value is ElapsedTime).Value;
var mem = (MemoryUsage)iteration.Single(m => m.Value is MemoryUsage).Value;
writer.WriteLine($"{iteration.Key},{mem.HeapMemoryBytes},{mem.WorkingSet},{mem.PrivateBytes},{elapsed.Elapsed.Ticks}");
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
using System;
using System.IO;
using Microsoft.Framework.Configuration;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNet.StressFramework
{
public class StressTestRunnerConfigReporter : DefaultRunnerReporter
{
private const string StressConfigFileName = "stressConfig.json";
private const string StressOutputReporterConfigName = "StressOutputReporter";
private static readonly Lazy<IRunnerReporter> _defaultReporter = new Lazy<IRunnerReporter>(
() => new DefaultStressTestRunnerReporter());
private static readonly Lazy<IRunnerReporter> _csvReporter = new Lazy<IRunnerReporter>(
() => new StressTestRunnerCSVReporter());
private IRunnerReporter _activeReporter;
private IRunnerReporter DefaultReporter => _defaultReporter.Value;
private IRunnerReporter CSVReporter => _csvReporter.Value;
public override string RunnerSwitch => "stressconfig";
public override string Description => "determine stress output mechanism based on ";
public override IMessageSink CreateMessageHandler(IRunnerLogger logger)
{
if (_activeReporter == null)
{
var stressConfigFile = new FileInfo(StressConfigFileName);
if (!stressConfigFile.Exists)
{
throw new InvalidOperationException(
$"Cannot determine stress configuration. {StressConfigFileName} does not exist.");
}
var config = new ConfigurationBuilder(".")
.AddJsonFile(StressConfigFileName)
.Build();
var stressTestOutputReporterName = config[StressOutputReporterConfigName];
if (!string.IsNullOrEmpty(stressTestOutputReporterName) ||
string.Equals(
stressTestOutputReporterName,
DefaultReporter.RunnerSwitch,
StringComparison.OrdinalIgnoreCase))
{
_activeReporter = DefaultReporter;
}
else if (string.Equals(
stressTestOutputReporterName,
CSVReporter.RunnerSwitch,
StringComparison.OrdinalIgnoreCase))
{
_activeReporter = CSVReporter;
}
else
{
throw new InvalidOperationException(
$"Unknown {StressOutputReporterConfigName} value in {StressConfigFileName}.");
}
}
return _activeReporter.CreateMessageHandler(logger);
}
}
}

Просмотреть файл

@ -1,10 +1,17 @@
{
"version": "1.0.0-*",
"dependencies": {
"xunit.runner.aspnet": "2.0.0-aspnet-*"
"Microsoft.Framework.Configuration.Json": "1.0.0-*",
"xunit.assert": "2.1.0-*",
"xunit.core": "2.1.0-*",
"xunit.runner.dnx": "2.1.0-*"
},
"frameworks": {
"dnx451": { },
"dnx451": {
"frameworkAssemblies": {
"System.Reflection": "4.0.0.0"
}
},
"dnxcore50": {
"dependencies": {
"System.Runtime": "4.0.21-beta-*"

Просмотреть файл

@ -1,7 +1,7 @@
{
"version": "1.0.0-*",
"commands": {
"test": "xunit.runner.aspnet"
"test": "xunit.runner.dnx"
},
"dependencies": {
"Microsoft.AspNet.StressFramework": "",

Просмотреть файл

@ -1,7 +1,7 @@
{
"version": "1.0.0-*",
"commands": {
"test": "xunit.runner.aspnet"
"test": "xunit.runner.dnx"
},
"dependencies": {
"Microsoft.AspNet.StressFramework": "",