Skip to content

Commit adf43e6

Browse files
committed
add support for non try branches in a time series
1 parent fa0b7f0 commit adf43e6

14 files changed

+445
-70
lines changed

tests/conftest.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,13 @@ def fixture_create_jobs(test_repository, failure_classifications):
315315

316316
def create(jobs):
317317
store_job_data(test_repository, jobs)
318-
return [th_models.Job.objects.get(id=i) for i in range(1, len(jobs) + 1)]
318+
retval = []
319+
for i in range(1, len(jobs) + 1):
320+
try:
321+
retval.append(th_models.Job.objects.get(id=i))
322+
except Exception:
323+
pass
324+
return retval
319325

320326
return create
321327

@@ -379,6 +385,29 @@ def task_mock(*args, **kwargs):
379385
monkeypatch.setattr(tasks, "parse_logs", task_mock)
380386

381387

388+
@pytest.fixture
389+
def mock_parser(monkeypatch):
390+
from celery import shared_task
391+
392+
from treeherder.log_parser import failureline
393+
394+
@shared_task
395+
def fetch_mock(*args, **kwargs):
396+
file_name = args[0].url.split("/")[-1]
397+
try:
398+
data_path = os.path.join(SAMPLE_DATA_PATH, "logs", file_name)
399+
with open(data_path) as f:
400+
fetch_data = f.read()
401+
except Exception:
402+
return
403+
404+
if not fetch_data:
405+
return
406+
return (json.loads(item.strip("\n")) for item in fetch_data.splitlines())
407+
408+
monkeypatch.setattr(failureline, "fetch_log", fetch_mock)
409+
410+
382411
@pytest.fixture
383412
def taskcluster_notify_mock(monkeypatch):
384413
mock = MagicMock()

tests/log_parser/test_store_failure_lines.py

Lines changed: 218 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55
from django.conf import settings
66
from requests.exceptions import HTTPError
77

8-
from treeherder.log_parser.failureline import (
9-
get_group_results,
10-
store_failure_lines,
11-
write_failure_lines,
12-
)
13-
from treeherder.model.models import FailureLine, Group, GroupStatus, JobLog
8+
from treeherder.log_parser.failureline import get_group_results, write_failure_lines
9+
from treeherder.log_parser.tasks import store_failure_lines
10+
from treeherder.model.models import FailureLine, Group, GroupStatus, Job, JobLog
1411

1512
from ..sampledata import SampleData
1613

@@ -140,7 +137,7 @@ def test_store_error_summary_500(activate_responses, test_repository, test_job):
140137
assert log_obj.status == JobLog.FAILED
141138

142139

143-
def test_store_error_summary_duplicate(activate_responses, test_repository, test_job):
140+
def test_store_error_summary_duplicate(activate_responses, test_repository, test_job, mock_parser):
144141
log_url = "http://my-log.mozilla.org"
145142
log_obj = JobLog.objects.create(job=test_job, name="errorsummary_json", url=log_url)
146143

@@ -173,13 +170,13 @@ def test_store_error_summary_group_status(activate_responses, test_repository, t
173170
ok_groups = Group.objects.filter(group_result__status=GroupStatus.OK)
174171
error_groups = Group.objects.filter(group_result__status=GroupStatus.ERROR)
175172

176-
assert ok_groups.count() == 28
177-
assert error_groups.count() == 1
173+
assert ok_groups.count() == 26
174+
assert error_groups.count() == 3
178175
assert log_obj.groups.count() == 29
179176

180177
assert log_obj.groups.all().first().name == "dom/base/test/browser.ini"
181178
assert ok_groups.first().name == "dom/base/test/browser.ini"
182-
assert error_groups.first().name == "toolkit/components/pictureinpicture/tests/browser.ini"
179+
assert error_groups.first().name == "dom/workers/test/browser.ini"
183180

184181

185182
def test_group_status_duration(activate_responses, test_repository, test_job):
@@ -238,3 +235,214 @@ def test_get_group_results_with_colon(activate_responses, test_repository, test_
238235
assert task_groups[
239236
"toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini:toolkit/components/extensions/test/xpcshell/xpcshell-common-e10s.ini"
240237
]
238+
239+
240+
def mock_full_log_parser(job_logs, mock_parser):
241+
from treeherder.log_parser.tasks import store_failure_lines
242+
243+
try:
244+
# note: I was using parse_logs, but that is less deterministic
245+
for jl in job_logs:
246+
store_failure_lines(jl)
247+
except:
248+
raise
249+
250+
251+
def create_errorsummary_job(base_job, create_jobs, log_filenames):
252+
import copy
253+
import random
254+
255+
job_defs = []
256+
urls = []
257+
for log_filename in log_filenames:
258+
log_path = SampleData().get_log_path(log_filename)
259+
log_url = f"http://my-log.mozilla.org/{log_path}"
260+
261+
with open(log_path) as log_handler:
262+
responses.add(responses.GET, log_url, body=log_handler.read(), status=200)
263+
264+
job_def = copy.deepcopy(base_job)
265+
266+
task_ending = ""
267+
if "_cf" in log_filename:
268+
task_ending = "-cf"
269+
270+
job_def["job"].update(
271+
{
272+
"status": "completed",
273+
"result": "success" if "_pass" in log_filename else "testfailed",
274+
"name": f"{job_def['job']['name']}{task_ending}",
275+
"reference_data_name": job_def["job"]["reference_data_name"].replace(
276+
"a", str(random.randint(0, 9))
277+
),
278+
"job_guid": job_def["job"]["job_guid"]
279+
.replace("e", str(random.randint(0, 9)))
280+
.replace("d", str(random.randint(0, 9))),
281+
"start_timestamp": job_def["job"]["start_timestamp"]
282+
+ 100
283+
+ random.randint(0, 100)
284+
+ random.randint(0, 100),
285+
"taskcluster_task_id": job_def["job"]["taskcluster_task_id"].replace(
286+
"T", str(random.randint(0, 9))
287+
),
288+
"taskcluster_retry_id": "0",
289+
}
290+
)
291+
job_defs.append(job_def)
292+
urls.append(log_url)
293+
294+
jobs = create_jobs(job_defs)
295+
296+
index = 0
297+
for job in jobs:
298+
log_obj = JobLog.objects.create(job=job, name="errorsummary_json", url=urls[index])
299+
store_failure_lines(log_obj)
300+
index += 1
301+
302+
return jobs
303+
304+
305+
def verify_classification_id(jobs, job1_fcid, job2_fcid):
306+
j1 = Job.objects.filter(id=jobs[0].id)
307+
j2 = Job.objects.filter(id=jobs[1].id)
308+
assert j1[0].failure_classification.id == job1_fcid
309+
assert j2[0].failure_classification.id == job2_fcid
310+
311+
312+
"""
313+
TODO: write tests for testing intermittents.py handling in the parser.
314+
* not supported yet: test infra/tooling error + 1x green - both green
315+
* test multiple push ids
316+
"""
317+
318+
319+
def test_infra_no_intermittent(activate_responses, hundred_job_blobs, mock_parser, create_jobs):
320+
# test fails, retrigger fails on infra, both unchanged
321+
log_filenames = [
322+
"mochitest-browser-chrome_errorsummary.log",
323+
"mochitest-browser-chrome_infra_errorsummary.log",
324+
]
325+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
326+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
327+
assert len(jobs) == len(log_filenames)
328+
329+
# this will parse and check for intermittents
330+
mock_full_log_parser(job_logs, mock_parser)
331+
verify_classification_id(jobs, 1, 1)
332+
333+
334+
def test_infra_intermittent(activate_responses, hundred_job_blobs, mock_parser, create_jobs):
335+
# test passes, retrigger is infra, infra -> unchanged (new feature needed to make intermittent)
336+
log_filenames = [
337+
"mochitest-browser-chrome_infra_errorsummary.log",
338+
"mochitest-browser-chrome_pass_errorsummary.log",
339+
]
340+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
341+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
342+
assert len(jobs) == len(log_filenames)
343+
344+
# this will parse and check for intermittents
345+
mock_full_log_parser(job_logs, mock_parser)
346+
verify_classification_id(jobs, 4, 1)
347+
348+
349+
def test_multiple_jobs_intermittent(
350+
activate_responses, hundred_job_blobs, mock_parser, create_jobs
351+
):
352+
# two sets of tests fail, both failures should be intermittent
353+
log_filenames = [
354+
"mochitest-browser-chrome_errorsummary.log",
355+
"mochitest-browser-chrome_2_errorsummary.log",
356+
"mochitest-browser-chrome_pass_errorsummary.log",
357+
]
358+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
359+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
360+
assert len(jobs) == len(log_filenames)
361+
362+
# this will parse and check for intermittents
363+
mock_full_log_parser(job_logs, mock_parser)
364+
verify_classification_id(jobs, 4, 4)
365+
366+
367+
def test_confirm_failure_no_intermittent(
368+
activate_responses, hundred_job_blobs, mock_parser, create_jobs
369+
):
370+
# test fails, -cf fails on same group, both unchanged
371+
log_filenames = [
372+
"mochitest-browser-chrome_errorsummary.log",
373+
"mochitest-browser-chrome_cf1_errorsummary.log",
374+
"mochitest-browser-chrome_cf2_errorsummary.log",
375+
]
376+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
377+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
378+
assert len(jobs) == len(log_filenames)
379+
380+
# this will parse and check for intermittents
381+
mock_full_log_parser(job_logs, mock_parser)
382+
verify_classification_id(jobs, 1, 1)
383+
384+
385+
def test_confirm_failure_partial_intermittent(
386+
activate_responses, hundred_job_blobs, mock_parser, create_jobs
387+
):
388+
# test fails, -cf fails on same group, both unchanged
389+
log_filenames = [
390+
"mochitest-browser-chrome_errorsummary.log",
391+
"mochitest-browser-chrome_cf1_errorsummary.log",
392+
]
393+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
394+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
395+
assert len(jobs) == len(log_filenames)
396+
397+
# this will parse and check for intermittents
398+
mock_full_log_parser(job_logs, mock_parser)
399+
verify_classification_id(jobs, 1, 1)
400+
401+
402+
def test_confirm_failure_pass_intermittent(
403+
activate_responses, hundred_job_blobs, mock_parser, create_jobs
404+
):
405+
# test fails, -cf passes, original -> intermittent
406+
log_filenames = [
407+
"mochitest-browser-chrome_errorsummary.log",
408+
"mochitest-browser-chrome_cf1_pass_errorsummary.log",
409+
"mochitest-browser-chrome_cf2_pass_errorsummary.log",
410+
"mochitest-browser-chrome_cf3_pass_errorsummary.log",
411+
]
412+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
413+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
414+
assert len(jobs) == len(log_filenames)
415+
416+
# this will parse and check for intermittents
417+
mock_full_log_parser(job_logs, mock_parser)
418+
verify_classification_id(jobs, 4, 1)
419+
420+
421+
def test_retrigger_no_intermittent(activate_responses, hundred_job_blobs, mock_parser, create_jobs):
422+
# test fails, retrigger fails on same group, both unchanged
423+
log_filenames = [
424+
"mochitest-browser-chrome_errorsummary.log",
425+
"mochitest-browser-chrome_errorsummary.log",
426+
]
427+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
428+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
429+
assert len(jobs) == len(log_filenames)
430+
431+
# this will parse and check for intermittents
432+
mock_full_log_parser(job_logs, mock_parser)
433+
verify_classification_id(jobs, 1, 1)
434+
435+
436+
def test_retrigger_intermittent(activate_responses, hundred_job_blobs, mock_parser, create_jobs):
437+
# test fails, retrigger has different failures on same group, both -> intermittent
438+
log_filenames = [
439+
"mochitest-browser-chrome_errorsummary.log",
440+
"mochitest-browser-chrome_2_errorsummary.log",
441+
]
442+
jobs = create_errorsummary_job(hundred_job_blobs[0], create_jobs, log_filenames)
443+
job_logs = JobLog.objects.filter(job_id__in=(j.id for j in jobs))
444+
assert len(jobs) == len(log_filenames)
445+
446+
# this will parse and check for intermittents
447+
mock_full_log_parser(job_logs, mock_parser)
448+
verify_classification_id(jobs, 4, 4)

tests/log_parser/test_tasks.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -228,23 +228,3 @@ 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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{"action": "test_groups", "line": 2, "groups": ["dom/base/test/browser.ini", "browser/components/contextualidentity/test/browser/browser.ini", "browser/base/content/test/tabMediaIndicator/browser.ini", "gfx/tests/browser/browser.ini", "browser/components/downloads/test/browser/browser.ini", "toolkit/components/satchel/test/browser/browser.ini", "toolkit/components/aboutperformance/tests/browser/browser.ini", "toolkit/components/aboutprocesses/tests/browser/browser.ini", "security/manager/ssl/tests/mochitest/browser/browser.ini", "toolkit/components/mozprotocol/tests/browser.ini", "netwerk/test/browser/browser.ini", "browser/base/content/test/siteIdentity/browser.ini", "dom/workers/test/browser.ini", "browser/components/preferences/tests/browser.ini", "browser/base/content/test/about/browser.ini", "browser/base/content/test/popups/browser.ini", "accessible/tests/browser/tree/browser.ini", "toolkit/components/remotepagemanager/tests/browser/browser.ini", "dom/xhr/tests/browser.ini", "dom/security/test/mixedcontentblocker/browser.ini", "browser/components/tests/browser/whats_new_page/browser.ini", "toolkit/content/tests/browser/browser.ini", "dom/broadcastchannel/tests/browser.ini", "caps/tests/mochitest/browser.ini", "browser/components/aboutconfig/test/browser/browser.ini", "browser/components/pocket/test/browser.ini", "accessible/tests/browser/browser.ini", "toolkit/components/pictureinpicture/tests/browser.ini", "dom/ipc/tests/browser.ini", "dom/ipc/tests/JSWindowActor/browser.ini"]}
2+
{"status": "FAIL", "subtest": "Uncaught exception", "group": "browser/base/content/test/popups/browser.ini", "duration": 3141, "action": "test_result", "known_intermittent": [], "test": "browser/base/content/test/popups/browser_fullscreen.js", "message": "undefined - timed out after 50 tries.", "line": 4167, "stack": null, "expected": "PASS"}
3+
{"status": "FAIL", "subtest": "Found an unexpected tab at the end of test run: http://example.com/browser/base/content/test/popups/test-page.html", "group": "browser/base/content/test/popups/browser.ini", "duration": 3141, "action": "test_result", "known_intermittent": [], "test": "browser/base/content/test/popups/browser_fullscreen.js", "message": "", "line": 4172, "stack": null, "expected": "PASS"}
4+
{"status": "FAIL", "subtest": "Test timed out", "group": "browser/base/content/test/popups/browser.ini", "duration": 3141, "action": "test_result", "known_intermittent": [], "test": "browser/base/content/test/popups/browser_videoSelection.js", "message": "", "line": 4269, "stack": null, "expected": "PASS"}
5+
{"status": "FAIL", "subtest": "A promise chain failed to handle a rejection: Video is being cloned visually. - timed out after 50 tries. - stack: (No stack available.)\nRejection date: Thu Oct 08 2020 20:47:46 GMT+0000 (Coordinated Universal Time) - false == true", "group": "browser/base/content/test/popups/browser.ini", "duration": 3141, "action": "test_result", "known_intermittent": [], "test": "browser/base/content/test/popups/browser_videoSelection.js", "message": "JS frame :: resource://testing-common/PromiseTestUtils.jsm :: assertNoUncaughtRejections :: line 265\nStack trace:\nresource://testing-common/PromiseTestUtils.jsm:assertNoUncaughtRejections:265\nchrome://mochikit/content/browser-test.js:nextTest:615\nchrome://mochikit/content/browser-test.js:timeoutFn:1195", "line": 4271, "stack": null, "expected": "PASS"}
6+
{"status": "FAIL", "subtest": "Found a tab after previous test timed out: http://example.com/browser/base/content/test/popups/test-video-selection.html", "group": "browser/base/content/test/popups/browser.ini", "duration": 3141, "action": "test_result", "known_intermittent": [], "test": "browser/base/content/test/popups/browser_videoSelection.js", "message": "", "line": 4275, "stack": null, "expected": "PASS"}
7+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/base/test/browser.ini"}
8+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/components/contextualidentity/test/browser/browser.ini"}
9+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/base/content/test/tabMediaIndicator/browser.ini"}
10+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "gfx/tests/browser/browser.ini"}
11+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/components/downloads/test/browser/browser.ini"}
12+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "toolkit/components/satchel/test/browser/browser.ini"}
13+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "toolkit/components/aboutperformance/tests/browser/browser.ini"}
14+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "toolkit/components/aboutprocesses/tests/browser/browser.ini"}
15+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "security/manager/ssl/tests/mochitest/browser/browser.ini"}
16+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "toolkit/components/mozprotocol/tests/browser.ini"}
17+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "netwerk/test/browser/browser.ini"}
18+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/base/content/test/siteIdentity/browser.ini"}
19+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/workers/test/browser.ini"}
20+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/components/preferences/tests/browser.ini"}
21+
{"status": "OK", "duration": 0, "action": "group_result", "line": 4865, "group": "toolkit/components/pictureinpicture/tests/browser.ini"}
22+
{"status": "ERROR", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/base/content/test/popups/browser.ini"}
23+
{"status": "ERROR", "duration": 3141, "action": "group_result", "line": 4865, "group": "accessible/tests/browser/tree/browser.ini"}
24+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "toolkit/components/remotepagemanager/tests/browser/browser.ini"}
25+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/xhr/tests/browser.ini"}
26+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/security/test/mixedcontentblocker/browser.ini"}
27+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "toolkit/content/tests/browser/browser.ini"}
28+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/broadcastchannel/tests/browser.ini"}
29+
{"status": "OK", "duration": null, "action": "group_result", "line": 4865, "group": "caps/tests/mochitest/browser.ini"}
30+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/components/aboutconfig/test/browser/browser.ini"}
31+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/components/pocket/test/browser.ini"}
32+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "accessible/tests/browser/browser.ini"}
33+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "browser/base/content/test/about/browser.ini"}
34+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/ipc/tests/browser.ini"}
35+
{"status": "OK", "duration": 3141, "action": "group_result", "line": 4865, "group": "dom/ipc/tests/JSWindowActor/browser.ini"}

0 commit comments

Comments
 (0)