Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: appengine/chromium_try_flakes/handlers/test/flake_issues_test.py

Issue 1660043002: Move flaky run processing into a taskqueue (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Added new files Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import datetime 5 import datetime
6 import json
6 import mock 7 import mock
7 8
8 from google.appengine.datastore import datastore_stub_util 9 from google.appengine.datastore import datastore_stub_util
9 from google.appengine.ext import ndb 10 from google.appengine.ext import ndb
10 11
11 from handlers.flake_issues import ProcessIssue 12 from handlers.flake_issues import ProcessIssue
12 import main 13 import main
13 from model.flake import Flake, FlakyRun, FlakeOccurrence 14 from model.flake import Flake, FlakyRun, FlakeOccurrence
14 from model.build_run import PatchsetBuilderRuns, BuildRun 15 from model.build_run import PatchsetBuilderRuns, BuildRun
15 from testing_utils import testing 16 from testing_utils import testing
16 from time_functions.testing import mock_datetime_utc 17 from time_functions.testing import mock_datetime_utc
17 18
18 19
20 TEST_BUILDBOT_JSON_REPLY = json.dumps({
21 'steps': [
22 # Simple case.
23 {'results': [2], 'name': 'foo1', 'text': ['bar1']},
24
25 # Invalid test results.
26 {'results': [2], 'name': 'foo2', 'text': ['TEST RESULTS WERE INVALID']},
27
28 # GTest tests.
29 {
30 'results': [2],
31 'name': 'foo3',
32 'text': ['failures:<br/>bar2<br/>bar3<br/><br/>ignored:<br/>bar4']
33 },
34
35 # GPU tests.
36 {
37 'results': [2],
38 'name': 'foo4',
39 'text': ['<"http://url/path?query&tests=bar5,bar6,,bar7">']
40 },
41
42 # Ignore non-success non-failure results (7 is TRY_PENDING).
43 {'results': [7], 'name': 'foo5', 'text': ['bar8']},
44
45 # Ignore steps that are failing without patch too (ToT is broken).
46 {'results': [2], 'name': 'foo6 (with patch)', 'text': ['bar9']},
47 {'results': [2], 'name': 'foo6 (without patch)', 'text': ['bar9']},
48
49 # Ignore steps that are duplicating error in another step.
50 {'results': [2], 'name': 'steps', 'text': ['bar10']},
51 {'results': [2], 'name': '[swarming] foo7', 'text': ['bar11']},
52 {'results': [2], 'name': 'presubmit', 'text': ['bar12']},
53 {'results': [2], 'name': 'recipe failure reason', 'text': ['bar12a']},
54 {'results': [2], 'name': 'test results', 'text': ['bar12b']},
55 {'results': [2], 'name': 'Uncaught Exception', 'text': ['bar12c']},
56 {'results': [2], 'name': 'bot_update', 'text': ['bot_update PATCH FAILED']},
57
58 # Only count first step (with patch) and ignore summary step.
59 {'results': [2], 'name': 'foo8 (with patch)', 'text': ['bar13']},
60 {'results': [0], 'name': 'foo8 (without patch)', 'text': ['bar14']},
61 {'results': [2], 'name': 'foo8', 'text': ['bar15']},
62
63 # GTest without flakes.
64 {
65 'results': [2],
66 'name': 'foo9',
67 'text': ['failures:<br/><br/><br/>']
68 },
69 ]
70 })
71
72
19 class MockComment(object): 73 class MockComment(object):
20 def __init__(self, created, author, comment=None): 74 def __init__(self, created, author, comment=None):
21 self.created = created 75 self.created = created
22 self.author = author 76 self.author = author
23 self.comment = comment 77 self.comment = comment
24 78
25 class MockIssue(object): 79 class MockIssue(object):
26 def __init__(self, issue_entry): 80 def __init__(self, issue_entry):
27 self.created = issue_entry.get('created') 81 self.created = issue_entry.get('created')
28 self.summary = issue_entry.get('summary') 82 self.summary = issue_entry.get('summary')
(...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after
532 Flake(name='foo', occurrences=[fr1, fr2, fr3, fr4, fr5])), 586 Flake(name='foo', occurrences=[fr1, fr2, fr3, fr4, fr5])),
533 datetime.datetime(2015, 10, 18, 8, 0, 0)) 587 datetime.datetime(2015, 10, 18, 8, 0, 0))
534 self.assertEqual( 588 self.assertEqual(
535 ProcessIssue._get_first_flake_occurrence_time( 589 ProcessIssue._get_first_flake_occurrence_time(
536 Flake(name='foo', occurrences=[fr1, fr2])), 590 Flake(name='foo', occurrences=[fr1, fr2])),
537 datetime.datetime(2015, 10, 12, 8, 0, 0)) 591 datetime.datetime(2015, 10, 12, 8, 0, 0))
538 self.assertEqual( 592 self.assertEqual(
539 ProcessIssue._get_first_flake_occurrence_time( 593 ProcessIssue._get_first_flake_occurrence_time(
540 Flake(name='foo', occurrences=[fr5])), 594 Flake(name='foo', occurrences=[fr5])),
541 datetime.datetime(2015, 10, 19, 11, 0, 0)) 595 datetime.datetime(2015, 10, 19, 11, 0, 0))
596
597 class CreateFlakyRunTestCase(testing.AppengineTestCase):
598 app_module = main.app
599
600 # This is needed to be able to test handlers using cross-group transactions.
601 datastore_stub_consistency_policy = (
602 datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1))
603
604 def _create_build_runs(self, ts, tf):
605 pbr = PatchsetBuilderRuns(
606 issue=123456789, patchset=20001, master='test.master',
607 builder='test-builder').put()
608 br_f = BuildRun(parent=pbr, buildnumber=100, result=2, time_started=ts,
609 time_finished=tf).put()
610 br_s = BuildRun(parent=pbr, buildnumber=101, result=0, time_started=ts,
611 time_finished=tf).put()
612 return br_f, br_s
613
614 def test_get_flaky_run_reason_ignores_invalid_json(self):
615 now = datetime.datetime.utcnow()
616 br_f, br_s = self._create_build_runs(now - datetime.timedelta(hours=1), now)
617
618 urlfetch_mock = mock.Mock()
619 urlfetch_mock.return_value.content = 'invalid-json'
620
621 with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock):
622 self.test_app.post('/issues/create_flaky_run',
623 {'failure_run_key': br_f.urlsafe(),
624 'success_run_key': br_s.urlsafe()})
625
626 def test_handles_incorrect_parameters(self):
627 self.test_app.post('/issues/create_flaky_run', {}, status=400)
628
629 def test_get_flaky_run_reason(self):
630 now = datetime.datetime.utcnow()
631 br_f, br_s = self._create_build_runs(now - datetime.timedelta(hours=1), now)
632
633 urlfetch_mock = mock.Mock()
634 urlfetch_mock.return_value.content = TEST_BUILDBOT_JSON_REPLY
635
636 # We also create one Flake to test that it is correctly updated. Other Flake
637 # entities will be created automatically.
638 Flake(id='bar5', name='bar5', occurrences=[],
639 last_time_seen=datetime.datetime.min).put()
640
641 with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock):
642 self.test_app.post('/issues/create_flaky_run',
643 {'failure_run_key': br_f.urlsafe(),
644 'success_run_key': br_s.urlsafe()})
645
646 flaky_runs = FlakyRun.query().fetch(100)
647 self.assertEqual(len(flaky_runs), 1)
648 flaky_run = flaky_runs[0]
649 self.assertEqual(flaky_run.failure_run, br_f)
650 self.assertEqual(flaky_run.success_run, br_s)
651 self.assertEqual(flaky_run.failure_run_time_finished, now)
652 self.assertEqual(flaky_run.failure_run_time_started,
653 now - datetime.timedelta(hours=1))
654
655 # Verify that we've used correct URL to access buildbot JSON endpoint.
656 urlfetch_mock.assert_called_once_with(
657 'http://build.chromium.org/p/test.master/json/builders/test-builder/'
658 'builds/100')
659
660 # Expected flakes to be found: list of (step_name, test_name).
661 expected_flakes = [
662 ('foo1', 'bar1'), ('foo2', 'TEST RESULTS WERE INVALID'),
663 ('foo3', 'bar2'), ('foo3', 'bar3'), ('foo4', 'bar5'), ('foo4', 'bar6'),
664 ('foo4', 'bar7'), ('foo8 (with patch)', 'bar13'),
665 ]
666
667 flake_occurrences = flaky_run.flakes
668 self.assertEqual(len(flake_occurrences), len(expected_flakes))
669 actual_flake_occurrences = [
670 (fo.name, fo.failure) for fo in flake_occurrences]
671 self.assertEqual(expected_flakes, actual_flake_occurrences)
672
673 # We compare sets below, because order of flakes returned by datastore
674 # doesn't have to be same as steps above.
675 flakes = Flake.query().fetch()
676 self.assertEqual(len(flakes), len(expected_flakes))
677 expected_flake_names = set([ef[1] for ef in expected_flakes])
678 actual_flake_names = set([f.name for f in flakes])
679 self.assertEqual(expected_flake_names, actual_flake_names)
680
681 for flake in flakes:
682 self.assertEqual(flake.occurrences, [flaky_run.key])
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698