1edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# Copyright 2015 The Chromium Authors. All rights reserved. 2edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# Use of this source code is governed by a BSD-style license that can be 3edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# found in the LICENSE file. 4edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 5edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport unittest 6edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 7edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport mock 8edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport webapp2 9edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairimport webtest 10edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 11cef7893435aa41160dd1255c43cb8498279738ccChris Craik# pylint: disable=unused-import 12cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom dashboard import mock_oauth2_decorator 13cef7893435aa41160dd1255c43cb8498279738ccChris Craik# pylint: enable=unused-import 14cef7893435aa41160dd1255c43cb8498279738ccChris Craik 15edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import associate_alerts 16cef7893435aa41160dd1255c43cb8498279738ccChris Craikfrom dashboard import issue_tracker_service 17edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import testing_common 18edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard import utils 19edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard.models import anomaly 20edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard.models import sheriff 21edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairfrom dashboard.models import stoppage_alert 22edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 23edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 24edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairclass AssociateAlertsTest(testing_common.TestCase): 25edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 26edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def setUp(self): 27edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair super(AssociateAlertsTest, self).setUp() 28edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair app = webapp2.WSGIApplication([( 29edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts', associate_alerts.AssociateAlertsHandler)]) 30edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.testapp = webtest.TestApp(app) 31edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair testing_common.SetSheriffDomains(['chromium.org']) 32edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.SetCurrentUser('foo@chromium.org', is_admin=True) 33edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 34edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def _AddSheriff(self): 35edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """Adds a Sheriff and returns its key.""" 36edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair return sheriff.Sheriff( 37edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair id='Chromium Perf Sheriff', email='sullivan@google.com').put() 38edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 39edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def _AddTests(self): 40edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """Adds sample Tests and returns a list of their keys.""" 41edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair testing_common.AddTests(['ChromiumGPU'], ['linux-release'], { 42edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 'scrolling-benchmark': { 43edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 'first_paint': {}, 44edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 'mean_frame_time': {}, 45edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair } 46edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair }) 47edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair return map(utils.TestKey, [ 48edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 'ChromiumGPU/linux-release/scrolling-benchmark/first_paint', 49edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 'ChromiumGPU/linux-release/scrolling-benchmark/mean_frame_time', 50edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair ]) 51edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 52edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def _AddAnomalies(self): 53edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """Adds sample Anomaly data and returns a dict of revision to key.""" 54edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair sheriff_key = self._AddSheriff() 55edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair test_keys = self._AddTests() 56edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = {} 57edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 58edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Add anomalies to the two tests alternately. 59edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair for end_rev in range(10000, 10120, 10): 60edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair test_key = test_keys[0] if end_rev % 20 == 0 else test_keys[1] 61edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair anomaly_key = anomaly.Anomaly( 62edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair start_revision=(end_rev - 5), end_revision=end_rev, test=test_key, 63edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair median_before_anomaly=100, median_after_anomaly=200, 64edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair sheriff=sheriff_key).put() 65edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[end_rev] = anomaly_key.urlsafe() 66edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 67edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Add an anomaly that overlaps. 68edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair anomaly_key = anomaly.Anomaly( 69edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair start_revision=9990, end_revision=9996, test=test_keys[0], 70edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair median_before_anomaly=100, median_after_anomaly=200, 71edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair sheriff=sheriff_key).put() 72edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[9996] = anomaly_key.urlsafe() 73edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 74edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Add an anomaly that overlaps and has bug ID. 75edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair anomaly_key = anomaly.Anomaly( 76edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair start_revision=9990, end_revision=9997, test=test_keys[0], 77edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair median_before_anomaly=100, median_after_anomaly=200, 78edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair sheriff=sheriff_key, bug_id=12345).put() 79edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[9997] = anomaly_key.urlsafe() 80edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair return key_map 81edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 82edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_NoKeys_ShowsError(self): 83edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get('/associate_alerts') 84edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('<div class="error">', response.body) 85edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 86edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_SameAsPost(self): 87edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair get_response = self.testapp.get('/associate_alerts') 88edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair post_response = self.testapp.post('/associate_alerts') 89edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual(get_response.body, post_response.body) 90edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 91edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_InvalidBugId_ShowsError(self): 92edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = self._AddAnomalies() 93edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get( 94edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts?keys=%s&bug_id=foo' % key_map[9996]) 95edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('<div class="error">', response.body) 96edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('Invalid bug ID', response.body) 97edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 98cef7893435aa41160dd1255c43cb8498279738ccChris Craik # Mocks fetching bugs from issue tracker. 99cef7893435aa41160dd1255c43cb8498279738ccChris Craik @mock.patch('issue_tracker_service.discovery.build', mock.MagicMock()) 100cef7893435aa41160dd1255c43cb8498279738ccChris Craik @mock.patch.object( 101cef7893435aa41160dd1255c43cb8498279738ccChris Craik issue_tracker_service.IssueTrackerService, 'List', 102cef7893435aa41160dd1255c43cb8498279738ccChris Craik mock.MagicMock(return_value={ 103cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'items': [ 104cef7893435aa41160dd1255c43cb8498279738ccChris Craik { 105cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'id': 12345, 106cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'summary': '5% regression in bot/suite/x at 10000:20000', 107cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'state': 'open', 108cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'status': 'New', 109cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'author': {'name': 'exam...@google.com'}, 110cef7893435aa41160dd1255c43cb8498279738ccChris Craik }, 111cef7893435aa41160dd1255c43cb8498279738ccChris Craik { 112cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'id': 13579, 113cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'summary': '1% regression in bot/suite/y at 10000:20000', 114cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'state': 'closed', 115cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'status': 'WontFix', 116cef7893435aa41160dd1255c43cb8498279738ccChris Craik 'author': {'name': 'exam...@google.com'}, 117cef7893435aa41160dd1255c43cb8498279738ccChris Craik }, 118cef7893435aa41160dd1255c43cb8498279738ccChris Craik ]})) 119edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_NoBugId_ShowsDialog(self): 120edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # When a GET request is made with some anomaly keys but no bug ID, 121edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # A HTML form is shown for the user to input a bug number. 122edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = self._AddAnomalies() 123edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get('/associate_alerts?keys=%s' % key_map[10000]) 124edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The response contains a table of recent bugs and a form. 125edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('12345', response.body) 126edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('13579', response.body) 127edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('<form', response.body) 128edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 129edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_WithBugId_AlertIsAssociatedWithBugId(self): 130edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # When the bug ID is given and the alerts overlap, then the Anomaly 131cef7893435aa41160dd1255c43cb8498279738ccChris Craik # entities are updated and there is a response indicating success. 132edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = self._AddAnomalies() 133edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get( 134edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts?keys=%s,%s&bug_id=12345' % ( 135edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[9996], key_map[10000])) 136edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The response page should have a bug number. 137edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('12345', response.body) 138edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The Anomaly entities should be updated. 139edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair for anomaly_entity in anomaly.Anomaly.query().fetch(): 140edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair if anomaly_entity.end_revision in (10000, 9996): 141edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual(12345, anomaly_entity.bug_id) 142edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair elif anomaly_entity.end_revision != 9997: 143edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIsNone(anomaly_entity.bug_id) 144edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 145edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_WithStoppageAlert_ChangesAlertBugId(self): 146edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair test_keys = self._AddTests() 147edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair rows = testing_common.AddRows(utils.TestPath(test_keys[0]), {10, 20}) 148edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair alert_key = stoppage_alert.CreateStoppageAlert( 149edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair test_keys[0].get(), rows[0]).put() 150edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.testapp.get( 151edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts?bug_id=123&keys=%s' % alert_key.urlsafe()) 152edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual(123, alert_key.get().bug_id) 153edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 154edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_TargetBugHasNoAlerts_DoesNotAskForConfirmation(self): 155edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Associating alert with bug ID that has no alerts is always OK. 156edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = self._AddAnomalies() 157edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get( 158edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts?keys=%s,%s&bug_id=578' % ( 159edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[9996], key_map[10000])) 160edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The response page should have a bug number. 161edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('578', response.body) 162edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The Anomaly entities should be updated. 163edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual( 164edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 578, anomaly.Anomaly.query( 165edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair anomaly.Anomaly.end_revision == 9996).get().bug_id) 166edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual( 167edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 578, anomaly.Anomaly.query( 168edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair anomaly.Anomaly.end_revision == 10000).get().bug_id) 169edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 170edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_NonOverlappingAlerts_AsksForConfirmation(self): 171edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Associating alert with bug ID that contains non-overlapping revision 172edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # ranges should show a confirmation page. 173edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = self._AddAnomalies() 174edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get( 175edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts?keys=%s,%s&bug_id=12345' % ( 176edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[10000], key_map[10010])) 177edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The response page should show confirmation page. 178edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('Do you want to continue?', response.body) 179edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The Anomaly entities should not be updated. 180edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair for anomaly_entity in anomaly.Anomaly.query().fetch(): 181edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair if anomaly_entity.end_revision != 9997: 182edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIsNone(anomaly_entity.bug_id) 183edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 184edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testGet_WithConfirm_AssociatesWithNewBugId(self): 185edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Associating alert with bug ID and with confirmed non-overlapping revision 186edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # range should update alert with bug ID. 187edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map = self._AddAnomalies() 188edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair response = self.testapp.get( 189edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '/associate_alerts?confirm=true&keys=%s,%s&bug_id=12345' % ( 190edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key_map[10000], key_map[10010])) 191edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The response page should have the bug number. 192edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIn('12345', response.body) 193edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # The Anomaly entities should be updated. 194edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair for anomaly_entity in anomaly.Anomaly.query().fetch(): 195edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair if anomaly_entity.end_revision in (10000, 10010): 196edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual(12345, anomaly_entity.bug_id) 197edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair elif anomaly_entity.end_revision != 9997: 198edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIsNone(anomaly_entity.bug_id) 199edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 200edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testRevisionRangeFromSummary(self): 201edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # If the summary is in the expected format, a pair is returned. 202edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertEqual( 203edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair (10000, 10500), 204edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair associate_alerts._RevisionRangeFromSummary( 205edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair '1% regression in bot/my_suite/test at 10000:10500')) 206edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # Otherwise None is returned. 207edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertIsNone( 208edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair associate_alerts._RevisionRangeFromSummary( 209edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 'Regression in rev ranges 12345 to 20000')) 210edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 211edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testRangesOverlap_NonOverlapping_ReturnsFalse(self): 212edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertFalse(associate_alerts._RangesOverlap((1, 5), (6, 9))) 213edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertFalse(associate_alerts._RangesOverlap((6, 9), (1, 5))) 214edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 215edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testRangesOverlap_NoneGiven_ReturnsFalse(self): 216edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertFalse(associate_alerts._RangesOverlap((1, 5), None)) 217edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertFalse(associate_alerts._RangesOverlap(None, (1, 5))) 218edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertFalse(associate_alerts._RangesOverlap(None, None)) 219edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 220edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testRangesOverlap_OneIncludesOther_ReturnsTrue(self): 221edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair # True if one range envelopes the other. 222edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertTrue(associate_alerts._RangesOverlap((1, 9), (2, 5))) 223edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertTrue(associate_alerts._RangesOverlap((2, 5), (1, 9))) 224edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 225edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testRangesOverlap_PartlyOverlap_ReturnsTrue(self): 226edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertTrue(associate_alerts._RangesOverlap((1, 6), (5, 9))) 227edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertTrue(associate_alerts._RangesOverlap((5, 9), (1, 6))) 228edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 229edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair def testRangesOverlap_CommonBoundary_ReturnsTrue(self): 230edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertTrue(associate_alerts._RangesOverlap((1, 6), (6, 9))) 231edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair self.assertTrue(associate_alerts._RangesOverlap((6, 9), (1, 6))) 232edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 233edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 234edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairif __name__ == '__main__': 235edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair unittest.main() 236