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