def testKickTestPlan_withError(self, create_test_run, set_test_run_state): """Tests that errors are handled when kicking off a test plan.""" test = ndb_models.Test(id='test_id') test_run_config = ndb_models.TestRunConfig( test_key=test.key, cluster='cluster', run_target='run_target') test_plan = ndb_models.TestPlan( name='test_plan', test_run_configs=[test_run_config, test_run_config]) test_plan.put() # First test run created successfully, but second fails even with retries test_run = ndb_models.TestRun(id='test_run_id') create_test_run.side_effect = ( [test_run] + test_plan_kicker.MAX_RETRY_COUNT * [RuntimeError('test_run_error')]) # Retries a few times before canceling test run and raising exception with self.assertRaises(RuntimeError): test_plan_kicker.KickTestPlan(test_plan.key.id()) self.assertEqual(create_test_run.call_count, 1 + test_plan_kicker.MAX_RETRY_COUNT) set_test_run_state.assert_called_once_with('test_run_id', ndb_models.TestRunState.CANCELED) # Stores the canceled test run key and the error message status = ndb_models.TestPlanStatus.query(ancestor=test_plan.key).get() self.assertEqual(status.last_run_keys, [test_run.key]) self.assertEqual(status.last_run_error, 'test_run_error')
def _CreateMockTestPlan(name, cron_exp='0 * * * *', test_run_configs=None): """Creates a mock test plan.""" test_plan = ndb_models.TestPlan(name=name, cron_exp=cron_exp, test_run_configs=test_run_configs or []) test_plan.put() return test_plan
def testScheduleCronKick_noCronExpression(self): """Tests that test plan without cron expressions do not get scheduled.""" test_plan = ndb_models.TestPlan(name='test_plan') test_plan.put() test_plan_kicker.ScheduleCronKick(test_plan.key.id()) # No tasks scheduled tasks = self.mock_task_scheduler.GetTasks( queue_names=[test_plan_kicker.TEST_PLAN_KICKER_QUEUE]) self.assertEmpty(tasks)
def testScheduleCronKick_invalidTimezone(self): """Tests that test plan with invalid timezones do not get scheduled.""" test_plan = ndb_models.TestPlan( name='test_plan', cron_exp='0 0 * * *', cron_exp_timezone='INVALID') test_plan.put() test_plan_kicker.ScheduleCronKick(test_plan.key.id()) # No tasks scheduled tasks = self.mock_task_scheduler.GetTasks( queue_names=[test_plan_kicker.TEST_PLAN_KICKER_QUEUE]) self.assertEmpty(tasks)
def testKickTestPlan_invalidTask(self): """Tests that a test plan kick is skipped if the task is invalid.""" test_plan = ndb_models.TestPlan(name='test_plan') test_plan.put() ndb_models.TestPlanStatus( parent=test_plan.key, next_run_task_name='task_name').put() executed = test_plan_kicker.KickTestPlan( test_plan.key.id(), task_name='invalid_task_name') self.assertFalse(executed)
def _CreateMockTestPlan(self, configs, device_action_keys=None, test_resource_pipes=None): """Creates a mock ndb_models.TestPlan object.""" test_plan = ndb_models.TestPlan( test_run_configs=configs, name='name', before_device_action_keys=device_action_keys, test_resource_pipes=test_resource_pipes) test_plan.put() return test_plan
def testCheckTestPlanNextRuns(self, get_current_time, schedule_cron_kick): now = datetime.datetime(1970, 1, 1, 0, 30, 0) get_current_time.return_value = now test_plan = ndb_models.TestPlan(name='foo', cron_exp='0 0 * * *') test_plan.put() test_plan_status = ndb_models.TestPlanStatus( parent=test_plan.key, next_run_time=datetime.datetime(1970, 1, 1, 0, 0, 0)) test_plan_status.put() test_plan_kicker.CheckTestPlanNextRuns() schedule_cron_kick.assert_called_with(test_plan.key.id(), next_run_time=now)
def testKickTestPlan(self, create_test_run): """Tests that a test plan can be kicked off.""" test = ndb_models.Test(id='test_id') test_device_action = ndb_models.DeviceAction(id='test_device_action') test_run_action = ndb_models.TestRunAction(id='test_run_action') # Create a test plan with multiple resources and actions test_plan = ndb_models.TestPlan( name='test_plan', labels=['label'], cron_exp='0 0 * * *', test_run_configs=[ ndb_models.TestRunConfig( test_key=test.key, cluster='cluster', run_target='run_target', before_device_action_keys=[test_device_action.key], test_run_action_refs=[ ndb_models.TestRunActionRef(action_key=test_run_action.key), ], test_resource_objs=[ ndb_models.TestResourceObj(name='res_1', url='url_1')]), ]) test_plan.put() # Test run will be created successfully test_run = ndb_models.TestRun(id='test_run_id') create_test_run.return_value = test_run executed = test_plan_kicker.KickTestPlan(test_plan.key.id()) self.assertTrue(executed) # Test run is created with the right test and test plan components create_test_run.assert_called_with( labels=['label'], test_plan_key=test_plan.key, test_run_config=ndb_models.TestRunConfig( test_key=test.key, cluster='cluster', run_target='run_target', before_device_action_keys=[test_device_action.key], test_run_action_refs=[ ndb_models.TestRunActionRef(action_key=test_run_action.key), ], test_resource_objs=[ ndb_models.TestResourceObj(name='res_1', url='url_1') ]), ) # Test run key is stored in the test plan status status = ndb_models.TestPlanStatus.query(ancestor=test_plan.key).get() self.assertEqual(status.last_run_keys, [test_run.key])
def Get(self, request): """Fetches a test plan. Parameters: test_plan_id: Test plan ID, or zero for an empty test plan """ if request.test_plan_id == '0': # For ID 0, return an empty test plan object to use as a template in UI. test_plan = ndb_models.TestPlan(id=0, name='') else: test_plan = mtt_messages.ConvertToKey(ndb_models.TestPlan, request.test_plan_id).get() if not test_plan: raise endpoints.NotFoundException('No test plan with ID %s' % request.test_plan_id) return mtt_messages.Convert(test_plan, mtt_messages.TestPlan)
def _TestPlanMessageConverter(msg): return ndb_models.TestPlan( key=ConvertToKeyOrNone(ndb_models.TestPlan, getattr(msg, 'id')), name=msg.name, labels=msg.labels, cron_exp=msg.cron_exp, cron_exp_timezone=msg.cron_exp_timezone, test_run_configs=ConvertList( msg.test_run_configs, ndb_models.TestRunConfig), test_resource_pipes=ConvertList( msg.test_resource_pipes, ndb_models.TestResourcePipe), before_device_action_keys=[ ConvertToKeyOrNone(ndb_models.DeviceAction, device_action_id) for device_action_id in msg.before_device_action_ids ], test_run_action_refs=ConvertList( msg.test_run_action_refs, ndb_models.TestRunActionRef))
def testScheduleCronKick_withTimezone(self, get_current_time): """Tests that test plans can be scheduled relative to a timezone.""" # Create test plan that runs every day at midnight in PT test_plan = ndb_models.TestPlan( name='test_plan', cron_exp='0 0 * * *', cron_exp_timezone='America/Los_Angeles') test_plan.put() # Current time is 12:30 AM UTC and next run time is midnight PT (8 AM UTC) get_current_time.return_value = datetime.datetime(1970, 1, 1, 0, 30, 0) next_run_time = pytz.UTC.localize(datetime.datetime(1970, 1, 1, 8, 0, 0)) test_plan_kicker.ScheduleCronKick(test_plan.key.id()) tasks = self.mock_task_scheduler.GetTasks( queue_names=[test_plan_kicker.TEST_PLAN_KICKER_QUEUE]) self.assertLen(tasks, 1) self.assertEqual(next_run_time, tasks[0].eta)
def testScheduleCronKick(self, get_current_time): """Tests that test plans can be scheduled.""" # Create test plan that runs every day at midnight test_plan = ndb_models.TestPlan(name='test_plan', cron_exp='0 0 * * *') test_plan.put() test_plan_id = test_plan.key.id() # Current time is 12:30 AM UTC and next run time is the following midnight get_current_time.return_value = datetime.datetime(1970, 1, 1, 0, 30, 0) next_run_time = pytz.UTC.localize(datetime.datetime(1970, 1, 2, 0, 0, 0)) test_plan_kicker.ScheduleCronKick(test_plan_id) tasks = self.mock_task_scheduler.GetTasks( queue_names=[test_plan_kicker.TEST_PLAN_KICKER_QUEUE]) self.assertLen(tasks, 1) self.assertEqual(next_run_time, tasks[0].eta) data = json.loads(tasks[0].payload) self.assertEqual(test_plan_id, data['test_plan_id'])
def setUp(self): super(TfcEventHandlerTest, self).setUp() self.mock_test = ndb_models.Test(name='test', command='command', result_file='result_file') self.mock_test.put() self.mock_test_plan = ndb_models.TestPlan(name='plan') self.mock_test_plan.put() self.mock_test_run = ndb_models.TestRun( test_plan_key=self.mock_test_plan.key, test_run_config=ndb_models.TestRunConfig( test_key=self.mock_test.key, run_target='run_target', command='mock test run command', retry_command='mock test run retry command'), test=self.mock_test, request_id='request_id', state=ndb_models.TestRunState.UNKNOWN) self.mock_test_run.put()
def testKickTestPlan_withRetry(self, create_test_run): """Tests that a test plan kick operations can be retried.""" test = ndb_models.Test(id='test_id') test_run_config = ndb_models.TestRunConfig( test_key=test.key, cluster='cluster', run_target='run_target') test_plan = ndb_models.TestPlan( name='test_plan', test_run_configs=[test_run_config]) test_plan.put() # First test run creation fails, but second attempt succeeds test_run = ndb_models.TestRun(id='test_run_id') create_test_run.side_effect = [RuntimeError('test_run_error'), test_run] executed = test_plan_kicker.KickTestPlan(test_plan.key.id()) self.assertTrue(executed) # Create test run operation retried until successful self.assertEqual(create_test_run.call_count, 2) status = ndb_models.TestPlanStatus.query(ancestor=test_plan.key).get() # Test run key stored and error message is empty self.assertEqual(status.last_run_keys, [test_run.key]) self.assertIsNone(status.last_run_error)
def testKickTestPlan_notFound(self): """Tests that a test plan kick is skipped if the plan is not found.""" test_plan = ndb_models.TestPlan(id='unknown') executed = test_plan_kicker.KickTestPlan(test_plan.key.id()) self.assertFalse(executed)