From 7f751f28fcc7bee4bb7119c0905967ec6045bfdc Mon Sep 17 00:00:00 2001 From: "Christian Holler (:decoder)" Date: Thu, 29 Nov 2012 15:36:30 +0100 Subject: [PATCH] WIP --- client/stats/correlate-coverage.py | 73 ++++++++++++++++++++++-------- client/stats/crithighfixes.py | 50 +++++++++++++++++--- 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/client/stats/correlate-coverage.py b/client/stats/correlate-coverage.py index 80ca80c..e3c73b1 100644 --- a/client/stats/correlate-coverage.py +++ b/client/stats/correlate-coverage.py @@ -53,36 +53,66 @@ def getFileCoverage(baseURL, filename): try: urlObj = mech.open(covURL) except: - return ("N/A", "N/A") + return ("N/A", "N/A", "N/A", "N/A") parsedUrlObj = lxml.html.parse(urlObj) - # Xpath to the coverage, see below - lineCoveragePercent = float(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[2]/td[7]")[0].text.replace(" %", "")); - # ------------------------------------------------------------------------------^ - # 2 - line coverage - # 3 - function coverage - # 4 - branch coverage - # ------------------------------------------------------------------------------------^ - # 5 - # hit - # 6 - # total - # 7 - % hit - - lineCoverageHit = int(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[2]/td[5]")[0].text) - lineCoverageTotal = int(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[2]/td[6]")[0].text) + branchCoveragePercent = "N/A" + branchCoverageMissing = "N/A" + lineCoveragePercent = "N/A" + lineCoverageMissing = "N/A" - lineCoverageMissing = lineCoverageTotal - lineCoverageHit - + try: + # Xpath to the coverage, see below + lineCoveragePercent = float(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[2]/td[7]")[0].text.replace(" %", "")); + # ------------------------------------------------------------------------------------^ + # 2 - line coverage + # 3 - function coverage + # 4 - branch coverage + # ------------------------------------------------------------------------------------------^ + # 5 - # hit + # 6 - # total + # 7 - % hit + + lineCoverageHit = int(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[2]/td[5]")[0].text) + lineCoverageTotal = int(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[2]/td[6]")[0].text) + lineCoverageMissing = lineCoverageTotal - lineCoverageHit + except ValueError: + pass + + try: + branchCoveragePercent = float(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[4]/td[7]")[0].text.replace(" %", "")); + branchCoverageHit = int(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[4]/td[5]")[0].text) + branchCoverageTotal = int(parsedUrlObj.xpath("/html/body/table[1]/tr[3]/td/table/tr[4]/td[6]")[0].text) + branchCoverageMissing = branchCoverageTotal - branchCoverageHit + except ValueError: + pass + mech.close() - return (lineCoveragePercent, lineCoverageMissing) + return (lineCoveragePercent, lineCoverageMissing, branchCoveragePercent, branchCoverageMissing) def main(): currentCovURL = getCurrentCoverageDirectory("http://people.mozilla.org/~choller/firefox/coverage/") for record in fileinput.input(): record = record.lstrip().rstrip() + + # Preserve comments, possibly append new data column description though + dataDescriptorFollows = False + if record.startswith('#'): + if "DATA DESCRIPTOR FOLLOWS" in record: + dataDescriptorFollows = True + print record + elif dataDescriptorFollows: + print record + " | % Line Coverage | Number of lines missing | % Branch Coverage | Number of branches missing" + dataDescriptorFollows = False + else: + print record + + continue + recordElements = record.split() if (len(recordElements) != 2): @@ -90,8 +120,15 @@ def main(): continue cov = getFileCoverage(currentCovURL, recordElements[0]) + + covMetric = "N/A" + + try: + covMetric = (float(recordElements[1]) / (float(cov[0]) / float(100))) * float(cov[1]) + except: + pass - print record + " " + str(cov[0]) + " " + str(cov[1]) + print record + " " + str(cov[0]) + " " + str(cov[1]) + " " + str(cov[2]) + " " + str(cov[3]) #+ " " + str(covMetric) if __name__ == '__main__': main() diff --git a/client/stats/crithighfixes.py b/client/stats/crithighfixes.py index 133795b..ebeba05 100644 --- a/client/stats/crithighfixes.py +++ b/client/stats/crithighfixes.py @@ -36,12 +36,12 @@ 'resolution': 'FIXED', # Ignore old bugs, should be fixed at most 180 days ago 'chfieldto': 'Now', - 'chfieldfrom': '-60d', + 'chfieldfrom': '-180d', 'chfield': 'resolution', 'chfieldvalue': 'FIXED', - # Advanced search criteria + # Advanced search criteria 'query_format': 'advanced', - # Should have an [sg:crit/high tag in whiteboard + # Should have an [sg:crit/high tag in whiteboard 'type0-0-0': 'regexp', 'field0-0-0': 'status_whiteboard', 'value0-0-0': '\[sg:(critical|high)', @@ -52,6 +52,16 @@ 'include_fields': '_default', } +debug = False + +def printOptionsComment(opts): + commentStr = str(opts) + commentStrParts = commentStr.strip('{}').split(', ') + + print "## Bugzilla Search Criteria ##" + for commentStrPart in commentStrParts: + print "# " + commentStrPart + def fetchBug(bug_id): """Fetch the bug for the given bug_id and apply necessary workarounds.""" bug = bz.get_bug(bug_id) @@ -78,6 +88,25 @@ def extractRevisionsFromURL(text): return None +def hgCheckLog(repoDir, rev, bugnum): + """Given a HG repository, a revision and a bug number, return true if the bug number is in the commit message.""" + origCwd = os.getcwd() + os.chdir(repoDir) + + if debug: + print "Running hg log..." + + logDesc = subprocess.check_output(['hg', 'log', '-r', rev, '--template', '{desc}']) + + os.chdir(origCwd) + + bugInLog = (str(bugnum) in logDesc) + + if not bugInLog: + print >> sys.stderr, "Warning: Ignoring possible false positive (bug %s, revision %s)" % (str(bugnum), rev) + + return bugInLog + def hgDiffStat(repoDir, rev): """Given a HG repository and revision, return the files touched by that revision.""" origCwd = os.getcwd() @@ -114,7 +143,7 @@ def scanBug(bugnum, repoDir): """ # Fetch the bug bug = fetchBug(bugnum) - + for comment in bug.comments: revs = extractRevisionsFromURL(comment.text) @@ -127,6 +156,9 @@ def scanBug(bugnum, repoDir): totalFiles = [] for rev in revs: + if not hgCheckLog(repoDir, rev, bugnum): + continue + files = hgDiffStat(repoDir, rev) totalFiles.extend(files) @@ -136,9 +168,14 @@ def scanBug(bugnum, repoDir): # a file twice, then this should not be counted as two bugs. return list(set(totalFiles)) - print "Warning: No fix found in bug " + str(bugnum) + print >> sys.stderr, "Warning: No fix found in bug %s" % str(bugnum) def main(): + printOptionsComment(options) + + print "# DATA DESCRIPTOR FOLLOWS" + print "# Filename | Number of Changes from Security Bugs" + # Get the bugs from the api buglist = bz.get_bug_list(options) @@ -146,7 +183,8 @@ def main(): print "No bugs found." sys.exit(0) - print "Found %s bugs:" % (len(buglist)) + if debug: + print "Found %s bugs:" % (len(buglist)) dirs = {} cnt = 0;