Best Python code snippet using gabbi_python
gtest_parser.py
Source:gtest_parser.py
1# Copyright (c) 2017 The Chromium OS Authors. All rights reserved.2# Use of this source code is governed by a BSD-style license that can be3# found in the LICENSE file.4"""Utility class to parse the output of a gtest suite run."""5import re6class gtest_parser(object):7 """This class knows how to understand GTest test output.8 The code was borrowed with minor changes from chrome utility gtest_command.9 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/10 log_parser/gtest_command.py?view=markup11 """12 def __init__(self):13 # State tracking for log parsing14 self._current_test = ''15 self._failure_description = []16 self._current_suppression_hash = ''17 self._current_suppression = []18 # Line number currently being processed.19 self._line_number = 020 # List of parsing errors, as human-readable strings.21 self.internal_error_lines = []22 # Tests are stored here as 'test.name': (status, [description]).23 # The status should be one of ('started', 'OK', 'failed', 'timeout').24 # The description is a list of lines detailing the test's error, as25 # reported in the log.26 self._test_status = {}27 # Suppressions are stored here as 'hash': [suppression].28 self._suppressions = {}29 # This may be either text or a number. It will be used in the phrase30 # '%s disabled' or '%s flaky' on the waterfall display.31 self.disabled_tests = 032 self.flaky_tests = 033 # Regular expressions for parsing GTest logs. Test names look like34 # SomeTestCase.SomeTest35 # SomeName/SomeTestCase.SomeTest/136 # This regexp also matches SomeName.SomeTest/1, which should be37 # harmless.38 test_name_regexp = r'((\w+/)?\w+\.\w+(\.\w+)?(/\d+)?)'39 self._test_start = re.compile('\[\s+RUN\s+\] ' + test_name_regexp)40 self._test_ok = re.compile('\[\s+OK\s+\] ' + test_name_regexp)41 self._test_fail = re.compile('\[\s+FAILED\s+\] ' + test_name_regexp)42 self._test_timeout = re.compile(43 'Test timeout \([0-9]+ ms\) exceeded for ' + test_name_regexp)44 self._disabled = re.compile(' YOU HAVE (\d+) DISABLED TEST')45 self._flaky = re.compile(' YOU HAVE (\d+) FLAKY TEST')46 self._suppression_start = re.compile(47 'Suppression \(error hash=#([0-9A-F]+)#\):')48 self._suppression_end = re.compile('^}\s*$')49 self._master_name_re = re.compile('\[Running for master: "([^"]*)"')50 self.master_name = ''51 self._error_logging_start_re = re.compile('=' * 70)52 self._error_logging_test_name_re = re.compile(53 '[FAIL|ERROR]: ' + test_name_regexp)54 self._error_logging_end_re = re.compile('-' * 70)55 self._error_logging_first_dash_found = False56 def _TestsByStatus(self, status, include_fails, include_flaky):57 """Returns list of tests with the given status.58 Args:59 status: test results status to search for.60 include_fails: If False, tests containing 'FAILS_' anywhere in61 their names will be excluded from the list.62 include_flaky: If False, tests containing 'FLAKY_' anywhere in63 their names will be excluded from the list.64 Returns:65 List of tests with the status.66 """67 test_list = [x[0] for x in self._test_status.items()68 if self._StatusOfTest(x[0]) == status]69 if not include_fails:70 test_list = [x for x in test_list if x.find('FAILS_') == -1]71 if not include_flaky:72 test_list = [x for x in test_list if x.find('FLAKY_') == -1]73 return test_list74 def _StatusOfTest(self, test):75 """Returns the status code for the given test, or 'not known'."""76 test_status = self._test_status.get(test, ('not known', []))77 return test_status[0]78 def _RecordError(self, line, reason):79 """Record a log line that produced a parsing error.80 Args:81 line: text of the line at which the error occurred.82 reason: a string describing the error.83 """84 self.internal_error_lines.append("%s: %s [%s]" % (self._line_number,85 line.strip(),86 reason))87 def TotalTests(self):88 """Returns the number of parsed tests."""89 return len(self._test_status)90 def PassedTests(self):91 """Returns list of tests that passed."""92 return self._TestsByStatus('OK', False, False)93 def FailedTests(self, include_fails=False, include_flaky=False):94 """Returns list of tests that failed, timed out, or didn't finish.95 This list will be incorrect until the complete log has been processed,96 because it will show currently running tests as having failed.97 Args:98 include_fails: If true, all failing tests with FAILS_ in their99 names will be included. Otherwise, they will only be included100 if they crashed.101 include_flaky: If true, all failing tests with FLAKY_ in their102 names will be included. Otherwise, they will only be included103 if they crashed.104 Returns:105 List of failed tests.106 """107 return (self._TestsByStatus('failed', include_fails, include_flaky) +108 self._TestsByStatus('timeout', include_fails, include_flaky) +109 self._TestsByStatus('started', include_fails, include_flaky))110 def FailureDescription(self, test):111 """Returns a list containing the failure description for the given test.112 If the test didn't fail or timeout, returns [].113 Args:114 test: Name to test to find failure reason.115 Returns:116 List of test name, and failure string.117 """118 test_status = self._test_status.get(test, ('', []))119 return test_status[1]120 def SuppressionHashes(self):121 """Returns list of suppression hashes found in the log."""122 return self._suppressions.keys()123 def Suppression(self, suppression_hash):124 """Returns a list containing the suppression for a given hash.125 If the suppression hash doesn't exist, returns [].126 Args:127 suppression_hash: name of hash.128 Returns:129 List of suppression for the hash.130 """131 return self._suppressions.get(suppression_hash, [])132 def ProcessLogLine(self, line):133 """This is called once with each line of the test log."""134 # Track line number for error messages.135 self._line_number += 1136 if not self.master_name:137 results = self._master_name_re.search(line)138 if results:139 self.master_name = results.group(1)140 # Is it a line reporting disabled tests?141 results = self._disabled.search(line)142 if results:143 try:144 disabled = int(results.group(1))145 except ValueError:146 disabled = 0147 if disabled > 0 and isinstance(self.disabled_tests, int):148 self.disabled_tests += disabled149 else:150 # If we can't parse the line, at least give a heads-up. This is151 # a safety net for a case that shouldn't happen but isn't a152 # fatal error.153 self.disabled_tests = 'some'154 return155 # Is it a line reporting flaky tests?156 results = self._flaky.search(line)157 if results:158 try:159 flaky = int(results.group(1))160 except ValueError:161 flaky = 0162 if flaky > 0 and isinstance(self.flaky_tests, int):163 self.flaky_tests = flaky164 else:165 # If we can't parse the line, at least give a heads-up. This is166 # a safety net for a case that shouldn't happen but isn't a167 # fatal error.168 self.flaky_tests = 'some'169 return170 # Is it the start of a test?171 results = self._test_start.search(line)172 if results:173 test_name = results.group(1)174 if test_name in self._test_status:175 self._RecordError(line, 'test started more than once')176 return177 if self._current_test:178 status = self._StatusOfTest(self._current_test)179 if status in ('OK', 'failed', 'timeout'):180 self._RecordError(line, 'start while in status %s' % status)181 return182 if status not in ('failed', 'timeout'):183 self._test_status[self._current_test] = (184 'failed', self._failure_description)185 self._test_status[test_name] = ('started', ['Did not complete.'])186 self._current_test = test_name187 self._failure_description = []188 return189 # Is it a test success line?190 results = self._test_ok.search(line)191 if results:192 test_name = results.group(1)193 status = self._StatusOfTest(test_name)194 if status != 'started':195 self._RecordError(line, 'success while in status %s' % status)196 return197 self._test_status[test_name] = ('OK', [])198 self._failure_description = []199 self._current_test = ''200 return201 # Is it a test failure line?202 results = self._test_fail.search(line)203 if results:204 test_name = results.group(1)205 status = self._StatusOfTest(test_name)206 if status not in ('started', 'failed', 'timeout'):207 self._RecordError(line, 'failure while in status %s' % status)208 return209 # Don't overwrite the failure description when a failing test is210 # listed a second time in the summary, or if it was already211 # recorded as timing out.212 if status not in ('failed', 'timeout'):213 self._test_status[test_name] = ('failed',214 self._failure_description)215 self._failure_description = []216 self._current_test = ''217 return218 # Is it a test timeout line?219 results = self._test_timeout.search(line)220 if results:221 test_name = results.group(1)222 status = self._StatusOfTest(test_name)223 if status not in ('started', 'failed'):224 self._RecordError(line, 'timeout while in status %s' % status)225 return226 self._test_status[test_name] = (227 'timeout', self._failure_description + ['Killed (timed out).'])228 self._failure_description = []229 self._current_test = ''230 return231 # Is it the start of a new valgrind suppression?232 results = self._suppression_start.search(line)233 if results:234 suppression_hash = results.group(1)235 if suppression_hash in self._suppressions:236 self._RecordError(line, 'suppression reported more than once')237 return238 self._suppressions[suppression_hash] = []239 self._current_suppression_hash = suppression_hash240 self._current_suppression = [line]241 return242 # Is it the end of a valgrind suppression?243 results = self._suppression_end.search(line)244 if results and self._current_suppression_hash:245 self._current_suppression.append(line)246 self._suppressions[self._current_suppression_hash] = (247 self._current_suppression)248 self._current_suppression_hash = ''249 self._current_suppression = []250 return251 # Is it the start of a test summary error message?252 results = self._error_logging_test_name_re.search(line)253 if results:254 test_name = results.group(1)255 self._test_status[test_name] = ('failed', ['Output not found.'])256 self._current_test = test_name257 self._failure_description = []258 self._error_logging_first_dash_found = False259 return260 # Is it the start of the next test summary signaling the end261 # of the previous message?262 results = self._error_logging_start_re.search(line)263 if results and self._current_test:264 self._test_status[self._current_test] = ('failed',265 self._failure_description)266 self._failure_description = []267 self._current_test = ''268 return269 # Is it the end of the extra test failure summaries?270 results = self._error_logging_end_re.search(line)271 if results and self._current_test:272 if self._error_logging_first_dash_found:273 self._test_status[self._current_test] = (274 'failed', self._failure_description)275 self._failure_description = []276 self._current_test = ''277 self._error_logging_first_dash_found = True278 return279 # Random line: if we're in a suppression, collect it. Suppressions are280 # generated after all tests are finished, so this should always belong281 # to the current suppression hash.282 if self._current_suppression_hash:283 self._current_suppression.append(line)284 return285 # Random line: if we're in a test, collect it for the failure286 # description. Tests may run simultaneously, so this might be off, but287 # it's worth a try.288 if self._current_test:...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!