summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/aaa_profiling/test_compiler.py2
-rw-r--r--test/aaa_profiling/test_memusage.py8
-rw-r--r--test/aaa_profiling/test_orm.py6
-rw-r--r--test/aaa_profiling/test_pool.py2
-rw-r--r--test/aaa_profiling/test_resultset.py2
-rw-r--r--test/aaa_profiling/test_zoomark.py2
-rw-r--r--test/aaa_profiling/test_zoomark_orm.py2
-rw-r--r--test/base/test_dependency.py6
-rw-r--r--test/base/test_events.py2
-rw-r--r--test/base/test_except.py2
-rw-r--r--test/base/test_utils.py8
-rw-r--r--test/bootstrap/__init__.py0
-rw-r--r--test/bootstrap/config.py173
-rw-r--r--test/bootstrap/noseplugin.py169
-rw-r--r--test/dialect/test_access.py2
-rw-r--r--test/dialect/test_firebird.py4
-rw-r--r--test/dialect/test_informix.py2
-rw-r--r--test/dialect/test_maxdb.py4
-rw-r--r--test/dialect/test_mssql.py6
-rw-r--r--test/dialect/test_mxodbc.py4
-rw-r--r--test/dialect/test_mysql.py8
-rw-r--r--test/dialect/test_oracle.py8
-rw-r--r--test/dialect/test_postgresql.py10
-rw-r--r--test/dialect/test_sqlite.py4
-rw-r--r--test/dialect/test_sybase.py2
-rw-r--r--test/engine/_base.py4
-rw-r--r--test/engine/test_bind.py8
-rw-r--r--test/engine/test_ddlevents.py10
-rw-r--r--test/engine/test_execute.py6
-rw-r--r--test/engine/test_metadata.py12
-rw-r--r--test/engine/test_parseconnect.py4
-rw-r--r--test/engine/test_pool.py6
-rw-r--r--test/engine/test_reconnect.py8
-rw-r--r--test/engine/test_reflection.py6
-rw-r--r--test/engine/test_transaction.py8
-rw-r--r--test/ex/test_examples.py2
-rw-r--r--test/ext/test_associationproxy.py6
-rw-r--r--test/ext/test_compiler.py2
-rw-r--r--test/ext/test_declarative.py8
-rw-r--r--test/ext/test_horizontal_shard.py4
-rw-r--r--test/ext/test_orderinglist.py4
-rw-r--r--test/ext/test_serializer.py8
-rw-r--r--test/ext/test_sqlsoup.py2
-rw-r--r--test/lib/__init__.py27
-rw-r--r--test/lib/assertsql.py314
-rw-r--r--test/lib/engines.py304
-rw-r--r--test/lib/entities.py83
-rw-r--r--test/lib/orm.py111
-rw-r--r--test/lib/pickleable.py75
-rw-r--r--test/lib/profiling.py221
-rw-r--r--test/lib/requires.py327
-rw-r--r--test/lib/schema.py79
-rw-r--r--test/lib/testing.py797
-rw-r--r--test/lib/util.py107
-rw-r--r--test/orm/_base.py8
-rw-r--r--test/orm/_fixtures.py6
-rw-r--r--test/orm/inheritance/test_abc_inheritance.py4
-rw-r--r--test/orm/inheritance/test_abc_polymorphic.py2
-rw-r--r--test/orm/inheritance/test_basic.py6
-rw-r--r--test/orm/inheritance/test_concrete.py10
-rw-r--r--test/orm/inheritance/test_magazine.py4
-rw-r--r--test/orm/inheritance/test_manytomany.py4
-rw-r--r--test/orm/inheritance/test_poly_linked_list.py4
-rw-r--r--test/orm/inheritance/test_polymorph.py4
-rw-r--r--test/orm/inheritance/test_polymorph2.py8
-rw-r--r--test/orm/inheritance/test_productspec.py4
-rw-r--r--test/orm/inheritance/test_query.py8
-rw-r--r--test/orm/inheritance/test_selects.py2
-rw-r--r--test/orm/inheritance/test_single.py6
-rw-r--r--test/orm/test_association.py6
-rw-r--r--test/orm/test_assorted_eager.py6
-rw-r--r--test/orm/test_attributes.py6
-rw-r--r--test/orm/test_backref_mutations.py10
-rw-r--r--test/orm/test_bind.py8
-rw-r--r--test/orm/test_cascade.py8
-rw-r--r--test/orm/test_collection.py8
-rw-r--r--test/orm/test_compile.py4
-rw-r--r--test/orm/test_cycles.py8
-rw-r--r--test/orm/test_defaults.py6
-rw-r--r--test/orm/test_deprecations.py6
-rw-r--r--test/orm/test_dynamic.py8
-rw-r--r--test/orm/test_eager_relations.py10
-rw-r--r--test/orm/test_evaluator.py8
-rw-r--r--test/orm/test_expire.py10
-rw-r--r--test/orm/test_extendedattr.py4
-rw-r--r--test/orm/test_generative.py10
-rw-r--r--test/orm/test_immediate_load.py4
-rw-r--r--test/orm/test_instrumentation.py8
-rw-r--r--test/orm/test_lazy_relations.py10
-rw-r--r--test/orm/test_load_on_fks.py6
-rw-r--r--test/orm/test_manytomany.py8
-rw-r--r--test/orm/test_mapper.py10
-rw-r--r--test/orm/test_merge.py8
-rw-r--r--test/orm/test_naturalpks.py10
-rw-r--r--test/orm/test_onetoone.py4
-rw-r--r--test/orm/test_pickled.py8
-rw-r--r--test/orm/test_query.py6
-rw-r--r--test/orm/test_relationships.py8
-rw-r--r--test/orm/test_scoping.py8
-rw-r--r--test/orm/test_selectable.py8
-rw-r--r--test/orm/test_session.py10
-rw-r--r--test/orm/test_subquery_relations.py10
-rw-r--r--test/orm/test_transaction.py6
-rw-r--r--test/orm/test_unitofwork.py14
-rw-r--r--test/orm/test_unitofworkv2.py8
-rw-r--r--test/orm/test_utils.py6
-rw-r--r--test/orm/test_versioning.py6
-rw-r--r--test/perf/insertspeed.py2
-rw-r--r--test/perf/large_flush.py2
-rw-r--r--test/perf/masscreate2.py2
-rw-r--r--test/perf/masseagerload.py2
-rw-r--r--test/perf/massload.py2
-rw-r--r--test/perf/masssave.py2
-rw-r--r--test/perf/objselectspeed.py4
-rw-r--r--test/perf/objupdatespeed.py4
-rw-r--r--test/perf/ormsession.py4
-rw-r--r--test/perf/poolload.py2
-rw-r--r--test/perf/sessions.py4
-rw-r--r--test/perf/wsgi.py2
-rw-r--r--test/sql/test_case_statement.py4
-rw-r--r--test/sql/test_columns.py4
-rw-r--r--test/sql/test_compiler.py4
-rw-r--r--test/sql/test_constraints.py10
-rw-r--r--test/sql/test_defaults.py8
-rw-r--r--test/sql/test_functions.py10
-rw-r--r--test/sql/test_generative.py4
-rw-r--r--test/sql/test_labels.py4
-rw-r--r--test/sql/test_query.py8
-rw-r--r--test/sql/test_quote.py2
-rw-r--r--test/sql/test_returning.py6
-rw-r--r--test/sql/test_rowcount.py2
-rw-r--r--test/sql/test_selectable.py4
-rw-r--r--test/sql/test_types.py12
-rw-r--r--test/sql/test_unicode.py4
-rw-r--r--test/zblog/tables.py2
-rw-r--r--test/zblog/test_zblog.py2
136 files changed, 3136 insertions, 349 deletions
diff --git a/test/aaa_profiling/test_compiler.py b/test/aaa_profiling/test_compiler.py
index 160385e95..ea33b96dc 100644
--- a/test/aaa_profiling/test_compiler.py
+++ b/test/aaa_profiling/test_compiler.py
@@ -1,5 +1,5 @@
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
class CompileTest(TestBase, AssertsExecutionResults):
diff --git a/test/aaa_profiling/test_memusage.py b/test/aaa_profiling/test_memusage.py
index 7c0979d57..f6de1fa2f 100644
--- a/test/aaa_profiling/test_memusage.py
+++ b/test/aaa_profiling/test_memusage.py
@@ -1,17 +1,17 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy.orm import mapper, relationship, create_session, \
clear_mappers, sessionmaker, class_mapper
from sqlalchemy.orm.mapper import _mapper_registry
from sqlalchemy.orm.session import _sessions
from sqlalchemy.util import jython
import operator
-from sqlalchemy.test import testing, engines
+from test.lib import testing, engines
from sqlalchemy import MetaData, Integer, String, ForeignKey, \
PickleType, create_engine, Unicode
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
import sqlalchemy as sa
from sqlalchemy.sql import column
-from sqlalchemy.test.util import gc_collect
+from test.lib.util import gc_collect
import gc
import weakref
from test.orm import _base
diff --git a/test/aaa_profiling/test_orm.py b/test/aaa_profiling/test_orm.py
index d7e7b8c00..f2edebe06 100644
--- a/test/aaa_profiling/test_orm.py
+++ b/test/aaa_profiling/test_orm.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
from sqlalchemy import exc as sa_exc, util, Integer, String, ForeignKey
from sqlalchemy.orm import exc as orm_exc, mapper, relationship, \
sessionmaker
-from sqlalchemy.test import testing, profiling
+from test.lib import testing, profiling
from test.orm import _base
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
class MergeTest(_base.MappedTest):
diff --git a/test/aaa_profiling/test_pool.py b/test/aaa_profiling/test_pool.py
index b34144feb..1df93d980 100644
--- a/test/aaa_profiling/test_pool.py
+++ b/test/aaa_profiling/test_pool.py
@@ -1,5 +1,5 @@
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy.pool import QueuePool
diff --git a/test/aaa_profiling/test_resultset.py b/test/aaa_profiling/test_resultset.py
index bd9d3ae50..9904267dc 100644
--- a/test/aaa_profiling/test_resultset.py
+++ b/test/aaa_profiling/test_resultset.py
@@ -1,5 +1,5 @@
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
NUM_FIELDS = 10
NUM_RECORDS = 1000
diff --git a/test/aaa_profiling/test_zoomark.py b/test/aaa_profiling/test_zoomark.py
index dc990e983..ec489beb1 100644
--- a/test/aaa_profiling/test_zoomark.py
+++ b/test/aaa_profiling/test_zoomark.py
@@ -7,7 +7,7 @@ import datetime
import sys
import time
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
ITERATIONS = 1
dbapi_session = engines.ReplayableSession()
metadata = None
diff --git a/test/aaa_profiling/test_zoomark_orm.py b/test/aaa_profiling/test_zoomark_orm.py
index 623ec67ba..11285f972 100644
--- a/test/aaa_profiling/test_zoomark_orm.py
+++ b/test/aaa_profiling/test_zoomark_orm.py
@@ -8,7 +8,7 @@ import sys
import time
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import *
+from test.lib import *
ITERATIONS = 1
dbapi_session = engines.ReplayableSession()
metadata = None
diff --git a/test/base/test_dependency.py b/test/base/test_dependency.py
index 605a16cc3..6d4662be5 100644
--- a/test/base/test_dependency.py
+++ b/test/base/test_dependency.py
@@ -1,7 +1,7 @@
import sqlalchemy.topological as topological
-from sqlalchemy.test import TestBase
-from sqlalchemy.test.testing import assert_raises, eq_
-from sqlalchemy.test.util import conforms_partial_ordering
+from test.lib import TestBase
+from test.lib.testing import assert_raises, eq_
+from test.lib.util import conforms_partial_ordering
from sqlalchemy import exc, util
diff --git a/test/base/test_events.py b/test/base/test_events.py
index 292059877..1baed241b 100644
--- a/test/base/test_events.py
+++ b/test/base/test_events.py
@@ -1,6 +1,6 @@
"""Test event registration and listening."""
-from sqlalchemy.test.testing import TestBase, eq_, assert_raises
+from test.lib.testing import TestBase, eq_, assert_raises
from sqlalchemy import event, exc, util
class TestEvents(TestBase):
diff --git a/test/base/test_except.py b/test/base/test_except.py
index 78a534e67..f02ca988b 100644
--- a/test/base/test_except.py
+++ b/test/base/test_except.py
@@ -2,7 +2,7 @@
from sqlalchemy import exc as sa_exceptions
-from sqlalchemy.test import TestBase
+from test.lib import TestBase
# Py3K
#StandardError = BaseException
diff --git a/test/base/test_utils.py b/test/base/test_utils.py
index d083a8458..4f02d3811 100644
--- a/test/base/test_utils.py
+++ b/test/base/test_utils.py
@@ -1,9 +1,9 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import copy, threading
from sqlalchemy import util, sql, exc
-from sqlalchemy.test import TestBase
-from sqlalchemy.test.testing import eq_, is_, ne_
-from sqlalchemy.test.util import gc_collect, picklers
+from test.lib import TestBase
+from test.lib.testing import eq_, is_, ne_
+from test.lib.util import gc_collect, picklers
from sqlalchemy.util import classproperty
diff --git a/test/bootstrap/__init__.py b/test/bootstrap/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/test/bootstrap/__init__.py
diff --git a/test/bootstrap/config.py b/test/bootstrap/config.py
new file mode 100644
index 000000000..ef37e4f20
--- /dev/null
+++ b/test/bootstrap/config.py
@@ -0,0 +1,173 @@
+import optparse, os, sys, re, ConfigParser, time, warnings
+
+# 2to3
+import StringIO
+
+logging = None
+
+__all__ = 'parser', 'configure', 'options',
+
+db = None
+db_label, db_url, db_opts = None, None, {}
+
+options = None
+file_config = None
+
+base_config = """
+[db]
+sqlite=sqlite:///:memory:
+sqlite_file=sqlite:///querytest.db
+postgresql=postgresql://scott:tiger@127.0.0.1:5432/test
+postgres=postgresql://scott:tiger@127.0.0.1:5432/test
+pg8000=postgresql+pg8000://scott:tiger@127.0.0.1:5432/test
+postgresql_jython=postgresql+zxjdbc://scott:tiger@127.0.0.1:5432/test
+mysql_jython=mysql+zxjdbc://scott:tiger@127.0.0.1:5432/test
+mysql=mysql://scott:tiger@127.0.0.1:3306/test
+oracle=oracle://scott:tiger@127.0.0.1:1521
+oracle8=oracle://scott:tiger@127.0.0.1:1521/?use_ansi=0
+mssql=mssql://scott:tiger@SQUAWK\\SQLEXPRESS/test
+firebird=firebird://sysdba:masterkey@localhost//tmp/test.fdb
+maxdb=maxdb://MONA:RED@/maxdb1
+"""
+
+def _log(option, opt_str, value, parser):
+ global logging
+ if not logging:
+ import logging
+ logging.basicConfig()
+
+ if opt_str.endswith('-info'):
+ logging.getLogger(value).setLevel(logging.INFO)
+ elif opt_str.endswith('-debug'):
+ logging.getLogger(value).setLevel(logging.DEBUG)
+
+
+def _list_dbs(*args):
+ print "Available --db options (use --dburi to override)"
+ for macro in sorted(file_config.options('db')):
+ print "%20s\t%s" % (macro, file_config.get('db', macro))
+ sys.exit(0)
+
+def _server_side_cursors(options, opt_str, value, parser):
+ db_opts['server_side_cursors'] = True
+
+def _engine_strategy(options, opt_str, value, parser):
+ if value:
+ db_opts['strategy'] = value
+
+class _ordered_map(object):
+ def __init__(self):
+ self._keys = list()
+ self._data = dict()
+
+ def __setitem__(self, key, value):
+ if key not in self._keys:
+ self._keys.append(key)
+ self._data[key] = value
+
+ def __iter__(self):
+ for key in self._keys:
+ yield self._data[key]
+
+# at one point in refactoring, modules were injecting into the config
+# process. this could probably just become a list now.
+post_configure = _ordered_map()
+
+def _engine_uri(options, file_config):
+ global db_label, db_url
+ db_label = 'sqlite'
+ if options.dburi:
+ db_url = options.dburi
+ db_label = db_url[:db_url.index(':')]
+ elif options.db:
+ db_label = options.db
+ db_url = None
+
+ if db_url is None:
+ if db_label not in file_config.options('db'):
+ raise RuntimeError(
+ "Unknown engine. Specify --dbs for known engines.")
+ db_url = file_config.get('db', db_label)
+post_configure['engine_uri'] = _engine_uri
+
+def _require(options, file_config):
+ if not(options.require or
+ (file_config.has_section('require') and
+ file_config.items('require'))):
+ return
+
+ try:
+ import pkg_resources
+ except ImportError:
+ raise RuntimeError("setuptools is required for version requirements")
+
+ cmdline = []
+ for requirement in options.require:
+ pkg_resources.require(requirement)
+ cmdline.append(re.split('\s*(<!>=)', requirement, 1)[0])
+
+ if file_config.has_section('require'):
+ for label, requirement in file_config.items('require'):
+ if not label == db_label or label.startswith('%s.' % db_label):
+ continue
+ seen = [c for c in cmdline if requirement.startswith(c)]
+ if seen:
+ continue
+ pkg_resources.require(requirement)
+post_configure['require'] = _require
+
+def _engine_pool(options, file_config):
+ if options.mockpool:
+ from sqlalchemy import pool
+ db_opts['poolclass'] = pool.AssertionPool
+post_configure['engine_pool'] = _engine_pool
+
+def _create_testing_engine(options, file_config):
+ from test.lib import engines, testing
+ global db
+ db = engines.testing_engine(db_url, db_opts)
+ testing.db = db
+post_configure['create_engine'] = _create_testing_engine
+
+def _prep_testing_database(options, file_config):
+ from test.lib import engines
+ from sqlalchemy import schema
+
+ # also create alt schemas etc. here?
+ if options.dropfirst:
+ e = engines.utf8_engine()
+ existing = e.table_names()
+ if existing:
+ print "Dropping existing tables in database: " + db_url
+ try:
+ print "Tables: %s" % ', '.join(existing)
+ except:
+ pass
+ print "Abort within 5 seconds..."
+ time.sleep(5)
+ md = schema.MetaData(e, reflect=True)
+ md.drop_all()
+ e.dispose()
+
+post_configure['prep_db'] = _prep_testing_database
+
+def _set_table_options(options, file_config):
+ from test.lib 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_configure['table_options'] = _set_table_options
+
+def _reverse_topological(options, file_config):
+ if options.reversetop:
+ from sqlalchemy.orm import unitofwork, session, mapper, dependency
+ from sqlalchemy import topological
+ from test.lib.util import RandomSet
+ topological.set = unitofwork.set = session.set = mapper.set = dependency.set = RandomSet
+post_configure['topological'] = _reverse_topological
+
diff --git a/test/bootstrap/noseplugin.py b/test/bootstrap/noseplugin.py
new file mode 100644
index 000000000..c2a152aa6
--- /dev/null
+++ b/test/bootstrap/noseplugin.py
@@ -0,0 +1,169 @@
+import logging
+import os
+import re
+import sys
+import time
+import warnings
+import ConfigParser
+import StringIO
+
+import nose.case
+from nose.plugins import Plugin
+
+from test.bootstrap import config
+
+from test.bootstrap.config import (
+ _create_testing_engine, _engine_pool, _engine_strategy, _engine_uri, _list_dbs, _log,
+ _prep_testing_database, _require, _reverse_topological, _server_side_cursors,
+ _set_table_options, base_config, db, db_label, db_url, file_config, post_configure)
+
+log = logging.getLogger('nose.plugins.sqlalchemy')
+
+class NoseSQLAlchemy(Plugin):
+ """
+ Handles the setup and extra properties required for testing SQLAlchemy
+ """
+ enabled = True
+
+ # nose 1.0 will allow us to replace the old "sqlalchemy" plugin,
+ # if installed, using the same name, but nose 1.0 isn't released yet...
+ name = '_sqlalchemy'
+ score = 100
+
+ def options(self, parser, env=os.environ):
+ Plugin.options(self, parser, env)
+ opt = parser.add_option
+ opt("--log-info", action="callback", type="string", callback=_log,
+ help="turn on info logging for <LOG> (multiple OK)")
+ opt("--log-debug", action="callback", type="string", callback=_log,
+ help="turn on debug logging for <LOG> (multiple OK)")
+ opt("--require", action="append", dest="require", default=[],
+ help="require a particular driver or module version (multiple OK)")
+ opt("--db", action="store", dest="db", default="sqlite",
+ help="Use prefab database uri")
+ opt('--dbs', action='callback', callback=_list_dbs,
+ help="List available prefab dbs")
+ opt("--dburi", action="store", dest="dburi",
+ help="Database uri (overrides --db)")
+ opt("--dropfirst", action="store_true", dest="dropfirst",
+ help="Drop all tables in the target database first (use with caution on Oracle, "
+ "MS-SQL)")
+ opt("--mockpool", action="store_true", dest="mockpool",
+ help="Use mock pool (asserts only one connection used)")
+ opt("--enginestrategy", action="callback", type="string",
+ callback=_engine_strategy,
+ help="Engine strategy (plain or threadlocal, defaults to plain)")
+ opt("--reversetop", action="store_true", dest="reversetop", default=False,
+ help="Use a random-ordering set implementation in the ORM (helps "
+ "reveal dependency issues)")
+ opt("--unhashable", action="store_true", dest="unhashable", default=False,
+ help="Disallow SQLAlchemy from performing a hash() on mapped test objects.")
+ opt("--noncomparable", action="store_true", dest="noncomparable", default=False,
+ help="Disallow SQLAlchemy from performing == on mapped test objects.")
+ opt("--truthless", action="store_true", dest="truthless", default=False,
+ help="Disallow SQLAlchemy from truth-evaluating mapped test objects.")
+ opt("--serverside", action="callback", callback=_server_side_cursors,
+ help="Turn on server side cursors for PG")
+ opt("--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.")
+ opt("--table-option", action="append", dest="tableopts", default=[],
+ help="Add a dialect-specific table option, key=value")
+
+ global file_config
+ file_config = ConfigParser.ConfigParser()
+ file_config.readfp(StringIO.StringIO(base_config))
+ file_config.read(['test.cfg', os.path.expanduser('~/.satest.cfg')])
+ config.file_config = file_config
+
+ def configure(self, options, conf):
+ Plugin.configure(self, options, conf)
+ self.options = options
+
+ def begin(self):
+ global testing, requires, util
+ from test.lib import testing, requires
+ from sqlalchemy import util
+
+ testing.db = db
+ testing.requires = requires
+
+ # Lazy setup of other options (post coverage)
+ for fn in post_configure:
+ fn(self.options, file_config)
+
+ def describeTest(self, test):
+ return ""
+
+ def wantClass(self, cls):
+ """Return true if you want the main test selector to collect
+ tests from this class, false if you don't, and None if you don't
+ care.
+
+ :Parameters:
+ cls : class
+ The class being examined by the selector
+
+ """
+
+ if not issubclass(cls, testing.TestBase):
+ return False
+ else:
+ if (hasattr(cls, '__whitelist__') and testing.db.name in cls.__whitelist__):
+ return True
+ else:
+ return not self.__should_skip_for(cls)
+
+ def __should_skip_for(self, cls):
+ if hasattr(cls, '__requires__'):
+ def test_suite(): return 'ok'
+ test_suite.__name__ = cls.__name__
+ for requirement in cls.__requires__:
+ check = getattr(requires, requirement)
+ if check(test_suite)() != 'ok':
+ # The requirement will perform messaging.
+ return True
+
+ if cls.__unsupported_on__:
+ spec = testing.db_spec(*cls.__unsupported_on__)
+ if spec(testing.db):
+ print "'%s' unsupported on DB implementation '%s'" % (
+ cls.__class__.__name__, testing.db.name)
+ return True
+
+ if getattr(cls, '__only_on__', None):
+ spec = testing.db_spec(*util.to_list(cls.__only_on__))
+ if not spec(testing.db):
+ print "'%s' unsupported on DB implementation '%s'" % (
+ cls.__class__.__name__, testing.db.name)
+ return True
+
+ if getattr(cls, '__skip_if__', False):
+ for c in getattr(cls, '__skip_if__'):
+ if c():
+ print "'%s' skipped by %s" % (
+ cls.__class__.__name__, c.__name__)
+ return True
+
+ for rule in getattr(cls, '__excluded_on__', ()):
+ if testing._is_excluded(*rule):
+ print "'%s' unsupported on DB %s version %s" % (
+ cls.__class__.__name__, testing.db.name,
+ _server_version())
+ return True
+ return False
+
+ def beforeTest(self, test):
+ testing.resetwarnings()
+
+ def afterTest(self, test):
+ testing.resetwarnings()
+
+ def afterContext(self):
+ testing.global_cleanup_assertions()
+
+ #def handleError(self, test, err):
+ #pass
+
+ #def finalize(self, result=None):
+ #pass
diff --git a/test/dialect/test_access.py b/test/dialect/test_access.py
index 0ea8d9a61..bd0d7c22a 100644
--- a/test/dialect/test_access.py
+++ b/test/dialect/test_access.py
@@ -1,7 +1,7 @@
from sqlalchemy import *
from sqlalchemy import sql
from sqlalchemy.databases import access
-from sqlalchemy.test import *
+from test.lib import *
class CompileTest(TestBase, AssertsCompiledSQL):
diff --git a/test/dialect/test_firebird.py b/test/dialect/test_firebird.py
index 41a50e6a3..814c267b5 100644
--- a/test/dialect/test_firebird.py
+++ b/test/dialect/test_firebird.py
@@ -1,9 +1,9 @@
-from sqlalchemy.test.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises
from sqlalchemy import *
from sqlalchemy.databases import firebird
from sqlalchemy.exc import ProgrammingError
from sqlalchemy.sql import table, column
-from sqlalchemy.test import *
+from test.lib import *
class DomainReflectionTest(TestBase, AssertsExecutionResults):
diff --git a/test/dialect/test_informix.py b/test/dialect/test_informix.py
index ceec587d9..ea74dcbe4 100644
--- a/test/dialect/test_informix.py
+++ b/test/dialect/test_informix.py
@@ -1,6 +1,6 @@
from sqlalchemy import *
from sqlalchemy.databases import informix
-from sqlalchemy.test import *
+from test.lib import *
class CompileTest(TestBase, AssertsCompiledSQL):
diff --git a/test/dialect/test_maxdb.py b/test/dialect/test_maxdb.py
index 4df049030..7d43d594b 100644
--- a/test/dialect/test_maxdb.py
+++ b/test/dialect/test_maxdb.py
@@ -1,12 +1,12 @@
"""MaxDB-specific tests."""
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import StringIO, sys
from sqlalchemy import *
from sqlalchemy import exc, sql
from decimal import Decimal
from sqlalchemy.databases import maxdb
-from sqlalchemy.test import *
+from test.lib import *
# TODO
diff --git a/test/dialect/test_mssql.py b/test/dialect/test_mssql.py
index e766a8301..26c53298c 100644
--- a/test/dialect/test_mssql.py
+++ b/test/dialect/test_mssql.py
@@ -1,5 +1,5 @@
# -*- encoding: utf-8
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import datetime
import os
import re
@@ -11,8 +11,8 @@ from sqlalchemy.sql import table, column
from sqlalchemy.databases import mssql
from sqlalchemy.dialects.mssql import pyodbc, mxodbc, pymssql
from sqlalchemy.engine import url
-from sqlalchemy.test import *
-from sqlalchemy.test.testing import eq_, emits_warning_on, \
+from test.lib import *
+from test.lib.testing import eq_, emits_warning_on, \
assert_raises_message
class CompileTest(TestBase, AssertsCompiledSQL):
diff --git a/test/dialect/test_mxodbc.py b/test/dialect/test_mxodbc.py
index f574177dd..36cfc9b08 100644
--- a/test/dialect/test_mxodbc.py
+++ b/test/dialect/test_mxodbc.py
@@ -1,6 +1,6 @@
from sqlalchemy import *
-from sqlalchemy.test.testing import eq_, TestBase
-from sqlalchemy.test import engines
+from test.lib.testing import eq_, TestBase
+from test.lib import engines
# TODO: we should probably build mock bases for
# these to share with test_reconnect, test_parseconnect
diff --git a/test/dialect/test_mysql.py b/test/dialect/test_mysql.py
index 499fd7bd2..7b06f412a 100644
--- a/test/dialect/test_mysql.py
+++ b/test/dialect/test_mysql.py
@@ -1,6 +1,6 @@
# coding: utf-8
-from sqlalchemy.test.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises
# Py2K
import sets
@@ -9,9 +9,9 @@ import sets
from sqlalchemy import *
from sqlalchemy import sql, exc, schema, types as sqltypes, event
from sqlalchemy.dialects.mysql import base as mysql
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test import *
-from sqlalchemy.test.engines import utf8_engine
+from test.lib.testing import eq_
+from test.lib import *
+from test.lib.engines import utf8_engine
import datetime
class TypesTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
diff --git a/test/dialect/test_oracle.py b/test/dialect/test_oracle.py
index 6627015b9..3a0fbac9a 100644
--- a/test/dialect/test_oracle.py
+++ b/test/dialect/test_oracle.py
@@ -1,12 +1,12 @@
# coding: utf-8
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy import *
from sqlalchemy import types as sqltypes, exc
from sqlalchemy.sql import table, column
-from sqlalchemy.test import *
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
-from sqlalchemy.test.engines import testing_engine
+from test.lib import *
+from test.lib.testing import eq_, assert_raises, assert_raises_message
+from test.lib.engines import testing_engine
from sqlalchemy.dialects.oracle import cx_oracle, base as oracle
from sqlalchemy.engine import default
from sqlalchemy.util import jython
diff --git a/test/dialect/test_postgresql.py b/test/dialect/test_postgresql.py
index 2baa65823..06a39ec26 100644
--- a/test/dialect/test_postgresql.py
+++ b/test/dialect/test_postgresql.py
@@ -1,6 +1,6 @@
# coding: utf-8
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
-from sqlalchemy.test import engines
+from test.lib.testing import eq_, assert_raises, assert_raises_message
+from test.lib import engines
import datetime
import decimal
from sqlalchemy import *
@@ -8,10 +8,10 @@ from sqlalchemy.orm import *
from sqlalchemy import exc, schema, types
from sqlalchemy.dialects.postgresql import base as postgresql
from sqlalchemy.engine.strategies import MockEngineStrategy
-from sqlalchemy.test import *
-from sqlalchemy.test.util import round_decimal
+from test.lib import *
+from test.lib.util import round_decimal
from sqlalchemy.sql import table, column
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.engine._base import TablesTest
import logging
diff --git a/test/dialect/test_sqlite.py b/test/dialect/test_sqlite.py
index d42e8dde9..34f5927ed 100644
--- a/test/dialect/test_sqlite.py
+++ b/test/dialect/test_sqlite.py
@@ -1,13 +1,13 @@
"""SQLite-specific tests."""
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
import datetime
from sqlalchemy import *
from sqlalchemy import exc, sql, schema, pool
from sqlalchemy.dialects.sqlite import base as sqlite, \
pysqlite as pysqlite_dialect
-from sqlalchemy.test import *
+from test.lib import *
class TestTypes(TestBase, AssertsExecutionResults):
diff --git a/test/dialect/test_sybase.py b/test/dialect/test_sybase.py
index 37de91d1c..54e4f32e1 100644
--- a/test/dialect/test_sybase.py
+++ b/test/dialect/test_sybase.py
@@ -1,7 +1,7 @@
from sqlalchemy import *
from sqlalchemy import sql
from sqlalchemy.databases import sybase
-from sqlalchemy.test import *
+from test.lib import *
class CompileTest(TestBase, AssertsCompiledSQL):
diff --git a/test/engine/_base.py b/test/engine/_base.py
index ec91243d2..773fa2fea 100644
--- a/test/engine/_base.py
+++ b/test/engine/_base.py
@@ -1,6 +1,6 @@
import sqlalchemy as sa
-from sqlalchemy.test import testing
-from sqlalchemy.test.testing import adict
+from test.lib import testing
+from test.lib.testing import adict
class TablesTest(testing.TestBase):
diff --git a/test/engine/test_bind.py b/test/engine/test_bind.py
index dfcc5e172..855c3611e 100644
--- a/test/engine/test_bind.py
+++ b/test/engine/test_bind.py
@@ -1,14 +1,14 @@
"""tests the "bind" attribute/argument across schema and SQL,
including the deprecated versions of these arguments"""
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy import engine, exc
from sqlalchemy import MetaData, ThreadLocalMetaData
from sqlalchemy import Integer, text
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
class BindTest(testing.TestBase):
diff --git a/test/engine/test_ddlevents.py b/test/engine/test_ddlevents.py
index d0e8af81d..733cc1fcf 100644
--- a/test/engine/test_ddlevents.py
+++ b/test/engine/test_ddlevents.py
@@ -1,13 +1,13 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy.schema import DDL, CheckConstraint, AddConstraint, \
DropConstraint
from sqlalchemy import create_engine
from sqlalchemy import MetaData, Integer, String, event, exc, text
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
import sqlalchemy as tsa
-from sqlalchemy.test import TestBase, testing, engines
-from sqlalchemy.test.testing import AssertsCompiledSQL, eq_
+from test.lib import TestBase, testing, engines
+from test.lib.testing import AssertsCompiledSQL, eq_
from nose import SkipTest
class DDLEventTest(TestBase):
diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py
index 7334f755a..6b0e86e2f 100644
--- a/test/engine/test_execute.py
+++ b/test/engine/test_execute.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises
import re
from sqlalchemy.interfaces import ConnectionProxy
from sqlalchemy import MetaData, Integer, String, INT, VARCHAR, func, \
bindparam, select, event
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
import sqlalchemy as tsa
-from sqlalchemy.test import TestBase, testing, engines
+from test.lib import TestBase, testing, engines
import logging
from sqlalchemy.dialects.oracle.zxjdbc import ReturningParam
diff --git a/test/engine/test_metadata.py b/test/engine/test_metadata.py
index 1feb4d9e2..6534f67be 100644
--- a/test/engine/test_metadata.py
+++ b/test/engine/test_metadata.py
@@ -1,17 +1,17 @@
-from sqlalchemy.test.testing import assert_raises
-from sqlalchemy.test.testing import assert_raises_message
-from sqlalchemy.test.testing import emits_warning
+from test.lib.testing import assert_raises
+from test.lib.testing import assert_raises_message
+from test.lib.testing import emits_warning
import pickle
from sqlalchemy import Integer, String, UniqueConstraint, \
CheckConstraint, ForeignKey, MetaData, Sequence, \
ForeignKeyConstraint, ColumnDefault, Index
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy import schema, exc
import sqlalchemy as tsa
-from sqlalchemy.test import TestBase, ComparesTables, \
+from test.lib import TestBase, ComparesTables, \
AssertsCompiledSQL, testing, engines
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
class MetaDataTest(TestBase, ComparesTables):
def test_metadata_connect(self):
diff --git a/test/engine/test_parseconnect.py b/test/engine/test_parseconnect.py
index 78b75ad2f..7000549e7 100644
--- a/test/engine/test_parseconnect.py
+++ b/test/engine/test_parseconnect.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message, eq_
+from test.lib.testing import assert_raises, assert_raises_message, eq_
import ConfigParser
import StringIO
import sqlalchemy.engine.url as url
from sqlalchemy import create_engine, engine_from_config
from sqlalchemy.engine import _coerce_config
import sqlalchemy as tsa
-from sqlalchemy.test import TestBase
+from test.lib import TestBase
class ParseConnectTest(TestBase):
diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py
index 06c9ac065..4da3a08e3 100644
--- a/test/engine/test_pool.py
+++ b/test/engine/test_pool.py
@@ -1,9 +1,9 @@
import threading, time
from sqlalchemy import pool, interfaces, create_engine, select, event
import sqlalchemy as tsa
-from sqlalchemy.test import TestBase, testing
-from sqlalchemy.test.util import gc_collect, lazy_gc
-from sqlalchemy.test.testing import eq_
+from test.lib import TestBase, testing
+from test.lib.util import gc_collect, lazy_gc
+from test.lib.testing import eq_
mcid = 1
class MockDBAPI(object):
diff --git a/test/engine/test_reconnect.py b/test/engine/test_reconnect.py
index 7675de9c3..e28985801 100644
--- a/test/engine/test_reconnect.py
+++ b/test/engine/test_reconnect.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises
import time
import weakref
from sqlalchemy import select, MetaData, Integer, String, pool
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
import sqlalchemy as tsa
-from sqlalchemy.test import TestBase, testing, engines
-from sqlalchemy.test.util import gc_collect
+from test.lib import TestBase, testing, engines
+from test.lib.util import gc_collect
from sqlalchemy import exc
class MockDisconnect(Exception):
diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py
index 91e73d4f2..5fd18dc81 100644
--- a/test/engine/test_reflection.py
+++ b/test/engine/test_reflection.py
@@ -1,12 +1,12 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import StringIO, unicodedata
from sqlalchemy import types as sql_types
from sqlalchemy import schema
from sqlalchemy.engine.reflection import Inspector
from sqlalchemy import MetaData
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
import sqlalchemy as sa
-from sqlalchemy.test import TestBase, ComparesTables, \
+from test.lib import TestBase, ComparesTables, \
testing, engines, AssertsCompiledSQL
create_inspector = Inspector.from_engine
diff --git a/test/engine/test_transaction.py b/test/engine/test_transaction.py
index 6f3186add..14d8bfd1b 100644
--- a/test/engine/test_transaction.py
+++ b/test/engine/test_transaction.py
@@ -1,13 +1,13 @@
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
import sys
import time
import threading
from sqlalchemy import create_engine, MetaData, INT, VARCHAR, Sequence, \
select, Integer, String, func, text, exc
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
-from sqlalchemy.test import TestBase, testing
+from test.lib.schema import Table
+from test.lib.schema import Column
+from test.lib import TestBase, testing
users, metadata = None, None
diff --git a/test/ex/test_examples.py b/test/ex/test_examples.py
index 9c68f1825..311eab041 100644
--- a/test/ex/test_examples.py
+++ b/test/ex/test_examples.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test import *
+from test.lib import *
import os
import re
diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py
index d22e45796..fb2f60ff1 100644
--- a/test/ext/test_associationproxy.py
+++ b/test/ext/test_associationproxy.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises
import copy
import pickle
@@ -7,8 +7,8 @@ from sqlalchemy.orm import *
from sqlalchemy.orm.collections import collection
from sqlalchemy.ext.associationproxy import *
from sqlalchemy.ext.associationproxy import _AssociationList
-from sqlalchemy.test import *
-from sqlalchemy.test.util import gc_collect
+from test.lib import *
+from test.lib.util import gc_collect
from sqlalchemy.sql import not_
from test.orm import _base
diff --git a/test/ext/test_compiler.py b/test/ext/test_compiler.py
index 3ed84fe61..31b893b48 100644
--- a/test/ext/test_compiler.py
+++ b/test/ext/test_compiler.py
@@ -5,7 +5,7 @@ from sqlalchemy.sql.expression import ClauseElement, ColumnClause,\
from sqlalchemy.schema import DDLElement
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql import table, column
-from sqlalchemy.test import *
+from test.lib import *
class UserDefinedTest(TestBase, AssertsCompiledSQL):
diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py
index 32173cdc1..85692161b 100644
--- a/test/ext/test_declarative.py
+++ b/test/ext/test_declarative.py
@@ -1,17 +1,17 @@
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
from sqlalchemy.ext import declarative as decl
from sqlalchemy import exc
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import MetaData, Integer, String, ForeignKey, \
ForeignKeyConstraint, asc, Index
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import relationship, create_session, class_mapper, \
joinedload, configure_mappers, backref, clear_mappers, \
polymorphic_union, deferred, column_property
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy.util import classproperty
from test.orm._base import ComparableEntity, MappedTest
from sqlalchemy.ext.declarative import declared_attr
diff --git a/test/ext/test_horizontal_shard.py b/test/ext/test_horizontal_shard.py
index a5cee5cad..b4f60519c 100644
--- a/test/ext/test_horizontal_shard.py
+++ b/test/ext/test_horizontal_shard.py
@@ -4,8 +4,8 @@ from sqlalchemy import sql
from sqlalchemy.orm import *
from sqlalchemy.ext.horizontal_shard import ShardedSession
from sqlalchemy.sql import operators
-from sqlalchemy.test import *
-from sqlalchemy.test.testing import eq_
+from test.lib import *
+from test.lib.testing import eq_
from nose import SkipTest
# TODO: ShardTest can be turned into a base for further subclasses
diff --git a/test/ext/test_orderinglist.py b/test/ext/test_orderinglist.py
index 559aefd1d..f7f8f7fa7 100644
--- a/test/ext/test_orderinglist.py
+++ b/test/ext/test_orderinglist.py
@@ -1,8 +1,8 @@
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.orderinglist import *
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test import *
+from test.lib.testing import eq_
+from test.lib import *
metadata = None
diff --git a/test/ext/test_serializer.py b/test/ext/test_serializer.py
index 24626bc8e..862a871af 100644
--- a/test/ext/test_serializer.py
+++ b/test/ext/test_serializer.py
@@ -2,14 +2,14 @@
from sqlalchemy.ext import serializer
from sqlalchemy import exc
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import MetaData, Integer, String, ForeignKey, select, \
desc, func, util
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import relationship, sessionmaker, scoped_session, \
class_mapper, mapper, joinedload, configure_mappers, aliased
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm._base import ComparableEntity, MappedTest
diff --git a/test/ext/test_sqlsoup.py b/test/ext/test_sqlsoup.py
index 7fe8ab178..f0ac6cbed 100644
--- a/test/ext/test_sqlsoup.py
+++ b/test/ext/test_sqlsoup.py
@@ -1,5 +1,5 @@
from sqlalchemy.ext import sqlsoup
-from sqlalchemy.test.testing import TestBase, eq_, assert_raises
+from test.lib.testing import TestBase, eq_, assert_raises
from sqlalchemy import create_engine, or_, desc, select, func, exc, \
Table, util
from sqlalchemy.orm import scoped_session, sessionmaker
diff --git a/test/lib/__init__.py b/test/lib/__init__.py
new file mode 100644
index 000000000..452848aff
--- /dev/null
+++ b/test/lib/__init__.py
@@ -0,0 +1,27 @@
+"""Testing environment and utilities.
+
+This package contains base classes and routines used by
+the unit tests. Tests are based on Nose and bootstrapped
+by noseplugin.NoseSQLAlchemy.
+
+"""
+
+from test.bootstrap import config
+from test.lib import testing, engines, requires, profiling, pickleable
+from test.lib.schema import Column, Table
+from test.lib.testing import \
+ AssertsCompiledSQL, \
+ AssertsExecutionResults, \
+ ComparesTables, \
+ TestBase, \
+ rowset
+
+
+__all__ = ('testing',
+ 'Column', 'Table',
+ 'rowset',
+ 'TestBase', 'AssertsExecutionResults',
+ 'AssertsCompiledSQL', 'ComparesTables',
+ 'engines', 'profiling', 'pickleable')
+
+
diff --git a/test/lib/assertsql.py b/test/lib/assertsql.py
new file mode 100644
index 000000000..b206f91fc
--- /dev/null
+++ b/test/lib/assertsql.py
@@ -0,0 +1,314 @@
+
+from sqlalchemy.interfaces import ConnectionProxy
+from sqlalchemy.engine.default import DefaultDialect
+from sqlalchemy.engine.base import Connection
+from sqlalchemy import util
+import re
+
+class AssertRule(object):
+
+ def process_execute(self, clauseelement, *multiparams, **params):
+ pass
+
+ def process_cursor_execute(self, statement, parameters, context,
+ executemany):
+ pass
+
+ def is_consumed(self):
+ """Return True if this rule has been consumed, False if not.
+
+ Should raise an AssertionError if this rule's condition has
+ definitely failed.
+
+ """
+
+ raise NotImplementedError()
+
+ def rule_passed(self):
+ """Return True if the last test of this rule passed, False if
+ failed, None if no test was applied."""
+
+ raise NotImplementedError()
+
+ def consume_final(self):
+ """Return True if this rule has been consumed.
+
+ Should raise an AssertionError if this rule's condition has not
+ been consumed or has failed.
+
+ """
+
+ if self._result is None:
+ assert False, 'Rule has not been consumed'
+ return self.is_consumed()
+
+class SQLMatchRule(AssertRule):
+ def __init__(self):
+ self._result = None
+ self._errmsg = ""
+
+ def rule_passed(self):
+ return self._result
+
+ def is_consumed(self):
+ if self._result is None:
+ return False
+
+ assert self._result, self._errmsg
+
+ return True
+
+class ExactSQL(SQLMatchRule):
+
+ def __init__(self, sql, params=None):
+ SQLMatchRule.__init__(self)
+ self.sql = sql
+ self.params = params
+
+ def process_cursor_execute(self, statement, parameters, context,
+ executemany):
+ if not context:
+ return
+ _received_statement = \
+ _process_engine_statement(context.unicode_statement,
+ context)
+ _received_parameters = context.compiled_parameters
+
+ # TODO: remove this step once all unit tests are migrated, as
+ # ExactSQL should really be *exact* SQL
+
+ sql = _process_assertion_statement(self.sql, context)
+ equivalent = _received_statement == sql
+ if self.params:
+ if util.callable(self.params):
+ params = self.params(context)
+ else:
+ params = self.params
+ if not isinstance(params, list):
+ params = [params]
+ equivalent = equivalent and params \
+ == context.compiled_parameters
+ else:
+ params = {}
+ self._result = equivalent
+ if not self._result:
+ self._errmsg = \
+ 'Testing for exact statement %r exact params %r, '\
+ 'received %r with params %r' % (sql, params,
+ _received_statement, _received_parameters)
+
+
+class RegexSQL(SQLMatchRule):
+
+ def __init__(self, regex, params=None):
+ SQLMatchRule.__init__(self)
+ self.regex = re.compile(regex)
+ self.orig_regex = regex
+ self.params = params
+
+ def process_cursor_execute(self, statement, parameters, context,
+ executemany):
+ if not context:
+ return
+ _received_statement = \
+ _process_engine_statement(context.unicode_statement,
+ context)
+ _received_parameters = context.compiled_parameters
+ equivalent = bool(self.regex.match(_received_statement))
+ if self.params:
+ if util.callable(self.params):
+ params = self.params(context)
+ else:
+ params = self.params
+ if not isinstance(params, list):
+ params = [params]
+
+ # do a positive compare only
+
+ for param, received in zip(params, _received_parameters):
+ for k, v in param.iteritems():
+ if k not in received or received[k] != v:
+ equivalent = False
+ break
+ else:
+ params = {}
+ self._result = equivalent
+ if not self._result:
+ self._errmsg = \
+ 'Testing for regex %r partial params %r, received %r '\
+ 'with params %r' % (self.orig_regex, params,
+ _received_statement,
+ _received_parameters)
+
+class CompiledSQL(SQLMatchRule):
+
+ def __init__(self, statement, params):
+ SQLMatchRule.__init__(self)
+ self.statement = statement
+ self.params = params
+
+ def process_cursor_execute(self, statement, parameters, context,
+ executemany):
+ if not context:
+ return
+ _received_parameters = list(context.compiled_parameters)
+
+ # recompile from the context, using the default dialect
+
+ compiled = \
+ context.compiled.statement.compile(dialect=DefaultDialect(),
+ column_keys=context.compiled.column_keys)
+ _received_statement = re.sub(r'\n', '', str(compiled))
+ equivalent = self.statement == _received_statement
+ if self.params:
+ if util.callable(self.params):
+ params = self.params(context)
+ else:
+ params = self.params
+ if not isinstance(params, list):
+ params = [params]
+ all_params = list(params)
+ all_received = list(_received_parameters)
+ while params:
+ param = dict(params.pop(0))
+ for k, v in context.compiled.params.iteritems():
+ param.setdefault(k, v)
+ if param not in _received_parameters:
+ equivalent = False
+ break
+ else:
+ _received_parameters.remove(param)
+ if _received_parameters:
+ equivalent = False
+ else:
+ params = {}
+ self._result = equivalent
+ if not self._result:
+ print 'Testing for compiled statement %r partial params '\
+ '%r, received %r with params %r' % (self.statement,
+ all_params, _received_statement, all_received)
+ self._errmsg = \
+ 'Testing for compiled statement %r partial params %r, '\
+ 'received %r with params %r' % (self.statement,
+ all_params, _received_statement, all_received)
+
+
+ # print self._errmsg
+
+class CountStatements(AssertRule):
+
+ def __init__(self, count):
+ self.count = count
+ self._statement_count = 0
+
+ def process_execute(self, clauseelement, *multiparams, **params):
+ self._statement_count += 1
+
+ def process_cursor_execute(self, statement, parameters, context,
+ executemany):
+ pass
+
+ def is_consumed(self):
+ return False
+
+ def consume_final(self):
+ assert self.count == self._statement_count, \
+ 'desired statement count %d does not match %d' \
+ % (self.count, self._statement_count)
+ return True
+
+class AllOf(AssertRule):
+
+ def __init__(self, *rules):
+ self.rules = set(rules)
+
+ def process_execute(self, clauseelement, *multiparams, **params):
+ for rule in self.rules:
+ rule.process_execute(clauseelement, *multiparams, **params)
+
+ def process_cursor_execute(self, statement, parameters, context,
+ executemany):
+ for rule in self.rules:
+ rule.process_cursor_execute(statement, parameters, context,
+ executemany)
+
+ def is_consumed(self):
+ if not self.rules:
+ return True
+ for rule in list(self.rules):
+ if rule.rule_passed(): # a rule passed, move on
+ self.rules.remove(rule)
+ return len(self.rules) == 0
+ assert False, 'No assertion rules were satisfied for statement'
+
+ def consume_final(self):
+ return len(self.rules) == 0
+
+def _process_engine_statement(query, context):
+ if util.jython:
+
+ # oracle+zxjdbc passes a PyStatement when returning into
+
+ query = unicode(query)
+ if context.engine.name == 'mssql' \
+ and query.endswith('; select scope_identity()'):
+ query = query[:-25]
+ query = re.sub(r'\n', '', query)
+ return query
+
+def _process_assertion_statement(query, context):
+ paramstyle = context.dialect.paramstyle
+ if paramstyle == 'named':
+ pass
+ elif paramstyle =='pyformat':
+ query = re.sub(r':([\w_]+)', r"%(\1)s", query)
+ else:
+ # positional params
+ repl = None
+ if paramstyle=='qmark':
+ repl = "?"
+ elif paramstyle=='format':
+ repl = r"%s"
+ elif paramstyle=='numeric':
+ repl = None
+ query = re.sub(r':([\w_]+)', repl, query)
+
+ return query
+
+class SQLAssert(object):
+
+ rules = None
+
+ def add_rules(self, rules):
+ self.rules = list(rules)
+
+ def statement_complete(self):
+ for rule in self.rules:
+ if not rule.consume_final():
+ assert False, \
+ 'All statements are complete, but pending '\
+ 'assertion rules remain'
+
+ def clear_rules(self):
+ del self.rules
+
+ def execute(self, conn, clauseelement, multiparams, params, result):
+ if self.rules is not None:
+ if not self.rules:
+ assert False, \
+ 'All rules have been exhausted, but further '\
+ 'statements remain'
+ rule = self.rules[0]
+ rule.process_execute(clauseelement, *multiparams, **params)
+ if rule.is_consumed():
+ self.rules.pop(0)
+
+ def cursor_execute(self, conn, cursor, statement, parameters,
+ context, executemany):
+ if self.rules:
+ rule = self.rules[0]
+ rule.process_cursor_execute(statement, parameters, context,
+ executemany)
+
+asserter = SQLAssert()
+
diff --git a/test/lib/engines.py b/test/lib/engines.py
new file mode 100644
index 000000000..fdf4163c8
--- /dev/null
+++ b/test/lib/engines.py
@@ -0,0 +1,304 @@
+import sys, types, weakref
+from collections import deque
+from test.bootstrap import config
+from sqlalchemy.util import function_named, callable
+from sqlalchemy import event
+import re
+import warnings
+
+class ConnectionKiller(object):
+ def __init__(self):
+ self.proxy_refs = weakref.WeakKeyDictionary()
+
+ def checkout(self, dbapi_con, con_record, con_proxy):
+ self.proxy_refs[con_proxy] = True
+
+ def _apply_all(self, methods):
+ # must copy keys atomically
+ for rec in self.proxy_refs.keys():
+ if rec is not None and rec.is_valid:
+ try:
+ for name in methods:
+ if callable(name):
+ name(rec)
+ else:
+ getattr(rec, name)()
+ except (SystemExit, KeyboardInterrupt):
+ raise
+ except Exception, e:
+ warnings.warn("testing_reaper couldn't close connection: %s" % e)
+
+ def rollback_all(self):
+ self._apply_all(('rollback',))
+
+ def close_all(self):
+ self._apply_all(('rollback', 'close'))
+
+ def assert_all_closed(self):
+ for rec in self.proxy_refs:
+ if rec.is_valid:
+ assert False
+
+testing_reaper = ConnectionKiller()
+
+def drop_all_tables(metadata):
+ testing_reaper.close_all()
+ metadata.drop_all()
+
+def assert_conns_closed(fn):
+ def decorated(*args, **kw):
+ try:
+ fn(*args, **kw)
+ finally:
+ testing_reaper.assert_all_closed()
+ return function_named(decorated, fn.__name__)
+
+def rollback_open_connections(fn):
+ """Decorator that rolls back all open connections after fn execution."""
+
+ def decorated(*args, **kw):
+ try:
+ fn(*args, **kw)
+ finally:
+ testing_reaper.rollback_all()
+ return function_named(decorated, fn.__name__)
+
+def close_first(fn):
+ """Decorator that closes all connections before fn execution."""
+ def decorated(*args, **kw):
+ testing_reaper.close_all()
+ fn(*args, **kw)
+ return function_named(decorated, fn.__name__)
+
+
+def close_open_connections(fn):
+ """Decorator that closes all connections after fn execution."""
+
+ def decorated(*args, **kw):
+ try:
+ fn(*args, **kw)
+ finally:
+ testing_reaper.close_all()
+ return function_named(decorated, fn.__name__)
+
+def all_dialects(exclude=None):
+ import sqlalchemy.databases as d
+ for name in d.__all__:
+ # TEMPORARY
+ if exclude and name in exclude:
+ continue
+ mod = getattr(d, name, None)
+ if not mod:
+ mod = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name)
+ yield mod.dialect()
+
+class ReconnectFixture(object):
+ def __init__(self, dbapi):
+ self.dbapi = dbapi
+ self.connections = []
+
+ def __getattr__(self, key):
+ return getattr(self.dbapi, key)
+
+ def connect(self, *args, **kwargs):
+ conn = self.dbapi.connect(*args, **kwargs)
+ self.connections.append(conn)
+ return conn
+
+ def shutdown(self):
+ # TODO: this doesn't cover all cases
+ # as nicely as we'd like, namely MySQLdb.
+ # would need to implement R. Brewer's
+ # proxy server idea to get better
+ # coverage.
+ for c in list(self.connections):
+ c.close()
+ self.connections = []
+
+def reconnecting_engine(url=None, options=None):
+ url = url or config.db_url
+ dbapi = config.db.dialect.dbapi
+ if not options:
+ options = {}
+ options['module'] = ReconnectFixture(dbapi)
+ engine = testing_engine(url, options)
+ engine.test_shutdown = engine.dialect.dbapi.shutdown
+ return engine
+
+def testing_engine(url=None, options=None):
+ """Produce an engine configured by --options with optional overrides."""
+
+ from sqlalchemy import create_engine
+ from test.lib.assertsql import asserter
+
+ url = url or config.db_url
+ options = options or config.db_opts
+
+ engine = create_engine(url, **options)
+ event.listen(asserter.execute, 'on_after_execute', engine)
+ event.listen(asserter.cursor_execute, 'on_after_cursor_execute', engine)
+ event.listen(testing_reaper.checkout, 'on_checkout', engine.pool)
+
+ # may want to call this, results
+ # in first-connect initializers
+ #engine.connect()
+
+ return engine
+
+def utf8_engine(url=None, options=None):
+ """Hook for dialects or drivers that don't handle utf8 by default."""
+
+ from sqlalchemy.engine import url as engine_url
+
+ if config.db.driver == 'mysqldb':
+ dbapi_ver = config.db.dialect.dbapi.version_info
+ if (dbapi_ver < (1, 2, 1) or
+ dbapi_ver in ((1, 2, 1, 'gamma', 1), (1, 2, 1, 'gamma', 2),
+ (1, 2, 1, 'gamma', 3), (1, 2, 1, 'gamma', 5))):
+ raise RuntimeError('Character set support unavailable with this '
+ 'driver version: %s' % repr(dbapi_ver))
+ else:
+ url = url or config.db_url
+ url = engine_url.make_url(url)
+ url.query['charset'] = 'utf8'
+ url.query['use_unicode'] = '0'
+ url = str(url)
+
+ return testing_engine(url, options)
+
+def mock_engine(dialect_name=None):
+ """Provides a mocking engine based on the current testing.db.
+
+ This is normally used to test DDL generation flow as emitted
+ by an Engine.
+
+ It should not be used in other cases, as assert_compile() and
+ assert_sql_execution() are much better choices with fewer
+ moving parts.
+
+ """
+
+ from sqlalchemy import create_engine
+
+ if not dialect_name:
+ dialect_name = config.db.name
+
+ buffer = []
+ def executor(sql, *a, **kw):
+ buffer.append(sql)
+ def assert_sql(stmts):
+ recv = [re.sub(r'[\n\t]', '', str(s)) for s in buffer]
+ assert recv == stmts, recv
+
+ engine = create_engine(dialect_name + '://',
+ strategy='mock', executor=executor)
+ assert not hasattr(engine, 'mock')
+ engine.mock = buffer
+ engine.assert_sql = assert_sql
+ return engine
+
+class ReplayableSession(object):
+ """A simple record/playback tool.
+
+ This is *not* a mock testing class. It only records a session for later
+ playback and makes no assertions on call consistency whatsoever. It's
+ unlikely to be suitable for anything other than DB-API recording.
+
+ """
+
+ Callable = object()
+ NoAttribute = object()
+ Natives = set([getattr(types, t)
+ for t in dir(types) if not t.startswith('_')]). \
+ difference([getattr(types, t)
+ # Py3K
+ #for t in ('FunctionType', 'BuiltinFunctionType',
+ # 'MethodType', 'BuiltinMethodType',
+ # 'LambdaType', )])
+
+ # Py2K
+ for t in ('FunctionType', 'BuiltinFunctionType',
+ 'MethodType', 'BuiltinMethodType',
+ 'LambdaType', 'UnboundMethodType',)])
+ # end Py2K
+ def __init__(self):
+ self.buffer = deque()
+
+ def recorder(self, base):
+ return self.Recorder(self.buffer, base)
+
+ def player(self):
+ return self.Player(self.buffer)
+
+ class Recorder(object):
+ def __init__(self, buffer, subject):
+ self._buffer = buffer
+ self._subject = subject
+
+ def __call__(self, *args, **kw):
+ subject, buffer = [object.__getattribute__(self, x)
+ for x in ('_subject', '_buffer')]
+
+ result = subject(*args, **kw)
+ if type(result) not in ReplayableSession.Natives:
+ buffer.append(ReplayableSession.Callable)
+ return type(self)(buffer, result)
+ else:
+ buffer.append(result)
+ return result
+
+ @property
+ def _sqla_unwrap(self):
+ return self._subject
+
+ def __getattribute__(self, key):
+ try:
+ return object.__getattribute__(self, key)
+ except AttributeError:
+ pass
+
+ subject, buffer = [object.__getattribute__(self, x)
+ for x in ('_subject', '_buffer')]
+ try:
+ result = type(subject).__getattribute__(subject, key)
+ except AttributeError:
+ buffer.append(ReplayableSession.NoAttribute)
+ raise
+ else:
+ if type(result) not in ReplayableSession.Natives:
+ buffer.append(ReplayableSession.Callable)
+ return type(self)(buffer, result)
+ else:
+ buffer.append(result)
+ return result
+
+ class Player(object):
+ def __init__(self, buffer):
+ self._buffer = buffer
+
+ def __call__(self, *args, **kw):
+ buffer = object.__getattribute__(self, '_buffer')
+ result = buffer.popleft()
+ if result is ReplayableSession.Callable:
+ return self
+ else:
+ return result
+
+ @property
+ def _sqla_unwrap(self):
+ return None
+
+ def __getattribute__(self, key):
+ try:
+ return object.__getattribute__(self, key)
+ except AttributeError:
+ pass
+ buffer = object.__getattribute__(self, '_buffer')
+ result = buffer.popleft()
+ if result is ReplayableSession.Callable:
+ return self
+ elif result is ReplayableSession.NoAttribute:
+ raise AttributeError(key)
+ else:
+ return result
+
diff --git a/test/lib/entities.py b/test/lib/entities.py
new file mode 100644
index 000000000..0ec677eea
--- /dev/null
+++ b/test/lib/entities.py
@@ -0,0 +1,83 @@
+import sqlalchemy as sa
+from sqlalchemy import exc as sa_exc
+
+_repr_stack = set()
+class BasicEntity(object):
+ def __init__(self, **kw):
+ for key, value in kw.iteritems():
+ setattr(self, key, value)
+
+ def __repr__(self):
+ if id(self) in _repr_stack:
+ return object.__repr__(self)
+ _repr_stack.add(id(self))
+ try:
+ return "%s(%s)" % (
+ (self.__class__.__name__),
+ ', '.join(["%s=%r" % (key, getattr(self, key))
+ for key in sorted(self.__dict__.keys())
+ if not key.startswith('_')]))
+ finally:
+ _repr_stack.remove(id(self))
+
+_recursion_stack = set()
+class ComparableEntity(BasicEntity):
+ def __hash__(self):
+ return hash(self.__class__)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ """'Deep, sparse compare.
+
+ Deeply compare two entities, following the non-None attributes of the
+ non-persisted object, if possible.
+
+ """
+ if other is self:
+ return True
+ elif not self.__class__ == other.__class__:
+ return False
+
+ if id(self) in _recursion_stack:
+ return True
+ _recursion_stack.add(id(self))
+
+ try:
+ # pick the entity thats not SA persisted as the source
+ try:
+ self_key = sa.orm.attributes.instance_state(self).key
+ except sa.orm.exc.NO_STATE:
+ self_key = None
+
+ if other is None:
+ a = self
+ b = other
+ elif self_key is not None:
+ a = other
+ b = self
+ else:
+ a = self
+ b = other
+
+ for attr in a.__dict__.keys():
+ if attr.startswith('_'):
+ continue
+ value = getattr(a, attr)
+
+ try:
+ # handle lazy loader errors
+ battr = getattr(b, attr)
+ except (AttributeError, sa_exc.UnboundExecutionError):
+ return False
+
+ if hasattr(value, '__iter__'):
+ if list(value) != list(battr):
+ return False
+ else:
+ if value is not None and value != battr:
+ return False
+ return True
+ finally:
+ _recursion_stack.remove(id(self))
diff --git a/test/lib/orm.py b/test/lib/orm.py
new file mode 100644
index 000000000..7ec13c555
--- /dev/null
+++ b/test/lib/orm.py
@@ -0,0 +1,111 @@
+import inspect, re
+import config, testing
+from sqlalchemy import orm
+
+__all__ = 'mapper',
+
+
+_whitespace = re.compile(r'^(\s+)')
+
+def _find_pragma(lines, current):
+ m = _whitespace.match(lines[current])
+ basis = m and m.group() or ''
+
+ for line in reversed(lines[0:current]):
+ if 'testlib.pragma' in line:
+ return line
+ m = _whitespace.match(line)
+ indent = m and m.group() or ''
+
+ # simplistic detection:
+
+ # >> # testlib.pragma foo
+ # >> center_line()
+ if indent == basis:
+ break
+ # >> # testlib.pragma foo
+ # >> if fleem:
+ # >> center_line()
+ if line.endswith(':'):
+ break
+ return None
+
+def _make_blocker(method_name, fallback):
+ """Creates tripwired variant of a method, raising when called.
+
+ To excempt an invocation from blockage, there are two options.
+
+ 1) add a pragma in a comment::
+
+ # testlib.pragma exempt:methodname
+ offending_line()
+
+ 2) add a magic cookie to the function's namespace::
+ __sa_baremethodname_exempt__ = True
+ ...
+ offending_line()
+ another_offending_lines()
+
+ The second is useful for testing and development.
+ """
+
+ if method_name.startswith('__') and method_name.endswith('__'):
+ frame_marker = '__sa_%s_exempt__' % method_name[2:-2]
+ else:
+ frame_marker = '__sa_%s_exempt__' % method_name
+ pragma_marker = 'exempt:' + method_name
+
+ def method(self, *args, **kw):
+ frame_r = None
+ try:
+ frame = inspect.stack()[1][0]
+ frame_r = inspect.getframeinfo(frame, 9)
+
+ module = frame.f_globals.get('__name__', '')
+
+ type_ = type(self)
+
+ pragma = _find_pragma(*frame_r[3:5])
+
+ exempt = (
+ (not module.startswith('sqlalchemy')) or
+ (pragma and pragma_marker in pragma) or
+ (frame_marker in frame.f_locals) or
+ ('self' in frame.f_locals and
+ getattr(frame.f_locals['self'], frame_marker, False)))
+
+ if exempt:
+ supermeth = getattr(super(type_, self), method_name, None)
+ if (supermeth is None or
+ getattr(supermeth, 'im_func', None) is method):
+ return fallback(self, *args, **kw)
+ else:
+ return supermeth(*args, **kw)
+ else:
+ raise AssertionError(
+ "%s.%s called in %s, line %s in %s" % (
+ type_.__name__, method_name, module, frame_r[1], frame_r[2]))
+ finally:
+ del frame
+ method.__name__ = method_name
+ return method
+
+def mapper(type_, *args, **kw):
+ forbidden = [
+ ('__hash__', 'unhashable', lambda s: id(s)),
+ ('__eq__', 'noncomparable', lambda s, o: s is o),
+ ('__ne__', 'noncomparable', lambda s, o: s is not o),
+ ('__cmp__', 'noncomparable', lambda s, o: object.__cmp__(s, o)),
+ ('__le__', 'noncomparable', lambda s, o: object.__le__(s, o)),
+ ('__lt__', 'noncomparable', lambda s, o: object.__lt__(s, o)),
+ ('__ge__', 'noncomparable', lambda s, o: object.__ge__(s, o)),
+ ('__gt__', 'noncomparable', lambda s, o: object.__gt__(s, o)),
+ ('__nonzero__', 'truthless', lambda s: 1), ]
+
+ if isinstance(type_, type) and type_.__bases__ == (object,):
+ for method_name, option, fallback in forbidden:
+ if (getattr(config.options, option, False) and
+ method_name not in type_.__dict__):
+ setattr(type_, method_name, _make_blocker(method_name, fallback))
+
+ return orm.mapper(type_, *args, **kw)
diff --git a/test/lib/pickleable.py b/test/lib/pickleable.py
new file mode 100644
index 000000000..9794e424d
--- /dev/null
+++ b/test/lib/pickleable.py
@@ -0,0 +1,75 @@
+"""
+
+some objects used for pickle tests, declared in their own module so that they
+are easily pickleable.
+
+"""
+
+
+class Foo(object):
+ def __init__(self, moredata):
+ self.data = 'im data'
+ self.stuff = 'im stuff'
+ self.moredata = moredata
+ __hash__ = object.__hash__
+ def __eq__(self, other):
+ return other.data == self.data and other.stuff == self.stuff and other.moredata==self.moredata
+
+
+class Bar(object):
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+ __hash__ = object.__hash__
+ def __eq__(self, other):
+ return other.__class__ is self.__class__ and other.x==self.x and other.y==self.y
+ def __str__(self):
+ return "Bar(%d, %d)" % (self.x, self.y)
+
+class OldSchool:
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+ def __eq__(self, other):
+ return other.__class__ is self.__class__ and other.x==self.x and other.y==self.y
+
+class OldSchoolWithoutCompare:
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+class BarWithoutCompare(object):
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+ def __str__(self):
+ return "Bar(%d, %d)" % (self.x, self.y)
+
+
+class NotComparable(object):
+ def __init__(self, data):
+ self.data = data
+
+ def __hash__(self):
+ return id(self)
+
+ def __eq__(self, other):
+ return NotImplemented
+
+ def __ne__(self, other):
+ return NotImplemented
+
+
+class BrokenComparable(object):
+ def __init__(self, data):
+ self.data = data
+
+ def __hash__(self):
+ return id(self)
+
+ def __eq__(self, other):
+ raise NotImplementedError
+
+ def __ne__(self, other):
+ raise NotImplementedError
+
diff --git a/test/lib/profiling.py b/test/lib/profiling.py
new file mode 100644
index 000000000..f6c21bde8
--- /dev/null
+++ b/test/lib/profiling.py
@@ -0,0 +1,221 @@
+"""Profiling support for unit and performance tests.
+
+These are special purpose profiling methods which operate
+in a more fine-grained way than nose's profiling plugin.
+
+"""
+
+import os, sys
+from test.lib.util import function_named, gc_collect
+from nose import SkipTest
+
+__all__ = 'profiled', 'function_call_count', 'conditional_call_count'
+
+all_targets = set()
+profile_config = { 'targets': set(),
+ 'report': True,
+ 'sort': ('time', 'calls'),
+ 'limit': None }
+profiler = None
+
+def profiled(target=None, **target_opts):
+ """Optional function profiling.
+
+ @profiled('label')
+ or
+ @profiled('label', report=True, sort=('calls',), limit=20)
+
+ Enables profiling for a function when 'label' is targetted for
+ profiling. Report options can be supplied, and override the global
+ configuration and command-line options.
+ """
+
+ # manual or automatic namespacing by module would remove conflict issues
+ if target is None:
+ target = 'anonymous_target'
+ elif target in all_targets:
+ print "Warning: redefining profile target '%s'" % target
+ all_targets.add(target)
+
+ filename = "%s.prof" % target
+
+ def decorator(fn):
+ def profiled(*args, **kw):
+ if (target not in profile_config['targets'] and
+ not target_opts.get('always', None)):
+ return fn(*args, **kw)
+
+ elapsed, load_stats, result = _profile(
+ filename, fn, *args, **kw)
+
+ report = target_opts.get('report', profile_config['report'])
+ if report:
+ sort_ = target_opts.get('sort', profile_config['sort'])
+ limit = target_opts.get('limit', profile_config['limit'])
+ print "Profile report for target '%s' (%s)" % (
+ target, filename)
+
+ stats = load_stats()
+ stats.sort_stats(*sort_)
+ if limit:
+ stats.print_stats(limit)
+ else:
+ stats.print_stats()
+ #stats.print_callers()
+ os.unlink(filename)
+ return result
+ return function_named(profiled, fn.__name__)
+ return decorator
+
+def function_call_count(count=None, versions={}, variance=0.05):
+ """Assert a target for a test case's function call count.
+
+ count
+ Optional, general target function call count.
+
+ versions
+ Optional, a dictionary of Python version strings to counts,
+ for example::
+
+ { '2.5.1': 110,
+ '2.5': 100,
+ '2.4': 150 }
+
+ The best match for the current running python will be used.
+ If none match, 'count' will be used as the fallback.
+
+ variance
+ An +/- deviation percentage, defaults to 5%.
+ """
+
+ # this could easily dump the profile report if --verbose is in effect
+
+ version_info = list(sys.version_info)
+ py_version = '.'.join([str(v) for v in sys.version_info])
+ try:
+ from sqlalchemy.cprocessors import to_float
+ cextension = True
+ except ImportError:
+ cextension = False
+
+ while version_info:
+ version = '.'.join([str(v) for v in version_info])
+ if cextension:
+ version += "+cextension"
+ if version in versions:
+ count = versions[version]
+ break
+ version_info.pop()
+
+ if count is None:
+ return lambda fn: fn
+
+ def decorator(fn):
+ def counted(*args, **kw):
+ try:
+ filename = "%s.prof" % fn.__name__
+
+ elapsed, stat_loader, result = _profile(
+ filename, fn, *args, **kw)
+
+ stats = stat_loader()
+ calls = stats.total_calls
+
+ stats.sort_stats('calls', 'cumulative')
+ stats.print_stats()
+ #stats.print_callers()
+ deviance = int(count * variance)
+ if (calls < (count - deviance) or
+ calls > (count + deviance)):
+ raise AssertionError(
+ "Function call count %s not within %s%% "
+ "of expected %s. (Python version %s)" % (
+ calls, (variance * 100), count, py_version))
+
+ return result
+ finally:
+ if os.path.exists(filename):
+ os.unlink(filename)
+ return function_named(counted, fn.__name__)
+ return decorator
+
+def conditional_call_count(discriminator, categories):
+ """Apply a function call count conditionally at runtime.
+
+ Takes two arguments, a callable that returns a key value, and a dict
+ mapping key values to a tuple of arguments to function_call_count.
+
+ The callable is not evaluated until the decorated function is actually
+ invoked. If the `discriminator` returns a key not present in the
+ `categories` dictionary, no call count assertion is applied.
+
+ Useful for integration tests, where running a named test in isolation may
+ have a function count penalty not seen in the full suite, due to lazy
+ initialization in the DB-API, SA, etc.
+ """
+
+ def decorator(fn):
+ def at_runtime(*args, **kw):
+ criteria = categories.get(discriminator(), None)
+ if criteria is None:
+ return fn(*args, **kw)
+
+ rewrapped = function_call_count(*criteria)(fn)
+ return rewrapped(*args, **kw)
+ return function_named(at_runtime, fn.__name__)
+ return decorator
+
+
+def _profile(filename, fn, *args, **kw):
+ global profiler
+ if not profiler:
+ if sys.version_info > (2, 5):
+ try:
+ import cProfile
+ profiler = 'cProfile'
+ except ImportError:
+ pass
+ if not profiler:
+ try:
+ import hotshot
+ profiler = 'hotshot'
+ except ImportError:
+ profiler = 'skip'
+
+ if profiler == 'skip':
+ raise SkipTest('Profiling not supported on this platform')
+ elif profiler == 'cProfile':
+ return _profile_cProfile(filename, fn, *args, **kw)
+ else:
+ return _profile_hotshot(filename, fn, *args, **kw)
+
+def _profile_cProfile(filename, fn, *args, **kw):
+ import cProfile, gc, pstats, time
+
+ load_stats = lambda: pstats.Stats(filename)
+ gc_collect()
+
+ began = time.time()
+ cProfile.runctx('result = fn(*args, **kw)', globals(), locals(),
+ filename=filename)
+ ended = time.time()
+
+ return ended - began, load_stats, locals()['result']
+
+def _profile_hotshot(filename, fn, *args, **kw):
+ import gc, hotshot, hotshot.stats, time
+ load_stats = lambda: hotshot.stats.load(filename)
+
+ gc_collect()
+ prof = hotshot.Profile(filename)
+ began = time.time()
+ prof.start()
+ try:
+ result = fn(*args, **kw)
+ finally:
+ prof.stop()
+ ended = time.time()
+ prof.close()
+
+ return ended - began, load_stats, result
+
diff --git a/test/lib/requires.py b/test/lib/requires.py
new file mode 100644
index 000000000..08fde66c3
--- /dev/null
+++ b/test/lib/requires.py
@@ -0,0 +1,327 @@
+"""Global database feature support policy.
+
+Provides decorators to mark tests requiring specific feature support from the
+target database.
+
+"""
+
+from testing import \
+ _block_unconditionally as no_support, \
+ _chain_decorators_on, \
+ exclude, \
+ emits_warning_on,\
+ skip_if,\
+ fails_on,\
+ fails_on_everything_except
+
+import testing
+import sys
+
+def deferrable_constraints(fn):
+ """Target database must support derferable constraints."""
+ return _chain_decorators_on(
+ fn,
+ no_support('firebird', 'not supported by database'),
+ no_support('mysql', 'not supported by database'),
+ no_support('mssql', 'not supported by database'),
+ )
+
+def foreign_keys(fn):
+ """Target database must support foreign keys."""
+ return _chain_decorators_on(
+ fn,
+ no_support('sqlite', 'not supported by database'),
+ )
+
+
+def unbounded_varchar(fn):
+ """Target database must support VARCHAR with no length"""
+ return _chain_decorators_on(
+ fn,
+ no_support('firebird', 'not supported by database'),
+ no_support('oracle', 'not supported by database'),
+ no_support('mysql', 'not supported by database'),
+ )
+
+def boolean_col_expressions(fn):
+ """Target database must support boolean expressions as columns"""
+ return _chain_decorators_on(
+ fn,
+ no_support('firebird', 'not supported by database'),
+ no_support('oracle', 'not supported by database'),
+ no_support('mssql', 'not supported by database'),
+ no_support('sybase', 'not supported by database'),
+ no_support('maxdb', 'FIXME: verify not supported by database'),
+ no_support('informix', 'not supported by database'),
+ )
+
+def identity(fn):
+ """Target database must support GENERATED AS IDENTITY or a facsimile.
+
+ Includes GENERATED AS IDENTITY, AUTOINCREMENT, AUTO_INCREMENT, or other
+ column DDL feature that fills in a DB-generated identifier at INSERT-time
+ without requiring pre-execution of a SEQUENCE or other artifact.
+
+ """
+ return _chain_decorators_on(
+ fn,
+ no_support('firebird', 'not supported by database'),
+ no_support('oracle', 'not supported by database'),
+ no_support('postgresql', 'not supported by database'),
+ no_support('sybase', 'not supported by database'),
+ )
+
+def independent_cursors(fn):
+ """Target must support simultaneous, independent database cursors on a single connection."""
+
+ return _chain_decorators_on(
+ fn,
+ no_support('mssql+pyodbc', 'no driver support'),
+ no_support('mssql+mxodbc', 'no driver support'),
+ )
+
+def independent_connections(fn):
+ """Target must support simultaneous, independent database connections."""
+
+ # This is also true of some configurations of UnixODBC and probably win32
+ # ODBC as well.
+ return _chain_decorators_on(
+ fn,
+ no_support('sqlite', 'no driver support'),
+ exclude('mssql', '<', (9, 0, 0),
+ 'SQL Server 2005+ is required for independent connections'),
+ )
+
+def row_triggers(fn):
+ """Target must support standard statement-running EACH ROW triggers."""
+ return _chain_decorators_on(
+ fn,
+ # no access to same table
+ no_support('mysql', 'requires SUPER priv'),
+ exclude('mysql', '<', (5, 0, 10), 'not supported by database'),
+
+ # huh? TODO: implement triggers for PG tests, remove this
+ no_support('postgresql', 'PG triggers need to be implemented for tests'),
+ )
+
+def correlated_outer_joins(fn):
+ """Target must support an outer join to a subquery which correlates to the parent."""
+
+ return _chain_decorators_on(
+ fn,
+ no_support('oracle', 'Raises "ORA-01799: a column may not be outer-joined to a subquery"')
+ )
+
+def savepoints(fn):
+ """Target database must support savepoints."""
+ return _chain_decorators_on(
+ fn,
+ emits_warning_on('mssql', 'Savepoint support in mssql is experimental and may lead to data loss.'),
+ no_support('access', 'not supported by database'),
+ no_support('sqlite', 'not supported by database'),
+ no_support('sybase', 'FIXME: guessing, needs confirmation'),
+ exclude('mysql', '<', (5, 0, 3), 'not supported by database'),
+ exclude('informix', '<', (11, 55, 'xC3'), 'not supported by database'),
+ )
+
+def denormalized_names(fn):
+ """Target database must have 'denormalized', i.e. UPPERCASE as case insensitive names."""
+
+ return skip_if(
+ lambda: not testing.db.dialect.requires_name_normalize,
+ "Backend does not require denomralized names."
+ )(fn)
+
+def schemas(fn):
+ """Target database must support external schemas, and have one named 'test_schema'."""
+
+ return _chain_decorators_on(
+ fn,
+ no_support('sqlite', 'no schema support'),
+ no_support('firebird', 'no schema support')
+ )
+
+def sequences(fn):
+ """Target database must support SEQUENCEs."""
+ return _chain_decorators_on(
+ fn,
+ no_support('access', 'no SEQUENCE support'),
+ no_support('mssql', 'no SEQUENCE support'),
+ no_support('mysql', 'no SEQUENCE support'),
+ no_support('sqlite', 'no SEQUENCE support'),
+ no_support('sybase', 'no SEQUENCE support'),
+ no_support('informix', 'no SEQUENCE support'),
+ )
+
+def update_nowait(fn):
+ """Target database must support SELECT...FOR UPDATE NOWAIT"""
+ return _chain_decorators_on(
+ fn,
+ no_support('access', 'no FOR UPDATE NOWAIT support'),
+ no_support('firebird', 'no FOR UPDATE NOWAIT support'),
+ no_support('mssql', 'no FOR UPDATE NOWAIT support'),
+ no_support('mysql', 'no FOR UPDATE NOWAIT support'),
+ no_support('sqlite', 'no FOR UPDATE NOWAIT support'),
+ no_support('sybase', 'no FOR UPDATE NOWAIT support'),
+ )
+
+def subqueries(fn):
+ """Target database must support subqueries."""
+ return _chain_decorators_on(
+ fn,
+ exclude('mysql', '<', (4, 1, 1), 'no subquery support'),
+ )
+
+def intersect(fn):
+ """Target database must support INTERSECT or equivlaent."""
+ return _chain_decorators_on(
+ fn,
+ fails_on('firebird', 'no support for INTERSECT'),
+ fails_on('mysql', 'no support for INTERSECT'),
+ fails_on('sybase', 'no support for INTERSECT'),
+ fails_on('informix', 'no support for INTERSECT'),
+ )
+
+def except_(fn):
+ """Target database must support EXCEPT or equivlaent (i.e. MINUS)."""
+ return _chain_decorators_on(
+ fn,
+ fails_on('firebird', 'no support for EXCEPT'),
+ fails_on('mysql', 'no support for EXCEPT'),
+ fails_on('sybase', 'no support for EXCEPT'),
+ fails_on('informix', 'no support for EXCEPT'),
+ )
+
+def offset(fn):
+ """Target database must support some method of adding OFFSET or equivalent to a result set."""
+ return _chain_decorators_on(
+ fn,
+ fails_on('sybase', 'no support for OFFSET or equivalent'),
+ )
+
+def returning(fn):
+ return _chain_decorators_on(
+ fn,
+ no_support('access', 'not supported by database'),
+ no_support('sqlite', 'not supported by database'),
+ no_support('mysql', 'not supported by database'),
+ no_support('maxdb', 'not supported by database'),
+ no_support('sybase', 'not supported by database'),
+ no_support('informix', 'not supported by database'),
+ )
+
+def two_phase_transactions(fn):
+ """Target database must support two-phase transactions."""
+ return _chain_decorators_on(
+ fn,
+ no_support('access', 'not supported by database'),
+ no_support('firebird', 'no SA implementation'),
+ no_support('maxdb', 'not supported by database'),
+ no_support('mssql', 'FIXME: guessing, needs confirmation'),
+ no_support('oracle', 'no SA implementation'),
+ no_support('sqlite', 'not supported by database'),
+ no_support('sybase', 'FIXME: guessing, needs confirmation'),
+ no_support('postgresql+zxjdbc', 'FIXME: JDBC driver confuses the transaction state, may '
+ 'need separate XA implementation'),
+ exclude('mysql', '<', (5, 0, 3), 'not supported by database'),
+ )
+
+def unicode_connections(fn):
+ """Target driver must support some encoding of Unicode across the wire."""
+ # TODO: expand to exclude MySQLdb versions w/ broken unicode
+ return _chain_decorators_on(
+ fn,
+ exclude('mysql', '<', (4, 1, 1), 'no unicode connection support'),
+ )
+
+def unicode_ddl(fn):
+ """Target driver must support some encoding of Unicode across the wire."""
+ # TODO: expand to exclude MySQLdb versions w/ broken unicode
+ return _chain_decorators_on(
+ fn,
+ no_support('maxdb', 'database support flakey'),
+ no_support('oracle', 'FIXME: no support in database?'),
+ no_support('sybase', 'FIXME: guessing, needs confirmation'),
+ no_support('mssql+pymssql', 'no FreeTDS support'),
+ exclude('mysql', '<', (4, 1, 1), 'no unicode connection support'),
+ )
+
+def sane_rowcount(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(lambda: not testing.db.dialect.supports_sane_rowcount)
+ )
+
+def cextensions(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(lambda: not _has_cextensions(), "C extensions not installed")
+ )
+
+def dbapi_lastrowid(fn):
+ return _chain_decorators_on(
+ fn,
+ fails_on_everything_except('mysql+mysqldb', 'mysql+oursql', 'sqlite+pysqlite')
+ )
+
+def sane_multi_rowcount(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(lambda: not testing.db.dialect.supports_sane_multi_rowcount)
+ )
+
+def reflects_pk_names(fn):
+ """Target driver reflects the name of primary key constraints."""
+ return _chain_decorators_on(
+ fn,
+ fails_on_everything_except('postgresql', 'oracle')
+ )
+
+def python2(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(
+ lambda: sys.version_info >= (3,),
+ "Python version 2.xx is required."
+ )
+ )
+
+def python26(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(
+ lambda: sys.version_info < (2, 6),
+ "Python version 2.6 or greater is required"
+ )
+ )
+
+def python25(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(
+ lambda: sys.version_info < (2, 5),
+ "Python version 2.5 or greater is required"
+ )
+ )
+
+def _has_cextensions():
+ try:
+ from sqlalchemy import cresultproxy, cprocessors
+ return True
+ except ImportError:
+ return False
+
+def _has_sqlite():
+ from sqlalchemy import create_engine
+ try:
+ e = create_engine('sqlite://')
+ return True
+ except ImportError:
+ return False
+
+def sqlite(fn):
+ return _chain_decorators_on(
+ fn,
+ skip_if(lambda: not _has_sqlite())
+ )
+
diff --git a/test/lib/schema.py b/test/lib/schema.py
new file mode 100644
index 000000000..614e5863e
--- /dev/null
+++ b/test/lib/schema.py
@@ -0,0 +1,79 @@
+"""Enhanced versions of schema.Table and schema.Column which establish
+desired state for different backends.
+"""
+
+from test.lib import testing
+from sqlalchemy import schema
+
+__all__ = 'Table', 'Column',
+
+table_options = {}
+
+def Table(*args, **kw):
+ """A schema.Table wrapper/hook for dialect-specific tweaks."""
+
+ test_opts = dict([(k,kw.pop(k)) for k in kw.keys()
+ if k.startswith('test_')])
+
+ kw.update(table_options)
+
+ if testing.against('mysql'):
+ if 'mysql_engine' not in kw and 'mysql_type' not in kw:
+ if 'test_needs_fk' in test_opts or 'test_needs_acid' in test_opts:
+ kw['mysql_engine'] = 'InnoDB'
+
+ # Apply some default cascading rules for self-referential foreign keys.
+ # MySQL InnoDB has some issues around seleting self-refs too.
+ if testing.against('firebird'):
+ table_name = args[0]
+ unpack = (testing.config.db.dialect.
+ identifier_preparer.unformat_identifiers)
+
+ # Only going after ForeignKeys in Columns. May need to
+ # expand to ForeignKeyConstraint too.
+ fks = [fk
+ for col in args if isinstance(col, schema.Column)
+ for fk in col.foreign_keys]
+
+ for fk in fks:
+ # root around in raw spec
+ ref = fk._colspec
+ if isinstance(ref, schema.Column):
+ name = ref.table.name
+ else:
+ # take just the table name: on FB there cannot be
+ # a schema, so the first element is always the
+ # table name, possibly followed by the field name
+ name = unpack(ref)[0]
+ if name == table_name:
+ if fk.ondelete is None:
+ fk.ondelete = 'CASCADE'
+ if fk.onupdate is None:
+ fk.onupdate = 'CASCADE'
+
+ return schema.Table(*args, **kw)
+
+
+def Column(*args, **kw):
+ """A schema.Column wrapper/hook for dialect-specific tweaks."""
+
+ test_opts = dict([(k,kw.pop(k)) for k in kw.keys()
+ if k.startswith('test_')])
+
+ col = schema.Column(*args, **kw)
+ if 'test_needs_autoincrement' in test_opts and \
+ kw.get('primary_key', False) and \
+ testing.against('firebird', 'oracle'):
+ def add_seq(tbl, c):
+ c._init_items(
+ schema.Sequence(_truncate_name(testing.db.dialect, tbl.name + '_' + c.name + '_seq'), optional=True)
+ )
+ col._on_table_attach(add_seq)
+ return col
+
+def _truncate_name(dialect, name):
+ if len(name) > dialect.max_identifier_length:
+ return name[0:max(dialect.max_identifier_length - 6, 0)] + "_" + hex(hash(name) % 64)[2:]
+ else:
+ return name
+
diff --git a/test/lib/testing.py b/test/lib/testing.py
new file mode 100644
index 000000000..da2a3dfd3
--- /dev/null
+++ b/test/lib/testing.py
@@ -0,0 +1,797 @@
+"""TestCase and TestSuite artifacts and testing decorators."""
+
+import itertools
+import operator
+import re
+import sys
+import types
+import warnings
+from cStringIO import StringIO
+
+from test.bootstrap import config
+from test.lib import assertsql, util as testutil
+from sqlalchemy.util import function_named, py3k
+from engines import drop_all_tables
+
+from sqlalchemy import exc as sa_exc, util, types as sqltypes, schema, pool, orm
+from sqlalchemy.engine import default
+from nose import SkipTest
+
+
+_ops = { '<': operator.lt,
+ '>': operator.gt,
+ '==': operator.eq,
+ '!=': operator.ne,
+ '<=': operator.le,
+ '>=': operator.ge,
+ 'in': operator.contains,
+ 'between': lambda val, pair: val >= pair[0] and val <= pair[1],
+ }
+
+# sugar ('testing.db'); set here by config() at runtime
+db = None
+
+# more sugar, installed by __init__
+requires = None
+
+def fails_if(callable_, reason=None):
+ """Mark a test as expected to fail if callable_ returns True.
+
+ If the callable returns false, the test is run and reported as normal.
+ However if the callable returns true, the test is expected to fail and the
+ unit test logic is inverted: if the test fails, a success is reported. If
+ the test succeeds, a failure is reported.
+ """
+
+ docstring = getattr(callable_, '__doc__', None) or callable_.__name__
+ description = docstring.split('\n')[0]
+
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if not callable_():
+ return fn(*args, **kw)
+ else:
+ try:
+ fn(*args, **kw)
+ except Exception, ex:
+ print ("'%s' failed as expected (condition: %s): %s " % (
+ fn_name, description, str(ex)))
+ return True
+ else:
+ raise AssertionError(
+ "Unexpected success for '%s' (condition: %s)" %
+ (fn_name, description))
+ return function_named(maybe, fn_name)
+ return decorate
+
+
+def future(fn):
+ """Mark a test as expected to unconditionally fail.
+
+ Takes no arguments, omit parens when using as a decorator.
+ """
+
+ fn_name = fn.__name__
+ def decorated(*args, **kw):
+ try:
+ fn(*args, **kw)
+ except Exception, ex:
+ print ("Future test '%s' failed as expected: %s " % (
+ fn_name, str(ex)))
+ return True
+ else:
+ raise AssertionError(
+ "Unexpected success for future test '%s'" % fn_name)
+ return function_named(decorated, fn_name)
+
+def db_spec(*dbs):
+ dialects = set([x for x in dbs if '+' not in x])
+ drivers = set([x[1:] for x in dbs if x.startswith('+')])
+ specs = set([tuple(x.split('+')) for x in dbs if '+' in x and x not in drivers])
+
+ def check(engine):
+ return engine.name in dialects or \
+ engine.driver in drivers or \
+ (engine.name, engine.driver) in specs
+
+ return check
+
+
+def fails_on(dbs, reason):
+ """Mark a test as expected to fail on the specified database
+ implementation.
+
+ Unlike ``crashes``, tests marked as ``fails_on`` will be run
+ for the named databases. The test is expected to fail and the unit test
+ logic is inverted: if the test fails, a success is reported. If the test
+ succeeds, a failure is reported.
+ """
+
+ spec = db_spec(dbs)
+
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if not spec(config.db):
+ return fn(*args, **kw)
+ else:
+ try:
+ fn(*args, **kw)
+ except Exception, ex:
+ print ("'%s' failed as expected on DB implementation "
+ "'%s+%s': %s" % (
+ fn_name, config.db.name, config.db.driver, reason))
+ return True
+ else:
+ raise AssertionError(
+ "Unexpected success for '%s' on DB implementation '%s+%s'" %
+ (fn_name, config.db.name, config.db.driver))
+ return function_named(maybe, fn_name)
+ return decorate
+
+def fails_on_everything_except(*dbs):
+ """Mark a test as expected to fail on most database implementations.
+
+ Like ``fails_on``, except failure is the expected outcome on all
+ databases except those listed.
+ """
+
+ spec = db_spec(*dbs)
+
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if spec(config.db):
+ return fn(*args, **kw)
+ else:
+ try:
+ fn(*args, **kw)
+ except Exception, ex:
+ print ("'%s' failed as expected on DB implementation "
+ "'%s+%s': %s" % (
+ fn_name, config.db.name, config.db.driver, str(ex)))
+ return True
+ else:
+ raise AssertionError(
+ "Unexpected success for '%s' on DB implementation '%s+%s'" %
+ (fn_name, config.db.name, config.db.driver))
+ return function_named(maybe, fn_name)
+ return decorate
+
+def crashes(db, reason):
+ """Mark a test as unsupported by a database implementation.
+
+ ``crashes`` tests will be skipped unconditionally. Use for feature tests
+ that cause deadlocks or other fatal problems.
+
+ """
+ carp = _should_carp_about_exclusion(reason)
+ spec = db_spec(db)
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if spec(config.db):
+ msg = "'%s' unsupported on DB implementation '%s+%s': %s" % (
+ fn_name, config.db.name, config.db.driver, reason)
+ print msg
+ if carp:
+ print >> sys.stderr, msg
+ return True
+ else:
+ return fn(*args, **kw)
+ return function_named(maybe, fn_name)
+ return decorate
+
+def _block_unconditionally(db, reason):
+ """Mark a test as unsupported by a database implementation.
+
+ Will never run the test against any version of the given database, ever,
+ no matter what. Use when your assumptions are infallible; past, present
+ and future.
+
+ """
+ carp = _should_carp_about_exclusion(reason)
+ spec = db_spec(db)
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if spec(config.db):
+ msg = "'%s' unsupported on DB implementation '%s+%s': %s" % (
+ fn_name, config.db.name, config.db.driver, reason)
+ print msg
+ if carp:
+ print >> sys.stderr, msg
+ return True
+ else:
+ return fn(*args, **kw)
+ return function_named(maybe, fn_name)
+ return decorate
+
+def only_on(dbs, reason):
+ carp = _should_carp_about_exclusion(reason)
+ spec = db_spec(*util.to_list(dbs))
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if spec(config.db):
+ return fn(*args, **kw)
+ else:
+ msg = "'%s' unsupported on DB implementation '%s+%s': %s" % (
+ fn_name, config.db.name, config.db.driver, reason)
+ print msg
+ if carp:
+ print >> sys.stderr, msg
+ return True
+ return function_named(maybe, fn_name)
+ return decorate
+
+def exclude(db, op, spec, reason):
+ """Mark a test as unsupported by specific database server versions.
+
+ Stackable, both with other excludes and other decorators. Examples::
+
+ # Not supported by mydb versions less than 1, 0
+ @exclude('mydb', '<', (1,0))
+ # Other operators work too
+ @exclude('bigdb', '==', (9,0,9))
+ @exclude('yikesdb', 'in', ((0, 3, 'alpha2'), (0, 3, 'alpha3')))
+
+ """
+ carp = _should_carp_about_exclusion(reason)
+
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if _is_excluded(db, op, spec):
+ msg = "'%s' unsupported on DB %s version '%s': %s" % (
+ fn_name, config.db.name, _server_version(), reason)
+ print msg
+ if carp:
+ print >> sys.stderr, msg
+ return True
+ else:
+ return fn(*args, **kw)
+ return function_named(maybe, fn_name)
+ return decorate
+
+def _should_carp_about_exclusion(reason):
+ """Guard against forgotten exclusions."""
+ assert reason
+ for _ in ('todo', 'fixme', 'xxx'):
+ if _ in reason.lower():
+ return True
+ else:
+ if len(reason) < 4:
+ return True
+
+def _is_excluded(db, op, spec):
+ """Return True if the configured db matches an exclusion specification.
+
+ db:
+ A dialect name
+ op:
+ An operator or stringified operator, such as '=='
+ spec:
+ A value that will be compared to the dialect's server_version_info
+ using the supplied operator.
+
+ Examples::
+ # Not supported by mydb versions less than 1, 0
+ _is_excluded('mydb', '<', (1,0))
+ # Other operators work too
+ _is_excluded('bigdb', '==', (9,0,9))
+ _is_excluded('yikesdb', 'in', ((0, 3, 'alpha2'), (0, 3, 'alpha3')))
+ """
+
+ vendor_spec = db_spec(db)
+
+ if not vendor_spec(config.db):
+ return False
+
+ version = _server_version()
+
+ oper = hasattr(op, '__call__') and op or _ops[op]
+ return oper(version, spec)
+
+def _server_version(bind=None):
+ """Return a server_version_info tuple."""
+
+ if bind is None:
+ bind = config.db
+
+ # force metadata to be retrieved
+ conn = bind.connect()
+ version = getattr(bind.dialect, 'server_version_info', ())
+ conn.close()
+ return version
+
+def skip_if(predicate, reason=None):
+ """Skip a test if predicate is true."""
+ reason = reason or predicate.__name__
+ carp = _should_carp_about_exclusion(reason)
+
+ def decorate(fn):
+ fn_name = fn.__name__
+ def maybe(*args, **kw):
+ if predicate():
+ msg = "'%s' skipped on DB %s version '%s': %s" % (
+ fn_name, config.db.name, _server_version(), reason)
+ print msg
+ if carp:
+ print >> sys.stderr, msg
+ return True
+ else:
+ return fn(*args, **kw)
+ return function_named(maybe, fn_name)
+ return decorate
+
+def emits_warning(*messages):
+ """Mark a test as emitting a warning.
+
+ With no arguments, squelches all SAWarning failures. Or pass one or more
+ strings; these will be matched to the root of the warning description by
+ warnings.filterwarnings().
+ """
+
+ # TODO: it would be nice to assert that a named warning was
+ # emitted. should work with some monkeypatching of warnings,
+ # and may work on non-CPython if they keep to the spirit of
+ # warnings.showwarning's docstring.
+ # - update: jython looks ok, it uses cpython's module
+ def decorate(fn):
+ def safe(*args, **kw):
+ # todo: should probably be strict about this, too
+ filters = [dict(action='ignore',
+ category=sa_exc.SAPendingDeprecationWarning)]
+ if not messages:
+ filters.append(dict(action='ignore',
+ category=sa_exc.SAWarning))
+ else:
+ filters.extend(dict(action='ignore',
+ message=message,
+ category=sa_exc.SAWarning)
+ for message in messages)
+ for f in filters:
+ warnings.filterwarnings(**f)
+ try:
+ return fn(*args, **kw)
+ finally:
+ resetwarnings()
+ return function_named(safe, fn.__name__)
+ return decorate
+
+def emits_warning_on(db, *warnings):
+ """Mark a test as emitting a warning on a specific dialect.
+
+ With no arguments, squelches all SAWarning failures. Or pass one or more
+ strings; these will be matched to the root of the warning description by
+ warnings.filterwarnings().
+ """
+ spec = db_spec(db)
+
+ def decorate(fn):
+ def maybe(*args, **kw):
+ if isinstance(db, basestring):
+ if not spec(config.db):
+ return fn(*args, **kw)
+ else:
+ wrapped = emits_warning(*warnings)(fn)
+ return wrapped(*args, **kw)
+ else:
+ if not _is_excluded(*db):
+ return fn(*args, **kw)
+ else:
+ wrapped = emits_warning(*warnings)(fn)
+ return wrapped(*args, **kw)
+ return function_named(maybe, fn.__name__)
+ return decorate
+
+def uses_deprecated(*messages):
+ """Mark a test as immune from fatal deprecation warnings.
+
+ With no arguments, squelches all SADeprecationWarning failures.
+ Or pass one or more strings; these will be matched to the root
+ of the warning description by warnings.filterwarnings().
+
+ As a special case, you may pass a function name prefixed with //
+ and it will be re-written as needed to match the standard warning
+ verbiage emitted by the sqlalchemy.util.deprecated decorator.
+ """
+
+
+ def decorate(fn):
+ def safe(*args, **kw):
+ # todo: should probably be strict about this, too
+ filters = [dict(action='ignore',
+ category=sa_exc.SAPendingDeprecationWarning)]
+ if not messages:
+ filters.append(dict(action='ignore',
+ category=sa_exc.SADeprecationWarning))
+ else:
+ filters.extend(
+ [dict(action='ignore',
+ message=message,
+ category=sa_exc.SADeprecationWarning)
+ for message in
+ [ (m.startswith('//') and
+ ('Call to deprecated function ' + m[2:]) or m)
+ for m in messages] ])
+
+ for f in filters:
+ warnings.filterwarnings(**f)
+ try:
+ return fn(*args, **kw)
+ finally:
+ resetwarnings()
+ return function_named(safe, fn.__name__)
+ return decorate
+
+def resetwarnings():
+ """Reset warning behavior to testing defaults."""
+
+ warnings.filterwarnings('ignore',
+ category=sa_exc.SAPendingDeprecationWarning)
+ warnings.filterwarnings('error', category=sa_exc.SADeprecationWarning)
+ warnings.filterwarnings('error', category=sa_exc.SAWarning)
+
+# warnings.simplefilter('error')
+
+
+def global_cleanup_assertions():
+ """Check things that have to be finalized at the end of a test suite.
+
+ Hardcoded at the moment, a modular system can be built here
+ to support things like PG prepared transactions, tables all
+ dropped, etc.
+
+ """
+
+ testutil.lazy_gc()
+ assert not pool._refs
+
+
+
+def against(*queries):
+ """Boolean predicate, compares to testing database configuration.
+
+ Given one or more dialect names, returns True if one is the configured
+ database engine.
+
+ Also supports comparison to database version when provided with one or
+ more 3-tuples of dialect name, operator, and version specification::
+
+ testing.against('mysql', 'postgresql')
+ testing.against(('mysql', '>=', (5, 0, 0))
+ """
+
+ for query in queries:
+ if isinstance(query, basestring):
+ if db_spec(query)(config.db):
+ return True
+ else:
+ name, op, spec = query
+ if not db_spec(name)(config.db):
+ continue
+
+ have = _server_version()
+
+ oper = hasattr(op, '__call__') and op or _ops[op]
+ if oper(have, spec):
+ return True
+ return False
+
+def _chain_decorators_on(fn, *decorators):
+ """Apply a series of decorators to fn, returning a decorated function."""
+ for decorator in reversed(decorators):
+ fn = decorator(fn)
+ return fn
+
+def rowset(results):
+ """Converts the results of sql execution into a plain set of column tuples.
+
+ Useful for asserting the results of an unordered query.
+ """
+
+ return set([tuple(row) for row in results])
+
+
+def eq_(a, b, msg=None):
+ """Assert a == b, with repr messaging on failure."""
+ assert a == b, msg or "%r != %r" % (a, b)
+
+def ne_(a, b, msg=None):
+ """Assert a != b, with repr messaging on failure."""
+ assert a != b, msg or "%r == %r" % (a, b)
+
+def is_(a, b, msg=None):
+ """Assert a is b, with repr messaging on failure."""
+ assert a is b, msg or "%r is not %r" % (a, b)
+
+def is_not_(a, b, msg=None):
+ """Assert a is not b, with repr messaging on failure."""
+ assert a is not b, msg or "%r is %r" % (a, b)
+
+def startswith_(a, fragment, msg=None):
+ """Assert a.startswith(fragment), with repr messaging on failure."""
+ assert a.startswith(fragment), msg or "%r does not start with %r" % (
+ a, fragment)
+
+def assert_raises(except_cls, callable_, *args, **kw):
+ try:
+ callable_(*args, **kw)
+ success = False
+ except except_cls, e:
+ success = True
+
+ # assert outside the block so it works for AssertionError too !
+ assert success, "Callable did not raise an exception"
+
+def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
+ try:
+ callable_(*args, **kwargs)
+ assert False, "Callable did not raise an exception"
+ except except_cls, e:
+ assert re.search(msg, str(e)), "%r !~ %s" % (msg, e)
+ print str(e)
+
+def fail(msg):
+ assert False, msg
+
+def fixture(table, columns, *rows):
+ """Insert data into table after creation."""
+ def onload(event, schema_item, connection):
+ insert = table.insert()
+ column_names = [col.key for col in columns]
+ connection.execute(insert, [dict(zip(column_names, column_values))
+ for column_values in rows])
+ table.append_ddl_listener('after-create', onload)
+
+def provide_metadata(fn):
+ """Provides a bound MetaData object for a single test,
+ drops it afterwards."""
+ def maybe(*args, **kw):
+ metadata = schema.MetaData(db)
+ context = dict(fn.func_globals)
+ context['metadata'] = metadata
+ # jython bug #1034
+ rebound = types.FunctionType(
+ fn.func_code, context, fn.func_name, fn.func_defaults,
+ fn.func_closure)
+ try:
+ return rebound(*args, **kw)
+ finally:
+ metadata.drop_all()
+ return function_named(maybe, fn.__name__)
+
+def resolve_artifact_names(fn):
+ """Decorator, augment function globals with tables and classes.
+
+ Swaps out the function's globals at execution time. The 'global' statement
+ will not work as expected inside a decorated function.
+
+ """
+ # This could be automatically applied to framework and test_ methods in
+ # the MappedTest-derived test suites but... *some* explicitness for this
+ # magic is probably good. Especially as 'global' won't work- these
+ # rebound functions aren't regular Python..
+ #
+ # Also: it's lame that CPython accepts a dict-subclass for globals, but
+ # only calls dict methods. That would allow 'global' to pass through to
+ # the func_globals.
+ def resolved(*args, **kwargs):
+ self = args[0]
+ context = dict(fn.func_globals)
+ for source in self._artifact_registries:
+ context.update(getattr(self, source))
+ # jython bug #1034
+ rebound = types.FunctionType(
+ fn.func_code, context, fn.func_name, fn.func_defaults,
+ fn.func_closure)
+ return rebound(*args, **kwargs)
+ return function_named(resolved, fn.func_name)
+
+class adict(dict):
+ """Dict keys available as attributes. Shadows."""
+ def __getattribute__(self, key):
+ try:
+ return self[key]
+ except KeyError:
+ return dict.__getattribute__(self, key)
+
+ def get_all(self, *keys):
+ return tuple([self[key] for key in keys])
+
+
+class TestBase(object):
+ # A sequence of database names to always run, regardless of the
+ # constraints below.
+ __whitelist__ = ()
+
+ # A sequence of requirement names matching testing.requires decorators
+ __requires__ = ()
+
+ # A sequence of dialect names to exclude from the test class.
+ __unsupported_on__ = ()
+
+ # If present, test class is only runnable for the *single* specified
+ # dialect. If you need multiple, use __unsupported_on__ and invert.
+ __only_on__ = None
+
+ # A sequence of no-arg callables. If any are True, the entire testcase is
+ # skipped.
+ __skip_if__ = None
+
+ _artifact_registries = ()
+
+ def assert_(self, val, msg=None):
+ assert val, msg
+
+class AssertsCompiledSQL(object):
+ def assert_compile(self, clause, result, params=None, checkparams=None, dialect=None, use_default_dialect=False):
+ if use_default_dialect:
+ dialect = default.DefaultDialect()
+
+ if dialect is None:
+ dialect = getattr(self, '__dialect__', None)
+
+ kw = {}
+ if params is not None:
+ kw['column_keys'] = params.keys()
+
+ if isinstance(clause, orm.Query):
+ context = clause._compile_context()
+ context.statement.use_labels = True
+ clause = context.statement
+
+ c = clause.compile(dialect=dialect, **kw)
+
+ param_str = repr(getattr(c, 'params', {}))
+ # Py3K
+ #param_str = param_str.encode('utf-8').decode('ascii', 'ignore')
+
+ print "\nSQL String:\n" + str(c) + param_str
+
+ cc = re.sub(r'[\n\t]', '', str(c))
+
+ eq_(cc, result, "%r != %r on dialect %r" % (cc, result, dialect))
+
+ if checkparams is not None:
+ eq_(c.construct_params(params), checkparams)
+
+class ComparesTables(object):
+ def assert_tables_equal(self, table, reflected_table, strict_types=False):
+ assert len(table.c) == len(reflected_table.c)
+ for c, reflected_c in zip(table.c, reflected_table.c):
+ eq_(c.name, reflected_c.name)
+ assert reflected_c is reflected_table.c[c.name]
+ eq_(c.primary_key, reflected_c.primary_key)
+ eq_(c.nullable, reflected_c.nullable)
+
+ if strict_types:
+ assert type(reflected_c.type) is type(c.type), \
+ "Type '%s' doesn't correspond to type '%s'" % (reflected_c.type, c.type)
+ else:
+ self.assert_types_base(reflected_c, c)
+
+ if isinstance(c.type, sqltypes.String):
+ eq_(c.type.length, reflected_c.type.length)
+
+ eq_(set([f.column.name for f in c.foreign_keys]), set([f.column.name for f in reflected_c.foreign_keys]))
+ if c.server_default:
+ assert isinstance(reflected_c.server_default,
+ schema.FetchedValue)
+
+ assert len(table.primary_key) == len(reflected_table.primary_key)
+ for c in table.primary_key:
+ assert reflected_table.primary_key.columns[c.name] is not None
+
+ def assert_types_base(self, c1, c2):
+ assert c1.type._compare_type_affinity(c2.type),\
+ "On column %r, type '%s' doesn't correspond to type '%s'" % \
+ (c1.name, c1.type, c2.type)
+
+class AssertsExecutionResults(object):
+ def assert_result(self, result, class_, *objects):
+ result = list(result)
+ print repr(result)
+ self.assert_list(result, class_, objects)
+
+ def assert_list(self, result, class_, list):
+ self.assert_(len(result) == len(list),
+ "result list is not the same size as test list, " +
+ "for class " + class_.__name__)
+ for i in range(0, len(list)):
+ self.assert_row(class_, result[i], list[i])
+
+ def assert_row(self, class_, rowobj, desc):
+ self.assert_(rowobj.__class__ is class_,
+ "item class is not " + repr(class_))
+ for key, value in desc.iteritems():
+ if isinstance(value, tuple):
+ if isinstance(value[1], list):
+ self.assert_list(getattr(rowobj, key), value[0], value[1])
+ else:
+ self.assert_row(value[0], getattr(rowobj, key), value[1])
+ else:
+ self.assert_(getattr(rowobj, key) == value,
+ "attribute %s value %s does not match %s" % (
+ key, getattr(rowobj, key), value))
+
+ def assert_unordered_result(self, result, cls, *expected):
+ """As assert_result, but the order of objects is not considered.
+
+ The algorithm is very expensive but not a big deal for the small
+ numbers of rows that the test suite manipulates.
+ """
+
+ class frozendict(dict):
+ def __hash__(self):
+ return id(self)
+
+ found = util.IdentitySet(result)
+ expected = set([frozendict(e) for e in expected])
+
+ for wrong in itertools.ifilterfalse(lambda o: type(o) == cls, found):
+ fail('Unexpected type "%s", expected "%s"' % (
+ type(wrong).__name__, cls.__name__))
+
+ if len(found) != len(expected):
+ fail('Unexpected object count "%s", expected "%s"' % (
+ len(found), len(expected)))
+
+ NOVALUE = object()
+ def _compare_item(obj, spec):
+ for key, value in spec.iteritems():
+ if isinstance(value, tuple):
+ try:
+ self.assert_unordered_result(
+ getattr(obj, key), value[0], *value[1])
+ except AssertionError:
+ return False
+ else:
+ if getattr(obj, key, NOVALUE) != value:
+ return False
+ return True
+
+ for expected_item in expected:
+ for found_item in found:
+ if _compare_item(found_item, expected_item):
+ found.remove(found_item)
+ break
+ else:
+ fail(
+ "Expected %s instance with attributes %s not found." % (
+ cls.__name__, repr(expected_item)))
+ return True
+
+ def assert_sql_execution(self, db, callable_, *rules):
+ assertsql.asserter.add_rules(rules)
+ try:
+ callable_()
+ assertsql.asserter.statement_complete()
+ finally:
+ assertsql.asserter.clear_rules()
+
+ def assert_sql(self, db, callable_, list_, with_sequences=None):
+ if with_sequences is not None and config.db.name in ('firebird', 'oracle', 'postgresql'):
+ rules = with_sequences
+ else:
+ rules = list_
+
+ newrules = []
+ for rule in rules:
+ if isinstance(rule, dict):
+ newrule = assertsql.AllOf(*[
+ assertsql.ExactSQL(k, v) for k, v in rule.iteritems()
+ ])
+ else:
+ newrule = assertsql.ExactSQL(*rule)
+ newrules.append(newrule)
+
+ self.assert_sql_execution(db, callable_, *newrules)
+
+ def assert_sql_count(self, db, callable_, count):
+ self.assert_sql_execution(db, callable_, assertsql.CountStatements(count))
+
+
diff --git a/test/lib/util.py b/test/lib/util.py
new file mode 100644
index 000000000..e5277f076
--- /dev/null
+++ b/test/lib/util.py
@@ -0,0 +1,107 @@
+from sqlalchemy.util import jython, function_named, defaultdict
+
+import gc
+import time
+import random
+
+if jython:
+ def gc_collect(*args):
+ """aggressive gc.collect for tests."""
+ gc.collect()
+ time.sleep(0.1)
+ gc.collect()
+ gc.collect()
+ return 0
+
+ # "lazy" gc, for VM's that don't GC on refcount == 0
+ lazy_gc = gc_collect
+
+else:
+ # assume CPython - straight gc.collect, lazy_gc() is a pass
+ gc_collect = gc.collect
+ def lazy_gc():
+ pass
+
+def picklers():
+ picklers = set()
+ # Py2K
+ try:
+ import cPickle
+ picklers.add(cPickle)
+ except ImportError:
+ pass
+ # end Py2K
+ import pickle
+ picklers.add(pickle)
+
+ # yes, this thing needs this much testing
+ for pickle in picklers:
+ for protocol in -1, 0, 1, 2:
+ yield pickle.loads, lambda d:pickle.dumps(d, protocol)
+
+
+def round_decimal(value, prec):
+ if isinstance(value, float):
+ return round(value, prec)
+
+ import decimal
+
+ # can also use shift() here but that is 2.6 only
+ return (value * decimal.Decimal("1" + "0" * prec)).to_integral(decimal.ROUND_FLOOR) / \
+ pow(10, prec)
+
+class RandomSet(set):
+ def __iter__(self):
+ l = list(set.__iter__(self))
+ random.shuffle(l)
+ return iter(l)
+
+ def pop(self):
+ index = random.randint(0, len(self) - 1)
+ item = list(set.__iter__(self))[index]
+ self.remove(item)
+ return item
+
+ def union(self, other):
+ return RandomSet(set.union(self, other))
+
+ def difference(self, other):
+ return RandomSet(set.difference(self, other))
+
+ def intersection(self, other):
+ return RandomSet(set.intersection(self, other))
+
+ def copy(self):
+ return RandomSet(self)
+
+def conforms_partial_ordering(tuples, sorted_elements):
+ """True if the given sorting conforms to the given partial ordering."""
+
+ deps = defaultdict(set)
+ for parent, child in tuples:
+ deps[parent].add(child)
+ for i, node in enumerate(sorted_elements):
+ for n in sorted_elements[i:]:
+ if node in deps[n]:
+ return False
+ else:
+ return True
+
+def all_partial_orderings(tuples, elements):
+ edges = defaultdict(set)
+ for parent, child in tuples:
+ edges[child].add(parent)
+
+ def _all_orderings(elements):
+
+ if len(elements) == 1:
+ yield list(elements)
+ else:
+ for elem in elements:
+ subset = set(elements).difference([elem])
+ if not subset.intersection(edges[elem]):
+ for sub_ordering in _all_orderings(subset):
+ yield [elem] + sub_ordering
+
+ return iter(_all_orderings(elements))
+
diff --git a/test/orm/_base.py b/test/orm/_base.py
index 4d0031f5a..4ccc10157 100644
--- a/test/orm/_base.py
+++ b/test/orm/_base.py
@@ -3,11 +3,11 @@ import sys
import types
import sqlalchemy as sa
import sqlalchemy.exceptions as sa_exc
-from sqlalchemy.test import config, testing
-from sqlalchemy.test.testing import resolve_artifact_names, adict
-from sqlalchemy.test.engines import drop_all_tables
+from test.lib import config, testing
+from test.lib.testing import resolve_artifact_names, adict
+from test.lib.engines import drop_all_tables
from sqlalchemy.util import function_named
-from sqlalchemy.test.entities import BasicEntity, ComparableEntity
+from test.lib.entities import BasicEntity, ComparableEntity
Entity = BasicEntity
diff --git a/test/orm/_fixtures.py b/test/orm/_fixtures.py
index a8df63b4a..8f128c287 100644
--- a/test/orm/_fixtures.py
+++ b/test/orm/_fixtures.py
@@ -1,8 +1,8 @@
from sqlalchemy import MetaData, Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import attributes
-from sqlalchemy.test.testing import fixture
+from test.lib.testing import fixture
from test.orm import _base
__all__ = ()
diff --git a/test/orm/inheritance/test_abc_inheritance.py b/test/orm/inheritance/test_abc_inheritance.py
index edbd476ec..08ab28a08 100644
--- a/test/orm/inheritance/test_abc_inheritance.py
+++ b/test/orm/inheritance/test_abc_inheritance.py
@@ -2,8 +2,8 @@ from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE
-from sqlalchemy.test import testing
-from sqlalchemy.test.schema import Table, Column
+from test.lib import testing
+from test.lib.schema import Table, Column
from test.orm import _base
diff --git a/test/orm/inheritance/test_abc_polymorphic.py b/test/orm/inheritance/test_abc_polymorphic.py
index 2dab59bb2..fb229003b 100644
--- a/test/orm/inheritance/test_abc_polymorphic.py
+++ b/test/orm/inheritance/test_abc_polymorphic.py
@@ -4,7 +4,7 @@ from sqlalchemy.orm import *
from sqlalchemy.util import function_named
from test.orm import _base, _fixtures
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
class ABCTest(_base.MappedTest):
@classmethod
diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py
index c57f1d095..5892b3c89 100644
--- a/test/orm/inheritance/test_basic.py
+++ b/test/orm/inheritance/test_basic.py
@@ -1,14 +1,14 @@
import warnings
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy import exc as sa_exc, util
from sqlalchemy.orm import *
from sqlalchemy.orm import exc as orm_exc
-from sqlalchemy.test import testing, engines
+from test.lib import testing, engines
from sqlalchemy.util import function_named
from test.orm import _base, _fixtures
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
class O2MTest(_base.MappedTest):
"""deals with inheritance and one-to-many relationships"""
diff --git a/test/orm/inheritance/test_concrete.py b/test/orm/inheritance/test_concrete.py
index a2d79284c..43ba36ace 100644
--- a/test/orm/inheritance/test_concrete.py
+++ b/test/orm/inheritance/test_concrete.py
@@ -1,15 +1,15 @@
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.orm import exc as orm_exc
-from sqlalchemy.test import *
+from test.lib import *
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from test.orm import _base
from sqlalchemy.orm import attributes
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test.schema import Table, Column
+from test.lib.testing import eq_
+from test.lib.schema import Table, Column
class Employee(object):
diff --git a/test/orm/inheritance/test_magazine.py b/test/orm/inheritance/test_magazine.py
index 125a5629c..307c54a9c 100644
--- a/test/orm/inheritance/test_magazine.py
+++ b/test/orm/inheritance/test_magazine.py
@@ -1,10 +1,10 @@
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy.util import function_named
from test.orm import _base
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
class BaseObject(object):
def __init__(self, *args, **kwargs):
diff --git a/test/orm/inheritance/test_manytomany.py b/test/orm/inheritance/test_manytomany.py
index 8390e2a1b..f5e3e63a1 100644
--- a/test/orm/inheritance/test_manytomany.py
+++ b/test/orm/inheritance/test_manytomany.py
@@ -1,8 +1,8 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import testing
+from test.lib import testing
from test.orm import _base
diff --git a/test/orm/inheritance/test_poly_linked_list.py b/test/orm/inheritance/test_poly_linked_list.py
index 01dad72e4..8b300f06a 100644
--- a/test/orm/inheritance/test_poly_linked_list.py
+++ b/test/orm/inheritance/test_poly_linked_list.py
@@ -2,8 +2,8 @@ from sqlalchemy import *
from sqlalchemy.orm import *
from test.orm import _base
-from sqlalchemy.test import testing
-from sqlalchemy.test.schema import Table, Column
+from test.lib import testing
+from test.lib.schema import Table, Column
class PolymorphicCircularTest(_base.MappedTest):
diff --git a/test/orm/inheritance/test_polymorph.py b/test/orm/inheritance/test_polymorph.py
index 33646c922..1f82834d9 100644
--- a/test/orm/inheritance/test_polymorph.py
+++ b/test/orm/inheritance/test_polymorph.py
@@ -1,11 +1,11 @@
"""tests basic polymorphic mapper loading/saving, minimal relationships"""
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.orm import exc as orm_exc
from sqlalchemy import exc as sa_exc
-from sqlalchemy.test import Column, testing
+from test.lib import Column, testing
from sqlalchemy.util import function_named
from test.orm import _fixtures, _base
diff --git a/test/orm/inheritance/test_polymorph2.py b/test/orm/inheritance/test_polymorph2.py
index 9852e8b09..030b931a5 100644
--- a/test/orm/inheritance/test_polymorph2.py
+++ b/test/orm/inheritance/test_polymorph2.py
@@ -2,16 +2,16 @@
inheritance setups for which we maintain compatibility.
"""
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy import *
from sqlalchemy import util
from sqlalchemy.orm import *
-from sqlalchemy.test import TestBase, AssertsExecutionResults, testing
+from test.lib import TestBase, AssertsExecutionResults, testing
from sqlalchemy.util import function_named
from test.orm import _base, _fixtures
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test.schema import Table, Column
+from test.lib.testing import eq_
+from test.lib.schema import Table, Column
class AttrSettable(object):
def __init__(self, **kwargs):
diff --git a/test/orm/inheritance/test_productspec.py b/test/orm/inheritance/test_productspec.py
index dc81d9245..a1ecf2562 100644
--- a/test/orm/inheritance/test_productspec.py
+++ b/test/orm/inheritance/test_productspec.py
@@ -2,9 +2,9 @@ from datetime import datetime
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import testing
+from test.lib import testing
from test.orm import _base
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
class InheritTest(_base.MappedTest):
"""tests some various inheritance round trips involving a particular set of polymorphic inheritance relationships"""
diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py
index 45b3e9da1..36a23204d 100644
--- a/test/orm/inheritance/test_query.py
+++ b/test/orm/inheritance/test_query.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.orm import interfaces
@@ -6,10 +6,10 @@ from sqlalchemy import exc as sa_exc
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.engine import default
-from sqlalchemy.test import AssertsCompiledSQL, testing
+from test.lib import AssertsCompiledSQL, testing
from test.orm import _base, _fixtures
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test.schema import Table, Column
+from test.lib.testing import eq_
+from test.lib.schema import Table, Column
class Company(_fixtures.Base):
pass
diff --git a/test/orm/inheritance/test_selects.py b/test/orm/inheritance/test_selects.py
index 7c9920f6f..5a7389386 100644
--- a/test/orm/inheritance/test_selects.py
+++ b/test/orm/inheritance/test_selects.py
@@ -1,7 +1,7 @@
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import testing
+from test.lib import testing
from test.orm._fixtures import Base
from test.orm._base import MappedTest
diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py
index 2efde2b32..a65851a5f 100644
--- a/test/orm/inheritance/test_single.py
+++ b/test/orm/inheritance/test_single.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import testing
+from test.lib import testing
from test.orm import _fixtures
from test.orm._base import MappedTest, ComparableEntity
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
class SingleInheritanceTest(testing.AssertsCompiledSQL, MappedTest):
diff --git a/test/orm/test_association.py b/test/orm/test_association.py
index c9b1584bb..e681099c5 100644
--- a/test/orm/test_association.py
+++ b/test/orm/test_association.py
@@ -1,10 +1,10 @@
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session
from test.orm import _base
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
class AssociationTest(_base.MappedTest):
diff --git a/test/orm/test_assorted_eager.py b/test/orm/test_assorted_eager.py
index ff9234d70..b11b3ddd9 100644
--- a/test/orm/test_assorted_eager.py
+++ b/test/orm/test_assorted_eager.py
@@ -9,11 +9,11 @@ be cleaned up and modernized.
import datetime
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, backref, create_session
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base
diff --git a/test/orm/test_attributes.py b/test/orm/test_attributes.py
index b5a6c1f5e..913c6ec52 100644
--- a/test/orm/test_attributes.py
+++ b/test/orm/test_attributes.py
@@ -3,10 +3,10 @@ from sqlalchemy.orm import attributes, instrumentation
from sqlalchemy.orm.collections import collection
from sqlalchemy.orm.interfaces import AttributeExtension
from sqlalchemy import exc as sa_exc
-from sqlalchemy.test import *
-from sqlalchemy.test.testing import eq_, ne_, assert_raises
+from test.lib import *
+from test.lib.testing import eq_, ne_, assert_raises
from test.orm import _base
-from sqlalchemy.test.util import gc_collect, all_partial_orderings
+from test.lib.util import gc_collect, all_partial_orderings
from sqlalchemy.util import cmp, jython
from sqlalchemy import event, topological
diff --git a/test/orm/test_backref_mutations.py b/test/orm/test_backref_mutations.py
index dd48df592..31cab962e 100644
--- a/test/orm/test_backref_mutations.py
+++ b/test/orm/test_backref_mutations.py
@@ -9,14 +9,14 @@ UPDATE in the database.
"""
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy import Integer, String, ForeignKey, Sequence, exc as sa_exc
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session, class_mapper, backref, sessionmaker
from sqlalchemy.orm import attributes, exc as orm_exc
-from sqlalchemy.test import testing
-from sqlalchemy.test.testing import eq_
+from test.lib import testing
+from test.lib.testing import eq_
from test.orm import _base, _fixtures
class O2MCollectionTest(_fixtures.FixtureTest):
diff --git a/test/orm/test_bind.py b/test/orm/test_bind.py
index 9b1c20b60..bab9be428 100644
--- a/test/orm/test_bind.py
+++ b/test/orm/test_bind.py
@@ -1,10 +1,10 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy import MetaData, Integer
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, create_session
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from test.orm import _base
diff --git a/test/orm/test_cascade.py b/test/orm/test_cascade.py
index bedff2170..17b186cd8 100644
--- a/test/orm/test_cascade.py
+++ b/test/orm/test_cascade.py
@@ -1,13 +1,13 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy import Integer, String, ForeignKey, Sequence, \
exc as sa_exc
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, \
sessionmaker, class_mapper, backref, Session
from sqlalchemy.orm import attributes, exc as orm_exc
-from sqlalchemy.test import testing
-from sqlalchemy.test.testing import eq_
+from test.lib import testing
+from test.lib.testing import eq_
from test.orm import _base, _fixtures
diff --git a/test/orm/test_collection.py b/test/orm/test_collection.py
index b0ce28663..540213745 100644
--- a/test/orm/test_collection.py
+++ b/test/orm/test_collection.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import sys
from operator import and_
@@ -6,14 +6,14 @@ import sqlalchemy.orm.collections as collections
from sqlalchemy.orm.collections import collection
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, text
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy import util, exc as sa_exc
from sqlalchemy.orm import create_session, mapper, relationship, \
attributes, instrumentation
from test.orm import _base
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
class Canary(sa.orm.interfaces.AttributeExtension):
def __init__(self):
diff --git a/test/orm/test_compile.py b/test/orm/test_compile.py
index 575c3ccfb..3294a853c 100644
--- a/test/orm/test_compile.py
+++ b/test/orm/test_compile.py
@@ -1,8 +1,8 @@
from sqlalchemy import *
from sqlalchemy import exc as sa_exc
from sqlalchemy.orm import *
-from sqlalchemy.test import *
-from sqlalchemy.test.testing import assert_raises_message
+from test.lib import *
+from test.lib.testing import assert_raises_message
from test.orm import _base
diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py
index 26765244a..e5e657933 100644
--- a/test/orm/test_cycles.py
+++ b/test/orm/test_cycles.py
@@ -5,13 +5,13 @@ T1<->T2, with o2m or m2o between them, and a third T3 with o2m/m2o to one/both
T1/T2.
"""
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, backref, \
create_session, sessionmaker
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test.assertsql import RegexSQL, ExactSQL, CompiledSQL, AllOf
+from test.lib.testing import eq_
+from test.lib.assertsql import RegexSQL, ExactSQL, CompiledSQL, AllOf
from test.orm import _base
diff --git a/test/orm/test_defaults.py b/test/orm/test_defaults.py
index 389fdbc5c..e5f419ccb 100644
--- a/test/orm/test_defaults.py
+++ b/test/orm/test_defaults.py
@@ -1,11 +1,11 @@
import sqlalchemy as sa
-from sqlalchemy.test import testing
from sqlalchemy import Integer, String, ForeignKey, event
-from sqlalchemy.test.schema import Table, Column
+from test.lib import testing
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session
from test.orm import _base
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
class TriggerDefaultsTest(_base.MappedTest):
diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py
index 76c59d029..2565105a6 100644
--- a/test/orm/test_deprecations.py
+++ b/test/orm/test_deprecations.py
@@ -5,10 +5,10 @@ modern (i.e. not deprecated) alternative to them. The tests snippets here can
be migrated directly to the wiki, docs, etc.
"""
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, func
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, relation, create_session, sessionmaker
from test.orm import _base
diff --git a/test/orm/test_dynamic.py b/test/orm/test_dynamic.py
index c06f6918a..c89503278 100644
--- a/test/orm/test_dynamic.py
+++ b/test/orm/test_dynamic.py
@@ -1,12 +1,12 @@
-from sqlalchemy.test.testing import eq_, ne_
+from test.lib.testing import eq_, ne_
import operator
from sqlalchemy.orm import dynamic_loader, backref
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, desc, select, func
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, Query, attributes
from sqlalchemy.orm.dynamic import AppenderMixin
-from sqlalchemy.test.testing import eq_, AssertsCompiledSQL, assert_raises_message
+from test.lib.testing import eq_, AssertsCompiledSQL, assert_raises_message
from sqlalchemy.util import function_named
from test.orm import _base, _fixtures
diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py
index 94d1b3464..6304ae45a 100644
--- a/test/orm/test_eager_relations.py
+++ b/test/orm/test_eager_relations.py
@@ -1,18 +1,18 @@
"""tests of joined-eager loaded attributes"""
-from sqlalchemy.test.testing import eq_, is_, is_not_
+from test.lib.testing import eq_, is_, is_not_
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy.orm import joinedload, deferred, undefer, \
joinedload_all, backref
from sqlalchemy import Integer, String, Date, ForeignKey, and_, select, \
func
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, \
lazyload, aliased
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
-from sqlalchemy.test.assertsql import CompiledSQL
+from test.lib.assertsql import CompiledSQL
from test.orm import _base, _fixtures
from sqlalchemy.util import OrderedDict as odict
import datetime
diff --git a/test/orm/test_evaluator.py b/test/orm/test_evaluator.py
index af6a3f89e..26f9f49c3 100644
--- a/test/orm/test_evaluator.py
+++ b/test/orm/test_evaluator.py
@@ -1,11 +1,11 @@
"""Evluating SQL expressions on ORM objects"""
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import String, Integer, select
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, create_session
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base
from sqlalchemy import and_, or_, not_
diff --git a/test/orm/test_expire.py b/test/orm/test_expire.py
index 0d9f5e745..7189290bc 100644
--- a/test/orm/test_expire.py
+++ b/test/orm/test_expire.py
@@ -1,12 +1,12 @@
"""Attribute/instance expiration, deferral of attributes, etc."""
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
-from sqlalchemy.test.util import gc_collect
+from test.lib.testing import eq_, assert_raises, assert_raises_message
+from test.lib.util import gc_collect
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, exc as sa_exc
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session, \
attributes, deferred, exc as orm_exc, defer, undefer,\
strategies, state, lazyload, backref
diff --git a/test/orm/test_extendedattr.py b/test/orm/test_extendedattr.py
index 6fabe7e43..aae1ecdbb 100644
--- a/test/orm/test_extendedattr.py
+++ b/test/orm/test_extendedattr.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import pickle
from sqlalchemy import util
from sqlalchemy.orm import attributes, instrumentation
@@ -7,7 +7,7 @@ from sqlalchemy.orm.attributes import set_attribute, get_attribute, del_attribut
from sqlalchemy.orm.instrumentation import is_instrumented
from sqlalchemy.orm import clear_mappers
from sqlalchemy.orm import InstrumentationManager
-from sqlalchemy.test import *
+from test.lib import *
from test.orm import _base
class MyTypesManager(InstrumentationManager):
diff --git a/test/orm/test_generative.py b/test/orm/test_generative.py
index 73f62b233..e3c5eee99 100644
--- a/test/orm/test_generative.py
+++ b/test/orm/test_generative.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, MetaData, func
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base, _fixtures
diff --git a/test/orm/test_immediate_load.py b/test/orm/test_immediate_load.py
index f85208bff..66794ad4b 100644
--- a/test/orm/test_immediate_load.py
+++ b/test/orm/test_immediate_load.py
@@ -1,8 +1,8 @@
"""basic tests of lazy loaded attributes"""
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy.orm import mapper, relationship, create_session, immediateload
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _fixtures
diff --git a/test/orm/test_instrumentation.py b/test/orm/test_instrumentation.py
index 7ffee2a2e..4bcf36351 100644
--- a/test/orm/test_instrumentation.py
+++ b/test/orm/test_instrumentation.py
@@ -1,12 +1,12 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import sqlalchemy as sa
from sqlalchemy import MetaData, Integer, ForeignKey, util, event
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session, \
attributes, class_mapper, clear_mappers, instrumentation, events
-from sqlalchemy.test.testing import eq_, ne_
+from test.lib.schema import Table
+from test.lib.schema import Column
+from test.lib.testing import eq_, ne_
from sqlalchemy.util import function_named
from test.orm import _base
diff --git a/test/orm/test_lazy_relations.py b/test/orm/test_lazy_relations.py
index f6147a3eb..1bf7eecaf 100644
--- a/test/orm/test_lazy_relations.py
+++ b/test/orm/test_lazy_relations.py
@@ -1,17 +1,17 @@
"""basic tests of lazy loaded attributes"""
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import datetime
from sqlalchemy import exc as sa_exc
from sqlalchemy.orm import attributes, exc as orm_exc
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, SmallInteger
from sqlalchemy.types import TypeDecorator
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base, _fixtures
diff --git a/test/orm/test_load_on_fks.py b/test/orm/test_load_on_fks.py
index 8e4f53b0d..3e7ddb8c2 100644
--- a/test/orm/test_load_on_fks.py
+++ b/test/orm/test_load_on_fks.py
@@ -2,11 +2,11 @@ from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.test.testing import TestBase, eq_, AssertsExecutionResults, assert_raises
-from sqlalchemy.test import testing
+from test.lib.testing import TestBase, eq_, AssertsExecutionResults, assert_raises
+from test.lib import testing
from sqlalchemy.orm.attributes import instance_state
from sqlalchemy.orm.exc import FlushError
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
engine = testing.db
diff --git a/test/orm/test_manytomany.py b/test/orm/test_manytomany.py
index d891e319e..0e7a6e40f 100644
--- a/test/orm/test_manytomany.py
+++ b/test/orm/test_manytomany.py
@@ -1,10 +1,10 @@
-from sqlalchemy.test.testing import assert_raises, \
+from test.lib.testing import assert_raises, \
assert_raises_message, eq_
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session, \
exc as orm_exc, sessionmaker
from test.orm import _base
diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py
index 78e0f2206..7ea4209a8 100644
--- a/test/orm/test_mapper.py
+++ b/test/orm/test_mapper.py
@@ -1,10 +1,10 @@
"""General mapper operations with an emphasis on selecting/loading."""
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import sqlalchemy as sa
-from sqlalchemy.test import testing, pickleable
+from test.lib import testing, pickleable
from sqlalchemy import MetaData, Integer, String, ForeignKey, func, util
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.engine import default
from sqlalchemy.orm import mapper, relationship, backref, \
create_session, class_mapper, configure_mappers, reconstructor, \
@@ -13,10 +13,10 @@ from sqlalchemy.orm import defer, deferred, synonym, attributes, \
column_property, composite, relationship, dynamic_loader, \
comparable_property, AttributeExtension
from sqlalchemy.orm.instrumentation import ClassManager
-from sqlalchemy.test.testing import eq_, AssertsCompiledSQL
+from test.lib.testing import eq_, AssertsCompiledSQL
from test.orm import _base, _fixtures
-from sqlalchemy.test.assertsql import AllOf, CompiledSQL
from sqlalchemy import event
+from test.lib.assertsql import AllOf, CompiledSQL
class MapperTest(_fixtures.FixtureTest):
diff --git a/test/orm/test_merge.py b/test/orm/test_merge.py
index 50d001a8c..fbdbcfe21 100644
--- a/test/orm/test_merge.py
+++ b/test/orm/test_merge.py
@@ -1,18 +1,18 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import sqlalchemy as sa
from sqlalchemy import Integer, PickleType, String
import operator
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy.util import OrderedSet
from sqlalchemy.orm import mapper, relationship, create_session, \
PropComparator, synonym, comparable_property, sessionmaker, \
attributes, Session
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm.interfaces import MapperOption
-from sqlalchemy.test.testing import eq_, ne_
+from test.lib.testing import eq_, ne_
from test.orm import _base, _fixtures
-from sqlalchemy.test.schema import Table, Column
from sqlalchemy import event
+from test.lib.schema import Table, Column
class MergeTest(_fixtures.FixtureTest):
"""Session.merge() functionality"""
diff --git a/test/orm/test_naturalpks.py b/test/orm/test_naturalpks.py
index ca88251d7..f9ce7b3ca 100644
--- a/test/orm/test_naturalpks.py
+++ b/test/orm/test_naturalpks.py
@@ -2,15 +2,15 @@
Primary key changing capabilities and passive/non-passive cascading updates.
"""
-from sqlalchemy.test.testing import eq_, ne_, \
+from test.lib.testing import eq_, ne_, \
assert_raises, assert_raises_message
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, Unicode
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, backref, Session
from sqlalchemy.orm.session import make_transient
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base, _fixtures
class NaturalPKTest(_base.MappedTest):
@@ -862,7 +862,7 @@ class CascadeToFKPKTest(_base.MappedTest, testing.AssertsCompiledSQL):
sess.add(u2)
sess.add(a2)
- from sqlalchemy.test.assertsql import CompiledSQL
+ from test.lib.assertsql import CompiledSQL
# test that the primary key columns of addresses are not
# being updated as well, since this is a row switch.
diff --git a/test/orm/test_onetoone.py b/test/orm/test_onetoone.py
index 4e3cade9b..7097a266e 100644
--- a/test/orm/test_onetoone.py
+++ b/test/orm/test_onetoone.py
@@ -1,7 +1,7 @@
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session
from test.orm import _base
diff --git a/test/orm/test_pickled.py b/test/orm/test_pickled.py
index f23bc92a1..a246ddbdc 100644
--- a/test/orm/test_pickled.py
+++ b/test/orm/test_pickled.py
@@ -1,10 +1,10 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import pickle
import sqlalchemy as sa
-from sqlalchemy.test import testing
-from sqlalchemy.test.testing import assert_raises_message
+from test.lib import testing
+from test.lib.testing import assert_raises_message
from sqlalchemy import Integer, String, ForeignKey, exc, MetaData
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, \
sessionmaker, attributes, interfaces,\
clear_mappers, exc as orm_exc,\
diff --git a/test/orm/test_query.py b/test/orm/test_query.py
index 18c95167f..db7e53a0e 100644
--- a/test/orm/test_query.py
+++ b/test/orm/test_query.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import operator
from sqlalchemy import *
from sqlalchemy import exc as sa_exc, util
@@ -7,10 +7,10 @@ from sqlalchemy.engine import default
from sqlalchemy.orm import *
from sqlalchemy.orm import attributes
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import sqlalchemy as sa
-from sqlalchemy.test import testing, AssertsCompiledSQL, Column, engines
+from test.lib import testing, AssertsCompiledSQL, Column, engines
from test.orm import _fixtures
from test.orm._fixtures import keywords, addresses, Base, \
diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py
index 2ad8bc8aa..423406837 100644
--- a/test/orm/test_relationships.py
+++ b/test/orm/test_relationships.py
@@ -1,14 +1,14 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import datetime
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import Integer, String, ForeignKey, MetaData, and_
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, relation, \
backref, create_session, configure_mappers, \
clear_mappers, sessionmaker, attributes,\
Session, composite, column_property
-from sqlalchemy.test.testing import eq_, startswith_
+from test.lib.testing import eq_, startswith_
from test.orm import _base, _fixtures
diff --git a/test/orm/test_scoping.py b/test/orm/test_scoping.py
index fa1777c85..43472eae3 100644
--- a/test/orm/test_scoping.py
+++ b/test/orm/test_scoping.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy.orm import scoped_session
from sqlalchemy import Integer, String, ForeignKey
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, query
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base
diff --git a/test/orm/test_selectable.py b/test/orm/test_selectable.py
index e46d8bbc8..30278b5bc 100644
--- a/test/orm/test_selectable.py
+++ b/test/orm/test_selectable.py
@@ -1,11 +1,11 @@
"""Generic mapping to Select statements"""
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
import sqlalchemy as sa
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy import String, Integer, select
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, create_session
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.orm import _base
diff --git a/test/orm/test_session.py b/test/orm/test_session.py
index 62047970c..0486a8124 100644
--- a/test/orm/test_session.py
+++ b/test/orm/test_session.py
@@ -1,18 +1,18 @@
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
-from sqlalchemy.test.util import gc_collect
+from test.lib.util import gc_collect
import inspect
import pickle
from sqlalchemy.orm import create_session, sessionmaker, attributes, \
make_transient, Session
from sqlalchemy.orm.attributes import instance_state
import sqlalchemy as sa
-from sqlalchemy.test import engines, testing, config
+from test.lib import engines, testing, config
from sqlalchemy import Integer, String, Sequence
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, backref, joinedload, \
exc as orm_exc, object_session
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from test.engine import _base as engine_base
from test.orm import _base, _fixtures
from sqlalchemy import event
diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py
index 5eba68e13..2aadf26d4 100644
--- a/test/orm/test_subquery_relations.py
+++ b/test/orm/test_subquery_relations.py
@@ -1,13 +1,13 @@
-from sqlalchemy.test.testing import eq_, is_, is_not_
-from sqlalchemy.test import testing
-from sqlalchemy.test.schema import Table, Column
+from test.lib.testing import eq_, is_, is_not_
+from test.lib import testing
+from test.lib.schema import Table, Column
from sqlalchemy import Integer, String, ForeignKey, bindparam
from sqlalchemy.orm import backref, subqueryload, subqueryload_all, \
mapper, relationship, clear_mappers, create_session, lazyload, \
aliased, joinedload, deferred, undefer, eagerload_all
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
-from sqlalchemy.test.assertsql import CompiledSQL
+from test.lib.assertsql import CompiledSQL
from test.orm import _base, _fixtures
import sqlalchemy as sa
diff --git a/test/orm/test_transaction.py b/test/orm/test_transaction.py
index 2617f12f0..7f0ada49f 100644
--- a/test/orm/test_transaction.py
+++ b/test/orm/test_transaction.py
@@ -1,12 +1,12 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy.orm import attributes
from sqlalchemy import exc as sa_exc
from sqlalchemy.orm import exc as orm_exc
from sqlalchemy.orm import *
-from sqlalchemy.test.util import gc_collect
-from sqlalchemy.test import testing
+from test.lib.util import gc_collect
+from test.lib import testing
from test.orm import _base
from test.orm._fixtures import FixtureTest, User, Address, users, addresses
diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py
index 511adde82..6b6251d66 100644
--- a/test/orm/test_unitofwork.py
+++ b/test/orm/test_unitofwork.py
@@ -1,23 +1,23 @@
# coding: utf-8
"""Tests unitofwork operations."""
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import datetime
import operator
from sqlalchemy.orm import mapper as orm_mapper
import sqlalchemy as sa
-from sqlalchemy.test import engines, testing, pickleable
from sqlalchemy import Integer, String, ForeignKey, literal_column, event
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.schema import Column
+from test.lib import engines, testing, pickleable
+from test.lib.schema import Table
+from test.lib.schema import Column
from sqlalchemy.orm import mapper, relationship, create_session, \
column_property, attributes, Session, reconstructor, object_session
-from sqlalchemy.test.testing import eq_, ne_
-from sqlalchemy.test.util import gc_collect
+from test.lib.testing import eq_, ne_
+from test.lib.util import gc_collect
from test.orm import _base, _fixtures
from test.engine import _base as engine_base
-from sqlalchemy.test.assertsql import AllOf, CompiledSQL
+from test.lib.assertsql import AllOf, CompiledSQL
import gc
class UnitOfWorkTest(object):
diff --git a/test/orm/test_unitofworkv2.py b/test/orm/test_unitofworkv2.py
index d91799305..73a884e0c 100644
--- a/test/orm/test_unitofworkv2.py
+++ b/test/orm/test_unitofworkv2.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
-from sqlalchemy.test import testing
-from sqlalchemy.test.schema import Table, Column
+from test.lib.testing import eq_, assert_raises, assert_raises_message
+from test.lib import testing
+from test.lib.schema import Table, Column
from sqlalchemy import Integer, String, ForeignKey, func
from test.orm import _fixtures, _base
from sqlalchemy.orm import mapper, relationship, backref, \
create_session, unitofwork, attributes
-from sqlalchemy.test.assertsql import AllOf, CompiledSQL
+from test.lib.assertsql import AllOf, CompiledSQL
from test.orm._fixtures import keywords, addresses, Base, Keyword, \
Dingaling, item_keywords, dingalings, User, items,\
diff --git a/test/orm/test_utils.py b/test/orm/test_utils.py
index 43a15056c..1ee34c50f 100644
--- a/test/orm/test_utils.py
+++ b/test/orm/test_utils.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy.orm import interfaces, util
from sqlalchemy import Column
from sqlalchemy import Integer
@@ -8,10 +8,10 @@ from sqlalchemy.orm import aliased
from sqlalchemy.orm import mapper, create_session
-from sqlalchemy.test import TestBase, testing
+from test.lib import TestBase, testing
from test.orm import _fixtures
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
class AliasedClassTest(TestBase):
diff --git a/test/orm/test_versioning.py b/test/orm/test_versioning.py
index 6cb2bb9e2..75f7fbb6e 100644
--- a/test/orm/test_versioning.py
+++ b/test/orm/test_versioning.py
@@ -1,9 +1,9 @@
import sqlalchemy as sa
-from sqlalchemy.test import engines, testing
+from test.lib import engines, testing
from sqlalchemy import Integer, String, ForeignKey, literal_column, orm, exc
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, column_property, sessionmaker
-from sqlalchemy.test.testing import eq_, ne_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, ne_, assert_raises, assert_raises_message
from test.orm import _base, _fixtures
from test.engine import _base as engine_base
diff --git a/test/perf/insertspeed.py b/test/perf/insertspeed.py
index 0491e9f95..3ae1ccbde 100644
--- a/test/perf/insertspeed.py
+++ b/test/perf/insertspeed.py
@@ -2,7 +2,7 @@ import testenv; testenv.simple_setup()
import sys, time
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import profiling
+from test.lib import profiling
db = create_engine('sqlite://')
metadata = MetaData(db)
diff --git a/test/perf/large_flush.py b/test/perf/large_flush.py
index 431a28944..b23de080c 100644
--- a/test/perf/large_flush.py
+++ b/test/perf/large_flush.py
@@ -3,7 +3,7 @@ from sqlalchemy import create_engine, MetaData, orm
from sqlalchemy import Column, ForeignKey
from sqlalchemy import Integer, String
from sqlalchemy.orm import mapper
-from sqlalchemy.test import profiling
+from test.lib import profiling
class Object(object):
pass
diff --git a/test/perf/masscreate2.py b/test/perf/masscreate2.py
index e525fcf99..6ad2194cd 100644
--- a/test/perf/masscreate2.py
+++ b/test/perf/masscreate2.py
@@ -3,7 +3,7 @@ import testenv; testenv.simple_setup()
import random, string
from sqlalchemy.orm import attributes
-from sqlalchemy.test.util import gc_collect
+from test.lib.util import gc_collect
# with this test, run top. make sure the Python process doenst grow in size arbitrarily.
diff --git a/test/perf/masseagerload.py b/test/perf/masseagerload.py
index 2ed8d2803..6e6d86d54 100644
--- a/test/perf/masseagerload.py
+++ b/test/perf/masseagerload.py
@@ -1,6 +1,6 @@
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import profiling
+from test.lib import profiling
NUM = 500
DIVISOR = 50
diff --git a/test/perf/massload.py b/test/perf/massload.py
index f6cde3adf..06cfae786 100644
--- a/test/perf/massload.py
+++ b/test/perf/massload.py
@@ -2,7 +2,7 @@ import time
#import sqlalchemy.orm.attributes as attributes
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import *
+from test.lib import *
"""
diff --git a/test/perf/masssave.py b/test/perf/masssave.py
index 41acd12cc..3c1547d38 100644
--- a/test/perf/masssave.py
+++ b/test/perf/masssave.py
@@ -2,7 +2,7 @@ import gc
import types
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import *
+from test.lib import *
NUM = 2500
diff --git a/test/perf/objselectspeed.py b/test/perf/objselectspeed.py
index 816643680..126c9c707 100644
--- a/test/perf/objselectspeed.py
+++ b/test/perf/objselectspeed.py
@@ -1,8 +1,8 @@
import time, resource
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test.util import gc_collect
-from sqlalchemy.test import profiling
+from test.lib.util import gc_collect
+from test.lib import profiling
db = create_engine('sqlite://')
metadata = MetaData(db)
diff --git a/test/perf/objupdatespeed.py b/test/perf/objupdatespeed.py
index fad22189a..078d95fa3 100644
--- a/test/perf/objupdatespeed.py
+++ b/test/perf/objupdatespeed.py
@@ -1,8 +1,8 @@
import time, resource
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import *
-from sqlalchemy.test.util import gc_collect
+from test.lib import *
+from test.lib.util import gc_collect
NUM = 100
diff --git a/test/perf/ormsession.py b/test/perf/ormsession.py
index 0b01fc5a3..aff265ff1 100644
--- a/test/perf/ormsession.py
+++ b/test/perf/ormsession.py
@@ -3,8 +3,8 @@ from datetime import datetime
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import *
-from sqlalchemy.test.profiling import profiled
+from test.lib import *
+from test.lib.profiling import profiled
class Item(object):
def __repr__(self):
diff --git a/test/perf/poolload.py b/test/perf/poolload.py
index 62c66fbae..345720f0c 100644
--- a/test/perf/poolload.py
+++ b/test/perf/poolload.py
@@ -2,7 +2,7 @@
import thread, time
from sqlalchemy import *
import sqlalchemy.pool as pool
-from sqlalchemy.test import testing
+from test.lib import testing
db = create_engine(testing.db.url, pool_timeout=30, echo_pool=True)
metadata = MetaData(db)
diff --git a/test/perf/sessions.py b/test/perf/sessions.py
index 4210732d6..2fe4f758f 100644
--- a/test/perf/sessions.py
+++ b/test/perf/sessions.py
@@ -1,8 +1,8 @@
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test.compat import gc_collect
-from sqlalchemy.test import TestBase, AssertsExecutionResults, profiling, testing
+from test.lib.compat import gc_collect
+from test.lib import TestBase, AssertsExecutionResults, profiling, testing
from test.orm import _fixtures
# in this test we are specifically looking for time spent in the attributes.InstanceState.__cleanup() method.
diff --git a/test/perf/wsgi.py b/test/perf/wsgi.py
index 549c92ade..27aa4a8c8 100644
--- a/test/perf/wsgi.py
+++ b/test/perf/wsgi.py
@@ -4,7 +4,7 @@
from sqlalchemy import *
from sqlalchemy.orm import *
import thread
-from sqlalchemy.test import *
+from test.lib import *
port = 8000
diff --git a/test/sql/test_case_statement.py b/test/sql/test_case_statement.py
index 1a106ee5e..7bc3ab31f 100644
--- a/test/sql/test_case_statement.py
+++ b/test/sql/test_case_statement.py
@@ -1,7 +1,7 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message, eq_
+from test.lib.testing import assert_raises, assert_raises_message, eq_
import sys
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy import util, exc
from sqlalchemy.sql import table, column
diff --git a/test/sql/test_columns.py b/test/sql/test_columns.py
index 3cbb01943..95933b41b 100644
--- a/test/sql/test_columns.py
+++ b/test/sql/test_columns.py
@@ -1,7 +1,7 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy import exc, sql
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy import Table, Column # don't use testlib's wrappers
diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py
index f6cffc116..01fe648a4 100644
--- a/test/sql/test_compiler.py
+++ b/test/sql/test_compiler.py
@@ -1,4 +1,4 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import datetime, re, operator
from sqlalchemy import *
from sqlalchemy import exc, sql, util
@@ -6,7 +6,7 @@ from sqlalchemy.sql import table, column, label, compiler
from sqlalchemy.sql.expression import ClauseList
from sqlalchemy.engine import default
from sqlalchemy.databases import *
-from sqlalchemy.test import *
+from test.lib import *
table1 = table('mytable',
column('myid', Integer),
diff --git a/test/sql/test_constraints.py b/test/sql/test_constraints.py
index 69f29a9bd..56c5c6205 100644
--- a/test/sql/test_constraints.py
+++ b/test/sql/test_constraints.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy import exc, schema
-from sqlalchemy.test import *
-from sqlalchemy.test import config, engines
+from test.lib import *
+from test.lib import config, engines
from sqlalchemy.engine import ddl
-from sqlalchemy.test.testing import eq_
-from sqlalchemy.test.assertsql import AllOf, RegexSQL, ExactSQL, CompiledSQL
+from test.lib.testing import eq_
+from test.lib.assertsql import AllOf, RegexSQL, ExactSQL, CompiledSQL
from sqlalchemy.dialects.postgresql import base as postgresql
class ConstraintTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
diff --git a/test/sql/test_defaults.py b/test/sql/test_defaults.py
index f49e4d0d3..7ec43f8d2 100644
--- a/test/sql/test_defaults.py
+++ b/test/sql/test_defaults.py
@@ -1,13 +1,13 @@
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import datetime
from sqlalchemy import Sequence, Column, func
from sqlalchemy.schema import CreateSequence, DropSequence
from sqlalchemy.sql import select, text
import sqlalchemy as sa
-from sqlalchemy.test import testing, engines
+from test.lib import testing, engines
from sqlalchemy import MetaData, Integer, String, ForeignKey, Boolean, exc
-from sqlalchemy.test.schema import Table
-from sqlalchemy.test.testing import eq_
+from test.lib.schema import Table
+from test.lib.testing import eq_
from test.sql import _base
diff --git a/test/sql/test_functions.py b/test/sql/test_functions.py
index c9bb8348c..396eaaf9b 100644
--- a/test/sql/test_functions.py
+++ b/test/sql/test_functions.py
@@ -1,17 +1,17 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import datetime
from sqlalchemy import *
from sqlalchemy.sql import table, column
from sqlalchemy import databases, sql, util
from sqlalchemy.sql.compiler import BIND_TEMPLATES
from sqlalchemy.engine import default
-from sqlalchemy.test.engines import all_dialects
+from test.lib.engines import all_dialects
from sqlalchemy import types as sqltypes
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy.sql.functions import GenericFunction
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from decimal import Decimal as _python_Decimal
-from sqlalchemy.test import testing
+from test.lib import testing
from sqlalchemy.databases import *
diff --git a/test/sql/test_generative.py b/test/sql/test_generative.py
index 26f9c1ad0..e129f69c4 100644
--- a/test/sql/test_generative.py
+++ b/test/sql/test_generative.py
@@ -1,11 +1,11 @@
from sqlalchemy import *
from sqlalchemy.sql import table, column, ClauseElement
from sqlalchemy.sql.expression import _clone, _from_objects
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy.sql.visitors import *
from sqlalchemy import util
from sqlalchemy.sql import util as sql_util
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
class TraversalTest(TestBase, AssertsExecutionResults):
"""test ClauseVisitor's traversal, particularly its ability to copy and modify
diff --git a/test/sql/test_labels.py b/test/sql/test_labels.py
index f67ba9855..0f84c30a0 100644
--- a/test/sql/test_labels.py
+++ b/test/sql/test_labels.py
@@ -1,7 +1,7 @@
-from sqlalchemy.test.testing import assert_raises, assert_raises_message
+from test.lib.testing import assert_raises, assert_raises_message
from sqlalchemy import *
from sqlalchemy import exc as exceptions
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy.engine import default
IDENT_LENGTH = 29
diff --git a/test/sql/test_query.py b/test/sql/test_query.py
index 410ff73a6..f59b34076 100644
--- a/test/sql/test_query.py
+++ b/test/sql/test_query.py
@@ -1,11 +1,11 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import datetime
from sqlalchemy import *
from sqlalchemy import exc, sql, util
from sqlalchemy.engine import default, base
-from sqlalchemy.test import *
-from sqlalchemy.test.testing import eq_, assert_raises_message, assert_raises
-from sqlalchemy.test.schema import Table, Column
+from test.lib import *
+from test.lib.testing import eq_, assert_raises_message, assert_raises
+from test.lib.schema import Table, Column
class QueryTest(TestBase):
diff --git a/test/sql/test_quote.py b/test/sql/test_quote.py
index 81a68ec74..8f27a7b3c 100644
--- a/test/sql/test_quote.py
+++ b/test/sql/test_quote.py
@@ -1,7 +1,7 @@
from sqlalchemy import *
from sqlalchemy import sql, schema
from sqlalchemy.sql import compiler
-from sqlalchemy.test import *
+from test.lib import *
class QuoteTest(TestBase, AssertsCompiledSQL):
@classmethod
diff --git a/test/sql/test_returning.py b/test/sql/test_returning.py
index 332f4eef5..632a739f1 100644
--- a/test/sql/test_returning.py
+++ b/test/sql/test_returning.py
@@ -1,7 +1,7 @@
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
from sqlalchemy import *
-from sqlalchemy.test import *
-from sqlalchemy.test.schema import Table, Column
+from test.lib import *
+from test.lib.schema import Table, Column
from sqlalchemy.types import TypeDecorator
class ReturningTest(TestBase, AssertsExecutionResults):
diff --git a/test/sql/test_rowcount.py b/test/sql/test_rowcount.py
index ccd0d8f5e..ed40f2801 100644
--- a/test/sql/test_rowcount.py
+++ b/test/sql/test_rowcount.py
@@ -1,5 +1,5 @@
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
class FoundRowsTest(TestBase, AssertsExecutionResults):
diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py
index 5bebbe05f..77acf896b 100644
--- a/test/sql/test_selectable.py
+++ b/test/sql/test_selectable.py
@@ -1,9 +1,9 @@
"""Test various algorithmic properties of selectables."""
-from sqlalchemy.test.testing import eq_, assert_raises, \
+from test.lib.testing import eq_, assert_raises, \
assert_raises_message
from sqlalchemy import *
-from sqlalchemy.test import *
+from test.lib import *
from sqlalchemy.sql import util as sql_util, visitors
from sqlalchemy import exc
from sqlalchemy.sql import table, column, null
diff --git a/test/sql/test_types.py b/test/sql/test_types.py
index 993843891..bfadca7c9 100644
--- a/test/sql/test_types.py
+++ b/test/sql/test_types.py
@@ -1,18 +1,18 @@
# coding: utf-8
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from test.lib.testing import eq_, assert_raises, assert_raises_message
import decimal
import datetime, os, re
from sqlalchemy import *
from sqlalchemy import exc, types, util, schema
from sqlalchemy.sql import operators, column, table
-from sqlalchemy.test.testing import eq_
+from test.lib.testing import eq_
import sqlalchemy.engine.url as url
from sqlalchemy.databases import *
-from sqlalchemy.test.schema import Table, Column
-from sqlalchemy.test import *
-from sqlalchemy.test.util import picklers
+from test.lib.schema import Table, Column
+from test.lib import *
+from test.lib.util import picklers
from decimal import Decimal
-from sqlalchemy.test.util import round_decimal
+from test.lib.util import round_decimal
class AdaptTest(TestBase):
diff --git a/test/sql/test_unicode.py b/test/sql/test_unicode.py
index a116d34cb..2eda4ffa8 100644
--- a/test/sql/test_unicode.py
+++ b/test/sql/test_unicode.py
@@ -2,8 +2,8 @@
"""verrrrry basic unicode column name testing"""
from sqlalchemy import *
-from sqlalchemy.test import *
-from sqlalchemy.test.engines import utf8_engine
+from test.lib import *
+from test.lib.engines import utf8_engine
from sqlalchemy.sql import column
class UnicodeSchemaTest(TestBase):
diff --git a/test/zblog/tables.py b/test/zblog/tables.py
index 4907259e1..3326c28fb 100644
--- a/test/zblog/tables.py
+++ b/test/zblog/tables.py
@@ -1,7 +1,7 @@
"""application table metadata objects are described here."""
from sqlalchemy import *
-from sqlalchemy.test.schema import Table, Column
+from test.lib.schema import Table, Column
metadata = MetaData()
diff --git a/test/zblog/test_zblog.py b/test/zblog/test_zblog.py
index 8103cde8b..ec6402a6e 100644
--- a/test/zblog/test_zblog.py
+++ b/test/zblog/test_zblog.py
@@ -1,6 +1,6 @@
from sqlalchemy import *
from sqlalchemy.orm import *
-from sqlalchemy.test import *
+from test.lib import *
from test.zblog import mappers, tables
from test.zblog.user import *
from test.zblog.blog import *