示例#1
0
    def test_run_step(self):
        def action_function_a():
            return 'test return value'

        mock_args = MagicMock()
        mock_kwargs = MagicMock()
        mock_pre_processor = MagicMock(return_value=(mock_args, mock_kwargs))
        step = Step(action_function_a, pre_processor=mock_pre_processor)
        step.context = 'mock_context'
        step.tags['n'] = 0

        process_patcher = patch('autotrail.layer1.trail.Process')
        mock_process = process_patcher.start()

        run_step(step)

        process_patcher.stop()

        args, kwargs = mock_process.call_args
        arg_step, arg_args, arg_kwargs = kwargs['args']
        self.assertEqual(step, arg_step)
        self.assertEqual(arg_args, mock_args)
        self.assertEqual(arg_kwargs, mock_kwargs)
        self.assertEqual(kwargs['target'], step_manager)

        self.assertIn(mock.call().start(), mock_process.mock_calls)
        self.assertEqual(step.state, step.RUN)
示例#2
0
def create_conditional_step(action_function, **tags):
    """Factory to create a Step that acts like a guard to a branch.
    If this step fails, all subsequent steps in that branch, will be skipped.

    Consider the following trail where 'b' is the "conditional" step.

                   +--> d -->+
         +--> b -->|         |-->+
         |         +--> e -->+   |
    a -->|                       |--> f
         +--> c --------------- >+

    Lets say we want the branch "b" to run only if some condition is matched.
    We can consider the step 'b' to be the 'conditional' for this branch, i.e., it should succeed only if the condition
    is satisfied. If the condition is not satisfied, it will fail.
    Failure of 'b' will have the effect of skipping the progeny i.e., if 'b' fails, steps d and e will be "skipped".

    Progeny here is strict i.e., progeny of 'b' are 'd' and 'e' but not 'f' (since 'f' has a parent that is not related
    to 'b').

    This is done by setting two of the step's attributes:
    Step.pause_on_fail = True           -- So that the step fails instead of being moved to Step.PAUSE.
    Step.skip_progeny_on_failure = True -- So that the progeny are skipped on failure.

    Returns:
    Step object whose pause_on_fail is False and skip_progeny_on_failure is True.
    """
    step = Step(action_function, **tags)
    step.pause_on_fail = False
    step.skip_progeny_on_failure = True
    return step
示例#3
0
    def test_deserialize_trail_mismatched_trail(self):
        # Make another DAG:
        #           +--> step_d -->+
        # step_a -->|              |--> step_b
        #           +--> step_c -->+
        def action_a():
            pass

        def action_b():
            pass

        def action_c():
            pass

        def action_d():
            pass

        step_a = Step(action_a)
        step_b = Step(action_b)
        step_c = Step(action_c)
        step_d = Step(action_d)

        trail_definition = [
            (step_a, step_d),
            (step_a, step_c),
            (step_d, step_b),
            (step_c, step_b),
        ]

        mismatching_trail_data = serialize_trail(make_dag(trail_definition))

        with self.assertRaises(MatchTrailsException):
            deserialize_trail(self.root_step, mismatching_trail_data, {})
示例#4
0
    def test_assign_sequence_numbers_to_steps(self):
        # Make this DAG:
        #           +--> step_b -->+
        # step_a -->|              |--> step_d
        #           +--> step_c -->+
        step_a = Step(lambda x: x)
        step_b = Step(lambda x: x)
        step_c = Step(lambda x: x)
        step_d = Step(lambda x: x)

        trail_definition = [
            (step_a, step_b),
            (step_a, step_c),
            (step_b, step_d),
            (step_c, step_d),
        ]

        root_step = make_dag(trail_definition)
        assign_sequence_numbers_to_steps(root_step)

        get_number = lambda x: x.tags['n']

        self.assertEqual(get_number(step_a), 0)
        self.assertEqual(get_number(step_d), 3)

        self.assertGreater(get_number(step_d), get_number(step_b))
        self.assertGreater(get_number(step_d), get_number(step_c))

        self.assertLess(get_number(step_a), get_number(step_b))
        self.assertLess(get_number(step_a), get_number(step_c))

        self.assertNotEqual(get_number(step_b), get_number(step_c))
示例#5
0
    def test_normal_run(self):
        trail_manager(self.mock_root_step,
                      self.mock_api_socket,
                      self.mock_backup,
                      delay=12,
                      context=self.mock_context,
                      state_transitions=self.mock_state_transitions)

        self.mock_topological_traverse.assert_called_once_with(
            self.mock_root_step)
        self.mock_state_transition.assert_called_once_with(
            self.mock_step, self.mock_context)

        root_step, done_check, ignore_check = self.mock_topological_while.call_args[
            0]
        self.assertEqual(root_step, self.mock_root_step)

        # This table is used to check the possible results of done_check and ignore_check functions
        check_table = [
            # State,                Expected result of done_check,      Expected result of ignore_check
            (Step.SUCCESS, True, False),
            (Step.SKIPPED, True, False),
            (Step.READY, False, False),
            (Step.RUN, False, False),
            (Step.FAILURE, False, True),
            (Step.BLOCKED, False, True),
            (Step.PAUSED, False, False),
            (Step.INTERRUPTED, False, False),
            (Step.PAUSED_ON_FAIL, False, False),
            (Step.WAIT, False, False),
            (Step.TOSKIP, False, False),
            (Step.TOBLOCK, False, False),
            (Step.TOPAUSE, False, False),
        ]
        for state, done_check_result, ignore_check_result in check_table:
            step = Step(lambda: True)
            step.state = state
            self.assertEqual(done_check(step), done_check_result)
            self.assertEqual(ignore_check(step), ignore_check_result)

        self.assertEqual(len(self.mock_serve_socket.mock_calls), 2)
        for mock_call in self.mock_serve_socket.mock_calls:
            socket, handler = mock_call[1]
            self.assertEqual(socket, self.mock_api_socket)
            mock_request = MagicMock()
            handler(mock_request)
            self.mock_handle_api_call.assert_called_with(
                mock_request, steps=[self.mock_step])

        self.assertEqual(list(self.step_iterator),
                         [])  # Make sure the iterator is drained.
        self.mock_backup.assert_called_once_with(self.mock_root_step)
        self.mock_sleep.assert_called_once_with(12)
        self.mock_api_socket.shutdown.assert_called_once_with(SHUT_RDWR)
示例#6
0
    def test_log_step(self):
        def action_function(trail_env, context):
            return 'test return value {}'.format(context)

        step = Step(action_function)
        step.tags[
            'n'] = 7  # Typically this is set automatically. We're setting this manually for testing purposes.

        mock_logger = MagicMock()

        log_step(mock_logger, step, 'mock message')

        mock_logger.assert_called_once_with(
            '[Step Name=action_function, n=7] mock message')
示例#7
0
    def test_step_manager(self):
        def action_function(trail_env, context):
            return 'test return value {}'.format(context)

        step = Step(action_function)
        step.tags[
            'n'] = 7  # Typically this is set automatically. We're setting this manually for testing purposes.
        step.result_queue = MagicMock()
        trail_environment = MagicMock()
        step_manager(step, (trail_environment, ), dict(context='foo'))

        expected_result = StepResult(result=Step.SUCCESS,
                                     return_value='test return value foo')
        step.result_queue.put.assert_called_once_with(expected_result)
示例#8
0
    def test_collect_prompt_messages_from_step(self):
        step = Step(lambda x: x)
        step.tags['n'] = 0
        step.prompt_queue = MagicMock()
        step.prompt_queue.get_nowait = MagicMock(
            side_effect=['foo', QueueEmpty()])

        collect_prompt_messages_from_step(step)

        self.assertEqual(step.prompt_queue.get_nowait.mock_calls,
                         [mock.call(), mock.call()])

        expected_messages = ['foo']

        self.assertEqual(step.prompt_messages, expected_messages)
示例#9
0
    def test_step_manager_with_exception(self):
        def action_function(trail_env, context):
            raise Exception('test exception')
            return 'test return value'

        step = Step(action_function)
        step.tags[
            'n'] = 7  # Typically this is set automatically. We're setting this manually for testing purposes.
        step.result_queue = MagicMock()
        trail_environment = MagicMock()
        step_manager(step, trail_environment, context='foo')

        expected_result = StepResult(result=Step.PAUSED_ON_FAIL,
                                     return_value='test exception')
        step.result_queue.put.assert_called_once_with(expected_result)
示例#10
0
    def test_instance_with_callable_object(self):
        class MockAction(object):
            def __call__(self):
                pass

            def __str__(self):
                return 'MockAction'

        mock_action_object = MockAction()
        step = Step(mock_action_object)

        self.assertIsInstance(step, Step)
        self.assertEqual(step.action_function, mock_action_object)
        self.assertEqual(step.tags, {'name': 'MockAction'})
        self.assertEqual(step.state, step.READY)
        self.assertEqual(step.process, None)
        self.assertEqual(step.return_value, None)
        self.assertEqual(step.result_queue, None)
        self.assertEqual(step.input_queue, None)
        self.assertEqual(step.output_queue, None)
        self.assertEqual(step.prompt_queue, None)
        self.assertEqual(step.prompt_messages, [])
        self.assertEqual(step.output_messages, [])
        self.assertEqual(step.input_messages, [])
        self.assertTrue(step.pause_on_fail)
        self.assertEqual(str(step), 'MockAction')
示例#11
0
    def setUp(self):
        self.log_step_patcher = patch('autotrail.layer1.trail.log_step')
        self.mock_log_step = self.log_step_patcher.start()

        self.skip_progeny_patcher = patch(
            'autotrail.layer1.trail.skip_progeny')
        self.mock_skip_progeny = self.skip_progeny_patcher.start()

        self.collect_output_messages_from_step_patcher = patch(
            'autotrail.layer1.trail.collect_output_messages_from_step')
        self.mock_collect_output_messages_from_step = self.collect_output_messages_from_step_patcher.start(
        )

        self.collect_prompt_messages_from_step_patcher = patch(
            'autotrail.layer1.trail.collect_prompt_messages_from_step')
        self.mock_collect_prompt_messages_from_step = self.collect_prompt_messages_from_step_patcher.start(
        )

        self.mock_result = MagicMock()
        self.mock_result.result = Step.SUCCESS
        self.mock_result.return_value = 'mock_return_value'
        self.mock_return_value = MagicMock()
        self.mock_failed_return_value = MagicMock()
        self.mock_post_processor = MagicMock(
            return_value=self.mock_return_value)
        self.mock_failure_handler = MagicMock(
            return_value=self.mock_failed_return_value)
        self.mock_step = Step(lambda x: x,
                              post_processor=self.mock_post_processor,
                              failure_handler=self.mock_failure_handler)
        self.mock_step.skip_progeny_on_failure = False
        self.mock_step.result_queue = MagicMock()
        self.mock_step.result_queue.get_nowait = MagicMock(
            return_value=self.mock_result)
示例#12
0
    def test_step_to_stepstatus_empty_fields(self):
        step = Step(lambda x: x, n=0)
        step_status = step_to_stepstatus(step, [])

        self.assertEqual(step_status, {
            StatusField.N: 0,
            StatusField.NAME: str(step)
        })
示例#13
0
    def test_make_dag_cyclic(self):
        # Make this cyclic DAG:
        # step_a --> step_b -->+
        #    ^                 |
        #    |                 v
        #    +<----- step_c <--+
        step_a = Step(lambda x: x)
        step_b = Step(lambda x: x)
        step_c = Step(lambda x: x)

        trail_definition = [
            (step_a, step_b),
            (step_b, step_c),
            (step_c, step_a),
        ]

        with self.assertRaises(CyclicException):
            root_step = make_dag(trail_definition)
示例#14
0
    def test_write_trail_definition_as_dot(self):
        # Make this DAG:
        # step_a --> step_b
        def a():
            pass

        def b():
            pass

        step_a = Step(a)
        step_b = Step(b)

        trail_definition = [(step_a, step_b)]

        dot_file = StringIO()
        write_trail_definition_as_dot(trail_definition, to=dot_file)

        self.assertEqual(dot_file.getvalue(), 'digraph trail {\nnode [style=rounded, shape=box];\n"a" -> "b";\n}\n')
示例#15
0
    def test_run_step_when_pre_processor_fails(self):
        def action_function_a():
            return 'test return value'

        mock_exception = MockException()
        mock_pre_processor = MagicMock(side_effect=mock_exception)
        step = Step(action_function_a, pre_processor=mock_pre_processor)
        step.context = 'mock_context'
        step.tags['n'] = 0

        process_patcher = patch('autotrail.layer1.trail.Process')
        mock_process = process_patcher.start()

        run_step(step)

        process_patcher.stop()
        self.assertEqual(step.process, None)
        self.assertEqual(mock_process.mock_calls, [])
        self.assertEqual(step.state, step.FAILURE)
        self.assertEqual(step.return_value, mock_exception)
示例#16
0
    def setUp(self):
        # Create a simple trail (root_step)
        #           +--> step_b (group=1)
        # step_a -->|--> step_c
        #           +--> step_d (group=1)
        def action_function_a():
            pass

        def action_function_b():
            pass

        def action_function_c():
            pass

        def action_function_d():
            pass

        self.step_a = Step(action_function_a)
        self.step_b = Step(action_function_b, group=1)
        self.step_c = Step(action_function_c)
        self.step_d = Step(action_function_d, group=1)
示例#17
0
    def test_serialize_trail(self):
        # Make this DAG:
        #           +--> step_b -->+
        # step_a -->|              |--> step_d
        #           +--> step_c -->+
        def action_a():
            pass

        def action_b():
            pass

        def action_c():
            pass

        def action_d():
            pass

        step_a = Step(action_a)
        step_b = Step(action_b)
        step_c = Step(action_c)
        step_d = Step(action_d)

        trail_definition = [
            (step_a, step_b),
            (step_a, step_c),
            (step_b, step_d),
            (step_c, step_d),
        ]

        root_step = make_dag(trail_definition)
        trail_data = serialize_trail(root_step)

        for step in [step_a, step_b, step_c, step_d]:
            self.assertIn(str(step), trail_data)
            self.assertEqual(trail_data[str(step)][StatusField.STATE],
                             str(step.state))
            self.assertEqual(trail_data[str(step)][StatusField.RETURN_VALUE],
                             str(step.return_value))
            for parent in step.parents:
                self.assertIn(str(parent), trail_data[str(step)]['parents'])
示例#18
0
    def test_run_step_with_rerun_of_a_step(self):
        def action_function_a():
            return 'test return value'

        step = Step(action_function_a)
        step.tags['n'] = 0

        # Setup some old values for the step. Similar to the effect of running the step might have.
        step.return_value = 'Some old return value.'
        step.prompt_messages = ['old message 1', 'old message 2']
        step.input_messages = ['response to old message 1']

        process_patcher = patch('autotrail.layer1.trail.Process')
        mock_process = process_patcher.start()

        run_step(step, 'mock_context')

        process_patcher.stop()

        args, kwargs = mock_process.call_args
        arg_step, arg_trail_env, arg_context = kwargs['args']
        self.assertEqual(step, arg_step)
        self.assertIsInstance(arg_trail_env, TrailEnvironment)
        self.assertEqual(arg_context, 'mock_context')
        self.assertEqual(kwargs['target'], step_manager)

        self.assertIn(call().start(), mock_process.mock_calls)
        self.assertEqual(step.state, step.RUN)

        # Because the step is being re-run, the old values of the following attributes should get reset.
        self.assertEqual(step.return_value, None)
        self.assertEqual(step.prompt_messages, [])
        self.assertEqual(step.input_messages, [])
示例#19
0
    def test_step_to_stepstatus_with_non_json_serializable_return_value(self):
        step = Step(lambda x: x, n=0)
        mock_exception = TypeError('is not JSON serializable')
        step.return_value = mock_exception

        step_status = step_to_stepstatus(step, [
            StatusField.STATE, StatusField.RETURN_VALUE, StatusField.TAGS,
            StatusField.OUTPUT_MESSAGES, StatusField.PROMPT_MESSAGES,
            StatusField.UNREPLIED_PROMPT_MESSAGE
        ])

        self.assertEqual(
            step_status, {
                StatusField.N: 0,
                StatusField.NAME: str(step),
                StatusField.TAGS: step.tags,
                StatusField.STATE: Step.READY,
                StatusField.RETURN_VALUE: str(mock_exception),
                StatusField.OUTPUT_MESSAGES: [],
                StatusField.PROMPT_MESSAGES: [],
                StatusField.UNREPLIED_PROMPT_MESSAGE: None
            })
示例#20
0
    def test_make_dag(self):
        # Make this DAG:
        #           +--> step_b -->+
        # step_a -->|              |--> step_d
        #           +--> step_c -->+
        step_a = Step(lambda x: x)
        step_b = Step(lambda x: x)
        step_c = Step(lambda x: x)
        step_d = Step(lambda x: x)

        trail_definition = [
            (step_a, step_b),
            (step_a, step_c),
            (step_b, step_d),
            (step_c, step_d),
        ]

        root_step = make_dag(trail_definition)
        self.assertEqual(root_step, step_a)

        self.assertEqual(step_a.tags['n'], 0)
        self.assertEqual(step_d.tags['n'], 3)
示例#21
0
    def test_run_step(self):
        def action_function_a():
            return 'test return value'

        step = Step(action_function_a)
        step.tags['n'] = 0

        process_patcher = patch('autotrail.layer1.trail.Process')
        mock_process = process_patcher.start()

        run_step(step, 'mock_context')

        process_patcher.stop()

        args, kwargs = mock_process.call_args
        arg_step, arg_trail_env, arg_context = kwargs['args']
        self.assertEqual(step, arg_step)
        self.assertIsInstance(arg_trail_env, TrailEnvironment)
        self.assertEqual(arg_context, 'mock_context')
        self.assertEqual(kwargs['target'], step_manager)

        self.assertIn(call().start(), mock_process.mock_calls)
        self.assertEqual(step.state, step.RUN)
示例#22
0
    def setUp(self):
        # Make this DAG:
        #           +--> step_b -->+
        # step_a -->|              |--> step_d
        #           +--> step_c -->+
        def action_a():
            pass

        def action_b():
            pass

        def action_c():
            pass

        def action_d():
            pass

        self.step_a = Step(action_a)
        self.step_b = Step(action_b)
        self.step_c = Step(action_c)
        self.step_d = Step(action_d)

        trail_definition = [
            (self.step_a, self.step_b),
            (self.step_a, self.step_c),
            (self.step_b, self.step_d),
            (self.step_c, self.step_d),
        ]

        self.root_step = make_dag(trail_definition)
        self.step_a.return_value = 'mock return value'
        self.step_a.prompt_messages = ['mock prompt_messages']
        self.step_a.output_messages = ['mock output_messages']
        self.step_a.input_messages = ['mock input_messages']

        self.trail_data = serialize_trail(self.root_step)
示例#23
0
    def test_create_namespaces_for_tag_keys_and_values(self):
        def action_a():
            pass

        step_a = Step(action_a, foo='bar', n=5)

        keys, values = create_namespaces_for_tag_keys_and_values(step_a)

        self.assertEqual(keys.foo, 'foo')
        self.assertEqual(keys.name, 'name')
        with self.assertRaises(AttributeError):
            keys.n

        self.assertEqual(values.bar, 'bar')
        self.assertEqual(values.action_a, 'action_a')
示例#24
0
    def test_search_steps_with_states(self):
        step_running = Step(lambda x: x)
        step_running.state = Step.RUN

        step_paused = Step(lambda x: x)
        step_paused.state = Step.PAUSED

        steps = [step_running, step_paused]

        filtered_steps = search_steps_with_states(steps, [Step.PAUSED])

        self.assertEqual(list(filtered_steps), [step_paused])
示例#25
0
    def test_step_to_stepstatus_all_fields(self):
        step = Step(lambda x: x, n=0)
        step_status = step_to_stepstatus(step, [
            StatusField.STATE, StatusField.RETURN_VALUE,
            StatusField.OUTPUT_MESSAGES, StatusField.PROMPT_MESSAGES,
            StatusField.UNREPLIED_PROMPT_MESSAGE
        ])

        self.assertEqual(
            step_status, {
                StatusField.N: 0,
                StatusField.NAME: str(step),
                StatusField.STATE: Step.READY,
                StatusField.RETURN_VALUE: None,
                StatusField.OUTPUT_MESSAGES: [],
                StatusField.PROMPT_MESSAGES: [],
                StatusField.UNREPLIED_PROMPT_MESSAGE: None
            })
示例#26
0
    def test_instance_with_name(self):
        def mock_function():
            pass

        step = Step(mock_function, name='custom_name')
        self.assertIsInstance(step, Step)
        self.assertEqual(step.action_function, mock_function)
        self.assertEqual(step.tags, {'name': 'custom_name'})
        self.assertEqual(step.state, step.READY)
        self.assertEqual(step.process, None)
        self.assertEqual(step.return_value, None)
        self.assertEqual(step.result_queue, None)
        self.assertEqual(step.input_queue, None)
        self.assertEqual(step.output_queue, None)
        self.assertEqual(step.prompt_queue, None)
        self.assertEqual(step.prompt_messages, [])
        self.assertEqual(step.output_messages, [])
        self.assertEqual(step.input_messages, [])
        self.assertTrue(step.pause_on_fail)
        self.assertEqual(str(step), 'custom_name')
示例#27
0
    def test_extract_essential_tags(self):
        step_1 = Step(lambda x: x, n=0)
        step_1.tags['name'] = 'step_1'
        step_2 = Step(lambda x: x, n=1)
        step_2.tags['name'] = 'step_2'

        essential_tags = list(extract_essential_tags([step_1, step_2]))

        self.assertEqual(essential_tags, [{
            'n': 0,
            'name': 'step_1'
        }, {
            'n': 1,
            'name': 'step_2'
        }])