private static IEnumerable <NamedTest> GetSelfVerifyingFacts <TTargetCompiler, TLanguageConversion>(string sourceFileText, List <NamedTest> runnableTestsInSource) where TTargetCompiler : ICompiler, new() where TLanguageConversion : ILanguageConversion, new() { // Lazy to avoid confusing test runner on error, but also avoid calculating multiple times var conversionResultAsync = new AsyncLazy <ConversionResult>(() => { var xUnitReferences = DefaultReferences.With(typeof(FactAttribute).Assembly, typeof(Assert).Assembly); return(ProjectConversion.ConvertTextAsync <TLanguageConversion>(sourceFileText, new TextConversionOptions(xUnitReferences))); }); var runnableTestsInTarget = new AsyncLazy <Dictionary <string, NamedTest> >(async() => GetConvertedNamedFacts <TTargetCompiler>(runnableTestsInSource, await conversionResultAsync.GetValueAsync())); return(runnableTestsInSource.Select(sourceFact => new NamedTest(sourceFact.Name, async() => { try { await sourceFact.Execute(); } catch (TargetInvocationException ex) { throw new XunitException( $"Source test failed, ensure the source is correct for \"{sourceFact.Name}\": {(ex.InnerException ?? ex)}"); } try { var test = await runnableTestsInTarget.GetValueAsync(); await test[sourceFact.Name].Execute(); } catch (TargetInvocationException ex) { var conversionResult = await conversionResultAsync.GetValueAsync(); throw new XunitException( $"Converted test failed, the conversion is incorrect for \"{sourceFact.Name}\": {(ex.InnerException ?? ex)}\r\nConverted Code: {conversionResult.ConvertedCode ?? conversionResult.GetExceptionsAsString()}"); } }) )); }
/// <summary> /// Compiles the given string of source code into an IL byte array. /// </summary> /// <remarks>The transitive closure of the references for <paramref name="requiredAssemblies"/> are added.</remarks> public static Assembly AssemblyFromCode(this ICompiler compiler, SyntaxTree syntaxTree, params Assembly[] requiredAssemblies) { var allReferences = DefaultReferences.With(requiredAssemblies); var compilation = compiler.CreateCompilationFromTree(syntaxTree, allReferences); using (var dllStream = new MemoryStream()) using (var pdbStream = new MemoryStream()) { var result = compilation.Emit(dllStream, pdbStream); if (!result.Success) { string codeLines = string.Join("\r\n", Utils.HomogenizeEol(syntaxTree.ToString()) .Split(new[] { "\r\n" }, StringSplitOptions.None) .Select((l, i) => $"{i+1:000}: {l}")); throw new CompilationException( $"{compiler.GetType().Name} error:\r\n{string.Join("\r\n", result.Diagnostics)}\r\n\r\nSource code:\r\n{codeLines}" ); } dllStream.Seek(0, SeekOrigin.Begin); pdbStream.Seek(0, SeekOrigin.Begin); return(Assembly.Load(dllStream.ToArray(), pdbStream.ToArray())); } }