| 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 datetime import datetime | 5 from datetime import datetime |
| 6 from datetime import timedelta | 6 from datetime import timedelta |
| 7 | 7 |
| 8 from common.waterfall import failure_type | 8 from common.waterfall import failure_type |
| 9 from model import analysis_status | 9 from model import analysis_status |
| 10 from model.wf_analysis import WfAnalysis | 10 from model.wf_analysis import WfAnalysis |
| 11 from model.wf_build import WfBuild | 11 from model.wf_build import WfBuild |
| 12 from model.wf_failure_group import WfFailureGroup | 12 from model.wf_failure_group import WfFailureGroup |
| 13 from model.wf_try_job import WfTryJob | 13 from model.wf_try_job import WfTryJob |
| 14 from waterfall import try_job_util | 14 from waterfall import try_job_util |
| 15 from waterfall.test import wf_testcase | 15 from waterfall.test import wf_testcase |
| 16 from waterfall.try_job_type import TryJobType | |
| 17 | |
| 18 | |
| 19 class _MockRootPipeline(object): | |
| 20 STARTED = False | |
| 21 | |
| 22 def __init__(self, *_): | |
| 23 pass | |
| 24 | |
| 25 def start(self, *_, **__): | |
| 26 _MockRootPipeline.STARTED = True | |
| 27 | |
| 28 @property | |
| 29 def pipeline_status_path(self): | |
| 30 return 'path' | |
| 31 | 16 |
| 32 | 17 |
| 33 class TryJobUtilTest(wf_testcase.WaterfallTestCase): | 18 class TryJobUtilTest(wf_testcase.WaterfallTestCase): |
| 34 | 19 |
| 20 def testShouldBailOutforOutdatedBuild(self): |
| 21 yesterday = datetime.utcnow() - timedelta(days=1) |
| 22 build = WfBuild.Create('m', 'b', 1) |
| 23 build.start_time = yesterday |
| 24 self.assertTrue(try_job_util._ShouldBailOutForOutdatedBuild(build)) |
| 25 |
| 26 build.start_time = yesterday + timedelta(hours=1) |
| 27 self.assertFalse(try_job_util._ShouldBailOutForOutdatedBuild(build)) |
| 28 |
| 35 def testNotNeedANewTryJobIfBuilderIsNotSupportedYet(self): | 29 def testNotNeedANewTryJobIfBuilderIsNotSupportedYet(self): |
| 36 master_name = 'master3' | 30 master_name = 'master3' |
| 37 builder_name = 'builder3' | 31 builder_name = 'builder3' |
| 38 build_number = 223 | 32 build_number = 223 |
| 39 WfAnalysis.Create(master_name, builder_name, build_number).put() | 33 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 40 failure_info = { | 34 failure_info = { |
| 41 'master_name': master_name, | 35 'master_name': master_name, |
| 42 'builder_name': builder_name, | 36 'builder_name': builder_name, |
| 43 'build_number': build_number, | 37 'build_number': build_number, |
| 44 'failed_steps': { | 38 'failed_steps': { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 58 'chromium_revision': '221-2' | 52 'chromium_revision': '221-2' |
| 59 }, | 53 }, |
| 60 '222': { | 54 '222': { |
| 61 'blame_list': ['222-1'], | 55 'blame_list': ['222-1'], |
| 62 'chromium_revision': '222-1' | 56 'chromium_revision': '222-1' |
| 63 }, | 57 }, |
| 64 '223': { | 58 '223': { |
| 65 'blame_list': ['223-1', '223-2', '223-3'], | 59 'blame_list': ['223-1', '223-2', '223-3'], |
| 66 'chromium_revision': '223-3' | 60 'chromium_revision': '223-3' |
| 67 } | 61 } |
| 68 } | 62 }, |
| 63 'failure_type': failure_type.COMPILE |
| 69 } | 64 } |
| 70 | 65 |
| 71 self.mock( | 66 need_try_job = try_job_util.NeedANewTryJob( |
| 72 try_job_util.swarming_tasks_to_try_job_pipeline, | 67 master_name, builder_name, build_number, failure_info, None, None) |
| 73 'SwarmingTasksToTryJobPipeline', _MockRootPipeline) | |
| 74 _MockRootPipeline.STARTED = False | |
| 75 | 68 |
| 76 failure_result_map = try_job_util.ScheduleTryJobIfNeeded( | 69 self.assertFalse(need_try_job) |
| 77 failure_info, None, None) | |
| 78 | |
| 79 self.assertFalse(_MockRootPipeline.STARTED) | |
| 80 self.assertEqual({}, failure_result_map) | |
| 81 | 70 |
| 82 def testBailOutForTestTryJob(self): | 71 def testBailOutForTestTryJob(self): |
| 83 master_name = 'master2' | 72 master_name = 'master2' |
| 84 builder_name = 'builder2' | 73 builder_name = 'builder2' |
| 85 build_number = 223 | 74 build_number = 223 |
| 86 WfAnalysis.Create(master_name, builder_name, build_number).put() | 75 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 87 failure_info = { | 76 failure_info = { |
| 88 'master_name': master_name, | 77 'master_name': master_name, |
| 89 'builder_name': builder_name, | 78 'builder_name': builder_name, |
| 90 'build_number': build_number, | 79 'build_number': build_number, |
| 91 'failed_steps': { | 80 'failed_steps': { |
| 92 'a_test': {} | 81 'a_test': {} |
| 93 }, | 82 }, |
| 94 'failure_type': failure_type.TEST | 83 'failure_type': failure_type.TEST |
| 95 } | 84 } |
| 96 | 85 |
| 97 def _MockShouldBailOutForOutdatedBuild(*_): | 86 def _MockShouldBailOutForOutdatedBuild(*_): |
| 98 return False | 87 return False |
| 99 self.mock( | 88 self.mock( |
| 100 try_job_util, '_ShouldBailOutForOutdatedBuild', | 89 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 101 _MockShouldBailOutForOutdatedBuild) | 90 _MockShouldBailOutForOutdatedBuild) |
| 102 | 91 |
| 103 failure_result_map = try_job_util.ScheduleTryJobIfNeeded( | 92 need_try_job = try_job_util.NeedANewTryJob( |
| 104 failure_info, None, None) | 93 master_name, builder_name, build_number, failure_info, None, None) |
| 105 | 94 |
| 106 self.assertEqual({}, failure_result_map) | 95 self.assertFalse(need_try_job) |
| 107 | 96 |
| 108 def testBailOutForTryJobWithOutdatedTimestamp(self): | 97 def testBailOutForTryJobWithOutdatedTimestamp(self): |
| 109 master_name = 'master1' | 98 master_name = 'master1' |
| 110 builder_name = 'builder1' | 99 builder_name = 'builder1' |
| 111 build_number = 223 | 100 build_number = 223 |
| 112 WfAnalysis.Create(master_name, builder_name, build_number).put() | 101 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 113 failure_info = { | 102 failure_info = { |
| 114 'master_name': master_name, | 103 'master_name': master_name, |
| 115 'builder_name': builder_name, | 104 'builder_name': builder_name, |
| 116 'build_number': build_number, | 105 'build_number': build_number, |
| 117 'failed_steps': { | 106 'failed_steps': { |
| 118 'compile': { | 107 'compile': { |
| 119 'current_failure': 221, | 108 'current_failure': 221, |
| 120 'first_failure': 221, | 109 'first_failure': 221, |
| 121 'last_pass': 220 | 110 'last_pass': 220 |
| 122 } | 111 } |
| 123 }, | 112 }, |
| 113 'failure_type': failure_type.COMPILE |
| 124 } | 114 } |
| 125 | 115 |
| 126 yesterday = datetime.utcnow() - timedelta(days=1) | 116 yesterday = datetime.utcnow() - timedelta(days=1) |
| 127 build = WfBuild.Create(master_name, builder_name, build_number) | 117 build = WfBuild.Create(master_name, builder_name, build_number) |
| 128 build.start_time = yesterday | 118 build.start_time = yesterday |
| 129 build.put() | 119 build.put() |
| 130 | 120 |
| 131 self.mock( | |
| 132 try_job_util.swarming_tasks_to_try_job_pipeline, | |
| 133 'SwarmingTasksToTryJobPipeline', _MockRootPipeline) | |
| 134 _MockRootPipeline.STARTED = False | |
| 135 | |
| 136 def _MockShouldBailOutForOutdatedBuild(*_): | 121 def _MockShouldBailOutForOutdatedBuild(*_): |
| 137 return True | 122 return True |
| 138 | 123 |
| 139 self.mock( | 124 self.mock( |
| 140 try_job_util, '_ShouldBailOutForOutdatedBuild', | 125 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 141 _MockShouldBailOutForOutdatedBuild) | 126 _MockShouldBailOutForOutdatedBuild) |
| 142 | 127 |
| 143 failure_result_map = try_job_util.ScheduleTryJobIfNeeded( | 128 need_try_job = try_job_util.NeedANewTryJob( |
| 144 failure_info, None, None, False) | 129 master_name, builder_name, build_number, failure_info, None, None) |
| 145 | 130 |
| 146 self.assertFalse(_MockRootPipeline.STARTED) | 131 self.assertFalse(need_try_job) |
| 147 self.assertEqual({}, failure_result_map) | |
| 148 | |
| 149 def testForceTryJob(self): | |
| 150 master_name = 'm' | |
| 151 builder_name = 'b' | |
| 152 build_number = 223 | |
| 153 WfAnalysis.Create(master_name, builder_name, build_number).put() | |
| 154 failure_info = { | |
| 155 'master_name': master_name, | |
| 156 'builder_name': builder_name, | |
| 157 'build_number': build_number, | |
| 158 'failed_steps': { | |
| 159 'compile': { | |
| 160 'current_failure': 223, | |
| 161 'first_failure': 223, | |
| 162 'last_pass': 222 | |
| 163 } | |
| 164 }, | |
| 165 'builds': { | |
| 166 '222': { | |
| 167 'blame_list': ['222-1'], | |
| 168 'chromium_revision': '222-1' | |
| 169 }, | |
| 170 '223': { | |
| 171 'blame_list': ['223-1', '223-2', '223-3'], | |
| 172 'chromium_revision': '223-3' | |
| 173 } | |
| 174 }, | |
| 175 'failure_type': failure_type.COMPILE | |
| 176 } | |
| 177 | |
| 178 self.mock( | |
| 179 try_job_util.swarming_tasks_to_try_job_pipeline, | |
| 180 'SwarmingTasksToTryJobPipeline', _MockRootPipeline) | |
| 181 _MockRootPipeline.STARTED = False | |
| 182 | |
| 183 try_job_util.ScheduleTryJobIfNeeded(failure_info, None, None, True) | |
| 184 | |
| 185 try_job = WfTryJob.Get(master_name, builder_name, build_number) | |
| 186 | |
| 187 self.assertTrue(_MockRootPipeline.STARTED) | |
| 188 self.assertIsNotNone(try_job) | |
| 189 | 132 |
| 190 def testNotNeedANewTryJobIfNotFirstTimeFailure(self): | 133 def testNotNeedANewTryJobIfNotFirstTimeFailure(self): |
| 191 master_name = 'm' | 134 master_name = 'm' |
| 192 builder_name = 'b' | 135 builder_name = 'b' |
| 193 build_number = 223 | 136 build_number = 223 |
| 194 WfAnalysis.Create(master_name, builder_name, build_number).put() | 137 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 195 failure_info = { | 138 failure_info = { |
| 196 'master_name': master_name, | 139 'master_name': master_name, |
| 197 'builder_name': builder_name, | 140 'builder_name': builder_name, |
| 198 'build_number': build_number, | 141 'build_number': build_number, |
| (...skipping 18 matching lines...) Expand all Loading... |
| 217 'chromium_revision': '222-1' | 160 'chromium_revision': '222-1' |
| 218 }, | 161 }, |
| 219 '223': { | 162 '223': { |
| 220 'blame_list': ['223-1', '223-2', '223-3'], | 163 'blame_list': ['223-1', '223-2', '223-3'], |
| 221 'chromium_revision': '223-3' | 164 'chromium_revision': '223-3' |
| 222 } | 165 } |
| 223 }, | 166 }, |
| 224 'failure_type': failure_type.COMPILE | 167 'failure_type': failure_type.COMPILE |
| 225 } | 168 } |
| 226 | 169 |
| 227 self.mock( | 170 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 228 try_job_util.swarming_tasks_to_try_job_pipeline, | |
| 229 'SwarmingTasksToTryJobPipeline', _MockRootPipeline) | |
| 230 _MockRootPipeline.STARTED = False | |
| 231 | 171 |
| 232 def _MockShouldBailOutForOutdatedBuild(*_): | 172 def _MockShouldBailOutForOutdatedBuild(*_): |
| 233 return False | 173 return False |
| 234 | |
| 235 self.mock( | 174 self.mock( |
| 236 try_job_util, '_ShouldBailOutForOutdatedBuild', | 175 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 237 _MockShouldBailOutForOutdatedBuild) | 176 _MockShouldBailOutForOutdatedBuild) |
| 238 | 177 |
| 239 try_job_util.ScheduleTryJobIfNeeded(failure_info, None, None) | 178 need_try_job = try_job_util.NeedANewTryJob( |
| 179 master_name, builder_name, build_number, failure_info, None, None) |
| 240 | 180 |
| 241 self.assertFalse(_MockRootPipeline.STARTED) | 181 self.assertFalse(need_try_job) |
| 242 | 182 |
| 243 def testBlameListsIntersect(self): | 183 def testBlameListsIntersect(self): |
| 244 self.assertFalse(try_job_util._BlameListsIntersection(['0'], ['1'])) | 184 self.assertFalse(try_job_util._BlameListsIntersection(['0'], ['1'])) |
| 245 self.assertFalse(try_job_util._BlameListsIntersection(['1'], [])) | 185 self.assertFalse(try_job_util._BlameListsIntersection(['1'], [])) |
| 246 self.assertFalse(try_job_util._BlameListsIntersection([], [])) | 186 self.assertFalse(try_job_util._BlameListsIntersection([], [])) |
| 247 self.assertTrue(try_job_util._BlameListsIntersection(['1'], ['1'])) | 187 self.assertTrue(try_job_util._BlameListsIntersection(['1'], ['1'])) |
| 248 self.assertTrue(try_job_util._BlameListsIntersection([ | 188 self.assertTrue(try_job_util._BlameListsIntersection([ |
| 249 '0', '1'], ['1', '2'])) | 189 '0', '1'], ['1', '2'])) |
| 250 self.assertTrue(try_job_util._BlameListsIntersection(['1'], ['1', '2'])) | 190 self.assertTrue(try_job_util._BlameListsIntersection(['1'], ['1', '2'])) |
| 251 | 191 |
| (...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 752 self.assertTrue(try_job_util._IsBuildFailureUniqueAcrossPlatforms( | 692 self.assertTrue(try_job_util._IsBuildFailureUniqueAcrossPlatforms( |
| 753 master_name_2, builder_name, build_number, failure_type.TEST, | 693 master_name_2, builder_name, build_number, failure_type.TEST, |
| 754 blame_list, failed_steps_2, None, None)) | 694 blame_list, failed_steps_2, None, None)) |
| 755 self.assertTrue( | 695 self.assertTrue( |
| 756 WfFailureGroup.Get(master_name_2, builder_name, build_number)) | 696 WfFailureGroup.Get(master_name_2, builder_name, build_number)) |
| 757 | 697 |
| 758 def testNotNeedANewTryJobIfOneWithResultExists(self): | 698 def testNotNeedANewTryJobIfOneWithResultExists(self): |
| 759 master_name = 'm' | 699 master_name = 'm' |
| 760 builder_name = 'b' | 700 builder_name = 'b' |
| 761 build_number = 223 | 701 build_number = 223 |
| 762 WfAnalysis.Create(master_name, builder_name, build_number).put() | 702 failure_info = { |
| 763 builds = { | 703 'failed_steps': { |
| 764 str(build_number): { | 704 'compile': { |
| 765 'blame_list': ['a'] | 705 'current_failure': 223, |
| 766 } | 706 'first_failure': 223, |
| 767 } | 707 'last_pass': 220 |
| 768 failed_steps = { | 708 } |
| 769 'compile': { | 709 }, |
| 770 'current_failure': 223, | 710 'builds': { |
| 771 'first_failure': 223, | 711 '222': { |
| 772 'last_pass': 220 | 712 'blame_list': ['222-1'], |
| 773 } | 713 'chromium_revision': '222-1' |
| 714 }, |
| 715 '223': { |
| 716 'blame_list': ['223-1', '223-2', '223-3'], |
| 717 'chromium_revision': '223-3' |
| 718 } |
| 719 }, |
| 720 'failure_type': failure_type.COMPILE |
| 774 } | 721 } |
| 775 | 722 |
| 776 try_job = WfTryJob.Create(master_name, builder_name, build_number) | 723 try_job = WfTryJob.Create(master_name, builder_name, build_number) |
| 777 try_job.compile_results = [['rev', 'failed']] | 724 try_job.compile_results = [['rev', 'failed']] |
| 778 try_job.status = analysis_status.COMPLETED | 725 try_job.status = analysis_status.COMPLETED |
| 779 try_job.put() | 726 try_job.put() |
| 780 | 727 |
| 781 failure_result_map = {} | 728 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 782 need_try_job, last_pass, try_job_type, targeted_tests = ( | |
| 783 try_job_util._NeedANewTryJob(master_name, builder_name, build_number, | |
| 784 failure_type.COMPILE, failed_steps, | |
| 785 failure_result_map, builds, None, None)) | |
| 786 | 729 |
| 787 expected_failure_result_map = { | 730 def _MockShouldBailOutForOutdatedBuild(*_): |
| 788 'compile': 'm/b/223' | 731 return False |
| 789 } | 732 self.mock( |
| 733 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 734 _MockShouldBailOutForOutdatedBuild) |
| 735 |
| 736 need_try_job = try_job_util.NeedANewTryJob( |
| 737 master_name, builder_name, build_number, failure_info, None, None) |
| 790 | 738 |
| 791 self.assertFalse(need_try_job) | 739 self.assertFalse(need_try_job) |
| 792 self.assertEqual(expected_failure_result_map, failure_result_map) | |
| 793 self.assertEqual(220, last_pass) | |
| 794 self.assertEqual(TryJobType.COMPILE, try_job_type) | |
| 795 self.assertIsNone(targeted_tests) | |
| 796 | 740 |
| 797 def testNeedANewTryJobIfExistingOneHasError(self): | 741 def testNeedANewTryJobIfExistingOneHasError(self): |
| 798 master_name = 'm' | 742 master_name = 'm' |
| 799 builder_name = 'b' | 743 builder_name = 'b' |
| 800 build_number = 223 | 744 build_number = 223 |
| 801 WfAnalysis.Create(master_name, builder_name, build_number).put() | 745 failure_info = { |
| 802 builds = { | 746 'failed_steps': { |
| 803 str(build_number): { | 747 'compile': { |
| 804 'blame_list': ['a'] | 748 'current_failure': 223, |
| 805 } | 749 'first_failure': 223, |
| 806 } | 750 'last_pass': 220 |
| 807 failed_steps = { | 751 } |
| 808 'compile': { | 752 }, |
| 809 'current_failure': 223, | 753 'builds': { |
| 810 'first_failure': 223, | 754 '222': { |
| 811 'last_pass': 220 | 755 'blame_list': ['222-1'], |
| 812 } | 756 'chromium_revision': '222-1' |
| 757 }, |
| 758 '223': { |
| 759 'blame_list': ['223-1', '223-2', '223-3'], |
| 760 'chromium_revision': '223-3' |
| 761 } |
| 762 }, |
| 763 'failure_type': failure_type.COMPILE |
| 813 } | 764 } |
| 814 | 765 |
| 815 try_job = WfTryJob.Create(master_name, builder_name, build_number) | 766 try_job = WfTryJob.Create(master_name, builder_name, build_number) |
| 816 try_job.status = analysis_status.ERROR | 767 try_job.status = analysis_status.ERROR |
| 817 try_job.put() | 768 try_job.put() |
| 818 | 769 |
| 819 failure_result_map = {} | 770 WfAnalysis.Create(master_name, builder_name, build_number).put() |
| 820 need_try_job, last_pass, try_job_type, targeted_tests = ( | |
| 821 try_job_util._NeedANewTryJob(master_name, builder_name, build_number, | |
| 822 failure_type.COMPILE, failed_steps, | |
| 823 failure_result_map, builds, None, None)) | |
| 824 | 771 |
| 825 expected_failure_result_map = { | 772 def _MockShouldBailOutForOutdatedBuild(*_): |
| 826 'compile': 'm/b/223' | 773 return False |
| 827 } | 774 self.mock( |
| 775 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 776 _MockShouldBailOutForOutdatedBuild) |
| 777 |
| 778 need_try_job = try_job_util.NeedANewTryJob( |
| 779 master_name, builder_name, build_number, failure_info, None, None) |
| 780 |
| 828 self.assertTrue(need_try_job) | 781 self.assertTrue(need_try_job) |
| 829 self.assertEqual(expected_failure_result_map, failure_result_map) | |
| 830 self.assertEqual(220, last_pass) | |
| 831 self.assertEqual(TryJobType.COMPILE, try_job_type) | |
| 832 self.assertIsNone(targeted_tests) | |
| 833 | 782 |
| 834 def testNotNeedANewTryJobIfLastPassCannotDetermine(self): | 783 def testNotNeedANewTryJobIfNoNewFailure(self): |
| 835 master_name = 'm' | 784 master_name = 'm' |
| 836 builder_name = 'b' | 785 builder_name = 'b' |
| 837 build_number = 223 | 786 build_number = 223 |
| 838 WfAnalysis.Create(master_name, builder_name, build_number).put() | 787 failure_info = { |
| 839 builds = { | 788 'failed_steps': { |
| 840 str(build_number): { | 789 'a': { |
| 841 'blame_list': ['a'] | 790 'current_failure': 223, |
| 791 'first_failure': 222, |
| 792 'last_pass': 221, |
| 793 'tests': { |
| 794 'a.t2': { |
| 795 'current_failure': 223, |
| 796 'first_failure': 222, |
| 797 'last_pass': 221 |
| 798 } |
| 799 } |
| 800 } |
| 801 }, |
| 802 'failure_type': failure_type.TEST |
| 803 } |
| 804 |
| 805 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 806 analysis.failure_result_map = { |
| 807 'a': { |
| 808 'a.t2': 'm/b/222' |
| 842 } | 809 } |
| 843 } | 810 } |
| 844 failed_steps = { | 811 analysis.put() |
| 845 'compile': { | |
| 846 'current_failure': 223, | |
| 847 'first_failure': 223 | |
| 848 } | |
| 849 } | |
| 850 | 812 |
| 851 try_job = WfTryJob.Create(master_name, builder_name, build_number) | 813 def _MockShouldBailOutForOutdatedBuild(*_): |
| 852 try_job.status = analysis_status.ERROR | 814 return False |
| 853 try_job.put() | 815 self.mock( |
| 816 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 817 _MockShouldBailOutForOutdatedBuild) |
| 854 | 818 |
| 855 failure_result_map = {} | 819 need_try_job = try_job_util.NeedANewTryJob( |
| 856 need_try_job, last_pass, try_job_type, targeted_tests = ( | 820 master_name, builder_name, build_number, failure_info, None, None) |
| 857 try_job_util._NeedANewTryJob(master_name, builder_name, build_number, | |
| 858 failure_type.COMPILE, failed_steps, | |
| 859 failure_result_map, builds, None, None)) | |
| 860 | 821 |
| 861 self.assertFalse(need_try_job) | 822 self.assertFalse(need_try_job) |
| 862 self.assertEqual({}, failure_result_map) | |
| 863 self.assertIsNone(last_pass) | |
| 864 self.assertEqual(TryJobType.COMPILE, try_job_type) | |
| 865 self.assertIsNone(targeted_tests) | |
| 866 | |
| 867 def testNeedANewTryJobIfTestFailureNonSwarming(self): | |
| 868 master_name = 'm' | |
| 869 builder_name = 'b' | |
| 870 build_number = 223 | |
| 871 WfAnalysis.Create(master_name, builder_name, build_number).put() | |
| 872 builds = { | |
| 873 str(build_number): { | |
| 874 'blame_list': ['a'] | |
| 875 } | |
| 876 } | |
| 877 failed_steps = { | |
| 878 'a': { | |
| 879 'current_failure': 223, | |
| 880 'first_failure': 223, | |
| 881 'last_pass': 222 | |
| 882 }, | |
| 883 'b': { | |
| 884 'current_failure': 223, | |
| 885 'first_failure': 222, | |
| 886 'last_pass': 221 | |
| 887 } | |
| 888 } | |
| 889 | |
| 890 failure_result_map = {} | |
| 891 need_try_job, last_pass, try_job_type, targeted_tests = ( | |
| 892 try_job_util._NeedANewTryJob(master_name, builder_name, build_number, | |
| 893 failure_type.TEST, failed_steps, | |
| 894 failure_result_map, builds, None, None)) | |
| 895 | |
| 896 expected_failure_result_map = { | |
| 897 'a': 'm/b/223', | |
| 898 'b': 'm/b/222' | |
| 899 } | |
| 900 | |
| 901 expected_targeted_tests = { | |
| 902 'a': [] | |
| 903 } | |
| 904 | |
| 905 self.assertTrue(need_try_job) | |
| 906 self.assertEqual(expected_failure_result_map, failure_result_map) | |
| 907 self.assertEqual(222, last_pass) | |
| 908 self.assertEqual('test', try_job_type) | |
| 909 self.assertEqual(expected_targeted_tests, targeted_tests) | |
| 910 | 823 |
| 911 def testNeedANewTryJobIfTestFailureSwarming(self): | 824 def testNeedANewTryJobIfTestFailureSwarming(self): |
| 912 master_name = 'm' | 825 master_name = 'm' |
| 913 builder_name = 'b' | 826 builder_name = 'b' |
| 914 build_number = 223 | 827 build_number = 223 |
| 915 WfAnalysis.Create(master_name, builder_name, build_number).put() | 828 failure_info = { |
| 916 builds = { | 829 'failed_steps': { |
| 917 str(build_number): { | 830 'a': { |
| 918 'blame_list': ['a'] | 831 'current_failure': 223, |
| 919 } | 832 'first_failure': 222, |
| 920 } | 833 'last_pass': 221, |
| 921 failed_steps = { | 834 'tests': { |
| 922 'a': { | 835 'a.PRE_t1': { |
| 923 'current_failure': 223, | 836 'current_failure': 223, |
| 924 'first_failure': 222, | 837 'first_failure': 223, |
| 925 'last_pass': 221, | 838 'last_pass': 221, |
| 926 'tests': { | 839 'base_test_name': 'a.t1' |
| 927 'a.PRE_t1': { | 840 }, |
| 928 'current_failure': 223, | 841 'a.t2': { |
| 929 'first_failure': 223, | 842 'current_failure': 223, |
| 930 'last_pass': 221, | 843 'first_failure': 222, |
| 931 'base_test_name': 'a.t1' | 844 'last_pass': 221 |
| 932 }, | 845 }, |
| 933 'a.t2': { | 846 'a.t3': { |
| 934 'current_failure': 223, | 847 'current_failure': 223, |
| 935 'first_failure': 222, | 848 'first_failure': 223, |
| 936 'last_pass': 221 | 849 'last_pass': 222 |
| 937 }, | 850 } |
| 938 'a.t3': { | 851 } |
| 939 'current_failure': 223, | 852 }, |
| 940 'first_failure': 223, | 853 'b': { |
| 941 'last_pass': 222 | 854 'current_failure': 223, |
| 855 'first_failure': 222, |
| 856 'last_pass': 221, |
| 857 'tests': { |
| 858 'b.t1': { |
| 859 'current_failure': 223, |
| 860 'first_failure': 222, |
| 861 'last_pass': 221 |
| 862 }, |
| 863 'b.t2': { |
| 864 'current_failure': 223, |
| 865 'first_failure': 222, |
| 866 'last_pass': 221 |
| 867 } |
| 942 } | 868 } |
| 943 } | 869 } |
| 944 }, | 870 }, |
| 945 'b': { | 871 'builds': { |
| 946 'current_failure': 223, | 872 '222': { |
| 947 'first_failure': 222, | 873 'blame_list': ['222-1'], |
| 948 'last_pass': 221, | 874 'chromium_revision': '222-1' |
| 949 'tests': { | 875 }, |
| 950 'b.t1': { | 876 '223': { |
| 951 'current_failure': 223, | 877 'blame_list': ['223-1', '223-2', '223-3'], |
| 952 'first_failure': 222, | 878 'chromium_revision': '223-3' |
| 953 'last_pass': 221 | |
| 954 }, | |
| 955 'b.t2': { | |
| 956 'current_failure': 223, | |
| 957 'first_failure': 222, | |
| 958 'last_pass': 221 | |
| 959 } | |
| 960 } | 879 } |
| 961 } | 880 }, |
| 881 'failure_type': failure_type.TEST |
| 962 } | 882 } |
| 963 | 883 |
| 964 failure_result_map = {} | 884 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 965 need_try_job, last_pass, try_job_type, targeted_tests = ( | 885 analysis.failure_result_map = { |
| 966 try_job_util._NeedANewTryJob(master_name, builder_name, build_number, | |
| 967 failure_type.TEST, failed_steps, | |
| 968 failure_result_map, builds, None, None)) | |
| 969 | |
| 970 expected_failure_result_map = { | |
| 971 'a': { | 886 'a': { |
| 972 'a.PRE_t1': 'm/b/223', | 887 'a.PRE_t1': 'm/b/223', |
| 973 'a.t2': 'm/b/222', | 888 'a.t2': 'm/b/222', |
| 974 'a.t3': 'm/b/223' | 889 'a.t3': 'm/b/223' |
| 975 }, | 890 }, |
| 976 'b': { | 891 'b': { |
| 977 'b.t1': 'm/b/222', | 892 'b.t1': 'm/b/222', |
| 978 'b.t2': 'm/b/222' | 893 'b.t2': 'm/b/222' |
| 979 }, | 894 } |
| 980 } | 895 } |
| 896 analysis.put() |
| 981 | 897 |
| 982 expected_targeted_tests = { | 898 def _MockShouldBailOutForOutdatedBuild(*_): |
| 983 'a': ['a.t1', 'a.t3'] | 899 return False |
| 984 } | 900 self.mock( |
| 901 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 902 _MockShouldBailOutForOutdatedBuild) |
| 903 |
| 904 need_try_job = try_job_util.NeedANewTryJob( |
| 905 master_name, builder_name, build_number, failure_info, None, None) |
| 985 | 906 |
| 986 self.assertTrue(need_try_job) | 907 self.assertTrue(need_try_job) |
| 987 self.assertEqual(expected_failure_result_map, failure_result_map) | |
| 988 self.assertEqual(221, last_pass) | |
| 989 self.assertEqual('test', try_job_type) | |
| 990 self.assertEqual(expected_targeted_tests, targeted_tests) | |
| 991 | 908 |
| 992 def testNeedANewTryJob(self): | 909 def testNeedANewTryJob(self): |
| 993 master_name = 'm' | 910 master_name = 'm' |
| 994 builder_name = 'b' | 911 builder_name = 'b' |
| 995 build_number = 223 | 912 build_number = 223 |
| 996 failure_info = { | 913 failure_info = { |
| 997 'master_name': master_name, | 914 'master_name': master_name, |
| 998 'builder_name': builder_name, | 915 'builder_name': builder_name, |
| 999 'build_number': build_number, | 916 'build_number': build_number, |
| 1000 'failed_steps': { | 917 'failed_steps': { |
| 1001 'compile': { | 918 'compile': { |
| 1002 'current_failure': 223, | 919 'current_failure': 223, |
| 1003 'first_failure': 223, | 920 'first_failure': 223, |
| 1004 'last_pass': 222 | 921 'last_pass': 222 |
| 1005 } | 922 } |
| 1006 }, | 923 }, |
| 1007 'builds': { | 924 'builds': { |
| 1008 '222': { | 925 '222': { |
| 1009 'blame_list': ['222-1'], | 926 'blame_list': ['222-1'], |
| 1010 'chromium_revision': '222-1' | 927 'chromium_revision': '222-1' |
| 1011 }, | 928 }, |
| 1012 '223': { | 929 '223': { |
| 1013 'blame_list': ['223-1', '223-2', '223-3'], | 930 'blame_list': ['223-1', '223-2', '223-3'], |
| 1014 'chromium_revision': '223-3' | 931 'chromium_revision': '223-3' |
| 1015 } | 932 } |
| 1016 }, | 933 }, |
| 1017 'failure_type': failure_type.COMPILE | 934 'failure_type': failure_type.COMPILE |
| 1018 } | 935 } |
| 1019 | 936 |
| 1020 self.mock( | 937 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 1021 try_job_util.swarming_tasks_to_try_job_pipeline, | 938 analysis.failure_result_map = { |
| 1022 'SwarmingTasksToTryJobPipeline', _MockRootPipeline) | 939 'compile': 'm/b/223' |
| 1023 _MockRootPipeline.STARTED = False | 940 } |
| 941 analysis.put() |
| 1024 | 942 |
| 1025 def _MockShouldBailOutForOutdatedBuild(*_): | 943 def _MockShouldBailOutForOutdatedBuild(*_): |
| 1026 return False | 944 return False |
| 1027 | |
| 1028 self.mock( | 945 self.mock( |
| 1029 try_job_util, '_ShouldBailOutForOutdatedBuild', | 946 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 1030 _MockShouldBailOutForOutdatedBuild) | 947 _MockShouldBailOutForOutdatedBuild) |
| 1031 | 948 |
| 1032 try_job_util.ScheduleTryJobIfNeeded(failure_info, None, None) | 949 need_try_job = try_job_util.NeedANewTryJob( |
| 950 master_name, builder_name, build_number, failure_info, None, None) |
| 1033 | 951 |
| 1034 try_job = WfTryJob.Get(master_name, builder_name, build_number) | 952 self.assertTrue(need_try_job) |
| 1035 | 953 |
| 1036 self.assertTrue(_MockRootPipeline.STARTED) | 954 def testNotNeedANewTryJobForOtherType(self): |
| 1037 self.assertIsNotNone(try_job) | 955 master_name = 'm' |
| 956 builder_name = 'b' |
| 957 build_number = 223 |
| 958 failure_info = { |
| 959 'master_name': master_name, |
| 960 'builder_name': builder_name, |
| 961 'build_number': build_number, |
| 962 'failed_steps': {}, |
| 963 'builds': { |
| 964 '222': { |
| 965 'blame_list': ['222-1'], |
| 966 'chromium_revision': '222-1' |
| 967 }, |
| 968 '223': { |
| 969 'blame_list': ['223-1', '223-2', '223-3'], |
| 970 'chromium_revision': '223-3' |
| 971 } |
| 972 }, |
| 973 'failure_type': failure_type.UNKNOWN |
| 974 } |
| 1038 | 975 |
| 1039 def testUseFailedOutputNodesFromSignals(self): | 976 def _MockShouldBailOutForOutdatedBuild(*_): |
| 1040 signals = { | 977 return False |
| 1041 'compile': { | 978 self.mock( |
| 1042 'failed_targets': [ | 979 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 1043 {'target': 'a.exe'}, | 980 _MockShouldBailOutForOutdatedBuild) |
| 1044 {'source': 'b.cc', 'target': 'b.o'}, | 981 |
| 1045 ], | 982 need_try_job = try_job_util.NeedANewTryJob( |
| 1046 'failed_output_nodes': ['a', 'b'], | 983 master_name, builder_name, build_number, failure_info, None, None) |
| 984 |
| 985 self.assertFalse(need_try_job) |
| 986 |
| 987 def testNotNeedANewTryJobForCompileTypeNoFailureInfo(self): |
| 988 master_name = 'm' |
| 989 builder_name = 'b' |
| 990 build_number = 223 |
| 991 failure_info = { |
| 992 'master_name': master_name, |
| 993 'builder_name': builder_name, |
| 994 'build_number': build_number, |
| 995 'failed_steps': {}, |
| 996 'builds': { |
| 997 '222': { |
| 998 'blame_list': ['222-1'], |
| 999 'chromium_revision': '222-1' |
| 1000 }, |
| 1001 '223': { |
| 1002 'blame_list': ['223-1', '223-2', '223-3'], |
| 1003 'chromium_revision': '223-3' |
| 1004 } |
| 1005 }, |
| 1006 'failure_type': failure_type.COMPILE |
| 1007 } |
| 1008 |
| 1009 def _MockShouldBailOutForOutdatedBuild(*_): |
| 1010 return False |
| 1011 self.mock( |
| 1012 try_job_util, '_ShouldBailOutForOutdatedBuild', |
| 1013 _MockShouldBailOutForOutdatedBuild) |
| 1014 |
| 1015 need_try_job = try_job_util.NeedANewTryJob( |
| 1016 master_name, builder_name, build_number, failure_info, None, None) |
| 1017 |
| 1018 self.assertFalse(need_try_job) |
| 1019 |
| 1020 def testForceTryJob(self): |
| 1021 master_name = 'm' |
| 1022 builder_name = 'b' |
| 1023 build_number = 223 |
| 1024 failure_info = { |
| 1025 'failed_steps': { |
| 1026 'a': { |
| 1027 'current_failure': 223, |
| 1028 'first_failure': 223, |
| 1029 'last_pass': 222, |
| 1030 'tests': { |
| 1031 'a.t2': { |
| 1032 'current_failure': 223, |
| 1033 'first_failure': 223, |
| 1034 'last_pass': 222 |
| 1035 } |
| 1036 } |
| 1037 } |
| 1038 }, |
| 1039 'builds': { |
| 1040 '222': { |
| 1041 'blame_list': ['222-1'], |
| 1042 'chromium_revision': '222-1' |
| 1043 }, |
| 1044 '223': { |
| 1045 'blame_list': ['223-1', '223-2', '223-3'], |
| 1046 'chromium_revision': '223-3' |
| 1047 } |
| 1048 }, |
| 1049 'failure_type': failure_type.TEST |
| 1050 } |
| 1051 |
| 1052 try_job = WfTryJob.Create(master_name, builder_name, build_number) |
| 1053 try_job.compile_results = [['rev', 'failed']] |
| 1054 try_job.status = analysis_status.COMPLETED |
| 1055 try_job.put() |
| 1056 |
| 1057 analysis = WfAnalysis.Create(master_name, builder_name, build_number) |
| 1058 analysis.failure_result_map = { |
| 1059 'a': { |
| 1060 'a.t2': 'm/b/223' |
| 1047 } | 1061 } |
| 1048 } | 1062 } |
| 1063 analysis.put() |
| 1049 | 1064 |
| 1050 self.assertEqual( | 1065 need_try_job = try_job_util.NeedANewTryJob( |
| 1051 try_job_util._GetFailedTargetsFromSignals(signals, 'm', 'b'), | 1066 master_name, builder_name, build_number, failure_info, None, None, True) |
| 1052 ['a', 'b']) | |
| 1053 | 1067 |
| 1054 def testGetFailedTargetsFromSignals(self): | 1068 self.assertTrue(need_try_job) |
| 1055 self.assertEqual( | |
| 1056 try_job_util._GetFailedTargetsFromSignals({}, 'm', 'b'), []) | |
| 1057 | |
| 1058 self.assertEqual( | |
| 1059 try_job_util._GetFailedTargetsFromSignals({'compile': {}}, 'm', 'b'), | |
| 1060 []) | |
| 1061 | |
| 1062 signals = { | |
| 1063 'compile': { | |
| 1064 'failed_targets': [ | |
| 1065 {'target': 'a.exe'}, | |
| 1066 {'source': 'b.cc', | |
| 1067 'target': 'b.o'}] | |
| 1068 } | |
| 1069 } | |
| 1070 | |
| 1071 self.assertEqual( | |
| 1072 try_job_util._GetFailedTargetsFromSignals(signals, 'm', 'b'), ['a.exe']) | |
| 1073 | |
| 1074 def testUseObjectFilesAsFailedTargetIfStrictRegexUsed(self): | |
| 1075 signals = { | |
| 1076 'compile': { | |
| 1077 'failed_targets': [ | |
| 1078 {'source': 'b.cc', 'target': 'b.o'}, | |
| 1079 ] | |
| 1080 } | |
| 1081 } | |
| 1082 | |
| 1083 self.assertEqual( | |
| 1084 try_job_util._GetFailedTargetsFromSignals( | |
| 1085 signals, 'master1', 'builder1'), | |
| 1086 ['b.o']) | |
| 1087 | 1069 |
| 1088 def testGenPotentialCulpritTupleListNoHeuristicResult(self): | 1070 def testGenPotentialCulpritTupleListNoHeuristicResult(self): |
| 1089 heuristic_result = None | 1071 heuristic_result = None |
| 1090 expected_suspected_revisions = [] | 1072 expected_suspected_revisions = [] |
| 1091 self.assertEqual( | 1073 self.assertEqual( |
| 1092 expected_suspected_revisions, | 1074 expected_suspected_revisions, |
| 1093 sorted(try_job_util.GenPotentialCulpritTupleList(heuristic_result))) | 1075 sorted(try_job_util.GenPotentialCulpritTupleList(heuristic_result))) |
| 1094 | 1076 |
| 1095 def testGenPotentialCulpritTupleListEmptyHeuristicResult(self): | 1077 def testGenPotentialCulpritTupleListEmptyHeuristicResult(self): |
| 1096 heuristic_result = {} | 1078 heuristic_result = {} |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1152 ('step2', 'r1', None), | 1134 ('step2', 'r1', None), |
| 1153 ('step2', 'r2', None), | 1135 ('step2', 'r2', None), |
| 1154 ('step3', 'abc', 'super_test_1'), | 1136 ('step3', 'abc', 'super_test_1'), |
| 1155 ('step3', 'def', 'super_test_2'), | 1137 ('step3', 'def', 'super_test_2'), |
| 1156 ('step3', 'ghi', 'super_test_2') | 1138 ('step3', 'ghi', 'super_test_2') |
| 1157 ] | 1139 ] |
| 1158 self.assertEqual( | 1140 self.assertEqual( |
| 1159 expected_suspected_revisions, | 1141 expected_suspected_revisions, |
| 1160 sorted(try_job_util.GenPotentialCulpritTupleList(heuristic_result))) | 1142 sorted(try_job_util.GenPotentialCulpritTupleList(heuristic_result))) |
| 1161 | 1143 |
| 1162 def testGetSuspectsFromHeuristicResultForCompile(self): | 1144 def testUseFailedOutputNodesFromSignals(self): |
| 1163 heuristic_result = { | 1145 signals = { |
| 1164 'failures': [ | 1146 'compile': { |
| 1165 { | 1147 'failed_targets': [ |
| 1166 'step_name': 'compile', | 1148 {'target': 'a.exe'}, |
| 1167 'suspected_cls': [ | 1149 {'source': 'b.cc', 'target': 'b.o'}, |
| 1168 { | 1150 ], |
| 1169 'revision': 'r1', | 1151 'failed_output_nodes': ['a', 'b'], |
| 1170 }, | 1152 } |
| 1171 { | |
| 1172 'revision': 'r2', | |
| 1173 }, | |
| 1174 ], | |
| 1175 }, | |
| 1176 ] | |
| 1177 } | 1153 } |
| 1178 expected_suspected_revisions = ['r1', 'r2'] | 1154 |
| 1179 self.assertEqual( | 1155 self.assertEqual( |
| 1180 expected_suspected_revisions, | 1156 try_job_util.GetFailedTargetsFromSignals(signals, 'm', 'b'), |
| 1181 try_job_util._GetSuspectsFromHeuristicResult(heuristic_result)) | 1157 ['a', 'b']) |
| 1182 | 1158 |
| 1183 def testGetSuspectsFromHeuristicResultForTest(self): | 1159 def testGetFailedTargetsFromSignals(self): |
| 1184 heuristic_result = { | 1160 self.assertEqual( |
| 1185 'failures': [ | 1161 try_job_util.GetFailedTargetsFromSignals({}, 'm', 'b'), []) |
| 1186 { | 1162 |
| 1187 'step_name': 'step1', | 1163 self.assertEqual( |
| 1188 'suspected_cls': [ | 1164 try_job_util.GetFailedTargetsFromSignals({'compile': {}}, 'm', 'b'), |
| 1189 { | 1165 []) |
| 1190 'revision': 'r1', | 1166 |
| 1191 }, | 1167 signals = { |
| 1192 { | 1168 'compile': { |
| 1193 'revision': 'r2', | 1169 'failed_targets': [ |
| 1194 }, | 1170 {'target': 'a.exe'}, |
| 1195 ], | 1171 {'source': 'b.cc', |
| 1196 }, | 1172 'target': 'b.o'}] |
| 1197 { | 1173 } |
| 1198 'step_name': 'step2', | |
| 1199 'suspected_cls': [ | |
| 1200 { | |
| 1201 'revision': 'r1', | |
| 1202 }, | |
| 1203 { | |
| 1204 'revision': 'r3', | |
| 1205 }, | |
| 1206 ], | |
| 1207 }, | |
| 1208 ] | |
| 1209 } | 1174 } |
| 1210 expected_suspected_revisions = ['r1', 'r2', 'r3'] | 1175 |
| 1211 self.assertEqual( | 1176 self.assertEqual( |
| 1212 expected_suspected_revisions, | 1177 try_job_util.GetFailedTargetsFromSignals(signals, 'm', 'b'), ['a.exe']) |
| 1213 try_job_util._GetSuspectsFromHeuristicResult(heuristic_result)) | |
| 1214 | 1178 |
| 1215 def testShouldBailOutforOutdatedBuild(self): | 1179 def testUseObjectFilesAsFailedTargetIfStrictRegexUsed(self): |
| 1216 yesterday = datetime.utcnow() - timedelta(days=1) | 1180 signals = { |
| 1217 build = WfBuild.Create('m', 'b', 1) | 1181 'compile': { |
| 1218 build.start_time = yesterday | 1182 'failed_targets': [ |
| 1219 self.assertTrue(try_job_util._ShouldBailOutForOutdatedBuild(build)) | 1183 {'source': 'b.cc', 'target': 'b.o'}, |
| 1184 ] |
| 1185 } |
| 1186 } |
| 1220 | 1187 |
| 1221 build.start_time = yesterday + timedelta(hours=1) | 1188 self.assertEqual( |
| 1222 self.assertFalse(try_job_util._ShouldBailOutForOutdatedBuild(build)) | 1189 try_job_util.GetFailedTargetsFromSignals( |
| 1190 signals, 'master1', 'builder1'), |
| 1191 ['b.o']) |
| OLD | NEW |