| 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 math | 5 import math |
| 6 import unittest | 6 import unittest |
| 7 | 7 |
| 8 from auto_bisect import source_control as source_control_module |
| 9 |
| 8 # Special import necessary because filename contains dash characters. | 10 # Special import necessary because filename contains dash characters. |
| 9 bisect_perf_module = __import__('bisect-perf-regression') | 11 bisect_perf_module = __import__('bisect-perf-regression') |
| 10 | 12 |
| 11 | 13 # Sample output for a performance test used in the results parsing tests below. |
| 12 RESULTS_OUTPUT = """RESULT write_operations: write_operations= 23089 count | 14 RESULTS_OUTPUT = """RESULT write_operations: write_operations= 23089 count |
| 13 RESULT read_bytes_gpu: read_bytes_gpu= 35201 kb | 15 RESULT read_bytes_gpu: read_bytes_gpu= 35201 kb |
| 14 RESULT write_bytes_gpu: write_bytes_gpu= 542 kb | 16 RESULT write_bytes_gpu: write_bytes_gpu= 542 kb |
| 15 RESULT telemetry_page_measurement_results: num_failed= 0 count | 17 RESULT telemetry_page_measurement_results: num_failed= 0 count |
| 16 RESULT telemetry_page_measurement_results: num_errored= 0 count | 18 RESULT telemetry_page_measurement_results: num_errored= 0 count |
| 17 *RESULT Total: Total_ref= %(value)s | 19 *RESULT Total: Total_ref= %(value)s |
| 18 """ | 20 """ |
| 19 | 21 |
| 20 | 22 |
| 21 class BisectPerfRegressionTest(unittest.TestCase): | 23 class BisectPerfRegressionTest(unittest.TestCase): |
| 22 """Test case for top-level functions in the bisect-perf-regrssion module.""" | 24 """Test case for top-level functions in the bisect-perf-regrssion module.""" |
| 23 | 25 |
| 24 def setUp(self): | 26 def setUp(self): |
| 25 """Sets up the test environment before each test method.""" | 27 """Sets up the test environment before each test method.""" |
| 26 pass | 28 pass |
| 27 | 29 |
| 28 def tearDown(self): | 30 def tearDown(self): |
| 29 """Cleans up the test environment after each test method.""" | 31 """Cleans up the test environment after each test method.""" |
| 30 pass | 32 pass |
| 31 | 33 |
| 34 def testConfidenceScore(self): |
| 35 """Tests the confidence calculation.""" |
| 36 bad_values = [[0, 1], [1, 2]] |
| 37 good_values = [[6, 7], [7, 8]] |
| 38 # Closest means are mean(1, 2) and mean(6, 7). |
| 39 distance = 6.5 - 1.5 |
| 40 # Standard deviation of [n-1, n, n, n+1] is 0.8165. |
| 41 stddev_sum = 0.8165 + 0.8165 |
| 42 # Expected confidence is an int in the range [0, 100]. |
| 43 expected_confidence = min(100, int(100 * distance / float(stddev_sum))) |
| 44 self.assertEqual( |
| 45 expected_confidence, |
| 46 bisect_perf_module.ConfidenceScore(bad_values, good_values)) |
| 47 |
| 48 def testConfidenceScoreZeroConfidence(self): |
| 49 """Tests the confidence calculation when it's expected to be 0.""" |
| 50 bad_values = [[0, 1], [1, 2], [4, 5], [0, 2]] |
| 51 good_values = [[4, 5], [6, 7], [7, 8]] |
| 52 # Both groups have value lists with means of 4.5, which means distance |
| 53 # between groups is zero, and thus confidence is zero. |
| 54 self.assertEqual( |
| 55 0, bisect_perf_module.ConfidenceScore(bad_values, good_values)) |
| 56 |
| 57 def testConfidenceScoreMaxConfidence(self): |
| 58 """Tests the confidence calculation when it's expected to be 100.""" |
| 59 bad_values = [[1, 1], [1, 1]] |
| 60 good_values = [[1.2, 1.2], [1.2, 1.2]] |
| 61 # Standard deviation in both groups is zero, so confidence is 100. |
| 62 self.assertEqual( |
| 63 100, bisect_perf_module.ConfidenceScore(bad_values, good_values)) |
| 64 |
| 32 def testParseDEPSStringManually(self): | 65 def testParseDEPSStringManually(self): |
| 33 """Tests DEPS parsing.""" | 66 """Tests DEPS parsing.""" |
| 34 bisect_options = bisect_perf_module.BisectOptions() | 67 bisect_options = bisect_perf_module.BisectOptions() |
| 35 bisect_instance = bisect_perf_module.BisectPerformanceMetrics( | 68 bisect_instance = bisect_perf_module.BisectPerformanceMetrics( |
| 36 None, bisect_options) | 69 None, bisect_options) |
| 37 | 70 |
| 38 deps_file_contents = """ | 71 deps_file_contents = """ |
| 39 vars = { | 72 vars = { |
| 40 'ffmpeg_hash': | 73 'ffmpeg_hash': |
| 41 '@ac4a9f31fe2610bd146857bbd55d7a260003a888', | 74 '@ac4a9f31fe2610bd146857bbd55d7a260003a888', |
| (...skipping 11 matching lines...) Expand all Loading... |
| 53 # filtered out. | 86 # filtered out. |
| 54 expected_vars_dict = { | 87 expected_vars_dict = { |
| 55 'ffmpeg_hash': '@ac4a9f31fe2610bd146857bbd55d7a260003a888', | 88 'ffmpeg_hash': '@ac4a9f31fe2610bd146857bbd55d7a260003a888', |
| 56 'webkit_rev': '@e01ac0a267d1017288bc67fa3c366b10469d8a24', | 89 'webkit_rev': '@e01ac0a267d1017288bc67fa3c366b10469d8a24', |
| 57 'angle_revision': '74697cf2064c0a2c0d7e1b1b28db439286766a05' | 90 'angle_revision': '74697cf2064c0a2c0d7e1b1b28db439286766a05' |
| 58 } | 91 } |
| 59 vars_dict = bisect_instance._ParseRevisionsFromDEPSFileManually( | 92 vars_dict = bisect_instance._ParseRevisionsFromDEPSFileManually( |
| 60 deps_file_contents) | 93 deps_file_contents) |
| 61 self.assertEqual(vars_dict, expected_vars_dict) | 94 self.assertEqual(vars_dict, expected_vars_dict) |
| 62 | 95 |
| 63 def testCalculateTruncatedMeanRaisesError(self): | |
| 64 """CalculateTrunctedMean raises an error when passed an empty list.""" | |
| 65 with self.assertRaises(TypeError): | |
| 66 bisect_perf_module.CalculateTruncatedMean([], 0) | |
| 67 | |
| 68 def testCalculateMeanSingleNum(self): | |
| 69 """Tests the CalculateMean function with a single number.""" | |
| 70 self.assertEqual(3.0, bisect_perf_module.CalculateMean([3])) | |
| 71 | |
| 72 def testCalculateMeanShortList(self): | |
| 73 """Tests the CalculateMean function with a short list.""" | |
| 74 self.assertEqual(0.5, bisect_perf_module.CalculateMean([-3, 0, 1, 4])) | |
| 75 | |
| 76 def testCalculateMeanCompareAlternateImplementation(self): | |
| 77 """Tests CalculateMean by comparing against an alternate implementation.""" | |
| 78 def AlternateMeanFunction(values): | |
| 79 """Simple arithmetic mean function.""" | |
| 80 return sum(values) / float(len(values)) | |
| 81 test_values_lists = [[1], [5, 6.5, 1.2, 3], [-3, 0, 1, 4], | |
| 82 [-3, -1, 0.12, 0.752, 3.33, 8, 16, 32, 439]] | |
| 83 for values in test_values_lists: | |
| 84 self.assertEqual( | |
| 85 AlternateMeanFunction(values), | |
| 86 bisect_perf_module.CalculateMean(values)) | |
| 87 | |
| 88 def testCalculateConfidence(self): | |
| 89 """Tests the confidence calculation.""" | |
| 90 bad_values = [[0, 1], [1, 2]] | |
| 91 good_values = [[6, 7], [7, 8]] | |
| 92 # Closest means are mean(1, 2) and mean(6, 7). | |
| 93 distance = 6.5 - 1.5 | |
| 94 # Standard deviation of [n-1, n, n, n+1] is 0.8165. | |
| 95 stddev_sum = 0.8165 + 0.8165 | |
| 96 # Expected confidence is an int in the range [0, 100]. | |
| 97 expected_confidence = min(100, int(100 * distance / float(stddev_sum))) | |
| 98 self.assertEqual( | |
| 99 expected_confidence, | |
| 100 bisect_perf_module.CalculateConfidence(bad_values, good_values)) | |
| 101 | |
| 102 def testCalculateConfidence0(self): | |
| 103 """Tests the confidence calculation when it's expected to be 0.""" | |
| 104 bad_values = [[0, 1], [1, 2], [4, 5], [0, 2]] | |
| 105 good_values = [[4, 5], [6, 7], [7, 8]] | |
| 106 # Both groups have value lists with means of 4.5, which means distance | |
| 107 # between groups is zero, and thus confidence is zero. | |
| 108 self.assertEqual( | |
| 109 0, bisect_perf_module.CalculateConfidence(bad_values, good_values)) | |
| 110 | |
| 111 def testCalculateConfidence100(self): | |
| 112 """Tests the confidence calculation when it's expected to be 100.""" | |
| 113 bad_values = [[1, 1], [1, 1]] | |
| 114 good_values = [[1.2, 1.2], [1.2, 1.2]] | |
| 115 # Standard deviation in both groups is zero, so confidence is 100. | |
| 116 self.assertEqual( | |
| 117 100, bisect_perf_module.CalculateConfidence(bad_values, good_values)) | |
| 118 | |
| 119 def testCalculateRelativeChange(self): | |
| 120 """Tests the common cases for calculating relative change.""" | |
| 121 # The change is relative to the first value, regardless of which is bigger. | |
| 122 self.assertEqual(0.5, bisect_perf_module.CalculateRelativeChange(1.0, 1.5)) | |
| 123 self.assertEqual(0.5, bisect_perf_module.CalculateRelativeChange(2.0, 1.0)) | |
| 124 | |
| 125 def testCalculateRelativeChangeFromZero(self): | |
| 126 """Tests what happens when relative change from zero is calculated.""" | |
| 127 # If the first number is zero, then the result is not a number. | |
| 128 self.assertEqual(0, bisect_perf_module.CalculateRelativeChange(0, 0)) | |
| 129 self.assertTrue( | |
| 130 math.isnan(bisect_perf_module.CalculateRelativeChange(0, 1))) | |
| 131 self.assertTrue( | |
| 132 math.isnan(bisect_perf_module.CalculateRelativeChange(0, -1))) | |
| 133 | |
| 134 def testCalculateRelativeChangeWithNegatives(self): | |
| 135 """Tests that relative change given is always positive.""" | |
| 136 self.assertEqual(3.0, bisect_perf_module.CalculateRelativeChange(-1, 2)) | |
| 137 self.assertEqual(3.0, bisect_perf_module.CalculateRelativeChange(1, -2)) | |
| 138 self.assertEqual(1.0, bisect_perf_module.CalculateRelativeChange(-1, -2)) | |
| 139 | |
| 140 def testTryParseResultValuesFromOutputWithSingleValue(self): | 96 def testTryParseResultValuesFromOutputWithSingleValue(self): |
| 141 """Tests result pattern <*>RESULT <graph>: <trace>= <value>""" | 97 """Tests result pattern <*>RESULT <graph>: <trace>= <value>""" |
| 142 bisect_options = bisect_perf_module.BisectOptions() | 98 bisect_options = bisect_perf_module.BisectOptions() |
| 143 bisect_instance = bisect_perf_module.BisectPerformanceMetrics( | 99 bisect_instance = bisect_perf_module.BisectPerformanceMetrics( |
| 144 None, bisect_options) | 100 None, bisect_options) |
| 145 metrics = ['Total', 'Total_ref'] | 101 metrics = ['Total', 'Total_ref'] |
| 146 self.assertEqual( | 102 self.assertEqual( |
| 147 [66.88], bisect_instance.TryParseResultValuesFromOutput( | 103 [66.88], bisect_instance.TryParseResultValuesFromOutput( |
| 148 metrics, RESULTS_OUTPUT % {'value': '66.88 kb'})) | 104 metrics, RESULTS_OUTPUT % {'value': '66.88 kb'})) |
| 149 self.assertEqual( | 105 self.assertEqual( |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 self.assertEqual( | 191 self.assertEqual( |
| 236 [], bisect_instance.TryParseResultValuesFromOutput( | 192 [], bisect_instance.TryParseResultValuesFromOutput( |
| 237 metrics, RESULTS_OUTPUT % {'value': '{}kb'})) | 193 metrics, RESULTS_OUTPUT % {'value': '{}kb'})) |
| 238 self.assertEqual( | 194 self.assertEqual( |
| 239 [], bisect_instance.TryParseResultValuesFromOutput( | 195 [], bisect_instance.TryParseResultValuesFromOutput( |
| 240 metrics, RESULTS_OUTPUT % {'value': '{XYZ}kb'})) | 196 metrics, RESULTS_OUTPUT % {'value': '{XYZ}kb'})) |
| 241 | 197 |
| 242 def testGetCompatibleCommand(self): | 198 def testGetCompatibleCommand(self): |
| 243 bisect_options = bisect_perf_module.BisectOptions() | 199 bisect_options = bisect_perf_module.BisectOptions() |
| 244 bisect_options.output_buildbot_annotations = None | 200 bisect_options.output_buildbot_annotations = None |
| 245 source_control = bisect_perf_module.DetermineAndCreateSourceControl( | 201 source_control = source_control_module.DetermineAndCreateSourceControl( |
| 246 bisect_options) | 202 bisect_options) |
| 247 bisect_instance = bisect_perf_module.BisectPerformanceMetrics( | 203 bisect_instance = bisect_perf_module.BisectPerformanceMetrics( |
| 248 source_control, bisect_options) | 204 source_control, bisect_options) |
| 249 bisect_instance.opts.target_platform = 'android' | 205 bisect_instance.opts.target_platform = 'android' |
| 250 depot = 'chromium' | 206 depot = 'chromium' |
| 251 # android-chrome-shell -> android-chromium-testshell | 207 # android-chrome-shell -> android-chromium-testshell |
| 252 revision = 274857 | 208 revision = 274857 |
| 253 git_revision = bisect_instance.source_control.ResolveToRevision( | 209 git_revision = bisect_instance.source_control.ResolveToRevision( |
| 254 revision, 'chromium', 100) | 210 revision, 'chromium', bisect_perf_module.DEPOT_DEPS_NAME, 100) |
| 255 command = ('tools/perf/run_benchmark -v ' | 211 command = ('tools/perf/run_benchmark -v ' |
| 256 '--browser=android-chrome-shell page_cycler.intl_ja_zh') | 212 '--browser=android-chrome-shell page_cycler.intl_ja_zh') |
| 257 expected_command = ('tools/perf/run_benchmark -v --browser=' | 213 expected_command = ('tools/perf/run_benchmark -v --browser=' |
| 258 'android-chromium-testshell page_cycler.intl_ja_zh') | 214 'android-chromium-testshell page_cycler.intl_ja_zh') |
| 259 self.assertEqual( | 215 self.assertEqual( |
| 260 bisect_instance.GetCompatibleCommand(command, git_revision, depot), | 216 bisect_instance.GetCompatibleCommand(command, git_revision, depot), |
| 261 expected_command) | 217 expected_command) |
| 262 | 218 |
| 263 # android-chromium-testshell -> android-chromium-testshell | 219 # android-chromium-testshell -> android-chromium-testshell |
| 264 revision = 274858 | 220 revision = 274858 |
| 265 git_revision = bisect_instance.source_control.ResolveToRevision( | 221 git_revision = bisect_instance.source_control.ResolveToRevision( |
| 266 revision, 'chromium', 100) | 222 revision, 'chromium', bisect_perf_module.DEPOT_DEPS_NAME, 100) |
| 267 command = ('tools/perf/run_benchmark -v ' | 223 command = ('tools/perf/run_benchmark -v ' |
| 268 '--browser=android-chromium-testshell page_cycler.intl_ja_zh') | 224 '--browser=android-chromium-testshell page_cycler.intl_ja_zh') |
| 269 expected_command = ('tools/perf/run_benchmark -v --browser=' | 225 expected_command = ('tools/perf/run_benchmark -v --browser=' |
| 270 'android-chromium-testshell page_cycler.intl_ja_zh') | 226 'android-chromium-testshell page_cycler.intl_ja_zh') |
| 271 self.assertEqual( | 227 self.assertEqual( |
| 272 bisect_instance.GetCompatibleCommand(command, git_revision, depot), | 228 bisect_instance.GetCompatibleCommand(command, git_revision, depot), |
| 273 expected_command) | 229 expected_command) |
| 274 | 230 |
| 275 # android-chromium-testshell -> android-chrome-shell | 231 # android-chromium-testshell -> android-chrome-shell |
| 276 revision = 276628 | 232 revision = 276628 |
| 277 git_revision = bisect_instance.source_control.ResolveToRevision( | 233 git_revision = bisect_instance.source_control.ResolveToRevision( |
| 278 revision, 'chromium', 100) | 234 revision, 'chromium', bisect_perf_module.DEPOT_DEPS_NAME, 100) |
| 279 command = ('tools/perf/run_benchmark -v ' | 235 command = ('tools/perf/run_benchmark -v ' |
| 280 '--browser=android-chromium-testshell page_cycler.intl_ja_zh') | 236 '--browser=android-chromium-testshell page_cycler.intl_ja_zh') |
| 281 expected_command = ('tools/perf/run_benchmark -v --browser=' | 237 expected_command = ('tools/perf/run_benchmark -v --browser=' |
| 282 'android-chrome-shell page_cycler.intl_ja_zh') | 238 'android-chrome-shell page_cycler.intl_ja_zh') |
| 283 self.assertEqual( | 239 self.assertEqual( |
| 284 bisect_instance.GetCompatibleCommand(command, git_revision, depot), | 240 bisect_instance.GetCompatibleCommand(command, git_revision, depot), |
| 285 expected_command) | 241 expected_command) |
| 286 # android-chrome-shell -> android-chrome-shell | 242 # android-chrome-shell -> android-chrome-shell |
| 287 command = ('tools/perf/run_benchmark -v ' | 243 command = ('tools/perf/run_benchmark -v ' |
| 288 '--browser=android-chrome-shell page_cycler.intl_ja_zh') | 244 '--browser=android-chrome-shell page_cycler.intl_ja_zh') |
| 289 expected_command = ('tools/perf/run_benchmark -v --browser=' | 245 expected_command = ('tools/perf/run_benchmark -v --browser=' |
| 290 'android-chrome-shell page_cycler.intl_ja_zh') | 246 'android-chrome-shell page_cycler.intl_ja_zh') |
| 291 self.assertEqual( | 247 self.assertEqual( |
| 292 bisect_instance.GetCompatibleCommand(command, git_revision, depot), | 248 bisect_instance.GetCompatibleCommand(command, git_revision, depot), |
| 293 expected_command) | 249 expected_command) |
| 294 # Not android platform | 250 # Not android platform |
| 295 bisect_instance.opts.target_platform = 'chromium' | 251 bisect_instance.opts.target_platform = 'chromium' |
| 296 command = ('tools/perf/run_benchmark -v ' | 252 command = ('tools/perf/run_benchmark -v ' |
| 297 '--browser=release page_cycler.intl_ja_zh') | 253 '--browser=release page_cycler.intl_ja_zh') |
| 298 expected_command = ('tools/perf/run_benchmark -v --browser=' | 254 expected_command = ('tools/perf/run_benchmark -v --browser=' |
| 299 'release page_cycler.intl_ja_zh') | 255 'release page_cycler.intl_ja_zh') |
| 300 self.assertEqual( | 256 self.assertEqual( |
| 301 bisect_instance.GetCompatibleCommand(command, git_revision, depot), | 257 bisect_instance.GetCompatibleCommand(command, git_revision, depot), |
| 302 expected_command) | 258 expected_command) |
| 303 | 259 |
| 260 |
| 304 if __name__ == '__main__': | 261 if __name__ == '__main__': |
| 305 unittest.main() | 262 unittest.main() |
| OLD | NEW |