class CheckBoxSessionStateControllerTests(TestCase): def setUp(self): self.ctrl = CheckBoxSessionStateController() def test_get_dependency_set(self): # Job with no dependencies job_a = JobDefinition({}) self.assertEqual(self.ctrl.get_dependency_set(job_a), set()) # Job with direct dependencies job_b = JobDefinition({'depends': 'j1, j2'}) self.assertEqual(self.ctrl.get_dependency_set(job_b), {('direct', 'j1'), ('direct', 'j2')}) # Job with resouce dependencies job_c = JobDefinition({'requires': 'j3.attr == 1'}) self.assertEqual(self.ctrl.get_dependency_set(job_c), {('resource', 'j3')}) # Job with both direct and resource dependencies job_d = JobDefinition({'depends': 'j4', 'requires': 'j5.attr == 1'}) self.assertEqual(self.ctrl.get_dependency_set(job_d), {('direct', 'j4'), ('resource', 'j5')}) # Job with both direct and resource dependencies # on the same job (j6) job_e = JobDefinition({'depends': 'j6', 'requires': 'j6.attr == 1'}) self.assertEqual(self.ctrl.get_dependency_set(job_e), {('direct', 'j6'), ('resource', 'j6')}) def test_get_inhibitor_list_PENDING_RESOURCE(self): # verify that jobs that require a resource that hasn't been # invoked yet produce the PENDING_RESOURCE inhibitor j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map['j2'].job = j2 session_state.resource_map = {} self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(JobReadinessInhibitor.PENDING_RESOURCE, j2, ResourceExpression('j2.attr == "ok"')) ]) def test_get_inhibitor_list_FAILED_RESOURCE(self): # verify that jobs that require a resource that has been # invoked and produced resources but the expression dones't # evaluate to True produce the FAILED_RESOURCE inhibitor j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map['j2'].job = j2 session_state.resource_map = {'j2': [Resource({'attr': 'not-ok'})]} self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(JobReadinessInhibitor.FAILED_RESOURCE, j2, ResourceExpression('j2.attr == "ok"')) ]) def test_get_inhibitor_list_good_resource(self): # verify that jobs that require a resource that has been invoked and # produced resources for which the expression evaluates to True don't # have any inhibitors j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) session_state.resource_map = {'j2': [Resource({'attr': 'ok'})]} session_state.job_state_map['j2'].job = j2 self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), []) def test_get_inhibitor_list_PENDING_DEP(self): # verify that jobs that depend on another job that hasn't # been invoked yet produce the PENDING_DEP inhibitor j1 = JobDefinition({'id': 'j1', 'depends': 'j2'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_NONE self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(JobReadinessInhibitor.PENDING_DEP, j2, None) ]) def test_get_inhibitor_list_FAILED_DEP(self): # verify that jobs that depend on another job that ran but # didn't result in OUTCOME_PASS produce the FAILED_DEP # inhibitor. j1 = JobDefinition({'id': 'j1', 'depends': 'j2'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_FAIL self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(JobReadinessInhibitor.FAILED_DEP, j2, None) ]) def test_get_inhibitor_list_good_dep(self): # verify that jobs that depend on another job that ran and has outcome # equal to OUTCOME_PASS don't have any inhibitors j1 = JobDefinition({'id': 'j1', 'depends': 'j2'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_PASS self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), []) def test_observe_result__normal(self): job = mock.Mock(spec=JobDefinition) result = mock.Mock(spec=IJobResult) session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once() session_state.on_job_result_changed.assert_called_once_with( job, result) def test_observe_result__OUTCOME_NONE(self): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_NONE) session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a resource was *not* defined self.assertEqual(session_state.set_resource_list.call_count, 0) def test_observe_result__resource(self): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [(0, 'stdout', b'attr: value1\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'attr: value2\n')] session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that new resource was defined session_state.set_resource_list.assert_called_once_with( job.id, [Resource({'attr': 'value1'}), Resource({'attr': 'value2'})]) @mock.patch('plainbox.impl.ctrl.logger') def test_observe_result__broken_resource(self, mock_logger): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [(0, 'stdout', b'barf\n')] session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a warning was logged mock_logger.warning.assert_called_once_with( "local script %s returned invalid RFC822 data: %s", job, RFC822SyntaxError(None, 1, "Unexpected non-empty line: 'barf\\n'")) @mock.patch('plainbox.impl.ctrl.gen_rfc822_records_from_io_log') def test_observe_result__local_typical(self, mock_gen): """ verify side effects of using observe_result() that would define a new job """ # Create a session that knows about no jobs yet # and happily adds jobs when add_job() gets called session_state = mock.MagicMock(spec=SessionState) session_state.add_job.side_effect = ( lambda new_job, recompute: new_job) # Create a job of which result we'll be observing job = mock.Mock(spec=JobDefinition, name='job', plugin='local') # Create a result for the job we'll be observing result = mock.Mock(spec=IJobResult, name='result', outcome=IJobResult.OUTCOME_PASS) # Mock what rfc822 parser returns mock_gen.return_value = [mock.Mock(spec=RFC822Record, name='record')] # Pretend that we are observing a 'result' of 'job' self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that new job was defined session_state.add_job.assert_called_once_with( job.create_child_job_from_record(), recompute=False) # Ensure that we didn't try to change the origin of the new job self.assertFalse( job.create_child_job_from_record().update_origin.called) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) @mock.patch('plainbox.impl.ctrl.gen_rfc822_records_from_io_log') @mock.patch('plainbox.impl.ctrl.logger') def test_observe_result__local_imperfect_clash(self, mock_logger, mock_gen): """ verify side effects of using observe_result() that would define a already existing job with the non-identical definition. We basically hope to see the old job being there intact and a warning to be logged. """ # Create a session that already knows about 'existing_job' # and raises a DependencyDuplicateError when add_job() gets called. existing_job = mock.Mock(spec=JobDefinition, name='existing_job') existing_job.id = 'generated' clashing_job = mock.Mock(spec=JobDefinition, name='existing_job') clashing_job.id = 'generated' session_state = mock.MagicMock(spec=SessionState, name='session_state') session_state.add_job.side_effect = DependencyDuplicateError( existing_job, clashing_job) # Create a job of which result we'll be observing job = mock.Mock(spec=JobDefinition, name='job', plugin='local') # Have job return clashing_job when create_child_job_record() is called job.create_child_job_from_record.return_value = clashing_job # Create a result for the job we'll be observing result = mock.Mock(spec=IJobResult, name='result', outcome=IJobResult.OUTCOME_PASS) # Mock what rfc822 parser returns mock_gen.return_value = [mock.Mock(spec=RFC822Record, name='record')] # Pretend that we are observing a 'result' of 'job' self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that we tried to define a new job by calling add_job() with # the clashing_job as argument session_state.add_job.assert_called_once_with(clashing_job, recompute=False) # Ensure that existing job origin was *not* updated self.assertFalse(existing_job.update_origin.called) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a warning was logged mock_logger.warning.assert_called_once_with( ("Local job %s produced job %r that collides with" " an existing job %s (from %s), the new job was" " discarded"), job, clashing_job, existing_job, existing_job.origin) @mock.patch('plainbox.impl.ctrl.gen_rfc822_records_from_io_log') def test_observe_result__local_perfect_clash(self, mock_gen): """ verify side effects of using observe_result() that would define a already existing job with the exactly identical definition. We basically hope to see the old job being there but the origin field should be updated to reflect the new association between 'existing_job' and 'job' """ # Create a session that already knows about 'existing_job' # and returns existing_job when add_job() gets called. existing_job = mock.Mock(spec=JobDefinition, name='existing_job') existing_job.id = 'generated' session_state = mock.MagicMock(spec=SessionState, name='session_state') session_state.add_job.side_effect = ( lambda new_job, recompute: existing_job) # Create a job of which result we'll be observing job = mock.Mock(spec=JobDefinition, name='job', plugin='local') # Create a result for the job we'll be observing result = mock.Mock(spec=IJobResult, name='result', outcome=IJobResult.OUTCOME_PASS) # Mock what rfc822 parser returns mock_gen.return_value = [mock.Mock(spec=RFC822Record, name='record')] # Pretend that we are observing a 'result' of 'job' self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that we tried to define a new job using # whatever create_child_job_from_record() returns. session_state.add_job.assert_called_once_with( job.create_child_job_from_record(), recompute=False) # Ensure that the origin of the existing_job was copied # from the origin of the generated job existing_job.update_origin.assert_called_once_with( job.create_child_job_from_record().origin) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result)
class CheckBoxSessionStateControllerTests(TestCase): def setUp(self): self.ctrl = CheckBoxSessionStateController() def test_get_dependency_set(self): # Job with no dependencies job_a = JobDefinition({}) self.assertEqual( self.ctrl.get_dependency_set(job_a), set()) # Job with direct dependencies job_b = JobDefinition({ 'depends': 'j1, j2' }) self.assertEqual( self.ctrl.get_dependency_set(job_b), {('direct', 'j1'), ('direct', 'j2')}) # Job with resouce dependencies job_c = JobDefinition({ 'requires': 'j3.attr == 1' }) self.assertEqual( self.ctrl.get_dependency_set(job_c), {('resource', 'j3')}) # Job with both direct and resource dependencies job_d = JobDefinition({ 'depends': 'j4', 'requires': 'j5.attr == 1' }) self.assertEqual( self.ctrl.get_dependency_set(job_d), {('direct', 'j4'), ('resource', 'j5')}) # Job with both direct and resource dependencies # on the same job (j6) job_e = JobDefinition({ 'depends': 'j6', 'requires': 'j6.attr == 1' }) self.assertEqual( self.ctrl.get_dependency_set(job_e), {('direct', 'j6'), ('resource', 'j6')}) def test_get_inhibitor_list_PENDING_RESOURCE(self): # verify that jobs that require a resource that hasn't been # invoked yet produce the PENDING_RESOURCE inhibitor j1 = JobDefinition({ 'id': 'j1', 'requires': 'j2.attr == "ok"' }) j2 = JobDefinition({ 'id': 'j2' }) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map['j2'].job = j2 session_state.resource_map = {} self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), [JobReadinessInhibitor( JobReadinessInhibitor.PENDING_RESOURCE, j2, ResourceExpression('j2.attr == "ok"'))]) def test_get_inhibitor_list_FAILED_RESOURCE(self): # verify that jobs that require a resource that has been # invoked and produced resources but the expression dones't # evaluate to True produce the FAILED_RESOURCE inhibitor j1 = JobDefinition({ 'id': 'j1', 'requires': 'j2.attr == "ok"' }) j2 = JobDefinition({ 'id': 'j2' }) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map['j2'].job = j2 session_state.resource_map = { 'j2': [Resource({'attr': 'not-ok'})] } self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), [JobReadinessInhibitor( JobReadinessInhibitor.FAILED_RESOURCE, j2, ResourceExpression('j2.attr == "ok"'))]) def test_get_inhibitor_list_good_resource(self): # verify that jobs that require a resource that has been invoked and # produced resources for which the expression evaluates to True don't # have any inhibitors j1 = JobDefinition({ 'id': 'j1', 'requires': 'j2.attr == "ok"' }) j2 = JobDefinition({ 'id': 'j2' }) session_state = mock.MagicMock(spec=SessionState) session_state.resource_map = { 'j2': [Resource({'attr': 'ok'})] } session_state.job_state_map['j2'].job = j2 self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), []) def test_get_inhibitor_list_PENDING_DEP(self): # verify that jobs that depend on another job that hasn't # been invoked yet produce the PENDING_DEP inhibitor j1 = JobDefinition({ 'id': 'j1', 'depends': 'j2' }) j2 = JobDefinition({ 'id': 'j2' }) session_state = mock.MagicMock(spec=SessionState) jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_NONE self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), [JobReadinessInhibitor( JobReadinessInhibitor.PENDING_DEP, j2, None)]) def test_get_inhibitor_list_FAILED_DEP(self): # verify that jobs that depend on another job that ran but # didn't result in OUTCOME_PASS produce the FAILED_DEP # inhibitor. j1 = JobDefinition({ 'id': 'j1', 'depends': 'j2' }) j2 = JobDefinition({ 'id': 'j2' }) session_state = mock.MagicMock(spec=SessionState) jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_FAIL self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), [JobReadinessInhibitor( JobReadinessInhibitor.FAILED_DEP, j2, None)]) def test_get_inhibitor_list_good_dep(self): # verify that jobs that depend on another job that ran and has outcome # equal to OUTCOME_PASS don't have any inhibitors j1 = JobDefinition({ 'id': 'j1', 'depends': 'j2' }) j2 = JobDefinition({ 'id': 'j2' }) session_state = mock.MagicMock(spec=SessionState) jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_PASS self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), []) def test_observe_result__normal(self): job = mock.Mock(spec=JobDefinition) result = mock.Mock(spec=IJobResult) session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once() session_state.on_job_result_changed.assert_called_once_with( job, result) def test_observe_result__OUTCOME_NONE(self): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_NONE) session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a resource was *not* defined self.assertEqual(session_state.set_resource_list.call_count, 0) def test_observe_result__resource(self): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [ (0, 'stdout', b'attr: value1\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'attr: value2\n')] session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that new resource was defined session_state.set_resource_list.assert_called_once_with( job.id, [ Resource({'attr': 'value1'}), Resource({'attr': 'value2'})]) @mock.patch('plainbox.impl.ctrl.logger') def test_observe_result__broken_resource(self, mock_logger): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [(0, 'stdout', b'barf\n')] session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a warning was logged mock_logger.warning.assert_called_once_with( "local script %s returned invalid RFC822 data: %s", job, RFC822SyntaxError( None, 1, "Unexpected non-empty line: 'barf\\n'")) @mock.patch('plainbox.impl.ctrl.gen_rfc822_records_from_io_log') def test_observe_result__local_typical(self, mock_gen): """ verify side effects of using observe_result() that would define a new job """ # Create a session that knows about no jobs yet # and happily adds jobs when add_job() gets called session_state = mock.MagicMock(spec=SessionState) session_state.add_job.side_effect = ( lambda new_job, recompute: new_job) # Create a job of which result we'll be observing job = mock.Mock(spec=JobDefinition, name='job', plugin='local') # Create a result for the job we'll be observing result = mock.Mock( spec=IJobResult, name='result', outcome=IJobResult.OUTCOME_PASS) # Mock what rfc822 parser returns mock_gen.return_value = [mock.Mock(spec=RFC822Record, name='record')] # Pretend that we are observing a 'result' of 'job' self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that new job was defined session_state.add_job.assert_called_once_with( job.create_child_job_from_record(), recompute=False) # Ensure that we didn't try to change the origin of the new job self.assertFalse( job.create_child_job_from_record().update_origin.called) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) @mock.patch('plainbox.impl.ctrl.gen_rfc822_records_from_io_log') @mock.patch('plainbox.impl.ctrl.logger') def test_observe_result__local_imperfect_clash( self, mock_logger, mock_gen): """ verify side effects of using observe_result() that would define a already existing job with the non-identical definition. We basically hope to see the old job being there intact and a warning to be logged. """ # Create a session that already knows about 'existing_job' # and raises a DependencyDuplicateError when add_job() gets called. existing_job = mock.Mock(spec=JobDefinition, name='existing_job') existing_job.id = 'generated' clashing_job = mock.Mock(spec=JobDefinition, name='existing_job') clashing_job.id = 'generated' session_state = mock.MagicMock(spec=SessionState, name='session_state') session_state.add_job.side_effect = DependencyDuplicateError( existing_job, clashing_job) # Create a job of which result we'll be observing job = mock.Mock(spec=JobDefinition, name='job', plugin='local') # Have job return clashing_job when create_child_job_record() is called job.create_child_job_from_record.return_value = clashing_job # Create a result for the job we'll be observing result = mock.Mock( spec=IJobResult, name='result', outcome=IJobResult.OUTCOME_PASS) # Mock what rfc822 parser returns mock_gen.return_value = [mock.Mock(spec=RFC822Record, name='record')] # Pretend that we are observing a 'result' of 'job' self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that we tried to define a new job by calling add_job() with # the clashing_job as argument session_state.add_job.assert_called_once_with( clashing_job, recompute=False) # Ensure that existing job origin was *not* updated self.assertFalse(existing_job.update_origin.called) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a warning was logged mock_logger.warning.assert_called_once_with( ("Local job %s produced job %r that collides with" " an existing job %s (from %s), the new job was" " discarded"), job, clashing_job, existing_job, existing_job.origin) @mock.patch('plainbox.impl.ctrl.gen_rfc822_records_from_io_log') def test_observe_result__local_perfect_clash(self, mock_gen): """ verify side effects of using observe_result() that would define a already existing job with the exactly identical definition. We basically hope to see the old job being there but the origin field should be updated to reflect the new association between 'existing_job' and 'job' """ # Create a session that already knows about 'existing_job' # and returns existing_job when add_job() gets called. existing_job = mock.Mock(spec=JobDefinition, name='existing_job') existing_job.id = 'generated' session_state = mock.MagicMock(spec=SessionState, name='session_state') session_state.add_job.side_effect = ( lambda new_job, recompute: existing_job) # Create a job of which result we'll be observing job = mock.Mock(spec=JobDefinition, name='job', plugin='local') # Create a result for the job we'll be observing result = mock.Mock( spec=IJobResult, name='result', outcome=IJobResult.OUTCOME_PASS) # Mock what rfc822 parser returns mock_gen.return_value = [mock.Mock(spec=RFC822Record, name='record')] # Pretend that we are observing a 'result' of 'job' self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs( session_state.job_state_map[job.id].result, result) # Ensure that we tried to define a new job using # whatever create_child_job_from_record() returns. session_state.add_job.assert_called_once_with( job.create_child_job_from_record(), recompute=False) # Ensure that the origin of the existing_job was copied # from the origin of the generated job existing_job.update_origin.assert_called_once_with( job.create_child_job_from_record().origin) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result)
class CheckBoxSessionStateControllerTests(TestCase): def setUp(self): self.ctrl = CheckBoxSessionStateController() def test_get_dependency_set(self): # Job with no dependencies job_a = JobDefinition({}) self.assertEqual(self.ctrl.get_dependency_set(job_a), set()) # Job with direct dependencies job_b = JobDefinition({'depends': 'j1, j2'}) self.assertEqual(self.ctrl.get_dependency_set(job_b), {('direct', 'j1'), ('direct', 'j2')}) # Job with resouce dependencies job_c = JobDefinition({'requires': 'j3.attr == 1'}) self.assertEqual(self.ctrl.get_dependency_set(job_c), {('resource', 'j3')}) # Job with ordering dependencies job_d = JobDefinition({'after': 'j1, j2'}) self.assertEqual(self.ctrl.get_dependency_set(job_d), {('ordering', 'j1'), ('ordering', 'j2')}) # Job with both direct and resource dependencies job_e = JobDefinition({'depends': 'j4', 'requires': 'j5.attr == 1'}) self.assertEqual(self.ctrl.get_dependency_set(job_e), {('direct', 'j4'), ('resource', 'j5')}) # Job with both direct and resource dependencies # on the same job (j6) job_f = JobDefinition({'depends': 'j6', 'requires': 'j6.attr == 1'}) self.assertEqual(self.ctrl.get_dependency_set(job_f), {('direct', 'j6'), ('resource', 'j6')}) def test_get_inhibitor_list_PENDING_RESOURCE(self): # verify that jobs that require a resource that hasn't been # invoked yet produce the PENDING_RESOURCE inhibitor j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map['j2'].job = j2 session_state.resource_map = {} self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(InhibitionCause.PENDING_RESOURCE, j2, ResourceExpression('j2.attr == "ok"')) ]) def test_get_inhibitor_list_FAILED_RESOURCE(self): # verify that jobs that require a resource that has been # invoked and produced resources but the expression dones't # evaluate to True produce the FAILED_RESOURCE inhibitor j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map['j2'].job = j2 session_state.resource_map = {'j2': [Resource({'attr': 'not-ok'})]} self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(InhibitionCause.FAILED_RESOURCE, j2, ResourceExpression('j2.attr == "ok"')) ]) def test_get_inhibitor_list_good_resource(self): # verify that jobs that require a resource that has been invoked and # produced resources for which the expression evaluates to True don't # have any inhibitors j1 = JobDefinition({'id': 'j1', 'requires': 'j2.attr == "ok"'}) j2 = JobDefinition({'id': 'j2'}) session_state = mock.MagicMock(spec=SessionState) session_state.resource_map = {'j2': [Resource({'attr': 'ok'})]} session_state.job_state_map['j2'].job = j2 self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), []) def test_get_inhibitor_list_PENDING_DEP(self): # verify that jobs that depend on another job or wait (via after) for # another that hasn't been invoked yet produce the PENDING_DEP # inhibitor j1 = JobDefinition({ 'id': 'j1', 'depends': 'j2', 'after': 'j3', }) j2 = JobDefinition({'id': 'j2'}) j3 = JobDefinition({'id': 'j3'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map = { 'j1': mock.Mock(spec_set=JobState), 'j2': mock.Mock(spec_set=JobState), 'j3': mock.Mock(spec_set=JobState), } jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_NONE jsm_j3 = session_state.job_state_map['j3'] jsm_j3.job = j3 jsm_j3.result.outcome = IJobResult.OUTCOME_NONE self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), [ JobReadinessInhibitor(InhibitionCause.PENDING_DEP, j2, None), JobReadinessInhibitor(InhibitionCause.PENDING_DEP, j3, None), ]) def test_get_inhibitor_list_FAILED_DEP(self): # verify that jobs that depend on another job that ran but # didn't result in OUTCOME_PASS produce the FAILED_DEP # inhibitor. j1 = JobDefinition({ 'id': 'j1', 'depends': 'j2', 'after': 'j3', }) j2 = JobDefinition({'id': 'j2'}) j3 = JobDefinition({'id': 'j3'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map = { 'j1': mock.Mock(spec_set=JobState), 'j2': mock.Mock(spec_set=JobState), 'j3': mock.Mock(spec_set=JobState), } jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_FAIL jsm_j3 = session_state.job_state_map['j3'] jsm_j3.job = j3 jsm_j3.result.outcome = IJobResult.OUTCOME_FAIL self.assertEqual( self.ctrl.get_inhibitor_list(session_state, j1), [JobReadinessInhibitor(InhibitionCause.FAILED_DEP, j2, None)]) def test_get_inhibitor_list_good_dep(self): # verify that jobs that depend on another job that ran and has outcome # equal to OUTCOME_PASS don't have any inhibitors j1 = JobDefinition({'id': 'j1', 'depends': 'j2', 'after': 'j3'}) j2 = JobDefinition({'id': 'j2'}) j3 = JobDefinition({'id': 'j3'}) session_state = mock.MagicMock(spec=SessionState) session_state.job_state_map = { 'j1': mock.Mock(spec_set=JobState), 'j2': mock.Mock(spec_set=JobState), 'j3': mock.Mock(spec_set=JobState), } jsm_j2 = session_state.job_state_map['j2'] jsm_j2.job = j2 jsm_j2.result.outcome = IJobResult.OUTCOME_PASS jsm_j3 = session_state.job_state_map['j3'] jsm_j3.job = j3 jsm_j3.result.outcome = IJobResult.OUTCOME_PASS self.assertEqual(self.ctrl.get_inhibitor_list(session_state, j1), []) def test_observe_result__normal(self): job = mock.Mock(spec=JobDefinition) result = mock.Mock(spec=IJobResult) session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) def test_observe_result__OUTCOME_NONE(self): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_NONE) session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a resource was *not* defined self.assertEqual(session_state.set_resource_list.call_count, 0) def test_observe_result__resource(self): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [(0, 'stdout', b'attr: value1\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'attr: value2\n')] session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that new resource was defined session_state.set_resource_list.assert_called_once_with( job.id, [Resource({'attr': 'value1'}), Resource({'attr': 'value2'})]) @mock.patch('plainbox.impl.ctrl.logger') def test_observe_result__broken_resource(self, mock_logger): job = mock.Mock(spec=JobDefinition, plugin='resource') result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [(0, 'stdout', b'barf\n')] session_state = mock.MagicMock(spec=SessionState) self.ctrl.observe_result(session_state, job, result) # Ensure that result got stored self.assertIs(session_state.job_state_map[job.id].result, result) # Ensure that signals got fired session_state.on_job_state_map_changed.assert_called_once_with() session_state.on_job_result_changed.assert_called_once_with( job, result) # Ensure that a warning was logged mock_logger.warning.assert_called_once_with( "local script %s returned invalid RFC822 data: %s", job.id, RFC822SyntaxError(None, 1, "Unexpected non-empty line: 'barf\\n'")) @mock.patch('plainbox.impl.ctrl.logger') def test_observe_result__missing_resource_key(self, mock_logger): job = make_job("R", plugin="resource") template = TemplateUnit({ 'template-resource': job.id, 'id': 'foo-{missing}', 'plugin': 'shell' }) result = mock.Mock(spec=IJobResult, outcome=IJobResult.OUTCOME_PASS) result.get_io_log.return_value = [(0, 'stdout', b'attr: value1\n'), (0, 'stdout', b'\n'), (0, 'stdout', b'attr: value2\n')] session_state = SessionState([template, job]) self.ctrl.observe_result(session_state, job, result) # Ensure that a warning was logged mock_logger.warning.assert_called_with( "Ignoring %s with missing template parameter %s", "foo-{missing}", "missing")