コード例 #1
0
ファイル: start_try_job_test.py プロジェクト: neven7/catapult
def _MockFetch(url=None):
    if start_try_job._BISECT_CONFIG_PATH in url:
        return testing_common.FakeResponseObject(
            200, base64.encodestring(_BISECT_CONFIG_CONTENTS))
    elif start_try_job._PERF_CONFIG_PATH in url:
        return testing_common.FakeResponseObject(
            200, base64.encodestring(_PERF_CONFIG_CONTENTS))
コード例 #2
0
ファイル: start_try_job_test.py プロジェクト: neven7/catapult
def _MockMakeRequest(path, *args, **kwargs):  # pylint: disable=unused-argument
    """Mocks out a request, returning a canned response."""
    if path.endswith('xsrf_token'):
        assert kwargs['headers']['X-Requesting-XSRF-Token'] == 1
        return testing_common.FakeResponseObject(200, _FAKE_XSRF_TOKEN)
    if path == 'upload':
        assert kwargs['method'] == 'POST'
        assert _EXPECTED_CONFIG_DIFF in kwargs['body'], (
            '%s\nnot in\n%s\n' % (_EXPECTED_CONFIG_DIFF, kwargs['body']))
        return testing_common.FakeResponseObject(200, _ISSUE_CREATED_RESPONSE)
    if path == '33001/upload_content/1/1001':
        assert kwargs['method'] == 'POST'
        assert _TEST_EXPECTED_CONFIG_CONTENTS in kwargs['body']
        return testing_common.FakeResponseObject(200, 'Dummy content')
    if path == '33001/upload_complete/1':
        assert kwargs['method'] == 'POST'
        return testing_common.FakeResponseObject(200, 'Dummy content')
    if path == '33001/try/1':
        assert _TEST_EXPECTED_BOT in kwargs['body']
        return testing_common.FakeResponseObject(200, 'Dummy content')
    assert False, 'Invalid url %s requested!' % path
コード例 #3
0
def _MockFetch(url=None):
    url_to_response_map = {
        'http://src.chromium.org/viewvc/chrome?view=revision&revision=20798':
        [200, _REVISION_RESPONSE],
        'http://src.chromium.org/viewvc/chrome?view=revision&revision=20799':
        [200, 'REVISION REQUEST FAILED!'],
        'https://codereview.chromium.org/api/17504006':
        [200, json.dumps(json.loads(_ISSUE_RESPONSE))],
    }

    if url not in url_to_response_map:
        assert False, 'Bad url %s' % url

    response_code = url_to_response_map[url][0]
    response = url_to_response_map[url][1]
    return testing_common.FakeResponseObject(response_code, response)
コード例 #4
0
class EmailSheriffTest(testing_common.TestCase):

  def _AddTestToStubDataStore(self):
    """Adds a test which will be used in the methods below."""
    bug_label_patterns.AddBugLabelPattern('label1', '*/*/dromaeo/dom')
    bug_label_patterns.AddBugLabelPattern('label2', '*/*/other/test')
    testing_common.AddTests(
        ['ChromiumPerf'], ['Win7'], {'dromaeo': {'dom': {}}})
    test = utils.TestKey('ChromiumPerf/Win7/dromaeo/dom').get()
    test.improvement_direction = anomaly.DOWN
    return test

  def _GetDefaultMailArgs(self):
    """Adds an Anomaly and returns arguments for email_sheriff.EmailSheriff."""
    test_entity = self._AddTestToStubDataStore()
    subscription_url = Subscription(
        name='Chromium Perf Sheriff URL',
        rotation_url=_SHERIFF_URL,
        bug_labels=['Performance-Sheriff-URL']
    )
    subscription_email = Subscription(
        name='Chromium Perf Sheriff Mail',
        notification_email=_SHERIFF_EMAIL,
        bug_labels=['Performance-Sheriff-Mail']
    )

    anomaly_entity = anomaly.Anomaly(
        median_before_anomaly=5.0,
        median_after_anomaly=10.0,
        start_revision=10002,
        end_revision=10004,
        subscription_names=[
            subscription_url.name,
            subscription_email.name,
        ],
        subscriptions=[subscription_url, subscription_email],
        test=utils.TestKey('ChromiumPerf/Win7/dromaeo/dom'))
    return {
        'subscriptions': [subscription_url, subscription_email],
        'test': test_entity,
        'anomaly': anomaly_entity
    }

  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, 'document.write(\'sullivan\')')))
  def testEmailSheriff_ContentAndRecipientAreCorrect(self):
    email_sheriff.EmailSheriff(**self._GetDefaultMailArgs())

    messages = self.mail_stub.get_sent_messages()
    self.assertEqual(1, len(messages))
    self.assertEqual('*****@*****.**', messages[0].sender)
    self.assertEqual(
        set(['*****@*****.**', '*****@*****.**']),
        set([s.strip() for s in messages[0].to.split(',')]))

    name = 'dromaeo/dom on Win7'
    expected_subject = '100.0%% regression in %s at 10002:10004' % name
    self.assertEqual(expected_subject, messages[0].subject)
    body = str(messages[0].body)
    self.assertIn('10002 - 10004', body)
    self.assertIn('100.0%', body)
    self.assertIn('ChromiumPerf', body)
    self.assertIn('Win7', body)
    self.assertIn('dromaeo/dom', body)

    html = str(messages[0].html)
    self.assertIn('<b>10002 - 10004</b>', html)
    self.assertIn('<b>100.0%</b>', html)
    self.assertIn('<b>ChromiumPerf</b>', html)
    self.assertIn('<b>Win7</b>', html)
    self.assertIn('<b>dromaeo/dom</b>', html)


  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, 'document.write(\'sonnyrao, digit\')')))
  def testEmailSheriff_MultipleSheriffs_AllGetEmailed(self):
    email_sheriff.EmailSheriff(**self._GetDefaultMailArgs())
    messages = self.mail_stub.get_sent_messages()
    self.assertEqual(1, len(messages))
    self.assertEqual('*****@*****.**', messages[0].sender)
    self.assertEqual(
        set(['*****@*****.**',
             '*****@*****.**', '*****@*****.**']),
        set([s.strip() for s in messages[0].to.split(',')]))

  def testEmail_NoSheriffUrl_EmailSentToSheriffRotationEmailAddress(self):
    args = self._GetDefaultMailArgs()
    args['subscriptions'][0].rotation_url = None
    email_sheriff.EmailSheriff(**args)
    messages = self.mail_stub.get_sent_messages()
    self.assertEqual(1, len(messages))
    # An email is only sent to the general sheriff rotation email;
    # There is no other specific sheriff to send it to.
    self.assertEqual('*****@*****.**', messages[0].to)

  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(
          return_value=testing_common.FakeResponseObject(200, 'garbage')))
  def testEmailSheriff_RotationUrlHasInvalidContent_EmailStillSent(self):
    """Tests the email to list when the rotation URL returns garbage."""
    args = self._GetDefaultMailArgs()
    email_sheriff.EmailSheriff(**args)
    messages = self.mail_stub.get_sent_messages()
    self.assertEqual(1, len(messages))
    # An email is only sent to the general sheriff rotation email.
    self.assertEqual('*****@*****.**', messages[0].to)

  def testEmailSheriff_PercentChangeMaxFloat_ContentSaysAlertSize(self):
    """Tests the email content for "freakin huge" alert."""
    args = self._GetDefaultMailArgs()
    args['subscriptions'][0].rotation_url = None
    args['anomaly'].median_before_anomaly = 0.0
    email_sheriff.EmailSheriff(**args)
    messages = self.mail_stub.get_sent_messages()
    self.assertEqual(1, len(messages))
    self.assertIn(anomaly.FREAKIN_HUGE, str(messages[0].subject))
    self.assertNotIn(str(sys.float_info.max), str(messages[0].body))
    self.assertIn(anomaly.FREAKIN_HUGE, str(messages[0].body))
    self.assertNotIn(str(sys.float_info.max), str(messages[0].html))
    self.assertIn(anomaly.FREAKIN_HUGE, str(messages[0].html))
コード例 #5
0
ファイル: utils_test.py プロジェクト: SavoBit/catapult
 def _MockFetch(_):
     response_text = json.dumps({'key': 'this is well-formed JSON.'})
     if base64_encoded:
         response_text = base64.b64encode(response_text)
     return testing_common.FakeResponseObject(status, response_text)
コード例 #6
0
class FileBugTest(testing_common.TestCase):
    def setUp(self):
        super(FileBugTest, self).setUp()
        testing_common.SetSheriffDomains(['chromium.org'])
        testing_common.SetIsInternalUser('*****@*****.**', True)
        testing_common.SetIsInternalUser('*****@*****.**', False)
        self.SetCurrentUser('*****@*****.**')
        self._issue_tracker_service = testing_common.FakeIssueTrackerService()
        self.PatchObject(file_bug.file_bug.issue_tracker_service,
                         'IssueTrackerService',
                         lambda *_: self._issue_tracker_service)
        app = webapp2.WSGIApplication([('/file_bug', file_bug.FileBugHandler)])
        self.testapp = webtest.TestApp(app)

    def tearDown(self):
        super(FileBugTest, self).tearDown()
        self.UnsetCurrentUser()

    def _AddSampleAlerts(self,
                         master='ChromiumPerf',
                         has_commit_positions=True):
        """Adds sample data and returns a dict of rev to anomaly key."""
        # Add sample sheriff, masters, bots, and tests.
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff'],
            bug_components=['Blink>Javascript'],
        )
        testing_common.AddTests(
            [master], ['linux'],
            {'scrolling': {
                'first_paint': {},
                'mean_frame_time': {},
            }})
        test_path1 = '%s/linux/scrolling/first_paint' % master
        test_path2 = '%s/linux/scrolling/mean_frame_time' % master
        test_key1 = utils.TestKey(test_path1)
        test_key2 = utils.TestKey(test_path2)
        anomaly_key1 = self._AddAnomaly(111995, 112005, test_key1,
                                        subscription)
        anomaly_key2 = self._AddAnomaly(112000, 112010, test_key2,
                                        subscription)
        anomaly_key3 = self._AddAnomaly(112015, 112015, test_key2,
                                        subscription)
        rows_1 = testing_common.AddRows(test_path1, [112005])
        rows_2 = testing_common.AddRows(test_path2, [112010])
        rows_2 = testing_common.AddRows(test_path2, [112015])
        if has_commit_positions:
            rows_1[0].r_commit_pos = 112005
            rows_2[0].r_commit_pos = 112010
        return (anomaly_key1, anomaly_key2, anomaly_key3)

    def _AddSampleClankAlerts(self):
        """Adds sample data and returns a dict of rev to anomaly key.

    The biggest difference here is that the start/end revs aren't chromium
    commit positions. This tests the _MilestoneLabel function to make sure
    it will update the end_revision if r_commit_pos is found.
    """
        # Add sample sheriff, masters, bots, and tests. Doesn't need to be Clank.
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff'],
            bug_components=['Blink>Javascript'],
        )
        testing_common.AddTests(
            ['ChromiumPerf'], ['linux'],
            {'scrolling': {
                'first_paint': {},
                'mean_frame_time': {},
            }})
        test_path1 = 'ChromiumPerf/linux/scrolling/first_paint'
        test_path2 = 'ChromiumPerf/linux/scrolling/mean_frame_time'
        test_key1 = utils.TestKey(test_path1)
        test_key2 = utils.TestKey(test_path2)
        anomaly_key1 = self._AddAnomaly(1476193324, 1476201840, test_key1,
                                        subscription)
        anomaly_key2 = self._AddAnomaly(1476193320, 1476201870, test_key2,
                                        subscription)
        anomaly_key3 = self._AddAnomaly(1476193390, 1476193390, test_key2,
                                        subscription)
        rows_1 = testing_common.AddRows(test_path1, [1476201840])
        rows_2 = testing_common.AddRows(test_path2, [1476201870])
        rows_3 = testing_common.AddRows(test_path2, [1476193390])
        # These will be the revisions used to determine label.
        rows_1[0].r_commit_pos = 112005
        rows_2[0].r_commit_pos = 112010
        rows_3[0].r_commit_pos = 112015
        return (anomaly_key1, anomaly_key2, anomaly_key3)

    def _AddAnomaly(self, start_rev, end_rev, test_key, subscription):
        return anomaly.Anomaly(start_revision=start_rev,
                               end_revision=end_rev,
                               test=test_key,
                               median_before_anomaly=100,
                               median_after_anomaly=200,
                               subscription_names=[subscription.name],
                               subscriptions=[subscription]).put()

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch(
        'google.appengine.api.app_identity.get_default_version_hostname',
        mock.MagicMock(return_value='chromeperf.appspot.com'))
    def testBisectDisabled(self):
        http = utils.ServiceAccountHttp()
        owner = ''
        cc = '*****@*****.**'
        summary = 'test'
        description = 'Test test.'
        project_id = None
        labels = []
        components = []
        test_path = 'ChromiumPerf/linux/scrolling/first_paint'
        test_key = utils.TestKey(test_path)
        subscription = Subscription(name='Sheriff', )
        keys = [self._AddAnomaly(10, 20, test_key, subscription).urlsafe()]
        bisect = False
        result = file_bug.file_bug.FileBug(http, owner, cc, summary,
                                           description, project_id, labels,
                                           components, keys, bisect)
        self.assertNotIn('bisect_error', result)
        self.assertNotIn('jobId', result)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch(
        'google.appengine.api.app_identity.get_default_version_hostname',
        mock.MagicMock(return_value='chromeperf.appspot.com'))
    def testSupportsCCList(self):
        http = utils.ServiceAccountHttp()
        owner = ''
        cc = '[email protected],[email protected],[email protected],,'
        summary = 'test'
        description = 'Test test.'
        project_id = None
        labels = []
        components = []
        test_path = 'ChromiumPerf/linux/scrolling/first_paint'
        test_key = utils.TestKey(test_path)
        subscription = Subscription(name='Sheriff', )
        keys = [self._AddAnomaly(10, 20, test_key, subscription).urlsafe()]
        bisect = False
        result = file_bug.file_bug.FileBug(http, owner, cc, summary,
                                           description, project_id, labels,
                                           components, keys, bisect)
        self.assertNotIn('bisect_error', result)
        self.assertNotIn('jobId', result)

    def testGet_WithNoKeys_ShowsError(self):
        # When a request is made and no keys parameter is given,
        # an error message is shown in the reply.
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&finish=true')
        self.assertIn('<div class="error">', response.body)
        self.assertIn('No alerts specified', response.body)

    def testGet_WithNoFinish_ShowsForm(self):
        # When a GET request is sent with keys specified but the finish parameter
        # is not given, the response should contain a form for the sheriff to fill
        # in bug details (summary, description, etc).
        alert_keys = self._AddSampleAlerts()
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&keys=%s' %
            alert_keys[0].urlsafe())
        self.assertEqual(1, len(response.html('form')))
        self.assertIn('<input name="cc" type="text" value="*****@*****.**">',
                      str(response.html('form')[0]))

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    def testInternalBugLabel(self):
        # If any of the alerts are marked as internal-only, which should happen
        # when the corresponding test is internal-only, then the create bug dialog
        # should suggest adding a Restrict-View-Google label.
        self.SetCurrentUser('*****@*****.**')
        alert_keys = self._AddSampleAlerts()
        anomaly_entity = alert_keys[0].get()
        anomaly_entity.internal_only = True
        anomaly_entity.put()
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&keys=%s' %
            alert_keys[0].urlsafe())
        self.assertIn('Restrict-View-Google', response.body)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    def testGet_SetsBugLabelsComponents(self):
        self.SetCurrentUser('*****@*****.**')
        alert_keys = self._AddSampleAlerts()
        bug_label_patterns.AddBugLabelPattern('label1-foo',
                                              '*/*/*/first_paint')
        bug_label_patterns.AddBugLabelPattern('Cr-Performance-Blink',
                                              '*/*/*/mean_frame_time')
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&keys=%s,%s' %
            (alert_keys[0].urlsafe(), alert_keys[1].urlsafe()))
        self.assertIn('label1-foo', response.body)
        self.assertIn('Performance&gt;Blink', response.body)
        self.assertIn('Performance-Sheriff', response.body)
        self.assertIn('Blink&gt;Javascript', response.body)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch(
        'google.appengine.api.app_identity.get_default_version_hostname',
        mock.MagicMock(return_value='chromeperf.appspot.com'))
    @mock.patch.object(file_bug.file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def _PostSampleBug(self,
                       has_commit_positions=True,
                       master='ChromiumPerf',
                       is_single_rev=False):
        if master == 'ClankInternal':
            alert_keys = self._AddSampleClankAlerts()
        else:
            alert_keys = self._AddSampleAlerts(master, has_commit_positions)
        if is_single_rev:
            alert_keys = alert_keys[2].urlsafe()
        else:
            alert_keys = '%s,%s' % (alert_keys[0].urlsafe(),
                                    alert_keys[1].urlsafe())
        response = self.testapp.post('/file_bug', [
            ('keys', alert_keys),
            ('summary', 's'),
            ('description', 'd\n'),
            ('finish', 'true'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        return response

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(file_bug.file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_CreatesBug(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug()

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # The anomaly entities should be updated.
        for anomaly_entity in anomaly.Anomaly.query().fetch():
            if anomaly_entity.end_revision in [112005, 112010]:
                self.assertEqual(277761, anomaly_entity.bug_id)
            else:
                self.assertIsNone(anomaly_entity.bug_id)

        # Two HTTP requests are made when filing a bug; only test 2nd request.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertIn(
            'https://chromeperf.appspot.com/group_report?bug_id=277761',
            comment)
        self.assertIn('https://chromeperf.appspot.com/group_report?sid=',
                      comment)
        self.assertIn(
            '\n\n\nBot(s) for this bug\'s original alert(s):\n\nlinux',
            comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(file_bug.file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_CreatesBug_WithDocs(self):
        diag_dict = generic_set.GenericSet(
            [[u'Benchmark doc link', u'http://docs']])
        diag = histogram.SparseDiagnostic(
            data=diag_dict.AsDict(),
            start_revision=1,
            end_revision=sys.maxsize,
            name=reserved_infos.DOCUMENTATION_URLS.name,
            test=utils.TestKey('ChromiumPerf/linux/scrolling'))
        diag.put()

        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug()

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # The anomaly entities should be updated.
        for anomaly_entity in anomaly.Anomaly.query().fetch():
            if anomaly_entity.end_revision in [112005, 112010]:
                self.assertEqual(277761, anomaly_entity.bug_id)
            else:
                self.assertIsNone(anomaly_entity.bug_id)

        # Two HTTP requests are made when filing a bug; only test 2nd request.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertIn(
            'https://chromeperf.appspot.com/group_report?bug_id=277761',
            comment)
        self.assertIn('https://chromeperf.appspot.com/group_report?sid=',
                      comment)
        self.assertIn(
            '\n\n\nBot(s) for this bug\'s original alert(s):\n\nlinux',
            comment)
        self.assertIn('scrolling - Benchmark doc link:', comment)
        self.assertIn('http://docs', comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(
        file_bug.file_bug.crrev_service, 'GetNumbering',
        mock.MagicMock(return_value={
            'git_sha': '852ba7672ce02911e9f8f2a22363283adc80940e'
        }))
    @mock.patch(
        'dashboard.services.gitiles_service.CommitInfo',
        mock.MagicMock(return_value={
            'author': {
                'email': '*****@*****.**'
            },
            'message': 'My first commit!'
        }))
    def testGet_WithFinish_CreatesBugSingleRevOwner(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        namespaced_stored_object.Set(
            'repositories', {
                "chromium": {
                    "repository_url":
                    "https://chromium.googlesource.com/chromium/src"
                }
            })
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug(is_single_rev=True)

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # Three HTTP requests are made when filing a bug with owner; test third
        # request for owner hame.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertIn(
            'Assigning to [email protected] because this is the only CL in range',
            comment)
        self.assertIn('My first commit', comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(
        file_bug.file_bug.crrev_service, 'GetNumbering',
        mock.MagicMock(return_value={
            'git_sha': '852ba7672ce02911e9f8f2a22363283adc80940e'
        }))
    @mock.patch('dashboard.services.gitiles_service.CommitInfo',
                mock.MagicMock(
                    return_value={
                        'author': {
                            'email':
                            'v8-ci-autoroll-builder@chops-service-'
                            'accounts.iam.gserviceaccount.com'
                        },
                        'message': 'This is a roll\n\[email protected]'
                    }))
    def testGet_WithFinish_CreatesBugSingleRevAutorollOwner(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        namespaced_stored_object.Set(
            'repositories', {
                "chromium": {
                    "repository_url":
                    "https://chromium.googlesource.com/chromium/src"
                }
            })
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug(is_single_rev=True)

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # Two HTTP requests are made when filing a bug; only test 2nd request.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertIn('Assigning to [email protected]', comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    def testGet_WithFinish_SingleRevOwner_Clank_Skips(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        namespaced_stored_object.Set(
            'repositories', {
                "chromium": {
                    "repository_url":
                    "https://chromium.googlesource.com/chromium/src"
                }
            })
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug(is_single_rev=True,
                                       master='ClankInternal')

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # Three HTTP requests are made when filing a bug with owner; test third
        # request for owner hame.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertNotIn(
            'Assigning to [email protected] because this is the only CL in range',
            comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    def testGet_WithFinish_SingleRevOwner_InvalidRepository_Skips(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        namespaced_stored_object.Set(
            'repositories', {
                "chromium": {
                    "repository_url":
                    "https://chromium.googlesource.com/chromium/src"
                }
            })
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug(is_single_rev=True, master='FakeMaster')

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # Three HTTP requests are made when filing a bug with owner; test third
        # request for owner hame.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertNotIn(
            'Assigning to [email protected] because this is the only CL in range',
            comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(
        file_bug.file_bug.crrev_service, 'GetNumbering',
        mock.MagicMock(return_value={
            'git_sha': '852ba7672ce02911e9f8f2a22363283adc80940e'
        }))
    @mock.patch(
        'dashboard.services.gitiles_service.CommitInfo',
        mock.MagicMock(return_value={
            'author': {
                'email': '*****@*****.**'
            },
            'message': 'My first commit!'
        }))
    def testGet_WithFinish_CreatesBugSingleRevDifferentMasterOwner(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        namespaced_stored_object.Set(
            'repositories', {
                "chromium": {
                    "repository_url":
                    "https://chromium.googlesource.com/chromium/src"
                }
            })
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug(is_single_rev=True, master='Foo')

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # Three HTTP requests are made when filing a bug with owner; test third
        # request for owner hame.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertNotIn(
            'Assigning to [email protected] because this is the only CL in range',
            comment)
        self.assertNotIn('My first commit', comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(
        file_bug.file_bug.crrev_service, 'GetNumbering',
        mock.MagicMock(return_value={
            'git_sha': '852ba7672ce02911e9f8f2a22363283adc80940e'
        }))
    @mock.patch('dashboard.services.gitiles_service.CommitInfo',
                mock.MagicMock(
                    return_value={
                        'author': {
                            'email':
                            'robot@chops-service-accounts'
                            '.iam.gserviceaccount.com'
                        },
                        'message': 'This is an autoroll\n\[email protected]',
                    }))
    def testGet_WithFinish_CreatesBugSingleRevAutorollSheriff(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        namespaced_stored_object.Set(
            'repositories', {
                "chromium": {
                    "repository_url":
                    "https://chromium.googlesource.com/chromium/src"
                }
            })
        self._issue_tracker_service._bug_id_counter = 277761
        response = self._PostSampleBug(is_single_rev=True)

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # Three HTTP requests are made when filing a bug with owner; test third
        # request for owner hame.
        comment = self._issue_tracker_service.add_comment_args[1]
        self.assertIn(
            'Assigning to [email protected] because this is the only CL in range',
            comment)

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[{
                           'versions': [{
                               'branch_base_position': '112000',
                               'current_version': '2.0'
                           }, {
                               'branch_base_position': '111990',
                               'current_version': '1.0'
                           }]
                       }]))
    @mock.patch.object(file_bug.file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_LabelsBugWithMilestone(self):
        # Here, we expect the bug to have the following end revisions:
        # [112005, 112010] and the milestones are M-1 for rev 111990 and
        # M-2 for 11200. Hence the expected behavior is to label the bug
        # M-2 since 111995 (lowest possible revision introducing regression)
        # is less than 112010 (revision for M-2).
        self._PostSampleBug()
        self.assertIn('M-2',
                      self._issue_tracker_service.new_bug_kwargs['labels'])

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[{
                           'versions': [{
                               'branch_base_position': '112000',
                               'current_version': '2.0'
                           }, {
                               'branch_base_position': '111990',
                               'current_version': '1.0'
                           }]
                       }]))
    @mock.patch.object(file_bug.file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_LabelsBugWithNoMilestoneBecauseNoCommitPos(self):
        # Here, we expect to return no Milestone label because the alerts do not
        # contain r_commit_pos (and therefore aren't chromium). Assuming
        # testGet_WithFinish_LabelsBugWithMilestone passes, M-2
        # would be the label that it would get if the alert was Chromium.
        self._PostSampleBug(has_commit_positions=False)
        labels = self._issue_tracker_service.new_bug_kwargs['labels']
        self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch.object(file_bug.file_bug,
                       '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[{
                           'versions': [{
                               'branch_base_position': '113000',
                               'current_version': '2.0'
                           }, {
                               'branch_base_position': '112000',
                               'current_version': '2.0'
                           }, {
                               'branch_base_position': '111990',
                               'current_version': '1.0'
                           }]
                       }]))
    @mock.patch.object(file_bug.file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_LabelsBugForClank(self):
        # Here, we expect to return M-2 even though the alert revisions aren't
        # even close to the branching points. We use r_commmit_pos to determine
        # which revision to check. There are 3 branching points to ensure we are
        # actually changing the revision that is checked to r_commit_pos instead
        # of just displaying the highest one (previous behavior).
        self._PostSampleBug(master='ClankInternal')
        self.assertIn('M-2',
                      self._issue_tracker_service.new_bug_kwargs['labels'])

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(
                    return_value=testing_common.FakeResponseObject(200, '[]')))
    def testGet_WithFinish_SucceedsWithNoVersions(self):
        # Here, we test that we don't label the bug with an unexpected value when
        # there is no version information from omahaproxy (for whatever reason)
        self._PostSampleBug()
        labels = self._issue_tracker_service.new_bug_kwargs['labels']
        self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(
                    return_value=testing_common.FakeResponseObject(200, '[]')))
    def testGet_WithFinish_SucceedsWithComponents(self):
        # Here, we test that components are posted separately from labels.
        self._PostSampleBug()
        self.assertIn('Foo>Bar',
                      self._issue_tracker_service.new_bug_kwargs['components'])

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(return_value=testing_common.FakeResponseObject(
                    200,
                    json.dumps([{
                        'versions': [{
                            'branch_base_position': '0',
                            'current_version': '1.0'
                        }]
                    }]))))
    def testGet_WithFinish_SucceedsWithRevisionOutOfRange(self):
        # Here, we test that we label the bug with the highest milestone when the
        # revision introducing regression is beyond all milestones in the list.
        self._PostSampleBug()
        self.assertIn('M-1',
                      self._issue_tracker_service.new_bug_kwargs['labels'])

    @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(return_value=testing_common.FakeResponseObject(
                    200,
                    json.dumps([{
                        'versions': [{
                            'branch_base_position': 'N/A',
                            'current_version': 'N/A'
                        }]
                    }]))))
    @mock.patch('logging.warn')
    def testGet_WithFinish_SucceedsWithNAAndLogsWarning(self, mock_warn):
        self._PostSampleBug()
        labels = self._issue_tracker_service.new_bug_kwargs['labels']
        self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))
        self.assertEqual(1, mock_warn.call_count)

    def testGet_OwnersAreEmptyEvenWithOwnership(self):
        ownership_samples = [{
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'emails': ['*****@*****.**']
        }, {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
            'emails': ['*****@*****.**']
        }]
        test_paths = [
            'ChromiumPerf/linux/scrolling/first_paint',
            'ChromiumPerf/linux/scrolling/mean_frame_time'
        ]
        test_keys = [utils.TestKey(test_path) for test_path in test_paths]
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff', 'Cr-Blink-Javascript'])
        anomaly_1 = anomaly.Anomaly(start_revision=1476193324,
                                    end_revision=1476201840,
                                    test=test_keys[0],
                                    median_before_anomaly=100,
                                    median_after_anomaly=200,
                                    subscriptions=[subscription],
                                    subscription_names=[subscription.name],
                                    ownership=ownership_samples[0]).put()
        anomaly_2 = anomaly.Anomaly(start_revision=1476193320,
                                    end_revision=1476201870,
                                    test=test_keys[1],
                                    median_before_anomaly=100,
                                    median_after_anomaly=200,
                                    subscriptions=[subscription],
                                    subscription_names=[subscription.name],
                                    ownership=ownership_samples[1]).put()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s,%s' % (anomaly_1.urlsafe(), anomaly_2.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertIn('<input type="text" name="owner" value="">',
                      response.body)
        response_changed_order = self.testapp.post('/file_bug', [
            ('keys', '%s,%s' % (anomaly_2.urlsafe(), anomaly_1.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertIn('<input type="text" name="owner" value="">',
                      response_changed_order.body)

    def testGet_OwnersNotFilledWhenNoOwnership(self):
        test_key = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff', 'Cr-Blink-Javascript'])
        anomaly_entity = anomaly.Anomaly(
            start_revision=1476193324,
            end_revision=1476201840,
            test=test_key,
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
        ).put()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s' % (anomaly_entity.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertIn('<input type="text" name="owner" value="">',
                      response.body)

    def testGet_WithAllOwnershipComponents(self):
        ownership_samples = [{
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': 'Abc>Xyz'
        }, {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
            'component': 'Def>123'
        }]
        test_paths = [
            'ChromiumPerf/linux/scrolling/first_paint',
            'ChromiumPerf/linux/scrolling/mean_frame_time'
        ]
        test_keys = [utils.TestKey(test_path) for test_path in test_paths]
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff', 'Cr-Blink-Javascript'])
        anomaly_1 = anomaly.Anomaly(start_revision=1476193324,
                                    end_revision=1476201840,
                                    test=test_keys[0],
                                    median_before_anomaly=100,
                                    median_after_anomaly=200,
                                    subscriptions=[subscription],
                                    subscription_names=[subscription.name],
                                    ownership=ownership_samples[0]).put()
        anomaly_2 = anomaly.Anomaly(start_revision=1476193320,
                                    end_revision=1476201870,
                                    test=test_keys[1],
                                    median_before_anomaly=100,
                                    median_after_anomaly=200,
                                    subscriptions=[subscription],
                                    subscription_names=[subscription.name],
                                    ownership=ownership_samples[1]).put()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s' % (anomaly_1.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertIn(
            '<input type="checkbox" checked name="component" value="Abc&gt;Xyz">',
            response.body)
        response_with_both_anomalies = self.testapp.post(
            '/file_bug', [
                ('keys', '%s,%s' % (anomaly_1.urlsafe(), anomaly_2.urlsafe())),
                ('summary', 's'),
                ('description', 'd\n'),
                ('label', 'one'),
                ('label', 'two'),
                ('component', 'Foo>Bar'),
            ])
        self.assertIn(
            '<input type="checkbox" checked name="component" value="Abc&gt;Xyz">',
            response_with_both_anomalies.body)
        self.assertIn(
            '<input type="checkbox" checked name="component" value="Def&gt;123">',
            response_with_both_anomalies.body)

    def testGet_UsesOnlyMostRecentComponents(self):
        ownership_samples = [
            {
                'type': 'Ownership',
                'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
                'component': 'Abc>Def'
            },
            {
                'type': 'Ownership',
                'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
                'component': '123>456'
            },
        ]
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff', 'Cr-Blink-Javascript'])
        test_key = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')
        now_datetime = datetime.datetime.now()
        older_alert = anomaly.Anomaly(start_revision=1476193320,
                                      end_revision=1476201870,
                                      test=test_key,
                                      median_before_anomaly=100,
                                      median_after_anomaly=200,
                                      subscriptions=[subscription],
                                      subscription_names=[subscription.name],
                                      ownership=ownership_samples[0],
                                      timestamp=now_datetime).put()
        newer_alert = anomaly.Anomaly(start_revision=1476193320,
                                      end_revision=1476201870,
                                      test=test_key,
                                      median_before_anomaly=100,
                                      median_after_anomaly=200,
                                      subscriptions=[subscription],
                                      subscription_names=[subscription.name],
                                      ownership=ownership_samples[1],
                                      timestamp=now_datetime +
                                      datetime.timedelta(10)).put()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s,%s' % (older_alert.urlsafe(), newer_alert.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertNotIn(
            '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
            response.body)
        self.assertIn(
            '<input type="checkbox" checked name="component" value="123&gt;456">',
            response.body)
        response_inverted_order = self.testapp.post('/file_bug', [
            ('keys', '%s,%s' % (newer_alert.urlsafe(), older_alert.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertNotIn(
            '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
            response_inverted_order.body)
        self.assertIn(
            '<input type="checkbox" checked name="component" value="123&gt;456">',
            response_inverted_order.body)

    def testGet_ComponentsChosenPerTest(self):
        ownership_samples = [
            {
                'type': 'Ownership',
                'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
                'component': 'Abc>Def'
            },
            {
                'type': 'Ownership',
                'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
                'component': '123>456'
            },
        ]
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff', 'Cr-Blink-Javascript'])
        test_paths = [
            'ChromiumPerf/linux/scrolling/first_paint',
            'ChromiumPerf/linux/scrolling/mean_frame_time'
        ]
        test_keys = [utils.TestKey(test_path) for test_path in test_paths]
        now_datetime = datetime.datetime.now()
        alert_test_key_0 = anomaly.Anomaly(
            start_revision=1476193320,
            end_revision=1476201870,
            test=test_keys[0],
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
            ownership=ownership_samples[0],
            timestamp=now_datetime).put()
        alert_test_key_1 = anomaly.Anomaly(
            start_revision=1476193320,
            end_revision=1476201870,
            test=test_keys[1],
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
            ownership=ownership_samples[1],
            timestamp=now_datetime + datetime.timedelta(10)).put()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s,%s' %
             (alert_test_key_0.urlsafe(), alert_test_key_1.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertIn(
            '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
            response.body)
        self.assertIn(
            '<input type="checkbox" checked name="component" value="123&gt;456">',
            response.body)

    def testGet_UsesFirstDefinedComponent(self):
        ownership_samples = [{
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
        }, {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': ''
        }, {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': 'Abc>Def'
        }]
        now_datetime = datetime.datetime.now()
        test_key = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')
        subscription = Subscription(
            name='Sheriff',
            bug_labels=['Performance-Sheriff', 'Cr-Blink-Javascript'])
        alert_without_ownership = anomaly.Anomaly(
            start_revision=1476193320,
            end_revision=1476201870,
            test=test_key,
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
            timestamp=now_datetime).put()
        alert_without_component = anomaly.Anomaly(
            start_revision=1476193320,
            end_revision=1476201870,
            test=test_key,
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
            ownership=ownership_samples[0],
            timestamp=now_datetime + datetime.timedelta(10)).put()
        alert_with_empty_component = anomaly.Anomaly(
            start_revision=1476193320,
            end_revision=1476201870,
            test=test_key,
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
            ownership=ownership_samples[1],
            timestamp=now_datetime + datetime.timedelta(20)).put()
        alert_with_component = anomaly.Anomaly(
            start_revision=1476193320,
            end_revision=1476201870,
            test=test_key,
            median_before_anomaly=100,
            median_after_anomaly=200,
            subscriptions=[subscription],
            subscription_names=[subscription.name],
            ownership=ownership_samples[2],
            timestamp=now_datetime + datetime.timedelta(30)).put()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s,%s,%s,%s' % (alert_without_ownership.urlsafe(),
                                      alert_without_component.urlsafe(),
                                      alert_with_empty_component.urlsafe(),
                                      alert_with_component.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        self.assertIn(
            '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
            response.body)
コード例 #7
0
class FileBugTest(testing_common.TestCase):

  def setUp(self):
    super(FileBugTest, self).setUp()
    app = webapp2.WSGIApplication([('/file_bug', file_bug.FileBugHandler)])
    self.testapp = webtest.TestApp(app)
    testing_common.SetSheriffDomains(['chromium.org'])
    testing_common.SetIsInternalUser('*****@*****.**', True)
    testing_common.SetIsInternalUser('*****@*****.**', False)
    self.SetCurrentUser('*****@*****.**')

    # Add a fake issue tracker service that we can get call values from.
    file_bug.issue_tracker_service = mock.MagicMock()
    self.original_service = file_bug.issue_tracker_service.IssueTrackerService
    self.service = MockIssueTrackerService
    file_bug.issue_tracker_service.IssueTrackerService = self.service

  def tearDown(self):
    super(FileBugTest, self).tearDown()
    file_bug.issue_tracker_service.IssueTrackerService = self.original_service
    self.UnsetCurrentUser()

  def _AddSampleAlerts(self, is_chromium=True):
    """Adds sample data and returns a dict of rev to anomaly key."""
    # Add sample sheriff, masters, bots, and tests.
    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()
    testing_common.AddTests(['ChromiumPerf'], ['linux'], {
        'scrolling': {
            'first_paint': {},
            'mean_frame_time': {},
        }
    })
    test_path1 = 'ChromiumPerf/linux/scrolling/first_paint'
    test_path2 = 'ChromiumPerf/linux/scrolling/mean_frame_time'
    test_key1 = utils.TestKey(test_path1)
    test_key2 = utils.TestKey(test_path2)
    anomaly_key1 = self._AddAnomaly(111995, 112005, test_key1, sheriff_key)
    anomaly_key2 = self._AddAnomaly(112000, 112010, test_key2, sheriff_key)
    rows_1 = testing_common.AddRows(test_path1, [112005])
    rows_2 = testing_common.AddRows(test_path2, [112010])
    if is_chromium:
      rows_1[0].r_commit_pos = 112005
      rows_2[0].r_commit_pos = 112010
    return (anomaly_key1, anomaly_key2)

  def _AddSampleClankAlerts(self):
    """Adds sample data and returns a dict of rev to anomaly key.

    The biggest difference here is that the start/end revs aren't chromium
    commit positions. This tests the _MilestoneLabel function to make sure
    it will update the end_revision if r_commit_pos is found.
    """
    # Add sample sheriff, masters, bots, and tests. Doesn't need to be Clank.
    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()
    testing_common.AddTests(['ChromiumPerf'], ['linux'], {
        'scrolling': {
            'first_paint': {},
            'mean_frame_time': {},
        }
    })
    test_path1 = 'ChromiumPerf/linux/scrolling/first_paint'
    test_path2 = 'ChromiumPerf/linux/scrolling/mean_frame_time'
    test_key1 = utils.TestKey(test_path1)
    test_key2 = utils.TestKey(test_path2)
    anomaly_key1 = self._AddAnomaly(1476193324, 1476201840,
                                    test_key1, sheriff_key)
    anomaly_key2 = self._AddAnomaly(1476193320, 1476201870,
                                    test_key2, sheriff_key)
    rows_1 = testing_common.AddRows(test_path1, [1476201840])
    rows_2 = testing_common.AddRows(test_path2, [1476201870])
    # These will be the revisions used to determine label.
    rows_1[0].r_commit_pos = 112005
    rows_2[0].r_commit_pos = 112010
    return (anomaly_key1, anomaly_key2)

  def _AddAnomaly(self, start_rev, end_rev, test_key, sheriff_key):
    return anomaly.Anomaly(
        start_revision=start_rev, end_revision=end_rev, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key).put()

  def testGet_WithNoKeys_ShowsError(self):
    # When a request is made and no keys parameter is given,
    # an error message is shown in the reply.
    response = self.testapp.get(
        '/file_bug?summary=s&description=d&finish=true')
    self.assertIn('<div class="error">', response.body)
    self.assertIn('No alerts specified', response.body)

  def testGet_WithNoFinish_ShowsForm(self):
    # When a GET request is sent with keys specified but the finish parameter
    # is not given, the response should contain a form for the sheriff to fill
    # in bug details (summary, description, etc).
    alert_keys = self._AddSampleAlerts()
    response = self.testapp.get(
        '/file_bug?summary=s&description=d&keys=%s' % alert_keys[0].urlsafe())
    self.assertEqual(1, len(response.html('form')))
    self.assertIn('<input name="cc" type="text" value="*****@*****.**">',
                  str(response.html('form')[0]))

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  def testInternalBugLabel(self):
    # If any of the alerts are marked as internal-only, which should happen
    # when the corresponding test is internal-only, then the create bug dialog
    # should suggest adding a Restrict-View-Google label.
    self.SetCurrentUser('*****@*****.**')
    alert_keys = self._AddSampleAlerts()
    anomaly_entity = alert_keys[0].get()
    anomaly_entity.internal_only = True
    anomaly_entity.put()
    response = self.testapp.get(
        '/file_bug?summary=s&description=d&keys=%s' % alert_keys[0].urlsafe())
    self.assertIn('Restrict-View-Google', response.body)

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  def testGet_SetsBugLabelsComponents(self):
    self.SetCurrentUser('*****@*****.**')
    alert_keys = self._AddSampleAlerts()
    bug_label_patterns.AddBugLabelPattern('label1-foo', '*/*/*/first_paint')
    bug_label_patterns.AddBugLabelPattern('Cr-Performance-Blink',
                                          '*/*/*/mean_frame_time')
    response = self.testapp.get(
        '/file_bug?summary=s&description=d&keys=%s,%s' % (
            alert_keys[0].urlsafe(), alert_keys[1].urlsafe()))
    self.assertIn('label1-foo', response.body)
    self.assertIn('Performance&gt;Blink', response.body)
    self.assertIn('Performance-Sheriff', response.body)
    self.assertIn('Blink&gt;Javascript', response.body)

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch(
      'google.appengine.api.app_identity.get_default_version_hostname',
      mock.MagicMock(return_value='chromeperf.appspot.com'))
  @mock.patch.object(
      file_bug.auto_bisect, 'StartNewBisectForBug',
      mock.MagicMock(return_value={'issue_id': 123, 'issue_url': 'foo.com'}))
  def _PostSampleBug(self, is_chromium=True, is_clankium=False):
    if is_clankium:
      alert_keys = self._AddSampleClankAlerts()
    else:
      alert_keys = self._AddSampleAlerts(is_chromium)
    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (alert_keys[0].urlsafe(),
                                alert_keys[1].urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('finish', 'true'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
    return response

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch.object(
      file_bug, '_GetAllCurrentVersionsFromOmahaProxy',
      mock.MagicMock(return_value=[]))
  @mock.patch.object(
      file_bug.auto_bisect, 'StartNewBisectForBug',
      mock.MagicMock(return_value={'issue_id': 123, 'issue_url': 'foo.com'}))
  def testGet_WithFinish_CreatesBug(self):
    # When a POST request is sent with keys specified and with the finish
    # parameter given, an issue will be created using the issue tracker
    # API, and the anomalies will be updated, and a response page will
    # be sent which indicates success.
    self.service.bug_id = 277761
    response = self._PostSampleBug()

    # The response page should have a bug number.
    self.assertIn('277761', response.body)

    # The anomaly entities should be updated.
    for anomaly_entity in anomaly.Anomaly.query().fetch():
      if anomaly_entity.end_revision in [112005, 112010]:
        self.assertEqual(277761, anomaly_entity.bug_id)
      else:
        self.assertIsNone(anomaly_entity.bug_id)

    # Two HTTP requests are made when filing a bug; only test 2nd request.
    comment = self.service.add_comment_args[1]
    self.assertIn(
        'https://chromeperf.appspot.com/group_report?bug_id=277761', comment)
    self.assertIn('https://chromeperf.appspot.com/group_report?sid=', comment)
    self.assertIn(
        '\n\n\nBot(s) for this bug\'s original alert(s):\n\nlinux', comment)

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch.object(
      file_bug, '_GetAllCurrentVersionsFromOmahaProxy',
      mock.MagicMock(return_value=[
          {
              'versions': [
                  {'branch_base_position': '112000', 'current_version': '2.0'},
                  {'branch_base_position': '111990', 'current_version': '1.0'}
              ]
          }
      ]))
  @mock.patch.object(
      file_bug.auto_bisect, 'StartNewBisectForBug',
      mock.MagicMock(return_value={'issue_id': 123, 'issue_url': 'foo.com'}))
  def testGet_WithFinish_LabelsBugWithMilestone(self):
    # Here, we expect the bug to have the following end revisions:
    # [112005, 112010] and the milestones are M-1 for rev 111990 and
    # M-2 for 11200. Hence the expected behavior is to label the bug
    # M-2 since 111995 (lowest possible revision introducing regression)
    # is less than 112010 (revision for M-2).
    self._PostSampleBug()
    self.assertIn('M-2', self.service.new_bug_kwargs['labels'])

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @unittest.skip('Flaky; see #1555.')
  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, json.dumps([
              {
                  'versions': [
                      {'branch_base_position': '111999',
                       'current_version': '3.0.1234.32'},
                      {'branch_base_position': '112000',
                       'current_version': '2.0'},
                      {'branch_base_position': '111990',
                       'current_version': '1.0'}
                  ]
              }
          ]))))
  def testGet_WithFinish_LabelsBugWithLowestMilestonePossible(self):
    # Here, we expect the bug to have the following start revisions:
    # [111995, 112005] and the milestones are M-1 for rev 111990, M-2
    # for 112000 and M-3 for 111999. Hence the expected behavior is to
    # label the bug M-2 since 111995 is less than 112000 (M-2) and 111999
    # (M-3) AND M-2 is lower than M-3.
    self._PostSampleBug()
    self.assertIn('M-2', self.service.new_bug_kwargs['labels'])

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch.object(
      file_bug, '_GetAllCurrentVersionsFromOmahaProxy',
      mock.MagicMock(return_value=[
          {
              'versions': [
                  {'branch_base_position': '112000', 'current_version': '2.0'},
                  {'branch_base_position': '111990', 'current_version': '1.0'}
              ]
          }
      ]))
  @mock.patch.object(
      file_bug.auto_bisect, 'StartNewBisectForBug',
      mock.MagicMock(return_value={'issue_id': 123, 'issue_url': 'foo.com'}))
  def testGet_WithFinish_LabelsBugWithNoMilestoneBecauseNotChromium(self):
    # Here, we expect to return no Milestone label because the alerts do not
    # contain r_commit_pos (and therefore aren't chromium). Assuming
    # testGet_WithFinish_LabelsBugWithMilestone passes, M-2
    # would be the label that it would get if the alert was Chromium.
    self._PostSampleBug(is_chromium=False)
    labels = self.service.new_bug_kwargs['labels']
    self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch.object(
      file_bug, '_GetAllCurrentVersionsFromOmahaProxy',
      mock.MagicMock(return_value=[
          {
              'versions': [
                  {'branch_base_position': '113000', 'current_version': '2.0'},
                  {'branch_base_position': '112000', 'current_version': '2.0'},
                  {'branch_base_position': '111990', 'current_version': '1.0'}
              ]
          }
      ]))
  @mock.patch.object(
      file_bug.auto_bisect, 'StartNewBisectForBug',
      mock.MagicMock(return_value={'issue_id': 123, 'issue_url': 'foo.com'}))
  def testGet_WithFinish_LabelsBugForClank(self):
    # Here, we expect to return M-2 even though the alert revisions aren't
    # even close to the branching points. We use r_commmit_pos to determine
    # which revision to check. There are 3 branching points to ensure we are
    # actually changing the revision that is checked to r_commit_pos instead
    # of just displaying the highest one (previous behavior).
    self._PostSampleBug(is_clankium=True)
    self.assertIn('M-2', self.service.new_bug_kwargs['labels'])

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, '[]')))
  def testGet_WithFinish_SucceedsWithNoVersions(self):
    # Here, we test that we don't label the bug with an unexpected value when
    # there is no version information from omahaproxy (for whatever reason)
    self._PostSampleBug()
    labels = self.service.new_bug_kwargs['labels']
    self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, '[]')))
  def testGet_WithFinish_SucceedsWithComponents(self):
    # Here, we test that components are posted separately from labels.
    self._PostSampleBug()
    self.assertIn('Foo>Bar', self.service.new_bug_kwargs['components'])

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, json.dumps([
              {
                  'versions': [
                      {'branch_base_position': '0', 'current_version': '1.0'}
                  ]
              }
          ]))))
  def testGet_WithFinish_SucceedsWithRevisionOutOfRange(self):
    # Here, we test that we label the bug with the highest milestone when the
    # revision introducing regression is beyond all milestones in the list.
    self._PostSampleBug()
    self.assertIn('M-1', self.service.new_bug_kwargs['labels'])

  @mock.patch.object(utils, 'ServiceAccountHttp', mock.MagicMock())
  @mock.patch(
      'google.appengine.api.urlfetch.fetch',
      mock.MagicMock(return_value=testing_common.FakeResponseObject(
          200, json.dumps([
              {
                  'versions': [
                      {'branch_base_position': 'N/A', 'current_version': 'N/A'}
                  ]
              }
          ]))))
  @mock.patch('logging.warn')
  def testGet_WithFinish_SucceedsWithNAAndLogsWarning(self, mock_warn):
    self._PostSampleBug()
    labels = self.service.new_bug_kwargs['labels']
    self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))
    self.assertEqual(1, mock_warn.call_count)

  def testGet_OwnersAreEmptyEvenWithOwnership(self):
    ownership_samples = [
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'emails': ['*****@*****.**']
        },
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
            'emails': ['*****@*****.**']
        }
    ]

    test_paths = ['ChromiumPerf/linux/scrolling/first_paint',
                  'ChromiumPerf/linux/scrolling/mean_frame_time']
    test_keys = [utils.TestKey(test_path) for test_path in test_paths]

    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()

    anomaly_1 = anomaly.Anomaly(
        start_revision=1476193324, end_revision=1476201840, test=test_keys[0],
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[0]).put()

    anomaly_2 = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_keys[1],
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[1]).put()

    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (anomaly_1.urlsafe(), anomaly_2.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="text" name="owner" value="">',
        response.body)

    response_changed_order = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (anomaly_2.urlsafe(), anomaly_1.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="text" name="owner" value="">',
        response_changed_order.body)

  def testGet_OwnersNotFilledWhenNoOwnership(self):
    test_key = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')
    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()

    anomaly_entity = anomaly.Anomaly(
        start_revision=1476193324, end_revision=1476201840, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key).put()

    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s' % (anomaly_entity.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="text" name="owner" value="">',
        response.body)

  def testGet_WithAllOwnershipComponents(self):
    ownership_samples = [
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': 'Abc>Xyz'
        },
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
            'component': 'Def>123'
        }
    ]

    test_paths = ['ChromiumPerf/linux/scrolling/first_paint',
                  'ChromiumPerf/linux/scrolling/mean_frame_time']
    test_keys = [utils.TestKey(test_path) for test_path in test_paths]

    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()

    anomaly_1 = anomaly.Anomaly(
        start_revision=1476193324, end_revision=1476201840, test=test_keys[0],
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[0]).put()

    anomaly_2 = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_keys[1],
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[1]).put()

    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s' % (anomaly_1.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="checkbox" checked name="component" value="Abc&gt;Xyz">',
        response.body)

    response_with_both_anomalies = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (anomaly_1.urlsafe(), anomaly_2.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="checkbox" checked name="component" value="Abc&gt;Xyz">',
        response_with_both_anomalies.body)

    self.assertIn(
        '<input type="checkbox" checked name="component" value="Def&gt;123">',
        response_with_both_anomalies.body)

  def testGet_UsesOnlyMostRecentComponents(self):
    ownership_samples = [
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
            'component': 'Abc>Def'
        },
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': '123>456'
        },
    ]

    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()

    test_key = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')

    now_datetime = datetime.datetime.now()

    older_alert = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[0],
        timestamp=now_datetime).put()

    newer_alert = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[1],
        timestamp=now_datetime + datetime.timedelta(10)).put()

    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (older_alert.urlsafe(),
                                newer_alert.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertNotIn(
        '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
        response.body)

    self.assertIn(
        '<input type="checkbox" checked name="component" value="123&gt;456">',
        response.body)

    response_inverted_order = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (newer_alert.urlsafe(),
                                older_alert.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertNotIn(
        '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
        response_inverted_order.body)

    self.assertIn(
        '<input type="checkbox" checked name="component" value="123&gt;456">',
        response_inverted_order.body)

  def testGet_ComponentsChosenPerTest(self):
    ownership_samples = [
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
            'component': 'Abc>Def'
        },
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': '123>456'
        },
    ]

    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()

    test_paths = ['ChromiumPerf/linux/scrolling/first_paint',
                  'ChromiumPerf/linux/scrolling/mean_frame_time']
    test_keys = [utils.TestKey(test_path) for test_path in test_paths]

    now_datetime = datetime.datetime.now()

    alert_test_key_0 = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_keys[0],
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[0],
        timestamp=now_datetime).put()

    alert_test_key_1 = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_keys[1],
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[1],
        timestamp=now_datetime + datetime.timedelta(10)).put()

    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s' % (alert_test_key_0.urlsafe(),
                                alert_test_key_1.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
        response.body)

    self.assertIn(
        '<input type="checkbox" checked name="component" value="123&gt;456">',
        response.body)

  def testGet_UsesFirstDefinedComponent(self):
    ownership_samples = [
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb827',
        },
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': ''
        },
        {
            'type': 'Ownership',
            'guid': 'eb212e80-db58-4cbd-b331-c2245ecbb826',
            'component': 'Abc>Def'
        }
    ]

    now_datetime = datetime.datetime.now()

    test_key = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')

    sheriff_key = sheriff.Sheriff(
        id='Sheriff',
        labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()

    alert_without_ownership = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, timestamp=now_datetime).put()

    alert_without_component = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[0],
        timestamp=now_datetime + datetime.timedelta(10)).put()

    alert_with_empty_component = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[1],
        timestamp=now_datetime + datetime.timedelta(20)).put()

    alert_with_component = anomaly.Anomaly(
        start_revision=1476193320, end_revision=1476201870, test=test_key,
        median_before_anomaly=100, median_after_anomaly=200,
        sheriff=sheriff_key, ownership=ownership_samples[2],
        timestamp=now_datetime + datetime.timedelta(30)).put()

    response = self.testapp.post(
        '/file_bug',
        [
            ('keys', '%s,%s,%s,%s' % (alert_without_ownership.urlsafe(),
                                      alert_without_component.urlsafe(),
                                      alert_with_empty_component.urlsafe(),
                                      alert_with_component.urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])

    self.assertIn(
        '<input type="checkbox" checked name="component" value="Abc&gt;Def">',
        response.body)
コード例 #8
0
ファイル: start_try_job_test.py プロジェクト: neven7/catapult
def _MockFailedFetch(url=None):  # pylint: disable=unused-argument
    return testing_common.FakeResponseObject(404, {})
コード例 #9
0
class FileBugTest(testing_common.TestCase):
    def setUp(self):
        super(FileBugTest, self).setUp()
        app = webapp2.WSGIApplication([('/file_bug', file_bug.FileBugHandler)])
        self.testapp = webtest.TestApp(app)
        testing_common.SetSheriffDomains(['chromium.org'])
        testing_common.SetIsInternalUser('*****@*****.**', True)
        testing_common.SetIsInternalUser('*****@*****.**', False)
        self.SetCurrentUser('*****@*****.**')

        # Add a fake issue tracker service that we can get call values from.
        file_bug.issue_tracker_service = mock.MagicMock()
        self.original_service = file_bug.issue_tracker_service.IssueTrackerService
        self.service = MockIssueTrackerService
        file_bug.issue_tracker_service.IssueTrackerService = self.service

    def tearDown(self):
        super(FileBugTest, self).tearDown()
        file_bug.issue_tracker_service.IssueTrackerService = self.original_service
        self.UnsetCurrentUser()

    def _AddSampleAlerts(self):
        """Adds sample data and returns a dict of rev to anomaly key."""
        # Add sample sheriff, masters, bots, and tests.
        sheriff_key = sheriff.Sheriff(
            id='Sheriff',
            labels=['Performance-Sheriff', 'Cr-Blink-Javascript']).put()
        testing_common.AddTests(
            ['ChromiumPerf'], ['linux'],
            {'scrolling': {
                'first_paint': {},
                'mean_frame_time': {},
            }})
        test_key1 = utils.TestKey('ChromiumPerf/linux/scrolling/first_paint')
        test_key2 = utils.TestKey(
            'ChromiumPerf/linux/scrolling/mean_frame_time')
        anomaly_key1 = self._AddAnomaly(111995, 112005, test_key1, sheriff_key)
        anomaly_key2 = self._AddAnomaly(112000, 112010, test_key2, sheriff_key)
        return (anomaly_key1, anomaly_key2)

    def _AddAnomaly(self, start_rev, end_rev, test_key, sheriff_key):
        return anomaly.Anomaly(start_revision=start_rev,
                               end_revision=end_rev,
                               test=test_key,
                               median_before_anomaly=100,
                               median_after_anomaly=200,
                               sheriff=sheriff_key).put()

    def testGet_WithNoKeys_ShowsError(self):
        # When a request is made and no keys parameter is given,
        # an error message is shown in the reply.
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&finish=true')
        self.assertIn('<div class="error">', response.body)
        self.assertIn('No alerts specified', response.body)

    def testGet_WithNoFinish_ShowsForm(self):
        # When a GET request is sent with keys specified but the finish parameter
        # is not given, the response should contain a form for the sheriff to fill
        # in bug details (summary, description, etc).
        alert_keys = self._AddSampleAlerts()
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&keys=%s' %
            alert_keys[0].urlsafe())
        self.assertEqual(1, len(response.html('form')))
        self.assertIn('<input name="cc" type="text" value="*****@*****.**">',
                      str(response.html('form')[0]))

    def testInternalBugLabel(self):
        # If any of the alerts are marked as internal-only, which should happen
        # when the corresponding test is internal-only, then the create bug dialog
        # should suggest adding a Restrict-View-Google label.
        self.SetCurrentUser('*****@*****.**')
        alert_keys = self._AddSampleAlerts()
        anomaly_entity = alert_keys[0].get()
        anomaly_entity.internal_only = True
        anomaly_entity.put()
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&keys=%s' %
            alert_keys[0].urlsafe())
        self.assertIn('Restrict-View-Google', response.body)

    def testGet_SetsBugLabelsComponents(self):
        self.SetCurrentUser('*****@*****.**')
        alert_keys = self._AddSampleAlerts()
        bug_label_patterns.AddBugLabelPattern('label1-foo',
                                              '*/*/*/first_paint')
        bug_label_patterns.AddBugLabelPattern('Cr-Performance-Blink',
                                              '*/*/*/mean_frame_time')
        response = self.testapp.get(
            '/file_bug?summary=s&description=d&keys=%s,%s' %
            (alert_keys[0].urlsafe(), alert_keys[1].urlsafe()))
        self.assertIn('label1-foo', response.body)
        self.assertIn('Performance&gt;Blink', response.body)
        self.assertIn('Performance-Sheriff', response.body)
        self.assertIn('Blink&gt;Javascript', response.body)

    @mock.patch(
        'google.appengine.api.app_identity.get_default_version_hostname',
        mock.MagicMock(return_value='chromeperf.appspot.com'))
    @mock.patch.object(file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def _PostSampleBug(self):
        alert_keys = self._AddSampleAlerts()
        response = self.testapp.post('/file_bug', [
            ('keys', '%s,%s' %
             (alert_keys[0].urlsafe(), alert_keys[1].urlsafe())),
            ('summary', 's'),
            ('description', 'd\n'),
            ('finish', 'true'),
            ('label', 'one'),
            ('label', 'two'),
            ('component', 'Foo>Bar'),
        ])
        return response

    @mock.patch.object(file_bug, '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_CreatesBug(self):
        # When a POST request is sent with keys specified and with the finish
        # parameter given, an issue will be created using the issue tracker
        # API, and the anomalies will be updated, and a response page will
        # be sent which indicates success.
        self.service.bug_id = 277761
        response = self._PostSampleBug()

        # The response page should have a bug number.
        self.assertIn('277761', response.body)

        # The anomaly entities should be updated.
        for anomaly_entity in anomaly.Anomaly.query().fetch():
            if anomaly_entity.end_revision in [112005, 112010]:
                self.assertEqual(277761, anomaly_entity.bug_id)
            else:
                self.assertIsNone(anomaly_entity.bug_id)

        # Two HTTP requests are made when filing a bug; only test 2nd request.
        comment = self.service.add_comment_args[1]
        self.assertIn(
            'https://chromeperf.appspot.com/group_report?bug_id=277761',
            comment)
        self.assertIn('https://chromeperf.appspot.com/group_report?keys=',
                      comment)
        self.assertIn(
            '\n\n\nBot(s) for this bug\'s original alert(s):\n\nlinux',
            comment)

    @mock.patch.object(file_bug, '_GetAllCurrentVersionsFromOmahaProxy',
                       mock.MagicMock(return_value=[{
                           'versions': [{
                               'branch_base_position': '112000',
                               'current_version': '2.0'
                           }, {
                               'branch_base_position': '111990',
                               'current_version': '1.0'
                           }]
                       }]))
    @mock.patch.object(file_bug.auto_bisect, 'StartNewBisectForBug',
                       mock.MagicMock(return_value={
                           'issue_id': 123,
                           'issue_url': 'foo.com'
                       }))
    def testGet_WithFinish_LabelsBugWithMilestone(self):
        # Here, we expect the bug to have the following start revisions:
        # [111995, 112005] and the milestones are M-1 for rev 111990 and
        # M-2 for 11200. Hence the expected behavior is to label the bug
        # M-2 since 111995 (lowest possible revision introducing regression)
        # is less than 112000 (revision for M-2).
        self._PostSampleBug()
        self.assertIn('M-2', self.service.new_bug_kwargs['labels'])

    @unittest.skip('Flaky; see #1555.')
    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(return_value=testing_common.FakeResponseObject(
                    200,
                    json.dumps([{
                        'versions': [{
                            'branch_base_position': '111999',
                            'current_version': '3.0.1234.32'
                        }, {
                            'branch_base_position': '112000',
                            'current_version': '2.0'
                        }, {
                            'branch_base_position': '111990',
                            'current_version': '1.0'
                        }]
                    }]))))
    def testGet_WithFinish_LabelsBugWithLowestMilestonePossible(self):
        # Here, we expect the bug to have the following start revisions:
        # [111995, 112005] and the milestones are M-1 for rev 111990, M-2
        # for 112000 and M-3 for 111999. Hence the expected behavior is to
        # label the bug M-2 since 111995 is less than 112000 (M-2) and 111999
        # (M-3) AND M-2 is lower than M-3.
        self._PostSampleBug()
        self.assertIn('M-2', self.service.new_bug_kwargs['labels'])

    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(
                    return_value=testing_common.FakeResponseObject(200, '[]')))
    def testGet_WithFinish_SucceedsWithNoVersions(self):
        # Here, we test that we don't label the bug with an unexpected value when
        # there is no version information from omahaproxy (for whatever reason)
        self._PostSampleBug()
        labels = self.service.new_bug_kwargs['labels']
        self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))

    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(
                    return_value=testing_common.FakeResponseObject(200, '[]')))
    def testGet_WithFinish_SucceedsWithComponents(self):
        # Here, we test that components are posted separately from labels.
        self._PostSampleBug()
        self.assertIn('Foo>Bar', self.service.new_bug_kwargs['components'])

    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(return_value=testing_common.FakeResponseObject(
                    200,
                    json.dumps([{
                        'versions': [{
                            'branch_base_position': '0',
                            'current_version': '1.0'
                        }]
                    }]))))
    def testGet_WithFinish_SucceedsWithRevisionOutOfRange(self):
        # Here, we test that we label the bug with the highest milestone when the
        # revision introducing regression is beyond all milestones in the list.
        self._PostSampleBug()
        self.assertIn('M-1', self.service.new_bug_kwargs['labels'])

    @mock.patch('google.appengine.api.urlfetch.fetch',
                mock.MagicMock(return_value=testing_common.FakeResponseObject(
                    200,
                    json.dumps([{
                        'versions': [{
                            'branch_base_position': 'N/A',
                            'current_version': 'N/A'
                        }]
                    }]))))
    @mock.patch('logging.warn')
    def testGet_WithFinish_SucceedsWithNAAndLogsWarning(self, mock_warn):
        self._PostSampleBug()
        labels = self.service.new_bug_kwargs['labels']
        self.assertEqual(0, len([x for x in labels if x.startswith(u'M-')]))
        self.assertEqual(1, mock_warn.call_count)
コード例 #10
0
def _MockFailedFetch(url=None, deadline=None):
  del url
  del deadline
  return testing_common.FakeResponseObject(404, {})
コード例 #11
0
 def _GenerateResponse(self, build_details):
     encoded_build_data = base64.b64encode(json.dumps(build_details))
     response_content = '12345%s' % json.dumps({'data': encoded_build_data})
     return testing_common.FakeResponseObject(200, response_content)