diff --git a/GoogleTestAdapter/Core.Tests/Core.Tests.csproj b/GoogleTestAdapter/Core.Tests/Core.Tests.csproj index 5a26f745f..ed76511a9 100644 --- a/GoogleTestAdapter/Core.Tests/Core.Tests.csproj +++ b/GoogleTestAdapter/Core.Tests/Core.Tests.csproj @@ -97,7 +97,6 @@ - diff --git a/GoogleTestAdapter/Core.Tests/TestResults/StandardOutputTestResultParserTests.cs b/GoogleTestAdapter/Core.Tests/TestResults/StandardOutputTestResultParserTests.cs deleted file mode 100644 index f62145120..000000000 --- a/GoogleTestAdapter/Core.Tests/TestResults/StandardOutputTestResultParserTests.cs +++ /dev/null @@ -1,489 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using FluentAssertions; -using GoogleTestAdapter.DiaResolver; -using GoogleTestAdapter.Model; -using GoogleTestAdapter.Tests.Common; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using static GoogleTestAdapter.Tests.Common.TestMetadata.TestCategories; - -namespace GoogleTestAdapter.TestResults -{ - [TestClass] - public class StandardOutputTestResultParserTests : TestsBase - { - private string[] ConsoleOutput1 { get; } = { - @"[==========] Running 3 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 3 tests from TestMath", - @"[ RUN ] TestMath.AddFails", - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", - @" Actual: 20", - @"Expected: 1000", - @"[ FAILED ] TestMath.AddFails (3 ms)", - @"[ RUN ] TestMath.AddPasses" - }; - - private string[] ConsoleOutput1WithInvalidDuration { get; } = { - @"[==========] Running 3 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 3 tests from TestMath", - @"[ RUN ] TestMath.AddFails", - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", - @" Actual: 20", - @"Expected: 1000", - @"[ FAILED ] TestMath.AddFails (3 s)" - }; - - private string[] ConsoleOutput1WithThousandsSeparatorInDuration { get; } = { - @"[==========] Running 3 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 3 tests from TestMath", - @"[ RUN ] TestMath.AddFails", - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", - @" Actual: 20", - @"Expected: 1000", - @"[ FAILED ] TestMath.AddFails (4,656 ms)", - }; - - private string[] ConsoleOutput2 { get; } = { - @"[ OK ] TestMath.AddPasses(0 ms)", - @"[ RUN ] TestMath.Crash", - @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", - }; - - private string[] ConsoleOutput3 { get; } = { - @"[ FAILED ] TestMath.Crash(9 ms)", - @"[----------] 3 tests from TestMath(26 ms total)", - @"", - @"[----------] Global test environment tear-down", - @"[==========] 3 tests from 1 test case ran. (36 ms total)", - @"[ PASSED ] 1 test.", - @"[ FAILED ] 2 tests, listed below:", - @"[ FAILED ] TestMath.AddFails", - @"[ FAILED ] TestMath.Crash", - @"", - @" 2 FAILED TESTS", - @"", - }; - - private string[] ConsoleOutputWithOutputOfExe { get; } = { - @"[==========] Running 1 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 1 tests from TestMath", - @"[ RUN ] TestMath.AddPasses", - @"Some output produced by the exe", - @"[ OK ] TestMath.AddPasses(0 ms)", - @"[----------] 1 tests from TestMath(26 ms total)", - @"", - @"[----------] Global test environment tear-down", - @"[==========] 3 tests from 1 test case ran. (36 ms total)", - @"[ PASSED ] 1 test.", - }; - - private string[] ConsoleOutputWithPrefixingTest { get; } = { - @"[==========] Running 2 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 2 tests from TestMath", - @"[ RUN ] Test.AB", - @"[ OK ] Test.A(0 ms)", - @"[ RUN ] Test.A", - @"[ OK ] Test.A(0 ms)", - @"[----------] 2 tests from TestMath(26 ms total)", - @"", - @"[----------] Global test environment tear-down", - @"[==========] 2 tests from 1 test case ran. (36 ms total)", - @"[ PASSED ] 2 test.", - }; - - /// - /// - /// - private string[] ConsoleOutputWithSkippedTest { get; } = @"[==========] Running 3 tests from 1 test suite. -[----------] Global test environment set-up. -[----------] 3 tests from Test -[ RUN ] Test.Succeed -[ OK ] Test.Succeed (0 ms) -[ RUN ] Test.Skip -[ SKIPPED ] Test.Skip (1 ms) -[ RUN ] Test.Fail -C:\...\test.cpp(14): error: Value of: false - Actual: false -Expected: true -[ FAILED ] Test.Fail (0 ms) -[----------] 3 tests from Test (3 ms total) - -[----------] Global test environment tear-down -[==========] 3 tests from 1 test suite ran. (6 ms total) -[ PASSED ] 1 test. -[ SKIPPED ] 1 test, listed below: -[ SKIPPED ] Test.Skip -[ FAILED ] 1 test, listed below: -[ FAILED ] Test.Fail - - 1 FAILED TEST -".Split('\n'); - - /// - /// - /// - private string[] ConsoleOutputWithSkippedTestAsLastTest { get; } = @"[==========] Running 3 tests from 1 test suite. -[----------] Global test environment set-up. -[----------] 3 tests from Test -[ RUN ] Test.Succeed -[ OK ] Test.Succeed (0 ms) -[ RUN ] Test.Fail -C:\...\test.cpp(14): error: Value of: false - Actual: false -Expected: true -[ FAILED ] Test.Fail (0 ms) -[ RUN ] Test.Skip -[ SKIPPED ] Test.Skip (1 ms) -[----------] 3 tests from Test (3 ms total) - -[----------] Global test environment tear-down -[==========] 3 tests from 1 test suite ran. (6 ms total) -[ PASSED ] 1 test. -[ SKIPPED ] 1 test, listed below: -[ SKIPPED ] Test.Skip -[ FAILED ] 1 test, listed below: -[ FAILED ] Test.Fail - - 1 FAILED TEST -".Split('\n'); - - - private List CrashesImmediately { get; set; } - private List CrashesAfterErrorMsg { get; set; } - private List Complete { get; set; } - private List WrongDurationUnit { get; set; } - private List ThousandsSeparatorInDuration { get; set; } - private List PassingTestProducesConsoleOutput { get; set; } - private List CompleteStandardOutput { get; set; } - - [TestInitialize] - public override void SetUp() - { - base.SetUp(); - - CrashesImmediately = new List(ConsoleOutput1); - - CrashesAfterErrorMsg = new List(ConsoleOutput1); - CrashesAfterErrorMsg.AddRange(ConsoleOutput2); - - Complete = new List(ConsoleOutput1); - Complete.AddRange(ConsoleOutput2); - Complete.AddRange(ConsoleOutput3); - - WrongDurationUnit = new List(ConsoleOutput1WithInvalidDuration); - - ThousandsSeparatorInDuration = new List(ConsoleOutput1WithThousandsSeparatorInDuration); - - PassingTestProducesConsoleOutput = new List(ConsoleOutputWithOutputOfExe); - - CompleteStandardOutput = new List(File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default)); - } - - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_CompleteOutput_ParsedCorrectly() - { - var results = ComputeTestResults(Complete); - - results.Should().HaveCount(3); - - results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); - XmlTestResultParserTests.AssertTestResultIsFailure(results[0]); - results[0].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); - results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(3)); - results[0].ErrorStackTrace.Should() - .Contain( - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"); - - results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); - XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); - results[1].Duration.Should().Be(StandardOutputTestResultParser.ShortTestDuration); - - results[2].TestCase.FullyQualifiedName.Should().Be("TestMath.Crash"); - XmlTestResultParserTests.AssertTestResultIsFailure(results[2]); - results[2].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); - results[2].Duration.Should().Be(TimeSpan.FromMilliseconds(9)); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithImmediateCrash_CorrectResultHasCrashText() - { - var results = ComputeTestResults(CrashesImmediately); - - results.Should().HaveCount(2); - - results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); - XmlTestResultParserTests.AssertTestResultIsFailure(results[0]); - results[0].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); - results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(3)); - results[0].ErrorStackTrace.Should().Contain(@"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"); - - results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); - XmlTestResultParserTests.AssertTestResultIsFailure(results[1]); - results[1].ErrorMessage.Should().Contain(StandardOutputTestResultParser.CrashText); - results[1].ErrorMessage.Should().NotContain("Test output:"); - results[1].Duration.Should().Be(TimeSpan.FromMilliseconds(0)); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithCrashAfterErrorMessage_CorrectResultHasCrashText() - { - var results = ComputeTestResults(CrashesAfterErrorMsg); - - results.Should().HaveCount(3); - - results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); - XmlTestResultParserTests.AssertTestResultIsFailure(results[0]); - results[0].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); - results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(3)); - results[0].ErrorStackTrace.Should().Contain(@"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"); - - results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); - XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); - results[1].Duration.Should().Be(StandardOutputTestResultParser.ShortTestDuration); - - results[2].TestCase.FullyQualifiedName.Should().Be("TestMath.Crash"); - XmlTestResultParserTests.AssertTestResultIsFailure(results[2]); - results[2].ErrorMessage.Should().Contain(StandardOutputTestResultParser.CrashText); - results[2].ErrorMessage.Should().Contain("Test output:"); - results[2].ErrorMessage.Should().Contain("unknown file: error: SEH exception with code 0xc0000005 thrown in the test body."); - results[2].Duration.Should().Be(TimeSpan.FromMilliseconds(0)); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithInvalidDurationUnit_DefaultDurationIsUsedAndWarningIsProduced() - { - var results = ComputeTestResults(WrongDurationUnit); - - results.Should().ContainSingle(); - results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); - results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(1)); - results[0].ErrorStackTrace.Should().Contain(@"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"); - - MockLogger.Verify(l => l.LogWarning( - It.Is(s => s.Contains("'[ FAILED ] TestMath.AddFails (3 s)'"))), Times.Exactly(1)); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithThousandsSeparatorInDuration_ParsedCorrectly() - { - CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US"); - try - { - var results = ComputeTestResults(ThousandsSeparatorInDuration); - - results.Should().ContainSingle(); - results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); - results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(4656)); - } - finally - { - Thread.CurrentThread.CurrentCulture = currentCulture; - } - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithConsoleOutput_ConsoleOutputIsIgnored() - { - var results = ComputeTestResults(PassingTestProducesConsoleOutput); - - results.Should().ContainSingle(); - results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); - XmlTestResultParserTests.AssertTestResultIsPassed(results[0]); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithPrefixingTest_BothTestsAreFound() - { - var cases = new List - { - TestDataCreator.ToTestCase("Test.AB", TestDataCreator.DummyExecutable, - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"), - TestDataCreator.ToTestCase("Test.A", TestDataCreator.DummyExecutable, - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp") - }; - - var results = new StandardOutputTestResultParser(cases, ConsoleOutputWithPrefixingTest, TestEnvironment.Logger) - .GetTestResults(); - - results.Should().HaveCount(2); - results[0].TestCase.FullyQualifiedName.Should().Be("Test.AB"); - XmlTestResultParserTests.AssertTestResultIsPassed(results[0]); - results[1].TestCase.FullyQualifiedName.Should().Be("Test.A"); - XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithSkippedTest_AllResultsAreFound() - { - var cases = new List - { - TestDataCreator.ToTestCase("Test.Succeed", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), - TestDataCreator.ToTestCase("Test.Skip", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), - TestDataCreator.ToTestCase("Test.Fail", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), - }; - - var results = new StandardOutputTestResultParser(cases, ConsoleOutputWithSkippedTest, TestEnvironment.Logger).GetTestResults(); - - results.Should().HaveCount(3); - - var result = results[0]; - result.TestCase.FullyQualifiedName.Should().Be("Test.Succeed"); - XmlTestResultParserTests.AssertTestResultIsPassed(result); - - result = results[1]; - result.TestCase.FullyQualifiedName.Should().Be("Test.Skip"); - XmlTestResultParserTests.AssertTestResultIsSkipped(result); - - result = results[2]; - result.TestCase.FullyQualifiedName.Should().Be("Test.Fail"); - XmlTestResultParserTests.AssertTestResultIsFailure(result); - } - - [TestMethod] - [TestCategory(Unit)] - public void GetTestResults_OutputWithSkippedTestAsLastTest_AllResultsAreFound() - { - var cases = new List - { - TestDataCreator.ToTestCase("Test.Succeed", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), - TestDataCreator.ToTestCase("Test.Skip", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), - TestDataCreator.ToTestCase("Test.Fail", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), - }; - - var results = new StandardOutputTestResultParser(cases, ConsoleOutputWithSkippedTestAsLastTest, TestEnvironment.Logger).GetTestResults(); - - results.Should().HaveCount(3); - - var result = results[0]; - result.TestCase.FullyQualifiedName.Should().Be("Test.Succeed"); - XmlTestResultParserTests.AssertTestResultIsPassed(result); - - result = results[1]; - result.TestCase.FullyQualifiedName.Should().Be("Test.Fail"); - XmlTestResultParserTests.AssertTestResultIsFailure(result); - - result = results[2]; - result.TestCase.FullyQualifiedName.Should().Be("Test.Skip"); - XmlTestResultParserTests.AssertTestResultIsSkipped(result); - } - - [TestMethod] - [TestCategory(Unit)] - public void OutputHandling_OutputManyLinesWithNewlines_IsParsedCorrectly() - { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_ManyLinesWithNewlines"); - var expectedErrorMessage = - "before test 1\nbefore test 2\nExpected: 1\nTo be equal to: 2\ntest output\nafter test 1\nafter test 2"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - [TestMethod] - [TestCategory(Unit)] - public void OutputHandling_OutputOneLineWithNewlines_IsParsedCorrectly() - { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_OneLineWithNewlines"); - var expectedErrorMessage = - "before test\nExpected: 1\nTo be equal to: 2\ntest output\nafter test"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - [TestMethod] - [TestCategory(Unit)] - public void OutputHandling_OutputOneLine_IsParsedCorrectly() - { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_OneLine"); - var expectedErrorMessage = - "before test\nExpected: 1\nTo be equal to: 2\ntest output\nafter test"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - [TestMethod] - [TestCategory(Unit)] - public void OutputHandling_ManyLinesWithNewlines_IsParsedCorrectly() - { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.ManyLinesWithNewlines"); - var expectedErrorMessage = - "before test 1\nbefore test 2\nExpected: 1\nTo be equal to: 2\nafter test 1\nafter test 2"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - [TestMethod] - [TestCategory(Unit)] - public void OutputHandling_OneLineWithNewlines_IsParsedCorrectly() - { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_OneLineWithNewlines"); - var expectedErrorMessage = - "before test\nExpected: 1\nTo be equal to: 2\ntest output\nafter test"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - [TestMethod] - [TestCategory(Unit)] - public void OutputHandling_OneLine_IsParsedCorrectly() - { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.OneLine"); - var expectedErrorMessage = - "before test\nExpected: 1\nTo be equal to: 2\nafter test"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - private IList GetTestResultsFromCompleteOutputFile() - { - var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) - .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); - return new StandardOutputTestResultParser(testCases, CompleteStandardOutput, MockLogger.Object) - .GetTestResults(); - } - - - private IList ComputeTestResults(List consoleOutput) - { - var cases = new List - { - TestDataCreator.ToTestCase("TestMath.AddFails", TestDataCreator.DummyExecutable, - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"), - TestDataCreator.ToTestCase("TestMath.Crash", TestDataCreator.DummyExecutable, - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"), - TestDataCreator.ToTestCase("TestMath.AddPasses", TestDataCreator.DummyExecutable, - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp") - }; - var parser = new StandardOutputTestResultParser(cases, consoleOutput, TestEnvironment.Logger); - return parser.GetTestResults(); - } - - } - -} \ No newline at end of file diff --git a/GoogleTestAdapter/Core.Tests/TestResults/StreamingStandardOutputTestResultParserTests.cs b/GoogleTestAdapter/Core.Tests/TestResults/StreamingStandardOutputTestResultParserTests.cs index b2fa5ac32..c356ea0d1 100644 --- a/GoogleTestAdapter/Core.Tests/TestResults/StreamingStandardOutputTestResultParserTests.cs +++ b/GoogleTestAdapter/Core.Tests/TestResults/StreamingStandardOutputTestResultParserTests.cs @@ -17,210 +17,48 @@ namespace GoogleTestAdapter.TestResults [TestClass] public class StreamingStandardOutputTestResultParserTests : TestsBase { - private string[] ConsoleOutput1 { get; } = { - @"[==========] Running 3 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 3 tests from TestMath", - @"[ RUN ] TestMath.AddFails", - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", - @" Actual: 20", - @"Expected: 1000", - @"[ FAILED ] TestMath.AddFails (3 ms)", - @"[ RUN ] TestMath.AddPasses" - }; - - private string[] ConsoleOutput1WithInvalidDuration { get; } = { - @"[==========] Running 3 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 3 tests from TestMath", - @"[ RUN ] TestMath.AddFails", - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", - @" Actual: 20", - @"Expected: 1000", - @"[ FAILED ] TestMath.AddFails (3 s)" - }; - - private string[] ConsoleOutput1WithThousandsSeparatorInDuration { get; } = { - @"[==========] Running 3 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 3 tests from TestMath", - @"[ RUN ] TestMath.AddFails", - @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", - @" Actual: 20", - @"Expected: 1000", - @"[ FAILED ] TestMath.AddFails (4,656 ms)", - }; - - private string[] ConsoleOutput2 { get; } = { - @"[ OK ] TestMath.AddPasses(0 ms)", - @"[ RUN ] TestMath.Crash", - @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", - }; - - private string[] ConsoleOutput2WithPrefixingOutput { get; } = { - @"DummyOutput[ OK ] TestMath.AddPasses(0 ms)", - @"[ RUN ] TestMath.Crash", - @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", - }; - - private string[] ConsoleOutput2WithPrefixingOutputAndFailing { get; } = { - @"DummyOutput[ FAILED ] TestMath.AddPasses(0 ms)", - @"[ RUN ] TestMath.Crash", - @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", - }; - - private string[] ConsoleOutput3 { get; } = { - @"[ FAILED ] TestMath.Crash(9 ms)", - @"[----------] 3 tests from TestMath(26 ms total)", - @"", - @"[----------] Global test environment tear-down", - @"[==========] 3 tests from 1 test case ran. (36 ms total)", - @"[ PASSED ] 1 test.", - @"[ FAILED ] 2 tests, listed below:", - @"[ FAILED ] TestMath.AddFails", - @"[ FAILED ] TestMath.Crash", - @"", - @" 2 FAILED TESTS", - @"", - }; - - private string[] ConsoleOutputWithOutputOfExe { get; } = { - @"[==========] Running 1 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 1 tests from TestMath", - @"[ RUN ] TestMath.AddPasses", - @"Some output produced by the exe", - @"[ OK ] TestMath.AddPasses(0 ms)", - @"[----------] 1 tests from TestMath(26 ms total)", - @"", - @"[----------] Global test environment tear-down", - @"[==========] 3 tests from 1 test case ran. (36 ms total)", - @"[ PASSED ] 1 test.", - }; - - private string[] ConsoleOutputWithPrefixingTest { get; } = { - @"[==========] Running 2 tests from 1 test case.", - @"[----------] Global test environment set-up.", - @"[----------] 2 tests from TestMath", - @"[ RUN ] Test.AB", - @"[ OK ] Test.A(0 ms)", - @"[ RUN ] Test.A", - @"[ OK ] Test.A(0 ms)", - @"[----------] 2 tests from TestMath(26 ms total)", - @"", - @"[----------] Global test environment tear-down", - @"[==========] 2 tests from 1 test case ran. (36 ms total)", - @"[ PASSED ] 2 test.", - }; - - /// - /// - /// - private string[] ConsoleOutputWithSkippedTest { get; } = @"[==========] Running 3 tests from 1 test suite. -[----------] Global test environment set-up. -[----------] 3 tests from Test -[ RUN ] Test.Succeed -[ OK ] Test.Succeed (0 ms) -[ RUN ] Test.Skip -[ SKIPPED ] Test.Skip (1 ms) -[ RUN ] Test.Fail -C:\...\test.cpp(14): error: Value of: false - Actual: false -Expected: true -[ FAILED ] Test.Fail (0 ms) -[----------] 3 tests from Test (3 ms total) - -[----------] Global test environment tear-down -[==========] 3 tests from 1 test suite ran. (6 ms total) -[ PASSED ] 1 test. -[ SKIPPED ] 1 test, listed below: -[ SKIPPED ] Test.Skip -[ FAILED ] 1 test, listed below: -[ FAILED ] Test.Fail - - 1 FAILED TEST -".Split('\n'); - - /// - /// - /// - private string[] ConsoleOutputWithSkippedTestAsLastTest { get; } = @"[==========] Running 3 tests from 1 test suite. -[----------] Global test environment set-up. -[----------] 3 tests from Test -[ RUN ] Test.Succeed -[ OK ] Test.Succeed (0 ms) -[ RUN ] Test.Fail -C:\...\test.cpp(14): error: Value of: false - Actual: false -Expected: true -[ FAILED ] Test.Fail (0 ms) -[ RUN ] Test.Skip -[ SKIPPED ] Test.Skip (1 ms) -[----------] 3 tests from Test (3 ms total) - -[----------] Global test environment tear-down -[==========] 3 tests from 1 test suite ran. (6 ms total) -[ PASSED ] 1 test. -[ SKIPPED ] 1 test, listed below: -[ SKIPPED ] Test.Skip -[ FAILED ] 1 test, listed below: -[ FAILED ] Test.Fail - - 1 FAILED TEST -".Split('\n'); - - - private List CrashesImmediately { get; set; } - private List CrashesAfterErrorMsg { get; set; } - private List Complete { get; set; } - private List WrongDurationUnit { get; set; } - private List ThousandsSeparatorInDuration { get; set; } - private List PassingTestProducesConsoleOutput { get; set; } - private List WithPrefixingOutputPassing { get; set; } - private List WithPrefixingOutputFailing { get; set; } - private List CompleteStandardOutput { get; set; } - - [TestInitialize] - public override void SetUp() - { - base.SetUp(); - - CrashesImmediately = new List(ConsoleOutput1); - - CrashesAfterErrorMsg = new List(ConsoleOutput1); - CrashesAfterErrorMsg.AddRange(ConsoleOutput2); - - Complete = new List(ConsoleOutput1); - Complete.AddRange(ConsoleOutput2); - Complete.AddRange(ConsoleOutput3); - - WrongDurationUnit = new List(ConsoleOutput1WithInvalidDuration); - - ThousandsSeparatorInDuration = new List(ConsoleOutput1WithThousandsSeparatorInDuration); - - PassingTestProducesConsoleOutput = new List(ConsoleOutputWithOutputOfExe); - - WithPrefixingOutputFailing = new List(ConsoleOutput1); - WithPrefixingOutputFailing.AddRange(ConsoleOutput2WithPrefixingOutputAndFailing); - - WithPrefixingOutputPassing = new List(ConsoleOutput1); - WithPrefixingOutputPassing.AddRange(ConsoleOutput2WithPrefixingOutput); - - CompleteStandardOutput = new List(File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default)); - } - - [TestMethod] [TestCategory(Unit)] public void GetTestResults_CompleteOutput_ParsedCorrectly() { - IList results = ComputeTestResults(Complete); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (3 ms)", + @"[ RUN ] TestMath.AddPasses", + @"[ OK ] TestMath.AddPasses(0 ms)", + @"[ RUN ] TestMath.Crash", + @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", + @"[ FAILED ] TestMath.Crash(9 ms)", + @"[----------] 3 tests from TestMath(26 ms total)", + @"", + @"[----------] Global test environment tear-down", + @"[==========] 3 tests from 1 test case ran. (36 ms total)", + @"[ PASSED ] 1 test.", + @"[ FAILED ] 2 tests, listed below:", + @"[ FAILED ] TestMath.AddFails", + @"[ FAILED ] TestMath.Crash", + @"", + @" 2 FAILED TESTS", + @"", + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().HaveCount(3); results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); XmlTestResultParserTests.AssertTestResultIsFailure(results[0]); - results[0].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); + results[0].ErrorMessage.Should().NotContain(StreamingStandardOutputTestResultParser.CrashText); results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(3)); results[0].ErrorStackTrace.Should() .Contain( @@ -228,93 +66,191 @@ public void GetTestResults_CompleteOutput_ParsedCorrectly() results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); - results[1].Duration.Should().Be(StandardOutputTestResultParser.ShortTestDuration); + results[1].Duration.Should().Be(StreamingStandardOutputTestResultParser.ShortTestDuration); results[2].TestCase.FullyQualifiedName.Should().Be("TestMath.Crash"); XmlTestResultParserTests.AssertTestResultIsFailure(results[2]); - results[2].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); + results[2].ErrorMessage.Should().NotContain(StreamingStandardOutputTestResultParser.CrashText); results[2].Duration.Should().Be(TimeSpan.FromMilliseconds(9)); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithImmediateCrash_CorrectResultHasCrashText() { - IList results = ComputeTestResults(CrashesImmediately); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (3 ms)", + @"[ RUN ] TestMath.AddPasses" + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().HaveCount(2); results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); XmlTestResultParserTests.AssertTestResultIsFailure(results[0]); - results[0].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); + results[0].ErrorMessage.Should().NotContain(StreamingStandardOutputTestResultParser.CrashText); results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(3)); results[0].ErrorStackTrace.Should().Contain(@"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"); results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); XmlTestResultParserTests.AssertTestResultIsFailure(results[1]); - results[1].ErrorMessage.Should().Contain(StandardOutputTestResultParser.CrashText); + results[1].ErrorMessage.Should().Contain(StreamingStandardOutputTestResultParser.CrashText); results[1].ErrorMessage.Should().NotContain("Test output:"); results[1].Duration.Should().Be(TimeSpan.FromMilliseconds(0)); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithCrashAfterErrorMessage_CorrectResultHasCrashText() { - IList results = ComputeTestResults(CrashesAfterErrorMsg); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (3 ms)", + @"[ RUN ] TestMath.AddPasses", + @"[ OK ] TestMath.AddPasses(0 ms)", + @"[ RUN ] TestMath.Crash", + @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().HaveCount(3); results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); XmlTestResultParserTests.AssertTestResultIsFailure(results[0]); - results[0].ErrorMessage.Should().NotContain(StandardOutputTestResultParser.CrashText); + results[0].ErrorMessage.Should().NotContain(StreamingStandardOutputTestResultParser.CrashText); results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(3)); results[0].ErrorStackTrace.Should().Contain(@"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp"); results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); - results[1].Duration.Should().Be(StandardOutputTestResultParser.ShortTestDuration); + results[1].Duration.Should().Be(StreamingStandardOutputTestResultParser.ShortTestDuration); results[2].TestCase.FullyQualifiedName.Should().Be("TestMath.Crash"); XmlTestResultParserTests.AssertTestResultIsFailure(results[2]); - results[2].ErrorMessage.Should().Contain(StandardOutputTestResultParser.CrashText); + results[2].ErrorMessage.Should().Contain(StreamingStandardOutputTestResultParser.CrashText); results[2].ErrorMessage.Should().Contain("Test output:"); results[2].ErrorMessage.Should().Contain("unknown file: error: SEH exception with code 0xc0000005 thrown in the test body."); results[2].Duration.Should().Be(TimeSpan.FromMilliseconds(0)); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithPrefixedPassedLine_PassingTestIsRecognized() { - IList results = ComputeTestResults(WithPrefixingOutputPassing); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (3 ms)", + @"[ RUN ] TestMath.AddPasses", + @"DummyOutput[ OK ] TestMath.AddPasses(0 ms)", + @"[ RUN ] TestMath.Crash", + @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().HaveCount(3); results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); - results[1].Duration.Should().Be(StandardOutputTestResultParser.ShortTestDuration); + results[1].Duration.Should().Be(StreamingStandardOutputTestResultParser.ShortTestDuration); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithPrefixedFailedLine_FailingTestIsRecognized() { - IList results = ComputeTestResults(WithPrefixingOutputFailing); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (3 ms)", + @"[ RUN ] TestMath.AddPasses", + @"DummyOutput[ FAILED ] TestMath.AddPasses(0 ms)", + @"[ RUN ] TestMath.Crash", + @"unknown file: error: SEH exception with code 0xc0000005 thrown in the test body.", + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().HaveCount(3); results[1].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); XmlTestResultParserTests.AssertTestResultIsFailure(results[1]); results[1].ErrorMessage.Should().Contain("DummyOutput"); - results[1].Duration.Should().Be(StandardOutputTestResultParser.ShortTestDuration); + results[1].Duration.Should().Be(StreamingStandardOutputTestResultParser.ShortTestDuration); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithInvalidDurationUnit_DefaultDurationIsUsedAndWarningIsProduced() { - IList results = ComputeTestResults(WrongDurationUnit); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (3 s)" + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().ContainSingle(); results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); @@ -323,6 +259,8 @@ public void GetTestResults_OutputWithInvalidDurationUnit_DefaultDurationIsUsedAn MockLogger.Verify(l => l.LogWarning( It.Is(s => s.Contains("'[ FAILED ] TestMath.AddFails (3 s)'"))), Times.Exactly(1)); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] @@ -333,11 +271,28 @@ public void GetTestResults_OutputWithThousandsSeparatorInDuration_ParsedCorrectl Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US"); try { - IList results = ComputeTestResults(ThousandsSeparatorInDuration); + string[] consoleOutput = { + @"[==========] Running 3 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 3 tests from TestMath", + @"[ RUN ] TestMath.AddFails", + @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp(6): error: Value of: Add(10, 10)", + @" Actual: 20", + @"Expected: 1000", + @"[ FAILED ] TestMath.AddFails (4,656 ms)", + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().ContainSingle(); results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddFails"); results[0].Duration.Should().Be(TimeSpan.FromMilliseconds(4656)); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } finally { @@ -349,17 +304,52 @@ public void GetTestResults_OutputWithThousandsSeparatorInDuration_ParsedCorrectl [TestCategory(Unit)] public void GetTestResults_OutputWithConsoleOutput_ConsoleOutputIsIgnored() { - IList results = ComputeTestResults(PassingTestProducesConsoleOutput); + string[] consoleOutput = { + @"[==========] Running 1 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 1 tests from TestMath", + @"[ RUN ] TestMath.AddPasses", + @"Some output produced by the exe", + @"[ OK ] TestMath.AddPasses(0 ms)", + @"[----------] 1 tests from TestMath(26 ms total)", + @"", + @"[----------] Global test environment tear-down", + @"[==========] 3 tests from 1 test case ran. (36 ms total)", + @"[ PASSED ] 1 test.", + }; + var cases = GetTestCases(); + + var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + IList results = parser.TestResults; results.Should().ContainSingle(); results[0].TestCase.FullyQualifiedName.Should().Be("TestMath.AddPasses"); XmlTestResultParserTests.AssertTestResultIsPassed(results[0]); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithPrefixingTest_BothTestsAreFound() { + string[] consoleOutput = + { + @"[==========] Running 2 tests from 1 test case.", + @"[----------] Global test environment set-up.", + @"[----------] 2 tests from TestMath", + @"[ RUN ] Test.AB", + @"[ OK ] Test.A(0 ms)", + @"[ RUN ] Test.A", + @"[ OK ] Test.A(0 ms)", + @"[----------] 2 tests from TestMath(26 ms total)", + @"", + @"[----------] Global test environment tear-down", + @"[==========] 2 tests from 1 test case ran. (36 ms total)", + @"[ PASSED ] 2 test.", + }; var cases = new List { TestDataCreator.ToTestCase("Test.AB", TestDataCreator.DummyExecutable, @@ -369,7 +359,7 @@ public void GetTestResults_OutputWithPrefixingTest_BothTestsAreFound() }; var parser = new StreamingStandardOutputTestResultParser(cases, TestEnvironment.Logger, MockFrameworkReporter.Object); - ConsoleOutputWithPrefixingTest.ToList().ForEach(parser.ReportLine); + consoleOutput.ToList().ForEach(parser.ReportLine); parser.Flush(); var results = parser.TestResults; @@ -378,12 +368,38 @@ public void GetTestResults_OutputWithPrefixingTest_BothTestsAreFound() XmlTestResultParserTests.AssertTestResultIsPassed(results[0]); results[1].TestCase.FullyQualifiedName.Should().Be("Test.A"); XmlTestResultParserTests.AssertTestResultIsPassed(results[1]); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithSkippedTest_AllResultsAreFound() { + string[] consoleOutput = @"[==========] Running 3 tests from 1 test suite. +[----------] Global test environment set-up. +[----------] 3 tests from Test +[ RUN ] Test.Succeed +[ OK ] Test.Succeed (0 ms) +[ RUN ] Test.Skip +[ SKIPPED ] Test.Skip (1 ms) +[ RUN ] Test.Fail +C:\...\test.cpp(14): error: Value of: false + Actual: false +Expected: true +[ FAILED ] Test.Fail (0 ms) +[----------] 3 tests from Test (3 ms total) + +[----------] Global test environment tear-down +[==========] 3 tests from 1 test suite ran. (6 ms total) +[ PASSED ] 1 test. +[ SKIPPED ] 1 test, listed below: +[ SKIPPED ] Test.Skip +[ FAILED ] 1 test, listed below: +[ FAILED ] Test.Fail + + 1 FAILED TEST +".Split('\n'); var cases = new List { TestDataCreator.ToTestCase("Test.Succeed", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), @@ -392,7 +408,7 @@ public void GetTestResults_OutputWithSkippedTest_AllResultsAreFound() }; var parser = new StreamingStandardOutputTestResultParser(cases, TestEnvironment.Logger, MockFrameworkReporter.Object); - ConsoleOutputWithSkippedTest.ToList().ForEach(parser.ReportLine); + consoleOutput.ToList().ForEach(parser.ReportLine); parser.Flush(); var results = parser.TestResults; @@ -409,12 +425,38 @@ public void GetTestResults_OutputWithSkippedTest_AllResultsAreFound() result = results[2]; result.TestCase.FullyQualifiedName.Should().Be("Test.Fail"); XmlTestResultParserTests.AssertTestResultIsFailure(result); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void GetTestResults_OutputWithSkippedTestAsLastTest_AllResultsAreFound() { + string[] consoleOutput = @"[==========] Running 3 tests from 1 test suite. +[----------] Global test environment set-up. +[----------] 3 tests from Test +[ RUN ] Test.Succeed +[ OK ] Test.Succeed (0 ms) +[ RUN ] Test.Fail +C:\...\test.cpp(14): error: Value of: false + Actual: false +Expected: true +[ FAILED ] Test.Fail (0 ms) +[ RUN ] Test.Skip +[ SKIPPED ] Test.Skip (1 ms) +[----------] 3 tests from Test (3 ms total) + +[----------] Global test environment tear-down +[==========] 3 tests from 1 test suite ran. (6 ms total) +[ PASSED ] 1 test. +[ SKIPPED ] 1 test, listed below: +[ SKIPPED ] Test.Skip +[ FAILED ] 1 test, listed below: +[ FAILED ] Test.Fail + + 1 FAILED TEST +".Split('\n'); var cases = new List { TestDataCreator.ToTestCase("Test.Succeed", TestDataCreator.DummyExecutable, @"c:\somepath\source.cpp"), @@ -423,7 +465,7 @@ public void GetTestResults_OutputWithSkippedTestAsLastTest_AllResultsAreFound() }; var parser = new StreamingStandardOutputTestResultParser(cases, TestEnvironment.Logger, MockFrameworkReporter.Object); - ConsoleOutputWithSkippedTestAsLastTest.ToList().ForEach(parser.ReportLine); + consoleOutput.ToList().ForEach(parser.ReportLine); parser.Flush(); var results = parser.TestResults; @@ -440,94 +482,137 @@ public void GetTestResults_OutputWithSkippedTestAsLastTest_AllResultsAreFound() result = results[2]; result.TestCase.FullyQualifiedName.Should().Be("Test.Skip"); XmlTestResultParserTests.AssertTestResultIsSkipped(result); + + CheckStandardOutputResultParser(cases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void OutputHandling_OutputManyLinesWithNewlines_IsParsedCorrectly() { - var results = GetTestResultsFromCompleteOutputFile(); + var consoleOutput = File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default); + var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) + .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); + + var parser = new StreamingStandardOutputTestResultParser(testCases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + var results = parser.TestResults; var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_ManyLinesWithNewlines"); var expectedErrorMessage = "before test 1\nbefore test 2\nExpected: 1\nTo be equal to: 2\ntest output\nafter test 1\nafter test 2"; testResult.ErrorMessage.Should().Be(expectedErrorMessage); + + CheckStandardOutputResultParser(testCases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void OutputHandling_OutputOneLineWithNewlines_IsParsedCorrectly() { - var results = GetTestResultsFromCompleteOutputFile(); + var consoleOutput = File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default); + var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) + .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); + + var parser = new StreamingStandardOutputTestResultParser(testCases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + var results = parser.TestResults; var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_OneLineWithNewlines"); var expectedErrorMessage = "before test\nExpected: 1\nTo be equal to: 2\ntest output\nafter test"; testResult.ErrorMessage.Should().Be(expectedErrorMessage); + + CheckStandardOutputResultParser(testCases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void OutputHandling_OutputOneLine_IsParsedCorrectly() { - var results = GetTestResultsFromCompleteOutputFile(); + var consoleOutput = File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default); + var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) + .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); + + var parser = new StreamingStandardOutputTestResultParser(testCases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + var results = parser.TestResults; var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_OneLine"); var expectedErrorMessage = "before test\nExpected: 1\nTo be equal to: 2\ntest output\nafter test"; testResult.ErrorMessage.Should().Be(expectedErrorMessage); + + CheckStandardOutputResultParser(testCases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void OutputHandling_ManyLinesWithNewlines_IsParsedCorrectly() { - var results = GetTestResultsFromCompleteOutputFile(); + var consoleOutput = File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default); + var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) + .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); + + var parser = new StreamingStandardOutputTestResultParser(testCases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + var results = parser.TestResults; var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.ManyLinesWithNewlines"); var expectedErrorMessage = "before test 1\nbefore test 2\nExpected: 1\nTo be equal to: 2\nafter test 1\nafter test 2"; testResult.ErrorMessage.Should().Be(expectedErrorMessage); + + CheckStandardOutputResultParser(testCases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void OutputHandling_OneLineWithNewlines_IsParsedCorrectly() { - var results = GetTestResultsFromCompleteOutputFile(); + var consoleOutput = File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default); + var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) + .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); + + var parser = new StreamingStandardOutputTestResultParser(testCases, MockLogger.Object, MockFrameworkReporter.Object); + consoleOutput.ToList().ForEach(parser.ReportLine); + parser.Flush(); + var results = parser.TestResults; var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.Output_OneLineWithNewlines"); var expectedErrorMessage = "before test\nExpected: 1\nTo be equal to: 2\ntest output\nafter test"; testResult.ErrorMessage.Should().Be(expectedErrorMessage); + + CheckStandardOutputResultParser(testCases, consoleOutput, results, parser.CrashedTestCase); } [TestMethod] [TestCategory(Unit)] public void OutputHandling_OneLine_IsParsedCorrectly() { - var results = GetTestResultsFromCompleteOutputFile(); - - var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.OneLine"); - var expectedErrorMessage = - "before test\nExpected: 1\nTo be equal to: 2\nafter test"; - testResult.ErrorMessage.Should().Be(expectedErrorMessage); - } - - private IList GetTestResultsFromCompleteOutputFile() - { + var consoleOutput = File.ReadAllLines(TestResources.Tests_ReleaseX64_Output, Encoding.Default); var testCases = new GoogleTestDiscoverer(MockLogger.Object, MockOptions.Object) .GetTestsFromExecutable(TestResources.Tests_ReleaseX64); var parser = new StreamingStandardOutputTestResultParser(testCases, MockLogger.Object, MockFrameworkReporter.Object); - CompleteStandardOutput.ForEach(parser.ReportLine); + consoleOutput.ToList().ForEach(parser.ReportLine); parser.Flush(); + var results = parser.TestResults; - return parser.TestResults; - } + var testResult = results.Single(tr => tr.DisplayName == "OutputHandling.OneLine"); + var expectedErrorMessage = + "before test\nExpected: 1\nTo be equal to: 2\nafter test"; + testResult.ErrorMessage.Should().Be(expectedErrorMessage); + CheckStandardOutputResultParser(testCases, consoleOutput, results, parser.CrashedTestCase); + } - private IList ComputeTestResults(List consoleOutput) + private List GetTestCases() { var cases = new List { @@ -538,14 +623,17 @@ private IList ComputeTestResults(List consoleOutput) TestDataCreator.ToTestCase("TestMath.AddPasses", TestDataCreator.DummyExecutable, @"c:\users\chris\documents\visual studio 2015\projects\consoleapplication1\consoleapplication1tests\source.cpp") }; + return cases; + } - var parser = new StreamingStandardOutputTestResultParser(cases, MockLogger.Object, MockFrameworkReporter.Object); - consoleOutput.ForEach(parser.ReportLine); - parser.Flush(); + private void CheckStandardOutputResultParser(IEnumerable testCasesRun, IEnumerable consoleOutput, + IList results, TestCase crashedTestCase) + { + var parser = new StandardOutputTestResultParser(testCasesRun, consoleOutput, MockLogger.Object); - return parser.TestResults; + parser.GetTestResults().Should().BeEquivalentTo(results); + parser.CrashedTestCase.Should().Be(crashedTestCase); } - } } \ No newline at end of file diff --git a/GoogleTestAdapter/Core.Tests/TestResults/XmlTestResultParserTests.cs b/GoogleTestAdapter/Core.Tests/TestResults/XmlTestResultParserTests.cs index 1cc9203de..6a83b06aa 100644 --- a/GoogleTestAdapter/Core.Tests/TestResults/XmlTestResultParserTests.cs +++ b/GoogleTestAdapter/Core.Tests/TestResults/XmlTestResultParserTests.cs @@ -115,7 +115,7 @@ public void GetTestResults_Sample1_FindsFailureResult() [TestMethod] [TestCategory(Unit)] - public void GetTestResults_Sample1_FindsParamterizedFailureResult() + public void GetTestResults_Sample1_FindsParameterizedFailureResult() { IEnumerable testCases = TestDataCreator.ToTestCase("ParameterizedTestsTest1/AllEnabledTest.TestInstance/11", TestDataCreator.DummyExecutable, @"someSimpleParameterizedTest.cpp").Yield(); @@ -164,6 +164,20 @@ public void GetTestResults_Sample2_FindsFailureResult() results[0].ErrorStackTrace.Should().Contain(@"c:\prod\gtest-1.7.0\staticallylinkedgoogletests\main.cpp"); } + [TestMethod] + [TestCategory(Unit)] + public void GetTestResults_Umlauts_FindsOneResultAndWarns() + { + IEnumerable testCases = TestDataCreator.CreateDummyTestCases("TheClass.Täst1", "TheClass.Töst1", "TheClass.Täst2"); + + var parser = new XmlTestResultParser(testCases, "someexecutable", TestResources.XmlUmlauts, TestEnvironment.Logger); + List results = parser.GetTestResults(); + + results.Should().ContainSingle(); + AssertTestResultIsPassed(results[0]); + MockLogger.Verify(l => l.LogWarning(It.IsAny()), Times.Once); + } + public static void AssertTestResultIsPassed(Model.TestResult testResult) { diff --git a/GoogleTestAdapter/Core/Runners/TestResultCollector.cs b/GoogleTestAdapter/Core/Runners/TestResultCollector.cs index 9f4d82b5e..f88ee7641 100644 --- a/GoogleTestAdapter/Core/Runners/TestResultCollector.cs +++ b/GoogleTestAdapter/Core/Runners/TestResultCollector.cs @@ -66,7 +66,7 @@ private void CollectResultsFromXmlFile(TestCase[] testCasesRun, string testExecu private void CollectResultsFromConsoleOutput(StandardOutputTestResultParser consoleParser, List testResults) { - List consoleResults = consoleParser.GetTestResults(); + var consoleResults = consoleParser.GetTestResults(); int nrOfCollectedTestResults = 0; foreach (TestResult testResult in consoleResults.Where( tr => !testResults.Exists(tr2 => tr.TestCase.FullyQualifiedName == tr2.TestCase.FullyQualifiedName))) diff --git a/GoogleTestAdapter/Core/TestResults/StandardOutputTestResultParser.cs b/GoogleTestAdapter/Core/TestResults/StandardOutputTestResultParser.cs index 2d5ac6036..834418e58 100644 --- a/GoogleTestAdapter/Core/TestResults/StandardOutputTestResultParser.cs +++ b/GoogleTestAdapter/Core/TestResults/StandardOutputTestResultParser.cs @@ -1,246 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Globalization; +using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using GoogleTestAdapter.Common; +using GoogleTestAdapter.Framework; using GoogleTestAdapter.Model; namespace GoogleTestAdapter.TestResults { public class StandardOutputTestResultParser { - private const string Run = "[ RUN ]"; - public const string Failed = "[ FAILED ]"; - public const string Passed = "[ OK ]"; - public const string Skipped = "[ SKIPPED ]"; - - public const string CrashText = "!! This test has probably CRASHED !!"; - - /// - /// Google Test reports test duration in complete ms. In case of 0ms, - /// we assume the actual duration to be <0.5ms, and thus go for 0.25ms on average - /// (which also makes VS display the duration properly as "<1ms"). - /// 2500 ticks = 0.25ms - /// - public static readonly TimeSpan ShortTestDuration = TimeSpan.FromTicks(2500); - - public TestCase CrashedTestCase { get; private set; } - - private readonly List _consoleOutput; - private readonly List _testCasesRun; - private readonly ILogger _logger; - - - public StandardOutputTestResultParser(IEnumerable testCasesRun, IEnumerable consoleOutput, ILogger logger) - { - _consoleOutput = consoleOutput.ToList(); - _testCasesRun = testCasesRun.ToList(); - _logger = logger; - } - - - public List GetTestResults() + private class DummyTestFrameworkReporter : ITestFrameworkReporter { - var testResults = new List(); - int indexOfNextTestcase = FindIndexOfNextTestcase(0); - while (indexOfNextTestcase >= 0) + public void ReportTestResults(IEnumerable testResults) { - var testResult = CreateTestResult(indexOfNextTestcase); - if (testResult != null) - testResults.Add(testResult); - - indexOfNextTestcase = FindIndexOfNextTestcase(indexOfNextTestcase + 1); - } - return testResults; - } - - - private TestResult CreateTestResult(int indexOfTestcase) - { - int currentLineIndex = indexOfTestcase; - - string line = _consoleOutput[currentLineIndex++]; - string qualifiedTestname = RemovePrefix(line).Trim(); - TestCase testCase = FindTestcase(qualifiedTestname); - if (testCase == null) - { - _logger.DebugWarning($"No known test case for test result of line '{line}' - are you repeating a test run, but tests have changed in the meantime?"); - return null; - } - - if (currentLineIndex >= _consoleOutput.Count) - { - CrashedTestCase = testCase; - return CreateFailedTestResult(testCase, TimeSpan.FromMilliseconds(0), CrashText, ""); - } - - line = _consoleOutput[currentLineIndex]; - SplitLineIfNecessary(ref line, currentLineIndex); - currentLineIndex++; - - - string errorMsg = ""; - while (!(IsFailedLine(line) || IsPassedLine(line) || IsSkippedLine(line)) && currentLineIndex <= _consoleOutput.Count) - { - errorMsg += line + "\n"; - line = currentLineIndex < _consoleOutput.Count ? _consoleOutput[currentLineIndex] : ""; - SplitLineIfNecessary(ref line, currentLineIndex); - currentLineIndex++; - } - if (IsFailedLine(line)) - { - ErrorMessageParser parser = new ErrorMessageParser(errorMsg); - parser.Parse(); - return CreateFailedTestResult(testCase, ParseDuration(line), parser.ErrorMessage, parser.ErrorStackTrace); - } - if (IsPassedLine(line)) - { - return CreatePassedTestResult(testCase, ParseDuration(line)); - } - if (IsSkippedLine(line)) - { - return CreateSkippedTestResult(testCase, ParseDuration(line)); - } - - CrashedTestCase = testCase; - string message = CrashText; - message += errorMsg == "" ? "" : "\nTest output:\n\n" + errorMsg; - return CreateFailedTestResult(testCase, TimeSpan.FromMilliseconds(0), message, ""); - } - - private void SplitLineIfNecessary(ref string line, int currentLineIndex) - { - Match testEndMatch = StreamingStandardOutputTestResultParser.PrefixedLineRegex.Match(line); - if (testEndMatch.Success) - { - string restOfErrorMessage = testEndMatch.Groups[1].Value; - string testEndPart = testEndMatch.Groups[2].Value; - - _consoleOutput.RemoveAt(currentLineIndex); - _consoleOutput.Insert(currentLineIndex, testEndPart); - _consoleOutput.Insert(currentLineIndex, restOfErrorMessage); - - line = restOfErrorMessage; } - } - - private TimeSpan ParseDuration(string line) - { - return ParseDuration(line, _logger); - } - public static TimeSpan ParseDuration(string line, ILogger logger) - { - int durationInMs = 1; - try + public void ReportTestsFound(IEnumerable testCases) { - // duration is a 64-bit number (no decimals) in the user's locale - int indexOfOpeningBracket = line.LastIndexOf('('); - int lengthOfDurationPart = line.Length - indexOfOpeningBracket - 2; - string durationPart = line.Substring(indexOfOpeningBracket + 1, lengthOfDurationPart); - durationPart = durationPart.Replace("ms", "").Trim(); - durationInMs = int.Parse(durationPart, NumberStyles.Number); } - catch (Exception) - { - logger.LogWarning("Could not parse duration in line '" + line + "'"); - } - - return NormalizeDuration(TimeSpan.FromMilliseconds(durationInMs)); - } - - public static TimeSpan NormalizeDuration(TimeSpan duration) - { - return duration.TotalMilliseconds < 1 - ? ShortTestDuration - : duration; - } - - public static TestResult CreatePassedTestResult(TestCase testCase, TimeSpan duration) - { - return new TestResult(testCase) - { - ComputerName = Environment.MachineName, - DisplayName = testCase.DisplayName, - Outcome = TestOutcome.Passed, - Duration = duration - }; - } - - public static TestResult CreateSkippedTestResult(TestCase testCase, TimeSpan duration) - { - return new TestResult(testCase) - { - ComputerName = Environment.MachineName, - DisplayName = testCase.DisplayName, - Outcome = TestOutcome.Skipped, - Duration = duration - }; - } - public static TestResult CreateFailedTestResult(TestCase testCase, TimeSpan duration, string errorMessage, string errorStackTrace) - { - return new TestResult(testCase) + public void ReportTestsStarted(IEnumerable testCases) { - ComputerName = Environment.MachineName, - DisplayName = testCase.DisplayName, - Outcome = TestOutcome.Failed, - ErrorMessage = errorMessage, - ErrorStackTrace = errorStackTrace, - Duration = duration - }; - } - - private int FindIndexOfNextTestcase(int currentIndex) - { - while (currentIndex < _consoleOutput.Count) - { - string line = _consoleOutput[currentIndex]; - if (IsRunLine(line)) - { - return currentIndex; - } - currentIndex++; } - return -1; - } - - private TestCase FindTestcase(string qualifiedTestname) - { - return FindTestcase(qualifiedTestname, _testCasesRun); - } - - public static TestCase FindTestcase(string qualifiedTestname, IList testCasesRun) - { - return testCasesRun.SingleOrDefault(tc => tc.FullyQualifiedName == qualifiedTestname); } - public static bool IsRunLine(string line) - { - return line.StartsWith(Run); - } + public TestCase CrashedTestCase { get; private set; } - public static bool IsPassedLine(string line) - { - return line.StartsWith(Passed); - } + private readonly List _consoleOutput; + private readonly List _testCasesRun; + private readonly ILogger _logger; - public static bool IsFailedLine(string line) + public StandardOutputTestResultParser(IEnumerable testCasesRun, IEnumerable consoleOutput, ILogger logger) { - return line.StartsWith(Failed); + _consoleOutput = consoleOutput.ToList(); + _testCasesRun = testCasesRun.ToList(); + _logger = logger; } - public static bool IsSkippedLine(string line) + public IList GetTestResults() { - return line.StartsWith(Skipped); - } + var streamingParser = new StreamingStandardOutputTestResultParser(_testCasesRun, _logger, new DummyTestFrameworkReporter()); + _consoleOutput.ForEach(l => streamingParser.ReportLine(l)); + streamingParser.Flush(); - public static string RemovePrefix(string line) - { - return line.Substring(Run.Length); + CrashedTestCase = streamingParser.CrashedTestCase; + return streamingParser.TestResults; } - } } \ No newline at end of file diff --git a/GoogleTestAdapter/Core/TestResults/StreamingStandardOutputTestResultParser.cs b/GoogleTestAdapter/Core/TestResults/StreamingStandardOutputTestResultParser.cs index 4c45aa797..6ed27bc35 100644 --- a/GoogleTestAdapter/Core/TestResults/StreamingStandardOutputTestResultParser.cs +++ b/GoogleTestAdapter/Core/TestResults/StreamingStandardOutputTestResultParser.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using GoogleTestAdapter.Common; @@ -11,7 +12,22 @@ namespace GoogleTestAdapter.TestResults { public class StreamingStandardOutputTestResultParser { - public static readonly Regex PrefixedLineRegex; + private const string Run = "[ RUN ]"; + private const string Failed = "[ FAILED ]"; + private const string Passed = "[ OK ]"; + private const string Skipped = "[ SKIPPED ]"; + + public const string CrashText = "!! This test has probably CRASHED !!"; + + /// + /// Google Test reports test duration in complete ms. In case of 0ms, + /// we assume the actual duration to be <0.5ms, and thus go for 0.25ms on average + /// (which also makes VS display the duration properly as "<1ms"). + /// 2500 ticks = 0.25ms + /// + public static readonly TimeSpan ShortTestDuration = TimeSpan.FromTicks(2500); + + private static readonly Regex PrefixedLineRegex; public TestCase CrashedTestCase { get; private set; } public IList TestResults { get; } = new List(); @@ -24,8 +40,8 @@ public class StreamingStandardOutputTestResultParser static StreamingStandardOutputTestResultParser() { - string passedMarker = Regex.Escape(StandardOutputTestResultParser.Passed); - string failedMarker = Regex.Escape(StandardOutputTestResultParser.Failed); + string passedMarker = Regex.Escape(Passed); + string failedMarker = Regex.Escape(Failed); PrefixedLineRegex = new Regex($"(.+)((?:{passedMarker}|{failedMarker}).*)", RegexOptions.Compiled); } @@ -57,7 +73,7 @@ public void ReportLine(string line) private void DoReportLine(string line) { - if (StandardOutputTestResultParser.IsRunLine(line)) + if (IsRunLine(line)) { if (_consoleOutput.Count > 0) { @@ -80,8 +96,8 @@ public void Flush() private void ReportTestStart(string line) { - string qualifiedTestname = StandardOutputTestResultParser.RemovePrefix(line).Trim(); - TestCase testCase = StandardOutputTestResultParser.FindTestcase(qualifiedTestname, _testCasesRun); + string qualifiedTestname = RemovePrefix(line).Trim(); + TestCase testCase = FindTestcase(qualifiedTestname, _testCasesRun); if (testCase != null) _reporter.ReportTestsStarted(testCase.Yield()); } @@ -100,15 +116,15 @@ private TestResult CreateTestResult() { int currentLineIndex = 0; while (currentLineIndex < _consoleOutput.Count && - !StandardOutputTestResultParser.IsRunLine(_consoleOutput[currentLineIndex])) + !IsRunLine(_consoleOutput[currentLineIndex])) currentLineIndex++; if (currentLineIndex == _consoleOutput.Count) return null; string line = _consoleOutput[currentLineIndex++]; - string qualifiedTestname = StandardOutputTestResultParser.RemovePrefix(line).Trim(); - TestCase testCase = StandardOutputTestResultParser.FindTestcase(qualifiedTestname, _testCasesRun); + string qualifiedTestname = RemovePrefix(line).Trim(); + TestCase testCase = FindTestcase(qualifiedTestname, _testCasesRun); if (testCase == null) { _logger.DebugWarning($"No known test case for test result of line '{line}'' - are you repeating a test run, but tests have changed in the meantime?"); @@ -118,53 +134,46 @@ private TestResult CreateTestResult() if (currentLineIndex == _consoleOutput.Count) { CrashedTestCase = testCase; - return StandardOutputTestResultParser.CreateFailedTestResult( + return CreateFailedTestResult( testCase, TimeSpan.FromMilliseconds(0), - StandardOutputTestResultParser.CrashText, + CrashText, ""); } line = _consoleOutput[currentLineIndex++]; string errorMsg = ""; - while ( - !(StandardOutputTestResultParser.IsFailedLine(line) - || StandardOutputTestResultParser.IsPassedLine(line) - || StandardOutputTestResultParser.IsSkippedLine(line)) + while (!(IsFailedLine(line) || IsPassedLine(line) || IsSkippedLine(line)) && currentLineIndex <= _consoleOutput.Count) { errorMsg += line + "\n"; line = currentLineIndex < _consoleOutput.Count ? _consoleOutput[currentLineIndex] : ""; currentLineIndex++; } - if (StandardOutputTestResultParser.IsFailedLine(line)) + if (IsFailedLine(line)) { ErrorMessageParser parser = new ErrorMessageParser(errorMsg); parser.Parse(); - return StandardOutputTestResultParser.CreateFailedTestResult( + return CreateFailedTestResult( testCase, - StandardOutputTestResultParser.ParseDuration(line, _logger), + ParseDuration(line, _logger), parser.ErrorMessage, parser.ErrorStackTrace); } - if (StandardOutputTestResultParser.IsPassedLine(line)) + if (IsPassedLine(line)) { - return StandardOutputTestResultParser.CreatePassedTestResult( - testCase, - StandardOutputTestResultParser.ParseDuration(line, _logger)); + return CreatePassedTestResult(testCase, ParseDuration(line, _logger)); } - if (StandardOutputTestResultParser.IsSkippedLine(line)) + if (IsSkippedLine(line)) { - return StandardOutputTestResultParser.CreateSkippedTestResult( - testCase, - StandardOutputTestResultParser.ParseDuration(line, _logger)); + return CreateSkippedTestResult(testCase, ParseDuration(line, _logger)); } CrashedTestCase = testCase; - string message = StandardOutputTestResultParser.CrashText; + string message = CrashText; message += errorMsg == "" ? "" : $"\nTest output:\n\n{errorMsg}"; - TestResult result = StandardOutputTestResultParser.CreateFailedTestResult( + TestResult result = CreateFailedTestResult( testCase, TimeSpan.FromMilliseconds(0), message, @@ -172,6 +181,97 @@ private TestResult CreateTestResult() return result; } + private TimeSpan ParseDuration(string line, ILogger logger) + { + int durationInMs = 1; + try + { + // duration is a 64-bit number (no decimals) in the user's locale + int indexOfOpeningBracket = line.LastIndexOf('('); + int lengthOfDurationPart = line.Length - indexOfOpeningBracket - 2; + string durationPart = line.Substring(indexOfOpeningBracket + 1, lengthOfDurationPart); + durationPart = durationPart.Replace("ms", "").Trim(); + durationInMs = Int32.Parse(durationPart, NumberStyles.Number); + } + catch (Exception) + { + logger.LogWarning("Could not parse duration in line '" + line + "'"); + } + + return NormalizeDuration(TimeSpan.FromMilliseconds(durationInMs)); + } + + public static TimeSpan NormalizeDuration(TimeSpan duration) + { + return duration.TotalMilliseconds < 1 + ? ShortTestDuration + : duration; + } + + private TestResult CreatePassedTestResult(TestCase testCase, TimeSpan duration) + { + return new TestResult(testCase) + { + ComputerName = Environment.MachineName, + DisplayName = testCase.DisplayName, + Outcome = TestOutcome.Passed, + Duration = duration + }; + } + + private TestResult CreateSkippedTestResult(TestCase testCase, TimeSpan duration) + { + return new TestResult(testCase) + { + ComputerName = Environment.MachineName, + DisplayName = testCase.DisplayName, + Outcome = TestOutcome.Skipped, + Duration = duration + }; + } + + private TestResult CreateFailedTestResult(TestCase testCase, TimeSpan duration, string errorMessage, string errorStackTrace) + { + return new TestResult(testCase) + { + ComputerName = Environment.MachineName, + DisplayName = testCase.DisplayName, + Outcome = TestOutcome.Failed, + ErrorMessage = errorMessage, + ErrorStackTrace = errorStackTrace, + Duration = duration + }; + } + + private TestCase FindTestcase(string qualifiedTestname, IList testCasesRun) + { + return testCasesRun.SingleOrDefault(tc => tc.FullyQualifiedName == qualifiedTestname); + } + + private bool IsRunLine(string line) + { + return line.StartsWith(Run); + } + + private bool IsPassedLine(string line) + { + return line.StartsWith(Passed); + } + + private bool IsFailedLine(string line) + { + return line.StartsWith(Failed); + } + + private bool IsSkippedLine(string line) + { + return line.StartsWith(Skipped); + } + + private string RemovePrefix(string line) + { + return line.Substring(Run.Length); + } } } \ No newline at end of file diff --git a/GoogleTestAdapter/Core/TestResults/XmlTestResultParser.cs b/GoogleTestAdapter/Core/TestResults/XmlTestResultParser.cs index d1ef25c8d..23b7a9baa 100644 --- a/GoogleTestAdapter/Core/TestResults/XmlTestResultParser.cs +++ b/GoogleTestAdapter/Core/TestResults/XmlTestResultParser.cs @@ -207,7 +207,7 @@ private string GetQualifiedName(XmlNode testcaseNode) private TimeSpan ParseDuration(string durationInSeconds) { return - StandardOutputTestResultParser + StreamingStandardOutputTestResultParser .NormalizeDuration(TimeSpan.FromSeconds(double.Parse(durationInSeconds, NumberFormatInfo))); } diff --git a/GoogleTestAdapter/Tests.Common/Resources/TestData/Umlauts.xml b/GoogleTestAdapter/Tests.Common/Resources/TestData/Umlauts.xml new file mode 100644 index 000000000..aa73caa4f --- /dev/null +++ b/GoogleTestAdapter/Tests.Common/Resources/TestData/Umlauts.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/GoogleTestAdapter/Tests.Common/TestResources.cs b/GoogleTestAdapter/Tests.Common/TestResources.cs index d9696661f..9bc2816d3 100644 --- a/GoogleTestAdapter/Tests.Common/TestResources.cs +++ b/GoogleTestAdapter/Tests.Common/TestResources.cs @@ -66,6 +66,7 @@ public static class TestResources public const string XmlFile1 = TestdataDir + @"SampleResult1.xml"; public const string XmlFile2 = TestdataDir + @"SampleResult2.xml"; + public const string XmlUmlauts = TestdataDir + @"Umlauts.xml"; public const string XmlFileBroken = TestdataDir + @"SampleResult1_Broken.xml"; // ReSharper disable once InconsistentNaming public const string XmlFileBroken_InvalidStatusAttibute = TestdataDir + @"SampleResult1 _Broken_InvalidStatusAttribute.xml"; diff --git a/GoogleTestAdapter/Tests.Common/Tests.Common.csproj b/GoogleTestAdapter/Tests.Common/Tests.Common.csproj index 4079c51f7..a2bf38328 100644 --- a/GoogleTestAdapter/Tests.Common/Tests.Common.csproj +++ b/GoogleTestAdapter/Tests.Common/Tests.Common.csproj @@ -137,6 +137,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest