Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: scripts/master/log_parser/gtest_command.py

Issue 9134001: Add parsing of "Failing tests:" from gtest output. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build/
Patch Set: '' Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """A buildbot command for running and interpreting GTest tests.""" 5 """A buildbot command for running and interpreting GTest tests."""
6 6
7 import re 7 import re
8 from buildbot.steps import shell 8 from buildbot.steps import shell
9 from buildbot.status import builder 9 from buildbot.status import builder
10 from buildbot.process import buildstep 10 from buildbot.process import buildstep
11 11
12 class TestObserver(buildstep.LogLineObserver): 12 class TestObserver(buildstep.LogLineObserver):
13 """This class knows how to understand GTest test output.""" 13 """This class knows how to understand GTest test output."""
14 # TestAbbrFromTestID needs to be a member function. 14 # TestAbbrFromTestID needs to be a member function.
15 # pylint: disable=R0201 15 # pylint: disable=R0201
16 16
17 def __init__(self): 17 def __init__(self):
18 buildstep.LogLineObserver.__init__(self) 18 buildstep.LogLineObserver.__init__(self)
19 19
20 # State tracking for log parsing 20 # State tracking for log parsing
21 self._current_test = '' 21 self._current_test = ''
22 self._failure_description = [] 22 self._failure_description = []
23 self._current_suppression_hash = '' 23 self._current_suppression_hash = ''
24 self._current_suppression = [] 24 self._current_suppression = []
25 self._parsing_failures = False
25 26
26 # Line number currently being processed. 27 # Line number currently being processed.
27 self._line_number = 0 28 self._line_number = 0
28 29
29 # List of parsing errors, as human-readable strings. 30 # List of parsing errors, as human-readable strings.
30 self.internal_error_lines = [] 31 self.internal_error_lines = []
31 32
32 # Tests are stored here as 'test.name': (status, [description]). 33 # Tests are stored here as 'test.name': (status, [description]).
33 # The status should be one of ('started', 'OK', 'failed', 'timeout', 34 # The status should be one of ('started', 'OK', 'failed', 'timeout',
34 # 'warning'). Warning indicates that a test did not pass when run in 35 # 'warning'). Warning indicates that a test did not pass when run in
35 # parallel with other tests but passed when run alone. The description is 36 # parallel with other tests but passed when run alone. The description is
36 # a list of lines detailing the test's error, as reported in the log. 37 # a list of lines detailing the test's error, as reported in the log.
37 self._test_status = {} 38 self._test_status = {}
38 39
39 # Suppressions are stored here as 'hash': [suppression]. 40 # Suppressions are stored here as 'hash': [suppression].
40 self._suppressions = {} 41 self._suppressions = {}
41 42
42 # This may be either text or a number. It will be used in the phrase 43 # This may be either text or a number. It will be used in the phrase
43 # '%s disabled' or '%s flaky' on the waterfall display. 44 # '%s disabled' or '%s flaky' on the waterfall display.
44 self.disabled_tests = 0 45 self.disabled_tests = 0
45 self.flaky_tests = 0 46 self.flaky_tests = 0
46 47
47 # Regular expressions for parsing GTest logs. Test names look like 48 # Regular expressions for parsing GTest logs. Test names look like
48 # SomeTestCase.SomeTest 49 # SomeTestCase.SomeTest
49 # SomeName/SomeTestCase.SomeTest/1 50 # SomeName/SomeTestCase.SomeTest/1
50 # This regexp also matches SomeName.SomeTest/1, which should be harmless. 51 # This regexp also matches SomeName.SomeTest/1, which should be harmless.
51 test_name_regexp = r'((\w+/)?\w+\.\w+(/\d+)?)' 52 test_name_regexp = r'((\w+/)?\w+\.\w+(/\d+)?)'
52 53
54 self._test_name = re.compile(test_name_regexp)
53 self._test_start = re.compile('\[\s+RUN\s+\] ' + test_name_regexp) 55 self._test_start = re.compile('\[\s+RUN\s+\] ' + test_name_regexp)
54 self._test_ok = re.compile('\[\s+OK\s+\] ' + test_name_regexp) 56 self._test_ok = re.compile('\[\s+OK\s+\] ' + test_name_regexp)
55 self._test_fail = re.compile('\[\s+FAILED\s+\] ' + test_name_regexp) 57 self._test_fail = re.compile('\[\s+FAILED\s+\] ' + test_name_regexp)
56 self._test_timeout = re.compile( 58 self._test_timeout = re.compile(
57 'Test timeout \([0-9]+ ms\) exceeded for ' + test_name_regexp) 59 'Test timeout \([0-9]+ ms\) exceeded for ' + test_name_regexp)
58 self._disabled = re.compile(' YOU HAVE (\d+) DISABLED TEST') 60 self._disabled = re.compile(' YOU HAVE (\d+) DISABLED TEST')
59 self._flaky = re.compile(' YOU HAVE (\d+) FLAKY TEST') 61 self._flaky = re.compile(' YOU HAVE (\d+) FLAKY TEST')
60 62
61 self._suppression_start = re.compile( 63 self._suppression_start = re.compile(
62 'Suppression \(error hash=#([0-9A-F]+)#\):') 64 'Suppression \(error hash=#([0-9A-F]+)#\):')
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 if self._current_suppression_hash: 281 if self._current_suppression_hash:
280 self._current_suppression.append(line) 282 self._current_suppression.append(line)
281 return 283 return
282 284
283 # Random line: if we're in a test, collect it for the failure description. 285 # Random line: if we're in a test, collect it for the failure description.
284 # Tests may run simultaneously, so this might be off, but it's worth a try. 286 # Tests may run simultaneously, so this might be off, but it's worth a try.
285 # This also won't work if a test times out before it begins running. 287 # This also won't work if a test times out before it begins running.
286 if self._current_test: 288 if self._current_test:
287 self._failure_description.append(line) 289 self._failure_description.append(line)
288 290
291 # Parse the "Failing tests:" list at the end of the output, and add any
292 # additional failed tests to the list. For example, this includes tests
293 # that crash after the OK line.
294 if self._parsing_failures:
295 results = self._test_name.search(line)
296 if results:
297 test_name = results.group(1)
298 if self._StatusOfTest(test_name) == 'OK':
299 self._test_status[test_name] = (
300 'failed', ['Unknown error, see stdio log.'])
301 else:
302 self._parsing_failures = False
303 elif line.find('Failing tests:') != -1:
M-A Ruel 2012/01/06 17:07:36 elif 'Failing tests:' in line:
Alexei Svitkine (slow) 2012/01/06 18:08:03 Changed to startswith(), which is more precise.
304 self._parsing_failures = True
289 305
290 class GTestCommand(shell.ShellCommand): 306 class GTestCommand(shell.ShellCommand):
291 """Buildbot command that knows how to display GTest output.""" 307 """Buildbot command that knows how to display GTest output."""
292 # TestAbbrFromTestID needs to be a member function. 308 # TestAbbrFromTestID needs to be a member function.
293 # pylint: disable=R0201 309 # pylint: disable=R0201
294 310
295 _GTEST_DASHBOARD_BASE = ("http://test-results.appspot.com" 311 _GTEST_DASHBOARD_BASE = ("http://test-results.appspot.com"
296 "/dashboards/flakiness_dashboard.html") 312 "/dashboards/flakiness_dashboard.html")
297 313
298 def __init__(self, **kwargs): 314 def __init__(self, **kwargs):
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 self.addCompleteLog(suppression_hash, 387 self.addCompleteLog(suppression_hash,
372 '\n'.join(observer.Suppression(suppression_hash))) 388 '\n'.join(observer.Suppression(suppression_hash)))
373 389
374 390
375 class GTestFullCommand(GTestCommand): 391 class GTestFullCommand(GTestCommand):
376 def TestAbbrFromTestID(self, testid): 392 def TestAbbrFromTestID(self, testid):
377 """ 393 """
378 Return the full TestCase.TestName ID. 394 Return the full TestCase.TestName ID.
379 """ 395 """
380 return testid 396 return testid
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698