| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import os | 5 import os |
| 6 import re | 6 import re |
| 7 import shutil | 7 import shutil |
| 8 import sys | 8 import sys |
| 9 import unittest | 9 import unittest |
| 10 | 10 |
| 11 SRC = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir) | 11 SRC = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir) |
| 12 sys.path.append(os.path.join(SRC, 'third_party', 'pymock')) | 12 sys.path.append(os.path.join(SRC, 'third_party', 'pymock')) |
| 13 | 13 |
| 14 import bisect_perf_regression | 14 import bisect_perf_regression |
| 15 import bisect_results | 15 import bisect_results |
| 16 import mock | 16 import mock |
| 17 import source_control | 17 import source_control |
| 18 | 18 |
| 19 | 19 |
| 20 # Regression confidence: 0% | |
| 21 CLEAR_NON_REGRESSION = [ | |
| 22 # Mean: 30.223 Std. Dev.: 11.383 | |
| 23 [[16.886], [16.909], [16.99], [17.723], [17.952], [18.118], [19.028], | |
| 24 [19.552], [21.954], [38.573], [38.839], [38.965], [40.007], [40.572], | |
| 25 [41.491], [42.002], [42.33], [43.109], [43.238]], | |
| 26 # Mean: 34.76 Std. Dev.: 11.516 | |
| 27 [[16.426], [17.347], [20.593], [21.177], [22.791], [27.843], [28.383], | |
| 28 [28.46], [29.143], [40.058], [40.303], [40.558], [41.918], [42.44], | |
| 29 [45.223], [46.494], [50.002], [50.625], [50.839]] | |
| 30 ] | |
| 31 # Regression confidence: ~ 90% | |
| 32 ALMOST_REGRESSION = [ | |
| 33 # Mean: 30.042 Std. Dev.: 2.002 | |
| 34 [[26.146], [28.04], [28.053], [28.074], [28.168], [28.209], [28.471], | |
| 35 [28.652], [28.664], [30.862], [30.973], [31.002], [31.897], [31.929], | |
| 36 [31.99], [32.214], [32.323], [32.452], [32.696]], | |
| 37 # Mean: 33.008 Std. Dev.: 4.265 | |
| 38 [[34.963], [30.741], [39.677], [39.512], [34.314], [31.39], [34.361], | |
| 39 [25.2], [30.489], [29.434]] | |
| 40 ] | |
| 41 # Regression confidence: ~ 98% | |
| 42 BARELY_REGRESSION = [ | |
| 43 # Mean: 28.828 Std. Dev.: 1.993 | |
| 44 [[26.96], [27.605], [27.768], [27.829], [28.006], [28.206], [28.393], | |
| 45 [28.911], [28.933], [30.38], [30.462], [30.808], [31.74], [31.805], | |
| 46 [31.899], [32.077], [32.454], [32.597], [33.155]], | |
| 47 # Mean: 31.156 Std. Dev.: 1.980 | |
| 48 [[28.729], [29.112], [29.258], [29.454], [29.789], [30.036], [30.098], | |
| 49 [30.174], [30.534], [32.285], [32.295], [32.552], [32.572], [32.967], | |
| 50 [33.165], [33.403], [33.588], [33.744], [34.147], [35.84]] | |
| 51 ] | |
| 52 # Regression confidence: 99.5% | |
| 53 CLEAR_REGRESSION = [ | |
| 54 # Mean: 30.254 Std. Dev.: 2.987 | |
| 55 [[26.494], [26.621], [26.701], [26.997], [26.997], [27.05], [27.37], | |
| 56 [27.488], [27.556], [31.846], [32.192], [32.21], [32.586], [32.596], | |
| 57 [32.618], [32.95], [32.979], [33.421], [33.457], [34.97]], | |
| 58 # Mean: 33.190 Std. Dev.: 2.972 | |
| 59 [[29.547], [29.713], [29.835], [30.132], [30.132], [30.33], [30.406], | |
| 60 [30.592], [30.72], [34.486], [35.247], [35.253], [35.335], [35.378], | |
| 61 [35.934], [36.233], [36.41], [36.947], [37.982]] | |
| 62 ] | |
| 63 # Default options for the dry run | 20 # Default options for the dry run |
| 64 DEFAULT_OPTIONS = { | 21 DEFAULT_OPTIONS = { |
| 65 'debug_ignore_build': True, | 22 'debug_ignore_build': True, |
| 66 'debug_ignore_sync': True, | 23 'debug_ignore_sync': True, |
| 67 'debug_ignore_perf_test': True, | 24 'debug_ignore_perf_test': True, |
| 68 'debug_ignore_regression_confidence': True, | |
| 69 'command': 'fake_command', | 25 'command': 'fake_command', |
| 70 'metric': 'fake/metric', | 26 'metric': 'fake/metric', |
| 71 'good_revision': 280000, | 27 'good_revision': 280000, |
| 72 'bad_revision': 280005, | 28 'bad_revision': 280005, |
| 73 } | 29 } |
| 74 | 30 |
| 75 # This global is a placeholder for a generator to be defined by the testcases | |
| 76 # that use _MockRunTest | |
| 77 _MockResultsGenerator = (x for x in []) | |
| 78 | |
| 79 def _FakeTestResult(values): | |
| 80 result_dict = {'mean': 0.0, 'std_err': 0.0, 'std_dev': 0.0, 'values': values} | |
| 81 success_code = 0 | |
| 82 return (result_dict, success_code) | |
| 83 | |
| 84 | |
| 85 def _MockRunTests(*args, **kwargs): | |
| 86 _, _ = args, kwargs | |
| 87 return _FakeTestResult(_MockResultsGenerator.next()) | |
| 88 | |
| 89 | 31 |
| 90 def _GetBisectPerformanceMetricsInstance(options_dict): | 32 def _GetBisectPerformanceMetricsInstance(options_dict): |
| 91 """Returns an instance of the BisectPerformanceMetrics class.""" | 33 """Returns an instance of the BisectPerformanceMetrics class.""" |
| 92 bisect_options = bisect_perf_regression.BisectOptions.FromDict(options_dict) | 34 bisect_options = bisect_perf_regression.BisectOptions.FromDict(options_dict) |
| 93 bisect_instance = bisect_perf_regression.BisectPerformanceMetrics( | 35 bisect_instance = bisect_perf_regression.BisectPerformanceMetrics( |
| 94 bisect_options) | 36 bisect_options) |
| 95 return bisect_instance | 37 return bisect_instance |
| 96 | 38 |
| 97 | 39 |
| 98 def _GetExtendedOptions(improvement_dir, fake_first, ignore_confidence=True): | 40 def _GetExtendedOptions(d, f): |
| 99 """Returns the a copy of the default options dict plus some options.""" | 41 """Returns the a copy of the default options dict plus some options.""" |
| 100 result = dict(DEFAULT_OPTIONS) | 42 result = dict(DEFAULT_OPTIONS) |
| 101 result.update({ | 43 result.update({ |
| 102 'improvement_direction': improvement_dir, | 44 'improvement_direction': d, |
| 103 'debug_fake_first_test_mean': fake_first, | 45 'debug_fake_first_test_mean': f}) |
| 104 'debug_ignore_regression_confidence': ignore_confidence}) | |
| 105 return result | 46 return result |
| 106 | 47 |
| 107 | 48 |
| 108 def _GenericDryRun(options, print_results=False): | 49 def _GenericDryRun(options, print_results=False): |
| 109 """Performs a dry run of the bisector. | 50 """Performs a dry run of the bisector. |
| 110 | 51 |
| 111 Args: | 52 Args: |
| 112 options: Dictionary containing the options for the bisect instance. | 53 options: Dictionary containing the options for the bisect instance. |
| 113 print_results: Boolean telling whether to call FormatAndPrintResults. | 54 print_results: Boolean telling whether to call FormatAndPrintResults. |
| 114 | 55 |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 def testDryRun(self): | 294 def testDryRun(self): |
| 354 """Does a dry run of the bisect script. | 295 """Does a dry run of the bisect script. |
| 355 | 296 |
| 356 This serves as a smoke test to catch errors in the basic execution of the | 297 This serves as a smoke test to catch errors in the basic execution of the |
| 357 script. | 298 script. |
| 358 """ | 299 """ |
| 359 _GenericDryRun(DEFAULT_OPTIONS, True) | 300 _GenericDryRun(DEFAULT_OPTIONS, True) |
| 360 | 301 |
| 361 def testBisectImprovementDirectionFails(self): | 302 def testBisectImprovementDirectionFails(self): |
| 362 """Dry run of a bisect with an improvement instead of regression.""" | 303 """Dry run of a bisect with an improvement instead of regression.""" |
| 304 |
| 363 # Test result goes from 0 to 100 where higher is better | 305 # Test result goes from 0 to 100 where higher is better |
| 364 results = _GenericDryRun(_GetExtendedOptions(1, 100)) | 306 results = _GenericDryRun(_GetExtendedOptions(1, 100)) |
| 365 self.assertIsNotNone(results.error) | 307 self.assertIsNotNone(results.error) |
| 366 self.assertIn('not a regression', results.error) | 308 self.assertIn('not a regression', results.error) |
| 367 | |
| 368 # Test result goes from 0 to -100 where lower is better | 309 # Test result goes from 0 to -100 where lower is better |
| 369 results = _GenericDryRun(_GetExtendedOptions(-1, -100)) | 310 results = _GenericDryRun(_GetExtendedOptions(-1, -100)) |
| 370 self.assertIsNotNone(results.error) | 311 self.assertIsNotNone(results.error) |
| 371 self.assertIn('not a regression', results.error) | 312 self.assertIn('not a regression', results.error) |
| 372 | 313 |
| 373 def testBisectImprovementDirectionSucceeds(self): | 314 def testBisectImprovementDirectionSucceeds(self): |
| 374 """Bisects with improvement direction matching regression range.""" | 315 """Bisects with improvement direction matching regression range.""" |
| 375 # Test result goes from 0 to 100 where lower is better | 316 # Test result goes from 0 to 100 where lower is better |
| 376 results = _GenericDryRun(_GetExtendedOptions(-1, 100)) | 317 results = _GenericDryRun(_GetExtendedOptions(-1, 100)) |
| 377 self.assertIsNone(results.error) | 318 self.assertIsNone(results.error) |
| 378 # Test result goes from 0 to -100 where higher is better | 319 # Test result goes from 0 to -100 where higher is better |
| 379 results = _GenericDryRun(_GetExtendedOptions(1, -100)) | 320 results = _GenericDryRun(_GetExtendedOptions(1, -100)) |
| 380 self.assertIsNone(results.error) | 321 self.assertIsNone(results.error) |
| 381 | 322 |
| 382 @mock.patch('bisect_perf_regression.BisectPerformanceMetrics.' | |
| 383 'RunPerformanceTestAndParseResults', _MockRunTests) | |
| 384 def testBisectStopsOnDoubtfulRegression(self): | |
| 385 global _MockResultsGenerator | |
| 386 _MockResultsGenerator = (rs for rs in CLEAR_NON_REGRESSION) | |
| 387 results = _GenericDryRun(_GetExtendedOptions(0, 0, False)) | |
| 388 self.assertIsNotNone(results.error) | |
| 389 self.assertIn('could not reproduce the regression', results.error) | |
| 390 | |
| 391 _MockResultsGenerator = (rs for rs in ALMOST_REGRESSION) | |
| 392 results = _GenericDryRun(_GetExtendedOptions(0, 0, False)) | |
| 393 self.assertIsNotNone(results.error) | |
| 394 self.assertIn('could not reproduce the regression', results.error) | |
| 395 | |
| 396 @mock.patch('bisect_perf_regression.BisectPerformanceMetrics.' | |
| 397 'RunPerformanceTestAndParseResults', _MockRunTests) | |
| 398 def testBisectContinuesOnClearRegression(self): | |
| 399 global _MockResultsGenerator | |
| 400 _MockResultsGenerator = (rs for rs in CLEAR_REGRESSION) | |
| 401 with self.assertRaises(StopIteration): | |
| 402 _GenericDryRun(_GetExtendedOptions(0, 0, False)) | |
| 403 | |
| 404 _MockResultsGenerator = (rs for rs in BARELY_REGRESSION) | |
| 405 with self.assertRaises(StopIteration): | |
| 406 _GenericDryRun(_GetExtendedOptions(0, 0, False)) | |
| 407 | 323 |
| 408 def testGetCommitPosition(self): | 324 def testGetCommitPosition(self): |
| 409 cp_git_rev = '7017a81991de983e12ab50dfc071c70e06979531' | 325 cp_git_rev = '7017a81991de983e12ab50dfc071c70e06979531' |
| 410 self.assertEqual(291765, source_control.GetCommitPosition(cp_git_rev)) | 326 self.assertEqual(291765, source_control.GetCommitPosition(cp_git_rev)) |
| 411 | 327 |
| 412 svn_git_rev = 'e6db23a037cad47299a94b155b95eebd1ee61a58' | 328 svn_git_rev = 'e6db23a037cad47299a94b155b95eebd1ee61a58' |
| 413 self.assertEqual(291467, source_control.GetCommitPosition(svn_git_rev)) | 329 self.assertEqual(291467, source_control.GetCommitPosition(svn_git_rev)) |
| 414 | 330 |
| 415 def testGetCommitPositionForV8(self): | 331 def testGetCommitPositionForV8(self): |
| 416 bisect_instance = _GetBisectPerformanceMetricsInstance(DEFAULT_OPTIONS) | 332 bisect_instance = _GetBisectPerformanceMetricsInstance(DEFAULT_OPTIONS) |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 626 '--diff=%s' % patch_content | 542 '--diff=%s' % patch_content |
| 627 ], (None, 0)) | 543 ], (None, 0)) |
| 628 ] | 544 ] |
| 629 self._SetupRunGitMock(try_cmd) | 545 self._SetupRunGitMock(try_cmd) |
| 630 bisect_perf_regression._BuilderTryjob( | 546 bisect_perf_regression._BuilderTryjob( |
| 631 git_revision, bot_name, bisect_job_name, patch) | 547 git_revision, bot_name, bisect_job_name, patch) |
| 632 | 548 |
| 633 | 549 |
| 634 if __name__ == '__main__': | 550 if __name__ == '__main__': |
| 635 unittest.main() | 551 unittest.main() |
| OLD | NEW |