Skip to content

Commit fa0b7f0

Browse files
committed
Add post log parser to look for repeated test runs and annotate as intermittent.
1 parent a0ec076 commit fa0b7f0

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

tests/log_parser/test_tasks.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,23 @@ def test_bug_suggestion_line_no_stb(
228228
),
229229
}
230230
]
231+
232+
233+
@pytest.mark.django_db
234+
def test_confirm_failure_intermittent(
235+
failure_classifications, jobs_with_local_log, sample_push, test_repository
236+
):
237+
"""
238+
TODO: write tests for testing intermittents.py handling in the parser.
239+
* test retrigger with 1 similar failure, but both jobs have different failures - both orange
240+
* test 5 jobs, 2 fail, 2 fail for other reasons, 1 pass - all green
241+
* test infra/tooling error + 1x green - both green
242+
* test failure w/3x tests + 3x confirm-failure tasks green - all green
243+
* test failure w/3x tests + 2x confirm-failure tasks green - original task still orange
244+
"""
245+
store_push_data(test_repository, sample_push)
246+
for job in jobs_with_local_log:
247+
job["job"]["result"] = "testfailed"
248+
job["revision"] = sample_push[0]["revision"]
249+
store_job_data(test_repository, jobs_with_local_log)
250+
assert 1 == 0
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from treeherder.model.models import Group, GroupStatus, Job
2+
3+
4+
def check_and_mark_intermittent(job_id):
5+
current_job = Job.objects.get(id=job_id)
6+
7+
if current_job.job_type.name.endswith("-cf"):
8+
jtname = [current_job.job_type.name, current_job.job_type.name.strip("-cf")]
9+
else:
10+
jtname = [current_job.job_type.name, f"{current_job.job_type.name}-cf"]
11+
12+
all_groups = Group.objects.filter(
13+
job_logs__job__push__id=current_job.push.id,
14+
job_logs__job__job_type__name__in=jtname,
15+
group_result__status__in=[GroupStatus.OK, GroupStatus.ERROR],
16+
).values(
17+
"name",
18+
"job_logs__job__id",
19+
"group_result__status",
20+
)
21+
22+
groups = {}
23+
jobs = {}
24+
for item in all_groups:
25+
if item["name"] not in groups:
26+
groups[item["name"]] = {}
27+
if item["job_logs__job__id"] not in groups[item["name"]]:
28+
groups[item["name"]][item["job_logs__job__id"]] = item["group_result__status"]
29+
30+
if item["job_logs__job__id"] not in jobs:
31+
jobs[item["job_logs__job__id"]] = {}
32+
if item["name"] not in jobs[item["job_logs__job__id"]]:
33+
jobs[item["job_logs__job__id"]][item["name"]] = item["group_result__status"]
34+
35+
if len(jobs.keys()) <= 1:
36+
# zero jobs == no groups reported (i.e. marionette)
37+
# 1 job == no additional data
38+
return
39+
40+
for job in jobs.keys():
41+
# for each similar task.label, ensure all groups have >=50% pass rate, if so flag failing
42+
# job as intermittent. for non test failures, ensure all groups are green
43+
all_green = True
44+
failed_groups = [g for g in jobs[job] if int(jobs[job][g]) == GroupStatus.ERROR]
45+
for group in failed_groups:
46+
all_status = [groups[group][j] for j in groups[group]]
47+
pass_rate = len([s for s in all_status if s == GroupStatus.OK]) / len(all_status)
48+
if pass_rate < 0.5:
49+
all_green = False
50+
break
51+
52+
target_job = Job.objects.filter(id=job)
53+
54+
if all_green and target_job[0].result != "success":
55+
target_job.update(failure_classification_id=4)

treeherder/log_parser/tasks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from treeherder.model.models import Job, JobLog
1414
from treeherder.workers.task import retryable_task
1515

16-
from . import failureline
16+
from . import failureline, intermittents
1717

1818
logger = logging.getLogger(__name__)
1919

@@ -81,6 +81,8 @@ def store_failure_lines(job_log):
8181
errorsummary file."""
8282
logger.info("Running store_failure_lines for job %s", job_log.job.id)
8383
failureline.store_failure_lines(job_log)
84+
logger.info("Running check_and_mark_intermittent for job %s", job_log.job.id)
85+
intermittents.check_and_mark_intermittent(job_log.job.id)
8486

8587

8688
def post_log_artifacts(job_log):

0 commit comments

Comments
 (0)