| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 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 from collections import defaultdict | 5 from collections import defaultdict |
| 6 import json | 6 import json |
| 7 | 7 |
| 8 from recipe_engine.config import Dict | 8 from recipe_engine.config import Dict |
| 9 from recipe_engine.config import List | 9 from recipe_engine.config import List |
| 10 from recipe_engine.config import Single | 10 from recipe_engine.config import Single |
| 11 from recipe_engine.recipe_api import Property | 11 from recipe_engine.recipe_api import Property |
| 12 | 12 |
| 13 | 13 |
| 14 DEPS = [ | 14 DEPS = [ |
| 15 'adb', | 15 'adb', |
| 16 'buildbucket', |
| 16 'depot_tools/bot_update', | 17 'depot_tools/bot_update', |
| 17 'chromium', | 18 'chromium', |
| 18 'chromium_android', | 19 'chromium_android', |
| 19 'chromium_tests', | 20 'chromium_tests', |
| 20 'commit_position', | 21 'commit_position', |
| 21 'findit', | 22 'findit', |
| 22 'depot_tools/gclient', | 23 'depot_tools/gclient', |
| 23 'isolate', | 24 'isolate', |
| 24 'recipe_engine/json', | 25 'recipe_engine/json', |
| 25 'recipe_engine/path', | 26 'recipe_engine/path', |
| (...skipping 14 matching lines...) Expand all Loading... |
| 40 'target_testername': Property( | 41 'target_testername': Property( |
| 41 kind=str, | 42 kind=str, |
| 42 help='The target tester to match test config to. If the tests are run ' | 43 help='The target tester to match test config to. If the tests are run ' |
| 43 'on a builder, just treat the builder as a tester.'), | 44 'on a builder, just treat the builder as a tester.'), |
| 44 'good_revision': Property( | 45 'good_revision': Property( |
| 45 kind=str, help='The last known good revision.'), | 46 kind=str, help='The last known good revision.'), |
| 46 'bad_revision': Property( | 47 'bad_revision': Property( |
| 47 kind=str, help='The first known good revision.'), | 48 kind=str, help='The first known good revision.'), |
| 48 'tests': Property( | 49 'tests': Property( |
| 49 kind=Dict(value_type=list), | 50 kind=Dict(value_type=list), |
| 51 default={}, |
| 50 help='The failed tests, the test name should be full name, e.g.: {' | 52 help='The failed tests, the test name should be full name, e.g.: {' |
| 51 ' "browser_tests": [' | 53 ' "browser_tests": [' |
| 52 ' "suite.test1", "suite.test2"' | 54 ' "suite.test1", "suite.test2"' |
| 53 ' ]' | 55 ' ]' |
| 54 '}'), | 56 '}'), |
| 57 'buildbucket': Property( |
| 58 default=None, |
| 59 help='The buildbucket property in which we can find build id.' |
| 60 'We need to use build id to get tests.'), |
| 55 'use_analyze': Property( | 61 'use_analyze': Property( |
| 56 kind=Single(bool, empty_val=False, required=False), default=True, | 62 kind=Single(bool, empty_val=False, required=False), default=True, |
| 57 help='Use analyze to skip commits that do not affect tests.'), | 63 help='Use analyze to skip commits that do not affect tests.'), |
| 58 'suspected_revisions': Property( | 64 'suspected_revisions': Property( |
| 59 kind=List(basestring), default=[], | 65 kind=List(basestring), default=[], |
| 60 help='A list of suspected revisions from heuristic analysis.'), | 66 help='A list of suspected revisions from heuristic analysis.'), |
| 61 } | 67 } |
| 62 | 68 |
| 63 | 69 |
| 64 class TestResult(object): | 70 class TestResult(object): |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 181 if step in failed_tests_dict: | 187 if step in failed_tests_dict: |
| 182 for test in tests: | 188 for test in tests: |
| 183 if test not in failed_tests_dict[step]: | 189 if test not in failed_tests_dict[step]: |
| 184 reduced_dict[step].append(test) | 190 reduced_dict[step].append(test) |
| 185 else: | 191 else: |
| 186 reduced_dict[step].extend(tests) | 192 reduced_dict[step].extend(tests) |
| 187 return reduced_dict | 193 return reduced_dict |
| 188 | 194 |
| 189 | 195 |
| 190 def RunSteps(api, target_mastername, target_testername, good_revision, | 196 def RunSteps(api, target_mastername, target_testername, good_revision, |
| 191 bad_revision, tests, use_analyze, suspected_revisions): | 197 bad_revision, tests, buildbucket, |
| 198 use_analyze, suspected_revisions): |
| 199 |
| 200 if not tests: |
| 201 # tests should be saved in build parameter in this case. |
| 202 buildbucket_json = json.loads(buildbucket) |
| 203 build_id = buildbucket_json['build']['id'] |
| 204 get_build_result = api.buildbucket.get_build(build_id) |
| 205 tests = json.loads( |
| 206 get_build_result.stdout['build']['parameters_json']).get( |
| 207 'additional_build_parameters', {}).get('tests') |
| 208 |
| 192 assert tests, 'No failed tests were specified.' | 209 assert tests, 'No failed tests were specified.' |
| 193 | 210 |
| 194 # Figure out which builder configuration we should match for compile config. | 211 # Figure out which builder configuration we should match for compile config. |
| 195 # Sometimes, the builder itself runs the tests and there is no tester. In | 212 # Sometimes, the builder itself runs the tests and there is no tester. In |
| 196 # such cases, just treat the builder as a "tester". Thus, we default to | 213 # such cases, just treat the builder as a "tester". Thus, we default to |
| 197 # the target tester. | 214 # the target tester. |
| 198 tester_config = api.chromium_tests.builders.get( | 215 tester_config = api.chromium_tests.builders.get( |
| 199 target_mastername).get('builders', {}).get(target_testername) | 216 target_mastername).get('builders', {}).get(target_testername) |
| 200 target_buildername = (tester_config.get('parent_buildername') or | 217 target_buildername = (tester_config.get('parent_buildername') or |
| 201 target_testername) | 218 target_testername) |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 # Set the report as a build property too, so that it will be reported back | 351 # Set the report as a build property too, so that it will be reported back |
| 335 # to Buildbucket and Findit will pull from there instead of buildbot master. | 352 # to Buildbucket and Findit will pull from there instead of buildbot master. |
| 336 step_result.presentation.properties['report'] = report | 353 step_result.presentation.properties['report'] = report |
| 337 | 354 |
| 338 return report | 355 return report |
| 339 | 356 |
| 340 | 357 |
| 341 def GenTests(api): | 358 def GenTests(api): |
| 342 def props( | 359 def props( |
| 343 tests, platform_name, tester_name, use_analyze=False, good_revision=None, | 360 tests, platform_name, tester_name, use_analyze=False, good_revision=None, |
| 344 bad_revision=None, suspected_revisions=None): | 361 bad_revision=None, suspected_revisions=None, buildbucket=None): |
| 345 properties = { | 362 properties = { |
| 346 'mastername': 'tryserver.chromium.%s' % platform_name, | 363 'mastername': 'tryserver.chromium.%s' % platform_name, |
| 347 'buildername': '%s_chromium_variable' % platform_name, | 364 'buildername': '%s_chromium_variable' % platform_name, |
| 348 'slavename': 'build1-a1', | 365 'slavename': 'build1-a1', |
| 349 'buildnumber': 1, | 366 'buildnumber': 1, |
| 350 'target_mastername': 'chromium.%s' % platform_name, | 367 'target_mastername': 'chromium.%s' % platform_name, |
| 351 'target_testername': tester_name, | 368 'target_testername': tester_name, |
| 352 'good_revision': good_revision or 'r0', | 369 'good_revision': good_revision or 'r0', |
| 353 'bad_revision': bad_revision or 'r1', | 370 'bad_revision': bad_revision or 'r1', |
| 354 'tests': tests, | |
| 355 'use_analyze': use_analyze, | 371 'use_analyze': use_analyze, |
| 356 } | 372 } |
| 373 if tests: |
| 374 properties['tests'] = tests |
| 357 if suspected_revisions: | 375 if suspected_revisions: |
| 358 properties['suspected_revisions'] = suspected_revisions | 376 properties['suspected_revisions'] = suspected_revisions |
| 377 if buildbucket: |
| 378 properties['buildbucket'] = buildbucket |
| 359 return api.properties(**properties) + api.platform.name(platform_name) | 379 return api.properties(**properties) + api.platform.name(platform_name) |
| 360 | 380 |
| 361 def simulated_gtest_output(failed_test_names=(), passed_test_names=()): | 381 def simulated_gtest_output(failed_test_names=(), passed_test_names=()): |
| 362 cur_iteration_data = {} | 382 cur_iteration_data = {} |
| 363 for test_name in failed_test_names: | 383 for test_name in failed_test_names: |
| 364 cur_iteration_data[test_name] = [{ | 384 cur_iteration_data[test_name] = [{ |
| 365 'elapsed_time_ms': 0, | 385 'elapsed_time_ms': 0, |
| 366 'output_snippet': '', | 386 'output_snippet': '', |
| 367 'status': 'FAILURE', | 387 'status': 'FAILURE', |
| 368 }] | 388 }] |
| 369 for test_name in passed_test_names: | 389 for test_name in passed_test_names: |
| 370 cur_iteration_data[test_name] = [{ | 390 cur_iteration_data[test_name] = [{ |
| 371 'elapsed_time_ms': 0, | 391 'elapsed_time_ms': 0, |
| 372 'output_snippet': '', | 392 'output_snippet': '', |
| 373 'status': 'SUCCESS', | 393 'status': 'SUCCESS', |
| 374 }] | 394 }] |
| 375 | 395 |
| 376 canned_jsonish = { | 396 canned_jsonish = { |
| 377 'per_iteration_data': [cur_iteration_data] | 397 'per_iteration_data': [cur_iteration_data] |
| 378 } | 398 } |
| 379 | 399 |
| 380 return api.test_utils.raw_gtest_output( | 400 return api.test_utils.raw_gtest_output( |
| 381 canned_jsonish, 1 if failed_test_names else 0) | 401 canned_jsonish, 1 if failed_test_names else 0) |
| 382 | 402 |
| 403 def simulated_buildbucket_output(additional_build_parameters): |
| 404 buildbucket_output = { |
| 405 'build':{ |
| 406 'parameters_json': json.dumps(additional_build_parameters) |
| 407 } |
| 408 } |
| 409 |
| 410 return api.buildbucket.step_data( |
| 411 'buildbucket.get', |
| 412 stdout=api.raw_io.output(json.dumps(buildbucket_output))) |
| 413 |
| 383 yield ( | 414 yield ( |
| 384 api.test('nonexistent_test_step_skipped') + | 415 api.test('nonexistent_test_step_skipped') + |
| 385 props({'newly_added_tests': ['Test.One', 'Test.Two', 'Test.Three']}, | 416 props({'newly_added_tests': ['Test.One', 'Test.Two', 'Test.Three']}, |
| 386 'win', 'Win7 Tests (1)') + | 417 'win', 'Win7 Tests (1)') + |
| 387 api.override_step_data('test r1.read test spec', api.json.output({ | 418 api.override_step_data('test r1.read test spec', api.json.output({ |
| 388 'Win7 Tests (1)': { | 419 'Win7 Tests (1)': { |
| 389 'gtest_tests': [ | 420 'gtest_tests': [ |
| 390 { | 421 { |
| 391 'test': 'gl_tests', | 422 'test': 'gl_tests', |
| 392 'swarming': {'can_use_on_swarming_builders': True}, | 423 'swarming': {'can_use_on_swarming_builders': True}, |
| (...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 887 'test r6.gl_tests (r6) on Mac-10.9', | 918 'test r6.gl_tests (r6) on Mac-10.9', |
| 888 simulated_gtest_output(failed_test_names=['Test.One']))+ | 919 simulated_gtest_output(failed_test_names=['Test.One']))+ |
| 889 api.override_step_data( | 920 api.override_step_data( |
| 890 'test r2.gl_tests (r2) on Mac-10.9', | 921 'test r2.gl_tests (r2) on Mac-10.9', |
| 891 simulated_gtest_output(passed_test_names=['Test.Two'])) + | 922 simulated_gtest_output(passed_test_names=['Test.Two'])) + |
| 892 api.override_step_data( | 923 api.override_step_data( |
| 893 'test r3.gl_tests (r3) on Mac-10.9', | 924 'test r3.gl_tests (r3) on Mac-10.9', |
| 894 simulated_gtest_output(failed_test_names=['Test.Two'])) | 925 simulated_gtest_output(failed_test_names=['Test.Two'])) |
| 895 ) | 926 ) |
| 896 | 927 |
| 897 | |
| 898 yield ( | 928 yield ( |
| 899 api.test('findit_consecutive_culprits') + | 929 api.test('findit_consecutive_culprits') + |
| 900 props( | 930 props( |
| 901 {'gl_tests': ['Test.One']}, | 931 {'gl_tests': ['Test.One']}, |
| 902 'mac', 'Mac10.9 Tests', use_analyze=False, | 932 'mac', 'Mac10.9 Tests', use_analyze=False, |
| 903 good_revision='r0', bad_revision='r6', | 933 good_revision='r0', bad_revision='r6', |
| 904 suspected_revisions=['r3', 'r4']) + | 934 suspected_revisions=['r3', 'r4']) + |
| 905 api.override_step_data('test r2.read test spec', api.json.output({ | 935 api.override_step_data('test r2.read test spec', api.json.output({ |
| 906 'Mac10.9 Tests': { | 936 'Mac10.9 Tests': { |
| 907 'gtest_tests': [ | 937 'gtest_tests': [ |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 966 'notice': [ | 996 'notice': [ |
| 967 { | 997 { |
| 968 'infra_status': { | 998 'infra_status': { |
| 969 'ping_status_code': 408, | 999 'ping_status_code': 408, |
| 970 }, | 1000 }, |
| 971 }, | 1001 }, |
| 972 ], | 1002 ], |
| 973 }), | 1003 }), |
| 974 retcode=1) | 1004 retcode=1) |
| 975 ) | 1005 ) |
| 1006 |
| 1007 yield ( |
| 1008 api.test('use_build_parameter_for_tests') + |
| 1009 props({}, 'mac', 'Mac10.9 Tests', use_analyze=False, |
| 1010 good_revision='r0', bad_revision='r6', |
| 1011 suspected_revisions=['r3', 'r4'], |
| 1012 buildbucket=json.dumps({'build': {'id': 'id1'}})) + |
| 1013 simulated_buildbucket_output({ |
| 1014 'additional_build_parameters' : { |
| 1015 'tests': { |
| 1016 'gl_tests': ['Test.One'] |
| 1017 } |
| 1018 }}) + |
| 1019 api.override_step_data('test r2.read test spec', api.json.output({ |
| 1020 'Mac10.9 Tests': { |
| 1021 'gtest_tests': [ |
| 1022 { |
| 1023 'test': 'gl_tests', |
| 1024 'swarming': {'can_use_on_swarming_builders': True}, |
| 1025 }, |
| 1026 ], |
| 1027 }, |
| 1028 })) + |
| 1029 api.override_step_data('test r3.read test spec', api.json.output({ |
| 1030 'Mac10.9 Tests': { |
| 1031 'gtest_tests': [ |
| 1032 { |
| 1033 'test': 'gl_tests', |
| 1034 'swarming': {'can_use_on_swarming_builders': True}, |
| 1035 }, |
| 1036 ], |
| 1037 }, |
| 1038 })) + |
| 1039 api.override_step_data('test r4.read test spec', api.json.output({ |
| 1040 'Mac10.9 Tests': { |
| 1041 'gtest_tests': [ |
| 1042 { |
| 1043 'test': 'gl_tests', |
| 1044 'swarming': {'can_use_on_swarming_builders': True}, |
| 1045 }, |
| 1046 ], |
| 1047 }, |
| 1048 })) + |
| 1049 api.override_step_data( |
| 1050 'git commits in range', |
| 1051 api.raw_io.stream_output( |
| 1052 '\n'.join('r%d' % i for i in reversed(range(1, 7))))) + |
| 1053 api.override_step_data( |
| 1054 'test r4.gl_tests (r4) on Mac-10.9', |
| 1055 simulated_gtest_output(failed_test_names=['Test.One'])) + |
| 1056 api.override_step_data( |
| 1057 'test r2.gl_tests (r2) on Mac-10.9', |
| 1058 simulated_gtest_output(passed_test_names=['Test.One'])) + |
| 1059 api.override_step_data( |
| 1060 'test r3.gl_tests (r3) on Mac-10.9', |
| 1061 simulated_gtest_output(passed_test_names=['Test.One'])) |
| 1062 ) |
| OLD | NEW |