/// <summary> /// Default constructor accepting only test run manager instance, rest of the requirements assume default values /// </summary> public PythonTestResultParser(ITestRunManager testRunManager, ITraceLogger logger, ITelemetryDataCollector telemetry) : base(testRunManager, logger, telemetry) { Logger.Info("PythonTestResultParser : Starting python test result parser."); Telemetry.AddOrUpdate(PythonTelemetryConstants.Initialize, true, PythonTelemetryConstants.EventArea); _state = ParserState.ExpectingTestResults; _currentTestRun = new TestRun($"{Name}/{Version}", "Python", _currentTestRunId); }
/// <summary> /// Detailed constructor where specified logger and telemetry data collector are initialized along with test run manager /// </summary> /// <param name="testRunPublisher"></param> /// <param name="diagnosticDataCollector"></param> /// <param name="telemetryDataCollector"></param> public MochaTestResultParser(ITestRunManager testRunManager, ITraceLogger logger, ITelemetryDataCollector telemetryDataCollector) : base(testRunManager, logger, telemetryDataCollector) { Logger.Info("MochaTestResultParser : Starting mocha test result parser."); Telemetry.AddOrUpdate(MochaTelemetryConstants.Initialize, true, MochaTelemetryConstants.EventArea); // Initialize the starting state of the parser var testRun = new TestRun($"{Name}/{Version}", "Mocha", 1); _stateContext = new MochaParserStateContext(testRun); _currentState = MochaParserStates.ExpectingTestResults; }
/// <summary> /// Parses input data to detect python test result. /// </summary> /// <param name="logData">Data to be parsed.</param> public override void Parse(LogData logData) { // Validate data input if (!IsValidInput(logData.Line)) { return; } // TODO: Fix an appropriate threshold based on performance on hosted machine with load using (var timer = new SimpleTimer("PythonParserParseOperation", PythonTelemetryConstants.EventArea, PythonTelemetryConstants.PythonParserTotalTime, logData.LineNumber, Logger, Telemetry, ParseOperationPermissibleThreshold)) { try { Telemetry.AddOrUpdate(PythonTelemetryConstants.TotalLinesParsed, logData.LineNumber, PythonTelemetryConstants.EventArea); switch (_state) { case ParserState.ExpectingSummary: if (string.IsNullOrWhiteSpace(logData.Line)) { return; } // Summary Test count and total time should have already been parsed // Try to parse test outcome, number of tests for each outcome if (TryParseSummaryOutcome(logData)) { PublishAndReset(logData); return; } // Summary was not parsed, reset the parser and try parse again. Reset(logData); Parse(logData); break; case ParserState.ExpectingFailedResults: // Try to parse for failed results and summary // If summary is parsed, change the state if (TryParseForFailedResult(logData)) { _stackTraceLinesAllowedToParse = 50; return; } if (TryParseSummaryTestAndTime(logData)) { _state = ParserState.ExpectingSummary; Logger.Info($"PythonTestResultParser : ExpectingFailedResults: transitioned to state ExpectingSummary at line {logData.LineNumber}"); return; } // Not expected, as Summary has not been encountered yet // If a new TestResult is found, reset the parser and Parse again if (TryParseTestResult(logData)) { Logger.Error($"PythonTestResultParser : Parse : Expecting failed result or summary but found new test result at line {logData.LineNumber}."); Telemetry.AddAndAggregate(PythonTelemetryConstants.SummaryOrFailedTestsNotFound, new List <int> { _currentTestRunId }, PythonTelemetryConstants.EventArea); Reset(logData); Parse(logData); } TryParseStackTrace(logData); break; case ParserState.ExpectingTestResults: default: if (TryParseTestResult(logData)) { return; } // Change the state and clear the partial result if failed result or summary is found if (TryParseForFailedResult(logData)) { _partialTestResult = null; _state = ParserState.ExpectingFailedResults; _stackTraceLinesAllowedToParse = 50; Logger.Info($"PythonTestResultParser : ExpectingTestResults: transitioned to state ExpectingFailedResults at line {logData.LineNumber}"); return; } if (TryParseSummaryTestAndTime(logData)) { _partialTestResult = null; _state = ParserState.ExpectingSummary; Logger.Info($"PythonTestResultParser : ExpectingTestResults: transitioned to state ExpectingSummary at line {logData.LineNumber}"); return; } break; } } catch (RegexMatchTimeoutException regexMatchTimeoutException) { Logger.Warning($"PythonTestResultParser : Parse : failed due to timeout with exception { regexMatchTimeoutException } at line {logData.LineNumber}"); Telemetry.AddAndAggregate(PythonTelemetryConstants.RegexTimeout, new List <string> { "UnknownRegex" }, PythonTelemetryConstants.EventArea); } catch (Exception ex) { Logger.Error($"PythonTestResultParser : Parse : Unable to parse the log line {logData.Line} with exception {ex.ToString()} at line {logData.LineNumber}"); Telemetry.AddAndAggregate(PythonTelemetryConstants.ParseException, new List <string> { ex.Message }, PythonTelemetryConstants.EventArea); Reset(logData); // Rethrow the exception so that the invoker of Parser is notified of a failure throw; } } }
/// <inheritdoc/> public override void Parse(LogData logData) { if (logData == null || logData.Line == null) { Logger.Error("JestTestResultParser : Parse : Input line was null."); return; } // TODO: Fix an appropriate threshold based on performance on hosted machine with load using (var timer = new SimpleTimer("JestParserParseOperation", JestTelemetryConstants.EventArea, JestTelemetryConstants.JestParserTotalTime, logData.LineNumber, Logger, Telemetry, ParseOperationPermissibleThreshold)) { try { _stateContext.CurrentLineNumber = logData.LineNumber; Telemetry.AddOrUpdate(JestTelemetryConstants.TotalLinesParsed, logData.LineNumber, JestTelemetryConstants.EventArea); // State model for the jest parser that defines the Regexs to match against in each state // Each state re-orders the Regexs based on the frequency of expected matches switch (_currentState) { // This state primarily looks for test run start indicator and // transitions to the next one after encountering one case JestParserStates.ExpectingTestRunStart: if (AttemptMatch(TestRunStart, logData)) { return; } break; // This state primarily looks for test results and transitions // to the next one after a stack trace or summary is encountered case JestParserStates.ExpectingTestResults: if (AttemptMatch(ExpectingTestResults, logData)) { return; } break; // This state primarily looks for stack traces/failed test cases // and transitions on encountering summary case JestParserStates.ExpectingStackTraces: if (AttemptMatch(ExpectingStackTraces, logData)) { return; } break; // This state primarily looks for test run summary // and transitions back to testresults state on encountering // another test run start marker indicating tests being run from // more than one file case JestParserStates.ExpectingTestRunSummary: if (AttemptMatch(ExpectingTestRunSummary, logData)) { return; } break; } } catch (Exception e) { Logger.Error($"JestTestResultParser : Parse : Failed with exception {e}."); // This might start taking a lot of space if each and every parse operation starts throwing // But if that happens then there's a lot more stuff broken. Telemetry.AddAndAggregate(JestTelemetryConstants.Exceptions, new List <string> { e.Message }, JestTelemetryConstants.EventArea); // Rethrowing this so that the plugin is aware that the parser is erroring out // Ideally this would never should happen throw; } } }
/// <inheritdoc/> public override void Parse(LogData logData) { if (logData == null || logData.Line == null) { Logger.Error("MochaTestResultParser : Parse : Input line was null."); return; } // TODO: Fix an appropriate threshold based on performance on hosted machine with load using (var timer = new SimpleTimer("MochaParserParseOperation", MochaTelemetryConstants.EventArea, MochaTelemetryConstants.MochaParserTotalTime, logData.LineNumber, Logger, Telemetry, ParseOperationPermissibleThreshold)) { try { _stateContext.CurrentLineNumber = logData.LineNumber; Telemetry.AddOrUpdate(MochaTelemetryConstants.TotalLinesParsed, logData.LineNumber, MochaTelemetryConstants.EventArea); // State model for the mocha parser that defines the Regexs to match against in each state // Each state re-orders the Regexs based on the frequency of expected matches switch (_currentState) { // This state primarily looks for test results // and transitions to the next one after a line of summary is encountered case MochaParserStates.ExpectingTestResults: if (AttemptMatch(ExpectingTestResults, logData)) { return; } break; // This state primarily looks for test run summary // If failed tests were found to be present transitions to the next one to look for stack traces // else goes back to the first state after publishing the run case MochaParserStates.ExpectingTestRunSummary: if (AttemptMatch(ExpectingTestRunSummary, logData)) { return; } break; // This state primarily looks for stack traces // If any other match occurs before all the expected stack traces are found, // it fires telemetry for unexpected behavior but moves on to the next test run case MochaParserStates.ExpectingStackTraces: if (AttemptMatch(ExpectingStackTraces, logData)) { return; } break; } } catch (Exception e) { Logger.Error($"MochaTestResultParser : Parse : Failed with exception {e}."); // This might start taking a lot of space if each and every parse operation starts throwing // But if that happens then there's a lot more stuff broken. Telemetry.AddAndAggregate("Exceptions", new List <string> { e.Message }, MochaTelemetryConstants.EventArea); // Rethrowing this so that the plugin is aware that the parser is erroring out // Ideally this never should happen throw; } } }