jamesren | dbeebf8 | 2010-04-08 22:58:26 +0000 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | import unittest |
| 4 | import common |
| 5 | from autotest_lib.frontend import setup_django_environment |
| 6 | from autotest_lib.client.common_lib.test_utils import mock |
| 7 | from autotest_lib.frontend.afe import frontend_test_utils, models as afe_models |
| 8 | from autotest_lib.frontend.afe import model_attributes as afe_model_attributes |
| 9 | from autotest_lib.frontend.shared import rest_client |
| 10 | from autotest_lib.frontend.planner import models, execution_engine, support |
| 11 | from autotest_lib.frontend.planner import model_attributes |
| 12 | |
| 13 | |
| 14 | class MockObject(object): |
| 15 | """ |
| 16 | Empty mock object class, so that setattr() works on all names |
| 17 | """ |
| 18 | pass |
| 19 | |
| 20 | |
| 21 | class MockAfeRest(object): |
| 22 | jobs = MockObject() |
| 23 | execution_info = MockObject() |
| 24 | queue_entries_request = MockObject() |
| 25 | |
| 26 | |
| 27 | class MockRestJobs(object): |
| 28 | def __init__(self, total_results): |
| 29 | self.total_results = total_results |
| 30 | |
| 31 | |
| 32 | class MockExecutionInfo(object): |
| 33 | execution_info = {} |
| 34 | |
| 35 | |
| 36 | class MockQueueEntriesRequest(object): |
| 37 | queue_entries = object() |
| 38 | |
| 39 | |
| 40 | class MockExecutionEngine(execution_engine.ExecutionEngine): |
| 41 | _planner_rpc = MockObject() |
| 42 | _tko_rpc = object() |
| 43 | _plan_id = object() |
| 44 | _server = object() |
| 45 | _afe_rest = MockAfeRest() |
| 46 | _label_name = object() |
| 47 | |
| 48 | |
| 49 | def __init__(self, *args, **kwargs): |
| 50 | pass |
| 51 | |
| 52 | |
| 53 | class MockTestPlanController(support.TestPlanController): |
| 54 | def __init__(self, *args, **kwargs): |
| 55 | super(MockTestPlanController, self).__init__(machine=None, |
| 56 | test_alias=None) |
| 57 | |
| 58 | |
| 59 | class ExecutionEngineTest(unittest.TestCase, |
| 60 | frontend_test_utils.FrontendTestMixin): |
| 61 | def setUp(self): |
| 62 | self._frontend_common_setup() |
| 63 | self.engine = MockExecutionEngine() |
| 64 | |
| 65 | |
| 66 | def tearDown(self): |
| 67 | self._frontend_common_teardown() |
| 68 | |
| 69 | |
| 70 | def _setup_test_initialize_plan(self): |
| 71 | self.god.stub_function(self.engine._planner_rpc, 'run') |
| 72 | self.god.stub_function(self.engine._afe_rest.jobs, 'get') |
| 73 | self.god.stub_function(self.engine, '_wait_for_initialization') |
| 74 | |
| 75 | |
| 76 | def test_initialize_plan_new_plan(self): |
| 77 | self._setup_test_initialize_plan() |
| 78 | self.god.stub_function(self.engine, '_launch_set_atomic_group_job') |
| 79 | |
| 80 | self.engine._planner_rpc.run.expect_call( |
| 81 | 'get_plan', id=self.engine._plan_id).and_return( |
| 82 | {'name': 'plan'}) |
| 83 | self.engine._afe_rest.jobs.get.expect_call( |
| 84 | name='plan_set_atomic_group').and_return(MockRestJobs(None)) |
| 85 | self.engine._launch_set_atomic_group_job.expect_call( |
| 86 | 'plan_set_atomic_group') |
| 87 | self.engine._wait_for_initialization.expect_call() |
| 88 | |
| 89 | self.engine._initialize_plan() |
| 90 | self.god.check_playback |
| 91 | |
| 92 | |
| 93 | def test_initialize_plan_existing(self): |
| 94 | self._setup_test_initialize_plan() |
| 95 | |
| 96 | self.engine._planner_rpc.run.expect_call( |
| 97 | 'get_plan', id=self.engine._plan_id).and_return( |
| 98 | {'name': 'plan'}) |
| 99 | self.engine._afe_rest.jobs.get.expect_call( |
| 100 | name='plan_set_atomic_group').and_return(MockRestJobs(object())) |
| 101 | self.engine._wait_for_initialization.expect_call() |
| 102 | |
| 103 | self.engine._initialize_plan() |
| 104 | self.god.check_playback |
| 105 | |
| 106 | |
| 107 | def _setup_test_launch_atomic_group_job(self, name): |
| 108 | DUMMY_CONTROL = object() |
| 109 | DUMMY_EXECUTION_INFO = MockExecutionInfo() |
| 110 | DUMMY_QUEUE_ENTRIES_REQUEST = MockQueueEntriesRequest() |
| 111 | |
| 112 | self.god.stub_function(self.engine._planner_rpc, 'run') |
| 113 | self.god.stub_function(self.engine._afe_rest.execution_info, 'get') |
| 114 | self.god.stub_function( |
| 115 | self.engine._afe_rest.queue_entries_request, 'get') |
| 116 | |
| 117 | self.engine._planner_rpc.run.expect_call( |
| 118 | 'get_hosts', plan_id=self.engine._plan_id).and_return( |
| 119 | self.hosts) |
| 120 | self.engine._planner_rpc.run.expect_call( |
| 121 | 'get_atomic_group_control_file').and_return(DUMMY_CONTROL) |
| 122 | self.engine._afe_rest.execution_info.get.expect_call().and_return( |
| 123 | DUMMY_EXECUTION_INFO) |
| 124 | self.engine._afe_rest.queue_entries_request.get.expect_call( |
| 125 | hosts=self.hosts).and_return(DUMMY_QUEUE_ENTRIES_REQUEST) |
| 126 | |
| 127 | DUMMY_EXECUTION_INFO.execution_info = { |
| 128 | 'control_file': DUMMY_CONTROL, |
| 129 | 'cleanup_before_job': afe_model_attributes.RebootBefore.NEVER, |
| 130 | 'cleanup_after_job': afe_model_attributes.RebootAfter.NEVER, |
| 131 | 'run_verify': False, |
| 132 | 'machines_per_execution': len(self.hosts)} |
| 133 | |
| 134 | keyvals = {'server': self.engine._server, |
| 135 | 'label_name': self.engine._label_name, |
| 136 | 'plan_id': self.engine._plan_id} |
| 137 | |
| 138 | job_req = {'name': name, |
| 139 | 'execution_info': DUMMY_EXECUTION_INFO.execution_info, |
| 140 | 'queue_entries': DUMMY_QUEUE_ENTRIES_REQUEST.queue_entries, |
| 141 | 'keyvals': keyvals} |
| 142 | |
| 143 | return job_req |
| 144 | |
| 145 | |
| 146 | def test_launch_atomic_group_job(self): |
| 147 | job_req = self._setup_test_launch_atomic_group_job('atomic_group_job') |
| 148 | self.god.stub_function(self.engine._afe_rest.jobs, 'post') |
| 149 | |
| 150 | self.engine._afe_rest.jobs.post.expect_call(job_req) |
| 151 | |
| 152 | self.engine._launch_set_atomic_group_job('atomic_group_job') |
| 153 | self.god.check_playback() |
| 154 | |
| 155 | |
| 156 | def _setup_mock_controller(self, controller_options): |
| 157 | mock_controller = MockTestPlanController() |
| 158 | for key, value in controller_options.iteritems(): |
| 159 | setattr(mock_controller, key, value) |
| 160 | self.god.stub_with(support, 'TestPlanController', |
| 161 | lambda *args, **kwargs : mock_controller) |
| 162 | return mock_controller |
| 163 | |
| 164 | |
| 165 | def _test_process_finished_runs_helper(self, status, should_block=False, |
| 166 | controller_options={}): |
| 167 | Status = model_attributes.TestRunStatus |
| 168 | TEST_RUN_ID = object() |
| 169 | TKO_TEST_ID = object() |
| 170 | HOST_ID = object() |
| 171 | |
| 172 | mock_controller = self._setup_mock_controller(controller_options) |
| 173 | |
| 174 | self.god.stub_function(self.engine._planner_rpc, 'run') |
| 175 | self.god.stub_function(self.engine, '_run_execute_after') |
| 176 | |
| 177 | test_run = {'id': TEST_RUN_ID, |
| 178 | 'host': {'host': self.hosts[0].hostname, |
| 179 | 'id': HOST_ID}, |
| 180 | 'test_job': {'test_config': {'alias': 'test_alias'}}, |
| 181 | 'tko_test': TKO_TEST_ID, |
| 182 | 'status': status} |
| 183 | |
| 184 | self.engine._planner_rpc.run.expect_call( |
| 185 | 'get_test_runs', |
| 186 | plan__id=self.engine._plan_id, |
| 187 | status__in=(Status.PASSED, Status.FAILED), |
| 188 | finalized=False).and_return([test_run]) |
| 189 | self.engine._run_execute_after.expect_call( |
| 190 | mock_controller, tko_test_id=TKO_TEST_ID, |
| 191 | success=(status == Status.PASSED)) |
| 192 | if should_block: |
| 193 | self.engine._planner_rpc.run.expect_call('modify_host', id=HOST_ID, |
| 194 | blocked=True) |
| 195 | self.engine._planner_rpc.run.expect_call('modify_test_run', |
| 196 | id=TEST_RUN_ID, finalized=True) |
| 197 | |
| 198 | self.engine._process_finished_runs() |
| 199 | |
| 200 | self.god.check_playback() |
| 201 | |
| 202 | |
| 203 | def test_process_finished_runs_pass(self): |
| 204 | self._test_process_finished_runs_helper( |
| 205 | model_attributes.TestRunStatus.PASSED) |
| 206 | |
| 207 | |
| 208 | def test_process_finished_runs_fail(self): |
| 209 | self._test_process_finished_runs_helper( |
| 210 | model_attributes.TestRunStatus.FAILED, should_block=True) |
| 211 | |
| 212 | |
| 213 | def test_process_finished_runs_fail_unblock(self): |
| 214 | self._test_process_finished_runs_helper( |
| 215 | model_attributes.TestRunStatus.FAILED, should_block=False, |
| 216 | controller_options={'_unblock': True}) |
| 217 | |
| 218 | |
| 219 | def _test_schedule_new_runs_helper(self, complete=False, should_skip=False, |
| 220 | controller_options={}): |
| 221 | TEST_CONFIG_ID = object() |
| 222 | |
| 223 | self.god.stub_function(self.engine._planner_rpc, 'run') |
| 224 | self.god.stub_function(self.engine, '_run_execute_before') |
| 225 | |
| 226 | result = {'complete': complete, |
| 227 | 'next_configs': [{'next_test_config_id': TEST_CONFIG_ID, |
| 228 | 'host': self.hosts[0].hostname, |
| 229 | 'next_test_config_alias': object()}]} |
| 230 | |
| 231 | mock_controller = self._setup_mock_controller(controller_options) |
| 232 | |
| 233 | self.engine._planner_rpc.run.expect_call( |
| 234 | 'get_next_test_configs', |
| 235 | plan_id=self.engine._plan_id).and_return(result) |
| 236 | |
| 237 | if not complete: |
| 238 | self.engine._run_execute_before.expect_call(mock_controller) |
| 239 | |
| 240 | if should_skip: |
| 241 | self.engine._planner_rpc.run.expect_call( |
| 242 | 'skip_test', test_config_id=TEST_CONFIG_ID, |
| 243 | hostname=self.hosts[0].hostname) |
| 244 | else: |
| 245 | self.god.stub_function(self.engine, '_run_job') |
| 246 | self.engine._run_job.expect_call( |
| 247 | hostname=self.hosts[0].hostname, |
| 248 | test_config_id=TEST_CONFIG_ID, |
| 249 | cleanup_before_job=mock_controller._reboot_before, |
| 250 | cleanup_after_job=mock_controller._reboot_after, |
| 251 | run_verify=mock_controller._run_verify) |
| 252 | |
| 253 | self.engine._schedule_new_runs() |
| 254 | |
| 255 | self.god.check_playback() |
| 256 | |
| 257 | |
| 258 | def test_schedule_new_runs(self): |
| 259 | self._test_schedule_new_runs_helper() |
| 260 | |
| 261 | |
| 262 | def test_schedule_new_runs_complete(self): |
| 263 | self._test_schedule_new_runs_helper(complete=True) |
| 264 | |
| 265 | |
| 266 | def test_schedule_new_runs_skip(self): |
| 267 | self._test_schedule_new_runs_helper(should_skip=True, |
| 268 | controller_options={'_skip': True}) |
| 269 | |
| 270 | |
| 271 | def test_run_global_support(self): |
| 272 | self._ran_global_support = False |
| 273 | support = """ |
| 274 | def test_global_support(controller): |
| 275 | controller._ran_global_support = True |
| 276 | """ |
| 277 | |
| 278 | DUMMY_PLAN = {'support': support} |
| 279 | |
| 280 | self.god.stub_function(self.engine._planner_rpc, 'run') |
| 281 | |
| 282 | self.engine._planner_rpc.run.expect_call( |
| 283 | 'get_plan', id=self.engine._plan_id).and_return(DUMMY_PLAN) |
| 284 | |
| 285 | self.engine._run_global_support(controller=self, |
| 286 | function_name='test_global_support') |
| 287 | |
| 288 | self.assertTrue(self._ran_global_support) |
| 289 | self.god.check_playback() |
| 290 | |
| 291 | |
| 292 | if __name__ == '__main__': |
| 293 | unittest.main() |