This commit is contained in:
2010-05-13 14:32:28 -07:00
parent 643117fe43
commit f276b27885
83 changed files with 0 additions and 3590 deletions

View File

@@ -1,6 +0,0 @@
At the minimum, you should set the following environment variables
Assuming you are using BASH shell
export dbuser=<your mysql database username>
export dbpasswd=<your mysql passwd>

View File

@@ -1,3 +0,0 @@
This is the README for the topfails application.
Please read the 'PREREQS' document which will explain about the environment variables to be set in order to run the django application.

View File

View File

@@ -1,2 +0,0 @@
rm `find . -name \*.pyc`
rm `find . -name \*~`

View File

@@ -1,121 +0,0 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is TopFails site code.
#
# The Initial Developer of the Original Code is
# Mozilla foundation
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Murali Nandigama <Murali.Nandigama@Gmail.COM>
#
# 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
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
#
# DB schema maintenance functions.
#
import logging
__all__ = \
[
"CreateDBSchema"
]
def CreateDBSchema(conn):
logging.info("Executing CreateDBSchema()")
conn.execute("""
CREATE TABLE viewer_tree (
id integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
name varchar(45) NOT NULL
)
""")
conn.execute("""
CREATE TABLE viewer_build (
id integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
os integer NOT NULL,
tree_id integer NOT NULL,
starttime integer,
status integer NOT NULL,
changeset varchar(80) NOT NULL,
logfile varchar(300) NOT NULL
)
""")
conn.execute("""
ALTER TABLE viewer_build
ADD CONSTRAINT tree_id_refs_id_11e44bee
FOREIGN KEY (tree_id)
REFERENCES viewer_tree (id)
""")
conn.execute("""
CREATE TABLE viewer_test (
id integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
name varchar(300) NOT NULL
)
""")
conn.execute("""
CREATE TABLE viewer_testfailure (
id integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
build_id integer NOT NULL,
test_id integer NOT NULL,
failtext varchar(400) NOT NULL
)
""")
conn.execute("""
ALTER TABLE viewer_testfailure
ADD CONSTRAINT test_id_refs_id_1cc1b9e6
FOREIGN KEY (test_id)
REFERENCES viewer_test (id)
""")
conn.execute("""
ALTER TABLE viewer_testfailure
ADD CONSTRAINT build_id_refs_id_112c09cb
FOREIGN KEY (build_id)
REFERENCES viewer_build (id)
""")
conn.execute("""
CREATE INDEX viewer_build_tree_id ON viewer_build (tree_id)
""")
conn.execute("""
CREATE INDEX viewer_testfailure_build_id ON viewer_testfailure (build_id)
""")
conn.execute("""
CREATE INDEX viewer_testfailure_test_id ON viewer_testfailure (test_id)
""")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,203 +0,0 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is TopFails site code.
#
# The Initial Developer of the Original Code is
# Mozilla foundation
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Jeff Hammel <jhammel@mozilla.com>
# Murali Nandigama <Murali.Nandigama@Gmail.COM>
#
# 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
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# TODO: document what all of the cases are, e.g
#
# - 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',"
#
# - leak at harness closure: see sample-logs/leaks-log.txt
import re
class LogParser(object):
"""abstract base class for parsing unittest logs"""
# 'TestFailed' expected log format is "result | test | optional text".
testfailedRe = re.compile(r"(TEST-UNEXPECTED-.*|PROCESS-CRASH) \| (.*) \|(.*)")
def get_potentialTestName(self, line):
"""return potential test name [None by default]"""
return None
def processTestName(self, test, reason, potentialTestName, lines, idx):
"""substitute the potential name for the test (if applicable)"""
# for process crash, take the test-runner (automation) as the test failure
# (as already reported in test) and reset the potentialTestName to None
if 'PROCESS-CRASH' in reason:
return test, idx
# an automation.py failure will ALWAYS be followed by a
# automationutils.processLeakLog line; so send a None here
# which will cause the parsing to continue and don't record this failure
if 'automation.py' in test:
return None, idx
if 'automationutils.processLeakLog' and (potentialTestName is not None):
len_lines = len(lines)
while (idx+1) < len_lines and ('automationutils.processLeakLog' in lines[idx+1]):
idx += 1
return potentialTestName, idx
# if these conditions are not met, return
# the test name and potentialTestName untouched
return test, idx # no name substitution
def parse(self, fp):
"""
parse the file, returning the test failures:
{'test': test, 'text': text, 'reason': mgroup(1)} ]
-fp: file-like object
"""
# Look for test failures.
failures = []
lines = fp.readlines()
potentialTestName = None
idx = 0
while idx < len(lines):
line = lines[idx]
# 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:
idx += 1
continue
# reason for failure [TEST-UNEXPECTED-.* or PROCESS-CRASH]
reason = m.group(1).rstrip()
# name of the test
test = m.group(2).strip() or "[unittest-log.py: no logged test]"
# fail log text
text = m.group(3).strip() or "[unittest-log.py: no logged text]"
# test to see if the harness hangs after a run completion
if lines[idx-1].strip().endswith('FINISHED'):
text = 'harness hangs after end of test run (or something)'
else:
# substitute potentialTestName for the test name if
# test is automation.py or automationutils.processLeakLog
test, idx = self.processTestName(test, reason, potentialTestName, lines, idx)
if test is None: # don't add this test (and don't reset potentialTestName)
idx += 1
continue
# reset 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 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]
# append interesting data to failures return value
failures.append({'test': test, 'text': text, 'reason': reason})
# increment the line counter
idx += 1
return failures
class ReftestParser(LogParser):
"""
applies to
- Reftest
- Crashtest
- JSReftest
"""
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
- Mochitest-plain
- Mochitest-chrome
- Mochitest-browserchrome
- Mochitest-a11y
"""
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
"""
class CheckParser(LogParser):
"""
parses results from `make check` (C compiled code tests)
"""

View File

@@ -1,11 +0,0 @@
#!/usr/bin/python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

View File

@@ -1,95 +0,0 @@
#!/usr/bin/env python
"""
Build name:
<OS> <branch> [type of build]
Examples:
The builder: <OS> <branch> build (e.g. 'Linux mozilla-central build')
-- in this case [type of build] is the string 'build'
A debug test: <OS> <branch> debug test <test type>
-- in this case [type of build] is the string 'debug test <test type>'
An opt test: <OS> <branch> opt test <test type>
A leak test: <OS> <branch> leak test <test type>
Talos:
<OS> <branch> talos (e.g. 'Rev3 Fedora 12x64 mozilla-central talos')
-or-
<OS> <branch> talos <type> (e.g. 'Rev3 Fedora 12x64 mozilla-central talos cold')
Currently, the mappings are coded here (in python); this has the restriction
that the mappings cannot be (intelligently) programmatically updated.
If it is desired that the mappings may be undated programmatically
(e.g. from the command line), then a [presumedly text-ish] storage method
should be used for these mappings, e.g. an .ini file
So....
All of this data lives in the buildbot-configs.
See http://hg.mozilla.org/build/buildbot-configs/file/tip/mozilla2/config.py
The mappings are duplicated here for expediency.
- what really should happen is that this config file should be imported and
used here. In order for this to happen:
- the config.py file should be refactored so that it is consumable (and probably the entire buildbot-configs as well)
- buildbot-configs (or whatever this piece is refactored into) should
become a real python package or otherwise installable/depended upon
"""
import re
# OS mappings
OSes = [ 'Linux',
'Linux x86-64',
'OS X 10.5.2',
'OS X 10.6.2',
'Rev3 Fedora 12',
'Rev3 Fedora 12x64',
'Rev3 MacOSX Leopard 10.5.8',
'Rev3 MacOSX Snow Leopard 10.6.2',
'Rev3 WINNT 5.1',
'Rev3 WINNT 6.1',
'WINNT 5.2' ]
OS_to_index = dict([(OS, index) for index, OS in enumerate(OSes)])
index_to_OS = dict([(index, OS) for index, OS in enumerate(OSes)])
OSdata = { 'Linux': {'name': 'Linux', 'bits': 32 },
'Rev3 Fedora 12': { 'name': 'Fedora', 'bits': 32},
'Rev3 Fedora 12x64': { 'name': 'Fedora', 'bits': 64},
'Linux x86-64': { 'name': 'Linux', 'bits': 64},
'OS X 10.5.2': { 'name': 'MAC OSX', 'bits': 32},
'OS X 10.6.2': { 'name': 'MAC OSX', 'bits': 64},
'Rev3 MacOSX Leopard 10.5.8': { 'name': 'MAC OSX', 'bits': 32},
'Rev3 MacOSX Snow Leopard 10.6.2': { 'name': 'MAC OSX', 'bits': 64},
'Rev3 WINNT 5.1': { 'name': 'Windows', 'bits': 32},
'Rev3 WINNT 6.1': { 'name': 'Windows', 'bits': 64},
'WINNT 5.2': { 'name': 'Windows', 'bits': 32},
}
# branch objects
# branches = [ 'mozilla-central',
# 'mozilla-1.9.2',
# 'comm-central',
# 'comm-central-trunk'
# ]
trees = { 'Firefox': 'mozilla-central',
'Firefox3.6': 'mozilla-1.9.2',
'Thunderbird': 'comm-central',
'SeaMonkey': 'comm-central-trunk',
}
build_name_regex = r'(?P<os>%s) (?P<branch>%s) (?P<type>.*)' % ('|'.join(OSes), '|'.join(trees.values()))
build_name_regex = re.compile(build_name_regex)
def parse_build_name(name):
match = re.match(build_name_regex, name)
if match is None:
return None
return match.groupdict()
if __name__ == '__main__':
import sys
for arg in sys.argv[1:]:
print parse_build_name(arg)

View File

@@ -1,86 +0,0 @@
# Django settings for topfails project.
import os
DEBUG = True
TEMPLATE_DEBUG = DEBUG
DBUSER = DBPASSWD = ''
if os.environ.has_key('dbuser'):
DBUSER=os.environ.get('dbuser')
#print DBUSER
if os.environ.has_key('dbpasswd'):
DBPASSWD=os.environ.get('dbpasswd')
#print DBPASSWD
ADMINS = (
('Murali Nandigama', 'murali.nandigama@gmail.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'mysql' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'topfails' # Or path to database file if using sqlite3.
DATABASE_USER = DBUSER # Not used with sqlite3.
DATABASE_PASSWORD = DBPASSWD # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = '^_5(02(8sng5+6+x=c$w+3(4xv+x^&b)2ty4y8m01(o7vz$o5_'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'topfails.urls'
TEMPLATE_DIRS = (
'./templates/'
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'topfails.viewer'
)

View File

@@ -1,121 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Topfails</title>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<style>
.linear{
display: block;
font-family: Helvetica,Arial,sans-serif;
font-weight: bold;
font-size: 110.5%;
text-decoration: none;
padding: 2px;
-moz-border-radius: 5px;
border: 1px solid #000000;
color: #999999;
background-image:url("http://people.mozilla.com/~mnandigama/bg.png")
}
.linear:hover {
color: #cc3300;
font-style:italic
}
.info {
font-size: 81%;
font-weight: normal;
}
.ralign{
text-align: right;
}
.lalign {
text-align: left;
}
.calign{
text-align: center;
}
DIV,UL,OL /* Left */
{
margin-top: 0px;
margin-bottom: 0px;
}
</style>
</head>
<body>
<div style="width: 180px;position:absolute; left:3px; top:102px; z-index:0">
<a class="linear ralign" href="{% url latest tree=tree %}">
<span> Latest Fails</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:135px; z-index:0">
<a class="linear ralign" href="{% url topfails tree=tree %}">
<span> Top 25 Fails</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:168px; z-index:0">
<a class="linear ralign" href="{% url tests tree=tree %}">
<span> All Failed Tests</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:201px; z-index:0">
<a class="linear ralign" href="{% url failswindow tree=tree %}?window=7d">
<span> Fails from X days</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:234px; z-index:0">
<a class="linear ralign" href="{% url Help tree=tree %}">
<span>Help</span>
</a>
</div>
<div id="image1" style="position:absolute; overflow:hidden; left:3px; top:1px; width:180px; height:68px; z-index:1">
<img src="http://people.mozilla.com/~mnandigama/images/logo-wordmark.png" alt="Firefox Logo" title="http://www.mozilla.com" border=0 width=179 height=68>
</div>
<div id="text1" style="position:absolute; overflow:hidden; left:321px; top:2px; width:591px; height:41px; z-index:3">
<div>
<div><font color="#CC4200" face="Tahoma" size="+3"><B>Mozilla Tinderbox Topfails Dashboard</B></font></div>
</div></div>
<div id="ff" style="width: 111px;position:absolute; left:311px; top:45px; z-index:2;">
<a class="linear calign" title="Click for Firefox Trunk Topfails Dashboard" href="{% url latest tree='Firefox' %}" >
<span> Firefox </span>
</a>
</div>
<div id="ff36" style="width: 111px;position:absolute; left:423px; top:45px; z-index:2">
<a class="linear calign" href="{% url latest tree='Firefox3.6' %}" title="Click for Firefox 3.6 Topfails Dashboard">
<span> Firefox3.6</span>
</a>
</div>
<div id="tb" style="width: 111px;position:absolute; left:535px; top:45px; z-index:2">
<a class="linear calign" title="Click for Thunderbird Trunk Topfails Dashboard" href="{% url latest tree='Thunderbird' %}">
<span>Thunderbird</span>
</a>
</div>
<div id="sm" style="width: 111px;position:absolute; left:647px; top:45px; z-index:2">
<a class="linear calign" title="Click for SeaMonkey Topfails Dashboard" href="{% url latest tree='SeaMonkey' %}">
<span> SeaMonkey</span>
</a>
</div>
<div id="text2" style="position:absolute; overflow:hidden; left:190px; top:102px; width:800px; z-index:4">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@@ -1,21 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div><font face="Calibri" class="ws11">This web site provides the bird eye view of top failures and other details of Firefox unit tests&nbsp; from the&nbsp; Tinderbox daily runs.</font></div>
<div><font face="Calibri" class="ws11"><BR></font></div>
<div><font face="Calibri" class="ws11">Meaning of the Left navigation links</font></div>
<UL>
<li><font face="Calibri" class="ws11">Latest Failures : This link provides the list of latest failures from the Tinderbox.</font></li>
<li><font face="Calibri" class="ws11">Top 25 Failures : This link provides the list of top 25 failures from the entire history of the topfails database. Please note that this data is as complete as the first date we started collecting results. The data we have in the topfails database does not contain all the failures details from the Time ZERO of tinderbox runs.</font></li>
<li><font face="Calibri" class="ws11">All Failed Tests: This link displays all the known failures that are stored in the topfails DB.</font></li>
<li><font face="Calibri" class="ws11"><b>Failures from X days</b>: This is a link which allows you to query for failures from NOW till a specific period in the past.</font></li>
</UL>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Example: 2d&nbsp; =&nbsp;&nbsp; 2 days&nbsp; in the past till&nbsp; now </font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7h&nbsp; =&nbsp;&nbsp; 7 hours in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5w =&nbsp; 5 weeks in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1m =&nbsp; 1 month in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10y =&nbsp; 10 years in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=2w">2 weeks 'Firefox' failures</a></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=2d">2 days 'Firefox' failures</a></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=24h">24 hours 'Firefox' failures</a></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=1m">1 month 'Firefox' failures</a></div>
{% endblock %}

View File

@@ -1,5 +0,0 @@
I have done a very bad and crude hack to add a top level navigation page. Instead of storing images for top navigation page in the correct location , I am jammin gall the top site nav stuff in here.
Should fix it when time permits.

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Changeset {{ changeset }}</h3>
<ul>
{% for build in builds %}
<li>{{ build.tree }}: {{ build.get_os_display }}: {{ build.get_status_display }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Changesets</h3>
<ul>
{% for c in changesets %}
<li><a href="{% url viewer.views.changeset c %}">{{ c }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Failed tests from last {{n}} {{d}} up to now</h3>
<table>
<tr><th>Count</th><th align="left">Test name</th></tr>
{% for f in failures %}
<tr><td>{{ f.count }}</td><td><a href="{% url viewer.views.test tree=tree %}?name={{ f.test__name }}">{{ f.test__name }}</a></td></tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "base.html" %}
{% block content %}
<style type="text/css">
li {
padding-bottom: 10px; }
</style>
<h3>Most recent test failures</h3>
<ul>
{% for f in failures %}
<li>{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree.name }} {{ f.build.get_os_display }}: <a href="{% url viewer.views.test %}?name={{ f.name }}">{{ f.name }}</a>,
<a href="{% url viewer.views.timeline %}?name={{ f.name }}">timeline</a>
- {{ f.description }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "base.html" %}
{% block content %}
<style type="text/css">
li {
padding-bottom: 10px; }
</style>
<h3>Most recent test failures</h3>
<ul>
{% for f in failures %}
<li>{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree.name }} {{ f.build.get_os_display }}: <a href="{% url viewer.views.test tree=tree %}?name={{ f.test.name }}">{{ f.test.name }}</a>
<!--<a href="{% url viewer.views.timeline tree=tree %}?name={{ f.test.name }}">timeline</a> -->
- {{ f.failtext }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,13 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Logfile links and Changeset links for {{ test }} failures</h3>
[ Showing up to latest 10000 failures' related log links ]<br />
<br />
<ul>
<li style="display:none"><ul>
{% for f in failures %}{% ifchanged f.build.id %}</ul></li><li>
<a href="{{ f.build.tinderboxlink|safe }}">{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree }} {{ f.build.get_os_display }}</a> &nbsp; [{{ f.build.changesetlink|safe }}]
<ul>{% endifchanged %}
<!--<li>{{ f.description }}</li>-->{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,27 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Logfile links and Changeset links for {{ test }} failures</h3>
<ul>
<li style="display:none"><ul>
{% for f in section %}{% ifchanged f.build.id %}</ul></li><li>
<a href="{{ f.build.tinderboxlink|safe }}">{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree }} {{ f.build.get_os_display }}</a> [{{ f.build.changesetlink|safe }}]:
<ul>{% endifchanged %}
<!--<li>{{ f.description }}</li>-->{% endfor %}
</ul>
{% endblock %}
<div class="pagination">
<span class="step-links">
{% if section.has_previous %}
<a href="?page={{ section.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ section.number }} of {{ section.paginator.num_pages }}.
</span>
{% if section.has_next %}
<a href="?page={{ section.next_page_number }}">next</a>
{% endif %}
</span>
</div>

View File

@@ -1,14 +0,0 @@
{% extends "base.html" %}
{% block content %}
<style type="text/css">
li {
padding-bottom: 10px; }
</style>
<h3>All known failing tests</h3>
<ul>
{% for t in tests %}
<li><a href="{% url viewer.views.test tree=tree %}?name={{ t }}">{{ t }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,72 +0,0 @@
<html>
<head>
<title>Timeline for {{ test }}</title>
<script src="http://static.simile.mit.edu/exhibit/api-2.0/exhibit-api.js?autoCreate=false"
type="text/javascript"></script>
<script src="http://static.simile.mit.edu/exhibit/extensions-2.0/time/time-extension.js"></script>
<script type="application/javascript">
var json = {"items": [
{% for bd in builds %}
{
"type": "Build",
"id": "bld{{ bd.build.id }}",
"label": "{{ bd.os }} {{ bd.build.changeset }}",
"desc": "{{ bd.description|escapejs|escape }}",
"desc_id": "{{ bd.desctype }}",
"time": "{{ bd.time }}",
"os": "{{ bd.os }}",
"rev": "{{ bd.build.changeset }}",
},
{% endfor %}
],
"properties": {
"time": {"valueType": "date"},
}
};
$(document).ready(function() {
window.database = Exhibit.Database.create();
window.database.loadData(json);
window.exhibit = Exhibit.create();
window.exhibit.configureFromDOM();
});
</script>
</head>
<body>
<h2>Timeline for {{ test }}</h2>
<div ex:role="lens" ex:itemTypes="Build">
<div><span ex:content=".os"></span> <a
ex:href-subcontent="http://hg.mozilla.org/mozilla-central/rev/&#123;{.rev}&#125;"
target="_blank" ex:content=".rev"></a></div> <pre ex:content=".desc"></pre>
</div>
<div id="blds" ex:role="exhibit-collection" ex:itemTypes="Build"></div>
<div ex:role="view"
ex:viewClass="Timeline"
ex:start=".time"
ex:topBandPixelsPerUnit="50",
ex:bottomBandPixelsPerUnit="50",
></div>
<table border="0">
<tr>
<td>
{% for desc in descriptions %}
<pre>{{desc}}</pre>
{% endfor %}
</td>
<td valign="top" style="padding-left: 5em;">
<div ex:role="facet" ex:expression=".desc_id"
ex:facetLabel="Description"></div>
</td>
<td valign="top" style="padding-left: 5em;">
<div ex:role="facet" ex:expression=".os"
ex:facetLabel="OS"></div>
</td>
</tr>
</table>
</body> </html>

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Top 25 failing tests</h3>
<table>
<tr><th>Count</th><th align="left">Test name</th></tr>
{% for f in failures %}
<tr><td>{{ f.count }}</td><td><a href="{% url viewer.views.test tree=tree %}?name={{ f.test__name }}">{{ f.test__name}}</a></td></tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>{{ tree }}</h3>
<ul>
{% for build in newestbuilds %}
<li>{{ build.get_os_display }}: {{ build.get_status_display }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Trees</h3>
<ul>
{% for tree in trees %}
<li><a href="{% url viewer.views.tree tree.name %}">{{ tree.name }}</a></li>
{% endfor %}
</ul>
{% endblock %}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,11 +0,0 @@
#!/usr/bin/python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

View File

@@ -1,86 +0,0 @@
# Django settings for topfails project.
import os
DEBUG = True
TEMPLATE_DEBUG = DEBUG
DBUSER = DBPASSWD = ''
if os.environ.has_key('dbuser'):
DBUSER=os.environ.get('dbuser')
#print DBUSER
if os.environ.has_key('dbpasswd'):
DBPASSWD=os.environ.get('dbpasswd')
#print DBPASSWD
ADMINS = (
('Murali Nandigama', 'murali.nandigama@gmail.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'mysql' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'topfails' # Or path to database file if using sqlite3.
DATABASE_USER = DBUSER # Not used with sqlite3.
DATABASE_PASSWORD = DBPASSWD # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = '^_5(02(8sng5+6+x=c$w+3(4xv+x^&b)2ty4y8m01(o7vz$o5_'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'topfails.urls'
TEMPLATE_DIRS = (
'./templates/'
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'topfails.viewer'
)

View File

@@ -1,121 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Topfails</title>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<style>
.linear{
display: block;
font-family: Helvetica,Arial,sans-serif;
font-weight: bold;
font-size: 110.5%;
text-decoration: none;
padding: 2px;
-moz-border-radius: 5px;
border: 1px solid #000000;
color: #999999;
background-image:url("http://people.mozilla.com/~mnandigama/bg.png")
}
.linear:hover {
color: #cc3300;
font-style:italic
}
.info {
font-size: 81%;
font-weight: normal;
}
.ralign{
text-align: right;
}
.lalign {
text-align: left;
}
.calign{
text-align: center;
}
DIV,UL,OL /* Left */
{
margin-top: 0px;
margin-bottom: 0px;
}
</style>
</head>
<body>
<div style="width: 180px;position:absolute; left:3px; top:102px; z-index:0">
<a class="linear ralign" href="{% url latest tree=tree %}">
<span> Latest Fails</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:135px; z-index:0">
<a class="linear ralign" href="{% url topfails tree=tree %}">
<span> Top 25 Fails</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:168px; z-index:0">
<a class="linear ralign" href="{% url tests tree=tree %}">
<span> All Failed Tests</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:201px; z-index:0">
<a class="linear ralign" href="{% url failswindow tree=tree %}?window=7d">
<span> Fails from X days</span>
</a>
</div>
<div style="width: 180px;position:absolute; left:3px; top:234px; z-index:0">
<a class="linear ralign" href="{% url Help tree=tree %}">
<span>Help</span>
</a>
</div>
<div id="image1" style="position:absolute; overflow:hidden; left:3px; top:1px; width:180px; height:68px; z-index:1">
<img src="http://people.mozilla.com/~mnandigama/images/logo-wordmark.png" alt="Firefox Logo" title="http://www.mozilla.com" border=0 width=179 height=68>
</div>
<div id="text1" style="position:absolute; overflow:hidden; left:321px; top:2px; width:591px; height:41px; z-index:3">
<div>
<div><font color="#CC4200" face="Tahoma" size="+3"><B>Mozilla Tinderbox Topfails Dashboard</B></font></div>
</div></div>
<div id="ff" style="width: 111px;position:absolute; left:311px; top:45px; z-index:2;">
<a class="linear calign" title="Click for Firefox Trunk Topfails Dashboard" href="{% url latest tree='Firefox' %}" >
<span> Firefox </span>
</a>
</div>
<div id="ff36" style="width: 111px;position:absolute; left:423px; top:45px; z-index:2">
<a class="linear calign" href="{% url latest tree='Firefox3.6' %}" title="Click for Firefox 3.6 Topfails Dashboard">
<span> Firefox3.6</span>
</a>
</div>
<div id="tb" style="width: 111px;position:absolute; left:535px; top:45px; z-index:2">
<a class="linear calign" title="Click for Thunderbird Trunk Topfails Dashboard" href="{% url latest tree='Thunderbird' %}">
<span>Thunderbird</span>
</a>
</div>
<div id="sm" style="width: 111px;position:absolute; left:647px; top:45px; z-index:2">
<a class="linear calign" title="Click for SeaMonkey Topfails Dashboard" href="{% url latest tree='SeaMonkey' %}">
<span> SeaMonkey</span>
</a>
</div>
<div id="text2" style="position:absolute; overflow:hidden; left:190px; top:102px; width:800px; z-index:4">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@@ -1,21 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div><font face="Calibri" class="ws11">This web site provides the bird eye view of top failures and other details of Firefox unit tests&nbsp; from the&nbsp; Tinderbox daily runs.</font></div>
<div><font face="Calibri" class="ws11"><BR></font></div>
<div><font face="Calibri" class="ws11">Meaning of the Left navigation links</font></div>
<UL>
<li><font face="Calibri" class="ws11">Latest Failures : This link provides the list of latest failures from the Tinderbox.</font></li>
<li><font face="Calibri" class="ws11">Top 25 Failures : This link provides the list of top 25 failures from the entire history of the topfails database. Please note that this data is as complete as the first date we started collecting results. The data we have in the topfails database does not contain all the failures details from the Time ZERO of tinderbox runs.</font></li>
<li><font face="Calibri" class="ws11">All Failed Tests: This link displays all the known failures that are stored in the topfails DB.</font></li>
<li><font face="Calibri" class="ws11"><b>Failures from X days</b>: This is a link which allows you to query for failures from NOW till a specific period in the past.</font></li>
</UL>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Example: 2d&nbsp; =&nbsp;&nbsp; 2 days&nbsp; in the past till&nbsp; now </font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7h&nbsp; =&nbsp;&nbsp; 7 hours in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5w =&nbsp; 5 weeks in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1m =&nbsp; 1 month in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10y =&nbsp; 10 years in the past till now</font></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=2w">2 weeks 'Firefox' failures</a></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=2d">2 days 'Firefox' failures</a></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=24h">24 hours 'Firefox' failures</a></div>
<div style="margin-left:40px;"><font face="Calibri" class="ws11">Example queries for advanced users <a href="../failswindow/Firefox?window=1m">1 month 'Firefox' failures</a></div>
{% endblock %}

View File

@@ -1,5 +0,0 @@
I have done a very bad and crude hack to add a top level navigation page. Instead of storing images for top navigation page in the correct location , I am jammin gall the top site nav stuff in here.
Should fix it when time permits.

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Changeset {{ changeset }}</h3>
<ul>
{% for build in builds %}
<li>{{ build.tree }}: {{ build.get_os_display }}: {{ build.get_status_display }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Changesets</h3>
<ul>
{% for c in changesets %}
<li><a href="{% url viewer.views.changeset c %}">{{ c }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Failed tests from last {{n}} {{d}} up to now</h3>
<table>
<tr><th>Count</th><th align="left">Test name</th></tr>
{% for f in failures %}
<tr><td>{{ f.count }}</td><td><a href="{% url viewer.views.test tree=tree %}?name={{ f.test__name }}">{{ f.test__name }}</a></td></tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "base.html" %}
{% block content %}
<style type="text/css">
li {
padding-bottom: 10px; }
</style>
<h3>Most recent test failures</h3>
<ul>
{% for f in failures %}
<li>{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree.name }} {{ f.build.get_os_display }}: <a href="{% url viewer.views.test %}?name={{ f.name }}">{{ f.name }}</a>,
<a href="{% url viewer.views.timeline %}?name={{ f.name }}">timeline</a>
- {{ f.description }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "base.html" %}
{% block content %}
<style type="text/css">
li {
padding-bottom: 10px; }
</style>
<h3>Most recent test failures</h3>
<ul>
{% for f in failures %}
<li>{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree.name }} {{ f.build.get_os_display }}: <a href="{% url viewer.views.test tree=tree %}?name={{ f.test.name }}">{{ f.test.name }}</a>
<!--<a href="{% url viewer.views.timeline tree=tree %}?name={{ f.test.name }}">timeline</a> -->
- {{ f.failtext }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,13 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Logfile links and Changeset links for {{ test }} failures</h3>
[ Showing up to latest 10000 failures' related log links ]<br />
<br />
<ul>
<li style="display:none"><ul>
{% for f in failures %}{% ifchanged f.build.id %}</ul></li><li>
<a href="{{ f.build.tinderboxlink|safe }}">{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree }} {{ f.build.get_os_display }}</a> &nbsp; [{{ f.build.changesetlink|safe }}]
<ul>{% endifchanged %}
<!--<li>{{ f.description }}</li>-->{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,27 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Logfile links and Changeset links for {{ test }} failures</h3>
<ul>
<li style="display:none"><ul>
{% for f in section %}{% ifchanged f.build.id %}</ul></li><li>
<a href="{{ f.build.tinderboxlink|safe }}">{{ f.build.startdate|date:"Y-m-d H:i" }} {{ f.build.tree }} {{ f.build.get_os_display }}</a> [{{ f.build.changesetlink|safe }}]:
<ul>{% endifchanged %}
<!--<li>{{ f.description }}</li>-->{% endfor %}
</ul>
{% endblock %}
<div class="pagination">
<span class="step-links">
{% if section.has_previous %}
<a href="?page={{ section.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ section.number }} of {{ section.paginator.num_pages }}.
</span>
{% if section.has_next %}
<a href="?page={{ section.next_page_number }}">next</a>
{% endif %}
</span>
</div>

View File

@@ -1,14 +0,0 @@
{% extends "base.html" %}
{% block content %}
<style type="text/css">
li {
padding-bottom: 10px; }
</style>
<h3>All known failing tests</h3>
<ul>
{% for t in tests %}
<li><a href="{% url viewer.views.test tree=tree %}?name={{ t }}">{{ t }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,72 +0,0 @@
<html>
<head>
<title>Timeline for {{ test }}</title>
<script src="http://static.simile.mit.edu/exhibit/api-2.0/exhibit-api.js?autoCreate=false"
type="text/javascript"></script>
<script src="http://static.simile.mit.edu/exhibit/extensions-2.0/time/time-extension.js"></script>
<script type="application/javascript">
var json = {"items": [
{% for bd in builds %}
{
"type": "Build",
"id": "bld{{ bd.build.id }}",
"label": "{{ bd.os }} {{ bd.build.changeset }}",
"desc": "{{ bd.description|escapejs|escape }}",
"desc_id": "{{ bd.desctype }}",
"time": "{{ bd.time }}",
"os": "{{ bd.os }}",
"rev": "{{ bd.build.changeset }}",
},
{% endfor %}
],
"properties": {
"time": {"valueType": "date"},
}
};
$(document).ready(function() {
window.database = Exhibit.Database.create();
window.database.loadData(json);
window.exhibit = Exhibit.create();
window.exhibit.configureFromDOM();
});
</script>
</head>
<body>
<h2>Timeline for {{ test }}</h2>
<div ex:role="lens" ex:itemTypes="Build">
<div><span ex:content=".os"></span> <a
ex:href-subcontent="http://hg.mozilla.org/mozilla-central/rev/&#123;{.rev}&#125;"
target="_blank" ex:content=".rev"></a></div> <pre ex:content=".desc"></pre>
</div>
<div id="blds" ex:role="exhibit-collection" ex:itemTypes="Build"></div>
<div ex:role="view"
ex:viewClass="Timeline"
ex:start=".time"
ex:topBandPixelsPerUnit="50",
ex:bottomBandPixelsPerUnit="50",
></div>
<table border="0">
<tr>
<td>
{% for desc in descriptions %}
<pre>{{desc}}</pre>
{% endfor %}
</td>
<td valign="top" style="padding-left: 5em;">
<div ex:role="facet" ex:expression=".desc_id"
ex:facetLabel="Description"></div>
</td>
<td valign="top" style="padding-left: 5em;">
<div ex:role="facet" ex:expression=".os"
ex:facetLabel="OS"></div>
</td>
</tr>
</table>
</body> </html>

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Top 25 failing tests</h3>
<table>
<tr><th>Count</th><th align="left">Test name</th></tr>
{% for f in failures %}
<tr><td>{{ f.count }}</td><td><a href="{% url viewer.views.test tree=tree %}?name={{ f.test__name }}">{{ f.test__name}}</a></td></tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -1,9 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>{{ tree }}</h3>
<ul>
{% for build in newestbuilds %}
<li>{{ build.get_os_display }}: {{ build.get_status_display }}</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,10 +0,0 @@
{% extends "base.html" %}
{% block content %}
<h3>Trees</h3>
<ul>
{% for tree in trees %}
<li><a href="{% url viewer.views.tree tree.name %}">{{ tree.name }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -1,20 +0,0 @@
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('topfails.viewer.views',
(r'^$', 'index'),
url(r'^trees/(?P<tree>.+)?$','trees', name='trees'),
url(r'^tree/(?P<tree>.+)?$','tree', name='tree'),
url(r'^changesets/(?P<tree>.+)?$','changesets', name='changesets'),
url(r'^changesets/(?P<tree>.+)/(?P<changeset>[a-f0-9]+)$', 'changeset', name='changeset'),
url(r'^tests/(?P<tree>.+)?$','tests', name='tests'),
url(r'^test/(?P<tree>.+)?$','test', name='test'),
url(r'^timeline/(?P<tree>.+)?$','timeline', name='timeline'),
url(r'^topfails/(?P<tree>.+)?$','topfails', name='topfails'),
url(r'^failswindow/(?P<tree>.+)?$','failswindow', name='failswindow'),
url(r'^latest/(?P<tree>.+)?$','latest', name='latest'),
url(r'^Help/(?P<tree>.+)?$','Help', name='Help'),
)

View File

@@ -1,115 +0,0 @@
import re
from django.db import models, connection
from datetime import datetime
from time import ctime, sleep, time
class OS():
Windows = 0
Mac = 1
Linux = 2
Unknown = 3
OS_CHOICES = (
(OS.Windows, 'Windows'),
(OS.Mac, 'Mac'),
(OS.Linux, 'Linux'),
(OS.Unknown, 'Unknown')
)
class BuildStatus():
Success = 0
TestFailed = 1
Burning = 2
Exception = 3
Unknown = 4
BUILDSTATUS_CHOICES = (
(BuildStatus.Success, 'Success'),
(BuildStatus.TestFailed, 'Test Failed'),
(BuildStatus.Burning, 'Burning'),
(BuildStatus.Exception, 'Exception'),
(BuildStatus.Unknown, 'Unknown')
)
class Tree(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=45, blank=True)
def __unicode__(self):
return self.name
class Build(models.Model):
id = models.AutoField(primary_key=True)
os = models.IntegerField(choices=OS_CHOICES)
tree = models.ForeignKey(Tree)
starttime = models.IntegerField(null=True, blank=True)
status = models.IntegerField(choices=BUILDSTATUS_CHOICES)
changeset = models.CharField(max_length=80,blank=True)
logfile = models.CharField(max_length=300,blank=True)
def startdate(self):
return datetime.fromtimestamp(self.starttime)
def changesetlink(self):
if str(self.tree) == 'Firefox':
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/mozilla-central", self.changeset, self.changeset)
elif str(self.tree) == 'Firefox3.6':
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/releases/mozilla-1.9.2", self.changeset, self.changeset)
elif str(self.tree) == 'Thunderbird' or str(self.tree) == 'SeaMonkey':
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/comm-central", self.changeset, self.changeset)
else :
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/mozilla-central", self.changeset, self.changeset)
def jsonchangesetlink(self):
return "%s/rev/%s" % ("http://hg.mozilla.org/mozilla-central", self.changeset)
def tinderboxlink(self):
if self.logfile:
return "http://tinderbox.mozilla.org/showlog.cgi?log=%s/%s" % (self.tree.name, self.logfile)
return "http://tinderbox.mozilla.org/showbuilds.cgi?tree=%s&maxdate=%d&hours=3" % (self.tree.name, self.starttime)
class Test(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=300, blank=True)
def __unicode__(self):
return self.name
class TestFailure(models.Model):
id = models.AutoField(primary_key=True)
build = models.ForeignKey(Build)
test = models.ForeignKey(Test)
failtext = models.CharField(max_length=400, blank=True)
@classmethod
def get_most_failing_tests(cls,tree):
return cls.objects.filter(build__tree__name=tree).values('test__name').annotate(count=models.Count('test__name')).order_by('-count')[:25]
@classmethod
def get_time_failing_tests(cls,tree):
return cls.objects.filter(build__tree__name=tree).values('test__name').annotate(count=models.Count('test__name')).order_by('-count')
@classmethod
def get_fails_in_timerange(cls,period,tree):
# Get current time, in seconds.
endtime = int(time())
m = re.match("(\d+)([ymwdh])", period)
if m is None:
print >>sys.stderr, "ERROR: bad timespan = '%s'!" % options.timespan
sys.exit(1)
timespan = int(m.group(1)) * {'y': 365 * 24 * 3600,
'm': 30 * 24 * 3600,
'w': 7 * 24 * 3600,
'd': 24 * 3600,
'h': 3600}[m.group(2)]
# Set current time to beginning of requested timespan ending now.
curtime = endtime - timespan
qs = cls.get_time_failing_tests(tree)
return qs.filter(build__starttime__gt=curtime)

View File

@@ -1,107 +0,0 @@
from django.shortcuts import render_to_response, get_list_or_404
from topfails.viewer.models import Build, Tree, Test,TestFailure, OS_CHOICES
import re
from django.http import HttpResponse
import json
def latest(request,tree='Firefox'):
failures = get_list_or_404(TestFailure.objects.filter(build__tree__name=tree).order_by('-build__starttime')[:20])
if request.GET.has_key('json'):
jtext = [{"testname":f.test.name, "buildstatus":f.build.status, "logfile": f.build.tinderboxlink(),"changeset":f.build.jsonchangesetlink() , "failtext":f.failtext} for f in failures]
return HttpResponse(json.dumps(jtext))
else:
return render_to_response('viewer/latest.html', {'failures': failures, 'tree' : tree})
def index(request,tree='Firefox'):
failures = get_list_or_404(TestFailure.objects.filter(build__tree__name=tree).order_by('-build__starttime')[:20])
return render_to_response('viewer/latest.html', {'failures': failures, 'tree' : tree})
def trees(request,tree='Firefox'):
alltrees = Tree.objects.all().order_by('name')
return render_to_response('viewer/trees.html', {'trees': alltrees , 'tree' : tree})
def tree(request, tree='Firefox'):
newestbuilds = get_list_or_404(Build.objects.filter(tree__name__exact=tree).order_by('-starttime')[:5])
return render_to_response('viewer/tree.html', {'tree': tree, 'newestbuilds': newestbuilds})
def changesets(request,tree='Firefox'):
build_csets = Build.objects.filter(tree__name__exact=tree).values('changeset').distinct()
return render_to_response('viewer/changesets.html', { 'tree' : tree,'changesets': [b['changeset'] for b in build_csets]})
def changeset(request, changeset,tree='Firefox'):
builds = get_list_or_404(Build, changeset__exact=changeset)
return render_to_response('viewer/changeset.html', {'changeset': changeset, 'builds': builds, 'tree' : tree})
def tests(request,tree='Firefox'):
test_names = TestFailure.objects.filter(build__tree__name__exact=tree).values('test__name').distinct()
if request.GET.has_key('json'):
jtext = list(test_names)
return HttpResponse(json.dumps(jtext))
else:
return render_to_response('viewer/tests.html', { 'tree' : tree, 'tests': [t['test__name'] for t in test_names]})
def test(request,tree='Firefox'):
failures = get_list_or_404(TestFailure.objects.filter(build__tree__name__exact=tree).filter(test__name__exact=request.GET['name']).order_by('-build__starttime')[:10000])
#if request.GET.has_key('json'):
#jtext = list(failures)
#return HttpResponse(json.dumps(jtext))
#else:
return render_to_response('viewer/test.html', {'test': request.GET['name'], 'failures': failures, 'tree' : tree})
def topfails(request,tree='Firefox'):
failures = TestFailure.get_most_failing_tests(tree)
if request.GET.has_key('json'):
jtext = list(failures)
return HttpResponse(json.dumps(jtext))
else:
return render_to_response('viewer/topfails.html', {'failures': failures, 'tree' : tree})
def Help(request,tree):
return render_to_response('viewer/Help.html',{'tree':tree})
def timeline(request,tree='Firefox'):
name = request.GET['name']
builds = get_list_or_404(Build.objects.filter(tree__name__exact=tree), test__name__exact=name)
buildlist = []
desc_list = []
for b in builds:
descs = b.testfailure_set.filter(testfailure__name__exact=name).order_by('id')
desc = '\n'.join(descs.values_list('description', flat=True))
if desc not in desc_list:
desc_list.append(desc)
desc_i = desc_list.index(desc)
buildlist.append({'build': b,
'desctype': desc_i,
'description': desc,
'os': OS_CHOICES[b.os][1],
'time': b.startdate().isoformat() + "Z",
})
return render_to_response('viewer/timeline.html', {'test': name,
'descriptions': desc_list,
'builds': buildlist, 'tree' : tree})
def failswindow(request,tree='Firefox'):
period=request.GET['window']
m = re.match("(\d+)([ymwdh])", period)
failures = TestFailure.get_fails_in_timerange(period,tree)
if request.GET.has_key('json'):
jtext = list(failures)
return HttpResponse(json.dumps(jtext))
else:
if m.group(2) == 'd':
prd='days'
elif m.group(2) == 'h':
prd = 'hours'
elif m.group(2) == 'w':
prd = 'weeks'
elif m.group(2) == 'm':
prd = 'months'
elif m.group(2) == 'y':
prd = 'years'
else:
prd = 'days'
return render_to_response('viewer/failswindow.html', {'failures': failures,'n':m.group(1),'d':prd, 'tree' : tree})

View File

@@ -1,532 +0,0 @@
#!/usr/bin/env python
#Indentation is 2 spaces ***** DO NOT USE TABS *****
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is TopFails site code.
#
# The Initial Developer of the Original Code is
# Mozilla foundation
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Serge Gautherie <sgautherie.bz@free.fr>
# Ted Mielczarek <ted.mielczarek@gmail.com>.
# Murali Nandigama <Murali.Nandigama@Gmail.COM>
# Jeff Hammel <jhammel@mozilla.com>
#
# 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
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import re, os, sys, urllib, logging
import MySQLdb # Moved from sqlite3 db to MySQL
from time import ctime, sleep, time
from math import ceil
from optparse import OptionParser
from gzip import GzipFile
import binascii
# local imports
import log_parser
import mappings
try:
# 'Availability: Unix.'
from time import tzset
except ImportError:
print >>sys.stderr, "WARNING: time.tzset() is not available on non-Unixes!"
# Define a fake function. (for development use only)
# ToDo: Investigate Windows/etc situation. (Bug 525699)
def tzset():
pass
try:
import simplejson as json
except ImportError:
try:
# 'New in version 2.6.'
import json
except ImportError:
print >>sys.stderr, "ERROR: no simplejson nor json package found!"
sys.exit(1)
from dbschema import CreateDBSchema
# Number of seconds in a hour: 60 mn * 60 s = 3600 s.
S_IN_H = 3600
# Download data in 24 hours chunks so as not to overwhelm the tinderbox server.
chunksize = 24 * S_IN_H
# seconds between requests
SLEEP_TIME = 1
class BuildStatus():
# Unavailable builds to skip.
# Values need (only) to be less than the 'Success' one.
NoBuild = -2
InProgress = -1
# Builds to save in the db.
# Do not change these values (without updating db data).
Success = 0
TestFailed = 1
Burning = 2
Exception = 3
Unknown = 4
csetre = re.compile("rev/([0-9A-Za-z]+)")
def FindChangesetInScrape(scrape):
for line in scrape:
m = csetre.search(line)
if m:
return m.group(1)
return None
buildStatuses = {
# "No build in progress".
"null": BuildStatus.NoBuild,
# "Build in progress".
"building": BuildStatus.InProgress,
# "Successful build".
"success": BuildStatus.Success,
# "Successful build, but tests failed".
"testfailed": BuildStatus.TestFailed,
# "Build failed".
"busted": BuildStatus.Burning,
# "Non-build failure". (i.e. "automation failure")
"exception": BuildStatus.Exception,
}
def BuildStatusFromText(status):
try:
return buildStatuses[status]
except KeyError:
# Log 'Unknown' status failure: this should not happen (unless new statuses are created), but we want to know if it does.
logging.info("WARNING: unknown status = '%s'!" % status)
return BuildStatus.Unknown
def GetOrInsertTree(conn, tree):
"""Get an id for a tree named |tree|. If it's not already in the trees
table, insert a new row and return the id."""
conn.execute("""
SELECT id FROM viewer_tree WHERE name = %s
""", (tree))
if conn.rowcount > 0:
return conn.fetchone()[0]
# need to insert it
conn.execute("""
INSERT INTO viewer_tree (name) VALUES (%s)
""", (tree))
connection.commit()
return conn.lastrowid
def GetOrInsertTest(conn, testname):
"""Get an id for a test named |testname|. If it's not already in the testnames
table, insert a new row and return the id."""
conn.execute("""
SELECT id FROM viewer_test WHERE name = %s
""", (testname))
if conn.rowcount > 0:
return conn.fetchone()[0]
# need to insert it
conn.execute("""
INSERT INTO viewer_test (name) VALUES (%s)
""", (testname))
connection.commit()
return conn.lastrowid
def HaveBuild(conn, treeid, _os, starttime):
"""See if we already have this build in our database."""
conn.execute("""
SELECT COUNT(*) FROM viewer_build WHERE tree_id = %s AND os = %s AND starttime = %s
""", (treeid, _os, starttime))
return conn.fetchone()[0] == 1
def UpdateLogfile(conn, treeid, _os, starttime, logfile):
"""Update empty 'logfile' for a given build (added in db schema v1)."""
conn.execute("""
UPDATE viewer_build SET logfile = %s WHERE tree_id = %s AND os = %s AND starttime = %s AND logfile IS NULL
""", (logfile, treeid, _os, starttime))
connection.commit()
def InsertBuild(conn, treeid, _os, starttime, status, logfile, changeset):
"""Insert a build into the builds table and return the id."""
conn.execute("""
INSERT INTO viewer_build (tree_id, os, starttime, status, logfile, changeset) VALUES (%s, %s, %s, %s, %s, %s)
""", (treeid, _os, starttime, status, logfile, changeset))
connection.commit()
return conn.lastrowid
def HaveFailRecord(conn,buildid, result, testnames_id):
"""See if we already have this failRecord in our database."""
conn.execute("""
SELECT COUNT(*) FROM viewer_testfailure WHERE build_id = %s AND test_id = %s
""", (buildid, testnames_id))
return conn.fetchone()[0] == 1
def InsertTest(conn, buildid, result, testnames_id, description):
# ToDo: Add column to save result.
conn.execute("""
INSERT INTO viewer_testfailure (build_id, test_id, failtext) VALUES (%s, %s, %s)
""", (buildid, testnames_id, description))
connection.commit()
def asciirepl(match):
# replace the hexadecimal characters with ascii characters
s = match.group()
return binascii.unhexlify(s)
def reformat_content(data):
p = re.compile(r'\\x(\w{2})')
return p.sub(asciirepl, data)
def fix_tbox_json(s):
"""Fixes up tinderbox json.
Tinderbox returns strings as single-quoted strings, and occasionally
includes the unquoted substring 'undef' (with quotes) in the output, e.g.
{'key': 'hello 'undef' world'}
should return a dictionary
{'key': 'hello \'undef\' world'}
"""
json_data = re.sub(r"^tinderbox_data\s*=\s*", "", s)
json_data = re.sub(r";$", "", json_data)
retval = []
in_str = False
in_esc = False
skip = 0
for i,c in enumerate(json_data):
# The tinderbox data is a fracked json. and it some times contains
# Control characters. that would totally fail the json.loads step.
# So, eliminate them .. all of them .. here -- Murali
if (c < '\xFD' and c > '\x1F') or c == '\n' or c == '\r' :
if skip > 0:
skip -= 1
continue
if in_str:
if in_esc:
if c == "'":
retval.append("'")
else:
retval.append("\\")
retval.append(c)
in_esc = False
elif c == "\\":
in_esc = True
elif c == "\"":
retval.append("\\\"")
elif c == "'":
if json_data[i:i+7] == "'undef'":
retval.append("'undef'")
skip = 7
else:
retval.append("\"")
in_str = False
else:
retval.append(c)
else:
if c == "'":
retval.append("\"")
in_str = True
else:
retval.append(c)
return "".join(retval)
parser = OptionParser()
parser.add_option("-s", "--span", action="store",
dest="timespan", default="20d",
help="Period of time to fetch data for (N[y,m,w,d,h], default=%default)")
parser.add_option("-t", "--tree", action="store",
dest="tree", default="Firefox",
help="Tinderbox tree to fetch data from (default=%default)")
parser.add_option("-d", "--database", action="store",
dest="db", default="topfails",
help="Database filename (default=%default)")
parser.add_option("--host", action="store",
dest="dbhost", default="localhost",
help="Database host name (default=%default)")
parser.add_option( "--port", action="store",
dest="dbport",default="3306",
help="Database port (default=%default)")
parser.add_option("-u", "--user", action="store",
dest="dbuser", default="root",
help="Database username (default=%default)")
parser.add_option("-p", "--passwd", action="store",
dest="dbpasswd",
help="Database user password")
parser.add_option("-v", "--verbose", action="store_true",
dest="verbose", default="False",
help="Enable verbose logging")
parser.add_option("--debug", action='store_true',
dest='debug', default=False,
help="enable interactive debugger on exceptions (pdb)")
parser.add_option("--die", action='store_true',
dest='die', default=False,
help="enable application to die on error")
(options, args) = parser.parse_args()
# check parsed options
if options.tree not in mappings.trees:
parser.error("Unknown tree: '%s'; should be one of [%s]" % (options.tree, ', '.join(mappings.trees)))
logging.basicConfig(level=options.verbose and logging.DEBUG or logging.WARNING)
os.environ['TZ'] = "US/Pacific"
tzset()
# Get current time, in seconds.
endtime = int(time())
m = re.match("(\d+)([ymwdh])", options.timespan)
if m is None:
print >>sys.stderr, "ERROR: bad timespan = '%s'!" % options.timespan
sys.exit(1)
timespan = int(m.group(1)) * {'y': 365 * 24 * S_IN_H,
'm': 30 * 24 * S_IN_H,
'w': 7 * 24 * S_IN_H,
'd': 24 * S_IN_H,
'h': S_IN_H}[m.group(2)]
# Set current time to beginning of requested timespan ending now.
curtime = endtime - timespan
createdb=False
try:
connection = MySQLdb.connect (host = options.dbhost,
port = int(options.dbport),
db = options.db,
user = options.dbuser,
passwd = options.dbpasswd)
conn=connection.cursor()
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
createdb = True
if createdb:
connection = MySQLdb.connect (host = options.dbhost,
port = int(options.dbport),
user = options.dbuser,
passwd = options.dbpasswd)
conn = connection.cursor()
try:
createdatabase='create database %s' %(options.db)
conn.execute (createdatabase)
conn.close()
connection.commit()
connection.close()
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit (1)
try:
connection = MySQLdb.connect (host = options.dbhost,
port = int(options.dbport),
db = options.db,
user = options.dbuser,
passwd = options.dbpasswd)
conn=connection.cursor()
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit(1)
CreateDBSchema(conn)
treeid = GetOrInsertTree(conn, options.tree)
logging.info("Reading tinderbox data...")
chunk = 0
# add a fudge factor here, since builds can take up to 3 hours to finish,
# and we can't get the changeset unless we ask for time up to the end of the
# build
endtime += 3 * S_IN_H
timespan += 3 * S_IN_H
totalchunks = int(ceil(float(timespan) / chunksize))
while curtime < endtime and chunk < totalchunks:
chunk += 1
logging.info("Chunk %d/%d" % (chunk, totalchunks))
if (endtime - curtime) < chunksize:
chunksize = endtime - curtime
tboxurl = "http://tinderbox.mozilla.org/showbuilds.cgi?tree=%(tree)s&maxdate=%(maxdate)d&noignore=1&hours=%(hours)d&json=1&noignore=1" \
% {'tree': options.tree,
'maxdate': curtime + chunksize, # tbox wants the end time
'hours': int(chunksize / S_IN_H)}
u = urllib.urlopen(tboxurl)
tboxjson = u.read()
#tboxjson = tboxjson.encode('utf-8').decode('string_escape').decode('utf-8')
#tboxjson = ''.join(u.readlines())
u.close()
tboxjson = fix_tbox_json(tboxjson)
try:
tboxdata = json.loads(tboxjson)
except Exception, inst:
print >>sys.stderr, "Error parsing JSON: %s" % inst
continue
# dictionary of parsers
parsers = {
'check': log_parser.CheckParser,
'mochitest': log_parser.MochitestParser,
'reftest': log_parser.ReftestParser,
'jsreftest': log_parser.ReftestParser,
'crashtest': log_parser.ReftestParser,
'xpcshell': log_parser.XPCshellParser,
}
# regular expression to find the harness
harness_regex = r'.* (%s)(-.*)?' % '|'.join(parsers.keys())
# we only care about unit test boxes
unittest_indices = [(logname, index) #tboxdata['build_name_index'][index]
for logname, index in tboxdata['build_name_index'].items()
if re.search("ref|mochi|xpc|check", logname)]
# 'TestFailed' expected log format is "result | test | optional text".
# testfailedRe = re.compile(r"(TEST-UNEXPECTED-.*) \| (.*) \|(.*)")
# XXX ^ to delete
# read build table
for timerow in tboxdata['build_table']:
for logname, index in unittest_indices:
if index >= len(timerow) or timerow[index] == -1:
continue
build = timerow[index]
if 'buildname' not in build or \
'logfile' not in build:
continue
status = BuildStatusFromText(build['buildstatus'])
# Skip unavailable "builds".
if status < BuildStatus.Success:
continue
name = build['buildname']
build_name_dict = mappings.parse_build_name(name)
if build_name_dict:
_os = OS_to_index[build_name_dict['os']]
else:
_os = -1 # UNKNOWN
starttime = int(build['buildtime'])
# skip builds we've already seen
if HaveBuild(conn, treeid, _os, starttime):
logging.info("Skipping already seen build '%s' at %d (%s)" % (name, starttime, ctime(starttime)))
# Call 'UpdateLogfile()' anyway.
UpdateLogfile(conn, treeid, _os, starttime, build['logfile'])
continue
# must have scrape data for changeset
if build['logfile'] not in tboxdata['scrape']:
continue
changeset = FindChangesetInScrape(tboxdata['scrape'][build['logfile']])
if changeset is None:
continue
buildid = InsertBuild(conn, treeid, _os, starttime, status, build['logfile'], changeset)
# 'Success' is fine as is.
if status == BuildStatus.Success:
pass
# Parse log to save 'TestFailed' results.
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
# assured to match because we search for this above
harness_type = re.match(harness_regex, logname).groups()[0]
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
test = failure['test']
text = failure['text']
reason = failure['reason']
testnames_id=GetOrInsertTest(conn,test)
if HaveFailRecord(conn,buildid, reason, testnames_id):
logging.info("Skipping already recorded failure '%s' in build with id '%s' with failure record '%s' " % (test, buildid, text))
else:
InsertTest(conn, buildid, reason, testnames_id, text)
except Exception, e:
errstring = "Unexpected error: %s" % e
if options.debug:
print errstring
import pdb; pdb.set_trace()
elif options.die:
raise
else:
logging.error(errstring)
# Ignore 'Burning' builds: tests may have run nontheless, but it's safer to discard them :-|
elif status == BuildStatus.Burning:
continue
# Ignore 'Exception' builds: should only be worse than 'Burning'.
# (Don't know much at time of writing, since this feature is not active yet: see bug 476656 and follow-ups.)
elif status == BuildStatus.Exception:
continue
# Save 'Unknown' status failure: this should not happen (unless new statuses are created), but we want to know if it does.
elif status == BuildStatus.Unknown:
# Add a fake test failure.
InsertTest(conn, buildid, "TEST-UNEXPECTED-FAIL", "99999999999", "Unknown status = '%s'!" % build['buildstatus'])
continue
if chunk < totalchunks:
sleep(SLEEP_TIME)
curtime += chunksize
conn.close()
connection.commit()
connection.close()
logging.info("Done")

View File

@@ -1,20 +0,0 @@
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('topfails.viewer.views',
(r'^$', 'index'),
url(r'^trees/(?P<tree>.+)?$','trees', name='trees'),
url(r'^tree/(?P<tree>.+)?$','tree', name='tree'),
url(r'^changesets/(?P<tree>.+)?$','changesets', name='changesets'),
url(r'^changesets/(?P<tree>.+)/(?P<changeset>[a-f0-9]+)$', 'changeset', name='changeset'),
url(r'^tests/(?P<tree>.+)?$','tests', name='tests'),
url(r'^test/(?P<tree>.+)?$','test', name='test'),
url(r'^timeline/(?P<tree>.+)?$','timeline', name='timeline'),
url(r'^topfails/(?P<tree>.+)?$','topfails', name='topfails'),
url(r'^failswindow/(?P<tree>.+)?$','failswindow', name='failswindow'),
url(r'^latest/(?P<tree>.+)?$','latest', name='latest'),
url(r'^Help/(?P<tree>.+)?$','Help', name='Help'),
)

View File

@@ -1,115 +0,0 @@
import re
from django.db import models, connection
from datetime import datetime
from time import ctime, sleep, time
class OS():
Windows = 0
Mac = 1
Linux = 2
Unknown = 3
OS_CHOICES = (
(OS.Windows, 'Windows'),
(OS.Mac, 'Mac'),
(OS.Linux, 'Linux'),
(OS.Unknown, 'Unknown')
)
class BuildStatus():
Success = 0
TestFailed = 1
Burning = 2
Exception = 3
Unknown = 4
BUILDSTATUS_CHOICES = (
(BuildStatus.Success, 'Success'),
(BuildStatus.TestFailed, 'Test Failed'),
(BuildStatus.Burning, 'Burning'),
(BuildStatus.Exception, 'Exception'),
(BuildStatus.Unknown, 'Unknown')
)
class Tree(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=45, blank=True)
def __unicode__(self):
return self.name
class Build(models.Model):
id = models.AutoField(primary_key=True)
os = models.IntegerField(choices=OS_CHOICES)
tree = models.ForeignKey(Tree)
starttime = models.IntegerField(null=True, blank=True)
status = models.IntegerField(choices=BUILDSTATUS_CHOICES)
changeset = models.CharField(max_length=80,blank=True)
logfile = models.CharField(max_length=300,blank=True)
def startdate(self):
return datetime.fromtimestamp(self.starttime)
def changesetlink(self):
if str(self.tree) == 'Firefox':
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/mozilla-central", self.changeset, self.changeset)
elif str(self.tree) == 'Firefox3.6':
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/releases/mozilla-1.9.2", self.changeset, self.changeset)
elif str(self.tree) == 'Thunderbird' or str(self.tree) == 'SeaMonkey':
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/comm-central", self.changeset, self.changeset)
else :
return '<a href="%s/rev/%s">%s</a>' % ("http://hg.mozilla.org/mozilla-central", self.changeset, self.changeset)
def jsonchangesetlink(self):
return "%s/rev/%s" % ("http://hg.mozilla.org/mozilla-central", self.changeset)
def tinderboxlink(self):
if self.logfile:
return "http://tinderbox.mozilla.org/showlog.cgi?log=%s/%s" % (self.tree.name, self.logfile)
return "http://tinderbox.mozilla.org/showbuilds.cgi?tree=%s&maxdate=%d&hours=3" % (self.tree.name, self.starttime)
class Test(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=300, blank=True)
def __unicode__(self):
return self.name
class TestFailure(models.Model):
id = models.AutoField(primary_key=True)
build = models.ForeignKey(Build)
test = models.ForeignKey(Test)
failtext = models.CharField(max_length=400, blank=True)
@classmethod
def get_most_failing_tests(cls,tree):
return cls.objects.filter(build__tree__name=tree).values('test__name').annotate(count=models.Count('test__name')).order_by('-count')[:25]
@classmethod
def get_time_failing_tests(cls,tree):
return cls.objects.filter(build__tree__name=tree).values('test__name').annotate(count=models.Count('test__name')).order_by('-count')
@classmethod
def get_fails_in_timerange(cls,period,tree):
# Get current time, in seconds.
endtime = int(time())
m = re.match("(\d+)([ymwdh])", period)
if m is None:
print >>sys.stderr, "ERROR: bad timespan = '%s'!" % options.timespan
sys.exit(1)
timespan = int(m.group(1)) * {'y': 365 * 24 * 3600,
'm': 30 * 24 * 3600,
'w': 7 * 24 * 3600,
'd': 24 * 3600,
'h': 3600}[m.group(2)]
# Set current time to beginning of requested timespan ending now.
curtime = endtime - timespan
qs = cls.get_time_failing_tests(tree)
return qs.filter(build__starttime__gt=curtime)

View File

@@ -1,107 +0,0 @@
from django.shortcuts import render_to_response, get_list_or_404
from topfails.viewer.models import Build, Tree, Test,TestFailure, OS_CHOICES
import re
from django.http import HttpResponse
import json
def latest(request,tree='Firefox'):
failures = get_list_or_404(TestFailure.objects.filter(build__tree__name=tree).order_by('-build__starttime')[:20])
if request.GET.has_key('json'):
jtext = [{"testname":f.test.name, "buildstatus":f.build.status, "logfile": f.build.tinderboxlink(),"changeset":f.build.jsonchangesetlink() , "failtext":f.failtext} for f in failures]
return HttpResponse(json.dumps(jtext))
else:
return render_to_response('viewer/latest.html', {'failures': failures, 'tree' : tree})
def index(request,tree='Firefox'):
failures = get_list_or_404(TestFailure.objects.filter(build__tree__name=tree).order_by('-build__starttime')[:20])
return render_to_response('viewer/latest.html', {'failures': failures, 'tree' : tree})
def trees(request,tree='Firefox'):
alltrees = Tree.objects.all().order_by('name')
return render_to_response('viewer/trees.html', {'trees': alltrees , 'tree' : tree})
def tree(request, tree='Firefox'):
newestbuilds = get_list_or_404(Build.objects.filter(tree__name__exact=tree).order_by('-starttime')[:5])
return render_to_response('viewer/tree.html', {'tree': tree, 'newestbuilds': newestbuilds})
def changesets(request,tree='Firefox'):
build_csets = Build.objects.filter(tree__name__exact=tree).values('changeset').distinct()
return render_to_response('viewer/changesets.html', { 'tree' : tree,'changesets': [b['changeset'] for b in build_csets]})
def changeset(request, changeset,tree='Firefox'):
builds = get_list_or_404(Build, changeset__exact=changeset)
return render_to_response('viewer/changeset.html', {'changeset': changeset, 'builds': builds, 'tree' : tree})
def tests(request,tree='Firefox'):
test_names = TestFailure.objects.filter(build__tree__name__exact=tree).values('test__name').distinct()
if request.GET.has_key('json'):
jtext = list(test_names)
return HttpResponse(json.dumps(jtext))
else:
return render_to_response('viewer/tests.html', { 'tree' : tree, 'tests': [t['test__name'] for t in test_names]})
def test(request,tree='Firefox'):
failures = get_list_or_404(TestFailure.objects.filter(build__tree__name__exact=tree).filter(test__name__exact=request.GET['name']).order_by('-build__starttime')[:10000])
#if request.GET.has_key('json'):
#jtext = list(failures)
#return HttpResponse(json.dumps(jtext))
#else:
return render_to_response('viewer/test.html', {'test': request.GET['name'], 'failures': failures, 'tree' : tree})
def topfails(request,tree='Firefox'):
failures = TestFailure.get_most_failing_tests(tree)
if request.GET.has_key('json'):
jtext = list(failures)
return HttpResponse(json.dumps(jtext))
else:
return render_to_response('viewer/topfails.html', {'failures': failures, 'tree' : tree})
def Help(request,tree):
return render_to_response('viewer/Help.html',{'tree':tree})
def timeline(request,tree='Firefox'):
name = request.GET['name']
builds = get_list_or_404(Build.objects.filter(tree__name__exact=tree), test__name__exact=name)
buildlist = []
desc_list = []
for b in builds:
descs = b.testfailure_set.filter(testfailure__name__exact=name).order_by('id')
desc = '\n'.join(descs.values_list('description', flat=True))
if desc not in desc_list:
desc_list.append(desc)
desc_i = desc_list.index(desc)
buildlist.append({'build': b,
'desctype': desc_i,
'description': desc,
'os': OS_CHOICES[b.os][1],
'time': b.startdate().isoformat() + "Z",
})
return render_to_response('viewer/timeline.html', {'test': name,
'descriptions': desc_list,
'builds': buildlist, 'tree' : tree})
def failswindow(request,tree='Firefox'):
period=request.GET['window']
m = re.match("(\d+)([ymwdh])", period)
failures = TestFailure.get_fails_in_timerange(period,tree)
if request.GET.has_key('json'):
jtext = list(failures)
return HttpResponse(json.dumps(jtext))
else:
if m.group(2) == 'd':
prd='days'
elif m.group(2) == 'h':
prd = 'hours'
elif m.group(2) == 'w':
prd = 'weeks'
elif m.group(2) == 'm':
prd = 'months'
elif m.group(2) == 'y':
prd = 'years'
else:
prd = 'days'
return render_to_response('viewer/failswindow.html', {'failures': failures,'n':m.group(1),'d':prd, 'tree' : tree})