From 5a81a55f077bbba49dd5191066538696b6e2c888 Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Fri, 15 May 2020 09:36:06 -0400 Subject: [PATCH 1/5] Feat: Support logging system-out and error-out, including verbose formatting option. --- README.md | 2 +- ...gitlab-test-popup-with-verbose-failure.png | Bin 18015 -> 54089 bytes docs/gitlab-recommendation.md | 2 +- .../JUnitXmlTestLogger.cs | 72 ++++++++++++++---- .../JUnitTestLoggerAcceptanceTests.cs | 15 +++- ...tTestLoggerFormatOptionsAcceptanceTests.cs | 51 +++++++++++-- .../UnitTest1.cs | 4 + 7 files changed, 120 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 4a251ca..563abc6 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ This option alters the testcase name attribute. By default, this contains only t #### FailureBodyFormat -When set to default, the body will contain only the exception which is captured by vstest. Verbose will prepend the body with 'Expected X, Actual Y' similar to how it is displayed in the standard test output. 'Expected X, Actual Y' are normally only contained in the failure message. See [here](/docs/gitlab-recommendation.md) for an example of why this might be useful. +When set to default, the body will contain only the exception which is captured by vstest. Verbose will prepend the body with 'Expected X, Actual Y' similar to how it is displayed in the standard test output. 'Expected X, Actual Y' are normally only contained in the failure message. Additionally, Verbose will include standard output from the test in the failure message. See [here](/docs/gitlab-recommendation.md) for an example of why this might be useful. ##### Allowed Values diff --git a/docs/assets/gitlab-test-popup-with-verbose-failure.png b/docs/assets/gitlab-test-popup-with-verbose-failure.png index eb9d722e840d00b30b85b6948c619f0e883d9df2..096e34e1ae888c3d639e844ecd97c17b2ba81d78 100644 GIT binary patch literal 54089 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 literal 18015 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 Kw&4C#zyAV!`9^sF diff --git a/docs/gitlab-recommendation.md b/docs/gitlab-recommendation.md index f3224cb..80bbdd7 100644 --- a/docs/gitlab-recommendation.md +++ b/docs/gitlab-recommendation.md @@ -42,4 +42,4 @@ Test: ## Notes -Screen shots and behavior are current as of GitLab Community Edition 11.11.8. +Screen shots and behavior are current as of GitLab Enterprise Edition 13.0.0-pre. diff --git a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs index e8158c7..328e4b0 100644 --- a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs +++ b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs @@ -1,5 +1,5 @@ -// Copyright (c) Spekt Contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// Copyright (c) Spekt Contributors. All rights reserved. Licensed under the MIT license. See +// LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger { @@ -9,7 +9,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger using System.IO; using System.Linq; using System.Text; - using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -32,6 +31,7 @@ public class JUnitXmlTestLogger : ITestLoggerWithParameters // Dicionary keys for command line arguments. public const string LogFilePathKey = "LogFilePath"; + public const string LogFileNameKey = "LogFileName"; public const string ResultDirectoryKey = "TestRunDirectory"; public const string MethodFormatKey = "MethodFormat"; @@ -43,11 +43,22 @@ public class JUnitXmlTestLogger : ITestLoggerWithParameters // Tokens to allow user to manipulate output file or directory names. private const string AssemblyToken = "{assembly}"; + private const string FrameworkToken = "{framework}"; private readonly object resultsGuard = new object(); private string outputFilePath; + /// + /// Recieves information messages, along with StdOut from test methods. + /// + private StringBuilder stdOut = new StringBuilder(); + + /// + /// Recieves both warning and error messages. + /// + private StringBuilder stdErr = new StringBuilder(); + private List results; private DateTime utcStartTime; @@ -130,7 +141,9 @@ public static IEnumerable GroupTestSuites(IEnumerable suit /// Initialized called by dotnet test. /// /// Test logger event. - /// A single string is assumed to be the test result directory argument. + /// + /// A single string is assumed to be the test result directory argument. + /// public void Initialize(TestLoggerEvents events, string testResultsDirPath) { if (events == null) @@ -151,7 +164,9 @@ public void Initialize(TestLoggerEvents events, string testResultsDirPath) /// Initialized called by dotnet test. /// /// Test logger event. - /// Dictionary of key value pairs provided by the user, semicolon delimited (i.e. 'key1=val1;key2=val2'). + /// + /// Dictionary of key value pairs provided by the user, semicolon delimited (i.e. 'key1=val1;key2=val2'). + /// public void Initialize(TestLoggerEvents events, Dictionary parameters) { if (events == null) @@ -241,10 +256,19 @@ public void Initialize(TestLoggerEvents events, Dictionary param } /// - /// Called when a test message is received. + /// Called when a test message is received. These messages are coming from the test + /// framework, and don't contain standard output produced by test code. /// internal void TestMessageHandler(object sender, TestRunMessageEventArgs e) { + if (e.Level == TestMessageLevel.Informational) + { + this.stdOut.AppendLine(e.Message); + } + else + { + this.stdErr.AppendLine(e.Message); + } } /// @@ -277,8 +301,17 @@ internal void TestResultHandler(object sender, TestResultEventArgs e) { TestResult result = e.Result; - var parsedName = TestCaseNameParser.Parse(result.TestCase.FullyQualifiedName); + if (e.Result.Messages.Count > 0) + { + this.stdOut.AppendLine(); + this.stdOut.AppendLine(result.TestCase.FullyQualifiedName); + this.stdOut.AppendLine( + string.Join( + Environment.NewLine, + e.Result.Messages.Select(x => x.Text))); + } + var parsedName = TestCaseNameParser.Parse(result.TestCase.FullyQualifiedName); lock (this.resultsGuard) { this.results.Add(new TestResultInfo( @@ -428,13 +461,14 @@ private XElement CreateTestSuiteElement(List results) var testCaseElements = results.Select(a => this.CreateTestCaseElement(a)); // Adding required properties, system-out, and system-err elements in the correct - // positions as required by the xsd. + // positions as required by the xsd. In system-out collapse consequtive newlines to a + // single newline. var element = new XElement( "testsuite", new XElement("properties"), testCaseElements, - new XElement("system-out", "Junit Logger does not log standard output"), - new XElement("system-err", "Junit Logger does not log error output")); + new XElement("system-out", this.stdOut.ToString().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine)), + new XElement("system-err", this.stdErr.ToString())); element.SetAttributeValue("name", Path.GetFileName(results.First().AssemblyPath)); @@ -472,9 +506,8 @@ private XElement CreateTestCaseElement(TestResultInfo result) testcaseElement.SetAttributeValue("name", result.Name); } - // Ensure time value is never zero because gitlab treats 0 like its null. - // 0.1 micro seconds should be low enough it won't interfere with anyone - // monitoring test duration. + // Ensure time value is never zero because gitlab treats 0 like its null. 0.1 micro + // seconds should be low enough it won't interfere with anyone monitoring test duration. testcaseElement.SetAttributeValue( "time", Math.Max(0.0000001f, result.Duration.TotalSeconds).ToString("0.0000000")); @@ -487,12 +520,23 @@ private XElement CreateTestCaseElement(TestResultInfo result) { failureBodySB.AppendLine(result.ErrorMessage); - // Stack trace included to mimic the normal test output + // Stack trace label included to mimic the normal test output failureBodySB.AppendLine("Stack Trace:"); } failureBodySB.AppendLine(result.ErrorStackTrace); + if (this.FailureBodyFormatOption == FailureBodyFormat.Verbose && + result.Messages.Count > 0) + { + failureBodySB.AppendLine("Standard Output:"); + + failureBodySB.AppendLine( + string.Join( + Environment.NewLine, + result.Messages.Select(x => x.Text))); + } + var failureElement = new XElement("failure", failureBodySB.ToString().Trim()); failureElement.SetAttributeValue("type", "failure"); // TODO are there failure types? diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs index 58df0f8..cb1c388 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs @@ -1,5 +1,5 @@ -// Copyright (c) Spekt Contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// Copyright (c) Spekt Contributors. All rights reserved. Licensed under the MIT license. See +// LICENSE file in the project root for full license information. namespace JUnit.Xml.TestLogger.AcceptanceTests { @@ -95,6 +95,17 @@ public void TestResultFileShouldContainTestCases() Assert.AreEqual(6, skips.Count()); } + [TestMethod] + public void TestResultFileShouldContainStandardOut() + { + var node = this.resultsXml.XPathSelectElement("/testsuites/testsuite/system-out"); + + Assert.IsTrue(node.Value.Contains("{2010CAE3-7BC0-4841-A5A3-7D5F947BB9FB}")); + Assert.IsTrue(node.Value.Contains("{998AC9EC-7429-42CD-AD55-72037E7AF3D8}")); + Assert.IsTrue(node.Value.Contains("{EEEE1DA6-6296-4486-BDA5-A50A19672F0F}")); + Assert.IsTrue(node.Value.Contains("{C33FF4B5-75E1-4882-B968-DF9608BFE7C2}")); + } + [TestMethod] public void LoggedXmlValidatesAgainstXsdSchema() { diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs index bdc9150..e41da20 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs @@ -1,5 +1,5 @@ -// Copyright (c) Spekt Contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// Copyright (c) Spekt Contributors. All rights reserved. Licensed under the MIT license. See +// LICENSE file in the project root for full license information. namespace JUnit.Xml.TestLogger.AcceptanceTests { @@ -15,8 +15,9 @@ namespace JUnit.Xml.TestLogger.AcceptanceTests /// Acceptance tests evaluate the most recent output of the build.ps1 script, NOT the most /// recent build performed by visual studio or dotnet.build /// - /// These acceptance tests look at the specific places output is expected to change because of the format option specified. - /// Accordingly, these tests cannot protect against other changes occurring due to the formatting option. + /// These acceptance tests look at the specific places output is expected to change because of + /// the format option specified. Accordingly, these tests cannot protect against other changes + /// occurring due to the formatting option. /// [TestClass] public class JUnitTestLoggerFormatOptionsAcceptanceTests @@ -35,7 +36,8 @@ public void FailureBodyFormat_Default_ShouldntStartWithMessage() foreach (var failure in failures) { - // Strip new line and carrige return. These may be inconsistent depending on environment settings + // Strip new line and carrige return. These may be inconsistent depending on + // environment settings var message = failure.Attribute("message").Value.Replace("\r", string.Empty).Replace("\n", string.Empty); var body = failure.Value.Replace("\r", string.Empty).Replace("\n", string.Empty); @@ -43,6 +45,22 @@ public void FailureBodyFormat_Default_ShouldntStartWithMessage() } } + [TestMethod] + public void FailureBodyFormat_Verbose_ShouldNotContainConsoleOut() + { + DotnetTestFixture.Execute("failure-verbose-test-results.xml;FailureBodyFormat=Default"); + string resultsFile = Path.Combine(DotnetTestFixture.RootDirectory, "failure-verbose-test-results.xml"); + XDocument resultsXml = XDocument.Load(resultsFile); + + var failures = resultsXml.XPathSelectElements("/testsuites/testsuite") + .Descendants() + .Where(x => x.Name.LocalName == "failure") + .ToList(); + + Assert.AreEqual(0, failures.Count(x => x.Value.Contains("{EEEE1DA6-6296-4486-BDA5-A50A19672F0F}"))); + Assert.AreEqual(0, failures.Count(x => x.Value.Contains("{C33FF4B5-75E1-4882-B968-DF9608BFE7C2}"))); + } + [TestMethod] public void FailureBodyFormat_Verbose_ShouldStartWithMessage() { @@ -57,7 +75,8 @@ public void FailureBodyFormat_Verbose_ShouldStartWithMessage() foreach (var failure in failures) { - // Strip new line and carrige return. These may be inconsistent depending on environment settings + // Strip new line and carrige return. These may be inconsistent depending on + // environment settings var message = failure.Attribute("message").Value.Replace("\r", string.Empty).Replace("\n", string.Empty); var body = failure.Value.Replace("\r", string.Empty).Replace("\n", string.Empty); @@ -65,6 +84,22 @@ public void FailureBodyFormat_Verbose_ShouldStartWithMessage() } } + [TestMethod] + public void FailureBodyFormat_Verbose_ShouldContainConsoleOut() + { + DotnetTestFixture.Execute("failure-verbose-test-results.xml;FailureBodyFormat=Verbose"); + string resultsFile = Path.Combine(DotnetTestFixture.RootDirectory, "failure-verbose-test-results.xml"); + XDocument resultsXml = XDocument.Load(resultsFile); + + var failures = resultsXml.XPathSelectElements("/testsuites/testsuite") + .Descendants() + .Where(x => x.Name.LocalName == "failure") + .ToList(); + + Assert.AreEqual(1, failures.Count(x => x.Value.Contains("{EEEE1DA6-6296-4486-BDA5-A50A19672F0F}"))); + Assert.AreEqual(1, failures.Count(x => x.Value.Contains("{C33FF4B5-75E1-4882-B968-DF9608BFE7C2}"))); + } + [TestMethod] public void MethodFormat_Default_ShouldBeOnlyTheMethod() { @@ -102,8 +137,8 @@ public void MethodFormat_Class_ShouldIncludeClass() { var parsedName = TestCaseNameParser.Parse(testcase.Attribute("name").Value); - // If the name is parsable into two pieces, then we have a two piece name - // and consider that to be a passing result. + // If the name is parsable into two pieces, then we have a two piece name and + // consider that to be a passing result. Assert.AreNotEqual(parsedName.TypeName, TestCaseNameParser.TestCaseParserUnknownType); } } diff --git a/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs b/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs index 2e038e2..f489a2e 100644 --- a/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs +++ b/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs @@ -11,12 +11,16 @@ public class UnitTest1 [Description("Passing test description")] public async Task PassTest11() { + Console.WriteLine("{2010CAE3-7BC0-4841-A5A3-7D5F947BB9FB}"); + Console.WriteLine("{998AC9EC-7429-42CD-AD55-72037E7AF3D8}"); await Task.Delay(TimeSpan.FromMilliseconds(400)); } [Test] public void FailTest11() { + Console.WriteLine("{EEEE1DA6-6296-4486-BDA5-A50A19672F0F}"); + Console.WriteLine("{C33FF4B5-75E1-4882-B968-DF9608BFE7C2}"); Assert.False(true); } From c0270dc70130e17b9c39192d4732ebaf72948006 Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Mon, 18 May 2020 21:55:12 -0400 Subject: [PATCH 2/5] Feat: Indent output messages for clarity. --- .../JUnitXmlTestLogger.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs index 328e4b0..a7ba417 100644 --- a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs +++ b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs @@ -305,10 +305,7 @@ internal void TestResultHandler(object sender, TestResultEventArgs e) { this.stdOut.AppendLine(); this.stdOut.AppendLine(result.TestCase.FullyQualifiedName); - this.stdOut.AppendLine( - string.Join( - Environment.NewLine, - e.Result.Messages.Select(x => x.Text))); + this.stdOut.AppendLine(Indent(result.Messages)); } var parsedName = TestCaseNameParser.Parse(result.TestCase.FullyQualifiedName); @@ -428,6 +425,20 @@ private static TestSuite AggregateTestSuites( }; } + /// + /// Produces a consistently indented output, taking into account that incoming messages + /// often have new lines within a message. + /// + private static string Indent(IReadOnlyCollection messages) + { + var indent = " "; + return + indent + + string.Join( + Environment.NewLine + indent, + messages.Select(x => x.Text.Replace(Environment.NewLine, Environment.NewLine + indent))); + } + private void InitializeImpl(TestLoggerEvents events, string outputPath) { events.TestRunMessage += this.TestMessageHandler; @@ -467,7 +478,7 @@ private XElement CreateTestSuiteElement(List results) "testsuite", new XElement("properties"), testCaseElements, - new XElement("system-out", this.stdOut.ToString().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine)), + new XElement("system-out", this.stdOut.ToString()), new XElement("system-err", this.stdErr.ToString())); element.SetAttributeValue("name", Path.GetFileName(results.First().AssemblyPath)); @@ -531,10 +542,7 @@ private XElement CreateTestCaseElement(TestResultInfo result) { failureBodySB.AppendLine("Standard Output:"); - failureBodySB.AppendLine( - string.Join( - Environment.NewLine, - result.Messages.Select(x => x.Text))); + failureBodySB.AppendLine(Indent(result.Messages)); } var failureElement = new XElement("failure", failureBodySB.ToString().Trim()); From eb1306bc829fea8f04b45df4c6fc753c5820f935 Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Fri, 22 May 2020 17:54:15 -0400 Subject: [PATCH 3/5] Fix indenting issue. Cleanup based on code review comments. --- src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs | 17 ++++++++++++----- .../JUnitTestLoggerAcceptanceTests.cs | 13 +++++++++++-- ...nitTestLoggerFormatOptionsAcceptanceTests.cs | 4 ++-- .../UnitTest1.cs | 3 +++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs index a7ba417..488b8a2 100644 --- a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs +++ b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs @@ -1,5 +1,5 @@ -// Copyright (c) Spekt Contributors. All rights reserved. Licensed under the MIT license. See -// LICENSE file in the project root for full license information. +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.TestPlatform.Extension.JUnit.Xml.TestLogger { @@ -431,12 +431,19 @@ private static TestSuite AggregateTestSuites( /// private static string Indent(IReadOnlyCollection messages) { - var indent = " "; + var indent = " "; + + // Splitting on any line feed or carrage return because a message may include new lines + // that are inconsistent with the Environment.NewLine. We then remove all blank lines so + // it shouldn't cause an issue that this generates extra line breaks. return indent + string.Join( - Environment.NewLine + indent, - messages.Select(x => x.Text.Replace(Environment.NewLine, Environment.NewLine + indent))); + $"{Environment.NewLine}{indent}", + messages.SelectMany(m => + m.Text.Split(new string[] { "\r", "\n" }, StringSplitOptions.None) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => x.Trim()))); } private void InitializeImpl(TestLoggerEvents events, string outputPath) diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs index cb1c388..4d7af25 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerAcceptanceTests.cs @@ -1,5 +1,5 @@ -// Copyright (c) Spekt Contributors. All rights reserved. Licensed under the MIT license. See -// LICENSE file in the project root for full license information. +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace JUnit.Xml.TestLogger.AcceptanceTests { @@ -106,6 +106,15 @@ public void TestResultFileShouldContainStandardOut() Assert.IsTrue(node.Value.Contains("{C33FF4B5-75E1-4882-B968-DF9608BFE7C2}")); } + [TestMethod] + public void TestResultFileShouldContainErrordOut() + { + var node = this.resultsXml.XPathSelectElement("/testsuites/testsuite/system-err"); + + Assert.IsTrue(node.Value.Contains("{D46DFA10-EEDD-49E5-804D-FE43051331A7}")); + Assert.IsTrue(node.Value.Contains("{33F5FD22-6F40-499D-98E4-481D87FAEAA1}")); + } + [TestMethod] public void LoggedXmlValidatesAgainstXsdSchema() { diff --git a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs index e41da20..afb041e 100644 --- a/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs +++ b/test/JUnit.Xml.TestLogger.AcceptanceTests/JUnitTestLoggerFormatOptionsAcceptanceTests.cs @@ -1,5 +1,5 @@ -// Copyright (c) Spekt Contributors. All rights reserved. Licensed under the MIT license. See -// LICENSE file in the project root for full license information. +// Copyright (c) Spekt Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace JUnit.Xml.TestLogger.AcceptanceTests { diff --git a/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs b/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs index f489a2e..c4813c4 100644 --- a/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs +++ b/test/assets/JUnit.Xml.TestLogger.NetCore.Tests/UnitTest1.cs @@ -21,6 +21,9 @@ public void FailTest11() { Console.WriteLine("{EEEE1DA6-6296-4486-BDA5-A50A19672F0F}"); Console.WriteLine("{C33FF4B5-75E1-4882-B968-DF9608BFE7C2}"); + Console.Error.WriteLine("{D46DFA10-EEDD-49E5-804D-FE43051331A7}"); + Console.Error.WriteLine("{33F5FD22-6F40-499D-98E4-481D87FAEAA1}"); + Assert.False(true); } From 1399074c1adebf5ae82eecfd0ece52fcea4ac3d4 Mon Sep 17 00:00:00 2001 From: Michael Conrad Date: Fri, 22 May 2020 17:57:18 -0400 Subject: [PATCH 4/5] Fix unwanted codemaid change. --- src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs index 488b8a2..e429eeb 100644 --- a/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs +++ b/src/JUnit.Xml.TestLogger/JUnitXmlTestLogger.cs @@ -31,7 +31,6 @@ public class JUnitXmlTestLogger : ITestLoggerWithParameters // Dicionary keys for command line arguments. public const string LogFilePathKey = "LogFilePath"; - public const string LogFileNameKey = "LogFileName"; public const string ResultDirectoryKey = "TestRunDirectory"; public const string MethodFormatKey = "MethodFormat"; @@ -43,7 +42,6 @@ public class JUnitXmlTestLogger : ITestLoggerWithParameters // Tokens to allow user to manipulate output file or directory names. private const string AssemblyToken = "{assembly}"; - private const string FrameworkToken = "{framework}"; private readonly object resultsGuard = new object(); From 6b522e206d867c3fc1feaaab491c34c53bdce2be Mon Sep 17 00:00:00 2001 From: Michael J Conrad <32316111+Siphonophora@users.noreply.github.com> Date: Wed, 16 Sep 2020 08:55:38 -0400 Subject: [PATCH 5/5] Doc: Update example test result with stdout and stderr --- docs/assets/TestResults.xml | 102 +++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/docs/assets/TestResults.xml b/docs/assets/TestResults.xml index 79cb8b2..638adb0 100644 --- a/docs/assets/TestResults.xml +++ b/docs/assets/TestResults.xml @@ -1,102 +1,130 @@  - + - - + + --TearDown - at JUnit.Xml.TestLogger.NetFull.Tests.FailingTearDown.TearDown() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 175 + at JUnit.Xml.TestLogger.NetFull.Tests.FailingTearDown.TearDown() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 182 - - at JUnit.Xml.TestLogger.NetFull.Tests.FailingTestSetup.SetUp() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 160 + + at JUnit.Xml.TestLogger.NetFull.Tests.FailingTestSetup.SetUp() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 167 - + - - at JUnit.Xml.TestLogger.NetFull.Tests.ParametrizedTestCases.TestData(Int32 x, String s) in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 225 + + at JUnit.Xml.TestLogger.NetFull.Tests.ParametrizedTestCases.TestData(Int32 x, String s) in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 232 - at JUnit.Xml.TestLogger.NetFull.Tests.ParametrizedTestCases.TestData(Int32 x, String s) in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 225 + at JUnit.Xml.TestLogger.NetFull.Tests.ParametrizedTestCases.TestData(Int32 x, String s) in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 232 - - at JUnit.Xml.TestLogger.NetFull.Tests.UnitTest1.FailTest11() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 20 + + at JUnit.Xml.TestLogger.NetFull.Tests.UnitTest1.FailTest11() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 27 - + - - + + - + - - at JUnit.Xml.TestLogger.NetFull.Tests.UnitTest2.FailTest22() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 87 + + at JUnit.Xml.TestLogger.NetFull.Tests.UnitTest2.FailTest22() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 94 - - - + + + - + --TearDown at JUnit.Xml.TestLogger.Tests2.FailingTearDown.TearDown() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 137 - + at JUnit.Xml.TestLogger.Tests2.FailingTestSetup.SetUp() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 122 - + - + at JUnit.Xml.TestLogger.Tests2.ParametrizedTestCases.TestData(Int32 x, String s) in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 187 - + at JUnit.Xml.TestLogger.Tests2.ParametrizedTestCases.TestData(Int32 x, String s) in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 187 - + - - + + at JUnit.Xml.TestLogger.Tests2.UnitTest1.FailTest11() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 20 - - + + - + at JUnit.Xml.TestLogger.Tests2.UnitTest2.FailTest22() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 49 - - - + + + - Junit Logger does not log standard output - Junit Logger does not log error output + NUnit Adapter 3.10.0.21: Test execution started +Running all tests in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\bin\Debug\netcoreapp2.0\JUnit.Xml.TestLogger.NetCore.Tests.dll +NUnit3TestExecutor converted 52 of 52 NUnit test cases + +JUnit.Xml.TestLogger.NetFull.Tests.UnitTest1.FailTest11 + {EEEE1DA6-6296-4486-BDA5-A50A19672F0F} + {C33FF4B5-75E1-4882-B968-DF9608BFE7C2} + +JUnit.Xml.TestLogger.NetFull.Tests.UnitTest1.PassTest11 + {2010CAE3-7BC0-4841-A5A3-7D5F947BB9FB} + {998AC9EC-7429-42CD-AD55-72037E7AF3D8} +NUnit Adapter 3.10.0.21: Test execution complete + + SetUp failed for test fixture JUnit.Xml.TestLogger.NetFull.Tests.FailingOneTimeSetUp +System.InvalidOperationException : Operation is not valid due to the current state of the object. + at JUnit.Xml.TestLogger.NetFull.Tests.FailingOneTimeSetUp.OneTimeSetUp() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 152 +TearDown failed for test fixture JUnit.Xml.TestLogger.NetFull.Tests.FailingOneTimeTearDown +TearDown : System.InvalidOperationException : Operation is not valid due to the current state of the object. +--TearDown + at JUnit.Xml.TestLogger.NetFull.Tests.FailingOneTimeTearDown.OneTimeTearDown() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest1.cs:line 197 +{D46DFA10-EEDD-49E5-804D-FE43051331A7} +{33F5FD22-6F40-499D-98E4-481D87FAEAA1} +SetUp failed for test fixture JUnit.Xml.TestLogger.Tests2.FailingOneTimeSetUp +System.InvalidOperationException : Operation is not valid due to the current state of the object. + at JUnit.Xml.TestLogger.Tests2.FailingOneTimeSetUp.OneTimeSetUp() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 107 +TearDown failed for test fixture JUnit.Xml.TestLogger.Tests2.FailingOneTimeTearDown +TearDown : System.InvalidOperationException : Operation is not valid due to the current state of the object. +--TearDown + at JUnit.Xml.TestLogger.Tests2.FailingOneTimeTearDown.OneTimeTearDown() in C:\Users\mjc82\Documents\GitHub\junit.testlogger\test\assets\JUnit.Xml.TestLogger.NetCore.Tests\UnitTest2.cs:line 152 + \ No newline at end of file