diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-09-14 21:41:13 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-09-14 21:41:13 -0400 |
commit | d7498cf4adb8ef9f35d1efa6dc747ac9eb489e60 (patch) | |
tree | de69b7ec88fef69c36704d2a7b06b1a0ffe0f17b | |
parent | 7fd3f05805a7cf1ff1fa29ffcf375c4eea6627ba (diff) | |
download | sqlalchemy-d7498cf4adb8ef9f35d1efa6dc747ac9eb489e60.tar.gz |
- remove some crufty old testing options
- reestablish the "bootstrap" system of loading the test runners
in testing/plugin; using the updated approach we just came up with for
alembic. Coverage should be fixed now when running either py.test or
nose. fixes #3196
- upgrade tox.ini and start using a .coveragerc file
-rw-r--r-- | .coveragerc | 5 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/plugin/bootstrap.py | 44 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/plugin/noseplugin.py | 32 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/plugin/plugin_base.py | 56 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/plugin/pytestplugin.py | 14 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/provision.py (renamed from lib/sqlalchemy/testing/plugin/provision.py) | 6 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/runner.py | 2 | ||||
-rwxr-xr-x | sqla_nose.py | 29 | ||||
-rwxr-xr-x | test/conftest.py | 20 | ||||
-rw-r--r-- | tox.ini | 24 |
11 files changed, 137 insertions, 97 deletions
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..5d6c2bdc4 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +include=lib/sqlalchemy/* + +[report] +omit=lib/sqlalchemy/testing/*
\ No newline at end of file diff --git a/.gitignore b/.gitignore index c22e53c39..55066f843 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,12 @@ /doc/build/output/ /dogpile_data/ *.orig +*,cover /.tox .venv *.egg-info .coverage +coverage.xml .*,cover *.class *.so diff --git a/lib/sqlalchemy/testing/plugin/bootstrap.py b/lib/sqlalchemy/testing/plugin/bootstrap.py new file mode 100644 index 000000000..497fcb7e5 --- /dev/null +++ b/lib/sqlalchemy/testing/plugin/bootstrap.py @@ -0,0 +1,44 @@ +""" +Bootstrapper for nose/pytest plugins. + +The entire rationale for this system is to get the modules in plugin/ +imported without importing all of the supporting library, so that we can +set up things for testing before coverage starts. + +The rationale for all of plugin/ being *in* the supporting library in the +first place is so that the testing and plugin suite is available to other +libraries, mainly external SQLAlchemy and Alembic dialects, to make use +of the same test environment and standard suites available to +SQLAlchemy/Alembic themselves without the need to ship/install a separate +package outside of SQLAlchemy. + +NOTE: copied/adapted from SQLAlchemy master for backwards compatibility; +this should be removable when Alembic targets SQLAlchemy 1.0.0. + +""" + +import os +import sys + +bootstrap_file = locals()['bootstrap_file'] +to_bootstrap = locals()['to_bootstrap'] + + +def load_file_as_module(name): + path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name) + if sys.version_info >= (3, 3): + from importlib import machinery + mod = machinery.SourceFileLoader(name, path).load_module() + else: + import imp + mod = imp.load_source(name, path) + return mod + +if to_bootstrap == "pytest": + sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") + sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin") +elif to_bootstrap == "nose": + sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base") + sys.modules["sqla_noseplugin"] = load_file_as_module("noseplugin") +else: + raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa diff --git a/lib/sqlalchemy/testing/plugin/noseplugin.py b/lib/sqlalchemy/testing/plugin/noseplugin.py index 6ef539142..eb4a3b258 100644 --- a/lib/sqlalchemy/testing/plugin/noseplugin.py +++ b/lib/sqlalchemy/testing/plugin/noseplugin.py @@ -12,6 +12,14 @@ way (e.g. as a package-less import). """ +try: + # installed by bootstrap.py + import sqla_plugin_base as plugin_base +except ImportError: + # assume we're a package, use traditional import + from . import plugin_base + + import os import sys @@ -19,16 +27,6 @@ from nose.plugins import Plugin fixtures = None py3k = sys.version_info >= (3, 0) -# no package imports yet! this prevents us from tripping coverage -# too soon. -path = os.path.join(os.path.dirname(__file__), "plugin_base.py") -if sys.version_info >= (3, 3): - from importlib import machinery - plugin_base = machinery.SourceFileLoader( - "plugin_base", path).load_module() -else: - import imp - plugin_base = imp.load_source("plugin_base", path) class NoseSQLAlchemy(Plugin): @@ -58,10 +56,10 @@ class NoseSQLAlchemy(Plugin): plugin_base.set_coverage_flag(options.enable_plugin_coverage) + def begin(self): global fixtures - from sqlalchemy.testing import fixtures + from sqlalchemy.testing import fixtures # noqa - def begin(self): plugin_base.post_begin() def describeTest(self, test): @@ -72,19 +70,21 @@ class NoseSQLAlchemy(Plugin): def wantMethod(self, fn): if py3k: + if not hasattr(fn.__self__, 'cls'): + return False cls = fn.__self__.cls else: cls = fn.im_class - print "METH:", fn, "CLS:", cls return plugin_base.want_method(cls, fn) def wantClass(self, cls): return plugin_base.want_class(cls) def beforeTest(self, test): - plugin_base.before_test(test, - test.test.cls.__module__, - test.test.cls, test.test.method.__name__) + plugin_base.before_test( + test, + test.test.cls.__module__, + test.test.cls, test.test.method.__name__) def afterTest(self, test): plugin_base.after_test(test) diff --git a/lib/sqlalchemy/testing/plugin/plugin_base.py b/lib/sqlalchemy/testing/plugin/plugin_base.py index 7ba31d3e3..6696427dc 100644 --- a/lib/sqlalchemy/testing/plugin/plugin_base.py +++ b/lib/sqlalchemy/testing/plugin/plugin_base.py @@ -31,8 +31,6 @@ if py3k: else: import ConfigParser as configparser -FOLLOWER_IDENT = None - # late imports fixtures = None engines = None @@ -72,8 +70,6 @@ def setup_options(make_option): help="Drop all tables in the target database first") make_option("--backend-only", action="store_true", dest="backend_only", help="Run only tests marked with __backend__") - make_option("--mockpool", action="store_true", dest="mockpool", - help="Use mock pool (asserts only one connection used)") make_option("--low-connections", action="store_true", dest="low_connections", help="Use a low number of distinct connections - " @@ -95,14 +91,6 @@ def setup_options(make_option): make_option("--exclude-tag", action="callback", callback=_exclude_tag, type="string", help="Exclude tests with tag <tag>") - make_option("--serverside", action="store_true", - help="Turn on server side cursors for PG") - make_option("--mysql-engine", action="store", - dest="mysql_engine", default=None, - help="Use the specified MySQL storage engine for all tables, " - "default is a db-default/InnoDB combo.") - make_option("--tableopts", action="append", dest="tableopts", default=[], - help="Add a dialect-specific table option, key=value") make_option("--write-profiles", action="store_true", dest="write_profiles", default=False, help="Write/update profiling data.") @@ -115,8 +103,8 @@ def configure_follower(follower_ident): database creation. """ - global FOLLOWER_IDENT - FOLLOWER_IDENT = follower_ident + from sqlalchemy.testing import provision + provision.FOLLOWER_IDENT = follower_ident def memoize_important_follower_config(dict_): @@ -177,12 +165,14 @@ def post_begin(): global util, fixtures, engines, exclusions, \ assertions, warnings, profiling,\ config, testing - from sqlalchemy import testing - from sqlalchemy.testing import fixtures, engines, exclusions, \ - assertions, warnings, profiling, config - from sqlalchemy import util + from sqlalchemy import testing # noqa + from sqlalchemy.testing import fixtures, engines, exclusions # noqa + from sqlalchemy.testing import assertions, warnings, profiling # noqa + from sqlalchemy.testing import config # noqa + from sqlalchemy import util # noqa warnings.setup_filters() + def _log(opt_str, value, parser): global logging if not logging: @@ -234,12 +224,6 @@ def _setup_options(opt, file_config): @pre -def _server_side_cursors(options, file_config): - if options.serverside: - db_opts['server_side_cursors'] = True - - -@pre def _monkeypatch_cdecimal(options, file_config): if options.cdecimal: import cdecimal @@ -250,7 +234,7 @@ def _monkeypatch_cdecimal(options, file_config): def _engine_uri(options, file_config): from sqlalchemy.testing import config from sqlalchemy import testing - from sqlalchemy.testing.plugin import provision + from sqlalchemy.testing import provision if options.dburi: db_urls = list(options.dburi) @@ -273,20 +257,13 @@ def _engine_uri(options, file_config): for db_url in db_urls: cfg = provision.setup_config( - db_url, db_opts, options, file_config, FOLLOWER_IDENT) + db_url, db_opts, options, file_config, provision.FOLLOWER_IDENT) if not config._current: cfg.set_as_current(cfg, testing) @post -def _engine_pool(options, file_config): - if options.mockpool: - from sqlalchemy import pool - db_opts['poolclass'] = pool.AssertionPool - - -@post def _requirements(options, file_config): requirement_cls = file_config.get('sqla_testing', "requirement_cls") @@ -369,19 +346,6 @@ def _prep_testing_database(options, file_config): @post -def _set_table_options(options, file_config): - from sqlalchemy.testing import schema - - table_options = schema.table_options - for spec in options.tableopts: - key, value = spec.split('=') - table_options[key] = value - - if options.mysql_engine: - table_options['mysql_engine'] = options.mysql_engine - - -@post def _reverse_topological(options, file_config): if options.reversetop: from sqlalchemy.orm.util import randomize_unitofwork diff --git a/lib/sqlalchemy/testing/plugin/pytestplugin.py b/lib/sqlalchemy/testing/plugin/pytestplugin.py index 005942913..4bbc8ed9a 100644 --- a/lib/sqlalchemy/testing/plugin/pytestplugin.py +++ b/lib/sqlalchemy/testing/plugin/pytestplugin.py @@ -1,7 +1,13 @@ +try: + # installed by bootstrap.py + import sqla_plugin_base as plugin_base +except ImportError: + # assume we're a package, use traditional import + from . import plugin_base + import pytest import argparse import inspect -from . import plugin_base import collections import itertools @@ -42,6 +48,8 @@ def pytest_configure(config): plugin_base.set_coverage_flag(bool(getattr(config.option, "cov_source", False))) + +def pytest_sessionstart(session): plugin_base.post_begin() if has_xdist: @@ -54,11 +62,11 @@ if has_xdist: plugin_base.memoize_important_follower_config(node.slaveinput) node.slaveinput["follower_ident"] = "test_%s" % next(_follower_count) - from . import provision + from sqlalchemy.testing import provision provision.create_follower_db(node.slaveinput["follower_ident"]) def pytest_testnodedown(node, error): - from . import provision + from sqlalchemy.testing import provision provision.drop_follower_db(node.slaveinput["follower_ident"]) diff --git a/lib/sqlalchemy/testing/plugin/provision.py b/lib/sqlalchemy/testing/provision.py index c6b9030f5..0bcdad959 100644 --- a/lib/sqlalchemy/testing/plugin/provision.py +++ b/lib/sqlalchemy/testing/provision.py @@ -1,8 +1,10 @@ from sqlalchemy.engine import url as sa_url from sqlalchemy import text from sqlalchemy.util import compat -from .. import config, engines -import os +from . import config, engines + + +FOLLOWER_IDENT = None class register(object): diff --git a/lib/sqlalchemy/testing/runner.py b/lib/sqlalchemy/testing/runner.py index df254520b..23d7a0a91 100644 --- a/lib/sqlalchemy/testing/runner.py +++ b/lib/sqlalchemy/testing/runner.py @@ -30,7 +30,7 @@ SQLAlchemy itself is possible. """ -from sqlalchemy.testing.plugin.noseplugin import NoseSQLAlchemy +from .plugin.noseplugin import NoseSQLAlchemy import nose diff --git a/sqla_nose.py b/sqla_nose.py index f89a1dce0..b977f4bf5 100755 --- a/sqla_nose.py +++ b/sqla_nose.py @@ -8,22 +8,25 @@ installs SQLAlchemy's testing plugin into the local environment. """ import sys import nose -import warnings +import os -from os import path for pth in ['./lib']: - sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth)) + sys.path.insert( + 0, os.path.join(os.path.dirname(os.path.abspath(__file__)), pth)) -# installing without importing SQLAlchemy, so that coverage includes -# SQLAlchemy itself. -path = "lib/sqlalchemy/testing/plugin/noseplugin.py" -if sys.version_info >= (3, 3): - from importlib import machinery - noseplugin = machinery.SourceFileLoader("noseplugin", path).load_module() -else: - import imp - noseplugin = imp.load_source("noseplugin", path) +# use bootstrapping so that test plugins are loaded +# without touching the main library before coverage starts +bootstrap_file = os.path.join( + os.path.dirname(__file__), "lib", "sqlalchemy", + "testing", "plugin", "bootstrap.py" +) +with open(bootstrap_file) as f: + code = compile(f.read(), "bootstrap.py", 'exec') + to_bootstrap = "nose" + exec(code, globals(), locals()) -nose.main(addplugins=[noseplugin.NoseSQLAlchemy()]) + +from noseplugin import NoseSQLAlchemy +nose.main(addplugins=[NoseSQLAlchemy()]) diff --git a/test/conftest.py b/test/conftest.py index 1dd442309..c697085ee 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -7,9 +7,23 @@ installs SQLAlchemy's testing plugin into the local environment. """ import sys +import os -from os import path for pth in ['../lib']: - sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth)) + sys.path.insert( + 0, + os.path.join(os.path.dirname(os.path.abspath(__file__)), pth)) -from sqlalchemy.testing.plugin.pytestplugin import * + +# use bootstrapping so that test plugins are loaded +# without touching the main library before coverage starts +bootstrap_file = os.path.join( + os.path.dirname(__file__), "..", "lib", "sqlalchemy", + "testing", "plugin", "bootstrap.py" +) + +with open(bootstrap_file) as f: + code = compile(f.read(), "bootstrap.py", 'exec') + to_bootstrap = "pytest" + exec(code, globals(), locals()) + from pytestplugin import * # noqa @@ -1,18 +1,15 @@ [tox] -envlist = full +envlist = full,py26,py27,py33,py34 [testenv] deps=pytest - flake8 - coverage mock sitepackages=True usedevelop=True commands= - python -m pytest {posargs} -envdir=pytest + python -m pytest -n4 {posargs} [testenv:full] @@ -21,21 +18,22 @@ envdir=pytest setenv= DISABLE_SQLALCHEMY_CEXT=1 +# see also .coveragerc +deps=coverage commands= - python -m pytest \ - --cov=lib/sqlalchemy \ - --exclude-tag memory-intensive \ - --exclude-tag timing-intensive \ - -k "not aaa_profiling" \ - {posargs} - python -m coverage xml --include=lib/sqlalchemy/* + python -m pytest --cov=sqlalchemy --cov-report term --cov-report xml \ + --exclude-tag memory-intensive \ + --exclude-tag timing-intensive \ + -k "not aaa_profiling" \ + {posargs} + [testenv:pep8] commands = python -m flake8 {posargs} [flake8] - +deps=flake8 show-source = True ignore = E711,E712,E721,F841,F811 exclude=.venv,.git,.tox,dist,doc,*egg,build |