Posted on May 3, 2012
Last night I sat down to work on my most recent project (full details another time, I promise). What I needed was a custom NUnit test runner. It didn't need to use Roslyn necessarily, but a large part of the project already relies on it, and you gotta love being able to emit directly to a memory buffer. Admittedly it only implements a subset of the NUnit spec, and I do intend to enhance it, but I did it in only a few hours! Probably most of that time was investigating the spec and writing tests.
002 | using System.Collections.Generic; |
004 | using System.Reflection; |
005 | using Roslyn.Compilers.CSharp; |
007 | using Roslyn.Compilers; |
008 | using NUnit.Framework; |
010 | namespace com.griffinscs.Roslyn.NUnit |
012 | public interface ITestResult |
014 | object Fixture { get ; } |
015 | MethodInfo Method { get ; } |
016 | Exception Result { get ; } |
019 | public class TestRunner |
021 | private class TestResult : ITestResult |
024 | public object Fixture |
030 | public MethodInfo Method |
036 | public Exception Result |
043 | private Exception CaptureException(Action action) |
045 | try { action.Invoke(); } |
046 | catch (TargetInvocationException e) |
048 | return e.InnerException; |
053 | private IEnumerable<ITestResult> RunTest( object fixture, MethodInfo method) |
055 | return method.GetCustomAttributes( |
056 | typeof (TestAttribute), true ).Cast<TestAttribute>().Select( |
059 | return new TestResult() |
063 | Result = CaptureException(() => method.Invoke(fixture, null )) |
066 | method.GetCustomAttributes( typeof (TestCaseAttribute), true ).Cast<TestCaseAttribute>().Select( |
069 | return new TestResult() |
073 | Result = CaptureException(() => method.Invoke(fixture, attr.Arguments)) |
079 | private IEnumerable<ITestResult> TryRunFixture(Type type) |
081 | return type.GetCustomAttributes( |
082 | typeof (TestFixtureAttribute), true ).Cast<TestFixtureAttribute>().Select( |
083 | attr=>type.GetConstructor(attr.TypeArgs).Invoke(attr.Arguments)).SelectMany( |
084 | fixture=> type.GetMethods(BindingFlags.Instance | BindingFlags.Public).SelectMany( |
085 | method=>RunTest(fixture, method))); |
088 | public IEnumerable<ITestResult> Run(Compilation compilation) |
091 | using (var emitStream = new MemoryStream()) |
093 | var emitResult = compilation.Emit(emitStream); |
094 | if (emitResult.Diagnostics.Any(diag => diag.Info.Severity == DiagnosticSeverity.Error)) |
099 | try { assembly = Assembly.Load(emitStream.GetBuffer()); } |
100 | catch (BadImageFormatException) |
105 | return assembly.GetTypes().SelectMany(TryRunFixture); |