diff --git a/TestPlatform.sln b/TestPlatform.sln index 091ee3b0c9..bbd177eff0 100644 --- a/TestPlatform.sln +++ b/TestPlatform.sln @@ -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 @@ -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 @@ -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 diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/BaseRunTests.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/BaseRunTests.cs index 06da1f28d8..893215107b 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/BaseRunTests.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Execution/BaseRunTests.cs @@ -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; } diff --git a/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/Constants.cs b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/Constants.cs new file mode 100644 index 0000000000..4ac8615d49 --- /dev/null +++ b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/Constants.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.TestPlatform.TestUtilities.PerfInstrumentation +{ + /// + /// The constants. + /// + public class Constants + { + /// + /// The discovery task. + /// + public const string DiscoveryTask = "Discovery"; + + /// + /// The execution task. + /// + public const string ExecutionTask = "Execution"; + + /// + /// The execution request task. + /// + public const string ExecutionRequestTask = "ExecutionRequest"; + + /// + /// The discovery request task. + /// + public const string DiscoveryRequestTask = "DiscoveryRequest"; + + /// + /// The test host task. + /// + public const string TestHostTask = "TestHost"; + + /// + /// The vs test console task. + /// + public const string VsTestConsoleTask = "VsTestConsole"; + + /// + /// The adapter search task. + /// + public const string AdapterSearchTask = "AdapterSearch"; + + /// + /// The adapter execution task. + /// + public const string AdapterExecutionTask = "AdapterExecution"; + + /// + /// The adapter discovery task. + /// + public const string AdapterDiscoveryTask = "AdapterDiscovery"; + + #region PayLoad Property Names + + /// + /// The execution uri property. + /// + public const string ExecutionUriProperty = "executorUri"; + + /// + /// The number of tests property. + /// + public const string NumberOfTestsProperty = "numberOfTests"; + #endregion + + } +} diff --git a/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/PerfAnalyzer.cs b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/PerfAnalyzer.cs new file mode 100644 index 0000000000..c7291343b6 --- /dev/null +++ b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/PerfAnalyzer.cs @@ -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 + + /// + /// The perf analyzer. + /// + public class PerfAnalyzer + { + /// + /// The etw session provider name. + /// + private const string ETWSessionProviderName = "TestPlatform"; + +#if NET46 + private string perfDataFileName; + private TraceEventSession traceEventSession; + private Dictionary> testPlatformTaskMap; +#endif + + /// + /// Initializes a new instance of the class. + /// + public PerfAnalyzer() + { +#if NET46 + this.perfDataFileName = "TestPlatformEventsData.etl"; + this.testPlatformTaskMap = new Dictionary>(); + this.traceEventSession = new TraceEventSession("TestPlatofrmSession", this.perfDataFileName); +#endif + } + + /// + /// The enable provider. + /// + public void EnableProvider() + { +#if NET46 + this.traceEventSession.StopOnDispose = true; + this.traceEventSession.EnableProvider(ETWSessionProviderName); +#endif + } + + /// + /// The disable provider. + /// + public void DisableProvider() + { +#if NET46 + this.traceEventSession.Dispose(); +#endif + } + + /// + /// The analyze events data. + /// + 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 { 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 + } + + /// + /// The get elapsed time by task name. + /// + /// + /// The task name. + /// + /// + /// The . + /// + 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; + } + + /// + /// The get event data by task name. + /// + /// + /// The task name. + /// + /// + /// The . + /// + public IDictionary GetEventDataByTaskName(string taskName) + { + IDictionary properties = new Dictionary(); +#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 GetPayloadProperties(TraceEvent data) + { + var payLoadProperties = new Dictionary(); + + 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 + } +} diff --git a/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/PerformanceTestBase.cs b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/PerformanceTestBase.cs new file mode 100644 index 0000000000..87eda9ea0c --- /dev/null +++ b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/PerformanceTestBase.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.TestPlatform.TestUtilities.PerfInstrumentation +{ + using System.Collections; + using System.Collections.Generic; + + using Microsoft.TestPlatform.TestUtilities; + + /// + /// The performance test base. + /// + public class PerformanceTestBase : IntegrationTestBase + { + private PerfAnalyzer perfAnalyzer; + + /// + /// Initializes a new instance of the class. + /// + public PerformanceTestBase() + : base() + { + this.perfAnalyzer = new PerfAnalyzer(); + } + + /// + /// The run execution performance tests. + /// + /// + /// The test asset. + /// + /// + /// The test adapter path. + /// + /// + /// The run settings. + /// + public void RunExecutionPerformanceTests(string testAsset, string testAdapterPath, string runSettings) + { + // Start session and listen +#if NET46 + this.perfAnalyzer.EnableProvider(); +#endif + // Run Test + this.InvokeVsTestForExecution(testAsset, testAdapterPath, runSettings); + + // Stop Listening +#if NET46 + this.perfAnalyzer.DisableProvider(); +#endif + } + + /// + /// The run discovery performance tests. + /// + /// + /// The test asset. + /// + /// + /// The test adapter path. + /// + /// + /// The run settings. + /// + public void RunDiscoveryPerformanceTests(string testAsset, string testAdapterPath, string runSettings) + { + // Start session and listen +#if NET46 + this.perfAnalyzer.EnableProvider(); +#endif + // Run Test + this.InvokeVsTestForDiscovery(testAsset, testAdapterPath, runSettings); + + // Stop Listening +#if NET46 + this.perfAnalyzer.DisableProvider(); +#endif + } + + /// + /// The analyze perf data. + /// + public void AnalyzePerfData() + { + this.perfAnalyzer.AnalyzeEventsData(); + } + + /// + /// The get execution time. + /// + /// + /// The . + /// + public double GetExecutionTime() + { + return this.perfAnalyzer.GetElapsedTimeByTaskName(Constants.ExecutionTask); + } + + public double GetDiscoveryTime() + { + return this.perfAnalyzer.GetElapsedTimeByTaskName(Constants.DiscoveryTask); + } + + public double GetVsTestTime() + { + return this.perfAnalyzer.GetElapsedTimeByTaskName(Constants.VsTestConsoleTask); + } + + public double GetTestHostTime() + { + return this.perfAnalyzer.GetElapsedTimeByTaskName(Constants.TestHostTask); + } + + public double GetAdapterSearchTime() + { + return this.perfAnalyzer.GetElapsedTimeByTaskName(Constants.AdapterSearchTask); + } + + public IDictionary GetDiscoveryData() + { + return this.perfAnalyzer.GetEventDataByTaskName(Constants.AdapterDiscoveryTask); + } + + public IDictionary GetExecutionData() + { + return this.perfAnalyzer.GetEventDataByTaskName(Constants.AdapterExecutionTask); + } + + public double GetAdapterExecutionTime(string executorUri) + { + return this.perfAnalyzer.GetAdapterExecutionTime(executorUri); + } + } +} diff --git a/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/TestPlatformTask.cs b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/TestPlatformTask.cs new file mode 100644 index 0000000000..9507c7113a --- /dev/null +++ b/test/Microsoft.TestPlatform.TestUtilities/PerfInstrumentation/TestPlatformTask.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.TestPlatform.TestUtilities.PerfInstrumentation +{ + using System.Collections.Generic; + + /// + /// The test platform task. + /// + public class TestPlatformTask + { + public string TaskName { get; set; } + + public double EventStarted { get; set; } + + public double EventStopped { get; set; } + + public IDictionary PayLoadProperties { get; set; } + + public TestPlatformTask(string taskName, double eventStarted) + { + this.EventStarted = eventStarted; + this.TaskName = taskName; + this.PayLoadProperties = new Dictionary(); + } + } +} diff --git a/test/Microsoft.TestPlatform.TestUtilities/project.json b/test/Microsoft.TestPlatform.TestUtilities/project.json index 959f01fc62..78f3980d00 100644 --- a/test/Microsoft.TestPlatform.TestUtilities/project.json +++ b/test/Microsoft.TestPlatform.TestUtilities/project.json @@ -26,7 +26,10 @@ }, "net46": { "frameworkAssemblies": { - "System.Runtime": "" + "System.Runtime": "" + }, + "dependencies": { + "Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.41" } } } diff --git a/test/Performance/Microsoft.TestPlatform.PerformanceTests/Microsoft.TestPlatform.PerformanceTests.xproj b/test/Performance/Microsoft.TestPlatform.PerformanceTests/Microsoft.TestPlatform.PerformanceTests.xproj new file mode 100644 index 0000000000..cce084146a --- /dev/null +++ b/test/Performance/Microsoft.TestPlatform.PerformanceTests/Microsoft.TestPlatform.PerformanceTests.xproj @@ -0,0 +1,22 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 12d59ced-9916-4c3f-af82-12e019757fd2 + Microsoft.TestPlatform.PerformanceTests + .\obj + .\bin\ + v4.5.2 + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Performance/Microsoft.TestPlatform.PerformanceTests/PerformanceTests.cs b/test/Performance/Microsoft.TestPlatform.PerformanceTests/PerformanceTests.cs new file mode 100644 index 0000000000..f82848e218 --- /dev/null +++ b/test/Performance/Microsoft.TestPlatform.PerformanceTests/PerformanceTests.cs @@ -0,0 +1,101 @@ +// 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.PerformanceTests +{ + using Microsoft.TestPlatform.TestUtilities.PerfInstrumentation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + /// + /// The performance tests. + /// + [TestClass] + public class PerformanceTests : PerformanceTestBase + { + [TestMethod] + public void ExecutionPerformanceTest() + { + this.RunExecutionPerformanceTests(this.GetSampleTestAssembly(), this.GetTestAdapterPath(), string.Empty); + + this.ValidateSummaryStatus(1, 1, 1); + this.ValidatePassedTests("SampleUnitTestProject.UnitTest1.PassingTest"); + this.ValidateFailedTests("SampleUnitTestProject.UnitTest1.FailingTest"); + this.ValidateSkippedTests("SampleUnitTestProject.UnitTest1.SkippingTest"); + + this.AnalyzePerfData(); + var actualExecutionTime = this.GetExecutionTime(); + + // Sample Assert statement to verify the performance. 500 will be replaced by the actual threshold value. + Assert.IsTrue(actualExecutionTime < 500); + } + + [TestMethod] + public void DiscoveryPerformanceTest() + { + this.RunDiscoveryPerformanceTests(this.GetSampleTestAssembly(), this.GetTestAdapterPath(), string.Empty); + + this.ValidateDiscoveredTests( + "SampleUnitTestProject.UnitTest1.PassingTest", + "SampleUnitTestProject.UnitTest1.FailingTest", + "SampleUnitTestProject.UnitTest1.SkippingTest"); + + this.AnalyzePerfData(); + var actualDiscoveryTime = this.GetDiscoveryTime(); + + // Sample Assert statement to verify the performance. 500 will be replaced by the actual threshold value. + Assert.IsTrue(actualDiscoveryTime < 500); + } + + [TestMethod] + public void VsTestConsolePerformanceTest() + { + this.RunExecutionPerformanceTests(this.GetSampleTestAssembly(), this.GetTestAdapterPath(), string.Empty); + + this.ValidateSummaryStatus(1, 1, 1); + this.ValidatePassedTests("SampleUnitTestProject.UnitTest1.PassingTest"); + this.ValidateFailedTests("SampleUnitTestProject.UnitTest1.FailingTest"); + this.ValidateSkippedTests("SampleUnitTestProject.UnitTest1.SkippingTest"); + + this.AnalyzePerfData(); + var actualVsTestTime = this.GetVsTestTime(); + + // Sample Assert statement to verify the performance. 1500 will be replaced by the actual threshold value. + Assert.IsTrue(actualVsTestTime < 1500); + } + + [TestMethod] + public void TestHostPerformanceTest() + { + this.RunExecutionPerformanceTests(this.GetSampleTestAssembly(), this.GetTestAdapterPath(), string.Empty); + + this.ValidateSummaryStatus(1, 1, 1); + this.ValidatePassedTests("SampleUnitTestProject.UnitTest1.PassingTest"); + this.ValidateFailedTests("SampleUnitTestProject.UnitTest1.FailingTest"); + this.ValidateSkippedTests("SampleUnitTestProject.UnitTest1.SkippingTest"); + + this.AnalyzePerfData(); + var actualTestHostTime = this.GetTestHostTime(); + + // Sample Assert statement to verify the performance. 1000 will be replaced by the actual threshold value. + Assert.IsTrue(actualTestHostTime < 1000); + } + + [TestMethod] + public void MsTestV2AdapterPerformanceTest() + { + this.RunExecutionPerformanceTests(this.GetSampleTestAssembly(), this.GetTestAdapterPath(), string.Empty); + + this.ValidateSummaryStatus(1, 1, 1); + this.ValidatePassedTests("SampleUnitTestProject.UnitTest1.PassingTest"); + this.ValidateFailedTests("SampleUnitTestProject.UnitTest1.FailingTest"); + this.ValidateSkippedTests("SampleUnitTestProject.UnitTest1.SkippingTest"); + + this.AnalyzePerfData(); + + var actualAdapterTimeTaken = this.GetAdapterExecutionTime("executor://mstestadapter/v2"); + + // Sample Assert statement to verify the performance. 300 will be replaced by the actual threshold value. + Assert.IsTrue(actualAdapterTimeTaken < 300); + } + } +} \ No newline at end of file diff --git a/test/Performance/Microsoft.TestPlatform.PerformanceTests/Properties/AssemblyInfo.cs b/test/Performance/Microsoft.TestPlatform.PerformanceTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..fa7b252b08 --- /dev/null +++ b/test/Performance/Microsoft.TestPlatform.PerformanceTests/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.TestPlatform.PerformanceTests")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("12d59ced-9916-4c3f-af82-12e019757fd2")] diff --git a/test/Performance/Microsoft.TestPlatform.PerformanceTests/project.json b/test/Performance/Microsoft.TestPlatform.PerformanceTests/project.json new file mode 100644 index 0000000000..c8a85d4e31 --- /dev/null +++ b/test/Performance/Microsoft.TestPlatform.PerformanceTests/project.json @@ -0,0 +1,47 @@ +{ + "version": "15.0.0-*", + + "buildOptions": { + "delaySign": true, + "keyFile": "../../../scripts/key.snk", + "warningsAsErrors": true, + "additionalArguments": [ "/ruleset:../../../scripts/stylecop.test.ruleset", "/additionalFile:../../../scripts/stylecop.json" ] + }, + + "dependencies": { + "MSTest.TestFramework": "1.0.0-preview", + "MSTest.TestAdapter": { + "version": "1.0.3-preview", + "exclude": "compile" + }, + + "StyleCop.Analyzers": { + "version": "1.0.0", + "type": "build" + }, + + "Microsoft.TestPlatform.TestUtilities": "15.0.0-*" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ], + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" + }, + "dotnet-test-mstest": { + "version": "1.0.1-preview", + "exclude": "compile" + } + } + }, + "net46": { } + }, + + "testRunner": "mstest" +}