Added test adapter for Cosmos test kernels.

This commit is contained in:
José Pedro 2018-08-04 02:32:20 +01:00
parent 4d88868362
commit c75ec2e126
No known key found for this signature in database
GPG key ID: B8247B9301707B83
8 changed files with 397 additions and 0 deletions

View file

@ -1,5 +1,9 @@
<Project>
<PropertyGroup>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<PropertyGroup>
<RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>
<IL2CPURepoRoot>$(RepoRoot)..\IL2CPU\</IL2CPURepoRoot>

View file

@ -160,6 +160,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cosmos.Debug.HyperVServer",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cosmos.TestRunner.Full", "Tests\Cosmos.TestRunner.Full\Cosmos.TestRunner.Full.csproj", "{65EE0B97-D3F0-400D-B432-85FF5553C44E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cosmos.TestRunner.TestAdapter", "Tests\Cosmos.TestRunner.TestAdapter\Cosmos.TestRunner.TestAdapter.csproj", "{8574E797-7C48-4F12-B537-4F927BCBA93B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -609,6 +611,14 @@ Global
{65EE0B97-D3F0-400D-B432-85FF5553C44E}.Release|Any CPU.Build.0 = Release|Any CPU
{65EE0B97-D3F0-400D-B432-85FF5553C44E}.Release|x86.ActiveCfg = Release|Any CPU
{65EE0B97-D3F0-400D-B432-85FF5553C44E}.Release|x86.Build.0 = Release|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Debug|x86.ActiveCfg = Debug|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Debug|x86.Build.0 = Debug|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Release|Any CPU.Build.0 = Release|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Release|x86.ActiveCfg = Release|Any CPU
{8574E797-7C48-4F12-B537-4F927BCBA93B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -685,6 +695,7 @@ Global
{CCFD198D-4859-462B-9EF7-B305A8B4E6FC} = {0E67EFE8-5944-4F6C-8B47-C5E06D4C79F5}
{3421E19D-16C7-4593-9F6B-291ECB86A3EB} = {F7C6CA93-1D02-443C-9C8B-A1988DE0306B}
{65EE0B97-D3F0-400D-B432-85FF5553C44E} = {0E67EFE8-5944-4F6C-8B47-C5E06D4C79F5}
{8574E797-7C48-4F12-B537-4F927BCBA93B} = {0E67EFE8-5944-4F6C-8B47-C5E06D4C79F5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4418C803-277E-448F-A0A0-52788FA215AD}

View file

@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<PackageDescription>Test adapter for Cosmos kernels.</PackageDescription>
<DevelopmentDependency>True</DevelopmentDependency>
<NoWarn>NU5111</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="15.7.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Cosmos.TestRunner.Core\Cosmos.TestRunner.Core.csproj" />
</ItemGroup>
<PropertyGroup>
<IncludeBuildOutput>False</IncludeBuildOutput>
<!-- NuGet 4.8+ -->
<SuppressDependenciesWhenPacking>True</SuppressDependenciesWhenPacking>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackTestAdapter</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="PackTestAdapter" DependsOnTargets="Publish">
<ItemGroup>
<_GeneratedFiles Include="$(PublishDepsFilePath)" />
</ItemGroup>
<ItemGroup>
<TfmSpecificPackageFile Include="@(_GeneratedFiles)">
<PackagePath>tools\%(_GeneratedFiles.RecursiveDir)%(_GeneratedFiles.Filename)%(_GeneratedFiles.Extension)</PackagePath>
</TfmSpecificPackageFile>
<TfmSpecificPackageFile Include="@(ResolvedFileToPublish->'$([MSBuild]::NormalizeDirectory($(PublishDir)))%(RelativePath)')">
<PackagePath>tools\%(ResolvedFileToPublish.RelativePath)</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
</Target>
</Project>

View file

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
namespace Cosmos.TestRunner.TestAdapter
{
[FileExtension(".dll")]
[DefaultExecutorUri(CosmosTestExecutor.ExecutorUri)]
public sealed class CosmosTestDiscoverer : ITestDiscoverer
{
private static readonly Uri ExecutorUri = new Uri(CosmosTestExecutor.ExecutorUri);
public void DiscoverTests(
IEnumerable<string> sources,
IDiscoveryContext discoveryContext,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink)
{
var testCases = DiscoverTests(sources, discoveryContext, logger);
foreach (var testCase in testCases)
{
discoverySink.SendTestCase(testCase);
}
}
internal IEnumerable<TestCase> DiscoverTests(
IEnumerable<string> sources,
IDiscoveryContext discoveryContext,
IMessageLogger logger)
{
var testCases = new List<TestCase>();
foreach (var source in sources)
{
try
{
using (var stream = File.OpenRead(source))
{
using (var peReader = new PEReader(stream))
{
if (!peReader.HasMetadata)
{
continue;
}
var metadataReader = peReader.GetMetadataReader();
if (!metadataReader.IsAssembly)
{
continue;
}
// todo: check exported types?
foreach (var typeHandle in metadataReader.TypeDefinitions)
{
var type = metadataReader.GetTypeDefinition(typeHandle);
if ((type.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public)
{
var baseTypeHandle = type.BaseType;
// has to be type reference, as it's not generic
// and it's not declared in the same module
if (baseTypeHandle.Kind == HandleKind.TypeReference)
{
var baseType = metadataReader.GetTypeReference((TypeReferenceHandle)baseTypeHandle);
var baseTypeNamespace = metadataReader.GetString(baseType.Namespace);
var baseTypeName = metadataReader.GetString(baseType.Name);
if (String.Equals(baseTypeNamespace, "Cosmos.System", StringComparison.Ordinal)
&& String.Equals(baseTypeName, "Kernel", StringComparison.Ordinal)
&& baseType.ResolutionScope.Kind == HandleKind.AssemblyReference)
{
var baseTypeAssemblyReference = metadataReader.GetAssemblyReference(
(AssemblyReferenceHandle)baseType.ResolutionScope);
var baseTypeAssemblyName = metadataReader.GetString(baseTypeAssemblyReference.Name);
if (String.Equals(baseTypeAssemblyName, "Cosmos.System2", StringComparison.Ordinal))
{
var typeNamespace = metadataReader.GetString(type.Namespace);
var typeName = metadataReader.GetString(type.Name);
var assemblyDefinition = metadataReader.GetAssemblyDefinition();
var assemblyName = metadataReader.GetString(assemblyDefinition.Name);
var fullyQualifiedName = $"{typeName}, {assemblyName}";
if (!String.IsNullOrEmpty(typeNamespace))
{
fullyQualifiedName = $"{typeNamespace}.{fullyQualifiedName}";
}
var testCase = new TestCase(fullyQualifiedName, ExecutorUri, source);
testCases.Add(testCase);
break;
}
}
}
}
}
}
}
}
catch
{
}
}
return testCases;
}
}
}

View file

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Cosmos.TestRunner.Core;
namespace Cosmos.TestRunner.TestAdapter
{
[ExtensionUri(ExecutorUri)]
public sealed class CosmosTestExecutor : ITestExecutor
{
public const string ExecutorUri = "executor://CosmosTestExecutor";
private CancellationTokenSource _cancellationTokenSource;
public void RunTests(
IEnumerable<TestCase> tests,
IRunContext runContext,
IFrameworkHandle frameworkHandle)
{
_cancellationTokenSource = new CancellationTokenSource();
foreach (var test in tests)
{
var configuration = new EngineConfiguration(new string[] { test.Source }, runContext);
var testEngine = new Engine(configuration);
var outputHandler = new TestAdapterOutputHandler(frameworkHandle);
testEngine.SetOutputHandler(outputHandler);
var testResult = new TestResult(test);
frameworkHandle.RecordStart(test);
var kernelTestResult = testEngine.Execute(_cancellationTokenSource.Token).KernelTestResults[0];
testResult.Outcome = kernelTestResult.Result ? TestOutcome.Passed : TestOutcome.Failed;
var messages = new Collection<TestResultMessage>();
foreach (var message in outputHandler.Messages)
{
messages.Add(new TestResultMessage(String.Empty, message));
}
frameworkHandle.RecordEnd(test, testResult.Outcome);
frameworkHandle.RecordResult(testResult);
}
}
public void RunTests(
IEnumerable<string> sources,
IRunContext runContext,
IFrameworkHandle frameworkHandle)
{
var discoverer = new CosmosTestDiscoverer();
var tests = discoverer.DiscoverTests(sources, runContext, null);
RunTests(tests, runContext, frameworkHandle);
}
public void Cancel() => _cancellationTokenSource.Cancel();
}
}

View file

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Cosmos.Build.Common;
using Cosmos.TestRunner.Core;
namespace Cosmos.TestRunner.TestAdapter
{
internal sealed class EngineConfiguration : IEngineConfiguration
{
public int AllowedSecondsInKernel { get; }
public IEnumerable<RunTargetEnum> RunTargets { get; }
public bool RunWithGDB { get; }
public bool StartBochsDebugGUI { get; }
public bool DebugIL2CPU { get; }
public string KernelPkg { get; }
public TraceAssemblies TraceAssembliesLevel { get; }
public bool EnableStackCorruptionChecks { get; }
public StackCorruptionDetectionLevel StackCorruptionDetectionLevel { get; }
public IEnumerable<string> KernelAssembliesToRun { get; }
private readonly IRunContext _context;
public EngineConfiguration(
IEnumerable<string> testKernels,
IRunContext context)
{
KernelAssembliesToRun = testKernels;
_context = context;
var settingsXml = _context.RunSettings.SettingsXml;
if (string.IsNullOrEmpty(settingsXml))
{
settingsXml = "<RunSettings />";
}
var doc = XDocument.Parse(settingsXml);
var runConfiguration = doc.Element("RunConfiguration");
AllowedSecondsInKernel = GetIntValue(runConfiguration, nameof(AllowedSecondsInKernel), 1200);
var runTargetsString = GetStringValue(runConfiguration, nameof(RunTargets), "Bochs");
var runTargets = new List<RunTargetEnum>();
foreach (var runTargetName in runTargetsString.Split(';'))
{
if (Enum.TryParse<RunTargetEnum>(runTargetName, out var runTarget))
{
runTargets.Add(runTarget);
}
}
RunTargets = runTargets;
RunWithGDB = GetBoolValue(runConfiguration, nameof(RunWithGDB), false);
StartBochsDebugGUI = GetBoolValue(runConfiguration, nameof(StartBochsDebugGUI), false);
DebugIL2CPU = GetBoolValue(runConfiguration, nameof(DebugIL2CPU), false);
KernelPkg = GetStringValue(runConfiguration, nameof(KernelPkg), String.Empty);
TraceAssembliesLevel = GetEnumValue(runConfiguration, nameof(KernelPkg), TraceAssemblies.User);
EnableStackCorruptionChecks = GetBoolValue(runConfiguration, nameof(EnableStackCorruptionChecks), true);
StackCorruptionDetectionLevel = GetEnumValue(
runConfiguration, nameof(KernelPkg), StackCorruptionDetectionLevel.AllInstructions);
}
private static bool GetBoolValue(XElement element, XName name, bool defaultValue)
{
var value = GetStringValue(element, name, null);
if (Boolean.TryParse(value, out var result))
{
return result;
}
return defaultValue;
}
private static T GetEnumValue<T>(XElement element, XName name, T defaultValue) where T : struct, Enum
{
var value = GetStringValue(element, name, null);
if (Enum.TryParse<T>(value, true, out var result))
{
return result;
}
return defaultValue;
}
private static int GetIntValue(XElement element, XName name, int defaultValue)
{
var value = GetStringValue(element, name, null);
if (Int32.TryParse(value, out var result))
{
return result;
}
return defaultValue;
}
private static string GetStringValue(XElement element, XName name, string defaultValue)
{
var childElement = element?.Element(name);
if (childElement != null)
{
return childElement.Value;
}
return defaultValue;
}
}
}

View file

@ -0,0 +1,4 @@
To debug vstest/test adapter:
set VSTEST_HOST_DEBUG=1
dotnet test

View file

@ -0,0 +1,27 @@
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Cosmos.TestRunner.Core;
namespace Cosmos.TestRunner.TestAdapter
{
internal class TestAdapterOutputHandler : OutputHandlerFullTextBase
{
public IReadOnlyList<string> Messages => _messages;
private readonly IFrameworkHandle _frameworkHandle;
private readonly List<string> _messages = new List<string>();
public TestAdapterOutputHandler(IFrameworkHandle frameworkHandle)
{
_frameworkHandle = frameworkHandle;
}
protected override void Log(string message)
{
_frameworkHandle.SendMessage(TestMessageLevel.Informational, message);
_messages.Add(message);
}
}
}