diff --git a/README.md b/README.md index c152529..7414479 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Github Releases](https://img.shields.io/github/release/xkbeyer/CatchTestAdapter/all.svg?label=pre-release)](https://github.com/xkbeyer/CatchTestAdapter/releases) [![Github Releases](https://img.shields.io/github/release/xkbeyer/CatchTestAdapter.svg)](https://github.com/xkbeyer/CatchTestAdapter/releases) +[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) # CatchTestAdapter A Visual Studio Extension to run [Catch2](https://github.com/catchorg/Catch2) unit tests within the Visual Studio TestExplorer. @@ -8,26 +8,25 @@ Use the latest [CatchTestAdapter.vsix](https://github.com/xkbeyer/CatchTestAdapt It can be installed by double clicking on the downloaded file. ### Visual Studio Compatibility -v1.6.x: Visual Studio 2019 v16.2 and newer - +##### v1.6.x: Works with Visual Studio 2019 v16.2 and newer Beginning with Visual Studio 2019 v16.2 the CatchTestAdapter 1.5.1 is broken. -The new TextExplorer Window doesn't accept new test cases as a sub test case. -Therefore Catch SECTIONs are no longer shown as test cases after the discovery phase. -They are shown as sub results. +The new TestExplorer Window doesn't accept new test cases as a sub test case. +As a result the Catch2 `SECTION`s are no longer shown as sub test cases after the first run. +Now they are shown as sub results of a test case. -v1.5.1: Visual Studio prior v16.2 +##### v1.5.1: Works with Visual Studio prior to v16.2 ### Status - Test cases are shown after discovery process. - Stack trace link to the source line. -- Catch2 TAGS are implemented as Traits. -- SECTION and SCENARIO are shown as sub results. +- Catch2 `TAGS` are implemented as Traits. +- `SECTION` and `SCENARIO` are shown as sub results. ### Testing To run the unit tests against the `CatchTestAdapter.dll` of the solution, the `Local.runsettings` file must be loaded. The `TestAdaptersPaths` should be adapted to point to the Solution directory. -``` +```xml c:\Path\to\the\Solution\bin\Debug ``` diff --git a/TestAdapter/CatchTestCase.cs b/TestAdapter/CatchTestCase.cs index 08e47d0..eeaa108 100644 --- a/TestAdapter/CatchTestCase.cs +++ b/TestAdapter/CatchTestCase.cs @@ -42,6 +42,8 @@ public TestCase[] Sections { } [XmlElement("OverallResult", typeof(OverallResult))] public OverallResult Result { get; set; } + [XmlElement("OverallResults", typeof(OverallResults), IsNullable = true)] + public OverallResults Results { get; set; } } public class Failure { @@ -75,4 +77,15 @@ public class OverallResult [XmlAttribute("durationInSeconds")] public string Duration = ""; } + public class OverallResults + { + [XmlAttribute("successes")] + public string Successes = ""; + [XmlAttribute("failures")] + public string Failures = ""; + [XmlAttribute("expectedFailures")] + public string ExpectedFailures = ""; + [XmlAttribute("durationInSeconds")] + public string Duration = ""; + } } diff --git a/TestAdapter/TestExecutor.cs b/TestAdapter/TestExecutor.cs index a39b174..f1e65e5 100644 --- a/TestAdapter/TestExecutor.cs +++ b/TestAdapter/TestExecutor.cs @@ -151,11 +151,21 @@ private void CreateResult(Tests.TestCase element, TestCase testCase, string name DisplayName = name.Replace(".", "\n\t"), ErrorMessage = $"", ErrorStackTrace = "", + Outcome = TestOutcome.None }; + if (element.Result != null) + { + subResult.Outcome = element.Result.Success == "true" ? TestOutcome.Passed : TestOutcome.Failed; subResult.Duration = TimeSpan.FromSeconds(Double.Parse(element.Result.Duration, CultureInfo.InvariantCulture)); + } int failedExpressions = ConstructResult(element.Expressions, subResult); + // Make sure the outcome is failed if the expressions have failed. + if (failedExpressions != 0) + { + subResult.Outcome = TestOutcome.Failed; + } foreach (var s in (element.Warning ?? new string[] { })) { @@ -179,7 +189,18 @@ private void CreateResult(Tests.TestCase element, TestCase testCase, string name subResult.ErrorStackTrace += $"at #{failedExpressions} - {name}() in {FilePath}:line {LineNumber}{Environment.NewLine}"; } - subResult.Outcome = failedExpressions == 0 ? TestOutcome.Passed : TestOutcome.Failed; + if( subResult.Outcome == TestOutcome.None ) + { + // Check if the OverallResults is set with an outcome. + if (element.Results != null) + { + if (Int32.Parse(element.Results.Failures) != 0) + subResult.Outcome = TestOutcome.Failed; + else + subResult.Outcome = TestOutcome.Passed; + } + } + results.Add(subResult); // Try to find the failure from a subsection of this element. diff --git a/TestAdapterTest/TestTestExecutor.cs b/TestAdapterTest/TestTestExecutor.cs index 442e166..15ff54d 100644 --- a/TestAdapterTest/TestTestExecutor.cs +++ b/TestAdapterTest/TestTestExecutor.cs @@ -135,17 +135,48 @@ public void WarningAndInfoMessage() } + [TestMethod] + public void OneTestResult() + { + // Set up a fake testing context. + var framework = new MockFrameworkHandle(); + + // Execute all tests. + TestExecutor executor = new TestExecutor(); + IList tests = new List(); + tests.Add(new TestCase("Has forced failure", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = @"ReferenceCatchProject\Tests.cpp", LineNumber = 37 }); + executor.RunTests(tests, new MockRunContext(), framework); + Assert.AreEqual(2, framework.Results.Count); + Assert.IsTrue(framework.Results.All(r => r.Outcome == TestOutcome.Failed)); + } + + [TestMethod] + public void SubResult() + { + // Set up a fake testing context. + var framework = new MockFrameworkHandle(); + + // Execute all tests. + TestExecutor executor = new TestExecutor(); + executor.RunTests(Common.ReferenceExeList, new MockRunContext(), framework); + Assert.IsTrue(framework.Results.Count != 0); + var resultsOfFoo = framework.Results.Where(r => r.TestCase.DisplayName == "Foo"); + Assert.AreEqual(5, resultsOfFoo.Count()); + Assert.IsTrue(resultsOfFoo.All(r => r.Outcome == TestOutcome.Failed)); + } + [TestMethod] public void BrokenXmlWithSingleTest() { var framework = new MockFrameworkHandle(); var runcontext = new MockRunContext(); var executor = new MockTestExecutor(); - IList xml_output = new List() { @"", -@"", -@" ", -@" " -}; + IList xml_output = new List() { + @"", + @"", + @" ", + @" " + }; IList tests = new List(); tests.Add(new TestCase("C++ assert", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = "ReferenceCatchProject\testrunnertest.cpp", LineNumber = 45 }); executor.MockComposeResults(xml_output, tests, framework); @@ -159,50 +190,51 @@ public void BrokenXml() var framework = new MockFrameworkHandle(); var runcontext = new MockRunContext(); var executor = new MockTestExecutor(); - IList xml_output = new List() { @"", -@"", -@" ", -@" ", -@" ", -@" ", -@" 3 == 4", -@" ", -@" ", -@" 3 == 4", -@" ", -@" ", -@" ", -@" ", -@" 9 == 0 ", -@" ", -@" ", -@" 9 == 0 ", -@" ", -@" ", -@" ", -@" ", -@" ", -@" ", -@" ", -@" a == b ", -@" ", -@" ", -@" 5 == 6 ", -@" ", -@" ", -@"
", -@" ", -@"
", -@" ", -@"
", -@" ", -@" ", -@" Fail check", -@" ", -@" ", -@" ", -@" ", -}; + IList xml_output = new List() { + @"", + @"", + @" ", + @" ", + @" ", + @" ", + @" 3 == 4", + @" ", + @" ", + @" 3 == 4", + @" ", + @" ", + @" ", + @" ", + @" 9 == 0 ", + @" ", + @" ", + @" 9 == 0 ", + @" ", + @" ", + @" ", + @" ", + @" ", + @" ", + @" ", + @" a == b ", + @" ", + @" ", + @" 5 == 6 ", + @" ", + @" ", + @"
", + @" ", + @"
", + @" ", + @"
", + @" ", + @" ", + @" Fail check", + @" ", + @" ", + @" ", + @" ", + }; IList tests = new List(); tests.Add(new TestCase("Simple test case", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = "ReferenceCatchProject\testrunnertest.cpp", LineNumber = 15 }); tests.Add(new TestCase("Another test case", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = "ReferenceCatchProject\testrunnertest.cpp", LineNumber = 23 }); @@ -211,7 +243,7 @@ public void BrokenXml() tests.Add(new TestCase("Last test case", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = "ReferenceCatchProject\testrunnertest.cpp", LineNumber = 54 }); executor.MockComposeResults(xml_output, tests, framework); Assert.AreEqual(6, framework.Results.Count); - for (int i = 0; i < 3; ++i) + for (int i = 0; i < 4; ++i) { Assert.AreNotEqual(TestOutcome.None, framework.Results[i].Outcome); } @@ -290,5 +322,110 @@ public void XmlReadTest() Assert.AreEqual(TestOutcome.Failed, framework.Results[i].Outcome); } } + + [TestMethod] + public void XmlFooTest() + { + var framework = new MockFrameworkHandle(); + var runcontext = new MockRunContext(); + var executor = new MockTestExecutor(); + IList xml_output = new List() { + @"", + @"", + @" ", + @" ", + @"
", + @" ", + @" ", + @" x < 100", + @" ", + @" ", + @" 168 < 100", + @" ", + @" ", + @" ", + @"
", + @"
", + @" ", + @" ", + @" x == 42", + @" ", + @" ", + @" 43 == 42", + @" ", + @" ", + @"
", + @" ", + @" ", + @" x == 42", + @" ", + @" ", + @" 44 == 42", + @" ", + @" ", + @" ", + @"
", + @" ", + @"
", + @"
", + @" ", + @" ", + @" x == 42", + @" ", + @" ", + @" 43 == 42", + @" ", + @" ", + @" ", + @"
", + @" ", + @"
", + @" ", + @"
", + @" ", + @"
" + }; + + IList tests = new List(); + tests.Add(new TestCase("Foo", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = @"ReferenceCatchProject\Tests.cpp", LineNumber = 59 }); + executor.MockComposeResults(xml_output, tests, framework); + var results = framework.Results.ToArray(); + Assert.AreEqual(5, framework.Results.Count); + Assert.IsTrue(results.All(r => r.Outcome == TestOutcome.Failed)); + } + + [TestMethod] + public void XmlPassedTestWithInfo() + { + var framework = new MockFrameworkHandle(); + var runcontext = new MockRunContext(); + var executor = new MockTestExecutor(); + IList xml_output = new List() { + @"", + @"", + @" ", + @" ", + @" ", + @" ", + @" expected == actual", + @" ", + @" ", + @" true == true", + @" ", + @" ", + @" ", + @" ", + @" ", + @" ", + @" ", + @"" + }; + IList tests = new List(); + tests.Add(new TestCase("First fixture", new Uri(TestExecutor.ExecutorUriString), "ReferenceCatchProject") { CodeFilePath = @"ReferenceCatchProject\fixture_test.cpp", LineNumber = 13 }); + executor.MockComposeResults(xml_output, tests, framework); + var results = framework.Results.ToArray(); + Assert.AreEqual(1, framework.Results.Count); + Assert.AreEqual(TestOutcome.Passed, results[0].Outcome); + } } }