-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #263 from csoltenborn/#261_parser_code_duplication
remove parser code duplication
- Loading branch information
Showing
11 changed files
with
503 additions
and
975 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
489 changes: 0 additions & 489 deletions
489
GoogleTestAdapter/Core.Tests/TestResults/StandardOutputTestResultParserTests.cs
This file was deleted.
Oops, something went wrong.
564 changes: 326 additions & 238 deletions
564
GoogleTestAdapter/Core.Tests/TestResults/StreamingStandardOutputTestResultParserTests.cs
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
236 changes: 20 additions & 216 deletions
236
GoogleTestAdapter/Core/TestResults/StandardOutputTestResultParser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <0.5ms, and thus go for 0.25ms on average | ||
/// (which also makes VS display the duration properly as "<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; | ||
} | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.