| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 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 common import appengine_util | 5 from common import appengine_util |
| 6 from common import constants | 6 from common import constants |
| 7 from common.pipeline_wrapper import BasePipeline | 7 from common.pipeline_wrapper import BasePipeline |
| 8 from common import time_util |
| 8 | 9 |
| 9 from model import analysis_status | 10 from model import analysis_status |
| 10 from model.flake.flake_swarming_task import FlakeSwarmingTask | 11 from model.flake.flake_swarming_task import FlakeSwarmingTask |
| 11 from model.flake.master_flake_analysis import MasterFlakeAnalysis | 12 from model.flake.master_flake_analysis import MasterFlakeAnalysis |
| 12 from waterfall import waterfall_config | 13 from waterfall import waterfall_config |
| 13 from waterfall.process_flake_swarming_task_result_pipeline import ( | 14 from waterfall.process_flake_swarming_task_result_pipeline import ( |
| 14 ProcessFlakeSwarmingTaskResultPipeline) | 15 ProcessFlakeSwarmingTaskResultPipeline) |
| 15 from waterfall.trigger_flake_swarming_task_pipeline import ( | 16 from waterfall.trigger_flake_swarming_task_pipeline import ( |
| 16 TriggerFlakeSwarmingTaskPipeline) | 17 TriggerFlakeSwarmingTaskPipeline) |
| 17 | 18 |
| 19 def _UpdateAnalysisStatusUponCompletion(master_flake_analysis, status, error): |
| 20 master_flake_analysis.completed_time = time_util.GetUTCNow() |
| 21 master_flake_analysis.status = status |
| 22 |
| 23 if error: |
| 24 master_flake_analysis.error = error |
| 25 |
| 26 master_flake_analysis.put() |
| 18 | 27 |
| 19 class RecursiveFlakePipeline(BasePipeline): | 28 class RecursiveFlakePipeline(BasePipeline): |
| 20 | 29 |
| 21 # Arguments number differs from overridden method - pylint: disable=W0221 | 30 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 22 def run(self, master_name, builder_name, run_build_number, step_name, | 31 def run(self, master_name, builder_name, run_build_number, step_name, |
| 23 test_name, master_build_number, flakiness_algorithm_results_dict, | 32 test_name, master_build_number, flakiness_algorithm_results_dict, |
| 24 queue_name=constants.DEFAULT_QUEUE): | 33 queue_name=constants.DEFAULT_QUEUE): |
| 25 """Pipeline to determine the regression range of a flaky test. | 34 """Pipeline to determine the regression range of a flaky test. |
| 26 | 35 |
| 27 Args: | 36 Args: |
| 28 master_name (str): The master name. | 37 master_name (str): The master name. |
| 29 builder_name (str): The builder name. | 38 builder_name (str): The builder name. |
| 30 run_build_number (int): The build number of the current swarming rerun. | 39 run_build_number (int): The build number of the current swarming rerun. |
| 31 step_name (str): The step name. | 40 step_name (str): The step name. |
| 32 test_name (str): The test name. | 41 test_name (str): The test name. |
| 33 master_build_number (int): The build number of the Master_Flake_analysis. | 42 master_build_number (int): The build number of the Master_Flake_analysis. |
| 34 flakiness_algorithm_results_dict (dict): A dictionary used by | 43 flakiness_algorithm_results_dict (dict): A dictionary used by |
| 35 NextBuildNumberPipeline | 44 NextBuildNumberPipeline |
| 36 queue_name (str): Which queue to run on. | 45 queue_name (str): Which queue to run on. |
| 37 | 46 |
| 38 Returns: | 47 Returns: |
| 39 A dict of lists for reliable/flaky tests. | 48 A dict of lists for reliable/flaky tests. |
| 40 """ | 49 """ |
| 41 master = MasterFlakeAnalysis.Get(master_name, builder_name, | 50 master_flake_analysis = MasterFlakeAnalysis.Get( |
| 42 master_build_number, step_name, test_name) | 51 master_name, builder_name, master_build_number, step_name, test_name) |
| 43 if master.status != analysis_status.RUNNING: # pragma: no branch | 52 if (master_flake_analysis.status != |
| 44 master.status = analysis_status.RUNNING | 53 analysis_status.RUNNING): # pragma: no branch |
| 45 master.put() | 54 master_flake_analysis.status = analysis_status.RUNNING |
| 55 master_flake_analysis.put() |
| 46 | 56 |
| 47 # Call trigger pipeline (flake style). | 57 # Call trigger pipeline (flake style). |
| 48 task_id = yield TriggerFlakeSwarmingTaskPipeline( | 58 task_id = yield TriggerFlakeSwarmingTaskPipeline( |
| 49 master_name, builder_name, run_build_number, step_name, [test_name]) | 59 master_name, builder_name, run_build_number, step_name, [test_name]) |
| 50 # Pass the trigger pipeline into a process pipeline. | 60 # Pass the trigger pipeline into a process pipeline. |
| 51 test_result_future = yield ProcessFlakeSwarmingTaskResultPipeline( | 61 test_result_future = yield ProcessFlakeSwarmingTaskResultPipeline( |
| 52 master_name, builder_name, run_build_number, | 62 master_name, builder_name, run_build_number, |
| 53 step_name, task_id, master_build_number, test_name) | 63 step_name, task_id, master_build_number, test_name) |
| 54 yield NextBuildNumberPipeline( | 64 yield NextBuildNumberPipeline( |
| 55 master_name, builder_name, master_build_number, run_build_number, | 65 master_name, builder_name, master_build_number, run_build_number, |
| 56 step_name, test_name, test_result_future, queue_name, | 66 step_name, test_name, test_result_future, queue_name, |
| 57 flakiness_algorithm_results_dict) | 67 flakiness_algorithm_results_dict) |
| 58 | 68 |
| 59 | 69 |
| 60 def get_next_run(master, flakiness_algorithm_results_dict): | 70 def get_next_run(master_flake_analysis, flakiness_algorithm_results_dict): |
| 61 # A description of this algorithm can be found at: | 71 # A description of this algorithm can be found at: |
| 62 # https://docs.google.com/document/d/1wPYFZ5OT998Yn7O8wGDOhgfcQ98mknoX13AesJaS
6ig/edit | 72 # https://docs.google.com/document/d/1wPYFZ5OT998Yn7O8wGDOhgfcQ98mknoX13AesJaS
6ig/edit |
| 63 # Get the last result. | 73 # Get the last result. |
| 64 last_result = master.success_rates[-1] | 74 last_result = master_flake_analysis.pass_rates[-1] |
| 65 cur_run = min(master.build_numbers) | 75 cur_run = min(master_flake_analysis.build_numbers) |
| 66 flake_settings = waterfall_config.GetCheckFlakeSettings() | 76 flake_settings = waterfall_config.GetCheckFlakeSettings() |
| 67 lower_flake_threshold = flake_settings.get('lower_flake_threshold') | 77 lower_flake_threshold = flake_settings.get('lower_flake_threshold') |
| 68 upper_flake_threshold = flake_settings.get('upper_flake_threshold') | 78 upper_flake_threshold = flake_settings.get('upper_flake_threshold') |
| 69 max_stable_in_a_row = flake_settings.get('max_stable_in_a_row') | 79 max_stable_in_a_row = flake_settings.get('max_stable_in_a_row') |
| 70 max_flake_in_a_row = flake_settings.get('max_flake_in_a_row') | 80 max_flake_in_a_row = flake_settings.get('max_flake_in_a_row') |
| 71 | 81 |
| 72 if last_result < 0: # Test doesn't exist in the current build number. | 82 if last_result < 0: # Test doesn't exist in the current build number. |
| 73 flakiness_algorithm_results_dict['stable_in_a_row'] += 1 | 83 flakiness_algorithm_results_dict['stable_in_a_row'] += 1 |
| 74 flakiness_algorithm_results_dict['stabled_out'] = True | 84 flakiness_algorithm_results_dict['stabled_out'] = True |
| 75 flakiness_algorithm_results_dict['flaked_out'] = True | 85 flakiness_algorithm_results_dict['flaked_out'] = True |
| 76 flakiness_algorithm_results_dict['lower_boundary_result'] = 'STABLE' | 86 flakiness_algorithm_results_dict['lower_boundary_result'] = 'STABLE' |
| 77 | 87 |
| 78 lower_boundary = master.build_numbers[ | 88 lower_boundary = master_flake_analysis.build_numbers[ |
| 79 -flakiness_algorithm_results_dict['stable_in_a_row']] | 89 -flakiness_algorithm_results_dict['stable_in_a_row']] |
| 80 | 90 |
| 81 flakiness_algorithm_results_dict['lower_boundary'] = lower_boundary | 91 flakiness_algorithm_results_dict['lower_boundary'] = lower_boundary |
| 82 flakiness_algorithm_results_dict['sequential_run_index'] += 1 | 92 flakiness_algorithm_results_dict['sequential_run_index'] += 1 |
| 83 return lower_boundary + 1 | 93 return lower_boundary + 1 |
| 84 elif (last_result < lower_flake_threshold or | 94 elif (last_result < lower_flake_threshold or |
| 85 last_result > upper_flake_threshold): # Stable result. | 95 last_result > upper_flake_threshold): # Stable result. |
| 86 flakiness_algorithm_results_dict['stable_in_a_row'] += 1 | 96 flakiness_algorithm_results_dict['stable_in_a_row'] += 1 |
| 87 if (flakiness_algorithm_results_dict['stable_in_a_row'] > | 97 if (flakiness_algorithm_results_dict['stable_in_a_row'] > |
| 88 max_stable_in_a_row): # Identified a stable region. | 98 max_stable_in_a_row): # Identified a stable region. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 not flakiness_algorithm_results_dict['lower_boundary']): | 130 not flakiness_algorithm_results_dict['lower_boundary']): |
| 121 # Identified a candidate for the lower boundary. | 131 # Identified a candidate for the lower boundary. |
| 122 # Latest flaky point to the left of a stable region. | 132 # Latest flaky point to the left of a stable region. |
| 123 flakiness_algorithm_results_dict['lower_boundary'] = cur_run | 133 flakiness_algorithm_results_dict['lower_boundary'] = cur_run |
| 124 flakiness_algorithm_results_dict['lower_boundary_result'] = 'FLAKE' | 134 flakiness_algorithm_results_dict['lower_boundary_result'] = 'FLAKE' |
| 125 flakiness_algorithm_results_dict['stable_in_a_row'] = 0 | 135 flakiness_algorithm_results_dict['stable_in_a_row'] = 0 |
| 126 step_size = flakiness_algorithm_results_dict['flakes_in_a_row'] + 1 | 136 step_size = flakiness_algorithm_results_dict['flakes_in_a_row'] + 1 |
| 127 return cur_run - step_size | 137 return cur_run - step_size |
| 128 | 138 |
| 129 | 139 |
| 130 def sequential_next_run(master, flakiness_algorithm_results_dict): | 140 def sequential_next_run( |
| 131 last_result = master.success_rates[-1] | 141 master_flake_analysis, flakiness_algorithm_results_dict): |
| 142 last_result = master_flake_analysis.pass_rates[-1] |
| 132 last_result_status = 'FLAKE' | 143 last_result_status = 'FLAKE' |
| 133 flake_settings = waterfall_config.GetCheckFlakeSettings() | 144 flake_settings = waterfall_config.GetCheckFlakeSettings() |
| 134 lower_flake_threshold = flake_settings.get('lower_flake_threshold') | 145 lower_flake_threshold = flake_settings.get('lower_flake_threshold') |
| 135 upper_flake_threshold = flake_settings.get('upper_flake_threshold') | 146 upper_flake_threshold = flake_settings.get('upper_flake_threshold') |
| 136 | 147 |
| 137 if (last_result < lower_flake_threshold or | 148 if (last_result < lower_flake_threshold or |
| 138 last_result > upper_flake_threshold): | 149 last_result > upper_flake_threshold): |
| 139 last_result_status = 'STABLE' | 150 last_result_status = 'STABLE' |
| 140 if flakiness_algorithm_results_dict['sequential_run_index'] > 0: | 151 if flakiness_algorithm_results_dict['sequential_run_index'] > 0: |
| 141 if (last_result_status != | 152 if (last_result_status != |
| 142 flakiness_algorithm_results_dict['lower_boundary_result']): | 153 flakiness_algorithm_results_dict['lower_boundary_result']): |
| 143 master.suspected_flake_build_number = ( | 154 master_flake_analysis.suspected_flake_build_number = ( |
| 144 flakiness_algorithm_results_dict['lower_boundary'] + | 155 flakiness_algorithm_results_dict['lower_boundary'] + |
| 145 flakiness_algorithm_results_dict['sequential_run_index']) | 156 flakiness_algorithm_results_dict['sequential_run_index']) |
| 146 master.put() | 157 master_flake_analysis.put() |
| 147 return 0 | 158 return 0 |
| 148 flakiness_algorithm_results_dict['sequential_run_index'] += 1 | 159 flakiness_algorithm_results_dict['sequential_run_index'] += 1 |
| 149 return (flakiness_algorithm_results_dict['lower_boundary'] + | 160 return (flakiness_algorithm_results_dict['lower_boundary'] + |
| 150 flakiness_algorithm_results_dict['sequential_run_index']) | 161 flakiness_algorithm_results_dict['sequential_run_index']) |
| 151 | 162 |
| 152 | 163 |
| 153 class NextBuildNumberPipeline(BasePipeline): | 164 class NextBuildNumberPipeline(BasePipeline): |
| 154 | 165 |
| 155 # Arguments number differs from overridden method - pylint: disable=W0221 | 166 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 156 # Unused argument - pylint: disable=W0613 | 167 # Unused argument - pylint: disable=W0613 |
| 157 def run(self, master_name, builder_name, master_build_number, | 168 def run(self, master_name, builder_name, master_build_number, |
| 158 run_build_number, step_name, test_name, test_result_future, | 169 run_build_number, step_name, test_name, test_result_future, |
| 159 queue_name, flakiness_algorithm_results_dict): | 170 queue_name, flakiness_algorithm_results_dict): |
| 160 | 171 |
| 161 # Get MasterFlakeAnalysis success list corresponding to parameters. | 172 # Get MasterFlakeAnalysis success list corresponding to parameters. |
| 162 master = MasterFlakeAnalysis.Get(master_name, builder_name, | 173 master_flake_analysis = MasterFlakeAnalysis.Get( |
| 163 master_build_number, step_name, test_name) | 174 master_name, builder_name, master_build_number, step_name, test_name) |
| 164 # Don't call another pipeline if we fail. | 175 # Don't call another pipeline if we fail. |
| 165 flake_swarming_task = FlakeSwarmingTask.Get( | 176 flake_swarming_task = FlakeSwarmingTask.Get( |
| 166 master_name, builder_name, run_build_number, step_name, test_name) | 177 master_name, builder_name, run_build_number, step_name, test_name) |
| 167 | 178 |
| 168 if flake_swarming_task.status == analysis_status.ERROR: | 179 if flake_swarming_task.status == analysis_status.ERROR: |
| 169 master.status = analysis_status.ERROR | 180 # TODO (lijeffrey): Implement more detailed error detection and reporting, |
| 170 master.put() | 181 # such as timeouts, dead bots, etc. |
| 182 error = { |
| 183 'error': 'An error occurred', |
| 184 'message': 'Unknown' |
| 185 } |
| 186 _UpdateAnalysisStatusUponCompletion( |
| 187 master_flake_analysis, analysis_status.ERROR, error) |
| 171 return | 188 return |
| 172 | 189 |
| 173 # Figure out what build_number we should call, if any | 190 # Figure out what build_number we should call, if any |
| 174 if (flakiness_algorithm_results_dict['stabled_out'] and | 191 if (flakiness_algorithm_results_dict['stabled_out'] and |
| 175 flakiness_algorithm_results_dict['flaked_out']): | 192 flakiness_algorithm_results_dict['flaked_out']): |
| 176 next_run = sequential_next_run(master, flakiness_algorithm_results_dict) | 193 next_run = sequential_next_run( |
| 194 master_flake_analysis, flakiness_algorithm_results_dict) |
| 177 else: | 195 else: |
| 178 next_run = get_next_run(master, flakiness_algorithm_results_dict) | 196 next_run = get_next_run( |
| 197 master_flake_analysis, flakiness_algorithm_results_dict) |
| 179 | 198 |
| 180 if next_run < flakiness_algorithm_results_dict['last_build_number']: | 199 if next_run < flakiness_algorithm_results_dict['last_build_number']: |
| 181 next_run = 0 | 200 next_run = 0 |
| 182 elif next_run >= master_build_number: | 201 elif next_run >= master_build_number: |
| 183 next_run = 0 | 202 next_run = 0 |
| 184 | 203 |
| 185 if next_run: | 204 if next_run: |
| 186 pipeline_job = RecursiveFlakePipeline( | 205 pipeline_job = RecursiveFlakePipeline( |
| 187 master_name, builder_name, next_run, step_name, test_name, | 206 master_name, builder_name, next_run, step_name, test_name, |
| 188 master_build_number, | 207 master_build_number, |
| 189 flakiness_algorithm_results_dict=flakiness_algorithm_results_dict) | 208 flakiness_algorithm_results_dict=flakiness_algorithm_results_dict) |
| 190 # pylint: disable=W0201 | 209 # pylint: disable=W0201 |
| 191 pipeline_job.target = appengine_util.GetTargetNameForModule( | 210 pipeline_job.target = appengine_util.GetTargetNameForModule( |
| 192 constants.WATERFALL_BACKEND) | 211 constants.WATERFALL_BACKEND) |
| 193 pipeline_job.start(queue_name=queue_name) | 212 pipeline_job.start(queue_name=queue_name) |
| 194 else: | 213 else: |
| 195 master.status = analysis_status.COMPLETED | 214 _UpdateAnalysisStatusUponCompletion( |
| 196 master.put() | 215 master_flake_analysis, analysis_status.COMPLETED, None) |
| OLD | NEW |