Mozilla BuildBot Trending/Snippets
Log File Parser
#!/usr/bin/env python """This is a log parser for Mozilla Testing Logs Written by John Ford in 2009 and made available under MPL/GPL/LGPL""" import re from sys import argv class Log: """I represent a Log. Currently, I only understand how to parse lines of test output. I do not yet know about myself""" lines = [] #All LogLine objects def __init__(self, filename): """Create a Log object based on the file at a location""" print "Log file: %s" % filename try: f = open(filename) self.parseFile(f) except IOError: print "Log: encountered error while reading file" finally: f.close() def parseFile(self, file): """I take a file object and parse all of its lines. Currently, I understand most standard XPCShell tests, feed tests, and Reftests""" rawlines = file.readlines() for line in rawlines: ############################### # Special case for feed tests # ############################### if(re.match(r"PASS \| xml", line) or re.match(r"FAIL \| xml", line)): try: self.lines.append(FeedTestLogLine(line)) except InvalidLogLineError: print "This line is not a feed test but shows up as such:\n%s" % line ############################# # Special case for reftests # ############################# elif(re.search(r"REFTEST", line)): try: self.lines.append(ReftestLogLine(line)) except InvalidLogLineError: print "This line is not a reftest but shows up as such:\n%s" % line ############################### # Standard test output format # ############################### elif(re.search(r"PASS", line) or re.search(r"FAIL", line) or re.search(r"EXPECTED RANDOM", line)): try: self.lines.append(TestLogLine(line)) except InvalidLogLineError: # TODO: figure out why this line isn't being executed ever print "This line is not a standard test but shows up as such:\n%s" % line ################################################################ # Make sure that the start time is captured for the feed tests # ################################################################ # This is a bad way of doing this but is there until timestamps # are added elif(re.match(r"Start: ", line)): time = line.replace("Start: ", "").strip() for x in self.lines: if(isinstance(x, FeedTestLogLine)): x.startTime = time def buildNumber(self): """I tell you which Build Number I am""" return 1 def startTime(self): """I tell you when I started""" return 1 def endTime(self): """I tell you when I finished""" return 10 def factory(self): """I tell you which buildbot Factory I came from""" return "f1" def builder(self): """I tell you which buildbot Builder I came from""" return "mozilla-buildbot" def slave(self): """I tell you which buildbot BuildSlave I came from""" return "local-osx" def testCount(self): """I tell you how many test's output I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): count = count + 1 return count def passCount(self): """I tell you how many test passes I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.passed()): count = count + 1 return count def failCount(self): """I tell you how many test fails I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.failed()): count = count + 1 return count def unexpectedCount(self): """I tell you how many unexpected test results I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.unexpected()): count = count + 1 return count def unexpectedPassCount(self): """I tell you how many unexpected passes I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.unexpectedPass()): count = count + 1 return count def unexpectedFailCount(self): """I tell you how many unexpected passes I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.unexpectedFail()): count = count + 1 return count def knownFailCount(self): """I tell you how many known fails I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.knownFail()): count = count + 1 return count def randomCount(self): """I tell you how many known fails I found""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.random()): count = count + 1 return count def badThingsCount(self): """I tell you how many BadThings happend""" count = 0 for line in self.lines: if isinstance(line, TestLogLine): if(line.unexpected() or line.fail()): count = count + 1 return count def logURL(self): """I tell you where you can find me""" return "http://www.example.com/waterfall" def logLines(self): """If you want my LogLines, ask me here""" return self.lines class LogLine: """I represent a Log Line. I might be more than one line of raw output from the log. I understand log output in the format "X | msg" where X is a time and msg is the rest of the output""" startTime = None # Time command starts msg = None # Message def __init__(self, line): chunks = line.split(" | ") self.startTime = chunks[0].strip() self.msg = chunks[1].strip() def time(self): """I Tell you the time it took to make this line""" return "%s minus %s" % startTime, endTime #def startTime(self): #"""I tell you the time this item was started""" #return startTime def endTime(self): """I tell you the time this line ended UNIMPLEMENTED""" return None class TestLogLine(LogLine): """I am a line of output specific to a Test run. I understand logs in the format "X | Y | Z | Q" Where X is a timestamp, Y is test status, Z is a test name and Q is the test message""" statusMsg = None # I store PASS, FAIL.... testName = None # I store the name of the test def __init__(self, line): """Parse a line into a TestLogLine output""" chunks = line.split(" | ") try: self.startTime = chunks[0].strip() self.statusMsg = chunks[1].strip() self.testName = chunks[2].strip() self.msg = chunks[3].strip() except IndexError: raise InvalidLogLineError("This line does not use the standard log output format") def passed(self): """I tell you if this line passed""" return re.search("PASS", self.statusMsg) != None def failed(self): """I tell you if this line failed""" return re.search("FAIL", self.statusMsg) != None def knownFail(self): """I tell you if this test was a known fail""" return re.search("KNOWN-FAIL", self.statusMsg) def unexpected(self): """I tell you if this was an unexpected result""" return re.search("UNEXPECTED", self.statusMsg) != None def unexpectedPass(self): """I tell you if this was an unexpected pass""" return self.passed() and self.unexpected() def unexpectedFail(self): """I tell you if this was an unexpected fail""" return self.failed() and self.unexpected() def random(self): """I tell you if this was expected random output""" return re.search(r"\(EXPECTED RANDOM\)", self.statusMsg) class FeedTestLogLine(TestLogLine): """I am a special case for the feed tests. I don't have timestamps in my output yet, so the Log's parsefile method adds the same timestamp to all of us. I understand log output in the form: X | Y | Z | Q where X is test status, Y and Z are the test name and Q is the test outcome""" def __init__(self, line): chunks = line.split(" | ") try: self.startTime = repr(0) self.statusMsg = chunks[0].strip() self.testName = "%s - %s" % (chunks[1].strip(), chunks[2].strip()) self.msg = chunks[3].strip() except IndexError: raise InvalidLogLineError("This line does not use the standard log output format") class ReftestLogLine(TestLogLine): """I am a line of Reftest output. I do not yet have timing information for each step and take a really long time to run so the suite's start time is not valid for me. I understand things in the format X | Y | Z where X is test status (with the word REFTEST removed), Y is the test's name and Z is any special messages""" def __init__(self, line): chunks = line.split(" | ") try: self.startTime = repr(0) self.statusMsg = chunks[0].replace("REFTEST ", "").strip() self.testName = chunks[1].strip() try: self.msg = chunks[2].strip() except IndexError: self.msg = "No Message" except IndexError: raise InvalidLogLineError("This Reftest does not use the standard log output format") class InvalidLogLineError(Exception): """I am an exception which is raised when someone makes an invalid LogLine""" pass if(__name__ == "__main__"): log = Log(argv[1]) # This is desired output format from Bug443329 # BUILDID/BUILDTIME | TESTTIME | TESTNAME | MACHINE(from this we can map to OS) | STATUS | MSG | link to full log for line in log.logLines(): if (isinstance(line, TestLogLine)): print "%s/%s | %s | %s | %s | %s | %s | %s" % ( log.buildNumber(), repr(log.startTime()), line.startTime, line.testName, log.slave(), line.statusMsg, line.msg, log.logURL()) print """Note: Build ID, Build Time, Machine Name, Link to log are all bogus fields""" """ This code is what i tested with for line in log.logLines(): if(isinstance(line, TestLogLine)): print "=" * 100 print "Line start: %s" % line.startTime print "Line status: %s" % line.statusMsg print "Line name: %s" % line.testName print "Line Msg: %s" % line.msg print "Count: %d" % log.testCount() print "Fails: %d" % log.failCount() print "Known Fails: %d" % log.knownFailCount() print "Passes: %d" % log.passCount() print "Unexpected: %d" % log.unexpectedCount() print "UnexpectedP: %d" % log.unexpectedPassCount() print "UnexpectedF: %d" % log.unexpectedFailCount() print "Random: %d" % log.randomCount()"""
Date Format Parser
This js code parses a terminal date string into a real date and time.
// This file is a parser for terminal "date" command // format strings. Likely, you will want to use only // timeParse(date, fmtstring) // Copyright 2009 John Ford // MPL-GPL-LGPL /* This function pads a signle digit to a leading 0 as a string */ function pad(number){ if(number <= 9){ return "0" + "" + number; } else { return number; } } /* Convert a specifer letter into a value based on date */ function specifier(date, letter){ switch(letter){ case 'Y': return "" + (1900 + date.getYear()); case 'm': //Whole numbering used for month return pad(date.getMonth() + 1); case 'd': return pad(date.getDate()); case 'H': return pad(date.getHours()); case 'M': return pad(date.getMinutes()); case 'S': return pad(date.getSeconds()); case 's': return date.valueOf(); default: return letter; } } /* This will parse date format strings to give valid output pased on POSIX date command when given a format and a date object. Returns string */ function timeParse(date, fmt){ var output =""; var input = "" + fmt; input = input.replace(/\+/, ""); input = input.replace(/%*/g, ""); for each (letter in input){ if (letter == '%'){ continue; } else { output += specifier(date, "" + letter); } } return output; }