Пример #1
0
    def __init__(self, parent, fixture_test_case_data):
        """
        Construct a test item.

        :param parent: The parent collector
        :type parent: ServicePlanTestCaseCollector
        :param fixture_test_case_data: The test case data
        :type fixture_test_case_data: FixtureTestCaseData
        """
        test_name = 'test__{fixture}__{test}'.format(
            fixture=fixture_test_case_data.fixture_name,
            test=fixture_test_case_data.name,
        )

        # First, we have to give the test plan test case class a method with this name, otherwise the TestCase class
        # cannot be instantiated. However, this should never be called, because the plugin overrides it.
        def fake_test(*_, **__):
            raise TypeError('The incorrect test method was called')

        fake_test.__doc__ = fixture_test_case_data.description
        if hasattr(parent.obj, test_name):
            # Lazy importing ensures that pytest-cov loads up coverage before this plugin loads other classes in PySOA
            from pysoa.test.plan.errors import StatusError
            raise StatusError(
                'Duplicate test name "{name}" in fixture "{fixture}"'.format(
                    name=fixture_test_case_data.name,
                    fixture=fixture_test_case_data.fixture_file), )
        setattr(parent.obj, test_name, fake_test)

        # Next we call super
        super(ServicePlanTestCaseTestFunction, self).__init__(name=test_name,
                                                              parent=parent)

        # Finally, we do some magic to trick PyTest into accepting and displaying the actual location of the test (the
        # fixture file and the line in that file) instead of the PySOA test plan parsing code.
        self._location = (
            self.session.fspath.bestrelpath(
                py.path.local(fixture_test_case_data.fixture_file)),
            fixture_test_case_data.line_number,
            self.location[2],
        )
        self.fspath = py.path.local(fixture_test_case_data.fixture_file)
        self._nodeid = '::'.join(
            self.nodeid.split('::', 2)[:2] +
            [fixture_test_case_data.fixture_name, fixture_test_case_data.name
             ], )

        self.fixture_test_case_data = fixture_test_case_data

        # Copy any class-level PyTest markers from the ServicePlanTestCase class to each fixture test case
        # This allows things like pytest.mark.skip[if], pytest.mark.django_db, etc. to work
        for mark in _get_unpacked_marks(parent.obj):
            mark_copy = getattr(MARK_GEN, mark.name)(*mark.args, **mark.kwargs)
            self.add_marker(mark_copy)

            if mark.name == 'skip' or (mark.name == 'skipif' and mark.args
                                       and mark.args[0]):
                PLUGIN_STATISTICS['fixture_tests_skipped'] += 1
Пример #2
0
    def __init__(self, parent, fixture_test_case_data):
        # type: (ServicePlanTestInstanceCollector, FixtureTestCaseData) -> None
        """
        Construct a test item.

        :param parent: The parent collector
        :param fixture_test_case_data: The test case data
        """
        cls = parent.parent.obj

        # First we construct the test method and attach it to the test class
        test_name = 'plan__{fixture}__{test}'.format(
            fixture=fixture_test_case_data.fixture_name,
            test=fixture_test_case_data.name,
        )
        if hasattr(cls, test_name):
            raise StatusError(
                'Duplicate test name "{name}" in fixture "{fixture}"'.format(
                    name=fixture_test_case_data.name,
                    fixture=fixture_test_case_data.fixture_file), )
        fixture_test_case_data.callable.__doc__ = fixture_test_case_data.description
        setattr(cls, test_name, fixture_test_case_data.callable)

        # Next we call super
        super(ServicePlanTestCaseTestFunction, self).__init__(name=test_name,
                                                              parent=parent)

        # Thirdly, we do some magic to trick PyTest into accepting and displaying the actual location of the test (the
        # fixture file and the line in that file) instead of the PySOA test plan parsing code.
        self._location = (
            self.session.fspath.bestrelpath(
                py.path.local(fixture_test_case_data.fixture_file)),
            fixture_test_case_data.line_number,
            self.location[2],
        )
        self.fspath = py.path.local(fixture_test_case_data.fixture_file)
        self._nodeid = '::'.join(
            self.nodeid.split('::', 2)[:2] +
            [fixture_test_case_data.fixture_name, fixture_test_case_data.name
             ], )

        self.fixture_test_case_data = fixture_test_case_data

        # Finally, copy any class-level PyTest markers from the ServicePlanTestCase class to each fixture test case
        # This allows things like pytest.mark.skip[if], pytest.mark.django_db, etc. to work
        skipped = False
        for mark in _get_unpacked_marks(cls):
            mark_copy = getattr(MARK_GEN, mark.name)(*mark.args, **mark.kwargs)
            self.add_marker(mark_copy)

            if mark.name == 'skip' or (mark.name == 'skipif' and mark.args
                                       and mark.args[0]):
                skipped = True

        if not skipped and fixture_test_case_data.skip:
            self.add_marker(
                pytest.mark.skip(reason=fixture_test_case_data.skip))
Пример #3
0
def substitute_variables(data, *sources):
    # type: (Union[MutableMapping, List], *Union[Mapping, List, Tuple, AbstractSet]) -> None
    """
    Overlay [[NAME]] values with values from sources, if possible.
    """
    for path in get_all_paths(data):
        try:
            value = path_get(data, path)
        except (KeyError, IndexError):
            continue
        if not value:
            continue
        if not isinstance(value, six.text_type):
            continue

        replacements = [{
            'token': m[0],
            'full_path': m[1],
            'action': m[2],
            'action_path': m[3] if m[2] else None
        } for m in VARIABLE_SUBSTITUTION_RE.findall(value)]
        if not replacements:
            continue

        for replacement in replacements:
            find_path = replacement['full_path']
            if replacement['action']:
                potential_action_name = replacement['action'].lower()
                for source in sources:
                    if potential_action_name in source:
                        # `action.#` paths don't denote a sublist, unlike most path expressions ... instead, the
                        # entire `action.#` value is a key in a dict, so we need to escape it. The result is
                        # `{action.#}.rest.of.path`.
                        find_path = '{{{}}}{}'.format(
                            potential_action_name, replacement['action_path'])

            try:
                replace_with = _find_path_in_sources(find_path, *sources)
            except KeyError:
                raise StatusError(
                    'Could not find value {path} for {replacement} in sources {sources}'
                    .format(
                        path=find_path,
                        replacement=replacement['token'],
                        sources=sources,
                    ))

            if value == replacement['token']:
                # preserve the type if this is the only replacement in the value
                value = replace_with
            else:
                value = value.replace(replacement['token'],
                                      six.text_type(replace_with))

        path_put(data, path, value)
Пример #4
0
        def test_function(self, *args, **kwargs):
            """
            This guy does the actual work of running a test case, and is invoked by PyTest when the time comes.

            :param self: The test case instance
            :type self: ServicePlanTestCase
            """

            # noinspection PyUnusedLocal
            # This instructs the traceback manipulator that this frame belongs to test_function, which is simpler than
            # having it analyze the code path details to determine the frame location.
            _test_function_frame = True  # noqa F841

            if not hasattr(self.__class__, '_test_fixture_setup_called'):
                self.__class__._test_fixture_setup_called = {}
            if not hasattr(self.__class__, '_test_fixture_setup_succeeded'):
                self.__class__._test_fixture_setup_succeeded = {}

            if not self._test_fixture_setup_called.get(fixture_name, False):
                # If this is the first test in the fixture, we need to set up the fixture
                self._test_fixture_setup_called[fixture_name] = self, test_fixture
                self.set_up_test_fixture(test_fixture)
                self._run_directive_hook('set_up_test_fixture', test_fixture)
                # After the fixture has set up without error, we note this so that all fixture tests can run
                self._test_fixture_setup_succeeded[fixture_name] = True

            if not self._test_fixture_setup_succeeded.get(fixture_name, False):
                # If the fixture was not successfully set up, then fixture setup must have failed on the first test, so
                # all remaining tests in this fixture are also invalid.
                raise StatusError('Test fixture {} not set up'.format(fixture_name))

            outer_exception = False
            try:  # LABEL: 1
                # First, we call the standard TestCase setUp, which we have taken over
                self.setUp()

                # Next, we call the fixture test case setup on the class and on all directives
                self.set_up_test_case(test_case, test_fixture)
                self._run_directive_hook('set_up_test_case', test_case, test_fixture)

                try:  # LABEL: 2
                    # Now we can actually run the test!
                    self._run_test_case(test_case, test_fixture, test_fixture_results, *args, **kwargs)
                except BaseException as e:
                    # We're only catching (everything) so that we can record which error happened in TRY 1
                    outer_exception = e
                    raise
                finally:
                    try:  # LABEL: 3
                        # Now we need to call the fixture test case teardown on the class and on all directives
                        self._run_directive_hook('tear_down_test_case', test_case, test_fixture)
                        self.tear_down_test_case(test_case, test_fixture)
                    except KeyboardInterrupt:
                        if outer_exception:
                            # If an error happened in TRY 2, raise it instead of the interrupt so we don't mask it
                            raise outer_exception
                        raise
                    except BaseException as e:
                        if not outer_exception:
                            raise  # If an error did not happen in TRY 2, just raise the tear-down error
                        self.addError(self, sys.exc_info())  # Otherwise, record the tear-down error so we don't mask
            except BaseException as e:
                outer_exception = e
                raise
            finally:
                try:  # LABEL: 4
                    # Almost done, we call the standard TestCase tearDown, which we have taken over
                    self.tearDown()
                except KeyboardInterrupt:
                    if outer_exception:
                        # If an error happened in TRY 1 - 3, raise it instead of the interrupt so we don't mask it
                        raise outer_exception
                    raise
                except BaseException as e:
                    if not outer_exception:
                        outer_exception = e
                        raise  # If an error did not happen in TRY 1 - 3, just raise the tear-down error
                    self.addError(self, sys.exc_info())  # Otherwise, record the tear-down error so we don't mask
                finally:
                    if test_function._last_fixture_test:
                        # If this is the last fixture test case, we need to assert and clean up the fixture
                        try:  # LABEL: 5
                            self._run_directive_hook('assert_test_fixture_results', test_fixture_results, test_fixture)
                        except KeyboardInterrupt:
                            if outer_exception:
                                # If an error happened in TRY 1 - 4, raise it instead of the interrupt so we don't mask
                                raise outer_exception
                            raise
                        except self.failureException as e:
                            # If the tear-down asserts raised an assertion error
                            if not outer_exception:
                                outer_exception = e
                                raise  # If an error did not happen in TRY 1 - 4, just raise the assertion error
                            self.addFailure(self, sys.exc_info())  # Otherwise, record the assertion error so no mask
                        except BaseException as e:
                            if not outer_exception:
                                outer_exception = e
                                raise  # If an error did not happen in TRY 1 - 4, just raise the on-assert error
                            self.addError(self, sys.exc_info())  # Otherwise, record the tear-down error so no mask
                        finally:
                            # noinspection PyBroadException
                            try:  # LABEL: 6
                                self._test_fixture_setup_succeeded[fixture_name] = False
                                self._test_fixture_setup_called[fixture_name] = False
                                self._run_directive_hook('tear_down_test_fixture', test_fixture)
                                self.tear_down_test_fixture(test_fixture)
                            except KeyboardInterrupt:
                                if outer_exception:
                                    # If an error happened in TRY 1 - 5, raise it instead of the interrupt so no mask
                                    raise outer_exception
                                raise
                            except BaseException:
                                if not outer_exception:
                                    raise  # If an error did not happen in TRY 1 - 5, just raise the tear-down error
                                self.addError(self, sys.exc_info())  # Otherwise, record the tear-down error so no mask