From 167e221e31a4f92bf127001c85ffcbbae8058cb6 Mon Sep 17 00:00:00 2001 From: Murali Nandigama Date: Tue, 11 May 2010 17:53:10 -0700 Subject: [PATCH] working refactor; no enhancements --- topfails/log_parser.py | 99 +++++++++++++++++++++++++++------------- topfails/unittest-log.py | 2 + 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/topfails/log_parser.py b/topfails/log_parser.py index 6ce649b..a86c0cc 100644 --- a/topfails/log_parser.py +++ b/topfails/log_parser.py @@ -19,7 +19,8 @@ # the Initial Developer. All Rights Reserved. # # Contributor(s): -# Jeff Hammel +# Jeff Hammel +# Murali Nandigama # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or @@ -43,6 +44,10 @@ class LogParser(object): # 'TestFailed' expected log format is "result | test | optional text". testfailedRe = re.compile(r"(TEST-UNEXPECTED-.*) \| (.*) \|(.*)") + def get_potentialTestName(self, line): + """return potential test name [None by default]""" + return None + def parse(self, fp): """ parse the file, returning the test failures: @@ -51,49 +56,44 @@ class LogParser(object): """ # Look for test failures. failures = [] - - for line in fp.readlines(): - # Check all lines if they have INFO Running or url=file:/// stuff. - # If it is mochitest, we see the former string pattern - # If it is jsreftest,crashtest we see the later string pattern. - if "INFO Running" in line or "[url = file:///" in line or "INFO | Loading" in line: - potentialTestName=line - if "[url = file:///" in line: - if "?test=" in line: - potentialTestName = potentialTestName.split('?test=')[1][0:-2] - else: - potentialTestName = potentialTestName.split('url = ')[1][0:-1] - elif "INFO Running" in line: - potentialTestName = potentialTestName.split('INFO Running ')[1][0:-4] - elif "INFO | Loading" in line: - potentialTestName = potentialTestName.split('INFO | Loading ')[1] - else : - potentialTestName= "[unittest-log.py: no logged test]" - + lines = fp.readlines() + potentialTestName = None + for idx, line in enumerate(lines): + + # get the potential real name for reporting + # a test for an automation.py or automationutils.processLeakLog failure + potentialTestName = self.get_potentialTestName(line) or potentialTestName + + # test to see if the line is a failure m = self.testfailedRe.match(line) if not m: continue - - test = rawtest = m.group(2).strip() or "[unittest-log.py: no logged test]" + + # name of the test + test = m.group(2).strip() or "[unittest-log.py: no logged test]" + + # substitute potentialTestName for the test name if + # test is automation.py or automationutils.processLeakLog if 'automation.py' in test or 'automationutils.processLeakLog' in test: - if potentialTestName != "": - test = rawtest = potentialTestName - potentialTestName="" + if potentialTestName is not None: + test = potentialTestName + potentialTestName = None # Code bits below try to change back slash to forward slash - # and get rid of varibale prepends to the /test/../.. names - - if rawtest.find('\\') != -1: - test = rawtest.replace('\\','/') - + # and get rid of varibale prepends to the /test/../.. names + if test.find('\\') != -1: + test = test.replace('\\','/') if test.find('/') != -1: tup=test.partition('build/') if len(tup[2]) > 2: test=tup[2] else : test=tup[0] - + + # fail log text text = m.group(3).strip() or "[unittest-log.py: no logged text]" + + # append interesting data to failures return value failures.append({'test': test, 'text': text, 'reason': m.group(1).rstrip()}) return failures @@ -104,8 +104,22 @@ class ReftestParser(LogParser): - Reftest - Crashtest - JSReftest + + TODO: + - look for PROCESS-CRASH as well as UNEXPECTED-FAIL + [PROCESS-CRASH is a harness crash] + - need an actual log file with TEST-UNEXPECTED-FAIL with automation.py """ + def get_potentialTestName(self, line): + """ + If it is jsreftest,crashtest we see 'INFO | Loading' in line + as the potential real test name + """ + if "INFO | Loading" in line: + return line.split('INFO | Loading ', 1)[-1] + + class MochitestParser(LogParser): """ applies to @@ -113,8 +127,31 @@ class MochitestParser(LogParser): - Mochitest-chrome - Mochitest-browserchrome - Mochitest-a11y + + TODO: unhandled cases: + - harness completes and automation.py hangs: + "'62521 INFO TEST-PASS | /tests/content/xbl/test/test_bug542406.xhtml | Field three readonly?\n', + '62523 INFO Passed: 60569\n', + '62524 INFO Failed: 44\n', + '62525 INFO Todo: 770\n', + '62526 INFO SimpleTest FINISHED\n', + 'TEST-UNEXPECTED-FAIL | automation.py | application timed out after 330 seconds with no output\n', + "Can't trigger Breakpad, just killing process\n", + 'INFO | automation.py | Application ran for: 0:24:44.270038\n', + 'INFO | automation.py | Reading PID log: /var/folders/H5/H5TD8hgwEqKq9hgKlayjWU+++TM/-Tmp-/tmpEjNEf2pidlog\n', + "WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!\n", + '\n', + 'INFO | runtests.py | Running tests: end.\n', + 'program finished with exit code 247\n', + 'elapsedTime=1496.639870\n'," """ + def get_potentialTestName(self, line): + """Check all lines if they have INFO Running""" + if "INFO Running" in line: + return line.split('INFO Running ', 1)[-1].rstrip('.') # strip trailing ellipsis + + class XPCshellParser(LogParser): """ parser XPCShell results diff --git a/topfails/unittest-log.py b/topfails/unittest-log.py index 86c7668..f6c8607 100755 --- a/topfails/unittest-log.py +++ b/topfails/unittest-log.py @@ -477,6 +477,7 @@ while curtime < endtime and chunk < totalchunks: elif status == BuildStatus.TestFailed : logging.info("Checking build log for '%s' at %d (%s)" % (name, starttime, ctime(starttime))) try: + failures = [] # Grab the build log. log, headers = urllib.urlretrieve("http://tinderbox.mozilla.org/%s/%s" % (options.tree, build['logfile'])) gz = GzipFile(log) # I need a list of lines from the build log @@ -486,6 +487,7 @@ while curtime < endtime and chunk < totalchunks: parser = parsers.get(harness_type, log_parser.LogParser)() failures = parser.parse(gz) + # add the failures to the database for failure in failures: # convenience variables; can probably delete