diff options
Diffstat (limited to 'lib/sqlalchemy/testing/plugin')
| -rw-r--r-- | lib/sqlalchemy/testing/plugin/plugin_base.py | 123 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/plugin/pytestplugin.py | 52 |
2 files changed, 90 insertions, 85 deletions
diff --git a/lib/sqlalchemy/testing/plugin/plugin_base.py b/lib/sqlalchemy/testing/plugin/plugin_base.py index 5a4bfe3a6..0b4451b3c 100644 --- a/lib/sqlalchemy/testing/plugin/plugin_base.py +++ b/lib/sqlalchemy/testing/plugin/plugin_base.py @@ -106,21 +106,28 @@ def setup_options(make_option): ) make_option( "--backend-only", - action="store_true", - dest="backend_only", - help="Run only tests marked with __backend__ or __sparse_backend__", + action="callback", + zeroarg_callback=_set_tag_include("backend"), + help=( + "Run only tests marked with __backend__ or __sparse_backend__; " + "this is now equivalent to the pytest -m backend mark expression" + ), ) make_option( "--nomemory", - action="store_true", - dest="nomemory", - help="Don't run memory profiling tests", + action="callback", + zeroarg_callback=_set_tag_exclude("memory_intensive"), + help="Don't run memory profiling tests; " + "this is now equivalent to the pytest -m 'not memory_intensive' " + "mark expression", ) make_option( "--notimingintensive", - action="store_true", - dest="notimingintensive", - help="Don't run timing intensive tests", + action="callback", + zeroarg_callback=_set_tag_exclude("timing_intensive"), + help="Don't run timing intensive tests; " + "this is now equivalent to the pytest -m 'not timing_intensive' " + "mark expression", ) make_option( "--profile-sort", @@ -171,26 +178,20 @@ def setup_options(make_option): help="requirements class for testing, overrides setup.cfg", ) make_option( - "--with-cdecimal", - action="store_true", - dest="cdecimal", - default=False, - help="Monkeypatch the cdecimal library into Python 'decimal' " - "for all tests", - ) - make_option( "--include-tag", action="callback", callback=_include_tag, type=str, - help="Include tests with tag <tag>", + help="Include tests with tag <tag>; " + "legacy, use pytest -m 'tag' instead", ) make_option( "--exclude-tag", action="callback", callback=_exclude_tag, type=str, - help="Exclude tests with tag <tag>", + help="Exclude tests with tag <tag>; " + "legacy, use pytest -m 'not tag' instead", ) make_option( "--write-profiles", @@ -240,15 +241,9 @@ def memoize_important_follower_config(dict_): This invokes in the parent process after normal config is set up. - This is necessary as pytest seems to not be using forking, so we - start with nothing in memory, *but* it isn't running our argparse - callables, so we have to just copy all of that over. + Hook is currently not used. """ - dict_["memoized_config"] = { - "include_tags": include_tags, - "exclude_tags": exclude_tags, - } def restore_important_follower_config(dict_): @@ -256,10 +251,9 @@ def restore_important_follower_config(dict_): This invokes in the follower process. + Hook is currently not used. + """ - global include_tags, exclude_tags - include_tags.update(dict_["memoized_config"]["include_tags"]) - exclude_tags.update(dict_["memoized_config"]["exclude_tags"]) def read_config(): @@ -322,6 +316,20 @@ def _requirements_opt(opt_str, value, parser): _setup_requirements(value) +def _set_tag_include(tag): + def _do_include_tag(opt_str, value, parser): + _include_tag(opt_str, tag, parser) + + return _do_include_tag + + +def _set_tag_exclude(tag): + def _do_exclude_tag(opt_str, value, parser): + _exclude_tag(opt_str, tag, parser) + + return _do_exclude_tag + + def _exclude_tag(opt_str, value, parser): exclude_tags.add(value.replace("-", "_")) @@ -350,26 +358,6 @@ def _setup_options(opt, file_config): options = opt -@pre -def _set_nomemory(opt, file_config): - if opt.nomemory: - exclude_tags.add("memory_intensive") - - -@pre -def _set_notimingintensive(opt, file_config): - if opt.notimingintensive: - exclude_tags.add("timing_intensive") - - -@pre -def _monkeypatch_cdecimal(options, file_config): - if options.cdecimal: - import cdecimal - - sys.modules["decimal"] = cdecimal - - @post def __ensure_cext(opt, file_config): if os.environ.get("REQUIRE_SQLALCHEMY_CEXT", "0") == "1": @@ -515,13 +503,6 @@ def want_class(name, cls): return False elif name.startswith("_"): return False - elif ( - config.options.backend_only - and not getattr(cls, "__backend__", False) - and not getattr(cls, "__sparse_backend__", False) - and not getattr(cls, "__only_on__", False) - ): - return False else: return True @@ -531,33 +512,13 @@ def want_method(cls, fn): return False elif fn.__module__ is None: return False - elif include_tags: - return ( - hasattr(cls, "__tags__") - and exclusions.tags(cls.__tags__).include_test( - include_tags, exclude_tags - ) - ) or ( - hasattr(fn, "_sa_exclusion_extend") - and fn._sa_exclusion_extend.include_test( - include_tags, exclude_tags - ) - ) - elif exclude_tags and hasattr(cls, "__tags__"): - return exclusions.tags(cls.__tags__).include_test( - include_tags, exclude_tags - ) - elif exclude_tags and hasattr(fn, "_sa_exclusion_extend"): - return fn._sa_exclusion_extend.include_test(include_tags, exclude_tags) else: return True -def generate_sub_tests(cls, module): - if getattr(cls, "__backend__", False) or getattr( - cls, "__sparse_backend__", False - ): - sparse = getattr(cls, "__sparse_backend__", False) +def generate_sub_tests(cls, module, markers): + if "backend" in markers or "sparse_backend" in markers: + sparse = "sparse_backend" in markers for cfg in _possible_configs_for_cls(cls, sparse=sparse): orig_name = cls.__name__ @@ -780,6 +741,10 @@ class FixtureFunctions(abc.ABC): def mark_base_test_class(self): raise NotImplementedError() + @abc.abstractproperty + def add_to_marker(self): + raise NotImplementedError() + _fixture_fn_class = None diff --git a/lib/sqlalchemy/testing/plugin/pytestplugin.py b/lib/sqlalchemy/testing/plugin/pytestplugin.py index 2ae6730bb..363a73ecc 100644 --- a/lib/sqlalchemy/testing/plugin/pytestplugin.py +++ b/lib/sqlalchemy/testing/plugin/pytestplugin.py @@ -69,6 +69,17 @@ def pytest_addoption(parser): def pytest_configure(config): + if plugin_base.exclude_tags or plugin_base.include_tags: + if config.option.markexpr: + raise ValueError( + "Can't combine explicit pytest marks with legacy options " + "such as --backend-only, --exclude-tags, etc. " + ) + config.option.markexpr = " and ".join( + list(plugin_base.include_tags) + + [f"not {tag}" for tag in plugin_base.exclude_tags] + ) + if config.pluginmanager.hasplugin("xdist"): config.pluginmanager.register(XDistHooks()) @@ -206,18 +217,43 @@ def pytest_collection_modifyitems(session, config, items): 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 + 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) - for fn in collect( - pytest.Class.from_parent( - name=sub_cls.__name__, parent=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 @@ -573,6 +609,10 @@ class PytestFixtureFunctions(plugin_base.FixtureFunctions): def skip_test_exception(self, *arg, **kw): return pytest.skip.Exception(*arg, **kw) + @property + def add_to_marker(self): + return pytest.mark + def mark_base_test_class(self): return pytest.mark.usefixtures( "setup_class_methods", "setup_test_methods" |
