From 06a1ba4f08359bbffb1f546a45a09ae9835ed9bd Mon Sep 17 00:00:00 2001 From: BennieCopeland Date: Mon, 3 Apr 2017 12:24:45 +0200 Subject: [PATCH] XUnitFormatter fixes for issues #176, #177 and #178 (#181) * Closes #177 and #178 * refactor(formatters): pack `using()` blocks * test(formatters): add test case for issue #177 * test(formatters): XUnitFormatter output XML must start with BOM * chore(test): don't mix TestFixture classes in XUnitFormatter test * fix(formatters): XUnitFormatter throws when no options are set * Closes #176 --- .../NSpec/Domain/Formatters/XUnitFormatter.cs | 17 ++- .../NSpec.Tests/describe_XUnitFormatter.cs | 139 ++++++++++++++++++ 2 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 sln/test/NSpec.Tests/describe_XUnitFormatter.cs diff --git a/sln/src/NSpec/Domain/Formatters/XUnitFormatter.cs b/sln/src/NSpec/Domain/Formatters/XUnitFormatter.cs index 1fde9030..baf300a0 100644 --- a/sln/src/NSpec/Domain/Formatters/XUnitFormatter.cs +++ b/sln/src/NSpec/Domain/Formatters/XUnitFormatter.cs @@ -4,12 +4,16 @@ using System.IO; using System.Linq; using System.Text; -using System.Xml; namespace NSpec.Domain.Formatters { public class XUnitFormatter : IFormatter { + public XUnitFormatter() + { + Options = new Dictionary(); + } + public void Write(ContextCollection contexts) { StringBuilder sb = new StringBuilder(); @@ -25,18 +29,23 @@ public void Write(ContextCollection contexts) xml.WriteAttributeString("skip", contexts.Pendings().Count().ToString()); contexts.Do(c => this.BuildContext(xmlWrapper, c)); + xml.WriteEndElement(); + xml.Flush(); + var results = sb.ToString(); bool didWriteToFile = false; if (Options.ContainsKey("file")) { var filePath = Path.Combine(Directory.GetCurrentDirectory(), Options["file"]); - using (StreamWriter ostream = File.CreateText(filePath)) + using (var stream = new FileStream(filePath, FileMode.Create)) + using (var writer = new StreamWriter(stream, Encoding.Unicode)) { - ostream.WriteLine(results); + writer.WriteLine(results); Console.WriteLine("Test results published to: {0}".With(filePath)); } + didWriteToFile = true; } if (didWriteToFile && Options.ContainsKey("console")) @@ -113,4 +122,4 @@ private void ComposePartialName(Context context, StringBuilder contextName) contextName.Append(context.Name); } } -} \ No newline at end of file +} diff --git a/sln/test/NSpec.Tests/describe_XUnitFormatter.cs b/sln/test/NSpec.Tests/describe_XUnitFormatter.cs new file mode 100644 index 00000000..a32bac67 --- /dev/null +++ b/sln/test/NSpec.Tests/describe_XUnitFormatter.cs @@ -0,0 +1,139 @@ +using FluentAssertions; +using NSpec.Domain; +using NSpec.Domain.Formatters; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace NSpec.Tests +{ + public abstract class describe_XUnitFormatter_base + { + protected class xunit_formatter_sample_spec : nspec + { + void a_context_with_a_pending_example() + { + it["pending example"] = todo; + } + + void a_context_with_a_grandchild_example() + { + context["a context with an example"] = () => + { + it["is passing"] = () => Assert.That(true, Is.True); + }; + } + + void a_context_without_an_example() { } + } + } + + [TestFixture] + public class describe_XUnitFormatter : describe_XUnitFormatter_base + { + [SetUp] + public void Setup() + { + formatter = new XUnitFormatter(); + + string outDirPath = Path.Combine( + Path.GetTempPath(), + "NSpec.Tests", + nameof(describe_XUnitFormatter)); + + Directory.CreateDirectory(outDirPath); + + outFilePath = Path.Combine( + outDirPath, + Path.ChangeExtension(Path.GetRandomFileName(), "xml")); + + formatter.Options = new Dictionary() + { + { "file", outFilePath }, + }; + + var invocation = new RunnerInvocation( + dll: typeof(describe_XUnitFormatter).GetTypeInfo().Assembly.Location, + tags: typeof(xunit_formatter_sample_spec).Name, + formatter: formatter, + failFast: false); + + contexts = invocation.Run(); + } + + [TearDown] + public void TearDown() + { + if (File.Exists(outFilePath)) + { + File.Delete(outFilePath); + } + } + + [Test] + public void all_output_is_flushed_to_file() + { + string actual = File.ReadAllText(outFilePath); + + actual.Should().EndWith("\r\n"); + } + + [Test] + public void output_file_starts_with_utf16_bom() + { + var utf16Encoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: true); + + byte[] expected = utf16Encoding.GetPreamble(); + + byte[] actual = new byte[expected.Length]; + + using (var fstream = new FileStream(outFilePath, FileMode.Open)) + { + fstream.Read(actual, 0, actual.Length); + + actual.ShouldBeEquivalentTo(expected); + } + } + + XUnitFormatter formatter; + string outFilePath; + ContextCollection contexts; + } + + [TestFixture] + public class describe_XUnitFormatter_without_options : describe_XUnitFormatter_base + { + [SetUp] + public void Setup() + { + formatter = new XUnitFormatter(); + + Run(typeof(xunit_formatter_sample_spec)); + } + + [Test] + public void writing_does_not_throw() + { + formatter.Write(contextCollection); + } + + // TODO refactor this with WhenRunningSpecs.when_running_specs + + protected void Run(params Type[] types) + { + var tagsFilter = new Tags().Parse(""); + + var builder = new ContextBuilder(new SpecFinder(types), tagsFilter, new DefaultConventions()); + + contextCollection = builder.Contexts(); + + contextCollection.Build(); + } + + XUnitFormatter formatter; + ContextCollection contextCollection; + } +}