From 20cdc64588b0f6ae52f8380c11d0ed848005377b Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 27 Sep 2012 02:37:33 -0400 Subject: trying different approaches to test layout. in this one, the testing modules become an externally usable package but still remains within the main sqlalchemy parent package. in this system, we use kind of an ugly hack to get the noseplugin imported outside of the "sqlalchemy" package, while still making it available within sqlalchemy for usage by third party libraries. --- lib/sqlalchemy/testing/__init__.py | 21 + lib/sqlalchemy/testing/assertions.py | 349 ++++++++++++++++ lib/sqlalchemy/testing/assertsql.py | 316 +++++++++++++++ lib/sqlalchemy/testing/config.py | 3 + lib/sqlalchemy/testing/engines.py | 429 ++++++++++++++++++++ lib/sqlalchemy/testing/entities.py | 83 ++++ lib/sqlalchemy/testing/exclusions.py | 269 +++++++++++++ lib/sqlalchemy/testing/fixtures.py | 334 ++++++++++++++++ lib/sqlalchemy/testing/pickleable.py | 107 +++++ lib/sqlalchemy/testing/plugin/__init__.py | 0 lib/sqlalchemy/testing/plugin/config.py | 186 +++++++++ lib/sqlalchemy/testing/plugin/noseplugin.py | 199 ++++++++++ lib/sqlalchemy/testing/profiling.py | 292 ++++++++++++++ lib/sqlalchemy/testing/requirements.py | 38 ++ lib/sqlalchemy/testing/schema.py | 85 ++++ lib/sqlalchemy/testing/suite/__init__.py | 0 lib/sqlalchemy/testing/suite/requirements.py | 24 ++ lib/sqlalchemy/testing/suite/test_ddl.py | 48 +++ lib/sqlalchemy/testing/suite/test_reflection.py | 0 lib/sqlalchemy/testing/suite/test_sequencing.py | 36 ++ lib/sqlalchemy/testing/util.py | 196 +++++++++ lib/sqlalchemy/testing/warnings.py | 43 ++ setup.cfg | 6 +- sqla_nose.py | 5 +- test/aaa_profiling/test_compiler.py | 2 +- test/aaa_profiling/test_memusage.py | 11 +- test/aaa_profiling/test_orm.py | 9 +- test/aaa_profiling/test_pool.py | 2 +- test/aaa_profiling/test_resultset.py | 4 +- test/aaa_profiling/test_zoomark.py | 2 +- test/aaa_profiling/test_zoomark_orm.py | 2 +- test/base/test_dependency.py | 6 +- test/base/test_events.py | 4 +- test/base/test_except.py | 4 +- test/base/test_inspect.py | 4 +- test/base/test_utils.py | 8 +- test/bootstrap/__init__.py | 0 test/bootstrap/config.py | 184 --------- test/bootstrap/noseplugin.py | 190 --------- test/dialect/test_access.py | 2 +- test/dialect/test_firebird.py | 7 +- test/dialect/test_informix.py | 2 +- test/dialect/test_maxdb.py | 4 +- test/dialect/test_mssql.py | 8 +- test/dialect/test_mxodbc.py | 6 +- test/dialect/test_mysql.py | 9 +- test/dialect/test_oracle.py | 8 +- test/dialect/test_postgresql.py | 21 +- test/dialect/test_pyodbc.py | 4 +- test/dialect/test_sqlite.py | 4 +- test/dialect/test_suite.py | 8 + test/dialect/test_sybase.py | 2 +- test/engine/test_bind.py | 10 +- test/engine/test_ddlevents.py | 17 +- test/engine/test_execute.py | 13 +- test/engine/test_parseconnect.py | 5 +- test/engine/test_pool.py | 10 +- test/engine/test_processors.py | 4 +- test/engine/test_reconnect.py | 13 +- test/engine/test_reflection.py | 9 +- test/engine/test_transaction.py | 12 +- test/ex/test_examples.py | 2 +- test/ext/declarative/test_basic.py | 12 +- test/ext/declarative/test_clsregistry.py | 6 +- test/ext/declarative/test_inheritance.py | 10 +- test/ext/declarative/test_mixin.py | 8 +- test/ext/declarative/test_reflection.py | 8 +- test/ext/test_associationproxy.py | 12 +- test/ext/test_compiler.py | 4 +- test/ext/test_extendedattr.py | 8 +- test/ext/test_horizontal_shard.py | 7 +- test/ext/test_hybrid.py | 6 +- test/ext/test_mutable.py | 10 +- test/ext/test_orderinglist.py | 9 +- test/ext/test_serializer.py | 10 +- test/lib/__init__.py | 22 -- test/lib/assertions.py | 349 ---------------- test/lib/assertsql.py | 316 --------------- test/lib/engines.py | 429 -------------------- test/lib/entities.py | 83 ---- test/lib/exclusions.py | 269 ------------- test/lib/fixtures.py | 334 ---------------- test/lib/pickleable.py | 107 ----- test/lib/profiles.txt | 264 ------------- test/lib/profiling.py | 289 -------------- test/lib/requires.py | 506 ------------------------ test/lib/schema.py | 85 ---- test/lib/testing.py | 25 -- test/lib/util.py | 196 --------- test/lib/warnings.py | 43 -- test/orm/_fixtures.py | 6 +- test/orm/inheritance/_poly_fixtures.py | 6 +- test/orm/inheritance/test_abc_inheritance.py | 6 +- test/orm/inheritance/test_abc_polymorphic.py | 8 +- test/orm/inheritance/test_assorted_poly.py | 12 +- test/orm/inheritance/test_basic.py | 13 +- test/orm/inheritance/test_concrete.py | 12 +- test/orm/inheritance/test_magazine.py | 8 +- test/orm/inheritance/test_manytomany.py | 6 +- test/orm/inheritance/test_poly_linked_list.py | 6 +- test/orm/inheritance/test_poly_persistence.py | 8 +- test/orm/inheritance/test_polymorphic_rel.py | 4 +- test/orm/inheritance/test_productspec.py | 6 +- test/orm/inheritance/test_relationship.py | 6 +- test/orm/inheritance/test_selects.py | 4 +- test/orm/inheritance/test_single.py | 8 +- test/orm/inheritance/test_with_poly.py | 6 +- test/orm/test_association.py | 8 +- test/orm/test_assorted_eager.py | 8 +- test/orm/test_attributes.py | 8 +- test/orm/test_backref_mutations.py | 12 +- test/orm/test_bind.py | 10 +- test/orm/test_cascade.py | 10 +- test/orm/test_collection.py | 12 +- test/orm/test_compile.py | 6 +- test/orm/test_composites.py | 10 +- test/orm/test_cycles.py | 10 +- test/orm/test_default_strategies.py | 4 +- test/orm/test_defaults.py | 9 +- test/orm/test_deprecations.py | 8 +- test/orm/test_descriptor.py | 4 +- test/orm/test_dynamic.py | 10 +- test/orm/test_eager_relations.py | 12 +- test/orm/test_evaluator.py | 10 +- test/orm/test_events.py | 10 +- test/orm/test_expire.py | 12 +- test/orm/test_froms.py | 9 +- test/orm/test_generative.py | 12 +- test/orm/test_hasparent.py | 12 +- test/orm/test_immediate_load.py | 4 +- test/orm/test_inspect.py | 4 +- test/orm/test_instrumentation.py | 10 +- test/orm/test_joins.py | 10 +- test/orm/test_lazy_relations.py | 12 +- test/orm/test_load_on_fks.py | 8 +- test/orm/test_lockmode.py | 4 +- test/orm/test_manytomany.py | 10 +- test/orm/test_mapper.py | 12 +- test/orm/test_merge.py | 10 +- test/orm/test_naturalpks.py | 12 +- test/orm/test_of_type.py | 9 +- test/orm/test_onetoone.py | 6 +- test/orm/test_pickled.py | 16 +- test/orm/test_query.py | 13 +- test/orm/test_rel_fn.py | 4 +- test/orm/test_relationships.py | 10 +- test/orm/test_scoping.py | 10 +- test/orm/test_selectable.py | 10 +- test/orm/test_session.py | 14 +- test/orm/test_subquery_relations.py | 12 +- test/orm/test_sync.py | 8 +- test/orm/test_transaction.py | 10 +- test/orm/test_unitofwork.py | 18 +- test/orm/test_unitofworkv2.py | 11 +- test/orm/test_update_delete.py | 6 +- test/orm/test_utils.py | 8 +- test/orm/test_versioning.py | 10 +- test/perf/insertspeed.py | 2 +- test/perf/large_flush.py | 2 +- test/perf/objselectspeed.py | 4 +- test/perf/objupdatespeed.py | 4 +- test/perf/ormsession.py | 4 +- test/perf/sessions.py | 4 +- test/profiles.txt | 264 +++++++++++++ test/requirements.py | 500 +++++++++++++++++++++++ test/sql/test_case_statement.py | 5 +- test/sql/test_compiler.py | 5 +- test/sql/test_constraints.py | 11 +- test/sql/test_cte.py | 4 +- test/sql/test_defaults.py | 11 +- test/sql/test_functions.py | 7 +- test/sql/test_generative.py | 9 +- test/sql/test_inspect.py | 4 +- test/sql/test_labels.py | 9 +- test/sql/test_metadata.py | 16 +- test/sql/test_operators.py | 5 +- test/sql/test_query.py | 10 +- test/sql/test_quote.py | 3 +- test/sql/test_returning.py | 8 +- test/sql/test_rowcount.py | 3 +- test/sql/test_selectable.py | 8 +- test/sql/test_type_expressions.py | 6 +- test/sql/test_types.py | 16 +- test/sql/test_unicode.py | 7 +- test/sql/test_update.py | 7 +- 185 files changed, 4414 insertions(+), 4234 deletions(-) create mode 100644 lib/sqlalchemy/testing/__init__.py create mode 100644 lib/sqlalchemy/testing/assertions.py create mode 100644 lib/sqlalchemy/testing/assertsql.py create mode 100644 lib/sqlalchemy/testing/config.py create mode 100644 lib/sqlalchemy/testing/engines.py create mode 100644 lib/sqlalchemy/testing/entities.py create mode 100644 lib/sqlalchemy/testing/exclusions.py create mode 100644 lib/sqlalchemy/testing/fixtures.py create mode 100644 lib/sqlalchemy/testing/pickleable.py create mode 100644 lib/sqlalchemy/testing/plugin/__init__.py create mode 100644 lib/sqlalchemy/testing/plugin/config.py create mode 100644 lib/sqlalchemy/testing/plugin/noseplugin.py create mode 100644 lib/sqlalchemy/testing/profiling.py create mode 100644 lib/sqlalchemy/testing/requirements.py create mode 100644 lib/sqlalchemy/testing/schema.py create mode 100644 lib/sqlalchemy/testing/suite/__init__.py create mode 100644 lib/sqlalchemy/testing/suite/requirements.py create mode 100644 lib/sqlalchemy/testing/suite/test_ddl.py create mode 100644 lib/sqlalchemy/testing/suite/test_reflection.py create mode 100644 lib/sqlalchemy/testing/suite/test_sequencing.py create mode 100644 lib/sqlalchemy/testing/util.py create mode 100644 lib/sqlalchemy/testing/warnings.py delete mode 100644 test/bootstrap/__init__.py delete mode 100644 test/bootstrap/config.py delete mode 100644 test/bootstrap/noseplugin.py create mode 100644 test/dialect/test_suite.py delete mode 100644 test/lib/__init__.py delete mode 100644 test/lib/assertions.py delete mode 100644 test/lib/assertsql.py delete mode 100644 test/lib/engines.py delete mode 100644 test/lib/entities.py delete mode 100644 test/lib/exclusions.py delete mode 100644 test/lib/fixtures.py delete mode 100644 test/lib/pickleable.py delete mode 100644 test/lib/profiles.txt delete mode 100644 test/lib/profiling.py delete mode 100644 test/lib/requires.py delete mode 100644 test/lib/schema.py delete mode 100644 test/lib/testing.py delete mode 100644 test/lib/util.py delete mode 100644 test/lib/warnings.py create mode 100644 test/profiles.txt create mode 100644 test/requirements.py diff --git a/lib/sqlalchemy/testing/__init__.py b/lib/sqlalchemy/testing/__init__.py new file mode 100644 index 000000000..415705e93 --- /dev/null +++ b/lib/sqlalchemy/testing/__init__.py @@ -0,0 +1,21 @@ +from __future__ import absolute_import + +from .warnings import testing_warn, assert_warnings, resetwarnings + +from . import config + +from .exclusions import db_spec, _is_excluded, fails_if, skip_if, future,\ + fails_on, fails_on_everything_except, skip, only_on, exclude, against,\ + _server_version + +from .assertions import emits_warning, emits_warning_on, uses_deprecated, \ + eq_, ne_, is_, is_not_, startswith_, assert_raises, \ + assert_raises_message, AssertsCompiledSQL, ComparesTables, AssertsExecutionResults + +from .util import run_as_contextmanager, rowset, fail, provide_metadata, adict + +crashes = skip + +from .config import db, requirements as requires + + diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py new file mode 100644 index 000000000..1e8559c1a --- /dev/null +++ b/lib/sqlalchemy/testing/assertions.py @@ -0,0 +1,349 @@ +from __future__ import absolute_import + +from . import util as testutil +from sqlalchemy import pool, orm, util +from sqlalchemy.engine import default +from sqlalchemy import exc as sa_exc +from sqlalchemy.util import decorator +from sqlalchemy import types as sqltypes, schema +import warnings +import re +from .warnings import resetwarnings +from .exclusions import db_spec, _is_excluded +from . import assertsql +from . import config +import itertools +from .util import fail + +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 + + @decorator + def decorate(fn, *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 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) + + @decorator + def decorate(fn, *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 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. + """ + + @decorator + def decorate(fn, *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 decorate + + + +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, str(pool._refs) + + + +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: + 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, unicode(e), re.UNICODE), u"%r !~ %s" % (msg, e) + print unicode(e).encode('utf-8') + + +class AssertsCompiledSQL(object): + def assert_compile(self, clause, result, params=None, + checkparams=None, dialect=None, + checkpositional=None, + use_default_dialect=False, + allow_dialect_select=False): + if use_default_dialect: + dialect = default.DefaultDialect() + elif dialect == None and not allow_dialect_select: + dialect = getattr(self, '__dialect__', None) + if dialect == 'default': + dialect = default.DefaultDialect() + elif dialect is None: + dialect = config.db.dialect + + 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) + if checkpositional is not None: + p = c.construct_params(params) + eq_(tuple([p[x] for x in c.positiontup]), checkpositional) + +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 immutabledict(dict): + def __hash__(self): + return id(self) + + found = util.IdentitySet(result) + expected = set([immutabledict(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.dialect.supports_sequences: + 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/lib/sqlalchemy/testing/assertsql.py b/lib/sqlalchemy/testing/assertsql.py new file mode 100644 index 000000000..897f4b3b1 --- /dev/null +++ b/lib/sqlalchemy/testing/assertsql.py @@ -0,0 +1,316 @@ + +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 = {} + all_params = {} + all_received = [] + 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/lib/sqlalchemy/testing/config.py b/lib/sqlalchemy/testing/config.py new file mode 100644 index 000000000..2945bd456 --- /dev/null +++ b/lib/sqlalchemy/testing/config.py @@ -0,0 +1,3 @@ +requirements = None +db = None + diff --git a/lib/sqlalchemy/testing/engines.py b/lib/sqlalchemy/testing/engines.py new file mode 100644 index 000000000..f7401550e --- /dev/null +++ b/lib/sqlalchemy/testing/engines.py @@ -0,0 +1,429 @@ +from __future__ import absolute_import + +import types +import weakref +from collections import deque +from . import config +from .util import decorator +from sqlalchemy import event, pool +import re +import warnings + +class ConnectionKiller(object): + def __init__(self): + self.proxy_refs = weakref.WeakKeyDictionary() + self.testing_engines = weakref.WeakKeyDictionary() + self.conns = set() + + def add_engine(self, engine): + self.testing_engines[engine] = True + + def connect(self, dbapi_conn, con_record): + self.conns.add(dbapi_conn) + + def checkout(self, dbapi_con, con_record, con_proxy): + self.proxy_refs[con_proxy] = True + + def _safe(self, fn): + try: + fn() + except (SystemExit, KeyboardInterrupt): + raise + except Exception, e: + warnings.warn( + "testing_reaper couldn't " + "rollback/close connection: %s" % e) + + def rollback_all(self): + for rec in self.proxy_refs.keys(): + if rec is not None and rec.is_valid: + self._safe(rec.rollback) + + def close_all(self): + for rec in self.proxy_refs.keys(): + if rec is not None: + self._safe(rec._close) + + def _after_test_ctx(self): + pass + # this can cause a deadlock with pg8000 - pg8000 acquires + # prepared statment lock inside of rollback() - if async gc + # is collecting in finalize_fairy, deadlock. + # not sure if this should be if pypy/jython only + #for conn in self.conns: + # self._safe(conn.rollback) + + def _stop_test_ctx(self): + if config.options.low_connections: + self._stop_test_ctx_minimal() + else: + self._stop_test_ctx_aggressive() + + def _stop_test_ctx_minimal(self): + self.close_all() + + self.conns = set() + + for rec in self.testing_engines.keys(): + if rec is not config.db: + rec.dispose() + + def _stop_test_ctx_aggressive(self): + self.close_all() + for conn in self.conns: + self._safe(conn.close) + self.conns = set() + for rec in self.testing_engines.keys(): + rec.dispose() + + 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, bind): + testing_reaper.close_all() + if hasattr(bind, 'close'): + bind.close() + metadata.drop_all(bind) + +@decorator +def assert_conns_closed(fn, *args, **kw): + try: + fn(*args, **kw) + finally: + testing_reaper.assert_all_closed() + +@decorator +def rollback_open_connections(fn, *args, **kw): + """Decorator that rolls back all open connections after fn execution.""" + + try: + fn(*args, **kw) + finally: + testing_reaper.rollback_all() + +@decorator +def close_first(fn, *args, **kw): + """Decorator that closes all connections before fn execution.""" + + testing_reaper.close_all() + fn(*args, **kw) + + +@decorator +def close_open_connections(fn, *args, **kw): + """Decorator that closes all connections after fn execution.""" + try: + fn(*args, **kw) + finally: + testing_reaper.close_all() + +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 _safe(self, fn): + try: + fn() + except (SystemExit, KeyboardInterrupt): + raise + except Exception, e: + warnings.warn( + "ReconnectFixture couldn't " + "close connection: %s" % e) + + 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): + self._safe(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) + _dispose = engine.dispose + def dispose(): + engine.dialect.dbapi.shutdown() + _dispose() + engine.test_shutdown = engine.dialect.dbapi.shutdown + engine.dispose = dispose + return engine + +def testing_engine(url=None, options=None): + """Produce an engine configured by --options with optional overrides.""" + + from sqlalchemy import create_engine + from .assertsql import asserter + + if not options: + use_reaper = True + else: + use_reaper = options.pop('use_reaper', True) + + url = url or config.db_url + if options is None: + options = config.db_opts + + engine = create_engine(url, **options) + if isinstance(engine.pool, pool.QueuePool): + engine.pool._timeout = 0 + engine.pool._max_overflow = 0 + event.listen(engine, 'after_execute', asserter.execute) + event.listen(engine, 'after_cursor_execute', asserter.cursor_execute) + if use_reaper: + event.listen(engine.pool, 'connect', testing_reaper.connect) + event.listen(engine.pool, 'checkout', testing_reaper.checkout) + testing_reaper.add_engine(engine) + + 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.dialect.name == 'mysql' and \ + config.db.driver in ['mysqldb', 'pymysql']: + # note 1.2.1.gamma.6 or greater of MySQLdb + # needed here + 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 + def print_sql(): + d = engine.dialect + return "\n".join( + str(s.compile(dialect=d)) + for s in engine.mock + ) + engine = create_engine(dialect_name + '://', + strategy='mock', executor=executor) + assert not hasattr(engine, 'mock') + engine.mock = buffer + engine.assert_sql = assert_sql + engine.print_sql = print_sql + return engine + +class DBAPIProxyCursor(object): + """Proxy a DBAPI cursor. + + Tests can provide subclasses of this to intercept + DBAPI-level cursor operations. + + """ + def __init__(self, engine, conn): + self.engine = engine + self.connection = conn + self.cursor = conn.cursor() + + def execute(self, stmt, parameters=None, **kw): + if parameters: + return self.cursor.execute(stmt, parameters, **kw) + else: + return self.cursor.execute(stmt, **kw) + + def executemany(self, stmt, params, **kw): + return self.cursor.executemany(stmt, params, **kw) + + def __getattr__(self, key): + return getattr(self.cursor, key) + +class DBAPIProxyConnection(object): + """Proxy a DBAPI connection. + + Tests can provide subclasses of this to intercept + DBAPI-level connection operations. + + """ + def __init__(self, engine, cursor_cls): + self.conn = self._sqla_unwrap = engine.pool._creator() + self.engine = engine + self.cursor_cls = cursor_cls + + def cursor(self): + return self.cursor_cls(self.engine, self.conn) + + def close(self): + self.conn.close() + + def __getattr__(self, key): + return getattr(self.conn, key) + +def proxying_engine(conn_cls=DBAPIProxyConnection, cursor_cls=DBAPIProxyCursor): + """Produce an engine that provides proxy hooks for + common methods. + + """ + def mock_conn(): + return conn_cls(config.db, cursor_cls) + return testing_engine(options={'creator':mock_conn}) + +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() + + # Py3K + #Natives = set([getattr(types, t) + # for t in dir(types) if not t.startswith('_')]). \ + # union([type(t) if not isinstance(t, type) + # else t for t in __builtins__.values()]).\ + # difference([getattr(types, t) + # for t in ('FunctionType', 'BuiltinFunctionType', + # 'MethodType', 'BuiltinMethodType', + # 'LambdaType', )]) + # Py2K + Natives = set([getattr(types, t) + for t in dir(types) if not t.startswith('_')]). \ + difference([getattr(types, t) + 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/lib/sqlalchemy/testing/entities.py b/lib/sqlalchemy/testing/entities.py new file mode 100644 index 000000000..1b24e73b7 --- /dev/null +++ b/lib/sqlalchemy/testing/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/lib/sqlalchemy/testing/exclusions.py b/lib/sqlalchemy/testing/exclusions.py new file mode 100644 index 000000000..ba2eebe4f --- /dev/null +++ b/lib/sqlalchemy/testing/exclusions.py @@ -0,0 +1,269 @@ +import operator +from nose import SkipTest +from sqlalchemy.util import decorator +from . import config +from sqlalchemy import util + + +def fails_if(predicate, reason=None): + predicate = _as_predicate(predicate) + + @decorator + def decorate(fn, *args, **kw): + if not predicate(): + return fn(*args, **kw) + else: + try: + fn(*args, **kw) + except Exception, ex: + print ("'%s' failed as expected (%s): %s " % ( + fn.__name__, predicate, str(ex))) + return True + else: + raise AssertionError( + "Unexpected success for '%s' (%s)" % + (fn.__name__, predicate)) + return decorate + +def skip_if(predicate, reason=None): + predicate = _as_predicate(predicate) + + @decorator + def decorate(fn, *args, **kw): + if predicate(): + if reason: + msg = "'%s' : %s" % ( + fn.__name__, + reason + ) + else: + msg = "'%s': %s" % ( + fn.__name__, predicate + ) + raise SkipTest(msg) + else: + return fn(*args, **kw) + return decorate + +def only_if(predicate, reason=None): + predicate = _as_predicate(predicate) + return skip_if(NotPredicate(predicate), reason) + +def succeeds_if(predicate, reason=None): + predicate = _as_predicate(predicate) + return fails_if(NotPredicate(predicate), reason) + +class Predicate(object): + @classmethod + def as_predicate(cls, predicate): + if isinstance(predicate, Predicate): + return predicate + elif isinstance(predicate, list): + return OrPredicate([cls.as_predicate(pred) for pred in predicate]) + elif isinstance(predicate, tuple): + return SpecPredicate(*predicate) + elif isinstance(predicate, basestring): + return SpecPredicate(predicate, None, None) + elif util.callable(predicate): + return LambdaPredicate(predicate) + else: + assert False, "unknown predicate type: %s" % predicate + +class SpecPredicate(Predicate): + def __init__(self, db, op=None, spec=None, description=None): + self.db = db + self.op = op + self.spec = spec + self.description = description + + _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], + } + + def __call__(self, engine=None): + if engine is None: + engine = config.db + + if "+" in self.db: + dialect, driver = self.db.split('+') + else: + dialect, driver = self.db, None + + if dialect and engine.name != dialect: + return False + if driver is not None and engine.driver != driver: + return False + + if self.op is not None: + assert driver is None, "DBAPI version specs not supported yet" + + version = _server_version(engine) + oper = hasattr(self.op, '__call__') and self.op \ + or self._ops[self.op] + return oper(version, self.spec) + else: + return True + + def _as_string(self, negate=False): + if self.description is not None: + return self.description + elif self.op is None: + if negate: + return "not %s" % self.db + else: + return "%s" % self.db + else: + if negate: + return "not %s %s %s" % ( + self.db, + self.op, + self.spec + ) + else: + return "%s %s %s" % ( + self.db, + self.op, + self.spec + ) + + def __str__(self): + return self._as_string() + +class LambdaPredicate(Predicate): + def __init__(self, lambda_, description=None, args=None, kw=None): + self.lambda_ = lambda_ + self.args = args or () + self.kw = kw or {} + if description: + self.description = description + elif lambda_.__doc__: + self.description = lambda_.__doc__ + else: + self.description = "custom function" + + def __call__(self): + return self.lambda_(*self.args, **self.kw) + + def _as_string(self, negate=False): + if negate: + return "not " + self.description + else: + return self.description + + def __str__(self): + return self._as_string() + +class NotPredicate(Predicate): + def __init__(self, predicate): + self.predicate = predicate + + def __call__(self, *arg, **kw): + return not self.predicate(*arg, **kw) + + def __str__(self): + return self.predicate._as_string(True) + +class OrPredicate(Predicate): + def __init__(self, predicates, description=None): + self.predicates = predicates + self.description = description + + def __call__(self, *arg, **kw): + for pred in self.predicates: + if pred(*arg, **kw): + self._str = pred + return True + return False + + _str = None + + def _eval_str(self, negate=False): + if self._str is None: + if negate: + conjunction = " and " + else: + conjunction = " or " + return conjunction.join(p._as_string(negate=negate) + for p in self.predicates) + else: + return self._str._as_string(negate=negate) + + def _negation_str(self): + if self.description is not None: + return "Not " + (self.description % {"spec": self._str}) + else: + return self._eval_str(negate=True) + + def _as_string(self, negate=False): + if negate: + return self._negation_str() + else: + if self.description is not None: + return self.description % {"spec": self._str} + else: + return self._eval_str() + + def __str__(self): + return self._as_string() + +_as_predicate = Predicate.as_predicate + +def _is_excluded(db, op, spec): + return SpecPredicate(db, op, spec)() + +def _server_version(engine): + """Return a server_version_info tuple.""" + + # force metadata to be retrieved + conn = engine.connect() + version = getattr(engine.dialect, 'server_version_info', ()) + conn.close() + return version + +def db_spec(*dbs): + return OrPredicate( + Predicate.as_predicate(db) for db in dbs + ) + +def open(fn): + return fn + +@decorator +def future(fn, *args, **kw): + return fails_if(LambdaPredicate(fn, *args, **kw), "Future feature") + +def fails_on(db, reason): + return fails_if(SpecPredicate(db), reason) + +def fails_on_everything_except(*dbs): + return succeeds_if( + OrPredicate([ + SpecPredicate(db) for db in dbs + ]) + ) + +def skip(db, reason): + return skip_if(SpecPredicate(db), reason) + +def only_on(dbs, reason): + return only_if( + OrPredicate([SpecPredicate(db) for db in util.to_list(dbs)]) + ) + + +def exclude(db, op, spec, reason): + return skip_if(SpecPredicate(db, op, spec), reason) + + +def against(*queries): + return OrPredicate([ + Predicate.as_predicate(query) + for query in queries + ])() diff --git a/lib/sqlalchemy/testing/fixtures.py b/lib/sqlalchemy/testing/fixtures.py new file mode 100644 index 000000000..018276d4d --- /dev/null +++ b/lib/sqlalchemy/testing/fixtures.py @@ -0,0 +1,334 @@ +from . import config +from . import assertions, schema +from .util import adict +from .engines import drop_all_tables +from .entities import BasicEntity, ComparableEntity +import sys +import sqlalchemy as sa +from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta + +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 + + def assert_(self, val, msg=None): + assert val, msg + +class TablesTest(TestBase): + + # 'once', None + run_setup_bind = 'once' + + # 'once', 'each', None + run_define_tables = 'once' + + # 'once', 'each', None + run_create_tables = 'once' + + # 'once', 'each', None + run_inserts = 'each' + + # 'each', None + run_deletes = 'each' + + # 'once', None + run_dispose_bind = None + + bind = None + metadata = None + tables = None + other = None + + @classmethod + def setup_class(cls): + cls._init_class() + + cls._setup_once_tables() + + cls._setup_once_inserts() + + @classmethod + def _init_class(cls): + if cls.run_define_tables == 'each': + if cls.run_create_tables == 'once': + cls.run_create_tables = 'each' + assert cls.run_inserts in ('each', None) + + if cls.other is None: + cls.other = adict() + + if cls.tables is None: + cls.tables = adict() + + if cls.bind is None: + setattr(cls, 'bind', cls.setup_bind()) + + if cls.metadata is None: + setattr(cls, 'metadata', sa.MetaData()) + + if cls.metadata.bind is None: + cls.metadata.bind = cls.bind + + @classmethod + def _setup_once_inserts(cls): + if cls.run_inserts == 'once': + cls._load_fixtures() + cls.insert_data() + + @classmethod + def _setup_once_tables(cls): + if cls.run_define_tables == 'once': + cls.define_tables(cls.metadata) + if cls.run_create_tables == 'once': + cls.metadata.create_all(cls.bind) + cls.tables.update(cls.metadata.tables) + + def _setup_each_tables(self): + if self.run_define_tables == 'each': + self.tables.clear() + if self.run_create_tables == 'each': + drop_all_tables(self.metadata, self.bind) + self.metadata.clear() + self.define_tables(self.metadata) + if self.run_create_tables == 'each': + self.metadata.create_all(self.bind) + self.tables.update(self.metadata.tables) + elif self.run_create_tables == 'each': + drop_all_tables(self.metadata, self.bind) + self.metadata.create_all(self.bind) + + def _setup_each_inserts(self): + if self.run_inserts == 'each': + self._load_fixtures() + self.insert_data() + + def _teardown_each_tables(self): + # no need to run deletes if tables are recreated on setup + if self.run_define_tables != 'each' and self.run_deletes == 'each': + for table in reversed(self.metadata.sorted_tables): + try: + table.delete().execute().close() + except sa.exc.DBAPIError, ex: + print >> sys.stderr, "Error emptying table %s: %r" % ( + table, ex) + + def setup(self): + self._setup_each_tables() + self._setup_each_inserts() + + def teardown(self): + self._teardown_each_tables() + + @classmethod + def _teardown_once_metadata_bind(cls): + if cls.run_create_tables: + drop_all_tables(cls.metadata, cls.bind) + + if cls.run_dispose_bind == 'once': + cls.dispose_bind(cls.bind) + + cls.metadata.bind = None + + if cls.run_setup_bind is not None: + cls.bind = None + + @classmethod + def teardown_class(cls): + cls._teardown_once_metadata_bind() + + @classmethod + def setup_bind(cls): + return config.db + + @classmethod + def dispose_bind(cls, bind): + if hasattr(bind, 'dispose'): + bind.dispose() + elif hasattr(bind, 'close'): + bind.close() + + @classmethod + def define_tables(cls, metadata): + pass + + @classmethod + def fixtures(cls): + return {} + + @classmethod + def insert_data(cls): + pass + + def sql_count_(self, count, fn): + self.assert_sql_count(self.bind, fn, count) + + def sql_eq_(self, callable_, statements, with_sequences=None): + self.assert_sql(self.bind, + callable_, statements, with_sequences) + + @classmethod + def _load_fixtures(cls): + """Insert rows as represented by the fixtures() method.""" + headers, rows = {}, {} + for table, data in cls.fixtures().iteritems(): + if len(data) < 2: + continue + if isinstance(table, basestring): + table = cls.tables[table] + headers[table] = data[0] + rows[table] = data[1:] + for table in cls.metadata.sorted_tables: + if table not in headers: + continue + cls.bind.execute( + table.insert(), + [dict(zip(headers[table], column_values)) + for column_values in rows[table]]) + + +class _ORMTest(object): + __requires__ = ('subqueries',) + + @classmethod + def teardown_class(cls): + sa.orm.session.Session.close_all() + sa.orm.clear_mappers() + +class ORMTest(_ORMTest, TestBase): + pass + +class MappedTest(_ORMTest, TablesTest, assertions.AssertsExecutionResults): + # 'once', 'each', None + run_setup_classes = 'once' + + # 'once', 'each', None + run_setup_mappers = 'each' + + classes = None + + @classmethod + def setup_class(cls): + cls._init_class() + + if cls.classes is None: + cls.classes = adict() + + cls._setup_once_tables() + cls._setup_once_classes() + cls._setup_once_mappers() + cls._setup_once_inserts() + + @classmethod + def teardown_class(cls): + cls._teardown_once_class() + cls._teardown_once_metadata_bind() + + def setup(self): + self._setup_each_tables() + self._setup_each_mappers() + self._setup_each_inserts() + + def teardown(self): + sa.orm.session.Session.close_all() + self._teardown_each_mappers() + self._teardown_each_tables() + + @classmethod + def _teardown_once_class(cls): + cls.classes.clear() + _ORMTest.teardown_class() + + + @classmethod + def _setup_once_classes(cls): + if cls.run_setup_classes == 'once': + cls._with_register_classes(cls.setup_classes) + + @classmethod + def _setup_once_mappers(cls): + if cls.run_setup_mappers == 'once': + cls._with_register_classes(cls.setup_mappers) + + def _setup_each_mappers(self): + if self.run_setup_mappers == 'each': + self._with_register_classes(self.setup_mappers) + + @classmethod + def _with_register_classes(cls, fn): + """Run a setup method, framing the operation with a Base class + that will catch new subclasses to be established within + the "classes" registry. + + """ + cls_registry = cls.classes + class FindFixture(type): + def __init__(cls, classname, bases, dict_): + cls_registry[classname] = cls + return type.__init__(cls, classname, bases, dict_) + + + class _Base(object): + __metaclass__ = FindFixture + class Basic(BasicEntity, _Base): + pass + class Comparable(ComparableEntity, _Base): + pass + cls.Basic = Basic + cls.Comparable = Comparable + fn() + + def _teardown_each_mappers(self): + # some tests create mappers in the test bodies + # and will define setup_mappers as None - + # clear mappers in any case + if self.run_setup_mappers != 'once': + sa.orm.clear_mappers() + + @classmethod + def setup_classes(cls): + pass + + @classmethod + def setup_mappers(cls): + pass + +class DeclarativeMappedTest(MappedTest): + run_setup_classes = 'once' + run_setup_mappers = 'once' + + @classmethod + def _setup_once_tables(cls): + pass + + @classmethod + def _with_register_classes(cls, fn): + cls_registry = cls.classes + class FindFixtureDeclarative(DeclarativeMeta): + def __init__(cls, classname, bases, dict_): + cls_registry[classname] = cls + return DeclarativeMeta.__init__( + cls, classname, bases, dict_) + class DeclarativeBasic(object): + __table_cls__ = schema.Table + _DeclBase = declarative_base(metadata=cls.metadata, + metaclass=FindFixtureDeclarative, + cls=DeclarativeBasic) + cls.DeclarativeBasic = _DeclBase + fn() + if cls.metadata.tables: + cls.metadata.create_all(config.db) diff --git a/lib/sqlalchemy/testing/pickleable.py b/lib/sqlalchemy/testing/pickleable.py new file mode 100644 index 000000000..f5b8b827c --- /dev/null +++ b/lib/sqlalchemy/testing/pickleable.py @@ -0,0 +1,107 @@ +"""Classes used in pickling tests, need to be at the module level for unpickling.""" + +from . import fixtures + +class User(fixtures.ComparableEntity): + pass + +class Order(fixtures.ComparableEntity): + pass + +class Dingaling(fixtures.ComparableEntity): + pass + +class EmailUser(User): + pass + +class Address(fixtures.ComparableEntity): + pass + +# TODO: these are kind of arbitrary.... +class Child1(fixtures.ComparableEntity): + pass + +class Child2(fixtures.ComparableEntity): + pass + +class Parent(fixtures.ComparableEntity): + pass + +class Screen(object): + def __init__(self, obj, parent=None): + self.obj = obj + self.parent = parent + +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/lib/sqlalchemy/testing/plugin/__init__.py b/lib/sqlalchemy/testing/plugin/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lib/sqlalchemy/testing/plugin/config.py b/lib/sqlalchemy/testing/plugin/config.py new file mode 100644 index 000000000..08b9753dc --- /dev/null +++ b/lib/sqlalchemy/testing/plugin/config.py @@ -0,0 +1,186 @@ +"""Option and configuration implementations, run by the nose plugin +on test suite startup.""" + +import time +import warnings +import sys +import re + +logging = None +db = None +db_label = None +db_url = None +db_opts = {} +options = None +file_config = None + +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 _zero_timeout(options, opt_str, value, parser): + warnings.warn("--zero-timeout testing option is now on in all cases") + +def _engine_strategy(options, opt_str, value, parser): + if value: + db_opts['strategy'] = value + +pre_configure = [] +post_configure = [] + +def _setup_options(opt, file_config): + global options + from sqlalchemy.testing import config + config.options = options = opt +pre_configure.append(_setup_options) + +def _monkeypatch_cdecimal(options, file_config): + if options.cdecimal: + import sys + import cdecimal + sys.modules['decimal'] = cdecimal +pre_configure.append(_monkeypatch_cdecimal) + +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.append(_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.append(_require) + +def _engine_pool(options, file_config): + if options.mockpool: + from sqlalchemy import pool + db_opts['poolclass'] = pool.AssertionPool +post_configure.append(_engine_pool) + +def _create_testing_engine(options, file_config): + from sqlalchemy.testing import engines, config + from sqlalchemy import testing + global db + config.db = testing.db = db = engines.testing_engine(db_url, db_opts) + config.db_opts = db_opts + config.db_url = db_url + +post_configure.append(_create_testing_engine) + +def _prep_testing_database(options, file_config): + from sqlalchemy.testing 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.append(_prep_testing_database) + +def _set_table_options(options, file_config): + from sqlalchemy.testing import schema + + table_options = schema.table_options + for spec in options.tableopts: + key, value = spec.split('=') + table_options[key] = value + + if options.mysql_engine: + table_options['mysql_engine'] = options.mysql_engine +post_configure.append(_set_table_options) + +def _reverse_topological(options, file_config): + if options.reversetop: + from sqlalchemy.orm import unitofwork, session, mapper, dependency + from sqlalchemy.util import topological + from sqlalchemy.testing.util import RandomSet + topological.set = unitofwork.set = session.set = mapper.set = \ + dependency.set = RandomSet +post_configure.append(_reverse_topological) + +def _requirements(options, file_config): + from sqlalchemy.testing import config + from sqlalchemy import testing + requirement_cls = file_config.get('sqla_testing', "requirement_cls") + + modname, clsname = requirement_cls.split(":") + + # importlib.import_module() only introduced in 2.7, a little + # late + mod = __import__(modname) + for component in modname.split(".")[1:]: + mod = getattr(mod, component) + req_cls = getattr(mod, clsname) + config.requirements = testing.requires = req_cls(db, config) + +post_configure.append(_requirements) + +def _setup_profiling(options, file_config): + from sqlalchemy.testing import profiling + profiling._profile_stats = profiling.ProfileStatsFile( + file_config.get('sqla_testing', 'profile_file')) + +post_configure.append(_setup_profiling) + diff --git a/lib/sqlalchemy/testing/plugin/noseplugin.py b/lib/sqlalchemy/testing/plugin/noseplugin.py new file mode 100644 index 000000000..c9e12c305 --- /dev/null +++ b/lib/sqlalchemy/testing/plugin/noseplugin.py @@ -0,0 +1,199 @@ +"""Enhance nose with extra options and behaviors for running SQLAlchemy tests. + +This module is imported relative to the "plugins" package as a top level +package by the sqla_nose.py runner, so that the plugin can be loaded with +the rest of nose including the coverage plugin before any of SQLAlchemy itself +is imported, so that coverage works. + +When third party libraries use this library, it can be imported +normally as "from sqlalchemy.testing.plugin import noseplugin". + +""" +import os +import ConfigParser + +from nose.plugins import Plugin +from nose import SkipTest +from . import config + +from .config import _log, _list_dbs, _zero_timeout, \ + _engine_strategy, _server_side_cursors, pre_configure,\ + post_configure + +# late imports +fixtures = None +engines = None +exclusions = None +warnings = None +profiling = None +assertions = None +requirements = None +util = None +file_config = None + +class NoseSQLAlchemy(Plugin): + """ + Handles the setup and extra properties required for testing SQLAlchemy + """ + enabled = True + + name = 'sqla_testing' + 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 (multiple OK)") + opt("--log-debug", action="callback", type="string", callback=_log, + help="turn on debug logging for (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") + opt("--mockpool", action="store_true", dest="mockpool", + help="Use mock pool (asserts only one connection used)") + opt("--zero-timeout", action="callback", callback=_zero_timeout, + help="Set pool_timeout to zero, applies to QueuePool only") + opt("--low-connections", action="store_true", dest="low_connections", + help="Use a low number of distinct connections - i.e. for Oracle TNS" + ) + 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("--with-cdecimal", action="store_true", dest="cdecimal", default=False, + help="Monkeypatch the cdecimal library into Python 'decimal' for all tests") + 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") + opt("--write-profiles", action="store_true", dest="write_profiles", default=False, + help="Write/update profiling data.") + global file_config + file_config = ConfigParser.ConfigParser() + file_config.read(['setup.cfg', '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 + for fn in pre_configure: + fn(self.options, file_config) + + def begin(self): + # Lazy setup of other options (post coverage) + for fn in post_configure: + fn(self.options, file_config) + + # late imports, has to happen after config as well + # as nose plugins like coverage + global util, fixtures, engines, exclusions, \ + assertions, warnings, profiling + from sqlalchemy.testing import fixtures, engines, exclusions, \ + assertions, warnings, profiling + from sqlalchemy import util + + def describeTest(self, test): + return "" + + def wantFunction(self, fn): + if fn.__module__.startswith('test.lib') or \ + fn.__module__.startswith('test.bootstrap'): + return False + + 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, fixtures.TestBase): + return False + elif cls.__name__.startswith('_'): + return False + else: + return True + + def _do_skips(self, cls): + from sqlalchemy.testing import config + if hasattr(cls, '__requires__'): + def test_suite(): + return 'ok' + test_suite.__name__ = cls.__name__ + for requirement in cls.__requires__: + check = getattr(config.requirements, requirement) + check(test_suite)() + + if cls.__unsupported_on__: + spec = exclusions.db_spec(*cls.__unsupported_on__) + if spec(config.db): + raise SkipTest( + "'%s' unsupported on DB implementation '%s'" % ( + cls.__name__, config.db.name) + ) + + if getattr(cls, '__only_on__', None): + spec = exclusions.db_spec(*util.to_list(cls.__only_on__)) + if not spec(config.db): + raise SkipTest( + "'%s' unsupported on DB implementation '%s'" % ( + cls.__name__, config.db.name) + ) + + if getattr(cls, '__skip_if__', False): + for c in getattr(cls, '__skip_if__'): + if c(): + raise SkipTest("'%s' skipped by %s" % ( + cls.__name__, c.__name__) + ) + + for db, op, spec in getattr(cls, '__excluded_on__', ()): + exclusions.exclude(db, op, spec, + "'%s' unsupported on DB %s version %s" % ( + cls.__name__, config.db.name, + exclusions._server_version(config.db))) + + def beforeTest(self, test): + warnings.resetwarnings() + profiling._current_test = test.id() + + def afterTest(self, test): + engines.testing_reaper._after_test_ctx() + warnings.resetwarnings() + + def startContext(self, ctx): + if not isinstance(ctx, type) \ + or not issubclass(ctx, fixtures.TestBase): + return + self._do_skips(ctx) + + def stopContext(self, ctx): + if not isinstance(ctx, type) \ + or not issubclass(ctx, fixtures.TestBase): + return + engines.testing_reaper._stop_test_ctx() + if not config.options.low_connections: + assertions.global_cleanup_assertions() diff --git a/lib/sqlalchemy/testing/profiling.py b/lib/sqlalchemy/testing/profiling.py new file mode 100644 index 000000000..be32b1d1d --- /dev/null +++ b/lib/sqlalchemy/testing/profiling.py @@ -0,0 +1,292 @@ +"""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 +import sys +from .util import gc_collect, decorator +from . import config +from nose import SkipTest +import pstats +import time +import collections +from sqlalchemy import util +try: + import cProfile +except ImportError: + cProfile = None +from sqlalchemy.util.compat import jython, pypy, win32 + +_current_test = None + +def profiled(target=None, **target_opts): + """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. + """ + + profile_config = {'targets': set(), + 'report': True, + 'print_callers': False, + 'print_callees': False, + 'graphic': False, + 'sort': ('time', 'calls'), + 'limit': None} + if target is None: + target = 'anonymous_target' + + filename = "%s.prof" % target + + @decorator + def decorate(fn, *args, **kw): + elapsed, load_stats, result = _profile( + filename, fn, *args, **kw) + + graphic = target_opts.get('graphic', profile_config['graphic']) + if graphic: + os.system("runsnake %s" % filename) + else: + 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() + + print_callers = target_opts.get('print_callers', + profile_config['print_callers']) + if print_callers: + stats.print_callers() + + print_callees = target_opts.get('print_callees', + profile_config['print_callees']) + if print_callees: + stats.print_callees() + + os.unlink(filename) + return result + return decorate + + +class ProfileStatsFile(object): + """"Store per-platform/fn profiling results in a file. + + We're still targeting Py2.5, 2.4 on 0.7 with no dependencies, + so no json lib :( need to roll something silly + + """ + def __init__(self, filename): + self.write = config.options is not None and config.options.write_profiles + self.fname = os.path.abspath(filename) + self.short_fname = os.path.split(self.fname)[-1] + self.data = collections.defaultdict(lambda: collections.defaultdict(dict)) + self._read() + if self.write: + # rewrite for the case where features changed, + # etc. + self._write() + + @util.memoized_property + def platform_key(self): + + dbapi_key = config.db.name + "_" + config.db.driver + + # keep it at 2.7, 3.1, 3.2, etc. for now. + py_version = '.'.join([str(v) for v in sys.version_info[0:2]]) + + platform_tokens = [py_version] + platform_tokens.append(dbapi_key) + if jython: + platform_tokens.append("jython") + if pypy: + platform_tokens.append("pypy") + if win32: + platform_tokens.append("win") + _has_cext = config.requirements._has_cextensions() + platform_tokens.append(_has_cext and "cextensions" or "nocextensions") + return "_".join(platform_tokens) + + def has_stats(self): + test_key = _current_test + return test_key in self.data and self.platform_key in self.data[test_key] + + def result(self, callcount): + test_key = _current_test + per_fn = self.data[test_key] + per_platform = per_fn[self.platform_key] + + if 'counts' not in per_platform: + per_platform['counts'] = counts = [] + else: + counts = per_platform['counts'] + + if 'current_count' not in per_platform: + per_platform['current_count'] = current_count = 0 + else: + current_count = per_platform['current_count'] + + has_count = len(counts) > current_count + + if not has_count: + counts.append(callcount) + if self.write: + self._write() + result = None + else: + result = per_platform['lineno'], counts[current_count] + per_platform['current_count'] += 1 + return result + + + def _header(self): + return \ + "# %s\n"\ + "# This file is written out on a per-environment basis.\n"\ + "# For each test in aaa_profiling, the corresponding function and \n"\ + "# environment is located within this file. If it doesn't exist,\n"\ + "# the test is skipped.\n"\ + "# If a callcount does exist, it is compared to what we received. \n"\ + "# assertions are raised if the counts do not match.\n"\ + "# \n"\ + "# To add a new callcount test, apply the function_call_count \n"\ + "# decorator and re-run the tests using the --write-profiles option - \n"\ + "# this file will be rewritten including the new count.\n"\ + "# \n"\ + "" % (self.fname) + + def _read(self): + try: + profile_f = open(self.fname) + except IOError: + return + for lineno, line in enumerate(profile_f): + line = line.strip() + if not line or line.startswith("#"): + continue + + test_key, platform_key, counts = line.split() + per_fn = self.data[test_key] + per_platform = per_fn[platform_key] + per_platform['counts'] = [int(count) for count in counts.split(",")] + per_platform['lineno'] = lineno + 1 + per_platform['current_count'] = 0 + profile_f.close() + + def _write(self): + print("Writing profile file %s" % self.fname) + profile_f = open(self.fname, "w") + profile_f.write(self._header()) + for test_key in sorted(self.data): + + per_fn = self.data[test_key] + profile_f.write("\n# TEST: %s\n\n" % test_key) + for platform_key in sorted(per_fn): + per_platform = per_fn[platform_key] + profile_f.write( + "%s %s %s\n" % ( + test_key, + platform_key, ",".join(str(count) for count in per_platform['counts']) + ) + ) + profile_f.close() + +from sqlalchemy.util.compat import update_wrapper + +def function_call_count(variance=0.05): + """Assert a target for a test case's function call count. + + The main purpose of this assertion is to detect changes in + callcounts for various functions - the actual number is not as important. + Callcounts are stored in a file keyed to Python version and OS platform + information. This file is generated automatically for new tests, + and versioned so that unexpected changes in callcounts will be detected. + + """ + + def decorate(fn): + def wrap(*args, **kw): + + + if cProfile is None: + raise SkipTest("cProfile is not installed") + + if not _profile_stats.has_stats() and not _profile_stats.write: + # run the function anyway, to support dependent tests + # (not a great idea but we have these in test_zoomark) + fn(*args, **kw) + raise SkipTest("No profiling stats available on this " + "platform for this function. Run tests with " + "--write-profiles to add statistics to %s for " + "this platform." % _profile_stats.short_fname) + + gc_collect() + + + timespent, load_stats, fn_result = _profile( + fn, *args, **kw + ) + stats = load_stats() + callcount = stats.total_calls + + expected = _profile_stats.result(callcount) + if expected is None: + expected_count = None + else: + line_no, expected_count = expected + + print("Pstats calls: %d Expected %s" % ( + callcount, + expected_count + ) + ) + stats.print_stats() + #stats.print_callers() + + if expected_count: + deviance = int(callcount * variance) + if abs(callcount - expected_count) > deviance: + raise AssertionError( + "Adjusted function call count %s not within %s%% " + "of expected %s. (Delete line %d of file %s to regenerate " + "this callcount, when tests are run with --write-profiles.)" + % ( + callcount, (variance * 100), + expected_count, line_no, + _profile_stats.fname)) + return fn_result + return update_wrapper(wrap, fn) + return decorate + + +def _profile(fn, *args, **kw): + filename = "%s.prof" % fn.__name__ + + def load_stats(): + st = pstats.Stats(filename) + os.unlink(filename) + return st + + began = time.time() + cProfile.runctx('result = fn(*args, **kw)', globals(), locals(), + filename=filename) + ended = time.time() + + return ended - began, load_stats, locals()['result'] + diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py new file mode 100644 index 000000000..eca883d4e --- /dev/null +++ b/lib/sqlalchemy/testing/requirements.py @@ -0,0 +1,38 @@ +"""Global database feature support policy. + +Provides decorators to mark tests requiring specific feature support from the +target database. + +""" + +from .exclusions import \ + skip, \ + skip_if,\ + only_if,\ + only_on,\ + fails_on,\ + fails_on_everything_except,\ + fails_if,\ + SpecPredicate,\ + against + +def no_support(db, reason): + return SpecPredicate(db, description=reason) + +def exclude(db, op, spec, description=None): + return SpecPredicate(db, op, spec, description=description) + + +def _chain_decorators_on(*decorators): + def decorate(fn): + for decorator in reversed(decorators): + fn = decorator(fn) + return fn + return decorate + +class Requirements(object): + def __init__(self, db, config): + self.db = db + self.config = config + + diff --git a/lib/sqlalchemy/testing/schema.py b/lib/sqlalchemy/testing/schema.py new file mode 100644 index 000000000..03da78c64 --- /dev/null +++ b/lib/sqlalchemy/testing/schema.py @@ -0,0 +1,85 @@ +"""Enhanced versions of schema.Table and schema.Column which establish +desired state for different backends. +""" + +from . import exclusions +from sqlalchemy import schema, event +from . import config + +__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 exclusions.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' + else: + kw['mysql_engine'] = 'MyISAM' + + # Apply some default cascading rules for self-referential foreign keys. + # MySQL InnoDB has some issues around seleting self-refs too. + if exclusions.against('firebird'): + table_name = args[0] + unpack = (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 \ + exclusions.against('firebird', 'oracle'): + def add_seq(c, tbl): + c._init_items( + schema.Sequence(_truncate_name( + config.db.dialect, tbl.name + '_' + c.name + '_seq'), + optional=True) + ) + event.listen(col, 'after_parent_attach', add_seq, propagate=True) + 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/lib/sqlalchemy/testing/suite/__init__.py b/lib/sqlalchemy/testing/suite/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/lib/sqlalchemy/testing/suite/requirements.py b/lib/sqlalchemy/testing/suite/requirements.py new file mode 100644 index 000000000..5eda39b2b --- /dev/null +++ b/lib/sqlalchemy/testing/suite/requirements.py @@ -0,0 +1,24 @@ +from ..requirements import Requirements +from .. import exclusions + + +class SuiteRequirements(Requirements): + + @property + def create_table(self): + """target platform can emit basic CreateTable DDL.""" + + return exclusions.open + + @property + def drop_table(self): + """target platform can emit basic DropTable DDL.""" + + return exclusions.open + + @property + def autoincrement_insert(self): + """target platform generates new surrogate integer primary key values + when insert() is executed, excluding the pk column.""" + + return exclusions.open diff --git a/lib/sqlalchemy/testing/suite/test_ddl.py b/lib/sqlalchemy/testing/suite/test_ddl.py new file mode 100644 index 000000000..1285c4196 --- /dev/null +++ b/lib/sqlalchemy/testing/suite/test_ddl.py @@ -0,0 +1,48 @@ +from .. import fixtures, config, util +from ..config import requirements +from ..assertions import eq_ + +from sqlalchemy import Table, Column, Integer, String + + +class TableDDLTest(fixtures.TestBase): + + def _simple_fixture(self): + return Table('test_table', self.metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + + def _simple_roundtrip(self): + with config.db.begin() as conn: + conn.execute("insert into test_table(id, data) values " + "(1, 'some data')") + result = conn.execute("select id, data from test_table") + eq_( + result.first(), + (1, 'some data') + ) + + + @requirements.create_table + @util.provide_metadata + def test_create_table(self): + table = self._simple_fixture() + table.create( + config.db, checkfirst=False + ) + self._simple_roundtrip() + + + @requirements.drop_table + @util.provide_metadata + def test_drop_table(self): + table = self._simple_fixture() + table.create( + config.db, checkfirst=False + ) + table.drop( + config.db, checkfirst=False + ) + +__all__ = ('TableDDLTest',) \ No newline at end of file diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py new file mode 100644 index 000000000..e69de29bb diff --git a/lib/sqlalchemy/testing/suite/test_sequencing.py b/lib/sqlalchemy/testing/suite/test_sequencing.py new file mode 100644 index 000000000..7b09ecb76 --- /dev/null +++ b/lib/sqlalchemy/testing/suite/test_sequencing.py @@ -0,0 +1,36 @@ +from .. import fixtures, config, util +from ..config import requirements +from ..assertions import eq_ + +from sqlalchemy import Table, Column, Integer, String + + +class InsertSequencingTest(fixtures.TablesTest): + run_deletes = 'each' + + @classmethod + def define_tables(cls, metadata): + Table('plain_pk', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + + def _assert_round_trip(self, table): + row = config.db.execute(table.select()).first() + eq_( + row, + (1, "some data") + ) + + @requirements.autoincrement_insert + def test_autoincrement_on_insert(self): + + config.db.execute( + self.tables.plain_pk.insert(), + data="some data" + ) + self._assert_round_trip(self.tables.plain_pk) + + + +__all__ = ('InsertSequencingTest',) \ No newline at end of file diff --git a/lib/sqlalchemy/testing/util.py b/lib/sqlalchemy/testing/util.py new file mode 100644 index 000000000..625b9e6a5 --- /dev/null +++ b/lib/sqlalchemy/testing/util.py @@ -0,0 +1,196 @@ +from sqlalchemy.util import jython, pypy, defaultdict, decorator +from sqlalchemy.util.compat import decimal + +import gc +import time +import random +import sys +import types + +if jython: + def jython_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 = jython_gc_collect +elif pypy: + def pypy_gc_collect(*args): + gc.collect() + gc.collect() + lazy_gc = pypy_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) + + # 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)) + + +def function_named(fn, name): + """Return a function with a given __name__. + + Will assign to __name__ and return the original function if possible on + the Python implementation, otherwise a new function will be constructed. + + This function should be phased out as much as possible + in favor of @decorator. Tests that "generate" many named tests + should be modernized. + + """ + try: + fn.__name__ = name + except TypeError: + fn = types.FunctionType(fn.func_code, fn.func_globals, name, + fn.func_defaults, fn.func_closure) + return fn + + + +def run_as_contextmanager(ctx, fn, *arg, **kw): + """Run the given function under the given contextmanager, + simulating the behavior of 'with' to support older + Python versions. + + """ + + obj = ctx.__enter__() + try: + result = fn(obj, *arg, **kw) + ctx.__exit__(None, None, None) + return result + except: + exc_info = sys.exc_info() + raise_ = ctx.__exit__(*exc_info) + if raise_ is None: + raise + else: + return raise_ + +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 fail(msg): + assert False, msg + + +@decorator +def provide_metadata(fn, *args, **kw): + """Provide bound MetaData for a single test, dropping afterwards.""" + + from . import config + from sqlalchemy import schema + + metadata = schema.MetaData(config.db) + self = args[0] + prev_meta = getattr(self, 'metadata', None) + self.metadata = metadata + try: + return fn(*args, **kw) + finally: + metadata.drop_all() + self.metadata = prev_meta + +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]) + + diff --git a/lib/sqlalchemy/testing/warnings.py b/lib/sqlalchemy/testing/warnings.py new file mode 100644 index 000000000..799fca128 --- /dev/null +++ b/lib/sqlalchemy/testing/warnings.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import + +import warnings +from sqlalchemy import exc as sa_exc +from sqlalchemy import util + +def testing_warn(msg, stacklevel=3): + """Replaces sqlalchemy.util.warn during tests.""" + + filename = "sqlalchemy.testing.warnings" + lineno = 1 + if isinstance(msg, basestring): + warnings.warn_explicit(msg, sa_exc.SAWarning, filename, lineno) + else: + warnings.warn_explicit(msg, filename, lineno) + +def resetwarnings(): + """Reset warning behavior to testing defaults.""" + + util.warn = util.langhelpers.warn = testing_warn + + warnings.filterwarnings('ignore', + category=sa_exc.SAPendingDeprecationWarning) + warnings.filterwarnings('error', category=sa_exc.SADeprecationWarning) + warnings.filterwarnings('error', category=sa_exc.SAWarning) + +def assert_warnings(fn, warnings): + """Assert that each of the given warnings are emitted by fn.""" + + from .assertions import eq_, emits_warning + + canary = [] + orig_warn = util.warn + def capture_warnings(*args, **kw): + orig_warn(*args, **kw) + popwarn = warnings.pop(0) + canary.append(popwarn) + eq_(args[0], popwarn) + util.warn = util.langhelpers.warn = capture_warnings + + result = emits_warning()(fn)() + assert canary, "No warning was emitted" + return result diff --git a/setup.cfg b/setup.cfg index 01e4149c9..ef1b8dbcc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,14 +2,14 @@ tag_build = dev [nosetests] -with-sqlalchemy = true +with-sqla_testing = true exclude = ^examples first-package-wins = true where = test [sqla_testing] -requirement_cls=test.lib.requires:DefaultRequirements -profile_file=test/lib/profiles.txt +requirement_cls=test.requirements:DefaultRequirements +profile_file=test/profiles.txt [db] sqlite=sqlite:///:memory: diff --git a/sqla_nose.py b/sqla_nose.py index 09481410c..290fc760e 100755 --- a/sqla_nose.py +++ b/sqla_nose.py @@ -9,10 +9,11 @@ installs SQLAlchemy's testing plugin into the local environment. import sys from os import path -for pth in ['.', './lib']: +for pth in ['.', './lib', './lib/sqlalchemy/testing']: sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), pth)) -from test.bootstrap.noseplugin import NoseSQLAlchemy + +from plugin.noseplugin import NoseSQLAlchemy import nose diff --git a/test/aaa_profiling/test_compiler.py b/test/aaa_profiling/test_compiler.py index deff49f0f..2776f05ab 100644 --- a/test/aaa_profiling/test_compiler.py +++ b/test/aaa_profiling/test_compiler.py @@ -1,5 +1,5 @@ from sqlalchemy import * -from test.lib import * +from sqlalchemy.testing import * from sqlalchemy.engine import default class CompileTest(fixtures.TestBase, AssertsExecutionResults): diff --git a/test/aaa_profiling/test_memusage.py b/test/aaa_profiling/test_memusage.py index 9e58a10aa..fca0635d2 100644 --- a/test/aaa_profiling/test_memusage.py +++ b/test/aaa_profiling/test_memusage.py @@ -1,23 +1,24 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy.orm import mapper, relationship, create_session, \ clear_mappers, sessionmaker, class_mapper, aliased,\ Session, subqueryload from sqlalchemy.orm.mapper import _mapper_registry from sqlalchemy.orm.session import _sessions import operator -from test.lib import testing, engines +from sqlalchemy import testing +from sqlalchemy.testing import engines from sqlalchemy import MetaData, Integer, String, ForeignKey, \ PickleType, create_engine, Unicode -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column import sqlalchemy as sa from sqlalchemy.sql import column from sqlalchemy.processors import to_decimal_processor_factory, \ to_unicode_processor_factory -from test.lib.util import gc_collect +from sqlalchemy.testing.util import gc_collect from sqlalchemy.util.compat import decimal import gc import weakref -from test.lib import fixtures +from sqlalchemy.testing import fixtures class A(fixtures.ComparableEntity): pass diff --git a/test/aaa_profiling/test_orm.py b/test/aaa_profiling/test_orm.py index 12b43b229..b9eeb8361 100644 --- a/test/aaa_profiling/test_orm.py +++ b/test/aaa_profiling/test_orm.py @@ -1,11 +1,12 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.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, Session -from test.lib import testing, profiling -from test.lib import fixtures -from test.lib.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing import profiling +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.schema import Table, Column import sys class MergeTest(fixtures.MappedTest): diff --git a/test/aaa_profiling/test_pool.py b/test/aaa_profiling/test_pool.py index 6a3d93230..cf41d2684 100644 --- a/test/aaa_profiling/test_pool.py +++ b/test/aaa_profiling/test_pool.py @@ -1,5 +1,5 @@ from sqlalchemy import * -from test.lib import * +from sqlalchemy.testing import * from sqlalchemy.pool import QueuePool from sqlalchemy import pool as pool_module diff --git a/test/aaa_profiling/test_resultset.py b/test/aaa_profiling/test_resultset.py index 1b519c3f8..cf3a12fe1 100644 --- a/test/aaa_profiling/test_resultset.py +++ b/test/aaa_profiling/test_resultset.py @@ -1,6 +1,6 @@ from sqlalchemy import * -from test.lib import * -from test.lib.testing import eq_ +from sqlalchemy.testing import * +from sqlalchemy.testing import eq_ NUM_FIELDS = 10 NUM_RECORDS = 1000 diff --git a/test/aaa_profiling/test_zoomark.py b/test/aaa_profiling/test_zoomark.py index 97d9f1243..7a9a0e11d 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 test.lib import * +from sqlalchemy.testing 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 64ceb46ff..ef6096022 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 test.lib import * +from sqlalchemy.testing import * ITERATIONS = 1 dbapi_session = engines.ReplayableSession() metadata = None diff --git a/test/base/test_dependency.py b/test/base/test_dependency.py index f3e19982b..f4a0a4c8b 100644 --- a/test/base/test_dependency.py +++ b/test/base/test_dependency.py @@ -1,8 +1,8 @@ from sqlalchemy.util import topological -from test.lib.testing import assert_raises, eq_ -from test.lib.util import conforms_partial_ordering +from sqlalchemy.testing import assert_raises, eq_ +from sqlalchemy.testing.util import conforms_partial_ordering from sqlalchemy import exc -from test.lib import fixtures +from sqlalchemy.testing import fixtures class DependencySortTest(fixtures.TestBase): diff --git a/test/base/test_events.py b/test/base/test_events.py index 657ddc1be..eb58f5183 100644 --- a/test/base/test_events.py +++ b/test/base/test_events.py @@ -1,9 +1,9 @@ """Test event registration and listening.""" -from test.lib.testing import eq_, assert_raises, assert_raises_message, \ +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message, \ is_, is_not_ from sqlalchemy import event, exc, util -from test.lib import fixtures +from sqlalchemy.testing import fixtures class EventsTest(fixtures.TestBase): """Test class- and instance-level event registration.""" diff --git a/test/base/test_except.py b/test/base/test_except.py index 597f1132b..a8c7de201 100644 --- a/test/base/test_except.py +++ b/test/base/test_except.py @@ -2,8 +2,8 @@ from sqlalchemy import exc as sa_exceptions -from test.lib import fixtures -from test.lib.testing import eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import eq_ # Py3K #StandardError = BaseException diff --git a/test/base/test_inspect.py b/test/base/test_inspect.py index c5a60481b..011e2d09f 100644 --- a/test/base/test_inspect.py +++ b/test/base/test_inspect.py @@ -1,9 +1,9 @@ """test the inspection registry system.""" -from test.lib.testing import eq_, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises_message from sqlalchemy import exc, util from sqlalchemy import inspection, inspect -from test.lib import fixtures +from sqlalchemy.testing import fixtures class TestFixture(object): pass diff --git a/test/base/test_utils.py b/test/base/test_utils.py index a9ff7da98..ab92d8c94 100644 --- a/test/base/test_utils.py +++ b/test/base/test_utils.py @@ -1,10 +1,10 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import copy, threading from sqlalchemy import util, sql, exc -from test.lib.testing import eq_, is_, ne_, fails_if -from test.lib.util import gc_collect, picklers +from sqlalchemy.testing import eq_, is_, ne_, fails_if +from sqlalchemy.testing.util import gc_collect, picklers from sqlalchemy.util import classproperty -from test.lib import fixtures +from sqlalchemy.testing import fixtures class OrderedDictTest(fixtures.TestBase): def test_odict(self): diff --git a/test/bootstrap/__init__.py b/test/bootstrap/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/bootstrap/config.py b/test/bootstrap/config.py deleted file mode 100644 index b0e92d630..000000000 --- a/test/bootstrap/config.py +++ /dev/null @@ -1,184 +0,0 @@ -"""Option and configuration implementations, run by the nose plugin -on test suite startup.""" - -import time -import warnings -import sys -import re - -logging = None -db = None -db_label = None -db_url = None -db_opts = {} -options = None -file_config = None - -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 _zero_timeout(options, opt_str, value, parser): - warnings.warn("--zero-timeout testing option is now on in all cases") - -def _engine_strategy(options, opt_str, value, parser): - if value: - db_opts['strategy'] = value - -pre_configure = [] -post_configure = [] - -def _setup_options(opt, file_config): - global options - options = opt -pre_configure.append(_setup_options) - -def _monkeypatch_cdecimal(options, file_config): - if options.cdecimal: - import sys - import cdecimal - sys.modules['decimal'] = cdecimal -pre_configure.append(_monkeypatch_cdecimal) - -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.append(_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.append(_require) - -def _engine_pool(options, file_config): - if options.mockpool: - from sqlalchemy import pool - db_opts['poolclass'] = pool.AssertionPool -post_configure.append(_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.append(_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.append(_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.append(_set_table_options) - -def _reverse_topological(options, file_config): - if options.reversetop: - from sqlalchemy.orm import unitofwork, session, mapper, dependency - from sqlalchemy.util import topological - from test.lib.util import RandomSet - topological.set = unitofwork.set = session.set = mapper.set = \ - dependency.set = RandomSet -post_configure.append(_reverse_topological) - -def _requirements(options, file_config): - from ..lib import testing - requirement_cls = file_config.get('sqla_testing', "requirement_cls") - - modname, clsname = requirement_cls.split(":") - - # importlib.import_module() only introduced in 2.7, a little - # late - mod = __import__(modname) - for component in modname.split(".")[1:]: - mod = getattr(mod, component) - - req_cls = getattr(mod, clsname) - global requirements - requirements = req_cls(db, sys.modules[__name__]) - testing.requires = requirements - -post_configure.append(_requirements) - - -def _setup_profiling(options, file_config): - from ..lib import profiling - profiling._profile_stats = profiling.ProfileStatsFile( - file_config.get('sqla_testing', 'profile_file')) - -post_configure.append(_setup_profiling) \ No newline at end of file diff --git a/test/bootstrap/noseplugin.py b/test/bootstrap/noseplugin.py deleted file mode 100644 index 5608a06a4..000000000 --- a/test/bootstrap/noseplugin.py +++ /dev/null @@ -1,190 +0,0 @@ -import os -import ConfigParser - -from nose.plugins import Plugin -from nose import SkipTest -from . import config - -from .config import _log, _list_dbs, _zero_timeout, \ - _engine_strategy, _server_side_cursors, pre_configure,\ - post_configure - -# late imports -testing = None -fixtures = None -engines = None -exclusions = None -warnings = None -profiling = None -assertions = None -requires = None -util = None -file_config = None - -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 (multiple OK)") - opt("--log-debug", action="callback", type="string", callback=_log, - help="turn on debug logging for (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") - opt("--mockpool", action="store_true", dest="mockpool", - help="Use mock pool (asserts only one connection used)") - opt("--zero-timeout", action="callback", callback=_zero_timeout, - help="Set pool_timeout to zero, applies to QueuePool only") - opt("--low-connections", action="store_true", dest="low_connections", - help="Use a low number of distinct connections - i.e. for Oracle TNS" - ) - 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("--with-cdecimal", action="store_true", dest="cdecimal", default=False, - help="Monkeypatch the cdecimal library into Python 'decimal' for all tests") - 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") - opt("--write-profiles", action="store_true", dest="write_profiles", default=False, - help="Write/update profiling data.") - global file_config - file_config = ConfigParser.ConfigParser() - file_config.read(['setup.cfg', '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 - for fn in pre_configure: - fn(self.options, file_config) - - def begin(self): - # Lazy setup of other options (post coverage) - for fn in post_configure: - fn(self.options, file_config) - - # late imports, has to happen after config as well - # as nose plugins like coverage - global testing, requires, util, fixtures, engines, exclusions, \ - assertions, warnings, profiling - from test.lib import testing, requires, fixtures, engines, exclusions, \ - assertions, warnings, profiling - from sqlalchemy import util - - def describeTest(self, test): - return "" - - def wantFunction(self, fn): - if fn.__module__.startswith('test.lib') or \ - fn.__module__.startswith('test.bootstrap'): - return False - - 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, fixtures.TestBase): - return False - elif cls.__name__.startswith('_'): - return False - else: - return True - - def _do_skips(self, cls): - if hasattr(cls, '__requires__'): - def test_suite(): - return 'ok' - test_suite.__name__ = cls.__name__ - for requirement in cls.__requires__: - check = getattr(config.requirements, requirement) - check(test_suite)() - - if cls.__unsupported_on__: - spec = exclusions.db_spec(*cls.__unsupported_on__) - if spec(config.db): - raise SkipTest( - "'%s' unsupported on DB implementation '%s'" % ( - cls.__name__, config.db.name) - ) - - if getattr(cls, '__only_on__', None): - spec = exclusions.db_spec(*util.to_list(cls.__only_on__)) - if not spec(config.db): - raise SkipTest( - "'%s' unsupported on DB implementation '%s'" % ( - cls.__name__, config.db.name) - ) - - if getattr(cls, '__skip_if__', False): - for c in getattr(cls, '__skip_if__'): - if c(): - raise SkipTest("'%s' skipped by %s" % ( - cls.__name__, c.__name__) - ) - - for db, op, spec in getattr(cls, '__excluded_on__', ()): - exclusions.exclude(db, op, spec, - "'%s' unsupported on DB %s version %s" % ( - cls.__name__, config.db.name, - exclusions._server_version())) - - def beforeTest(self, test): - warnings.resetwarnings() - profiling._current_test = test.id() - - def afterTest(self, test): - engines.testing_reaper._after_test_ctx() - warnings.resetwarnings() - - def startContext(self, ctx): - if not isinstance(ctx, type) \ - or not issubclass(ctx, fixtures.TestBase): - return - self._do_skips(ctx) - - def stopContext(self, ctx): - if not isinstance(ctx, type) \ - or not issubclass(ctx, fixtures.TestBase): - return - engines.testing_reaper._stop_test_ctx() - if not config.options.low_connections: - assertions.global_cleanup_assertions() diff --git a/test/dialect/test_access.py b/test/dialect/test_access.py index e2c20804f..951d43e1a 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 test.lib import * +from sqlalchemy.testing import * class CompileTest(fixtures.TestBase, AssertsCompiledSQL): diff --git a/test/dialect/test_firebird.py b/test/dialect/test_firebird.py index aa57711e4..5a80a3776 100644 --- a/test/dialect/test_firebird.py +++ b/test/dialect/test_firebird.py @@ -1,11 +1,12 @@ -from test.lib.testing import eq_, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises_message from sqlalchemy import exc from sqlalchemy.databases import firebird from sqlalchemy.exc import ProgrammingError from sqlalchemy.sql import table, column from sqlalchemy import types as sqltypes -from test.lib import fixtures, AssertsExecutionResults, AssertsCompiledSQL -from test.lib import testing, engines +from sqlalchemy.testing import fixtures, AssertsExecutionResults, AssertsCompiledSQL +from sqlalchemy import testing +from sqlalchemy.testing import engines from sqlalchemy import String, VARCHAR, NVARCHAR, Unicode, Integer,\ func, insert, update, MetaData, select, Table, Column, text,\ Sequence, Float diff --git a/test/dialect/test_informix.py b/test/dialect/test_informix.py index 3fa2f0470..332edd24e 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 test.lib import * +from sqlalchemy.testing import * class CompileTest(fixtures.TestBase, AssertsCompiledSQL): diff --git a/test/dialect/test_maxdb.py b/test/dialect/test_maxdb.py index e0c3eafbe..237d6c9ff 100644 --- a/test/dialect/test_maxdb.py +++ b/test/dialect/test_maxdb.py @@ -1,12 +1,12 @@ """MaxDB-specific tests.""" -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import StringIO, sys from sqlalchemy import * from sqlalchemy import exc, sql from sqlalchemy.util.compat import decimal from sqlalchemy.databases import maxdb -from test.lib import * +from sqlalchemy.testing import * # TODO diff --git a/test/dialect/test_mssql.py b/test/dialect/test_mssql.py index 9a7c90665..e15324dca 100644 --- a/test/dialect/test_mssql.py +++ b/test/dialect/test_mssql.py @@ -1,5 +1,5 @@ # -*- encoding: utf-8 -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import datetime import os import re @@ -12,8 +12,8 @@ from sqlalchemy.databases import mssql from sqlalchemy.dialects.mssql import pyodbc, mxodbc, pymssql from sqlalchemy.dialects.mssql.base import TIME from sqlalchemy.engine import url -from test.lib import * -from test.lib.testing import eq_, emits_warning_on, \ +from sqlalchemy.testing import * +from sqlalchemy.testing import eq_, emits_warning_on, \ assert_raises_message from sqlalchemy.util.compat import decimal from sqlalchemy.engine.reflection import Inspector @@ -780,7 +780,7 @@ class QueryUnicodeTest(fixtures.TestBase): finally: meta.drop_all() -from test.lib.assertsql import ExactSQL +from sqlalchemy.testing.assertsql import ExactSQL class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase): __only_on__ = 'mssql' diff --git a/test/dialect/test_mxodbc.py b/test/dialect/test_mxodbc.py index 6c6a4b423..32cad4168 100644 --- a/test/dialect/test_mxodbc.py +++ b/test/dialect/test_mxodbc.py @@ -1,7 +1,7 @@ from sqlalchemy import * -from test.lib.testing import eq_ -from test.lib import engines -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import engines +from sqlalchemy.testing import fixtures # 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 92eb52624..2a3ffe7c4 100644 --- a/test/dialect/test_mysql.py +++ b/test/dialect/test_mysql.py @@ -1,15 +1,16 @@ # coding: utf-8 -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message from sqlalchemy import * from sqlalchemy import sql, exc, schema, types as sqltypes from sqlalchemy.dialects.mysql import base as mysql from sqlalchemy.engine.url import make_url -from test.lib import fixtures, AssertsCompiledSQL, AssertsExecutionResults -from test.lib import testing, engines -from test.lib.engines import utf8_engine +from sqlalchemy.testing import fixtures, AssertsCompiledSQL, AssertsExecutionResults +from sqlalchemy import testing +from sqlalchemy.testing import engines +from sqlalchemy.testing.engines import utf8_engine import datetime class CompileTest(fixtures.TestBase, AssertsCompiledSQL): diff --git a/test/dialect/test_oracle.py b/test/dialect/test_oracle.py index 55b5bb4ba..dc39f8e8a 100644 --- a/test/dialect/test_oracle.py +++ b/test/dialect/test_oracle.py @@ -1,12 +1,12 @@ # coding: utf-8 -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy import * from sqlalchemy import types as sqltypes, exc from sqlalchemy.sql import table, column -from test.lib import * -from test.lib.testing import eq_, assert_raises, assert_raises_message -from test.lib.engines import testing_engine +from sqlalchemy.testing import * +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing.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 73633c128..6351ce9d0 100644 --- a/test/dialect/test_postgresql.py +++ b/test/dialect/test_postgresql.py @@ -2,19 +2,24 @@ from __future__ import with_statement -from test.lib.testing import eq_, assert_raises, assert_raises_message, is_ -from test.lib import engines +from sqlalchemy.testing.assertions import eq_, assert_raises, \ + assert_raises_message, is_, AssertsExecutionResults, \ + AssertsCompiledSQL, ComparesTables +from sqlalchemy.testing import engines, fixtures +from sqlalchemy import testing import datetime -from sqlalchemy import * -from sqlalchemy.orm import * +from sqlalchemy import Table, Column, select, MetaData, text, Integer, \ + String, Sequence, ForeignKey, join, Numeric, \ + PrimaryKeyConstraint, DateTime, tuple_, Float, BigInteger, \ + func, literal_column, literal, bindparam, cast, extract, \ + SmallInteger, Enum, REAL, update, insert, Index, delete, \ + and_, Date, TypeDecorator, Time, Unicode, Interval +from sqlalchemy.orm import Session, mapper, aliased from sqlalchemy import exc, schema, types from sqlalchemy.dialects.postgresql import base as postgresql -from sqlalchemy.engine.strategies import MockEngineStrategy from sqlalchemy.util.compat import decimal -from test.lib import * -from test.lib.util import round_decimal +from sqlalchemy.testing.util import round_decimal from sqlalchemy.sql import table, column -from test.lib.testing import eq_ import logging class SequenceTest(fixtures.TestBase, AssertsCompiledSQL): diff --git a/test/dialect/test_pyodbc.py b/test/dialect/test_pyodbc.py index 52d6bc7c4..f5850fe98 100644 --- a/test/dialect/test_pyodbc.py +++ b/test/dialect/test_pyodbc.py @@ -1,6 +1,6 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy.connectors import pyodbc -from test.lib import fixtures +from sqlalchemy.testing import fixtures class PyODBCTest(fixtures.TestBase): def test_pyodbc_version(self): diff --git a/test/dialect/test_sqlite.py b/test/dialect/test_sqlite.py index 07995cdc4..53720d26b 100644 --- a/test/dialect/test_sqlite.py +++ b/test/dialect/test_sqlite.py @@ -1,6 +1,6 @@ """SQLite-specific tests.""" -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message import datetime from sqlalchemy import * @@ -8,7 +8,7 @@ from sqlalchemy import exc, sql, schema, pool, types as sqltypes from sqlalchemy.dialects.sqlite import base as sqlite, \ pysqlite as pysqlite_dialect from sqlalchemy.engine.url import make_url -from test.lib import * +from sqlalchemy.testing import * import os from sqlalchemy.schema import CreateTable diff --git a/test/dialect/test_suite.py b/test/dialect/test_suite.py new file mode 100644 index 000000000..9abff4287 --- /dev/null +++ b/test/dialect/test_suite.py @@ -0,0 +1,8 @@ +from sqlalchemy.testing.suite.test_ddl import * +from sqlalchemy.testing.suite.test_sequencing import * + + + + + + diff --git a/test/dialect/test_sybase.py b/test/dialect/test_sybase.py index 0a7cbf6b6..025d49aae 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 test.lib import * +from sqlalchemy.testing import * class CompileTest(fixtures.TestBase, AssertsCompiledSQL): diff --git a/test/engine/test_bind.py b/test/engine/test_bind.py index 0d7b7d693..30ee43b3b 100644 --- a/test/engine/test_bind.py +++ b/test/engine/test_bind.py @@ -1,15 +1,15 @@ """tests the "bind" attribute/argument across schema and SQL, including the deprecated versions of these arguments""" -from test.lib.testing import eq_, assert_raises +from sqlalchemy.testing import eq_, assert_raises from sqlalchemy import engine, exc from sqlalchemy import MetaData, ThreadLocalMetaData from sqlalchemy import Integer, text -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column import sqlalchemy as sa -from test.lib import testing -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import fixtures class BindTest(fixtures.TestBase): def test_create_drop_explicit(self): diff --git a/test/engine/test_ddlevents.py b/test/engine/test_ddlevents.py index b341801eb..71379ec7e 100644 --- a/test/engine/test_ddlevents.py +++ b/test/engine/test_ddlevents.py @@ -1,16 +1,17 @@ from __future__ import with_statement -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.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 test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column import sqlalchemy as tsa -from test.lib import testing, engines -from test.lib.testing import AssertsCompiledSQL, eq_ +from sqlalchemy import testing +from sqlalchemy.testing import engines +from sqlalchemy.testing import AssertsCompiledSQL, eq_ from nose import SkipTest -from test.lib import fixtures +from sqlalchemy.testing import fixtures class DDLEventTest(fixtures.TestBase): @@ -472,10 +473,10 @@ class DDLExecutionTest(fixtures.TestBase): # We're abusing the DDL() # construct here by pushing a SELECT through it - # so that we can verify the round trip. + # so that we can verify the round trip. # the DDL() will trigger autocommit, which prohibits # some DBAPIs from returning results (pyodbc), so we - # run in an explicit transaction. + # run in an explicit transaction. with testing.db.begin() as conn: eq_( conn.execute( diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index 156a9fc35..ced72f276 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -1,22 +1,23 @@ from __future__ import with_statement -from test.lib.testing import eq_, assert_raises, assert_raises_message, \ +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message, \ config, is_ import re -from test.lib.util import picklers +from sqlalchemy.testing.util import picklers from sqlalchemy.interfaces import ConnectionProxy from sqlalchemy import MetaData, Integer, String, INT, VARCHAR, func, \ bindparam, select, event, TypeDecorator, create_engine, Sequence from sqlalchemy.sql import column, literal -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column import sqlalchemy as tsa -from test.lib import testing, engines -from test.lib.engines import testing_engine +from sqlalchemy import testing +from sqlalchemy.testing import engines +from sqlalchemy.testing.engines import testing_engine import logging from sqlalchemy.dialects.oracle.zxjdbc import ReturningParam from sqlalchemy.engine import result as _result, default from sqlalchemy.engine.base import Connection, Engine -from test.lib import fixtures +from sqlalchemy.testing import fixtures import StringIO users, metadata, users_autoinc = None, None, None diff --git a/test/engine/test_parseconnect.py b/test/engine/test_parseconnect.py index a87c8d056..a00a942cb 100644 --- a/test/engine/test_parseconnect.py +++ b/test/engine/test_parseconnect.py @@ -1,4 +1,4 @@ -from test.lib.testing import assert_raises, assert_raises_message, eq_ +from sqlalchemy.testing import assert_raises, assert_raises_message, eq_ import ConfigParser import StringIO import sqlalchemy.engine.url as url @@ -6,7 +6,8 @@ from sqlalchemy import create_engine, engine_from_config, exc, pool from sqlalchemy.engine.util import _coerce_config from sqlalchemy.engine.default import DefaultDialect import sqlalchemy as tsa -from test.lib import fixtures, testing +from sqlalchemy.testing import fixtures +from sqlalchemy import testing class ParseConnectTest(fixtures.TestBase): def test_rfc1738(self): diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py index 6f00a3c80..d991cc7be 100644 --- a/test/engine/test_pool.py +++ b/test/engine/test_pool.py @@ -1,11 +1,11 @@ import threading, time from sqlalchemy import pool, interfaces, select, event import sqlalchemy as tsa -from test.lib import testing -from test.lib.util import gc_collect, lazy_gc -from test.lib.testing import eq_, assert_raises -from test.lib.engines import testing_engine -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing.util import gc_collect, lazy_gc +from sqlalchemy.testing import eq_, assert_raises +from sqlalchemy.testing.engines import testing_engine +from sqlalchemy.testing import fixtures mcid = 1 class MockDBAPI(object): diff --git a/test/engine/test_processors.py b/test/engine/test_processors.py index d05de6902..bc9af7305 100644 --- a/test/engine/test_processors.py +++ b/test/engine/test_processors.py @@ -1,5 +1,5 @@ -from test.lib import fixtures -from test.lib.testing import assert_raises_message, eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import assert_raises_message, eq_ class _DateProcessorTest(fixtures.TestBase): diff --git a/test/engine/test_reconnect.py b/test/engine/test_reconnect.py index 360a20eb2..6416ce149 100644 --- a/test/engine/test_reconnect.py +++ b/test/engine/test_reconnect.py @@ -1,14 +1,15 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message import time import weakref from sqlalchemy import select, MetaData, Integer, String, pool, create_engine -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column import sqlalchemy as tsa -from test.lib import testing, engines -from test.lib.util import gc_collect +from sqlalchemy import testing +from sqlalchemy.testing import engines +from sqlalchemy.testing.util import gc_collect from sqlalchemy import exc -from test.lib import fixtures -from test.lib.engines import testing_engine +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.engines import testing_engine class MockDisconnect(Exception): pass diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py index 2713bd80b..a909803a1 100644 --- a/test/engine/test_reflection.py +++ b/test/engine/test_reflection.py @@ -5,10 +5,11 @@ from sqlalchemy import types as sql_types from sqlalchemy import schema, events, event, inspect from sqlalchemy import MetaData, Integer, String from sqlalchemy.engine.reflection import Inspector -from test.lib import ComparesTables, \ - testing, engines, AssertsCompiledSQL, fixtures -from test.lib.schema import Table, Column -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import ComparesTables, \ + engines, AssertsCompiledSQL, fixtures +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy import testing metadata, users = None, None diff --git a/test/engine/test_transaction.py b/test/engine/test_transaction.py index 709f0d2f1..ad6813f87 100644 --- a/test/engine/test_transaction.py +++ b/test/engine/test_transaction.py @@ -1,15 +1,15 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, ne_ import sys import time import threading -from test.lib.engines import testing_engine +from sqlalchemy.testing.engines import testing_engine from sqlalchemy import create_engine, MetaData, INT, VARCHAR, Sequence, \ select, Integer, String, func, text, exc -from test.lib.schema import Table -from test.lib.schema import Column -from test.lib import testing -from test.lib import fixtures +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column +from sqlalchemy import testing +from sqlalchemy.testing import fixtures users, metadata = None, None diff --git a/test/ex/test_examples.py b/test/ex/test_examples.py index ce44067fe..3182b5ab5 100644 --- a/test/ex/test_examples.py +++ b/test/ex/test_examples.py @@ -1,4 +1,4 @@ -from test.lib import * +from sqlalchemy.testing import * import os import re diff --git a/test/ext/declarative/test_basic.py b/test/ext/declarative/test_basic.py index 5af2b88dc..19141143f 100644 --- a/test/ext/declarative/test_basic.py +++ b/test/ext/declarative/test_basic.py @@ -1,22 +1,22 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, is_ from sqlalchemy.ext import declarative as decl from sqlalchemy import exc import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import MetaData, Integer, String, ForeignKey, \ ForeignKeyConstraint, Index -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import relationship, create_session, class_mapper, \ joinedload, configure_mappers, backref, clear_mappers, \ deferred, column_property, composite,\ Session -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy.util import classproperty from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase, ConcreteBase -from test.lib import fixtures -from test.lib.util import gc_collect +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.util import gc_collect Base = None diff --git a/test/ext/declarative/test_clsregistry.py b/test/ext/declarative/test_clsregistry.py index f657beb65..db4b01470 100644 --- a/test/ext/declarative/test_clsregistry.py +++ b/test/ext/declarative/test_clsregistry.py @@ -1,6 +1,6 @@ -from test.lib import fixtures -from test.lib.util import gc_collect -from test.lib.testing import assert_raises_message, is_, eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.util import gc_collect +from sqlalchemy.testing import assert_raises_message, is_, eq_ from sqlalchemy import exc, MetaData from sqlalchemy.ext.declarative import clsregistry import weakref diff --git a/test/ext/declarative/test_inheritance.py b/test/ext/declarative/test_inheritance.py index 86bd1c28e..ab78cc3e2 100644 --- a/test/ext/declarative/test_inheritance.py +++ b/test/ext/declarative/test_inheritance.py @@ -1,21 +1,21 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, is_ from sqlalchemy.ext import declarative as decl from sqlalchemy import exc import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import MetaData, Integer, String, ForeignKey, \ ForeignKeyConstraint, Index -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import relationship, create_session, class_mapper, \ joinedload, configure_mappers, backref, clear_mappers, \ polymorphic_union, deferred, column_property, composite,\ Session -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy.util import classproperty from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase, ConcreteBase -from test.lib import fixtures +from sqlalchemy.testing import fixtures Base = None diff --git a/test/ext/declarative/test_mixin.py b/test/ext/declarative/test_mixin.py index bec9a659d..fb674f27a 100644 --- a/test/ext/declarative/test_mixin.py +++ b/test/ext/declarative/test_mixin.py @@ -1,17 +1,17 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, is_ from sqlalchemy.ext import declarative as decl import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import relationship, create_session, class_mapper, \ configure_mappers, clear_mappers, \ deferred, column_property, \ Session from sqlalchemy.util import classproperty from sqlalchemy.ext.declarative import declared_attr -from test.lib import fixtures +from sqlalchemy.testing import fixtures Base = None diff --git a/test/ext/declarative/test_reflection.py b/test/ext/declarative/test_reflection.py index d5fd8e787..013439f93 100644 --- a/test/ext/declarative/test_reflection.py +++ b/test/ext/declarative/test_reflection.py @@ -1,12 +1,12 @@ -from test.lib.testing import eq_, assert_raises +from sqlalchemy.testing import eq_, assert_raises from sqlalchemy.ext import declarative as decl -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import MetaData, Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import relationship, create_session, \ clear_mappers, \ Session -from test.lib import fixtures +from sqlalchemy.testing import fixtures class DeclarativeReflectionBase(fixtures.TablesTest): __requires__ = 'reflectable_autoincrement', diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index 7ae467363..5bec0ad1d 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -1,4 +1,4 @@ -from test.lib.testing import eq_, assert_raises +from sqlalchemy.testing import eq_, assert_raises import copy import pickle @@ -7,12 +7,12 @@ from sqlalchemy.orm import * from sqlalchemy.orm.collections import collection, attribute_mapped_collection from sqlalchemy.ext.associationproxy import * from sqlalchemy.ext.associationproxy import _AssociationList -from test.lib import * -from test.lib.testing import assert_raises_message -from test.lib.util import gc_collect +from sqlalchemy.testing import assert_raises_message +from sqlalchemy.testing.util import gc_collect from sqlalchemy.sql import not_ -from test.lib import fixtures - +from sqlalchemy.testing import fixtures, AssertsCompiledSQL +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column class DictCollection(dict): @collection.appender diff --git a/test/ext/test_compiler.py b/test/ext/test_compiler.py index d4931931a..c1f8b6258 100644 --- a/test/ext/test_compiler.py +++ b/test/ext/test_compiler.py @@ -8,8 +8,8 @@ from sqlalchemy.schema import DDLElement from sqlalchemy.ext.compiler import compiles, deregister from sqlalchemy import exc from sqlalchemy.sql import table, column, visitors -from test.lib.testing import assert_raises_message -from test.lib import * +from sqlalchemy.testing import assert_raises_message +from sqlalchemy.testing import fixtures, AssertsCompiledSQL class UserDefinedTest(fixtures.TestBase, AssertsCompiledSQL): __dialect__ = 'default' diff --git a/test/ext/test_extendedattr.py b/test/ext/test_extendedattr.py index 286c1905f..a550ae4d0 100644 --- a/test/ext/test_extendedattr.py +++ b/test/ext/test_extendedattr.py @@ -1,14 +1,14 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message, ne_ +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message, ne_ from sqlalchemy import util from sqlalchemy.orm import attributes from sqlalchemy.orm.attributes import set_attribute, get_attribute, del_attribute from sqlalchemy.orm.instrumentation import is_instrumented from sqlalchemy.orm import clear_mappers -from test.lib import * -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import fixtures from sqlalchemy.ext import instrumentation from sqlalchemy.orm.instrumentation import register_class -from test.lib.util import decorator +from sqlalchemy.testing.util import decorator from sqlalchemy.orm import events from sqlalchemy import event diff --git a/test/ext/test_horizontal_shard.py b/test/ext/test_horizontal_shard.py index 93ebae48f..0b9b89d2b 100644 --- a/test/ext/test_horizontal_shard.py +++ b/test/ext/test_horizontal_shard.py @@ -5,9 +5,10 @@ from sqlalchemy import sql, util from sqlalchemy.orm import * from sqlalchemy.ext.horizontal_shard import ShardedSession from sqlalchemy.sql import operators -from test.lib import * -from test.lib.engines import testing_engine -from test.lib.testing import eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy import testing +from sqlalchemy.testing.engines import testing_engine +from sqlalchemy.testing import eq_ from nose import SkipTest # TODO: ShardTest can be turned into a base for further subclasses diff --git a/test/ext/test_hybrid.py b/test/ext/test_hybrid.py index 77966e6e5..e7f392a33 100644 --- a/test/ext/test_hybrid.py +++ b/test/ext/test_hybrid.py @@ -1,10 +1,10 @@ from sqlalchemy import func, Integer, String, ForeignKey from sqlalchemy.orm import relationship, Session, aliased -from test.lib.schema import Column +from sqlalchemy.testing.schema import Column from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext import hybrid -from test.lib.testing import eq_, AssertsCompiledSQL, assert_raises_message -from test.lib import fixtures +from sqlalchemy.testing import eq_, AssertsCompiledSQL, assert_raises_message +from sqlalchemy.testing import fixtures class PropertyComparatorTest(fixtures.TestBase, AssertsCompiledSQL): __dialect__ = 'default' diff --git a/test/ext/test_mutable.py b/test/ext/test_mutable.py index c345d8fe3..f56ce4037 100644 --- a/test/ext/test_mutable.py +++ b/test/ext/test_mutable.py @@ -3,11 +3,11 @@ from sqlalchemy.types import PickleType, TypeDecorator, VARCHAR from sqlalchemy.orm import mapper, Session, composite from sqlalchemy.orm.mapper import Mapper from sqlalchemy.orm.instrumentation import ClassManager -from test.lib.schema import Table, Column -from test.lib.testing import eq_, assert_raises_message -from test.lib.util import picklers -from test.lib import testing -from test.lib import fixtures +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import eq_, assert_raises_message +from sqlalchemy.testing.util import picklers +from sqlalchemy import testing +from sqlalchemy.testing import fixtures import sys import pickle diff --git a/test/ext/test_orderinglist.py b/test/ext/test_orderinglist.py index 0b7d4328e..37b992f5b 100644 --- a/test/ext/test_orderinglist.py +++ b/test/ext/test_orderinglist.py @@ -1,10 +1,11 @@ from sqlalchemy import Integer, ForeignKey, String, MetaData from sqlalchemy.orm import relationship, mapper, create_session from sqlalchemy.ext.orderinglist import ordering_list -from test.lib.testing import eq_ -from test.lib import fixtures, testing -from test.lib.schema import Table, Column -from test.lib.util import picklers +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing.util import picklers metadata = None diff --git a/test/ext/test_serializer.py b/test/ext/test_serializer.py index 0496479aa..bf268fbbb 100644 --- a/test/ext/test_serializer.py +++ b/test/ext/test_serializer.py @@ -2,16 +2,16 @@ from sqlalchemy.ext import serializer from sqlalchemy import exc import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import MetaData, Integer, String, ForeignKey, select, \ desc, func, util -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import relationship, sessionmaker, scoped_session, \ class_mapper, mapper, joinedload, configure_mappers, aliased -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import fixtures class User(fixtures.ComparableEntity): pass diff --git a/test/lib/__init__.py b/test/lib/__init__.py deleted file mode 100644 index a1b8eb6d1..000000000 --- a/test/lib/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Testing environment and utilities. - -""" - -from ..bootstrap import config -from . import testing, engines, requires, profiling, pickleable, \ - fixtures -from .schema import Column, Table -from .assertions import AssertsCompiledSQL, \ - AssertsExecutionResults, ComparesTables -from .util import rowset - - -__all__ = ('testing', - 'Column', 'Table', - 'rowset', - 'fixtures', - 'AssertsExecutionResults', - 'AssertsCompiledSQL', 'ComparesTables', - 'engines', 'profiling', 'pickleable') - - diff --git a/test/lib/assertions.py b/test/lib/assertions.py deleted file mode 100644 index 70284799c..000000000 --- a/test/lib/assertions.py +++ /dev/null @@ -1,349 +0,0 @@ -from __future__ import absolute_import - -from . import util as testutil -from sqlalchemy import pool, orm, util -from sqlalchemy.engine import default -from sqlalchemy import exc as sa_exc -from sqlalchemy.util import decorator -from sqlalchemy import types as sqltypes, schema -import warnings -import re -from .warnings import resetwarnings -from .exclusions import db_spec, _is_excluded -from . import assertsql -from ..bootstrap import config -import itertools -from .util import fail - -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 - - @decorator - def decorate(fn, *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 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) - - @decorator - def decorate(fn, *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 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. - """ - - @decorator - def decorate(fn, *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 decorate - - - -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, str(pool._refs) - - - -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: - 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, unicode(e), re.UNICODE), u"%r !~ %s" % (msg, e) - print unicode(e).encode('utf-8') - - -class AssertsCompiledSQL(object): - def assert_compile(self, clause, result, params=None, - checkparams=None, dialect=None, - checkpositional=None, - use_default_dialect=False, - allow_dialect_select=False): - if use_default_dialect: - dialect = default.DefaultDialect() - elif dialect == None and not allow_dialect_select: - dialect = getattr(self, '__dialect__', None) - if dialect == 'default': - dialect = default.DefaultDialect() - elif dialect is None: - dialect = config.db.dialect - - 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) - if checkpositional is not None: - p = c.construct_params(params) - eq_(tuple([p[x] for x in c.positiontup]), checkpositional) - -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 immutabledict(dict): - def __hash__(self): - return id(self) - - found = util.IdentitySet(result) - expected = set([immutabledict(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.dialect.supports_sequences: - 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/assertsql.py b/test/lib/assertsql.py deleted file mode 100644 index 897f4b3b1..000000000 --- a/test/lib/assertsql.py +++ /dev/null @@ -1,316 +0,0 @@ - -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 = {} - all_params = {} - all_received = [] - 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 deleted file mode 100644 index 1431d5a86..000000000 --- a/test/lib/engines.py +++ /dev/null @@ -1,429 +0,0 @@ -from __future__ import absolute_import - -import types -import weakref -from collections import deque -from ..bootstrap import config -from .util import decorator -from sqlalchemy import event, pool -import re -import warnings - -class ConnectionKiller(object): - def __init__(self): - self.proxy_refs = weakref.WeakKeyDictionary() - self.testing_engines = weakref.WeakKeyDictionary() - self.conns = set() - - def add_engine(self, engine): - self.testing_engines[engine] = True - - def connect(self, dbapi_conn, con_record): - self.conns.add(dbapi_conn) - - def checkout(self, dbapi_con, con_record, con_proxy): - self.proxy_refs[con_proxy] = True - - def _safe(self, fn): - try: - fn() - except (SystemExit, KeyboardInterrupt): - raise - except Exception, e: - warnings.warn( - "testing_reaper couldn't " - "rollback/close connection: %s" % e) - - def rollback_all(self): - for rec in self.proxy_refs.keys(): - if rec is not None and rec.is_valid: - self._safe(rec.rollback) - - def close_all(self): - for rec in self.proxy_refs.keys(): - if rec is not None: - self._safe(rec._close) - - def _after_test_ctx(self): - pass - # this can cause a deadlock with pg8000 - pg8000 acquires - # prepared statment lock inside of rollback() - if async gc - # is collecting in finalize_fairy, deadlock. - # not sure if this should be if pypy/jython only - #for conn in self.conns: - # self._safe(conn.rollback) - - def _stop_test_ctx(self): - if config.options.low_connections: - self._stop_test_ctx_minimal() - else: - self._stop_test_ctx_aggressive() - - def _stop_test_ctx_minimal(self): - from test.lib import testing - self.close_all() - - self.conns = set() - - for rec in self.testing_engines.keys(): - if rec is not testing.db: - rec.dispose() - - def _stop_test_ctx_aggressive(self): - self.close_all() - for conn in self.conns: - self._safe(conn.close) - self.conns = set() - for rec in self.testing_engines.keys(): - rec.dispose() - - 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, bind): - testing_reaper.close_all() - if hasattr(bind, 'close'): - bind.close() - metadata.drop_all(bind) - -@decorator -def assert_conns_closed(fn, *args, **kw): - try: - fn(*args, **kw) - finally: - testing_reaper.assert_all_closed() - -@decorator -def rollback_open_connections(fn, *args, **kw): - """Decorator that rolls back all open connections after fn execution.""" - - try: - fn(*args, **kw) - finally: - testing_reaper.rollback_all() - -@decorator -def close_first(fn, *args, **kw): - """Decorator that closes all connections before fn execution.""" - - testing_reaper.close_all() - fn(*args, **kw) - - -@decorator -def close_open_connections(fn, *args, **kw): - """Decorator that closes all connections after fn execution.""" - try: - fn(*args, **kw) - finally: - testing_reaper.close_all() - -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 _safe(self, fn): - try: - fn() - except (SystemExit, KeyboardInterrupt): - raise - except Exception, e: - warnings.warn( - "ReconnectFixture couldn't " - "close connection: %s" % e) - - 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): - self._safe(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) - _dispose = engine.dispose - def dispose(): - engine.dialect.dbapi.shutdown() - _dispose() - engine.test_shutdown = engine.dialect.dbapi.shutdown - engine.dispose = dispose - 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 - - if not options: - use_reaper = True - else: - use_reaper = options.pop('use_reaper', True) - - url = url or config.db_url - options = options or config.db_opts - - engine = create_engine(url, **options) - if isinstance(engine.pool, pool.QueuePool): - engine.pool._timeout = 0 - engine.pool._max_overflow = 0 - event.listen(engine, 'after_execute', asserter.execute) - event.listen(engine, 'after_cursor_execute', asserter.cursor_execute) - if use_reaper: - event.listen(engine.pool, 'connect', testing_reaper.connect) - event.listen(engine.pool, 'checkout', testing_reaper.checkout) - testing_reaper.add_engine(engine) - - 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.dialect.name == 'mysql' and \ - config.db.driver in ['mysqldb', 'pymysql']: - # note 1.2.1.gamma.6 or greater of MySQLdb - # needed here - 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 - def print_sql(): - d = engine.dialect - return "\n".join( - str(s.compile(dialect=d)) - for s in engine.mock - ) - engine = create_engine(dialect_name + '://', - strategy='mock', executor=executor) - assert not hasattr(engine, 'mock') - engine.mock = buffer - engine.assert_sql = assert_sql - engine.print_sql = print_sql - return engine - -class DBAPIProxyCursor(object): - """Proxy a DBAPI cursor. - - Tests can provide subclasses of this to intercept - DBAPI-level cursor operations. - - """ - def __init__(self, engine, conn): - self.engine = engine - self.connection = conn - self.cursor = conn.cursor() - - def execute(self, stmt, parameters=None, **kw): - if parameters: - return self.cursor.execute(stmt, parameters, **kw) - else: - return self.cursor.execute(stmt, **kw) - - def executemany(self, stmt, params, **kw): - return self.cursor.executemany(stmt, params, **kw) - - def __getattr__(self, key): - return getattr(self.cursor, key) - -class DBAPIProxyConnection(object): - """Proxy a DBAPI connection. - - Tests can provide subclasses of this to intercept - DBAPI-level connection operations. - - """ - def __init__(self, engine, cursor_cls): - self.conn = self._sqla_unwrap = engine.pool._creator() - self.engine = engine - self.cursor_cls = cursor_cls - - def cursor(self): - return self.cursor_cls(self.engine, self.conn) - - def close(self): - self.conn.close() - - def __getattr__(self, key): - return getattr(self.conn, key) - -def proxying_engine(conn_cls=DBAPIProxyConnection, cursor_cls=DBAPIProxyCursor): - """Produce an engine that provides proxy hooks for - common methods. - - """ - def mock_conn(): - return conn_cls(config.db, cursor_cls) - return testing_engine(options={'creator':mock_conn}) - -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() - - # Py3K - #Natives = set([getattr(types, t) - # for t in dir(types) if not t.startswith('_')]). \ - # union([type(t) if not isinstance(t, type) - # else t for t in __builtins__.values()]).\ - # difference([getattr(types, t) - # for t in ('FunctionType', 'BuiltinFunctionType', - # 'MethodType', 'BuiltinMethodType', - # 'LambdaType', )]) - # Py2K - Natives = set([getattr(types, t) - for t in dir(types) if not t.startswith('_')]). \ - difference([getattr(types, t) - 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 deleted file mode 100644 index 1b24e73b7..000000000 --- a/test/lib/entities.py +++ /dev/null @@ -1,83 +0,0 @@ -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/exclusions.py b/test/lib/exclusions.py deleted file mode 100644 index b3f5e0f73..000000000 --- a/test/lib/exclusions.py +++ /dev/null @@ -1,269 +0,0 @@ -import operator -from nose import SkipTest -from sqlalchemy.util import decorator -from ..bootstrap import config -from sqlalchemy import util - - -def fails_if(predicate, reason=None): - predicate = _as_predicate(predicate) - - @decorator - def decorate(fn, *args, **kw): - if not predicate(): - return fn(*args, **kw) - else: - try: - fn(*args, **kw) - except Exception, ex: - print ("'%s' failed as expected (%s): %s " % ( - fn.__name__, predicate, str(ex))) - return True - else: - raise AssertionError( - "Unexpected success for '%s' (%s)" % - (fn.__name__, predicate)) - return decorate - -def skip_if(predicate, reason=None): - predicate = _as_predicate(predicate) - - @decorator - def decorate(fn, *args, **kw): - if predicate(): - if reason: - msg = "'%s' : %s" % ( - fn.__name__, - reason - ) - else: - msg = "'%s': %s" % ( - fn.__name__, predicate - ) - raise SkipTest(msg) - else: - return fn(*args, **kw) - return decorate - -def only_if(predicate, reason=None): - predicate = _as_predicate(predicate) - return skip_if(NotPredicate(predicate), reason) - -def succeeds_if(predicate, reason=None): - predicate = _as_predicate(predicate) - return fails_if(NotPredicate(predicate), reason) - -class Predicate(object): - @classmethod - def as_predicate(cls, predicate): - if isinstance(predicate, Predicate): - return predicate - elif isinstance(predicate, list): - return OrPredicate([cls.as_predicate(pred) for pred in predicate]) - elif isinstance(predicate, tuple): - return SpecPredicate(*predicate) - elif isinstance(predicate, basestring): - return SpecPredicate(predicate, None, None) - elif util.callable(predicate): - return LambdaPredicate(predicate) - else: - assert False, "unknown predicate type: %s" % predicate - -class SpecPredicate(Predicate): - def __init__(self, db, op=None, spec=None, description=None): - self.db = db - self.op = op - self.spec = spec - self.description = description - - _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], - } - - def __call__(self, engine=None): - if engine is None: - engine = config.db - - if "+" in self.db: - dialect, driver = self.db.split('+') - else: - dialect, driver = self.db, None - - if dialect and engine.name != dialect: - return False - if driver is not None and engine.driver != driver: - return False - - if self.op is not None: - assert driver is None, "DBAPI version specs not supported yet" - - version = _server_version() - oper = hasattr(self.op, '__call__') and self.op \ - or self._ops[self.op] - return oper(version, self.spec) - else: - return True - - def _as_string(self, negate=False): - if self.description is not None: - return self.description - elif self.op is None: - if negate: - return "not %s" % self.db - else: - return "%s" % self.db - else: - if negate: - return "not %s %s %s" % ( - self.db, - self.op, - self.spec - ) - else: - return "%s %s %s" % ( - self.db, - self.op, - self.spec - ) - - def __str__(self): - return self._as_string() - -class LambdaPredicate(Predicate): - def __init__(self, lambda_, description=None, args=None, kw=None): - self.lambda_ = lambda_ - self.args = args or () - self.kw = kw or {} - if description: - self.description = description - elif lambda_.__doc__: - self.description = lambda_.__doc__ - else: - self.description = "custom function" - - def __call__(self): - return self.lambda_(*self.args, **self.kw) - - def _as_string(self, negate=False): - if negate: - return "not " + self.description - else: - return self.description - - def __str__(self): - return self._as_string() - -class NotPredicate(Predicate): - def __init__(self, predicate): - self.predicate = predicate - - def __call__(self, *arg, **kw): - return not self.predicate(*arg, **kw) - - def __str__(self): - return self.predicate._as_string(True) - -class OrPredicate(Predicate): - def __init__(self, predicates, description=None): - self.predicates = predicates - self.description = description - - def __call__(self, *arg, **kw): - for pred in self.predicates: - if pred(*arg, **kw): - self._str = pred - return True - return False - - _str = None - - def _eval_str(self, negate=False): - if self._str is None: - if negate: - conjunction = " and " - else: - conjunction = " or " - return conjunction.join(p._as_string(negate=negate) - for p in self.predicates) - else: - return self._str._as_string(negate=negate) - - def _negation_str(self): - if self.description is not None: - return "Not " + (self.description % {"spec": self._str}) - else: - return self._eval_str(negate=True) - - def _as_string(self, negate=False): - if negate: - return self._negation_str() - else: - if self.description is not None: - return self.description % {"spec": self._str} - else: - return self._eval_str() - - def __str__(self): - return self._as_string() - -_as_predicate = Predicate.as_predicate - -def _is_excluded(db, op, spec): - return SpecPredicate(db, op, 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 db_spec(*dbs): - return OrPredicate( - Predicate.as_predicate(db) for db in dbs - ) - -@decorator -def future(fn, *args, **kw): - return fails_if(LambdaPredicate(fn, *args, **kw), "Future feature") - -def fails_on(db, reason): - return fails_if(SpecPredicate(db), reason) - -def fails_on_everything_except(*dbs): - return succeeds_if( - OrPredicate([ - SpecPredicate(db) for db in dbs - ]) - ) - -def skip(db, reason): - return skip_if(SpecPredicate(db), reason) - -def only_on(dbs, reason): - return only_if( - OrPredicate([SpecPredicate(db) for db in util.to_list(dbs)]) - ) - - -def exclude(db, op, spec, reason): - return skip_if(SpecPredicate(db, op, spec), reason) - - -def against(*queries): - return OrPredicate([ - Predicate.as_predicate(query) - for query in queries - ])() diff --git a/test/lib/fixtures.py b/test/lib/fixtures.py deleted file mode 100644 index 722b64cfc..000000000 --- a/test/lib/fixtures.py +++ /dev/null @@ -1,334 +0,0 @@ -from ..bootstrap import config -from . import assertions, schema -from .util import adict -from .engines import drop_all_tables -from .entities import BasicEntity, ComparableEntity -import sys -import sqlalchemy as sa -from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta - -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 - - def assert_(self, val, msg=None): - assert val, msg - -class TablesTest(TestBase): - - # 'once', None - run_setup_bind = 'once' - - # 'once', 'each', None - run_define_tables = 'once' - - # 'once', 'each', None - run_create_tables = 'once' - - # 'once', 'each', None - run_inserts = 'each' - - # 'each', None - run_deletes = 'each' - - # 'once', None - run_dispose_bind = None - - bind = None - metadata = None - tables = None - other = None - - @classmethod - def setup_class(cls): - cls._init_class() - - cls._setup_once_tables() - - cls._setup_once_inserts() - - @classmethod - def _init_class(cls): - if cls.run_define_tables == 'each': - if cls.run_create_tables == 'once': - cls.run_create_tables = 'each' - assert cls.run_inserts in ('each', None) - - if cls.other is None: - cls.other = adict() - - if cls.tables is None: - cls.tables = adict() - - if cls.bind is None: - setattr(cls, 'bind', cls.setup_bind()) - - if cls.metadata is None: - setattr(cls, 'metadata', sa.MetaData()) - - if cls.metadata.bind is None: - cls.metadata.bind = cls.bind - - @classmethod - def _setup_once_inserts(cls): - if cls.run_inserts == 'once': - cls._load_fixtures() - cls.insert_data() - - @classmethod - def _setup_once_tables(cls): - if cls.run_define_tables == 'once': - cls.define_tables(cls.metadata) - if cls.run_create_tables == 'once': - cls.metadata.create_all(cls.bind) - cls.tables.update(cls.metadata.tables) - - def _setup_each_tables(self): - if self.run_define_tables == 'each': - self.tables.clear() - if self.run_create_tables == 'each': - drop_all_tables(self.metadata, self.bind) - self.metadata.clear() - self.define_tables(self.metadata) - if self.run_create_tables == 'each': - self.metadata.create_all(self.bind) - self.tables.update(self.metadata.tables) - elif self.run_create_tables == 'each': - drop_all_tables(self.metadata, self.bind) - self.metadata.create_all(self.bind) - - def _setup_each_inserts(self): - if self.run_inserts == 'each': - self._load_fixtures() - self.insert_data() - - def _teardown_each_tables(self): - # no need to run deletes if tables are recreated on setup - if self.run_define_tables != 'each' and self.run_deletes == 'each': - for table in reversed(self.metadata.sorted_tables): - try: - table.delete().execute().close() - except sa.exc.DBAPIError, ex: - print >> sys.stderr, "Error emptying table %s: %r" % ( - table, ex) - - def setup(self): - self._setup_each_tables() - self._setup_each_inserts() - - def teardown(self): - self._teardown_each_tables() - - @classmethod - def _teardown_once_metadata_bind(cls): - if cls.run_create_tables: - drop_all_tables(cls.metadata, cls.bind) - - if cls.run_dispose_bind == 'once': - cls.dispose_bind(cls.bind) - - cls.metadata.bind = None - - if cls.run_setup_bind is not None: - cls.bind = None - - @classmethod - def teardown_class(cls): - cls._teardown_once_metadata_bind() - - @classmethod - def setup_bind(cls): - return config.db - - @classmethod - def dispose_bind(cls, bind): - if hasattr(bind, 'dispose'): - bind.dispose() - elif hasattr(bind, 'close'): - bind.close() - - @classmethod - def define_tables(cls, metadata): - pass - - @classmethod - def fixtures(cls): - return {} - - @classmethod - def insert_data(cls): - pass - - def sql_count_(self, count, fn): - self.assert_sql_count(self.bind, fn, count) - - def sql_eq_(self, callable_, statements, with_sequences=None): - self.assert_sql(self.bind, - callable_, statements, with_sequences) - - @classmethod - def _load_fixtures(cls): - """Insert rows as represented by the fixtures() method.""" - headers, rows = {}, {} - for table, data in cls.fixtures().iteritems(): - if len(data) < 2: - continue - if isinstance(table, basestring): - table = cls.tables[table] - headers[table] = data[0] - rows[table] = data[1:] - for table in cls.metadata.sorted_tables: - if table not in headers: - continue - cls.bind.execute( - table.insert(), - [dict(zip(headers[table], column_values)) - for column_values in rows[table]]) - - -class _ORMTest(object): - __requires__ = ('subqueries',) - - @classmethod - def teardown_class(cls): - sa.orm.session.Session.close_all() - sa.orm.clear_mappers() - -class ORMTest(_ORMTest, TestBase): - pass - -class MappedTest(_ORMTest, TablesTest, assertions.AssertsExecutionResults): - # 'once', 'each', None - run_setup_classes = 'once' - - # 'once', 'each', None - run_setup_mappers = 'each' - - classes = None - - @classmethod - def setup_class(cls): - cls._init_class() - - if cls.classes is None: - cls.classes = adict() - - cls._setup_once_tables() - cls._setup_once_classes() - cls._setup_once_mappers() - cls._setup_once_inserts() - - @classmethod - def teardown_class(cls): - cls._teardown_once_class() - cls._teardown_once_metadata_bind() - - def setup(self): - self._setup_each_tables() - self._setup_each_mappers() - self._setup_each_inserts() - - def teardown(self): - sa.orm.session.Session.close_all() - self._teardown_each_mappers() - self._teardown_each_tables() - - @classmethod - def _teardown_once_class(cls): - cls.classes.clear() - _ORMTest.teardown_class() - - - @classmethod - def _setup_once_classes(cls): - if cls.run_setup_classes == 'once': - cls._with_register_classes(cls.setup_classes) - - @classmethod - def _setup_once_mappers(cls): - if cls.run_setup_mappers == 'once': - cls._with_register_classes(cls.setup_mappers) - - def _setup_each_mappers(self): - if self.run_setup_mappers == 'each': - self._with_register_classes(self.setup_mappers) - - @classmethod - def _with_register_classes(cls, fn): - """Run a setup method, framing the operation with a Base class - that will catch new subclasses to be established within - the "classes" registry. - - """ - cls_registry = cls.classes - class FindFixture(type): - def __init__(cls, classname, bases, dict_): - cls_registry[classname] = cls - return type.__init__(cls, classname, bases, dict_) - - - class _Base(object): - __metaclass__ = FindFixture - class Basic(BasicEntity, _Base): - pass - class Comparable(ComparableEntity, _Base): - pass - cls.Basic = Basic - cls.Comparable = Comparable - fn() - - def _teardown_each_mappers(self): - # some tests create mappers in the test bodies - # and will define setup_mappers as None - - # clear mappers in any case - if self.run_setup_mappers != 'once': - sa.orm.clear_mappers() - - @classmethod - def setup_classes(cls): - pass - - @classmethod - def setup_mappers(cls): - pass - -class DeclarativeMappedTest(MappedTest): - run_setup_classes = 'once' - run_setup_mappers = 'once' - - @classmethod - def _setup_once_tables(cls): - pass - - @classmethod - def _with_register_classes(cls, fn): - cls_registry = cls.classes - class FindFixtureDeclarative(DeclarativeMeta): - def __init__(cls, classname, bases, dict_): - cls_registry[classname] = cls - return DeclarativeMeta.__init__( - cls, classname, bases, dict_) - class DeclarativeBasic(object): - __table_cls__ = schema.Table - _DeclBase = declarative_base(metadata=cls.metadata, - metaclass=FindFixtureDeclarative, - cls=DeclarativeBasic) - cls.DeclarativeBasic = _DeclBase - fn() - if cls.metadata.tables: - cls.metadata.create_all(config.db) diff --git a/test/lib/pickleable.py b/test/lib/pickleable.py deleted file mode 100644 index f5b8b827c..000000000 --- a/test/lib/pickleable.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Classes used in pickling tests, need to be at the module level for unpickling.""" - -from . import fixtures - -class User(fixtures.ComparableEntity): - pass - -class Order(fixtures.ComparableEntity): - pass - -class Dingaling(fixtures.ComparableEntity): - pass - -class EmailUser(User): - pass - -class Address(fixtures.ComparableEntity): - pass - -# TODO: these are kind of arbitrary.... -class Child1(fixtures.ComparableEntity): - pass - -class Child2(fixtures.ComparableEntity): - pass - -class Parent(fixtures.ComparableEntity): - pass - -class Screen(object): - def __init__(self, obj, parent=None): - self.obj = obj - self.parent = parent - -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/profiles.txt b/test/lib/profiles.txt deleted file mode 100644 index 5decc3aaa..000000000 --- a/test/lib/profiles.txt +++ /dev/null @@ -1,264 +0,0 @@ -# /Users/classic/dev/sqlalchemy/./test/lib/profiles.txt -# This file is written out on a per-environment basis. -# For each test in aaa_profiling, the corresponding function and -# environment is located within this file. If it doesn't exist, -# the test is skipped. -# If a callcount does exist, it is compared to what we received. -# assertions are raised if the counts do not match. -# -# To add a new callcount test, apply the function_call_count -# decorator and re-run the tests using the --write-profiles option - -# this file will be rewritten including the new count. -# - -# TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert - -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.5_sqlite_pysqlite_nocextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.6_sqlite_pysqlite_nocextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_cextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_nocextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_cextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_nocextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_cextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_nocextensions 62 - -# TEST: test.aaa_profiling.test_compiler.CompileTest.test_select - -test.aaa_profiling.test_compiler.CompileTest.test_select 2.5_sqlite_pysqlite_nocextensions 134 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.6_sqlite_pysqlite_nocextensions 135 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_cextensions 135 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_nocextensions 135 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_cextensions 135 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_nocextensions 135 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_cextensions 135 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_nocextensions 135 - -# TEST: test.aaa_profiling.test_compiler.CompileTest.test_update - -test.aaa_profiling.test_compiler.CompileTest.test_update 2.5_sqlite_pysqlite_nocextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.6_sqlite_pysqlite_nocextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_cextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_nocextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_cextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_nocextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_cextensions 65 -test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_nocextensions 65 - -# TEST: test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause - -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.5_sqlite_pysqlite_nocextensions 129 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.6_sqlite_pysqlite_nocextensions 130 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_cextensions 130 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_nocextensions 130 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_cextensions 130 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_nocextensions 130 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_cextensions 130 -test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_nocextensions 130 - -# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity - -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.5_sqlite_pysqlite_nocextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.6_sqlite_pysqlite_nocextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_mysql_mysqldb_cextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_mysql_mysqldb_nocextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_postgresql_psycopg2_cextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_postgresql_psycopg2_nocextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_sqlite_pysqlite_cextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_sqlite_pysqlite_nocextensions 17987 - -# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity - -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.5_sqlite_pysqlite_nocextensions 116289 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.6_sqlite_pysqlite_nocextensions 116790 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_cextensions 122540 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_nocextensions 125290 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_cextensions 115040 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_nocextensions 117790 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_cextensions 114040 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_nocextensions 116790 - -# TEST: test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks - -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.5_sqlite_pysqlite_nocextensions 19852 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.6_sqlite_pysqlite_nocextensions 19217 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_cextensions 19491 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_nocextensions 19781 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_cextensions 18878 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_nocextensions 19168 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_cextensions 18957 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_nocextensions 19217 - -# TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_load - -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.5_sqlite_pysqlite_nocextensions 1178 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.6_sqlite_pysqlite_nocextensions 1174 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_cextensions 1341 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_nocextensions 1366 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_cextensions 1200 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_nocextensions 1225 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_cextensions 1149 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_nocextensions 1174 - -# TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_no_load - -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.5_sqlite_pysqlite_nocextensions 108,22 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.6_sqlite_pysqlite_nocextensions 100,18 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_cextensions 100,18 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_nocextensions 100,18 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_cextensions 100,18 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_nocextensions 100,18 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_cextensions 100,18 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_nocextensions 100,18 - -# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect - -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.5_sqlite_pysqlite_nocextensions 71 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.6_sqlite_pysqlite_nocextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_mysql_mysqldb_cextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_mysql_mysqldb_nocextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_postgresql_psycopg2_cextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_postgresql_psycopg2_nocextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlite_cextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlite_nocextensions 67 - -# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect - -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.5_sqlite_pysqlite_nocextensions 32 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.6_sqlite_pysqlite_nocextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_mysql_mysqldb_cextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_mysql_mysqldb_nocextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_postgresql_psycopg2_cextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_postgresql_psycopg2_nocextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqlite_cextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqlite_nocextensions 29 - -# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect - -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.5_sqlite_pysqlite_nocextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.6_sqlite_pysqlite_nocextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_mysql_mysqldb_cextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_mysql_mysqldb_nocextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_postgresql_psycopg2_cextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_postgresql_psycopg2_nocextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sqlite_pysqlite_cextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sqlite_pysqlite_nocextensions 6 - -# TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute - -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.5_sqlite_pysqlite_nocextensions 41 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.6_sqlite_pysqlite_nocextensions 42 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_mysql_mysqldb_cextensions 40 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_mysql_mysqldb_nocextensions 42 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_postgresql_psycopg2_cextensions 40 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_postgresql_psycopg2_nocextensions 42 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_cextensions 40 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_nocextensions 42 - -# TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute - -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.5_sqlite_pysqlite_nocextensions 64 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.6_sqlite_pysqlite_nocextensions 65 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_cextensions 63 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_nocextensions 65 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_cextensions 63 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_nocextensions 65 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_cextensions 63 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_nocextensions 65 - -# TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile - -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.5_sqlite_pysqlite_nocextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.6_sqlite_pysqlite_nocextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_cextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_nocextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_cextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_nocextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_cextensions 14 -test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_nocextensions 14 - -# TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_string - -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.5_sqlite_pysqlite_nocextensions 14413 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.6_sqlite_pysqlite_nocextensions 14414 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_cextensions 452 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_nocextensions 14472 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_cextensions 20438 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_nocextensions 34458 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_cextensions 394 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_nocextensions 14414 - -# TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_unicode - -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.5_sqlite_pysqlite_nocextensions 14413 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.6_sqlite_pysqlite_nocextensions 14414 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_cextensions 452 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_nocextensions 44472 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_cextensions 20438 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_nocextensions 34458 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_cextensions 394 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_nocextensions 14414 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5044 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 5088 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 247 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 247 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 3366 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 3590 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 10366 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 11982 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1005 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1109 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 1736 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 1779 - -# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview - -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_cextensions 2219 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_nocextensions 2449 - -# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate - -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5977 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 6096 - -# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert - -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 392 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 399 - -# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties - -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 6124 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 6356 - -# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions - -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 18140 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 19571 - -# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates - -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1018 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1114 - -# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing - -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 2614 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 2677 diff --git a/test/lib/profiling.py b/test/lib/profiling.py deleted file mode 100644 index ab0bc3f2b..000000000 --- a/test/lib/profiling.py +++ /dev/null @@ -1,289 +0,0 @@ -"""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 -import sys -from .util import gc_collect, decorator -from ..bootstrap import config -from nose import SkipTest -import pstats -import time -import collections -from sqlalchemy import util -try: - import cProfile -except ImportError: - cProfile = None -from sqlalchemy.util.compat import jython, pypy, win32 - -_current_test = None - -def profiled(target=None, **target_opts): - """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. - """ - - profile_config = {'targets': set(), - 'report': True, - 'print_callers': False, - 'print_callees': False, - 'graphic': False, - 'sort': ('time', 'calls'), - 'limit': None} - if target is None: - target = 'anonymous_target' - - filename = "%s.prof" % target - - @decorator - def decorate(fn, *args, **kw): - elapsed, load_stats, result = _profile( - filename, fn, *args, **kw) - - graphic = target_opts.get('graphic', profile_config['graphic']) - if graphic: - os.system("runsnake %s" % filename) - else: - 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() - - print_callers = target_opts.get('print_callers', - profile_config['print_callers']) - if print_callers: - stats.print_callers() - - print_callees = target_opts.get('print_callees', - profile_config['print_callees']) - if print_callees: - stats.print_callees() - - os.unlink(filename) - return result - return decorate - - -class ProfileStatsFile(object): - """"Store per-platform/fn profiling results in a file. - - We're still targeting Py2.5, 2.4 on 0.7 with no dependencies, - so no json lib :( need to roll something silly - - """ - def __init__(self, filename): - self.write = config.options is not None and config.options.write_profiles - self.fname = os.path.abspath(filename) - self.short_fname = os.path.split(self.fname)[-1] - self.data = collections.defaultdict(lambda: collections.defaultdict(dict)) - self._read() - if self.write: - # rewrite for the case where features changed, - # etc. - self._write() - - @util.memoized_property - def platform_key(self): - - dbapi_key = config.db.name + "_" + config.db.driver - - # keep it at 2.7, 3.1, 3.2, etc. for now. - py_version = '.'.join([str(v) for v in sys.version_info[0:2]]) - - platform_tokens = [py_version] - platform_tokens.append(dbapi_key) - if jython: - platform_tokens.append("jython") - if pypy: - platform_tokens.append("pypy") - if win32: - platform_tokens.append("win") - _has_cext = config.requirements._has_cextensions() - platform_tokens.append(_has_cext and "cextensions" or "nocextensions") - return "_".join(platform_tokens) - - def has_stats(self): - test_key = _current_test - return test_key in self.data and self.platform_key in self.data[test_key] - - def result(self, callcount): - test_key = _current_test - per_fn = self.data[test_key] - per_platform = per_fn[self.platform_key] - - if 'counts' not in per_platform: - per_platform['counts'] = counts = [] - else: - counts = per_platform['counts'] - - if 'current_count' not in per_platform: - per_platform['current_count'] = current_count = 0 - else: - current_count = per_platform['current_count'] - - has_count = len(counts) > current_count - - if not has_count: - counts.append(callcount) - if self.write: - self._write() - result = None - else: - result = per_platform['lineno'], counts[current_count] - per_platform['current_count'] += 1 - return result - - - def _header(self): - return \ - "# %s\n"\ - "# This file is written out on a per-environment basis.\n"\ - "# For each test in aaa_profiling, the corresponding function and \n"\ - "# environment is located within this file. If it doesn't exist,\n"\ - "# the test is skipped.\n"\ - "# If a callcount does exist, it is compared to what we received. \n"\ - "# assertions are raised if the counts do not match.\n"\ - "# \n"\ - "# To add a new callcount test, apply the function_call_count \n"\ - "# decorator and re-run the tests using the --write-profiles option - \n"\ - "# this file will be rewritten including the new count.\n"\ - "# \n"\ - "" % (self.fname) - - def _read(self): - profile_f = open(self.fname) - for lineno, line in enumerate(profile_f): - line = line.strip() - if not line or line.startswith("#"): - continue - - test_key, platform_key, counts = line.split() - per_fn = self.data[test_key] - per_platform = per_fn[platform_key] - per_platform['counts'] = [int(count) for count in counts.split(",")] - per_platform['lineno'] = lineno + 1 - per_platform['current_count'] = 0 - profile_f.close() - - def _write(self): - print("Writing profile file %s" % self.fname) - profile_f = open(self.fname, "w") - profile_f.write(self._header()) - for test_key in sorted(self.data): - - per_fn = self.data[test_key] - profile_f.write("\n# TEST: %s\n\n" % test_key) - for platform_key in sorted(per_fn): - per_platform = per_fn[platform_key] - profile_f.write( - "%s %s %s\n" % ( - test_key, - platform_key, ",".join(str(count) for count in per_platform['counts']) - ) - ) - profile_f.close() - -from sqlalchemy.util.compat import update_wrapper - -def function_call_count(variance=0.05): - """Assert a target for a test case's function call count. - - The main purpose of this assertion is to detect changes in - callcounts for various functions - the actual number is not as important. - Callcounts are stored in a file keyed to Python version and OS platform - information. This file is generated automatically for new tests, - and versioned so that unexpected changes in callcounts will be detected. - - """ - - def decorate(fn): - def wrap(*args, **kw): - - - if cProfile is None: - raise SkipTest("cProfile is not installed") - - if not _profile_stats.has_stats() and not _profile_stats.write: - # run the function anyway, to support dependent tests - # (not a great idea but we have these in test_zoomark) - fn(*args, **kw) - raise SkipTest("No profiling stats available on this " - "platform for this function. Run tests with " - "--write-profiles to add statistics to %s for " - "this platform." % _profile_stats.short_fname) - - gc_collect() - - - timespent, load_stats, fn_result = _profile( - fn, *args, **kw - ) - stats = load_stats() - callcount = stats.total_calls - - expected = _profile_stats.result(callcount) - if expected is None: - expected_count = None - else: - line_no, expected_count = expected - - print("Pstats calls: %d Expected %s" % ( - callcount, - expected_count - ) - ) - stats.print_stats() - #stats.print_callers() - - if expected_count: - deviance = int(callcount * variance) - if abs(callcount - expected_count) > deviance: - raise AssertionError( - "Adjusted function call count %s not within %s%% " - "of expected %s. (Delete line %d of file %s to regenerate " - "this callcount, when tests are run with --write-profiles.)" - % ( - callcount, (variance * 100), - expected_count, line_no, - _profile_stats.fname)) - return fn_result - return update_wrapper(wrap, fn) - return decorate - - -def _profile(fn, *args, **kw): - filename = "%s.prof" % fn.__name__ - - def load_stats(): - st = pstats.Stats(filename) - os.unlink(filename) - return st - - began = time.time() - cProfile.runctx('result = fn(*args, **kw)', globals(), locals(), - filename=filename) - ended = time.time() - - return ended - began, load_stats, locals()['result'] - diff --git a/test/lib/requires.py b/test/lib/requires.py deleted file mode 100644 index 1fff3d7c2..000000000 --- a/test/lib/requires.py +++ /dev/null @@ -1,506 +0,0 @@ -"""Global database feature support policy. - -Provides decorators to mark tests requiring specific feature support from the -target database. - -""" - -from .exclusions import \ - skip, \ - skip_if,\ - only_if,\ - only_on,\ - fails_on,\ - fails_on_everything_except,\ - fails_if,\ - SpecPredicate,\ - against - -def no_support(db, reason): - return SpecPredicate(db, description=reason) - -def exclude(db, op, spec, description=None): - return SpecPredicate(db, op, spec, description=description) - -from sqlalchemy import util -from ..bootstrap import config -import sys - -crashes = skip - -def _chain_decorators_on(*decorators): - def decorate(fn): - for decorator in reversed(decorators): - fn = decorator(fn) - return fn - return decorate - -class Requirements(object): - def __init__(self, db, config): - self.db = db - self.config = config - - -class DefaultRequirements(Requirements): - @property - def deferrable_or_no_constraints(self): - """Target database must support derferable constraints.""" - - return skip_if([ - no_support('firebird', 'not supported by database'), - no_support('mysql', 'not supported by database'), - no_support('mssql', 'not supported by database'), - ]) - - @property - def foreign_keys(self): - """Target database must support foreign keys.""" - - return skip_if( - no_support('sqlite', 'not supported by database') - ) - - - @property - def unbounded_varchar(self): - """Target database must support VARCHAR with no length""" - - return skip_if([ - "firebird", "oracle", "mysql" - ], "not supported by database" - ) - - @property - def boolean_col_expressions(self): - """Target database must support boolean expressions as columns""" - return skip_if([ - 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'), - ]) - - @property - def standalone_binds(self): - """target database/driver supports bound parameters as column expressions - without being in the context of a typed column. - - """ - return skip_if(["firebird", "mssql+mxodbc"], - "not supported by driver") - - @property - def identity(self): - """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 skip_if(["firebird", "oracle", "postgresql", "sybase"], - "not supported by database" - ) - - @property - def reflectable_autoincrement(self): - """Target database must support tables that can automatically generate - PKs assuming they were reflected. - - this is essentially all the DBs in "identity" plus Postgresql, which - has SERIAL support. FB and Oracle (and sybase?) require the Sequence to - be explicitly added, including if the table was reflected. - """ - return skip_if(["firebird", "oracle", "sybase"], - "not supported by database" - ) - - @property - def binary_comparisons(self): - """target database/driver can allow BLOB/BINARY fields to be compared - against a bound parameter value. - """ - return skip_if(["oracle", "mssql"], - "not supported by database/driver" - ) - - @property - def independent_cursors(self): - """Target must support simultaneous, independent database cursors - on a single connection.""" - - return skip_if(["mssql+pyodbc", "mssql+mxodbc"], "no driver support") - - @property - def independent_connections(self): - """Target must support simultaneous, independent database connections.""" - - # This is also true of some configurations of UnixODBC and probably win32 - # ODBC as well. - return skip_if([ - no_support("sqlite", - "independent connections disabled " - "when :memory: connections are used"), - exclude("mssql", "<", (9, 0, 0), - "SQL Server 2005+ is required for " - "independent connections" - ) - ] - ) - - @property - def updateable_autoincrement_pks(self): - """Target must support UPDATE on autoincrement/integer primary key.""" - - return skip_if(["mssql", "sybase"], - "IDENTITY columns can't be updated") - - @property - def isolation_level(self): - return _chain_decorators_on( - only_on(('postgresql', 'sqlite', 'mysql'), - "DBAPI has no isolation level support"), - fails_on('postgresql+pypostgresql', - 'pypostgresql bombs on multiple isolation level calls') - ) - - @property - def row_triggers(self): - """Target must support standard statement-running EACH ROW triggers.""" - - return skip_if([ - # 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'), - ]) - - @property - def correlated_outer_joins(self): - """Target must support an outer join to a subquery which - correlates to the parent.""" - - return skip_if("oracle", 'Raises "ORA-01799: a column may not be ' - 'outer-joined to a subquery"') - - @property - def update_from(self): - """Target must support UPDATE..FROM syntax""" - - return only_on(['postgresql', 'mssql', 'mysql'], - "Backend does not support UPDATE..FROM") - - - @property - def savepoints(self): - """Target database must support savepoints.""" - - return skip_if([ - "access", - "sqlite", - "sybase", - ("mysql", "<", (5, 0, 3)), - ("informix", "<", (11, 55, "xC3")) - ], "savepoints not supported") - - @property - def denormalized_names(self): - """Target database must have 'denormalized', i.e. - UPPERCASE as case insensitive names.""" - - return skip_if( - lambda: not self.db.dialect.requires_name_normalize, - "Backend does not require denormalized names." - ) - - @property - def schemas(self): - """Target database must support external schemas, and have one - named 'test_schema'.""" - - return skip_if([ - "sqlite", - "firebird" - ], "no schema support") - - @property - def sequences(self): - """Target database must support SEQUENCEs.""" - - return only_if([ - "postgresql", "firebird", "oracle" - ], "no SEQUENCE support") - - @property - def update_nowait(self): - """Target database must support SELECT...FOR UPDATE NOWAIT""" - return skip_if(["access", "firebird", "mssql", "mysql", "sqlite", "sybase"], - "no FOR UPDATE NOWAIT support" - ) - - @property - def subqueries(self): - """Target database must support subqueries.""" - - return skip_if(exclude('mysql', '<', (4, 1, 1)), 'no subquery support') - - @property - def intersect(self): - """Target database must support INTERSECT or equivalent.""" - - return fails_if([ - "firebird", "mysql", "sybase", "informix" - ], 'no support for INTERSECT') - - @property - def except_(self): - """Target database must support EXCEPT or equivalent (i.e. MINUS).""" - return fails_if([ - "firebird", "mysql", "sybase", "informix" - ], 'no support for EXCEPT') - - @property - def offset(self): - """Target database must support some method of adding OFFSET or - equivalent to a result set.""" - return fails_if([ - "sybase" - ], 'no support for OFFSET or equivalent') - - @property - def window_functions(self): - return only_if([ - "postgresql", "mssql", "oracle" - ], "Backend does not support window functions") - - @property - def returning(self): - return only_if(["postgresql", "mssql", "oracle", "firebird"], - "'returning' not supported by database" - ) - - @property - def two_phase_transactions(self): - """Target database must support two-phase transactions.""" - - return skip_if([ - no_support('access', 'two-phase xact not supported by database'), - no_support('firebird', 'no SA implementation'), - no_support('maxdb', 'two-phase xact not supported by database'), - no_support('mssql', 'two-phase xact not supported by drivers'), - no_support('oracle', 'two-phase xact not implemented in SQLA/oracle'), - no_support('drizzle', 'two-phase xact not supported by database'), - no_support('sqlite', 'two-phase xact not supported by database'), - no_support('sybase', 'two-phase xact not supported by drivers/SQLA'), - no_support('postgresql+zxjdbc', - 'FIXME: JDBC driver confuses the transaction state, may ' - 'need separate XA implementation'), - exclude('mysql', '<', (5, 0, 3), - 'two-phase xact not supported by database'), - ]) - - @property - def views(self): - """Target database must support VIEWs.""" - - return skip_if("drizzle", "no VIEW support") - - @property - def unicode_connections(self): - """Target driver must support some encoding of Unicode across the wire.""" - # TODO: expand to exclude MySQLdb versions w/ broken unicode - return skip_if([ - exclude('mysql', '<', (4, 1, 1), 'no unicode connection support'), - ]) - - @property - def unicode_ddl(self): - """Target driver must support some encoding of Unicode across the wire.""" - # TODO: expand to exclude MySQLdb versions w/ broken unicode - return skip_if([ - 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'), - ]) - - @property - def sane_rowcount(self): - return skip_if( - lambda: not self.db.dialect.supports_sane_rowcount, - "driver doesn't support 'sane' rowcount" - ) - - @property - def cextensions(self): - return skip_if( - lambda: not self._has_cextensions(), "C extensions not installed" - ) - - - @property - def emulated_lastrowid(self): - """"target dialect retrieves cursor.lastrowid or an equivalent - after an insert() construct executes. - """ - return fails_on_everything_except('mysql+mysqldb', 'mysql+oursql', - 'sqlite+pysqlite', 'mysql+pymysql', - 'mssql+pyodbc', 'mssql+mxodbc') - - @property - def dbapi_lastrowid(self): - """"target backend includes a 'lastrowid' accessor on the DBAPI - cursor object. - - """ - return fails_on_everything_except('mysql+mysqldb', 'mysql+oursql', - 'sqlite+pysqlite', 'mysql+pymysql') - - @property - def sane_multi_rowcount(self): - return skip_if( - lambda: not self.db.dialect.supports_sane_multi_rowcount, - "driver doesn't support 'sane' multi row count" - ) - - @property - def nullsordering(self): - """Target backends that support nulls ordering.""" - return _chain_decorators_on( - fails_on_everything_except('postgresql', 'oracle', 'firebird') - ) - - @property - def reflects_pk_names(self): - """Target driver reflects the name of primary key constraints.""" - return _chain_decorators_on( - fails_on_everything_except('postgresql', 'oracle') - ) - - @property - def python2(self): - return _chain_decorators_on( - skip_if( - lambda: sys.version_info >= (3,), - "Python version 2.xx is required." - ) - ) - - @property - def python3(self): - return _chain_decorators_on( - skip_if( - lambda: sys.version_info < (3,), - "Python version 3.xx is required." - ) - ) - - @property - def python26(self): - return _chain_decorators_on( - skip_if( - lambda: sys.version_info < (2, 6), - "Python version 2.6 or greater is required" - ) - ) - - @property - def python25(self): - return _chain_decorators_on( - skip_if( - lambda: sys.version_info < (2, 5), - "Python version 2.5 or greater is required" - ) - ) - - @property - def cpython(self): - return _chain_decorators_on( - only_if(lambda: util.cpython, - "cPython interpreter needed" - ) - ) - - @property - def predictable_gc(self): - """target platform must remove all cycles unconditionally when - gc.collect() is called, as well as clean out unreferenced subclasses. - - """ - return self.cpython - - @property - def sqlite(self): - return _chain_decorators_on( - skip_if(lambda: not self._has_sqlite()) - ) - - @property - def ad_hoc_engines(self): - """Test environment must allow ad-hoc engine/connection creation. - - DBs that scale poorly for many connections, even when closed, i.e. - Oracle, may use the "--low-connections" option which flags this requirement - as not present. - - """ - return _chain_decorators_on( - skip_if(lambda: config.options.low_connections) - ) - - @property - def skip_mysql_on_windows(self): - """Catchall for a large variety of MySQL on Windows failures""" - - return _chain_decorators_on( - skip_if(self._has_mysql_on_windows, - "Not supported on MySQL + Windows" - ) - ) - - @property - def english_locale_on_postgresql(self): - return _chain_decorators_on( - skip_if(lambda: against('postgresql') \ - and not self.db.scalar('SHOW LC_COLLATE').startswith('en')) - ) - - @property - def selectone(self): - """target driver must support the literal statement 'select 1'""" - return _chain_decorators_on( - skip_if(lambda: against('oracle'), - "non-standard SELECT scalar syntax"), - skip_if(lambda: against('firebird'), - "non-standard SELECT scalar syntax") - ) - - def _has_cextensions(self): - try: - from sqlalchemy import cresultproxy, cprocessors - return True - except ImportError: - return False - - def _has_sqlite(self): - from sqlalchemy import create_engine - try: - create_engine('sqlite://') - return True - except ImportError: - return False - - def _has_mysql_on_windows(self): - return against('mysql') and \ - self.db.dialect._detect_casing(self.db) == 1 - - def _has_mysql_fully_case_sensitive(self): - return against('mysql') and \ - self.db.dialect._detect_casing(self.db) == 0 - diff --git a/test/lib/schema.py b/test/lib/schema.py deleted file mode 100644 index 6c7a684e2..000000000 --- a/test/lib/schema.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Enhanced versions of schema.Table and schema.Column which establish -desired state for different backends. -""" - -from . import exclusions -from sqlalchemy import schema, event -from ..bootstrap import config - -__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 exclusions.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' - else: - kw['mysql_engine'] = 'MyISAM' - - # Apply some default cascading rules for self-referential foreign keys. - # MySQL InnoDB has some issues around seleting self-refs too. - if exclusions.against('firebird'): - table_name = args[0] - unpack = (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 \ - exclusions.against('firebird', 'oracle'): - def add_seq(c, tbl): - c._init_items( - schema.Sequence(_truncate_name( - config.db.dialect, tbl.name + '_' + c.name + '_seq'), - optional=True) - ) - event.listen(col, 'after_parent_attach', add_seq, propagate=True) - 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 deleted file mode 100644 index f244dfecc..000000000 --- a/test/lib/testing.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import absolute_import - -from .warnings import testing_warn, assert_warnings, resetwarnings - -from ..bootstrap import config -from . import assertsql, util as testutil -from sqlalchemy.util import decorator - -from .exclusions import db_spec, _is_excluded, fails_if, skip_if, future,\ - fails_on, fails_on_everything_except, skip, only_on, exclude, against,\ - _server_version - -from .assertions import emits_warning, emits_warning_on, uses_deprecated, \ - eq_, ne_, is_, is_not_, startswith_, assert_raises, \ - assert_raises_message, AssertsCompiledSQL, ComparesTables, AssertsExecutionResults - -from .util import run_as_contextmanager, rowset, fail, provide_metadata, adict - -crashes = skip - -# various sugar installed by config.py -db = None -requires = None - - diff --git a/test/lib/util.py b/test/lib/util.py deleted file mode 100644 index c2144f6bd..000000000 --- a/test/lib/util.py +++ /dev/null @@ -1,196 +0,0 @@ -from sqlalchemy.util import jython, pypy, defaultdict, decorator -from sqlalchemy.util.compat import decimal - -import gc -import time -import random -import sys -import types - -if jython: - def jython_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 = jython_gc_collect -elif pypy: - def pypy_gc_collect(*args): - gc.collect() - gc.collect() - lazy_gc = pypy_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) - - # 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)) - - -def function_named(fn, name): - """Return a function with a given __name__. - - Will assign to __name__ and return the original function if possible on - the Python implementation, otherwise a new function will be constructed. - - This function should be phased out as much as possible - in favor of @decorator. Tests that "generate" many named tests - should be modernized. - - """ - try: - fn.__name__ = name - except TypeError: - fn = types.FunctionType(fn.func_code, fn.func_globals, name, - fn.func_defaults, fn.func_closure) - return fn - - - -def run_as_contextmanager(ctx, fn, *arg, **kw): - """Run the given function under the given contextmanager, - simulating the behavior of 'with' to support older - Python versions. - - """ - - obj = ctx.__enter__() - try: - result = fn(obj, *arg, **kw) - ctx.__exit__(None, None, None) - return result - except: - exc_info = sys.exc_info() - raise_ = ctx.__exit__(*exc_info) - if raise_ is None: - raise - else: - return raise_ - -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 fail(msg): - assert False, msg - - -@decorator -def provide_metadata(fn, *args, **kw): - """Provide bound MetaData for a single test, dropping afterwards.""" - - from ..bootstrap.config import db - from sqlalchemy import schema - - metadata = schema.MetaData(db) - self = args[0] - prev_meta = getattr(self, 'metadata', None) - self.metadata = metadata - try: - return fn(*args, **kw) - finally: - metadata.drop_all() - self.metadata = prev_meta - -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]) - - diff --git a/test/lib/warnings.py b/test/lib/warnings.py deleted file mode 100644 index d17d9f465..000000000 --- a/test/lib/warnings.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import absolute_import - -import warnings -from sqlalchemy import exc as sa_exc -from sqlalchemy import util - -def testing_warn(msg, stacklevel=3): - """Replaces sqlalchemy.util.warn during tests.""" - - filename = "test.lib.testing" - lineno = 1 - if isinstance(msg, basestring): - warnings.warn_explicit(msg, sa_exc.SAWarning, filename, lineno) - else: - warnings.warn_explicit(msg, filename, lineno) - -def resetwarnings(): - """Reset warning behavior to testing defaults.""" - - util.warn = util.langhelpers.warn = testing_warn - - warnings.filterwarnings('ignore', - category=sa_exc.SAPendingDeprecationWarning) - warnings.filterwarnings('error', category=sa_exc.SADeprecationWarning) - warnings.filterwarnings('error', category=sa_exc.SAWarning) - -def assert_warnings(fn, warnings): - """Assert that each of the given warnings are emitted by fn.""" - - from .assertions import eq_, emits_warning - - canary = [] - orig_warn = util.warn - def capture_warnings(*args, **kw): - orig_warn(*args, **kw) - popwarn = warnings.pop(0) - canary.append(popwarn) - eq_(args[0], popwarn) - util.warn = util.langhelpers.warn = capture_warnings - - result = emits_warning()(fn)() - assert canary, "No warning was emitted" - return result diff --git a/test/orm/_fixtures.py b/test/orm/_fixtures.py index e51924b77..c21833619 100644 --- a/test/orm/_fixtures.py +++ b/test/orm/_fixtures.py @@ -1,10 +1,10 @@ from sqlalchemy import MetaData, Integer, String, ForeignKey from sqlalchemy import util -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import attributes, mapper, relationship, \ backref, configure_mappers -from test.lib import fixtures +from sqlalchemy.testing import fixtures __all__ = () diff --git a/test/orm/inheritance/_poly_fixtures.py b/test/orm/inheritance/_poly_fixtures.py index 6c502f58d..55783cd69 100644 --- a/test/orm/inheritance/_poly_fixtures.py +++ b/test/orm/inheritance/_poly_fixtures.py @@ -6,9 +6,9 @@ from sqlalchemy.orm import interfaces, relationship, mapper, \ from sqlalchemy import exc as sa_exc from sqlalchemy.engine import default -from test.lib import AssertsCompiledSQL, fixtures, testing -from test.lib.schema import Table, Column -from test.lib.testing import assert_raises, eq_ +from sqlalchemy.testing import AssertsCompiledSQL, fixtures, testing +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import assert_raises, eq_ class Company(fixtures.ComparableEntity): pass diff --git a/test/orm/inheritance/test_abc_inheritance.py b/test/orm/inheritance/test_abc_inheritance.py index 3a61adb18..757f8868e 100644 --- a/test/orm/inheritance/test_abc_inheritance.py +++ b/test/orm/inheritance/test_abc_inheritance.py @@ -2,9 +2,9 @@ from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE -from test.lib import testing -from test.lib.schema import Table, Column -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import fixtures def produce_test(parent, child, direction): diff --git a/test/orm/inheritance/test_abc_polymorphic.py b/test/orm/inheritance/test_abc_polymorphic.py index 746bf0f13..f32805cc7 100644 --- a/test/orm/inheritance/test_abc_polymorphic.py +++ b/test/orm/inheritance/test_abc_polymorphic.py @@ -2,11 +2,11 @@ from sqlalchemy import * from sqlalchemy import util from sqlalchemy.orm import * -from test.lib.util import function_named -from test.lib import fixtures +from sqlalchemy.testing.util import function_named +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.testing import eq_ -from test.lib.schema import Table, Column +from sqlalchemy.testing import eq_ +from sqlalchemy.testing.schema import Table, Column class ABCTest(fixtures.MappedTest): @classmethod diff --git a/test/orm/inheritance/test_assorted_poly.py b/test/orm/inheritance/test_assorted_poly.py index fe9fb493b..9c0ba989e 100644 --- a/test/orm/inheritance/test_assorted_poly.py +++ b/test/orm/inheritance/test_assorted_poly.py @@ -3,17 +3,17 @@ These are generally tests derived from specific user issues. """ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy import * from sqlalchemy import util from sqlalchemy.orm import * from sqlalchemy.orm.interfaces import MANYTOONE -from test.lib import AssertsExecutionResults, testing -from test.lib.util import function_named -from test.lib import fixtures +from sqlalchemy.testing import AssertsExecutionResults, testing +from sqlalchemy.testing.util import function_named +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.testing import eq_ -from test.lib.schema import Table, Column +from sqlalchemy.testing import eq_ +from sqlalchemy.testing.schema import Table, Column class AttrSettable(object): def __init__(self, **kwargs): diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index ad30ab44d..66991e922 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -1,18 +1,19 @@ import warnings -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message from sqlalchemy import * from sqlalchemy import exc as sa_exc, util, event from sqlalchemy.orm import * from sqlalchemy.orm import exc as orm_exc, attributes -from test.lib.assertsql import AllOf, CompiledSQL +from sqlalchemy.testing.assertsql import AllOf, CompiledSQL from sqlalchemy.sql import table, column -from test.lib import testing, engines -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import engines +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy import inspect from sqlalchemy.ext.declarative import declarative_base -from test.lib.util import gc_collect +from sqlalchemy.testing.util import gc_collect class O2MTest(fixtures.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 d51efa62b..40c3ab31b 100644 --- a/test/orm/inheritance/test_concrete.py +++ b/test/orm/inheritance/test_concrete.py @@ -1,15 +1,15 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.orm import exc as orm_exc -from test.lib import * +from sqlalchemy.testing import * import sqlalchemy as sa -from test.lib import testing -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import fixtures from sqlalchemy.orm import attributes -from test.lib.testing import eq_ -from test.lib.schema import Table, Column +from sqlalchemy.testing import eq_ +from sqlalchemy.testing.schema import Table, Column class Employee(object): diff --git a/test/orm/inheritance/test_magazine.py b/test/orm/inheritance/test_magazine.py index a1118aa86..ae5aa8c8d 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 test.lib import testing -from test.lib.util import function_named -from test.lib import fixtures -from test.lib.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing.util import function_named +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.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 1d5d8b297..e4df5d133 100644 --- a/test/orm/inheritance/test_manytomany.py +++ b/test/orm/inheritance/test_manytomany.py @@ -1,9 +1,9 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy import * from sqlalchemy.orm import * -from test.lib import testing -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import fixtures class InheritTest(fixtures.MappedTest): diff --git a/test/orm/inheritance/test_poly_linked_list.py b/test/orm/inheritance/test_poly_linked_list.py index f87662587..1915007de 100644 --- a/test/orm/inheritance/test_poly_linked_list.py +++ b/test/orm/inheritance/test_poly_linked_list.py @@ -1,9 +1,9 @@ from sqlalchemy import * from sqlalchemy.orm import * -from test.lib import fixtures -from test.lib import testing -from test.lib.schema import Table, Column +from sqlalchemy.testing import fixtures +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column class PolymorphicCircularTest(fixtures.MappedTest): diff --git a/test/orm/inheritance/test_poly_persistence.py b/test/orm/inheritance/test_poly_persistence.py index 5b5844b70..3450c133c 100644 --- a/test/orm/inheritance/test_poly_persistence.py +++ b/test/orm/inheritance/test_poly_persistence.py @@ -1,14 +1,14 @@ """tests basic polymorphic mapper loading/saving, minimal relationships""" -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.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 test.lib import Column, testing -from test.lib.util import function_named +from sqlalchemy.testing import Column, testing +from sqlalchemy.testing.util import function_named from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures class Person(fixtures.ComparableEntity): pass diff --git a/test/orm/inheritance/test_polymorphic_rel.py b/test/orm/inheritance/test_polymorphic_rel.py index 6d7bcb676..8bbde972d 100644 --- a/test/orm/inheritance/test_polymorphic_rel.py +++ b/test/orm/inheritance/test_polymorphic_rel.py @@ -4,8 +4,8 @@ from sqlalchemy.orm import interfaces, create_session, joinedload, joinedload_al class_mapper from sqlalchemy import exc as sa_exc -from test.lib import testing -from test.lib.testing import assert_raises, eq_ +from sqlalchemy import testing +from sqlalchemy.testing import assert_raises, eq_ from _poly_fixtures import Company, Person, Engineer, Manager, Boss, \ Machine, Paperwork, _Polymorphic,\ diff --git a/test/orm/inheritance/test_productspec.py b/test/orm/inheritance/test_productspec.py index d7c7eb24a..c1e99e338 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 test.lib import testing -from test.lib import fixtures -from test.lib.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.schema import Table, Column class InheritTest(fixtures.MappedTest): """tests some various inheritance round trips involving a particular set of polymorphic inheritance relationships""" diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py index a4e19b988..5511614c8 100644 --- a/test/orm/inheritance/test_relationship.py +++ b/test/orm/inheritance/test_relationship.py @@ -5,9 +5,9 @@ from sqlalchemy.orm import create_session, relationship, mapper, \ from sqlalchemy import Integer, String, ForeignKey from sqlalchemy.engine import default -from test.lib import AssertsCompiledSQL, fixtures, testing -from test.lib.schema import Table, Column -from test.lib.testing import assert_raises, eq_ +from sqlalchemy.testing import AssertsCompiledSQL, fixtures, testing +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import assert_raises, eq_ class Company(fixtures.ComparableEntity): pass diff --git a/test/orm/inheritance/test_selects.py b/test/orm/inheritance/test_selects.py index 9758744ca..dd9c8c8b8 100644 --- a/test/orm/inheritance/test_selects.py +++ b/test/orm/inheritance/test_selects.py @@ -1,9 +1,9 @@ from sqlalchemy import * from sqlalchemy.orm import * -from test.lib import testing +from sqlalchemy import testing -from test.lib import fixtures +from sqlalchemy.testing import fixtures class InheritingSelectablesTest(fixtures.MappedTest): @classmethod diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 774626c48..08a693b92 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -1,11 +1,11 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy import * from sqlalchemy.orm import * -from test.lib import testing +from sqlalchemy import testing from test.orm import _fixtures -from test.lib import fixtures -from test.lib.schema import Table, Column +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.schema import Table, Column class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest): diff --git a/test/orm/inheritance/test_with_poly.py b/test/orm/inheritance/test_with_poly.py index dc0035b6a..cbf2c9651 100644 --- a/test/orm/inheritance/test_with_poly.py +++ b/test/orm/inheritance/test_with_poly.py @@ -6,9 +6,9 @@ from sqlalchemy.orm import interfaces, relationship, mapper, \ from sqlalchemy import exc as sa_exc from sqlalchemy.engine import default -from test.lib import AssertsCompiledSQL, fixtures, testing -from test.lib.schema import Table, Column -from test.lib.testing import assert_raises, eq_ +from sqlalchemy.testing import AssertsCompiledSQL, fixtures, testing +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import assert_raises, eq_ from _poly_fixtures import Company, Person, Engineer, Manager, Boss, \ Machine, Paperwork, _PolymorphicFixtureBase, _Polymorphic,\ diff --git a/test/orm/test_association.py b/test/orm/test_association.py index 47d0b1ddc..376cfaa84 100644 --- a/test/orm/test_association.py +++ b/test/orm/test_association.py @@ -1,10 +1,10 @@ -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session -from test.lib import fixtures -from test.lib.testing import eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import eq_ class AssociationTest(fixtures.MappedTest): diff --git a/test/orm/test_assorted_eager.py b/test/orm/test_assorted_eager.py index dded00256..a1c96bdfa 100644 --- a/test/orm/test_assorted_eager.py +++ b/test/orm/test_assorted_eager.py @@ -9,12 +9,12 @@ be cleaned up and modernized. import datetime import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, backref, create_session -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures class EagerTest(fixtures.MappedTest): diff --git a/test/orm/test_attributes.py b/test/orm/test_attributes.py index 7c0c9527a..ecdfef294 100644 --- a/test/orm/test_attributes.py +++ b/test/orm/test_attributes.py @@ -3,11 +3,11 @@ from sqlalchemy.orm import attributes, instrumentation, exc as orm_exc from sqlalchemy.orm.collections import collection from sqlalchemy.orm.interfaces import AttributeExtension from sqlalchemy import exc as sa_exc -from test.lib import * -from test.lib.testing import eq_, ne_, assert_raises, \ +from sqlalchemy.testing import * +from sqlalchemy.testing import eq_, ne_, assert_raises, \ assert_raises_message -from test.lib import fixtures -from test.lib.util import gc_collect, all_partial_orderings +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.util import gc_collect, all_partial_orderings from sqlalchemy.util import jython from sqlalchemy import event diff --git a/test/orm/test_backref_mutations.py b/test/orm/test_backref_mutations.py index 7e425a457..925eedfa9 100644 --- a/test/orm/test_backref_mutations.py +++ b/test/orm/test_backref_mutations.py @@ -9,16 +9,16 @@ UPDATE in the database. """ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message from sqlalchemy import Integer, String, ForeignKey, Sequence, exc as sa_exc -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, create_session, \ class_mapper, backref, sessionmaker, Session from sqlalchemy.orm import attributes, exc as orm_exc -from test.lib import testing -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures class O2MCollectionTest(_fixtures.FixtureTest): diff --git a/test/orm/test_bind.py b/test/orm/test_bind.py index 00d9bb094..0d869130b 100644 --- a/test/orm/test_bind.py +++ b/test/orm/test_bind.py @@ -1,11 +1,11 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message from sqlalchemy import MetaData, Integer -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, create_session import sqlalchemy as sa -from test.lib import testing -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import fixtures class BindTest(fixtures.MappedTest): diff --git a/test/orm/test_cascade.py b/test/orm/test_cascade.py index ee3c7b63e..f681fc498 100644 --- a/test/orm/test_cascade.py +++ b/test/orm/test_cascade.py @@ -1,15 +1,15 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message from sqlalchemy import Integer, String, ForeignKey, Sequence, \ exc as sa_exc -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session, \ sessionmaker, class_mapper, backref, Session, util as orm_util,\ configure_mappers from sqlalchemy.orm import attributes, exc as orm_exc -from test.lib import testing -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures class CascadeArgTest(fixtures.MappedTest): diff --git a/test/orm/test_collection.py b/test/orm/test_collection.py index b3de03aae..dc8aa2bc4 100644 --- a/test/orm/test_collection.py +++ b/test/orm/test_collection.py @@ -1,4 +1,4 @@ -from test.lib.testing import eq_ +from sqlalchemy.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 test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, text -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy import util, exc as sa_exc from sqlalchemy.orm import create_session, mapper, relationship, \ attributes, instrumentation -from test.lib import fixtures -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message class Canary(sa.orm.interfaces.AttributeExtension): def __init__(self): @@ -1669,7 +1669,7 @@ class ColumnMappedWSerialize(fixtures.MappedTest): ]) def _run_test(self, specs): - from test.lib.util import picklers + from sqlalchemy.testing.util import picklers for spec, obj, expected in specs: coll = collections.column_mapped_collection(spec)() eq_( diff --git a/test/orm/test_compile.py b/test/orm/test_compile.py index 1b2714d70..d5823b7a6 100644 --- a/test/orm/test_compile.py +++ b/test/orm/test_compile.py @@ -1,9 +1,9 @@ from sqlalchemy import * from sqlalchemy import exc as sa_exc from sqlalchemy.orm import * -from test.lib import * -from test.lib.testing import assert_raises_message -from test.lib import fixtures +from sqlalchemy.testing import * +from sqlalchemy.testing import assert_raises_message +from sqlalchemy.testing import fixtures class CompileTest(fixtures.ORMTest): diff --git a/test/orm/test_composites.py b/test/orm/test_composites.py index 5e5775dc2..f9af0c702 100644 --- a/test/orm/test_composites.py +++ b/test/orm/test_composites.py @@ -1,17 +1,17 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import MetaData, Integer, String, ForeignKey, func, \ util, select -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, backref, \ class_mapper, CompositeProperty, \ validates, aliased from sqlalchemy.orm import attributes, \ composite, relationship, \ Session -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index dce8e04de..1da57b050 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -5,14 +5,14 @@ T1<->T2, with o2m or m2o between them, and a third T3 with o2m/m2o to one/both T1/T2. """ -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, backref, \ create_session, sessionmaker -from test.lib.testing import eq_ -from test.lib.assertsql import RegexSQL, ExactSQL, CompiledSQL, AllOf -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing.assertsql import RegexSQL, ExactSQL, CompiledSQL, AllOf +from sqlalchemy.testing import fixtures class SelfReferentialTest(fixtures.MappedTest): diff --git a/test/orm/test_default_strategies.py b/test/orm/test_default_strategies.py index 675cebda8..b986ac568 100644 --- a/test/orm/test_default_strategies.py +++ b/test/orm/test_default_strategies.py @@ -1,10 +1,10 @@ from test.orm import _fixtures -from test.lib import testing +from sqlalchemy import testing from sqlalchemy.orm import mapper, relationship, create_session from sqlalchemy import util from sqlalchemy.util import any import sqlalchemy as sa -from test.lib.testing import eq_, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises_message class DefaultStrategyOptionsTest(_fixtures.FixtureTest): diff --git a/test/orm/test_defaults.py b/test/orm/test_defaults.py index a246cddae..2c4656e10 100644 --- a/test/orm/test_defaults.py +++ b/test/orm/test_defaults.py @@ -1,11 +1,10 @@ - import sqlalchemy as sa from sqlalchemy import Integer, String, ForeignKey, event -from test.lib import testing -from test.lib.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session -from test.lib import fixtures -from test.lib.testing import eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import eq_ class TriggerDefaultsTest(fixtures.MappedTest): diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index e6222d167..95fa9dd29 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -5,12 +5,12 @@ modern (i.e. not deprecated) alternative to them. The tests snippets here can be migrated directly to the wiki, docs, etc. """ -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, func -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, relation, create_session, sessionmaker -from test.lib import fixtures +from sqlalchemy.testing import fixtures class QueryAlternativesTest(fixtures.MappedTest): diff --git a/test/orm/test_descriptor.py b/test/orm/test_descriptor.py index b8b876794..2134d87b2 100644 --- a/test/orm/test_descriptor.py +++ b/test/orm/test_descriptor.py @@ -5,8 +5,8 @@ from sqlalchemy.sql import column from sqlalchemy import Column, Integer, func, String from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.util import partial -from test.lib import fixtures -from test.lib.testing import eq_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import eq_ class TestDescriptor(descriptor_props.DescriptorProperty): def __init__(self, cls, key, descriptor=None, doc=None, diff --git a/test/orm/test_dynamic.py b/test/orm/test_dynamic.py index fd8793778..3c4ad4b11 100644 --- a/test/orm/test_dynamic.py +++ b/test/orm/test_dynamic.py @@ -1,13 +1,13 @@ -from test.lib.testing import eq_, ne_ +from sqlalchemy.testing import eq_, ne_ import operator from sqlalchemy.orm import dynamic_loader, backref, configure_mappers -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, desc, select, func, exc -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session, Query, attributes, exc as orm_exc from sqlalchemy.orm.dynamic import AppenderMixin -from test.lib.testing import eq_, AssertsCompiledSQL, assert_raises_message, assert_raises -from test.lib import fixtures +from sqlalchemy.testing import eq_, AssertsCompiledSQL, assert_raises_message, assert_raises +from sqlalchemy.testing import fixtures from test.orm import _fixtures diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py index 4ef55293b..664ddb8e3 100644 --- a/test/orm/test_eager_relations.py +++ b/test/orm/test_eager_relations.py @@ -1,20 +1,20 @@ """tests of joined-eager loaded attributes""" -from test.lib.testing import eq_, is_, is_not_ +from sqlalchemy.testing import eq_, is_, is_not_ import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy.orm import joinedload, deferred, undefer, \ joinedload_all, backref, eagerload, Session, immediateload from sqlalchemy import Integer, String, Date, ForeignKey, and_, select, \ func -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session, \ lazyload, aliased, column_property from sqlalchemy.sql import operators -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message -from test.lib.assertsql import CompiledSQL -from test.lib import fixtures +from sqlalchemy.testing.assertsql import CompiledSQL +from sqlalchemy.testing import fixtures from test.orm import _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 f632561f1..4678100eb 100644 --- a/test/orm/test_evaluator.py +++ b/test/orm/test_evaluator.py @@ -1,12 +1,12 @@ """Evluating SQL expressions on ORM objects""" import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import String, Integer, select -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, create_session -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from sqlalchemy import and_, or_, not_ from sqlalchemy.orm import evaluator diff --git a/test/orm/test_events.py b/test/orm/test_events.py index e49a4a6a7..d8bef66d3 100644 --- a/test/orm/test_events.py +++ b/test/orm/test_events.py @@ -1,15 +1,15 @@ -from test.lib.testing import assert_raises_message, assert_raises +from sqlalchemy.testing import assert_raises_message, assert_raises import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, \ create_session, class_mapper, \ Mapper, column_property, \ Session, sessionmaker, attributes from sqlalchemy.orm.instrumentation import ClassManager -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures from sqlalchemy import event diff --git a/test/orm/test_expire.py b/test/orm/test_expire.py index 16ca8b0ba..0b1350e0a 100644 --- a/test/orm/test_expire.py +++ b/test/orm/test_expire.py @@ -1,16 +1,16 @@ """Attribute/instance expiration, deferral of attributes, etc.""" -from test.lib.testing import eq_, assert_raises, assert_raises_message -from test.lib.util import gc_collect +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing.util import gc_collect import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, exc as sa_exc -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, create_session, \ attributes, deferred, exc as orm_exc, defer, undefer,\ strategies, state, lazyload, backref, Session -from test.lib import fixtures +from sqlalchemy.testing import fixtures from test.orm import _fixtures diff --git a/test/orm/test_froms.py b/test/orm/test_froms.py index f70f08470..05a017bb9 100644 --- a/test/orm/test_froms.py +++ b/test/orm/test_froms.py @@ -1,4 +1,4 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message import operator from sqlalchemy import * from sqlalchemy import exc as sa_exc, util @@ -7,14 +7,15 @@ from sqlalchemy.engine import default from sqlalchemy.orm import * from sqlalchemy.orm import attributes -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import sqlalchemy as sa -from test.lib import testing, AssertsCompiledSQL, Column, engines +from sqlalchemy import testing +from sqlalchemy.testing import AssertsCompiledSQL, Column, engines from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures from sqlalchemy.orm.util import join, outerjoin, with_parent diff --git a/test/orm/test_generative.py b/test/orm/test_generative.py index de3de3d63..3f5da69c3 100644 --- a/test/orm/test_generative.py +++ b/test/orm/test_generative.py @@ -1,12 +1,12 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, MetaData, func -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, create_session -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures diff --git a/test/orm/test_hasparent.py b/test/orm/test_hasparent.py index b9d1cb15a..fd246b527 100644 --- a/test/orm/test_hasparent.py +++ b/test/orm/test_hasparent.py @@ -1,18 +1,18 @@ """test the current state of the hasparent() flag.""" -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message from sqlalchemy import Integer, String, ForeignKey, Sequence, \ exc as sa_exc -from test.lib.schema import Table, Column +from sqlalchemy.testing.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 test.lib import testing -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.util import gc_collect +from sqlalchemy.testing.util import gc_collect class ParentRemovalTest(fixtures.MappedTest): diff --git a/test/orm/test_immediate_load.py b/test/orm/test_immediate_load.py index e809ebfe6..38dec75bd 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 test.lib import testing +from sqlalchemy import testing from sqlalchemy.orm import mapper, relationship, create_session, immediateload -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from test.orm import _fixtures diff --git a/test/orm/test_inspect.py b/test/orm/test_inspect.py index bd43e43d6..f504ad16e 100644 --- a/test/orm/test_inspect.py +++ b/test/orm/test_inspect.py @@ -1,12 +1,12 @@ """test the inspection registry system.""" -from test.lib.testing import eq_, assert_raises_message, is_ +from sqlalchemy.testing import eq_, assert_raises_message, is_ from sqlalchemy import exc, util from sqlalchemy import inspect from test.orm import _fixtures from sqlalchemy.orm import class_mapper, synonym, Session, aliased from sqlalchemy.orm.attributes import instance_state, NO_VALUE -from test.lib import testing +from sqlalchemy import testing class TestORMInspection(_fixtures.FixtureTest): @classmethod diff --git a/test/orm/test_instrumentation.py b/test/orm/test_instrumentation.py index d2388856f..01b678fac 100644 --- a/test/orm/test_instrumentation.py +++ b/test/orm/test_instrumentation.py @@ -1,13 +1,13 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import sqlalchemy as sa from sqlalchemy import MetaData, Integer, ForeignKey, util, event from sqlalchemy.orm import mapper, relationship, create_session, \ attributes, class_mapper, clear_mappers, instrumentation, events -from test.lib.schema import Table -from test.lib.schema import Column -from test.lib.testing import eq_, ne_ -from test.lib import fixtures, testing +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column +from sqlalchemy.testing import eq_, ne_ +from sqlalchemy.testing import fixtures, testing class InitTest(fixtures.ORMTest): diff --git a/test/orm/test_joins.py b/test/orm/test_joins.py index 086897186..358c3ae8d 100644 --- a/test/orm/test_joins.py +++ b/test/orm/test_joins.py @@ -1,4 +1,4 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message import operator from sqlalchemy import * from sqlalchemy import exc as sa_exc, util @@ -7,14 +7,14 @@ from sqlalchemy.engine import default from sqlalchemy.orm import * from sqlalchemy.orm import attributes -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import sqlalchemy as sa -from test.lib import testing, AssertsCompiledSQL, Column, engines - +from sqlalchemy import testing +from sqlalchemy.testing import AssertsCompiledSQL, Column, engines from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures from sqlalchemy.orm.util import join, outerjoin, with_parent diff --git a/test/orm/test_lazy_relations.py b/test/orm/test_lazy_relations.py index 297d027f5..66b1eb5e4 100644 --- a/test/orm/test_lazy_relations.py +++ b/test/orm/test_lazy_relations.py @@ -1,18 +1,18 @@ """basic tests of lazy loaded attributes""" -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.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 test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, SmallInteger from sqlalchemy.types import TypeDecorator -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, create_session -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures diff --git a/test/orm/test_load_on_fks.py b/test/orm/test_load_on_fks.py index 05b78ccb8..813d8d17a 100644 --- a/test/orm/test_load_on_fks.py +++ b/test/orm/test_load_on_fks.py @@ -2,12 +2,12 @@ from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base -from test.lib.testing import eq_, AssertsExecutionResults, assert_raises -from test.lib import testing -from test.lib import fixtures +from sqlalchemy.testing import eq_, AssertsExecutionResults, assert_raises +from sqlalchemy import testing +from sqlalchemy.testing import fixtures from sqlalchemy.orm.attributes import instance_state from sqlalchemy.orm.exc import FlushError -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column engine = testing.db diff --git a/test/orm/test_lockmode.py b/test/orm/test_lockmode.py index b2bc60865..0fe82f394 100644 --- a/test/orm/test_lockmode.py +++ b/test/orm/test_lockmode.py @@ -2,8 +2,8 @@ from sqlalchemy.engine import default from sqlalchemy.databases import * from sqlalchemy.orm import mapper from sqlalchemy.orm import Session -from test.lib import AssertsCompiledSQL -from test.lib.testing import assert_raises_message +from sqlalchemy.testing import AssertsCompiledSQL +from sqlalchemy.testing import assert_raises_message from test.orm import _fixtures diff --git a/test/orm/test_manytomany.py b/test/orm/test_manytomany.py index ed9075833..ca9c3c095 100644 --- a/test/orm/test_manytomany.py +++ b/test/orm/test_manytomany.py @@ -1,13 +1,13 @@ -from test.lib.testing import assert_raises, \ +from sqlalchemy.testing import assert_raises, \ assert_raises_message, eq_ import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, create_session, \ exc as orm_exc, sessionmaker -from test.lib import fixtures +from sqlalchemy.testing import fixtures class M2MTest(fixtures.MappedTest): diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index 487ec066f..b2e36b273 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 test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import MetaData, Integer, String, ForeignKey, func, util -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.engine import default from sqlalchemy.orm import mapper, relationship, backref, \ create_session, class_mapper, configure_mappers, reconstructor, \ @@ -12,10 +12,10 @@ from sqlalchemy.orm import mapper, relationship, backref, \ column_property, composite, dynamic_loader, \ comparable_property, Session from sqlalchemy.orm.persistence import _sort_states -from test.lib.testing import eq_, AssertsCompiledSQL, is_ -from test.lib import fixtures +from sqlalchemy.testing import eq_, AssertsCompiledSQL, is_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.assertsql import CompiledSQL +from sqlalchemy.testing.assertsql import CompiledSQL import logging class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL): diff --git a/test/orm/test_merge.py b/test/orm/test_merge.py index 47203b874..e1474f39b 100644 --- a/test/orm/test_merge.py +++ b/test/orm/test_merge.py @@ -1,19 +1,19 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import sqlalchemy as sa from sqlalchemy import Integer, PickleType, String, ForeignKey import operator -from test.lib import testing +from sqlalchemy import testing from sqlalchemy.util import OrderedSet from sqlalchemy.orm import mapper, relationship, create_session, \ PropComparator, synonym, comparable_property, sessionmaker, \ attributes, Session, backref, configure_mappers from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.orm.interfaces import MapperOption -from test.lib.testing import eq_, ne_ -from test.lib import fixtures +from sqlalchemy.testing import eq_, ne_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures from sqlalchemy import event, and_, case -from test.lib.schema import Table, Column +from sqlalchemy.testing.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 0592b1e10..9ad54fd10 100644 --- a/test/orm/test_naturalpks.py +++ b/test/orm/test_naturalpks.py @@ -3,16 +3,16 @@ Primary key changing capabilities and passive/non-passive cascading updates. """ from __future__ import with_statement -from test.lib.testing import eq_, ne_, \ +from sqlalchemy.testing import eq_, ne_, \ assert_raises, assert_raises_message import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, Unicode -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session, backref, Session from sqlalchemy.orm.session import make_transient -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures class NaturalPKTest(fixtures.MappedTest): @@ -965,7 +965,7 @@ class CascadeToFKPKTest(fixtures.MappedTest, testing.AssertsCompiledSQL): sess.add(u2) sess.add(a2) - from test.lib.assertsql import CompiledSQL + from sqlalchemy.testing.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_of_type.py b/test/orm/test_of_type.py index 40d553fe8..28bcc6e81 100644 --- a/test/orm/test_of_type.py +++ b/test/orm/test_of_type.py @@ -2,11 +2,12 @@ from sqlalchemy.orm import Session, aliased, with_polymorphic, \ contains_eager, joinedload, subqueryload, relationship,\ subqueryload_all, joinedload_all from sqlalchemy import and_ -from test.lib import testing, fixtures -from test.lib.testing import assert_raises, eq_ -from test.lib.schema import Column +from sqlalchemy import testing +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import assert_raises, eq_ +from sqlalchemy.testing.schema import Column from sqlalchemy.engine import default -from test.lib.entities import ComparableEntity +from sqlalchemy.testing.entities import ComparableEntity from sqlalchemy import Integer, String, ForeignKey from .inheritance._poly_fixtures import Company, Person, Engineer, Manager, Boss, \ Machine, Paperwork, _PolymorphicFixtureBase, _Polymorphic,\ diff --git a/test/orm/test_onetoone.py b/test/orm/test_onetoone.py index 09dc379b8..19b13c4dd 100644 --- a/test/orm/test_onetoone.py +++ b/test/orm/test_onetoone.py @@ -1,9 +1,9 @@ import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session -from test.lib import fixtures +from sqlalchemy.testing import fixtures class O2OTest(fixtures.MappedTest): diff --git a/test/orm/test_pickled.py b/test/orm/test_pickled.py index 88867edcb..d70d36b59 100644 --- a/test/orm/test_pickled.py +++ b/test/orm/test_pickled.py @@ -1,11 +1,11 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy.util import pickle import sqlalchemy as sa -from test.lib import testing -from test.lib.util import picklers -from test.lib.testing import assert_raises_message +from sqlalchemy import testing +from sqlalchemy.testing.util import picklers +from sqlalchemy.testing import assert_raises_message from sqlalchemy import Integer, String, ForeignKey, exc, MetaData -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, create_session, \ sessionmaker, attributes, interfaces,\ clear_mappers, exc as orm_exc,\ @@ -13,9 +13,9 @@ from sqlalchemy.orm import mapper, relationship, create_session, \ lazyload, aliased from sqlalchemy.orm.collections import attribute_mapped_collection, \ column_mapped_collection -from test.lib import fixtures +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.pickleable import User, Address, Dingaling, Order, \ +from sqlalchemy.testing.pickleable import User, Address, Dingaling, Order, \ Child1, Child2, Parent, Screen, EmailUser @@ -87,7 +87,7 @@ class PickleTest(fixtures.MappedTest): assert_raises_message( orm_exc.UnmappedInstanceError, - "Cannot deserialize object of type - no mapper()", + "Cannot deserialize object of type - no mapper()", pickle.loads, u1_pickled) def test_no_instrumentation(self): diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 38739afff..56275a735 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1,4 +1,3 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message import operator from sqlalchemy import MetaData, null, exists, text, union, literal, \ literal_column, func, between, Unicode, desc, and_, bindparam, \ @@ -11,14 +10,14 @@ from sqlalchemy.orm import attributes, mapper, relationship, backref, \ configure_mappers, create_session, synonym, Session, class_mapper, \ aliased, column_property, joinedload_all, joinedload, Query,\ util as orm_util -from test.lib.assertsql import CompiledSQL -from test.lib.testing import eq_ -from test.lib.schema import Table, Column - +from sqlalchemy.testing.assertsql import CompiledSQL +from sqlalchemy.testing.schema import Table, Column import sqlalchemy as sa -from test.lib import testing, AssertsCompiledSQL, engines +from sqlalchemy import testing +from sqlalchemy.testing.assertions import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import AssertsCompiledSQL from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures, engines from sqlalchemy.orm.util import join, outerjoin, with_parent diff --git a/test/orm/test_rel_fn.py b/test/orm/test_rel_fn.py index f5fa1d4c9..f23d40be7 100644 --- a/test/orm/test_rel_fn.py +++ b/test/orm/test_rel_fn.py @@ -1,6 +1,6 @@ -from test.lib.testing import assert_raises, assert_raises_message, eq_, \ +from sqlalchemy.testing import assert_raises, assert_raises_message, eq_, \ AssertsCompiledSQL, is_ -from test.lib import fixtures +from sqlalchemy.testing import fixtures from sqlalchemy.orm import relationships, foreign, remote, remote_foreign from sqlalchemy import MetaData, Table, Column, ForeignKey, Integer, \ select, ForeignKeyConstraint, exc, func, and_ diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py index 394a1fe7a..a2a81d0ee 100644 --- a/test/orm/test_relationships.py +++ b/test/orm/test_relationships.py @@ -1,17 +1,17 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import datetime import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, MetaData, and_ -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, relation, \ backref, create_session, configure_mappers, \ clear_mappers, sessionmaker, attributes,\ Session, composite, column_property, foreign,\ remote from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE, MANYTOMANY -from test.lib.testing import eq_, startswith_, AssertsCompiledSQL, is_ -from test.lib import fixtures +from sqlalchemy.testing import eq_, startswith_, AssertsCompiledSQL, is_ +from sqlalchemy.testing import fixtures from test.orm import _fixtures from sqlalchemy import exc diff --git a/test/orm/test_scoping.py b/test/orm/test_scoping.py index 978a8bc1d..87557a162 100644 --- a/test/orm/test_scoping.py +++ b/test/orm/test_scoping.py @@ -1,12 +1,12 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy.orm import scoped_session from sqlalchemy import Integer, String, ForeignKey -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, query -from test.lib.testing import eq_ -from test.lib import fixtures +from sqlalchemy.testing import eq_ +from sqlalchemy.testing import fixtures diff --git a/test/orm/test_selectable.py b/test/orm/test_selectable.py index 1a46e3b6d..42347213e 100644 --- a/test/orm/test_selectable.py +++ b/test/orm/test_selectable.py @@ -1,12 +1,12 @@ """Generic mapping to Select statements""" -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message import sqlalchemy as sa -from test.lib import testing +from sqlalchemy import testing from sqlalchemy import String, Integer, select -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, Session -from test.lib.testing import eq_, AssertsCompiledSQL -from test.lib import fixtures +from sqlalchemy.testing import eq_, AssertsCompiledSQL +from sqlalchemy.testing import fixtures diff --git a/test/orm/test_session.py b/test/orm/test_session.py index 28293675d..61fed6d18 100644 --- a/test/orm/test_session.py +++ b/test/orm/test_session.py @@ -1,20 +1,20 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, assert_warnings -from test.lib.util import gc_collect -from test.lib import pickleable +from sqlalchemy.testing.util import gc_collect +from sqlalchemy.testing import pickleable from sqlalchemy.util import pickle import inspect from sqlalchemy.orm import create_session, sessionmaker, attributes, \ make_transient, Session import sqlalchemy as sa -from test.lib import engines, testing, config +from sqlalchemy.testing import engines, testing, config from sqlalchemy import Integer, String, Sequence -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, backref, joinedload, \ exc as orm_exc, object_session from sqlalchemy.util import pypy -from test.lib import fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import fixtures from test.orm import _fixtures class SessionTest(_fixtures.FixtureTest): diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index 8d7c89044..f6e2299b4 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -1,15 +1,15 @@ -from test.lib.testing import eq_, is_, is_not_ -from test.lib import testing -from test.lib.schema import Table, Column +from sqlalchemy.testing import eq_, is_, is_not_ +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column from sqlalchemy import Integer, String, ForeignKey, bindparam, inspect from sqlalchemy.orm import backref, subqueryload, subqueryload_all, \ mapper, relationship, clear_mappers, create_session, lazyload, \ aliased, joinedload, deferred, undefer, eagerload_all,\ Session -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message -from test.lib.assertsql import CompiledSQL -from test.lib import fixtures +from sqlalchemy.testing.assertsql import CompiledSQL +from sqlalchemy.testing import fixtures from test.orm import _fixtures import sqlalchemy as sa diff --git a/test/orm/test_sync.py b/test/orm/test_sync.py index 9134e47ce..a2c894725 100644 --- a/test/orm/test_sync.py +++ b/test/orm/test_sync.py @@ -1,8 +1,8 @@ -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.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures from sqlalchemy import Integer, String, ForeignKey, func from sqlalchemy.orm import mapper, relationship, backref, \ create_session, unitofwork, attributes,\ diff --git a/test/orm/test_transaction.py b/test/orm/test_transaction.py index fdbf6ae1f..d2b8b8b85 100644 --- a/test/orm/test_transaction.py +++ b/test/orm/test_transaction.py @@ -1,15 +1,15 @@ -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, assert_warnings from sqlalchemy import * from sqlalchemy.orm import attributes from sqlalchemy import exc as sa_exc, event from sqlalchemy.orm import exc as orm_exc from sqlalchemy.orm import * -from test.lib.util import gc_collect -from test.lib import testing -from test.lib import fixtures -from test.lib import engines +from sqlalchemy.testing.util import gc_collect +from sqlalchemy import testing +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import engines from test.orm._fixtures import FixtureTest diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py index 9691c38a6..8f354b083 100644 --- a/test/orm/test_unitofwork.py +++ b/test/orm/test_unitofwork.py @@ -1,25 +1,25 @@ # coding: utf-8 """Tests unitofwork operations.""" -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.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 import Integer, String, ForeignKey, literal_column, event -from test.lib import engines, testing, pickleable -from test.lib.schema import Table -from test.lib.schema import Column +from sqlalchemy.testing import engines, testing, pickleable +from sqlalchemy.testing.schema import Table +from sqlalchemy.testing.schema import Column from sqlalchemy.orm import mapper, relationship, create_session, \ column_property, attributes, Session, reconstructor, object_session,\ exc as orm_exc -from test.lib.testing import eq_, ne_ -from test.lib.util import gc_collect -from test.lib import fixtures +from sqlalchemy.testing import eq_, ne_ +from sqlalchemy.testing.util import gc_collect +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib import fixtures -from test.lib.assertsql import AllOf, CompiledSQL +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.assertsql import AllOf, CompiledSQL import gc class UnitOfWorkTest(object): diff --git a/test/orm/test_unitofworkv2.py b/test/orm/test_unitofworkv2.py index 860fffdf5..7c7337c62 100644 --- a/test/orm/test_unitofworkv2.py +++ b/test/orm/test_unitofworkv2.py @@ -1,14 +1,15 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message -from test.lib import testing, engines -from test.lib.schema import Table, Column +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy import testing +from sqlalchemy.testing import engines +from sqlalchemy.testing.schema import Table, Column from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures from sqlalchemy import Integer, String, ForeignKey, func from sqlalchemy.orm import mapper, relationship, backref, \ create_session, unitofwork, attributes,\ Session, class_mapper, sync, exc as orm_exc -from test.lib.assertsql import AllOf, CompiledSQL +from sqlalchemy.testing.assertsql import AllOf, CompiledSQL class AssertsUOW(object): def _get_test_uow(self, session): diff --git a/test/orm/test_update_delete.py b/test/orm/test_update_delete.py index e259c5229..85b880710 100644 --- a/test/orm/test_update_delete.py +++ b/test/orm/test_update_delete.py @@ -1,9 +1,9 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message -from test.lib import fixtures, testing +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import fixtures, testing from sqlalchemy import Integer, String, ForeignKey, or_, and_, exc, select, func from sqlalchemy.orm import mapper, relationship, backref, Session, joinedload -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column diff --git a/test/orm/test_utils.py b/test/orm/test_utils.py index 617f1d538..00fbe7771 100644 --- a/test/orm/test_utils.py +++ b/test/orm/test_utils.py @@ -1,4 +1,4 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message from sqlalchemy.orm import interfaces, util from sqlalchemy import Column from sqlalchemy import Integer @@ -6,10 +6,10 @@ from sqlalchemy import MetaData from sqlalchemy import Table from sqlalchemy.orm import aliased from sqlalchemy.orm import mapper, create_session -from test.lib import testing -from test.lib import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ class AliasedClassTest(fixtures.TestBase): diff --git a/test/orm/test_versioning.py b/test/orm/test_versioning.py index 0faeb133d..aa1e99602 100644 --- a/test/orm/test_versioning.py +++ b/test/orm/test_versioning.py @@ -1,16 +1,16 @@ import datetime import sqlalchemy as sa -from test.lib import engines, testing +from sqlalchemy.testing import engines, testing from sqlalchemy import Integer, String, Date, ForeignKey, literal_column, \ orm, exc, select, TypeDecorator -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.orm import mapper, relationship, Session, \ create_session, column_property, sessionmaker,\ exc as orm_exc -from test.lib.testing import eq_, ne_, assert_raises, assert_raises_message -from test.lib import fixtures +from sqlalchemy.testing import eq_, ne_, assert_raises, assert_raises_message +from sqlalchemy.testing import fixtures from test.orm import _fixtures -from test.lib import fixtures +from sqlalchemy.testing import fixtures _uuids = [ diff --git a/test/perf/insertspeed.py b/test/perf/insertspeed.py index 9b397771d..03d2c4144 100644 --- a/test/perf/insertspeed.py +++ b/test/perf/insertspeed.py @@ -1,7 +1,7 @@ import sys, time from sqlalchemy import * from sqlalchemy.orm import * -from test.lib import profiling +from sqlalchemy.testing import profiling db = create_engine('sqlite://') metadata = MetaData(db) diff --git a/test/perf/large_flush.py b/test/perf/large_flush.py index b23de080c..b1ecce852 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 test.lib import profiling +from sqlalchemy.testing import profiling class Object(object): pass diff --git a/test/perf/objselectspeed.py b/test/perf/objselectspeed.py index f64c73c69..c0ed88444 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 test.lib.util import gc_collect -from test.lib import profiling +from sqlalchemy.testing.util import gc_collect +from sqlalchemy.testing import profiling db = create_engine('sqlite://') metadata = MetaData(db) diff --git a/test/perf/objupdatespeed.py b/test/perf/objupdatespeed.py index 078d95fa3..98d10180e 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 test.lib import * -from test.lib.util import gc_collect +from sqlalchemy.testing import * +from sqlalchemy.testing.util import gc_collect NUM = 100 diff --git a/test/perf/ormsession.py b/test/perf/ormsession.py index aff265ff1..5e38d6e80 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 test.lib import * -from test.lib.profiling import profiled +from sqlalchemy.testing import * +from sqlalchemy.testing.profiling import profiled class Item(object): def __repr__(self): diff --git a/test/perf/sessions.py b/test/perf/sessions.py index 75106f6ca..80553fe48 100644 --- a/test/perf/sessions.py +++ b/test/perf/sessions.py @@ -1,8 +1,8 @@ from sqlalchemy import * from sqlalchemy.orm import * -from test.lib.compat import gc_collect -from test.lib import AssertsExecutionResults, profiling, testing +from sqlalchemy.testing.compat import gc_collect +from sqlalchemy.testing import 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/profiles.txt b/test/profiles.txt new file mode 100644 index 000000000..5decc3aaa --- /dev/null +++ b/test/profiles.txt @@ -0,0 +1,264 @@ +# /Users/classic/dev/sqlalchemy/./test/lib/profiles.txt +# This file is written out on a per-environment basis. +# For each test in aaa_profiling, the corresponding function and +# environment is located within this file. If it doesn't exist, +# the test is skipped. +# If a callcount does exist, it is compared to what we received. +# assertions are raised if the counts do not match. +# +# To add a new callcount test, apply the function_call_count +# decorator and re-run the tests using the --write-profiles option - +# this file will be rewritten including the new count. +# + +# TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert + +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.5_sqlite_pysqlite_nocextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.6_sqlite_pysqlite_nocextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_cextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_nocextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_cextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_nocextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_cextensions 62 +test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_nocextensions 62 + +# TEST: test.aaa_profiling.test_compiler.CompileTest.test_select + +test.aaa_profiling.test_compiler.CompileTest.test_select 2.5_sqlite_pysqlite_nocextensions 134 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.6_sqlite_pysqlite_nocextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_cextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_nocextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_cextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_nocextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_cextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_nocextensions 135 + +# TEST: test.aaa_profiling.test_compiler.CompileTest.test_update + +test.aaa_profiling.test_compiler.CompileTest.test_update 2.5_sqlite_pysqlite_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.6_sqlite_pysqlite_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_cextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_cextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_cextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_nocextensions 65 + +# TEST: test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause + +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.5_sqlite_pysqlite_nocextensions 129 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.6_sqlite_pysqlite_nocextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_cextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_nocextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_cextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_nocextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_cextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_nocextensions 130 + +# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity + +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.5_sqlite_pysqlite_nocextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.6_sqlite_pysqlite_nocextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_mysql_mysqldb_cextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_mysql_mysqldb_nocextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_postgresql_psycopg2_cextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_postgresql_psycopg2_nocextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_sqlite_pysqlite_cextensions 17987 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_sqlite_pysqlite_nocextensions 17987 + +# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity + +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.5_sqlite_pysqlite_nocextensions 116289 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.6_sqlite_pysqlite_nocextensions 116790 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_cextensions 122540 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_nocextensions 125290 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_cextensions 115040 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_nocextensions 117790 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_cextensions 114040 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_nocextensions 116790 + +# TEST: test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks + +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.5_sqlite_pysqlite_nocextensions 19852 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.6_sqlite_pysqlite_nocextensions 19217 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_cextensions 19491 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_nocextensions 19781 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_cextensions 18878 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_nocextensions 19168 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_cextensions 18957 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_nocextensions 19217 + +# TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_load + +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.5_sqlite_pysqlite_nocextensions 1178 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.6_sqlite_pysqlite_nocextensions 1174 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_cextensions 1341 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_nocextensions 1366 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_cextensions 1200 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_nocextensions 1225 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_cextensions 1149 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_nocextensions 1174 + +# TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_no_load + +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.5_sqlite_pysqlite_nocextensions 108,22 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.6_sqlite_pysqlite_nocextensions 100,18 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_cextensions 100,18 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_mysql_mysqldb_nocextensions 100,18 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_cextensions 100,18 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_nocextensions 100,18 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_cextensions 100,18 +test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_nocextensions 100,18 + +# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect + +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.5_sqlite_pysqlite_nocextensions 71 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.6_sqlite_pysqlite_nocextensions 67 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_mysql_mysqldb_cextensions 67 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_mysql_mysqldb_nocextensions 67 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_postgresql_psycopg2_cextensions 67 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_postgresql_psycopg2_nocextensions 67 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlite_cextensions 67 +test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlite_nocextensions 67 + +# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect + +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.5_sqlite_pysqlite_nocextensions 32 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.6_sqlite_pysqlite_nocextensions 29 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_mysql_mysqldb_cextensions 29 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_mysql_mysqldb_nocextensions 29 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_postgresql_psycopg2_cextensions 29 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_postgresql_psycopg2_nocextensions 29 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqlite_cextensions 29 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqlite_nocextensions 29 + +# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect + +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.5_sqlite_pysqlite_nocextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.6_sqlite_pysqlite_nocextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_mysql_mysqldb_cextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_mysql_mysqldb_nocextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_postgresql_psycopg2_cextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_postgresql_psycopg2_nocextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sqlite_pysqlite_cextensions 6 +test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sqlite_pysqlite_nocextensions 6 + +# TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute + +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.5_sqlite_pysqlite_nocextensions 41 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.6_sqlite_pysqlite_nocextensions 42 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_mysql_mysqldb_cextensions 40 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_mysql_mysqldb_nocextensions 42 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_postgresql_psycopg2_cextensions 40 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_postgresql_psycopg2_nocextensions 42 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_cextensions 40 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_nocextensions 42 + +# TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute + +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.5_sqlite_pysqlite_nocextensions 64 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.6_sqlite_pysqlite_nocextensions 65 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_cextensions 63 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_nocextensions 65 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_cextensions 63 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_nocextensions 65 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_cextensions 63 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_nocextensions 65 + +# TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile + +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.5_sqlite_pysqlite_nocextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.6_sqlite_pysqlite_nocextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_cextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_nocextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_cextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_nocextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_cextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_nocextensions 14 + +# TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_string + +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.5_sqlite_pysqlite_nocextensions 14413 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.6_sqlite_pysqlite_nocextensions 14414 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_cextensions 452 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_nocextensions 14472 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_cextensions 20438 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_nocextensions 34458 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_cextensions 394 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_nocextensions 14414 + +# TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_unicode + +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.5_sqlite_pysqlite_nocextensions 14413 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.6_sqlite_pysqlite_nocextensions 14414 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_cextensions 452 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_nocextensions 44472 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_cextensions 20438 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_nocextensions 34458 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_cextensions 394 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_nocextensions 14414 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5044 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 5088 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 247 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 247 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 3366 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 3590 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 10366 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 11982 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1005 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1109 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 1736 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 1779 + +# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview + +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_cextensions 2219 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_nocextensions 2449 + +# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate + +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5977 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 6096 + +# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert + +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 392 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 399 + +# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties + +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 6124 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 6356 + +# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions + +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 18140 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 19571 + +# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates + +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1018 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1114 + +# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing + +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 2614 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 2677 diff --git a/test/requirements.py b/test/requirements.py new file mode 100644 index 000000000..042604935 --- /dev/null +++ b/test/requirements.py @@ -0,0 +1,500 @@ +"""Global database feature support policy. + +Provides decorators to mark tests requiring specific feature support from the +target database. + +""" + +from sqlalchemy import util +import sys +from sqlalchemy.testing.suite.requirements import SuiteRequirements +from sqlalchemy.testing.exclusions import \ + skip, \ + skip_if,\ + only_if,\ + only_on,\ + fails_on,\ + fails_on_everything_except,\ + fails_if,\ + SpecPredicate,\ + against + +def no_support(db, reason): + return SpecPredicate(db, description=reason) + +def exclude(db, op, spec, description=None): + return SpecPredicate(db, op, spec, description=description) + + +crashes = skip + +def _chain_decorators_on(*decorators): + def decorate(fn): + for decorator in reversed(decorators): + fn = decorator(fn) + return fn + return decorate + +class DefaultRequirements(SuiteRequirements): + @property + def deferrable_or_no_constraints(self): + """Target database must support derferable constraints.""" + + return skip_if([ + no_support('firebird', 'not supported by database'), + no_support('mysql', 'not supported by database'), + no_support('mssql', 'not supported by database'), + ]) + + @property + def foreign_keys(self): + """Target database must support foreign keys.""" + + return skip_if( + no_support('sqlite', 'not supported by database') + ) + + + @property + def unbounded_varchar(self): + """Target database must support VARCHAR with no length""" + + return skip_if([ + "firebird", "oracle", "mysql" + ], "not supported by database" + ) + + @property + def boolean_col_expressions(self): + """Target database must support boolean expressions as columns""" + return skip_if([ + 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'), + ]) + + @property + def standalone_binds(self): + """target database/driver supports bound parameters as column expressions + without being in the context of a typed column. + + """ + return skip_if(["firebird", "mssql+mxodbc"], + "not supported by driver") + + @property + def identity(self): + """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 skip_if(["firebird", "oracle", "postgresql", "sybase"], + "not supported by database" + ) + + @property + def reflectable_autoincrement(self): + """Target database must support tables that can automatically generate + PKs assuming they were reflected. + + this is essentially all the DBs in "identity" plus Postgresql, which + has SERIAL support. FB and Oracle (and sybase?) require the Sequence to + be explicitly added, including if the table was reflected. + """ + return skip_if(["firebird", "oracle", "sybase"], + "not supported by database" + ) + + @property + def binary_comparisons(self): + """target database/driver can allow BLOB/BINARY fields to be compared + against a bound parameter value. + """ + return skip_if(["oracle", "mssql"], + "not supported by database/driver" + ) + + @property + def independent_cursors(self): + """Target must support simultaneous, independent database cursors + on a single connection.""" + + return skip_if(["mssql+pyodbc", "mssql+mxodbc"], "no driver support") + + @property + def independent_connections(self): + """Target must support simultaneous, independent database connections.""" + + # This is also true of some configurations of UnixODBC and probably win32 + # ODBC as well. + return skip_if([ + no_support("sqlite", + "independent connections disabled " + "when :memory: connections are used"), + exclude("mssql", "<", (9, 0, 0), + "SQL Server 2005+ is required for " + "independent connections" + ) + ] + ) + + @property + def updateable_autoincrement_pks(self): + """Target must support UPDATE on autoincrement/integer primary key.""" + + return skip_if(["mssql", "sybase"], + "IDENTITY columns can't be updated") + + @property + def isolation_level(self): + return _chain_decorators_on( + only_on(('postgresql', 'sqlite', 'mysql'), + "DBAPI has no isolation level support"), + fails_on('postgresql+pypostgresql', + 'pypostgresql bombs on multiple isolation level calls') + ) + + @property + def row_triggers(self): + """Target must support standard statement-running EACH ROW triggers.""" + + return skip_if([ + # 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'), + ]) + + @property + def correlated_outer_joins(self): + """Target must support an outer join to a subquery which + correlates to the parent.""" + + return skip_if("oracle", 'Raises "ORA-01799: a column may not be ' + 'outer-joined to a subquery"') + + @property + def update_from(self): + """Target must support UPDATE..FROM syntax""" + + return only_on(['postgresql', 'mssql', 'mysql'], + "Backend does not support UPDATE..FROM") + + + @property + def savepoints(self): + """Target database must support savepoints.""" + + return skip_if([ + "access", + "sqlite", + "sybase", + ("mysql", "<", (5, 0, 3)), + ("informix", "<", (11, 55, "xC3")) + ], "savepoints not supported") + + @property + def denormalized_names(self): + """Target database must have 'denormalized', i.e. + UPPERCASE as case insensitive names.""" + + return skip_if( + lambda: not self.db.dialect.requires_name_normalize, + "Backend does not require denormalized names." + ) + + @property + def schemas(self): + """Target database must support external schemas, and have one + named 'test_schema'.""" + + return skip_if([ + "sqlite", + "firebird" + ], "no schema support") + + @property + def sequences(self): + """Target database must support SEQUENCEs.""" + + return only_if([ + "postgresql", "firebird", "oracle" + ], "no SEQUENCE support") + + @property + def update_nowait(self): + """Target database must support SELECT...FOR UPDATE NOWAIT""" + return skip_if(["access", "firebird", "mssql", "mysql", "sqlite", "sybase"], + "no FOR UPDATE NOWAIT support" + ) + + @property + def subqueries(self): + """Target database must support subqueries.""" + + return skip_if(exclude('mysql', '<', (4, 1, 1)), 'no subquery support') + + @property + def intersect(self): + """Target database must support INTERSECT or equivalent.""" + + return fails_if([ + "firebird", "mysql", "sybase", "informix" + ], 'no support for INTERSECT') + + @property + def except_(self): + """Target database must support EXCEPT or equivalent (i.e. MINUS).""" + return fails_if([ + "firebird", "mysql", "sybase", "informix" + ], 'no support for EXCEPT') + + @property + def offset(self): + """Target database must support some method of adding OFFSET or + equivalent to a result set.""" + return fails_if([ + "sybase" + ], 'no support for OFFSET or equivalent') + + @property + def window_functions(self): + return only_if([ + "postgresql", "mssql", "oracle" + ], "Backend does not support window functions") + + @property + def returning(self): + return only_if(["postgresql", "mssql", "oracle", "firebird"], + "'returning' not supported by database" + ) + + @property + def two_phase_transactions(self): + """Target database must support two-phase transactions.""" + + return skip_if([ + no_support('access', 'two-phase xact not supported by database'), + no_support('firebird', 'no SA implementation'), + no_support('maxdb', 'two-phase xact not supported by database'), + no_support('mssql', 'two-phase xact not supported by drivers'), + no_support('oracle', 'two-phase xact not implemented in SQLA/oracle'), + no_support('drizzle', 'two-phase xact not supported by database'), + no_support('sqlite', 'two-phase xact not supported by database'), + no_support('sybase', 'two-phase xact not supported by drivers/SQLA'), + no_support('postgresql+zxjdbc', + 'FIXME: JDBC driver confuses the transaction state, may ' + 'need separate XA implementation'), + exclude('mysql', '<', (5, 0, 3), + 'two-phase xact not supported by database'), + ]) + + @property + def views(self): + """Target database must support VIEWs.""" + + return skip_if("drizzle", "no VIEW support") + + @property + def unicode_connections(self): + """Target driver must support some encoding of Unicode across the wire.""" + # TODO: expand to exclude MySQLdb versions w/ broken unicode + return skip_if([ + exclude('mysql', '<', (4, 1, 1), 'no unicode connection support'), + ]) + + @property + def unicode_ddl(self): + """Target driver must support some encoding of Unicode across the wire.""" + # TODO: expand to exclude MySQLdb versions w/ broken unicode + return skip_if([ + 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'), + ]) + + @property + def sane_rowcount(self): + return skip_if( + lambda: not self.db.dialect.supports_sane_rowcount, + "driver doesn't support 'sane' rowcount" + ) + + @property + def cextensions(self): + return skip_if( + lambda: not self._has_cextensions(), "C extensions not installed" + ) + + + @property + def emulated_lastrowid(self): + """"target dialect retrieves cursor.lastrowid or an equivalent + after an insert() construct executes. + """ + return fails_on_everything_except('mysql+mysqldb', 'mysql+oursql', + 'sqlite+pysqlite', 'mysql+pymysql', + 'mssql+pyodbc', 'mssql+mxodbc') + + @property + def dbapi_lastrowid(self): + """"target backend includes a 'lastrowid' accessor on the DBAPI + cursor object. + + """ + return fails_on_everything_except('mysql+mysqldb', 'mysql+oursql', + 'sqlite+pysqlite', 'mysql+pymysql') + + @property + def sane_multi_rowcount(self): + return skip_if( + lambda: not self.db.dialect.supports_sane_multi_rowcount, + "driver doesn't support 'sane' multi row count" + ) + + @property + def nullsordering(self): + """Target backends that support nulls ordering.""" + return _chain_decorators_on( + fails_on_everything_except('postgresql', 'oracle', 'firebird') + ) + + @property + def reflects_pk_names(self): + """Target driver reflects the name of primary key constraints.""" + return _chain_decorators_on( + fails_on_everything_except('postgresql', 'oracle') + ) + + @property + def python2(self): + return _chain_decorators_on( + skip_if( + lambda: sys.version_info >= (3,), + "Python version 2.xx is required." + ) + ) + + @property + def python3(self): + return _chain_decorators_on( + skip_if( + lambda: sys.version_info < (3,), + "Python version 3.xx is required." + ) + ) + + @property + def python26(self): + return _chain_decorators_on( + skip_if( + lambda: sys.version_info < (2, 6), + "Python version 2.6 or greater is required" + ) + ) + + @property + def python25(self): + return _chain_decorators_on( + skip_if( + lambda: sys.version_info < (2, 5), + "Python version 2.5 or greater is required" + ) + ) + + @property + def cpython(self): + return _chain_decorators_on( + only_if(lambda: util.cpython, + "cPython interpreter needed" + ) + ) + + @property + def predictable_gc(self): + """target platform must remove all cycles unconditionally when + gc.collect() is called, as well as clean out unreferenced subclasses. + + """ + return self.cpython + + @property + def sqlite(self): + return _chain_decorators_on( + skip_if(lambda: not self._has_sqlite()) + ) + + @property + def ad_hoc_engines(self): + """Test environment must allow ad-hoc engine/connection creation. + + DBs that scale poorly for many connections, even when closed, i.e. + Oracle, may use the "--low-connections" option which flags this requirement + as not present. + + """ + return _chain_decorators_on( + skip_if(lambda: self.config.options.low_connections) + ) + + @property + def skip_mysql_on_windows(self): + """Catchall for a large variety of MySQL on Windows failures""" + + return _chain_decorators_on( + skip_if(self._has_mysql_on_windows, + "Not supported on MySQL + Windows" + ) + ) + + @property + def english_locale_on_postgresql(self): + return _chain_decorators_on( + skip_if(lambda: against('postgresql') \ + and not self.db.scalar('SHOW LC_COLLATE').startswith('en')) + ) + + @property + def selectone(self): + """target driver must support the literal statement 'select 1'""" + return _chain_decorators_on( + skip_if(lambda: against('oracle'), + "non-standard SELECT scalar syntax"), + skip_if(lambda: against('firebird'), + "non-standard SELECT scalar syntax") + ) + + def _has_cextensions(self): + try: + from sqlalchemy import cresultproxy, cprocessors + return True + except ImportError: + return False + + def _has_sqlite(self): + from sqlalchemy import create_engine + try: + create_engine('sqlite://') + return True + except ImportError: + return False + + def _has_mysql_on_windows(self): + return against('mysql') and \ + self.db.dialect._detect_casing(self.db) == 1 + + def _has_mysql_fully_case_sensitive(self): + return against('mysql') and \ + self.db.dialect._detect_casing(self.db) == 0 + diff --git a/test/sql/test_case_statement.py b/test/sql/test_case_statement.py index 7b8ae88db..944a15384 100644 --- a/test/sql/test_case_statement.py +++ b/test/sql/test_case_statement.py @@ -1,7 +1,8 @@ -from test.lib.testing import assert_raises, assert_raises_message, eq_ +from sqlalchemy.testing import assert_raises, assert_raises_message, eq_ import sys from sqlalchemy import * -from test.lib import * +from sqlalchemy.testing import fixtures, AssertsCompiledSQL +from sqlalchemy import testing from sqlalchemy import util, exc from sqlalchemy.sql import table, column diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 356f2e8b1..3c6e687ca 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -10,7 +10,9 @@ styling and coherent test organization. """ -from test.lib.testing import eq_, is_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, is_, assert_raises, assert_raises_message +from sqlalchemy import testing +from sqlalchemy.testing import fixtures, AssertsCompiledSQL import datetime, re, operator, decimal from sqlalchemy import * from sqlalchemy import exc, sql, util, types, schema @@ -18,7 +20,6 @@ from sqlalchemy.sql import table, column, label, compiler from sqlalchemy.sql.expression import ClauseList, _literal_as_text, HasPrefixes from sqlalchemy.engine import default from sqlalchemy.databases import * -from test.lib import * from sqlalchemy.ext.compiler import compiles table1 = table('mytable', diff --git a/test/sql/test_constraints.py b/test/sql/test_constraints.py index 75a7cb2e1..261932a7f 100644 --- a/test/sql/test_constraints.py +++ b/test/sql/test_constraints.py @@ -1,11 +1,12 @@ -from test.lib.testing import assert_raises, assert_raises_message +from sqlalchemy.testing import assert_raises, assert_raises_message from sqlalchemy import * from sqlalchemy import exc, schema -from test.lib import * -from test.lib import config, engines +from sqlalchemy.testing import fixtures, AssertsExecutionResults, AssertsCompiledSQL +from sqlalchemy import testing +from sqlalchemy.testing import config, engines from sqlalchemy.engine import ddl -from test.lib.testing import eq_ -from test.lib.assertsql import AllOf, RegexSQL, ExactSQL, CompiledSQL +from sqlalchemy.testing import eq_ +from sqlalchemy.testing.assertsql import AllOf, RegexSQL, ExactSQL, CompiledSQL from sqlalchemy.dialects.postgresql import base as postgresql class ConstraintTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL): diff --git a/test/sql/test_cte.py b/test/sql/test_cte.py index 6360e278c..28756873f 100644 --- a/test/sql/test_cte.py +++ b/test/sql/test_cte.py @@ -1,5 +1,5 @@ -from test.lib import fixtures -from test.lib.testing import AssertsCompiledSQL, assert_raises_message +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import AssertsCompiledSQL, assert_raises_message from sqlalchemy.sql import table, column, select, func, literal from sqlalchemy.dialects import mssql from sqlalchemy.engine import default diff --git a/test/sql/test_defaults.py b/test/sql/test_defaults.py index 55aa86633..1c31314d8 100644 --- a/test/sql/test_defaults.py +++ b/test/sql/test_defaults.py @@ -1,16 +1,17 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message import datetime from sqlalchemy.schema import CreateSequence, DropSequence from sqlalchemy.sql import select, text, literal_column import sqlalchemy as sa -from test.lib import testing, engines +from sqlalchemy import testing +from sqlalchemy.testing import engines from sqlalchemy import MetaData, Integer, String, ForeignKey, Boolean, exc,\ Sequence, func, literal, Unicode from sqlalchemy.types import TypeDecorator, TypeEngine -from test.lib.schema import Table, Column -from test.lib.testing import eq_ +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy.testing import eq_ from sqlalchemy.dialects import sqlite -from test.lib import fixtures +from sqlalchemy.testing import fixtures class DefaultTest(fixtures.TestBase): diff --git a/test/sql/test_functions.py b/test/sql/test_functions.py index 478f41381..ae8e28e24 100644 --- a/test/sql/test_functions.py +++ b/test/sql/test_functions.py @@ -1,15 +1,16 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import datetime from sqlalchemy import * from sqlalchemy.sql import table, column from sqlalchemy import sql, util from sqlalchemy.sql.compiler import BIND_TEMPLATES -from test.lib.engines import all_dialects +from sqlalchemy.testing.engines import all_dialects from sqlalchemy import types as sqltypes from sqlalchemy.sql import functions from sqlalchemy.sql.functions import GenericFunction from sqlalchemy.util.compat import decimal -from test.lib import testing, fixtures, AssertsCompiledSQL, engines +from sqlalchemy import testing +from sqlalchemy.testing import fixtures, AssertsCompiledSQL, engines from sqlalchemy.dialects import sqlite, postgresql, mysql, oracle diff --git a/test/sql/test_generative.py b/test/sql/test_generative.py index b78559960..f1c118e15 100644 --- a/test/sql/test_generative.py +++ b/test/sql/test_generative.py @@ -1,11 +1,14 @@ from sqlalchemy import * from sqlalchemy.sql import table, column, ClauseElement, operators from sqlalchemy.sql.expression import _clone, _from_objects -from test.lib import * -from sqlalchemy.sql.visitors import * +from sqlalchemy.testing import fixtures, AssertsExecutionResults, \ + AssertsCompiledSQL +from sqlalchemy import testing +from sqlalchemy.sql.visitors import ClauseVisitor, CloningVisitor, \ + cloned_traverse, ReplacingCloningVisitor from sqlalchemy import util, exc from sqlalchemy.sql import util as sql_util -from test.lib.testing import eq_, ne_, assert_raises +from sqlalchemy.testing import eq_, ne_, assert_raises class TraversalTest(fixtures.TestBase, AssertsExecutionResults): """test ClauseVisitor's traversal, particularly its diff --git a/test/sql/test_inspect.py b/test/sql/test_inspect.py index 9fcba16bd..8a0251498 100644 --- a/test/sql/test_inspect.py +++ b/test/sql/test_inspect.py @@ -2,8 +2,8 @@ from sqlalchemy import inspect from sqlalchemy import Table, Column, Integer, MetaData -from test.lib import fixtures -from test.lib.testing import is_ +from sqlalchemy.testing import fixtures +from sqlalchemy.testing import is_ class TestCoreInspection(fixtures.TestBase): diff --git a/test/sql/test_labels.py b/test/sql/test_labels.py index c814a0130..d6ebf3f80 100644 --- a/test/sql/test_labels.py +++ b/test/sql/test_labels.py @@ -1,11 +1,12 @@ -from test.lib.testing import assert_raises, assert_raises_message, eq_ -from test.lib.engines import testing_engine -from test.lib import fixtures, AssertsCompiledSQL, testing +from sqlalchemy.testing import assert_raises, assert_raises_message, eq_ +from sqlalchemy.testing.engines import testing_engine +from sqlalchemy.testing import fixtures, AssertsCompiledSQL +from sqlalchemy import testing from sqlalchemy import * from sqlalchemy import exc as exceptions from sqlalchemy.engine import default from sqlalchemy.sql import table, column -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column IDENT_LENGTH = 29 diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index dcf9820cc..ba5c9e987 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -1,19 +1,19 @@ -from test.lib.testing import assert_raises -from test.lib.testing import assert_raises_message -from test.lib.testing import emits_warning +from sqlalchemy.testing import assert_raises +from sqlalchemy.testing import assert_raises_message +from sqlalchemy.testing import emits_warning import pickle from sqlalchemy import Integer, String, UniqueConstraint, \ CheckConstraint, ForeignKey, MetaData, Sequence, \ ForeignKeyConstraint, ColumnDefault, Index, event,\ events, Unicode -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column from sqlalchemy import schema, exc import sqlalchemy as tsa -from test.lib import fixtures -from test.lib import testing -from test.lib.testing import ComparesTables, AssertsCompiledSQL -from test.lib.testing import eq_, is_ +from sqlalchemy.testing import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import ComparesTables, AssertsCompiledSQL +from sqlalchemy.testing import eq_, is_ class MetaDataTest(fixtures.TestBase, ComparesTables): def test_metadata_connect(self): diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py index b104f6097..24b458958 100644 --- a/test/sql/test_operators.py +++ b/test/sql/test_operators.py @@ -1,5 +1,6 @@ -from test.lib import fixtures, testing -from test.lib.testing import assert_raises_message +from sqlalchemy.testing import fixtures +from sqlalchemy import testing +from sqlalchemy.testing import assert_raises_message from sqlalchemy.sql import column, desc, asc, literal, collate from sqlalchemy.sql.expression import BinaryExpression, \ ClauseList, Grouping, \ diff --git a/test/sql/test_query.py b/test/sql/test_query.py index 70e3e97a8..d14cafc86 100644 --- a/test/sql/test_query.py +++ b/test/sql/test_query.py @@ -1,10 +1,12 @@ -from test.lib.testing import eq_, assert_raises_message, assert_raises, is_ +from sqlalchemy.testing import eq_, assert_raises_message, assert_raises, is_ +from sqlalchemy import testing +from sqlalchemy.testing import fixtures, engines +from sqlalchemy import util import datetime from sqlalchemy import * -from sqlalchemy import exc, sql, util +from sqlalchemy import exc, sql from sqlalchemy.engine import default, result as _result -from test.lib import * -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column class QueryTest(fixtures.TestBase): diff --git a/test/sql/test_quote.py b/test/sql/test_quote.py index a714002b1..f736a69ad 100644 --- a/test/sql/test_quote.py +++ b/test/sql/test_quote.py @@ -1,7 +1,8 @@ from sqlalchemy import * from sqlalchemy import sql, schema from sqlalchemy.sql import compiler -from test.lib import * +from sqlalchemy.testing import fixtures, AssertsCompiledSQL +from sqlalchemy import testing class QuoteTest(fixtures.TestBase, AssertsCompiledSQL): __dialect__ = 'default' diff --git a/test/sql/test_returning.py b/test/sql/test_returning.py index c1ee900ef..55f4f7926 100644 --- a/test/sql/test_returning.py +++ b/test/sql/test_returning.py @@ -1,9 +1,9 @@ -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ from sqlalchemy import * -from test.lib import * -from test.lib.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.types import TypeDecorator -from test.lib import fixtures +from sqlalchemy.testing import fixtures, AssertsExecutionResults, engines class ReturningTest(fixtures.TestBase, AssertsExecutionResults): __requires__ = 'returning', diff --git a/test/sql/test_rowcount.py b/test/sql/test_rowcount.py index c14fa22a1..f14f78989 100644 --- a/test/sql/test_rowcount.py +++ b/test/sql/test_rowcount.py @@ -1,5 +1,6 @@ from sqlalchemy import * -from test.lib import * +from sqlalchemy.testing import fixtures, AssertsExecutionResults +from sqlalchemy import testing class FoundRowsTest(fixtures.TestBase, AssertsExecutionResults): diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py index b81bd8e6f..374147a1b 100644 --- a/test/sql/test_selectable.py +++ b/test/sql/test_selectable.py @@ -1,14 +1,16 @@ """Test various algorithmic properties of selectables.""" -from test.lib.testing import eq_, assert_raises, \ +from sqlalchemy.testing import eq_, assert_raises, \ assert_raises_message, is_ from sqlalchemy import * -from test.lib import * +from sqlalchemy.testing import fixtures, AssertsCompiledSQL, \ + AssertsExecutionResults +from sqlalchemy import testing from sqlalchemy.sql import util as sql_util, visitors from sqlalchemy import exc from sqlalchemy.sql import table, column, null from sqlalchemy import util -from test.lib import fixtures +from sqlalchemy.testing import fixtures metadata = MetaData() table1 = Table('table1', metadata, diff --git a/test/sql/test_type_expressions.py b/test/sql/test_type_expressions.py index 320dc5d7c..4bb8a928d 100644 --- a/test/sql/test_type_expressions.py +++ b/test/sql/test_type_expressions.py @@ -1,6 +1,8 @@ from sqlalchemy import Table, Column, String, func, MetaData, select, TypeDecorator, cast -from test.lib import fixtures, AssertsCompiledSQL, testing -from test.lib.testing import eq_ +from sqlalchemy.testing import fixtures, AssertsCompiledSQL +from sqlalchemy import testing +from sqlalchemy.testing import eq_ + class _ExprFixture(object): def _fixture(self): diff --git a/test/sql/test_types.py b/test/sql/test_types.py index ff22ea4de..81b572989 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -1,5 +1,5 @@ # coding: utf-8 -from test.lib.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy.testing import eq_, assert_raises, assert_raises_message import decimal import datetime, os, re from sqlalchemy import * @@ -7,15 +7,17 @@ from sqlalchemy import exc, types, util, schema, dialects for name in dialects.__all__: __import__("sqlalchemy.dialects.%s" % name) from sqlalchemy.sql import operators, column, table -from test.lib.testing import eq_ +from sqlalchemy.testing import eq_ import sqlalchemy.engine.url as url from sqlalchemy.engine import default -from test.lib.schema import Table, Column -from test.lib import * -from test.lib.util import picklers +from sqlalchemy.testing.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing import AssertsCompiledSQL, AssertsExecutionResults, \ + engines, pickleable +from sqlalchemy.testing.util import picklers from sqlalchemy.util.compat import decimal -from test.lib.util import round_decimal -from test.lib import fixtures +from sqlalchemy.testing.util import round_decimal +from sqlalchemy.testing import fixtures class AdaptTest(fixtures.TestBase): def _all_dialect_modules(self): diff --git a/test/sql/test_unicode.py b/test/sql/test_unicode.py index e3fa0a4b3..37e44522e 100644 --- a/test/sql/test_unicode.py +++ b/test/sql/test_unicode.py @@ -2,10 +2,11 @@ """verrrrry basic unicode column name testing""" from sqlalchemy import * -from test.lib import fixtures, engines, testing -from test.lib.engines import utf8_engine +from sqlalchemy.testing import fixtures, engines +from sqlalchemy import testing +from sqlalchemy.testing.engines import utf8_engine from sqlalchemy.sql import column -from test.lib.schema import Table, Column +from sqlalchemy.testing.schema import Table, Column class UnicodeSchemaTest(fixtures.TestBase): __requires__ = ('unicode_ddl',) diff --git a/test/sql/test_update.py b/test/sql/test_update.py index 79079e512..b46489cd2 100644 --- a/test/sql/test_update.py +++ b/test/sql/test_update.py @@ -1,10 +1,11 @@ -from test.lib.testing import eq_, assert_raises_message, assert_raises, AssertsCompiledSQL +from sqlalchemy.testing import eq_, assert_raises_message, assert_raises, AssertsCompiledSQL import datetime from sqlalchemy import * from sqlalchemy import exc, sql, util from sqlalchemy.engine import default, base -from test.lib import * -from test.lib.schema import Table, Column +from sqlalchemy import testing +from sqlalchemy.testing import fixtures +from sqlalchemy.testing.schema import Table, Column from sqlalchemy.dialects import mysql class _UpdateFromTestBase(object): -- cgit v1.2.1