Ejemplo n.º 1
0
def pytest_runtest_setup(item):
    from sqlalchemy.testing import asyncio

    if not isinstance(item, pytest.Function):
        return

    # pytest_runtest_setup runs *before* pytest fixtures with scope="class".
    # plugin_base.start_test_class_outside_fixtures may opt to raise SkipTest
    # for the whole class and has to run things that are across all current
    # databases, so we run this outside of the pytest fixture system altogether
    # and ensure asyncio greenlet if any engines are async

    global _current_class

    if _current_class is None:
        asyncio._maybe_async_provisioning(
            plugin_base.start_test_class_outside_fixtures,
            item.parent.parent.cls,
        )
        _current_class = item.parent.parent

        def finalize():
            global _current_class
            _current_class = None

            asyncio._maybe_async_provisioning(
                plugin_base.stop_test_class_outside_fixtures,
                item.parent.parent.cls,
            )

        item.parent.parent.addfinalizer(finalize)
Ejemplo n.º 2
0
def pytest_collection_modifyitems(session, config, items):

    # look for all those classes that specify __backend__ and
    # expand them out into per-database test cases.

    # this is much easier to do within pytest_pycollect_makeitem, however
    # pytest is iterating through cls.__dict__ as makeitem is
    # called which causes a "dictionary changed size" error on py3k.
    # I'd submit a pullreq for them to turn it into a list first, but
    # it's to suit the rather odd use case here which is that we are adding
    # new classes to a module on the fly.

    from sqlalchemy.testing import asyncio

    rebuilt_items = collections.defaultdict(
        lambda: collections.defaultdict(list))

    items[:] = [
        item for item in items if isinstance(item.parent, pytest.Instance)
        and not item.parent.parent.name.startswith("_")
    ]

    test_classes = set(item.parent for item in items)

    def setup_test_classes():
        for test_class in test_classes:
            for sub_cls in plugin_base.generate_sub_tests(
                    test_class.cls, test_class.parent.module):
                if sub_cls is not test_class.cls:
                    per_cls_dict = rebuilt_items[test_class.cls]

                    # support pytest 5.4.0 and above pytest.Class.from_parent
                    ctor = getattr(pytest.Class, "from_parent", pytest.Class)
                    for inst in ctor(
                            name=sub_cls.__name__,
                            parent=test_class.parent.parent).collect():
                        for t in inst.collect():
                            per_cls_dict[t.name].append(t)

    # class requirements will sometimes need to access the DB to check
    # capabilities, so need to do this for async
    asyncio._maybe_async_provisioning(setup_test_classes)

    newitems = []
    for item in items:
        if item.parent.cls in rebuilt_items:
            newitems.extend(rebuilt_items[item.parent.cls][item.name])
        else:
            newitems.append(item)

    # seems like the functions attached to a test class aren't sorted already?
    # is that true and why's that? (when using unittest, they're sorted)
    items[:] = sorted(
        newitems,
        key=lambda item: (
            item.parent.parent.parent.name,
            item.parent.parent.name,
            item.name,
        ),
    )
Ejemplo n.º 3
0
    def pytest_testnodedown(node, error):
        from sqlalchemy.testing import provision
        from sqlalchemy.testing import asyncio

        asyncio._maybe_async_provisioning(
            provision.drop_follower_db, node.workerinput["follower_ident"]
        )
Ejemplo n.º 4
0
        def finalize():
            global _current_class, _current_report
            _current_class = None

            try:
                asyncio._maybe_async_provisioning(
                    plugin_base.stop_test_class_outside_fixtures,
                    item.parent.parent.cls,
                )
            except Exception as e:
                # in case of an exception during teardown attach the original
                # error to the exception message, otherwise it will get lost
                if _current_report.failed:
                    if not e.args:
                        e.args = ("__Original test failure__:\n" +
                                  _current_report.longreprtext, )
                    elif e.args[-1] and isinstance(e.args[-1], string_types):
                        args = list(e.args)
                        args[-1] += ("\n__Original test failure__:\n" +
                                     _current_report.longreprtext)
                        e.args = tuple(args)
                    else:
                        e.args += (
                            "__Original test failure__",
                            _current_report.longreprtext,
                        )
                raise
            finally:
                _current_report = None
Ejemplo n.º 5
0
        def finalize():
            global _current_class
            _current_class = None

            asyncio._maybe_async_provisioning(
                plugin_base.stop_test_class_outside_fixtures,
                item.parent.parent.cls,
            )
Ejemplo n.º 6
0
def pytest_sessionfinish(session):
    from sqlalchemy.testing import asyncio

    asyncio._maybe_async_provisioning(plugin_base.final_process_cleanup)

    if session.config.option.dump_pyannotate:
        from pyannotate_runtime import collect_types

        collect_types.dump_stats(session.config.option.dump_pyannotate)
Ejemplo n.º 7
0
def pytest_runtest_setup(item):
    from sqlalchemy.testing import asyncio
    from sqlalchemy.util import string_types

    if not isinstance(item, pytest.Function):
        return

    # pytest_runtest_setup runs *before* pytest fixtures with scope="class".
    # plugin_base.start_test_class_outside_fixtures may opt to raise SkipTest
    # for the whole class and has to run things that are across all current
    # databases, so we run this outside of the pytest fixture system altogether
    # and ensure asyncio greenlet if any engines are async

    global _current_class

    if _current_class is None:
        asyncio._maybe_async_provisioning(
            plugin_base.start_test_class_outside_fixtures,
            item.parent.parent.cls,
        )
        _current_class = item.parent.parent

        def finalize():
            global _current_class, _current_report
            _current_class = None

            try:
                asyncio._maybe_async_provisioning(
                    plugin_base.stop_test_class_outside_fixtures,
                    item.parent.parent.cls,
                )
            except Exception as e:
                # in case of an exception during teardown attach the original
                # error to the exception message, otherwise it will get lost
                if _current_report.failed:
                    if not e.args:
                        e.args = (
                            "__Original test failure__:\n"
                            + _current_report.longreprtext,
                        )
                    elif e.args[-1] and isinstance(e.args[-1], string_types):
                        args = list(e.args)
                        args[-1] += (
                            "\n__Original test failure__:\n"
                            + _current_report.longreprtext
                        )
                        e.args = tuple(args)
                    else:
                        e.args += (
                            "__Original test failure__",
                            _current_report.longreprtext,
                        )
                raise
            finally:
                _current_report = None

        item.parent.parent.addfinalizer(finalize)
Ejemplo n.º 8
0
    def pytest_configure_node(node):
        from sqlalchemy.testing import provision
        from sqlalchemy.testing import asyncio

        # the master for each node fills workerinput dictionary
        # which pytest-xdist will transfer to the subprocess

        plugin_base.memoize_important_follower_config(node.workerinput)

        node.workerinput["follower_ident"] = "test_%s" % uuid.uuid4().hex[0:12]

        asyncio._maybe_async_provisioning(provision.create_follower_db,
                                          node.workerinput["follower_ident"])
Ejemplo n.º 9
0
def pytest_runtest_teardown(item, nextitem):
    # runs inside of pytest function fixture scope
    # after test function runs

    from sqlalchemy.testing import asyncio

    asyncio._maybe_async(plugin_base.after_test, item)

    yield
    # this is now after all the fixture teardown have run, the class can be
    # finalized. Since pytest v7 this finalizer can no longer be added in
    # pytest_runtest_setup since the class has not yet been setup at that
    # time.
    # See https://github.com/pytest-dev/pytest/issues/9343
    global _current_class, _current_report

    if _current_class is not None and (
        # last test or a new class
        nextitem is None
        or nextitem.getparent(pytest.Class) is not _current_class
    ):
        _current_class = None

        try:
            asyncio._maybe_async_provisioning(
                plugin_base.stop_test_class_outside_fixtures, item.cls
            )
        except Exception as e:
            # in case of an exception during teardown attach the original
            # error to the exception message, otherwise it will get lost
            if _current_report.failed:
                if not e.args:
                    e.args = (
                        "__Original test failure__:\n"
                        + _current_report.longreprtext,
                    )
                elif e.args[-1] and isinstance(e.args[-1], str):
                    args = list(e.args)
                    args[-1] += (
                        "\n__Original test failure__:\n"
                        + _current_report.longreprtext
                    )
                    e.args = tuple(args)
                else:
                    e.args += (
                        "__Original test failure__",
                        _current_report.longreprtext,
                    )
            raise
        finally:
            _current_report = None
Ejemplo n.º 10
0
def pytest_collection_modifyitems(session, config, items):

    # look for all those classes that specify __backend__ and
    # expand them out into per-database test cases.

    # this is much easier to do within pytest_pycollect_makeitem, however
    # pytest is iterating through cls.__dict__ as makeitem is
    # called which causes a "dictionary changed size" error on py3k.
    # I'd submit a pullreq for them to turn it into a list first, but
    # it's to suit the rather odd use case here which is that we are adding
    # new classes to a module on the fly.

    from sqlalchemy.testing import asyncio

    rebuilt_items = collections.defaultdict(
        lambda: collections.defaultdict(list)
    )

    items[:] = [
        item
        for item in items
        if item.getparent(pytest.Class) is not None
        and not item.getparent(pytest.Class).name.startswith("_")
    ]

    test_classes = set(item.getparent(pytest.Class) for item in items)

    def collect(element):
        for inst_or_fn in element.collect():
            if isinstance(inst_or_fn, pytest.Collector):
                yield from collect(inst_or_fn)
            else:
                yield inst_or_fn

    def setup_test_classes():
        for test_class in test_classes:

            # transfer legacy __backend__ and __sparse_backend__ symbols
            # to be markers
            add_markers = set()
            if getattr(test_class.cls, "__backend__", False) or getattr(
                test_class.cls, "__only_on__", False
            ):
                add_markers = {"backend"}
            elif getattr(test_class.cls, "__sparse_backend__", False):
                add_markers = {"sparse_backend"}
            else:
                add_markers = frozenset()

            existing_markers = {
                mark.name for mark in test_class.iter_markers()
            }
            add_markers = add_markers - existing_markers
            all_markers = existing_markers.union(add_markers)

            for marker in add_markers:
                test_class.add_marker(marker)

            for sub_cls in plugin_base.generate_sub_tests(
                test_class.cls, test_class.module, all_markers
            ):
                if sub_cls is not test_class.cls:
                    per_cls_dict = rebuilt_items[test_class.cls]

                    module = test_class.getparent(pytest.Module)

                    new_cls = pytest.Class.from_parent(
                        name=sub_cls.__name__, parent=module
                    )
                    for marker in add_markers:
                        new_cls.add_marker(marker)

                    for fn in collect(new_cls):
                        per_cls_dict[fn.name].append(fn)

    # class requirements will sometimes need to access the DB to check
    # capabilities, so need to do this for async
    asyncio._maybe_async_provisioning(setup_test_classes)

    newitems = []
    for item in items:
        cls_ = item.cls
        if cls_ in rebuilt_items:
            newitems.extend(rebuilt_items[cls_][item.name])
        else:
            newitems.append(item)

    # seems like the functions attached to a test class aren't sorted already?
    # is that true and why's that? (when using unittest, they're sorted)
    items[:] = sorted(
        newitems,
        key=lambda item: (
            item.getparent(pytest.Module).name,
            item.getparent(pytest.Class).name,
            item.name,
        ),
    )
Ejemplo n.º 11
0
def class_setup(item):
    from sqlalchemy.testing import asyncio

    asyncio._maybe_async_provisioning(plugin_base.start_test_class, item.cls)