예제 #1
0
    def assertSearchLogging2(self, logger):
        """
        The two put actions are logged, followed by the two get actions.
        """
        [put1, put2] = LoggedAction.of_type(logger.messages, LOG_SEARCH_PUT)
        assertContainsFields(
            self, put1.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'e',
             'indexType': u'i',
             'searchType': u'type1',
             'result': u'result1'})
        assertContainsFields(
            self, put1.end_message,
            {'searchValue': u'value'})
        self.assertTrue(put1.succeeded)
        assertContainsFields(
            self, put2.start_message,
            {'searchClass': SearchClasses.PREFIX,
             'environment': u'e',
             'indexType': u'i',
             'searchType': u'type2',
             'result': u'result2'})
        assertContainsFields(
            self, put2.end_message,
            {'searchValue': u'value'})
        self.assertTrue(put2.succeeded)

        [get1, get2] = LoggedAction.of_type(logger.messages, LOG_SEARCH_GET)
        assertContainsFields(
            self, get1.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'e',
             'indexType': u'i',
             'searchValue': u'value',
             'searchType': None})
        assertContainsFields(
            self,
            get1.end_message,
            {'results': [{u'result': u'result1',
                          u'type': u'type1'}]})
        self.assertTrue(get1.succeeded)
        assertContainsFields(
            self, get2.start_message,
            {'searchClass': SearchClasses.PREFIX,
             'environment': u'e',
             'indexType': u'i',
             'searchValue': u'va',
             'searchType': None})
        assertContainsFields(
            self,
            get2.end_message,
            {'results': [{u'result': u'result2',
                          u'type': u'type2'}]})
        self.assertTrue(get2.succeeded)
예제 #2
0
    def assertSearchLogging(self, logger):
        """
        The put action is logged, followed by the get action, followed by the
        delete, followed by the second get.
        """
        [put] = LoggedAction.of_type(logger.messages, LOG_SEARCH_PUT)
        assertContainsFields(
            self, put.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'result': u'result',
             'searchType': u'type'})
        assertContainsFields(
            self, put.end_message,
            {'searchValue': u'somevalue'})
        self.assertTrue(put.succeeded)

        [delete] = LoggedAction.of_type(logger.messages, LOG_SEARCH_DELETE)
        assertContainsFields(
            self, delete.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'result': u'result',
             'searchType': u'type'})
        self.assertTrue(delete.succeeded)

        [get1, get2] = LoggedAction.of_type(logger.messages, LOG_SEARCH_GET)
        assertContainsFields(
            self, get1.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'searchValue': u'somevalue',
             'searchType': None})
        assertContainsFields(
            self,
            get1.end_message,
            {'results': [{u'result': u'result',
                          u'type': u'type'}]})
        self.assertTrue(get1.succeeded)
        assertContainsFields(
            self, get2.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'searchValue': u'somevalue',
             'searchType': None})
        assertContainsFields(self, get2.end_message, {'results': []})
        self.assertTrue(get2.succeeded)
예제 #3
0
    def test_boto_ec2response_error(self, logger):
        """
        1. Invalid parameters to Boto's EBS API calls
        raise the right exception after logging to Eliot.
        2. Verify Eliot log output for expected message fields
        from logging decorator for boto.exception.EC2Exception
        originating from boto.ec2.connection.EC2Connection.
        """
        # Test 1: Create volume with size 0.
        # Raises: ClientError
        self.assertRaises(ClientError, self.api.create_volume,
                          dataset_id=uuid4(), size=0,)

        # Test 2: Set EC2 connection zone to an invalid string.
        # Raises: ClientError
        self.api.zone = u'invalid_zone'
        self.assertRaises(
            ClientError,
            self.api.create_volume,
            dataset_id=uuid4(),
            size=self.minimum_allocatable_size,
        )

        # Validate decorated method for exception logging
        # actually logged to ``Eliot`` logger.
        expected_message_keys = {AWS_CODE.key, AWS_MESSAGE.key,
                                 AWS_REQUEST_ID.key}
        for logged in LoggedAction.of_type(logger.messages, AWS_ACTION,):
            key_subset = set(key for key in expected_message_keys
                             if key in logged.end_message.keys())
            self.assertEqual(expected_message_keys, key_subset)
예제 #4
0
    def test_iterates(self, logger):
        """
        If the predicate returns something falsey followed by something truthy,
        then ``loop_until`` returns it immediately.
        """
        result = object()
        results = [None, result]

        def predicate():
            return results.pop(0)

        clock = Clock()

        d = loop_until(clock, predicate)

        self.assertNoResult(d)

        clock.advance(0.1)
        self.assertEqual(self.successResultOf(d), result)

        action = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)[0]
        assertContainsFields(self, action.start_message, {
            'predicate': predicate,
        })
        assertContainsFields(self, action.end_message, {
            'result': result,
        })
        self.assertTrue(action.succeeded)
        message = LoggedMessage.of_type(logger.messages,
                                        LOOP_UNTIL_ITERATION_MESSAGE)[0]
        self.assertEqual(action.children, [message])
        assertContainsFields(self, message.message, {
            'result': None,
        })
예제 #5
0
    def test_multiple_iterations(self, logger):
        """
        If the predicate returns something falsey followed by something truthy,
        then ``loop_until`` returns it immediately.
        """
        result = object()
        results = [None, False, result]
        expected_results = results[:-1]

        def predicate():
            return results.pop(0)

        clock = Clock()

        d = loop_until(clock, predicate)

        clock.advance(0.1)
        self.assertNoResult(d)
        clock.advance(0.1)

        self.assertEqual(self.successResultOf(d), result)

        action = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)[0]
        assertContainsFields(self, action.start_message, {"predicate": predicate})
        assertContainsFields(self, action.end_message, {"result": result})
        self.assertTrue(action.succeeded)
        messages = LoggedMessage.of_type(logger.messages, LOOP_UNTIL_ITERATION_MESSAGE)
        self.assertEqual(action.children, messages)
        self.assertEqual([messages[0].message["result"], messages[1].message["result"]], expected_results)
예제 #6
0
def test_blueprint_container_dependencies_graph_with_two_last_steps(logger):
    mock_step1 = create_mock_step("step1", last=True)
    mock_step2 = create_mock_step("step2", requires={mock_step1})
    mock_step3 = create_mock_step("step3", last=True)

    mock_bootsteps = [mock_step1, mock_step2, mock_step3]

    class MyBlueprintContainer(BlueprintContainer):
        bootsteps = mock_bootsteps

    with pytest.raises(ValueError,
                       match="Only one boot step can be last. Found 2."):
        MyBlueprintContainer.blueprint

    logged_actions = LoggedAction.of_type(
        logger.messages, "bootsteps:blueprint:building_dependency_graph")
    logged_action = logged_actions[0]

    assert_log_message_field_equals(logged_action.start_message, "name",
                                    MyBlueprintContainer.name)
    assert_logged_action_failed(logged_action)
    assert_log_message_field_equals(
        logged_action.end_message, "reason",
        "Only one boot step can be last. Found 2.")
    assert_log_message_field_equals(logged_action.end_message, "exception",
                                    "builtins.ValueError")
    def test_iterates(self, logger):
        """
        If the predicate returns something falsey followed by something truthy,
        then ``loop_until`` returns it immediately.
        """
        result = object()
        results = [None, result]

        def predicate():
            return results.pop(0)
        clock = Clock()

        d = loop_until(clock, predicate)

        self.assertNoResult(d)

        clock.advance(0.1)
        self.assertEqual(
            self.successResultOf(d),
            result)

        action = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)[0]
        assertContainsFields(self, action.start_message, {
            'predicate': predicate,
        })
        assertContainsFields(self, action.end_message, {
            'result': result,
        })
        self.assertTrue(action.succeeded)
        message = LoggedMessage.of_type(
            logger.messages, LOOP_UNTIL_ITERATION_MESSAGE)[0]
        self.assertEqual(action.children, [message])
        assertContainsFields(self, message.message, {
            'result': None,
        })
예제 #8
0
    def test_boto_ec2response_error(self, logger):
        """
        1. Invalid parameters to Boto's EBS API calls
        raise the right exception after logging to Eliot.
        2. Verify Eliot log output for expected message fields
        from logging decorator for boto.exception.EC2Exception
        originating from boto.ec2.connection.EC2Connection.
        """
        # Test 1: Create volume with size 0.
        # Raises: ClientError
        self.assertRaises(ClientError, self.api.create_volume,
                          dataset_id=uuid4(), size=0,)

        # Test 2: Set EC2 connection zone to an invalid string.
        # Raises: ClientError
        self.api.zone = u'invalid_zone'
        self.assertRaises(
            ClientError,
            self.api.create_volume,
            dataset_id=uuid4(),
            size=self.minimum_allocatable_size,
        )

        # Validate decorated method for exception logging
        # actually logged to ``Eliot`` logger.
        expected_message_keys = {AWS_CODE.key, AWS_MESSAGE.key,
                                 AWS_REQUEST_ID.key}
        for logged in LoggedAction.of_type(logger.messages, AWS_ACTION,):
            key_subset = set(key for key in expected_message_keys
                             if key in logged.end_message.keys())
            self.assertEqual(expected_message_keys, key_subset)
예제 #9
0
    def assertSearchLogging(self, logger):
        """
        The put action is logged, followed by the get action, followed by the
        delete, followed by the second get.
        """
        [put] = LoggedAction.of_type(logger.messages, LOG_SEARCH_PUT)
        assertContainsFields(
            self, put.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'result': u'result',
             'searchType': u'type'})
        assertContainsFields(
            self, put.end_message,
            {'searchValue': u'somevalue'})
        self.assertTrue(put.succeeded)

        [delete] = LoggedAction.of_type(logger.messages, LOG_SEARCH_DELETE)
        assertContainsFields(
            self, delete.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'result': u'result',
             'searchType': u'type'})
        self.assertTrue(delete.succeeded)

        [get1, get2] = LoggedAction.of_type(logger.messages, LOG_SEARCH_GET)
        assertContainsFields(
            self, get1.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'searchValue': u'somevalue',
             'searchType': None})
        assertContainsFields(self, get1.end_message, {'results': [u'result']})
        self.assertTrue(get1.succeeded)
        assertContainsFields(
            self, get2.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'someenv',
             'indexType': u'someindex',
             'searchValue': u'somevalue',
             'searchType': None})
        assertContainsFields(self, get2.end_message, {'results': []})
        self.assertTrue(get2.succeeded)
예제 #10
0
    def assertSearchLogging2(self, logger):
        """
        The two put actions are logged, followed by the two get actions.
        """
        [put1, put2] = LoggedAction.of_type(logger.messages, LOG_SEARCH_PUT)
        assertContainsFields(
            self, put1.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'e',
             'indexType': u'i',
             'searchType': u'type1',
             'result': u'result1'})
        assertContainsFields(
            self, put1.end_message,
            {'searchValue': u'value'})
        self.assertTrue(put1.succeeded)
        assertContainsFields(
            self, put2.start_message,
            {'searchClass': SearchClasses.PREFIX,
             'environment': u'e',
             'indexType': u'i',
             'searchType': u'type2',
             'result': u'result2'})
        assertContainsFields(
            self, put2.end_message,
            {'searchValue': u'value'})
        self.assertTrue(put2.succeeded)

        [get1, get2] = LoggedAction.of_type(logger.messages, LOG_SEARCH_GET)
        assertContainsFields(
            self, get1.start_message,
            {'searchClass': SearchClasses.EXACT,
             'environment': u'e',
             'indexType': u'i',
             'searchValue': u'value',
             'searchType': None})
        assertContainsFields(self, get1.end_message, {'results': [u'result1']})
        self.assertTrue(get1.succeeded)
        assertContainsFields(
            self, get2.start_message,
            {'searchClass': SearchClasses.PREFIX,
             'environment': u'e',
             'indexType': u'i',
             'searchValue': u'va',
             'searchType': None})
        assertContainsFields(self, get2.end_message, {'results': [u'result2']})
        self.assertTrue(get2.succeeded)
예제 #11
0
 def assert_context_preserved(self, logger):
     """
     Logging in the method running in the thread pool is child of caller's
     Eliot context.
     """
     parent = assertHasAction(self, logger, LOG_IN_CALLER, True, {})
     # in-between we expect a eliot:remote_task...
     self.assertIn(parent.children[0].children[0],
                   LoggedAction.of_type(logger.messages, LOG_IN_SPY))
예제 #12
0
 def test_run_successful_test(self, logger):
     """
     Running a test with the Eliot reporter logs an action.
     """
     reporter = make_reporter()
     test = make_successful_test()
     test.run(reporter)
     [action] = LoggedAction.of_type(logger.serialize(), TEST)
     assertContainsFields(self, action.start_message, {"test": test.id()})
예제 #13
0
 def assert_context_preserved(self, logger):
     """
     Logging in the method running in the thread pool is child of caller's
     Eliot context.
     """
     parent = assertHasAction(self, logger, LOG_IN_CALLER, True, {})
     # in-between we expect a eliot:remote_task...
     self.assertIn(parent.children[0].children[0],
                   LoggedAction.of_type(logger.messages, LOG_IN_SPY))
예제 #14
0
 def test_successful_test(self, logger):
     """
     Starting and stopping a test logs a whole action.
     """
     reporter = make_reporter()
     test = make_successful_test()
     reporter.startTest(test)
     reporter.stopTest(test)
     [action] = LoggedAction.of_type(logger.serialize(), TEST)
     assertContainsFields(self, action.start_message, {"test": test.id()})
예제 #15
0
    def assertLookupLogging(self, logger):
        """
        The put action is logged, followed by the get action.
        """
        [put] = LoggedAction.of_type(logger.messages, LOG_LOOKUP_PUT)
        assertContainsFields(
            self, put.start_message,
            {'environment': u'someenv',
             'indexType': u'sometype',
             'key': u'somekey'})
        assertContainsFields(self, put.end_message, {'value': b'data'})
        self.assertTrue(put.succeeded)

        [get] = LoggedAction.of_type(logger.messages, LOG_LOOKUP_GET)
        assertContainsFields(
            self, get.start_message,
            {'environment': u'someenv',
             'indexType': u'sometype',
             'key': u'somekey'})
        assertContainsFields(self, get.end_message, {'value': b'data'})
        self.assertTrue(get.succeeded)
예제 #16
0
    def assertLookupLogging(self, logger):
        """
        The put action is logged, followed by the get action.
        """
        [put] = LoggedAction.of_type(logger.messages, LOG_LOOKUP_PUT)
        assertContainsFields(
            self, put.start_message,
            {'environment': u'someenv',
             'indexType': u'sometype',
             'key': u'somekey'})
        assertContainsFields(self, put.end_message, {'value': b'data'})
        self.assertTrue(put.succeeded)

        [get] = LoggedAction.of_type(logger.messages, LOG_LOOKUP_GET)
        assertContainsFields(
            self, get.start_message,
            {'environment': u'someenv',
             'indexType': u'sometype',
             'key': u'somekey'})
        assertContainsFields(self, get.end_message, {'value': b'data'})
        self.assertTrue(get.succeeded)
예제 #17
0
 def assertMissingGetLogging(self, logger):
     """
     When a I{GET} results in I{Not found}, a successful action is logged
     with a C{None} value.
     """
     [get] = LoggedAction.of_type(logger.messages, LOG_LOOKUP_GET)
     assertContainsFields(
         self, get.start_message,
         {'environment': u'someenv',
          'indexType': u'sometype',
          'key': u'somekey'})
     assertContainsFields(self, get.end_message, {'value': None})
     self.assertTrue(get.succeeded)
예제 #18
0
 def assertMissingGetLogging(self, logger):
     """
     When a I{GET} results in I{Not found}, a successful action is logged
     with a C{None} value.
     """
     [get] = LoggedAction.of_type(logger.messages, LOG_LOOKUP_GET)
     assertContainsFields(
         self, get.start_message,
         {'environment': u'someenv',
          'indexType': u'sometype',
          'key': u'somekey'})
     assertContainsFields(self, get.end_message, {'value': None})
     self.assertTrue(get.succeeded)
예제 #19
0
    def test_immediate_success(self, logger):
        """
        If the predicate returns something truthy immediately, then
        ``loop_until`` returns a deferred that has already fired with that
        value.
        """
        result = object()

        def predicate():
            return result

        clock = Clock()
        d = loop_until(clock, predicate)
        self.assertEqual(self.successResultOf(d), result)

        action = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)[0]
        assertContainsFields(self, action.start_message, {"predicate": predicate})
        assertContainsFields(self, action.end_message, {"action_status": "succeeded", "result": result})
예제 #20
0
    def test_partial_predicate(self, logger):
        """
        Predicate can be a functools.partial function.
        """
        result = object()

        def check():
            return result

        predicate = partial(check)

        clock = Clock()
        d = loop_until(clock, predicate)
        self.assertEqual(self.successResultOf(d), result)

        [action] = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)
        assertContainsFields(self, action.start_message, {"predicate": predicate})
        assertContainsFields(self, action.end_message, {"action_status": "succeeded", "result": result})
예제 #21
0
def test_blueprint_container_dependencies_graph(logger):
    mock_step1 = create_mock_step("step1")
    mock_step2 = create_mock_step("step2", requires={mock_step1})
    mock_step3 = create_mock_step("step3", last=True)
    mock_step4 = create_mock_step("step4", required_by={mock_step2})
    mock_step5 = create_mock_step("step5", include_if=False)

    mock_bootsteps = [
        mock_step1, mock_step4, mock_step2, mock_step3, mock_step5
    ]

    class MyBlueprintContainer(BlueprintContainer):
        bootsteps = mock_bootsteps

    mock_bootsteps.remove(mock_step5)
    assert list(MyBlueprintContainer.blueprint._steps.nodes) == mock_bootsteps
    assert set(MyBlueprintContainer.blueprint._steps.edges) == {
        (mock_step2, mock_step1),
        (mock_step2, mock_step4),
        (mock_step3, mock_step1),
        (mock_step3, mock_step4),
        (mock_step3, mock_step2),
    }

    logged_actions = LoggedAction.of_type(
        logger.messages, "bootsteps:blueprint:building_dependency_graph")
    logged_action = logged_actions[0]

    assert_log_message_field_equals(logged_action.start_message, "name",
                                    MyBlueprintContainer.blueprint.name)

    assert_log_message_field_equals(logged_action.end_message, "name",
                                    MyBlueprintContainer.blueprint.name)
    assert_log_message_field_equals(
        logged_action.end_message,
        "graph",
        lambda value: value.nodes == MyBlueprintContainer.blueprint._steps.
        nodes and value.edges == MyBlueprintContainer.blueprint._steps.edges,
    )
    assert_logged_action_succeeded(logged_action)
예제 #22
0
    def test_partial_predicate(self, logger):
        """
        Predicate can be a functools.partial function.
        """
        result = object()

        def check():
            return result

        predicate = partial(check)

        clock = Clock()
        d = loop_until(clock, predicate)
        self.assertEqual(self.successResultOf(d), result)

        [action] = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)
        assertContainsFields(self, action.start_message, {
            'predicate': predicate,
        })
        assertContainsFields(self, action.end_message, {
            'action_status': 'succeeded',
            'result': result,
        })
예제 #23
0
    def test_immediate_success(self, logger):
        """
        If the predicate returns something truthy immediately, then
        ``loop_until`` returns a deferred that has already fired with that
        value.
        """
        result = object()

        def predicate():
            return result

        clock = Clock()
        d = loop_until(clock, predicate)
        self.assertEqual(self.successResultOf(d), result)

        action = LoggedAction.of_type(logger.messages, LOOP_UNTIL_ACTION)[0]
        assertContainsFields(self, action.start_message, {
            'predicate': predicate,
        })
        assertContainsFields(self, action.end_message, {
            'action_status': 'succeeded',
            'result': result,
        })
예제 #24
0
async def test_blueprint_stop_failure(bootsteps_graph,
                                      mock_execution_order_strategy_class,
                                      logger):
    mock_step1 = create_mock_step("step1")
    mock_step2 = create_start_stop_mock_step("step2")
    mock_step3 = create_mock_step("step3")
    mock_step4 = create_start_stop_mock_step("step4",
                                             mock_class=TrioCoroutineMock)
    mock_step4.stop.side_effect = expected_exception = RuntimeError(
        "Expected Failure")
    mock_step5 = create_mock_step("step5")
    mock_step6 = create_mock_step("step6",
                                  spec=AsyncStep,
                                  mock_class=TrioCoroutineMock)

    # We're using a parent mock simply to record the order of calls to different
    # steps
    m = Mock()
    m.attach_mock(mock_step1, "mock_step1")
    m.attach_mock(mock_step2, "mock_step2")
    m.attach_mock(mock_step3, "mock_step3")
    m.attach_mock(mock_step4, "mock_step4")
    m.attach_mock(mock_step5, "mock_step5")
    m.attach_mock(mock_step6, "mock_step6")

    expected_execution_order = [
        [m.mock_step1, m.mock_step2],
        [m.mock_step3, m.mock_step4, m.mock_step5],
        [m.mock_step6],
    ]
    mock_iterator = MagicMock()
    mock_iterator.__iter__.return_value = expected_execution_order
    reversed_func = Mock(return_value=reversed(expected_execution_order))
    mock_iterator.__reversed__ = reversed_func
    mock_execution_order_strategy_class.return_value = mock_iterator

    blueprint = Blueprint(
        bootsteps_graph,
        name="Test",
        execution_order_strategy_class=mock_execution_order_strategy_class,
    )

    with pytest.raises(RuntimeError):
        async with trio.open_nursery() as nursery:
            nursery.start_soon(blueprint.stop)

    with trio.fail_after(1):
        assert (await blueprint.state_changes_receive_channel.receive() ==
                BlueprintState.TERMINATING)

    with trio.fail_after(1):
        assert await blueprint.state_changes_receive_channel.receive() == (
            BlueprintState.FAILED,
            expected_exception,
        )

    mock_execution_order_strategy_class.assert_called_once_with(
        blueprint._steps)

    assert_parallelized_steps_are_in_order(m.method_calls,
                                           [[call.mock_step4.stop()]])

    mock_step1.assert_not_called()
    mock_step2.stop.assert_not_called()
    mock_step3.assert_not_called()
    mock_step5.assert_not_called()
    mock_step6.assert_not_called()

    logged_actions = LoggedAction.of_type(logger.messages,
                                          "bootsteps:blueprint:stop")
    assert len(logged_actions) == 1
    logged_action = logged_actions[0]
    assert_log_message_field_equals(logged_action.start_message, "name",
                                    blueprint.name)
    assert_logged_action_failed(logged_action)

    messages = LoggedMessage.of_type(logger.messages,
                                     "bootsteps:blueprint:next_bootsteps")
    assert len(messages) == 1, messages

    assert_log_message_field_equals(messages[0].message, "name",
                                    blueprint.name)
    assert_log_message_field_equals(messages[0].message, "next_bootsteps",
                                    [m.mock_step4])

    logged_actions = LoggedAction.of_type(
        logger.messages, "bootsteps:blueprint:executing_bootstep")
    assert len(logged_actions) == 1

    start_messages = [
        logged_action.start_message for logged_action in logged_actions
    ]

    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step4.stop)

    assert_logged_action_failed(logged_actions[0])
예제 #25
0
async def test_blueprint_start(bootsteps_graph,
                               mock_execution_order_strategy_class, logger):
    mock_step1 = create_mock_step("step1")
    mock_step2 = create_start_stop_mock_step("step2")
    mock_step3 = create_mock_step("step3")
    mock_step4 = create_start_stop_mock_step("step4",
                                             mock_class=TrioCoroutineMock)
    mock_step5 = create_mock_step("step5")
    mock_step6 = create_mock_step("step6",
                                  spec=AsyncStep,
                                  mock_class=TrioCoroutineMock)

    # We're using a parent mock simply to record the order of calls to different
    # steps
    m = Mock()
    m.attach_mock(mock_step1, "mock_step1")
    m.attach_mock(mock_step2, "mock_step2")
    m.attach_mock(mock_step3, "mock_step3")
    m.attach_mock(mock_step4, "mock_step4")
    m.attach_mock(mock_step5, "mock_step5")
    m.attach_mock(mock_step6, "mock_step6")

    expected_execution_order = [
        [m.mock_step1, m.mock_step2],
        [m.mock_step3, m.mock_step4, m.mock_step5],
        [m.mock_step6],
    ]
    mock_iterator = MagicMock()
    mock_iterator.__iter__.return_value = expected_execution_order
    mock_execution_order_strategy_class.return_value = mock_iterator

    blueprint = Blueprint(
        bootsteps_graph,
        name="Test",
        execution_order_strategy_class=mock_execution_order_strategy_class,
    )

    async with trio.open_nursery() as nursery:
        nursery.start_soon(blueprint.start)

        with trio.fail_after(1):
            assert (await blueprint.state_changes_receive_channel.receive() ==
                    BlueprintState.RUNNING)
        with trio.fail_after(1):
            assert (await blueprint.state_changes_receive_channel.receive() ==
                    BlueprintState.COMPLETED)

    mock_execution_order_strategy_class.assert_called_once_with(
        blueprint._steps)

    assert_parallelized_steps_are_in_order(
        m.method_calls,
        [
            [call.mock_step1(), call.mock_step2.start()],
            [call.mock_step3(),
             call.mock_step4.start(),
             call.mock_step5()],
            [call.mock_step6()],
        ],
    )

    mock_step6.assert_awaited_once_with()
    mock_step4.start.assert_awaited_once_with()

    logged_actions = LoggedAction.of_type(logger.messages,
                                          "bootsteps:blueprint:start")
    assert len(logged_actions) == 1
    logged_action = logged_actions[0]
    assert_log_message_field_equals(logged_action.start_message, "name",
                                    blueprint.name)
    assert_logged_action_succeeded(logged_action)

    messages = LoggedMessage.of_type(logger.messages,
                                     "bootsteps:blueprint:next_bootsteps")
    assert len(messages) == 3

    assert_log_message_field_equals(messages[0].message, "name",
                                    blueprint.name)
    assert_log_message_field_equals(messages[0].message, "next_bootsteps",
                                    [m.mock_step1, m.mock_step2])

    assert_log_message_field_equals(messages[1].message, "name",
                                    blueprint.name)
    assert_log_message_field_equals(
        messages[1].message,
        "next_bootsteps",
        [m.mock_step3, m.mock_step4, m.mock_step5],
    )

    assert_log_message_field_equals(messages[2].message, "name",
                                    blueprint.name)
    assert_log_message_field_equals(messages[2].message, "next_bootsteps",
                                    [m.mock_step6])

    logged_actions = LoggedAction.of_type(
        logger.messages, "bootsteps:blueprint:executing_bootstep")
    assert len(logged_actions) == 6

    start_messages = [
        logged_action.start_message for logged_action in logged_actions
    ]

    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step1)
    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step2.start)
    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step3)
    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step4.start)
    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step5)
    assert_field_equals_in_any_message(start_messages, "bootstep",
                                       m.mock_step6)

    assert_logged_action_succeeded(logged_actions[0])
    assert_logged_action_succeeded(logged_actions[1])
    assert_logged_action_succeeded(logged_actions[2])
    assert_logged_action_succeeded(logged_actions[3])
    assert_logged_action_succeeded(logged_actions[4])
    assert_logged_action_succeeded(logged_actions[5])