Пример #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'
        }])