Skip to content

Commit

Permalink
- remove some crufty old testing options
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
zzzeek committed Sep 15, 2014
1 parent 7fd3f05 commit d7498cf
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 97 deletions.
5 changes: 5 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[run]
include=lib/sqlalchemy/*

[report]
omit=lib/sqlalchemy/testing/*
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
/doc/build/output/
/dogpile_data/
*.orig
*,cover
/.tox
.venv
*.egg-info
.coverage
coverage.xml
.*,cover
*.class
*.so
Expand Down
44 changes: 44 additions & 0 deletions lib/sqlalchemy/testing/plugin/bootstrap.py
Original file line number Diff line number Diff line change
@@ -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
32 changes: 16 additions & 16 deletions lib/sqlalchemy/testing/plugin/noseplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,21 @@
"""

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

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):
Expand Down Expand Up @@ -58,10 +56,10 @@ def configure(self, options, conf):

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):
Expand All @@ -72,19 +70,21 @@ def wantFunction(self, fn):

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)
Expand Down
56 changes: 10 additions & 46 deletions lib/sqlalchemy/testing/plugin/plugin_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
else:
import ConfigParser as configparser

FOLLOWER_IDENT = None

# late imports
fixtures = None
engines = None
Expand Down Expand Up @@ -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 - "
Expand All @@ -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.")
Expand All @@ -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_):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -233,12 +223,6 @@ def _setup_options(opt, file_config):
options = opt


@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:
Expand All @@ -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)
Expand All @@ -273,19 +257,12 @@ 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):

Expand Down Expand Up @@ -368,19 +345,6 @@ def _prep_testing_database(options, file_config):
schema=enum['schema'])))


@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:
Expand Down
14 changes: 11 additions & 3 deletions lib/sqlalchemy/testing/plugin/pytestplugin.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -54,11 +62,11 @@ def pytest_configure_node(node):
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"])


Expand Down
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
2 changes: 1 addition & 1 deletion lib/sqlalchemy/testing/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"""

from sqlalchemy.testing.plugin.noseplugin import NoseSQLAlchemy
from .plugin.noseplugin import NoseSQLAlchemy

import nose

Expand Down
29 changes: 16 additions & 13 deletions sqla_nose.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@
"""
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()])
20 changes: 17 additions & 3 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@
"""
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
Loading

0 comments on commit d7498cf

Please sign in to comment.