Skip to content

Commit

Permalink
Merge pull request #263 from csoltenborn/#261_parser_code_duplication
Browse files Browse the repository at this point in the history
remove parser code duplication
  • Loading branch information
csoltenborn authored Feb 6, 2019
2 parents bee333f + 96f5391 commit 829d16b
Show file tree
Hide file tree
Showing 11 changed files with 503 additions and 975 deletions.
1 change: 0 additions & 1 deletion GoogleTestAdapter/Core.Tests/Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
<Compile Include="TestCases\TestCaseFactoryTests.cs" />
<Compile Include="TestCases\ListTestsParserTests.cs" />
<Compile Include="TestResults\StreamingStandardOutputTestResultParserTests.cs" />
<Compile Include="TestResults\StandardOutputTestResultParserTests.cs" />
<Compile Include="TestResults\ErrorMessageParserTests.cs" />
<Compile Include="TestResults\XmlTestResultParserTests.cs" />
<Compile Include="Scheduling\DurationBasedTestsSplitterTests.cs" />
Expand Down

This file was deleted.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public void GetTestResults_Sample1_FindsFailureResult()

[TestMethod]
[TestCategory(Unit)]
public void GetTestResults_Sample1_FindsParamterizedFailureResult()
public void GetTestResults_Sample1_FindsParameterizedFailureResult()
{
IEnumerable<Model.TestCase> testCases = TestDataCreator.ToTestCase("ParameterizedTestsTest1/AllEnabledTest.TestInstance/11", TestDataCreator.DummyExecutable, @"someSimpleParameterizedTest.cpp").Yield();

Expand Down Expand Up @@ -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<Model.TestCase> testCases = TestDataCreator.CreateDummyTestCases("TheClass.Täst1", "TheClass.Töst1", "TheClass.Täst2");

var parser = new XmlTestResultParser(testCases, "someexecutable", TestResources.XmlUmlauts, TestEnvironment.Logger);
List<Model.TestResult> results = parser.GetTestResults();

results.Should().ContainSingle();
AssertTestResultIsPassed(results[0]);
MockLogger.Verify(l => l.LogWarning(It.IsAny<string>()), Times.Once);
}


public static void AssertTestResultIsPassed(Model.TestResult testResult)
{
Expand Down
2 changes: 1 addition & 1 deletion GoogleTestAdapter/Core/Runners/TestResultCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private void CollectResultsFromXmlFile(TestCase[] testCasesRun, string testExecu

private void CollectResultsFromConsoleOutput(StandardOutputTestResultParser consoleParser, List<TestResult> testResults)
{
List<TestResult> 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)))
Expand Down
236 changes: 20 additions & 216 deletions GoogleTestAdapter/Core/TestResults/StandardOutputTestResultParser.cs
Original file line number Diff line number Diff line change
@@ -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 !!";

/// <summary>
/// Google Test reports test duration in complete ms. In case of 0ms,
/// we assume the actual duration to be &lt;0.5ms, and thus go for 0.25ms on average
/// (which also makes VS display the duration properly as "&lt;1ms").
/// 2500 ticks = 0.25ms
/// </summary>
public static readonly TimeSpan ShortTestDuration = TimeSpan.FromTicks(2500);

public TestCase CrashedTestCase { get; private set; }

private readonly List<string> _consoleOutput;
private readonly List<TestCase> _testCasesRun;
private readonly ILogger _logger;


public StandardOutputTestResultParser(IEnumerable<TestCase> testCasesRun, IEnumerable<string> consoleOutput, ILogger logger)
{
_consoleOutput = consoleOutput.ToList();
_testCasesRun = testCasesRun.ToList();
_logger = logger;
}


public List<TestResult> GetTestResults()
private class DummyTestFrameworkReporter : ITestFrameworkReporter
{
var testResults = new List<TestResult>();
int indexOfNextTestcase = FindIndexOfNextTestcase(0);
while (indexOfNextTestcase >= 0)
public void ReportTestResults(IEnumerable<TestResult> 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<TestCase> 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<TestCase> 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<TestCase> 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<string> _consoleOutput;
private readonly List<TestCase> _testCasesRun;
private readonly ILogger _logger;

public static bool IsFailedLine(string line)
public StandardOutputTestResultParser(IEnumerable<TestCase> testCasesRun, IEnumerable<string> consoleOutput, ILogger logger)
{
return line.StartsWith(Failed);
_consoleOutput = consoleOutput.ToList();
_testCasesRun = testCasesRun.ToList();
_logger = logger;
}

public static bool IsSkippedLine(string line)
public IList<TestResult> 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;
}

}

}
Loading

0 comments on commit 829d16b

Please sign in to comment.