| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import datetime |
| import sys |
| import unittest |
| |
| import mock |
| import webapp2 |
| import webtest |
| |
| from dashboard import auto_bisect |
| from dashboard import request_handler |
| from dashboard import start_try_job |
| from dashboard import stored_object |
| from dashboard import testing_common |
| from dashboard import utils |
| from dashboard.models import anomaly |
| from dashboard.models import try_job |
| |
| |
| @mock.patch.object(utils, 'TickMonitoringCustomMetric', mock.MagicMock()) |
| class AutoBisectTest(testing_common.TestCase): |
| |
| def setUp(self): |
| super(AutoBisectTest, self).setUp() |
| stored_object.Set( |
| start_try_job._TESTER_DIRECTOR_MAP_KEY, |
| { |
| 'linux_perf_tester': 'linux_perf_bisector', |
| 'win64_nv_tester': 'linux_perf_bisector', |
| }) |
| app = webapp2.WSGIApplication( |
| [('/auto_bisect', auto_bisect.AutoBisectHandler)]) |
| self.testapp = webtest.TestApp(app) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| def testPost_FailedJobRunTwice_JobRestarted(self, mock_perform_bisect): |
| testing_common.AddTests( |
| ['ChromiumPerf'], ['linux-release'], {'sunspider': {'score': {}}}) |
| test_key = utils.TestKey('ChromiumPerf/linux-release/sunspider/score') |
| anomaly.Anomaly( |
| bug_id=111, test=test_key, |
| start_revision=300100, end_revision=300200, |
| median_before_anomaly=100, median_after_anomaly=200).put() |
| try_job.TryJob( |
| bug_id=111, status='failed', |
| last_ran_timestamp=datetime.datetime.now() - datetime.timedelta(days=8), |
| run_count=2).put() |
| self.testapp.post('/auto_bisect') |
| mock_perform_bisect.assert_called_once_with( |
| try_job.TryJob.query(try_job.TryJob.bug_id == 111).get()) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| def testPost_FailedJobRunOnce_JobRestarted(self, mock_perform_bisect): |
| try_job.TryJob( |
| bug_id=222, status='failed', |
| last_ran_timestamp=datetime.datetime.now(), |
| run_count=1).put() |
| self.testapp.post('/auto_bisect') |
| mock_perform_bisect.assert_called_once_with( |
| try_job.TryJob.query(try_job.TryJob.bug_id == 222).get()) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'LogBisectResult') |
| def testPost_JobRunTooManyTimes_LogsMessage(self, mock_log_result): |
| job_key = try_job.TryJob( |
| bug_id=333, status='failed', |
| last_ran_timestamp=datetime.datetime.now(), |
| run_count=len(auto_bisect._BISECT_RESTART_PERIOD_DAYS)+1).put() |
| self.testapp.post('/auto_bisect') |
| self.assertIsNone(job_key.get()) |
| mock_log_result.assert_called_once_with(333, mock.ANY) |
| |
| def testGet_WithStatsParameter_ListsTryJobs(self): |
| now = datetime.datetime.now() |
| try_job.TryJob( |
| bug_id=222, status='failed', |
| last_ran_timestamp=now, run_count=2).put() |
| try_job.TryJob( |
| bug_id=444, status='started', |
| last_ran_timestamp=now, run_count=1).put() |
| try_job.TryJob( |
| bug_id=777, status='started', |
| last_ran_timestamp=now, use_buildbucket=True, run_count=1).put() |
| try_job.TryJob( |
| bug_id=555, status=None, |
| last_ran_timestamp=now, run_count=1).put() |
| response = self.testapp.get('/auto_bisect?stats') |
| self.assertIn('Failed jobs: 1', response.body) |
| self.assertIn('Started jobs: 2', response.body) |
| |
| |
| class StartNewBisectForBugTest(testing_common.TestCase): |
| def setUp(self): |
| super(StartNewBisectForBugTest, self).setUp() |
| stored_object.Set( |
| start_try_job._TESTER_DIRECTOR_MAP_KEY, |
| { |
| 'linux_perf_tester': 'linux_perf_bisector', |
| 'win64_nv_tester': 'linux_perf_bisector', |
| }) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| def testStartNewBisectForBug_StartsBisect(self, mock_perform_bisect): |
| testing_common.AddTests( |
| ['ChromiumPerf'], ['linux-release'], {'sunspider': {'score': {}}}) |
| test_key = utils.TestKey('ChromiumPerf/linux-release/sunspider/score') |
| anomaly.Anomaly( |
| bug_id=111, test=test_key, |
| start_revision=300100, end_revision=300200, |
| median_before_anomaly=100, median_after_anomaly=200).put() |
| auto_bisect.StartNewBisectForBug(111) |
| job = try_job.TryJob.query(try_job.TryJob.bug_id == 111).get() |
| mock_perform_bisect.assert_called_once_with(job) |
| |
| def testStartNewBisectForBug_RevisionTooLow_ReturnsError(self): |
| testing_common.AddTests( |
| ['ChromiumPerf'], ['linux-release'], {'sunspider': {'score': {}}}) |
| test_key = utils.TestKey('ChromiumPerf/linux-release/sunspider/score') |
| anomaly.Anomaly( |
| bug_id=222, test=test_key, |
| start_revision=1200, end_revision=1250, |
| median_before_anomaly=100, median_after_anomaly=200).put() |
| result = auto_bisect.StartNewBisectForBug(222) |
| self.assertEqual({'error': 'Invalid "good" revision: 1199.'}, result) |
| |
| @mock.patch.object( |
| auto_bisect.start_try_job, 'PerformBisect', |
| mock.MagicMock(side_effect=request_handler.InvalidInputError( |
| 'Some reason'))) |
| def testStartNewBisectForBug_InvalidInputErrorRaised_ReturnsError(self): |
| testing_common.AddTests(['Foo'], ['bar'], {'sunspider': {'score': {}}}) |
| test_key = utils.TestKey('Foo/bar/sunspider/score') |
| anomaly.Anomaly( |
| bug_id=345, test=test_key, |
| start_revision=300100, end_revision=300200, |
| median_before_anomaly=100, median_after_anomaly=200).put() |
| result = auto_bisect.StartNewBisectForBug(345) |
| self.assertEqual({'error': 'Some reason'}, result) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| def testStartNewBisectForBug_WithDefaultRevs_StartsBisect( |
| self, mock_perform_bisect): |
| testing_common.AddTests( |
| ['ChromiumPerf'], ['linux-release'], {'sunspider': {'score': {}}}) |
| test_key = utils.TestKey('ChromiumPerf/linux-release/sunspider/score') |
| testing_common.AddRows( |
| 'ChromiumPerf/linux-release/sunspider/score', |
| { |
| 1199: { |
| 'a_default_rev': 'r_foo', |
| 'r_foo': '9e29b5bcd08357155b2859f87227d50ed60cf857' |
| }, |
| 1250: { |
| 'a_default_rev': 'r_foo', |
| 'r_foo': 'fc34e5346446854637311ad7793a95d56e314042' |
| } |
| }) |
| anomaly.Anomaly( |
| bug_id=333, test=test_key, |
| start_revision=1200, end_revision=1250, |
| median_before_anomaly=100, median_after_anomaly=200).put() |
| auto_bisect.StartNewBisectForBug(333) |
| job = try_job.TryJob.query(try_job.TryJob.bug_id == 333).get() |
| mock_perform_bisect.assert_called_once_with(job) |
| |
| def testStartNewBisectForBug_UnbisectableTest_ReturnsError(self): |
| testing_common.AddTests(['V8'], ['x86'], {'v8': {'sunspider': {}}}) |
| # The test suite "v8" is in the black-list of test suite names. |
| test_key = utils.TestKey('V8/x86/v8/sunspider') |
| anomaly.Anomaly( |
| bug_id=444, test=test_key, |
| start_revision=155000, end_revision=155100, |
| median_before_anomaly=100, median_after_anomaly=200).put() |
| result = auto_bisect.StartNewBisectForBug(444) |
| self.assertEqual({'error': 'Could not select a test.'}, result) |
| |
| |
| class TickMonitoringCustomMetricTest(testing_common.TestCase): |
| |
| def setUp(self): |
| super(TickMonitoringCustomMetricTest, self).setUp() |
| app = webapp2.WSGIApplication( |
| [('/auto_bisect', auto_bisect.AutoBisectHandler)]) |
| self.testapp = webtest.TestApp(app) |
| |
| @mock.patch.object(utils, 'TickMonitoringCustomMetric') |
| def testPost_NoTryJobs_CustomMetricTicked(self, mock_tick): |
| self.testapp.post('/auto_bisect') |
| mock_tick.assert_called_once_with('RestartFailedBisectJobs') |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| @mock.patch.object(utils, 'TickMonitoringCustomMetric') |
| def testPost_RunCount1_ExceptionInPerformBisect_CustomMetricNotTicked( |
| self, mock_tick, mock_perform_bisect): |
| mock_perform_bisect.side_effect = Exception('Error') |
| try_job.TryJob( |
| bug_id=222, status='failed', |
| last_ran_timestamp=datetime.datetime.now(), |
| run_count=1).put() |
| self.testapp.post('/auto_bisect') |
| self.assertEqual(0, mock_tick.call_count) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| @mock.patch.object(utils, 'TickMonitoringCustomMetric') |
| def testPost_RunCount2_ExceptionInPerformBisect_CustomMetricNotTicked( |
| self, mock_tick, mock_perform_bisect): |
| mock_perform_bisect.side_effect = Exception('Error') |
| try_job.TryJob( |
| bug_id=111, status='failed', |
| last_ran_timestamp=datetime.datetime.now() - datetime.timedelta(days=8), |
| run_count=2).put() |
| self.testapp.post('/auto_bisect') |
| self.assertEqual(0, mock_tick.call_count) |
| |
| @mock.patch.object(auto_bisect.start_try_job, 'PerformBisect') |
| @mock.patch.object(utils, 'TickMonitoringCustomMetric') |
| def testPost_NoExceptionInPerformBisect_CustomMetricTicked( |
| self, mock_tick, mock_perform_bisect): |
| try_job.TryJob( |
| bug_id=222, status='failed', |
| last_ran_timestamp=datetime.datetime.now(), |
| run_count=1).put() |
| self.testapp.post('/auto_bisect') |
| self.assertEqual(1, mock_perform_bisect.call_count) |
| mock_tick.assert_called_once_with('RestartFailedBisectJobs') |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |