125 lines
4 KiB
Python
Executable file
125 lines
4 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
"""
|
|
This script prints out a csv file of `suite,test,path/to/control.file` where
|
|
each row is a test that has failed every time that it ran for the past N days,
|
|
where N is that one constant lower in this file.
|
|
|
|
You run it like this
|
|
|
|
./always_failing_tests.py | tee output
|
|
|
|
But please note that since we're using the models to do queries, you'll probably
|
|
need to move your local shadow config out of the way before you run this script
|
|
so that you point at prod.
|
|
"""
|
|
|
|
import time
|
|
import hashlib
|
|
import re
|
|
import datetime
|
|
import sys
|
|
|
|
import common
|
|
from autotest_lib.frontend import setup_django_readonly_environment
|
|
|
|
# Django and the models are only setup after
|
|
# the setup_django_readonly_environment module is imported.
|
|
from autotest_lib.frontend.tko import models as tko_models
|
|
from autotest_lib.frontend.afe import models as afe_models
|
|
from autotest_lib.server.cros.dynamic_suite import suite
|
|
|
|
|
|
_DAYS_NOT_RUNNING_CUTOFF = 30
|
|
|
|
|
|
def md5(s):
|
|
m = hashlib.md5()
|
|
m.update(s)
|
|
return m.hexdigest()
|
|
|
|
|
|
def main():
|
|
cutoff_delta = datetime.timedelta(_DAYS_NOT_RUNNING_CUTOFF)
|
|
cutoff_date = datetime.datetime.today() - cutoff_delta
|
|
statuses = {s.status_idx: s.word for s in tko_models.Status.objects.all()}
|
|
now = time.time()
|
|
|
|
tests = tko_models.Test.objects.select_related('job'
|
|
).filter(started_time__gte=cutoff_date
|
|
).exclude(test__icontains='/'
|
|
).exclude(test__icontains='_JOB'
|
|
).exclude(test='provision'
|
|
).exclude(test__icontains='try_new_image')
|
|
tests = list(tests)
|
|
# These prints are vague profiling work. We're handling a lot of data, so I
|
|
# had to dump some decent work into making sure things chug along at a
|
|
# decent speed.
|
|
print "DB: %d -- len=%d" % (time.time()-now, len(tests))
|
|
|
|
def only_failures(d, t):
|
|
word = statuses[t.status_id]
|
|
if word == 'TEST_NA':
|
|
return d
|
|
if word == 'GOOD' or word == 'WARN':
|
|
passed = True
|
|
else:
|
|
passed = False
|
|
d[t.test] = d.get(t.test, False) or passed
|
|
return d
|
|
dct = reduce(only_failures, tests, {})
|
|
print "OF: %d -- len=%d" % (time.time()-now, len(dct))
|
|
|
|
all_fail = filter(lambda x: x.test in dct and not dct[x.test], tests)
|
|
print "AF: %d -- len=%d" % (time.time()-now, len(all_fail))
|
|
|
|
hash_to_file = {}
|
|
fs_getter = suite.Suite.create_fs_getter(common.autotest_dir)
|
|
for control_file in fs_getter.get_control_file_list():
|
|
with open(control_file, 'rb') as f:
|
|
h = md5(f.read())
|
|
hash_to_file[h] = control_file.replace(common.autotest_dir, '')\
|
|
.lstrip('/')
|
|
print "HF: %d -- len=%d" % (time.time()-now, len(hash_to_file))
|
|
|
|
afe_job_ids = set(map(lambda t: t.job.afe_job_id, all_fail))
|
|
afe_jobs = afe_models.Job.objects.select_related('parent_job')\
|
|
.filter(id__in=afe_job_ids)
|
|
print "AJ: %d -- len=%d" % (time.time()-now, len(afe_jobs))
|
|
|
|
job_to_hash = {}
|
|
for job in afe_jobs:
|
|
job_to_hash[job.id] = md5(job.control_file)
|
|
print "JH: %d -- len=%d" % (time.time()-now, len(job_to_hash))
|
|
|
|
job_to_suite = {}
|
|
rgx = re.compile("test_suites/control.(\w+)")
|
|
for job in afe_jobs:
|
|
job_id = job.parent_job
|
|
if not job_id:
|
|
job_id = job
|
|
x = rgx.search(job_id.name)
|
|
if not x:
|
|
print job_id.name
|
|
continue
|
|
job_to_suite[job.id] = x.groups(1)[0]
|
|
|
|
def collect_by_suite_name(d, t):
|
|
s = job_to_suite.get(t.job.afe_job_id, None)
|
|
d.setdefault((s, t.test), []).append(t)
|
|
return d
|
|
by_name = reduce(collect_by_suite_name, all_fail, {})
|
|
print "BN: %d -- len=%d" % (time.time()-now, len(by_name))
|
|
|
|
for (s, testname), tests in by_name.iteritems():
|
|
for test in tests:
|
|
h = job_to_hash[test.job.afe_job_id]
|
|
if h in hash_to_file:
|
|
print "%s,%s,%s" % (s, testname, hash_to_file[h])
|
|
break
|
|
else:
|
|
print "%s,%s,?" % (s, testname)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|