Difference between revisions of "Mozilla BuildBot Trending/Snippets"

From CDOT Wiki
Jump to: navigation, search
(New page: ==Date Format Parser== This js code parses a terminal date string into a real date and time. <pre> // This file is a parser for terminal "date" command // format strings. Likely, you wil...)
 
 
Line 1: Line 1:
 +
==Log File Parser==
 +
<pre>
 +
#!/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()"""
 +
</pre>
  
 
==Date Format Parser==
 
==Date Format Parser==

Latest revision as of 00:09, 23 February 2009

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;
}