| 1 | import logging | 
|---|
| 2 | import os | 
|---|
| 3 | import re | 
|---|
| 4 | import sys | 
|---|
| 5 | import time | 
|---|
| 6 | import warnings | 
|---|
| 7 | import ConfigParser | 
|---|
| 8 | import StringIO | 
|---|
| 9 | from config import db, db_label, db_url, file_config, base_config, \ | 
|---|
| 10 |                            post_configure, \ | 
|---|
| 11 |                            _list_dbs, _server_side_cursors, _engine_strategy, \ | 
|---|
| 12 |                            _engine_uri, _require, _engine_pool, \ | 
|---|
| 13 |                            _create_testing_engine, _prep_testing_database, \ | 
|---|
| 14 |                            _set_table_options, _reverse_topological, _log | 
|---|
| 15 | from sqlalchemy.test import testing, config, requires | 
|---|
| 16 | from nose.plugins import Plugin | 
|---|
| 17 | from nose.util import tolist | 
|---|
| 18 | import nose.case | 
|---|
| 19 |  | 
|---|
| 20 | log = logging.getLogger('nose.plugins.sqlalchemy') | 
|---|
| 21 |  | 
|---|
| 22 | class NoseSQLAlchemy(Plugin): | 
|---|
| 23 |     """ | 
|---|
| 24 |     Handles the setup and extra properties required for testing SQLAlchemy | 
|---|
| 25 |     """ | 
|---|
| 26 |     enabled = True | 
|---|
| 27 |     name = 'sqlalchemy' | 
|---|
| 28 |     score = 100 | 
|---|
| 29 |  | 
|---|
| 30 |     def options(self, parser, env=os.environ): | 
|---|
| 31 |         Plugin.options(self, parser, env) | 
|---|
| 32 |         opt = parser.add_option | 
|---|
| 33 |         #opt("--verbose", action="store_true", dest="verbose", | 
|---|
| 34 |             #help="enable stdout echoing/printing") | 
|---|
| 35 |         #opt("--quiet", action="store_true", dest="quiet", help="suppress output") | 
|---|
| 36 |         opt("--log-info", action="callback", type="string", callback=_log, | 
|---|
| 37 |             help="turn on info logging for <LOG> (multiple OK)") | 
|---|
| 38 |         opt("--log-debug", action="callback", type="string", callback=_log, | 
|---|
| 39 |             help="turn on debug logging for <LOG> (multiple OK)") | 
|---|
| 40 |         opt("--require", action="append", dest="require", default=[], | 
|---|
| 41 |             help="require a particular driver or module version (multiple OK)") | 
|---|
| 42 |         opt("--db", action="store", dest="db", default="sqlite", | 
|---|
| 43 |             help="Use prefab database uri") | 
|---|
| 44 |         opt('--dbs', action='callback', callback=_list_dbs, | 
|---|
| 45 |             help="List available prefab dbs") | 
|---|
| 46 |         opt("--dburi", action="store", dest="dburi", | 
|---|
| 47 |             help="Database uri (overrides --db)") | 
|---|
| 48 |         opt("--dropfirst", action="store_true", dest="dropfirst", | 
|---|
| 49 |             help="Drop all tables in the target database first (use with caution on Oracle, MS-SQL)") | 
|---|
| 50 |         opt("--mockpool", action="store_true", dest="mockpool", | 
|---|
| 51 |             help="Use mock pool (asserts only one connection used)") | 
|---|
| 52 |         opt("--enginestrategy", action="callback", type="string", | 
|---|
| 53 |             callback=_engine_strategy, | 
|---|
| 54 |             help="Engine strategy (plain or threadlocal, defaults to plain)") | 
|---|
| 55 |         opt("--reversetop", action="store_true", dest="reversetop", default=False, | 
|---|
| 56 |             help="Reverse the collection ordering for topological sorts (helps " | 
|---|
| 57 |                   "reveal dependency issues)") | 
|---|
| 58 |         opt("--unhashable", action="store_true", dest="unhashable", default=False, | 
|---|
| 59 |             help="Disallow SQLAlchemy from performing a hash() on mapped test objects.") | 
|---|
| 60 |         opt("--noncomparable", action="store_true", dest="noncomparable", default=False, | 
|---|
| 61 |             help="Disallow SQLAlchemy from performing == on mapped test objects.") | 
|---|
| 62 |         opt("--truthless", action="store_true", dest="truthless", default=False, | 
|---|
| 63 |             help="Disallow SQLAlchemy from truth-evaluating mapped test objects.") | 
|---|
| 64 |         opt("--serverside", action="callback", callback=_server_side_cursors, | 
|---|
| 65 |             help="Turn on server side cursors for PG") | 
|---|
| 66 |         opt("--mysql-engine", action="store", dest="mysql_engine", default=None, | 
|---|
| 67 |             help="Use the specified MySQL storage engine for all tables, default is " | 
|---|
| 68 |                  "a db-default/InnoDB combo.") | 
|---|
| 69 |         opt("--table-option", action="append", dest="tableopts", default=[], | 
|---|
| 70 |             help="Add a dialect-specific table option, key=value") | 
|---|
| 71 |  | 
|---|
| 72 |         global file_config | 
|---|
| 73 |         file_config = ConfigParser.ConfigParser() | 
|---|
| 74 |         file_config.readfp(StringIO.StringIO(base_config)) | 
|---|
| 75 |         file_config.read(['test.cfg', os.path.expanduser('~/.satest.cfg')]) | 
|---|
| 76 |         config.file_config = file_config | 
|---|
| 77 |          | 
|---|
| 78 |     def configure(self, options, conf): | 
|---|
| 79 |         Plugin.configure(self, options, conf) | 
|---|
| 80 |  | 
|---|
| 81 |         import testing, requires | 
|---|
| 82 |         testing.db = db | 
|---|
| 83 |         testing.requires = requires | 
|---|
| 84 |  | 
|---|
| 85 |         # Lazy setup of other options (post coverage) | 
|---|
| 86 |         for fn in post_configure: | 
|---|
| 87 |             fn(options, file_config) | 
|---|
| 88 |          | 
|---|
| 89 |     def describeTest(self, test): | 
|---|
| 90 |         return "" | 
|---|
| 91 |          | 
|---|
| 92 |     def wantClass(self, cls): | 
|---|
| 93 |         """Return true if you want the main test selector to collect | 
|---|
| 94 |         tests from this class, false if you don't, and None if you don't | 
|---|
| 95 |         care. | 
|---|
| 96 |  | 
|---|
| 97 |         :Parameters: | 
|---|
| 98 |            cls : class | 
|---|
| 99 |              The class being examined by the selector | 
|---|
| 100 |  | 
|---|
| 101 |         """ | 
|---|
| 102 |  | 
|---|
| 103 |         if not issubclass(cls, testing.TestBase): | 
|---|
| 104 |             return False | 
|---|
| 105 |         else: | 
|---|
| 106 |             if (hasattr(cls, '__whitelist__') and | 
|---|
| 107 |                 testing.db.name in cls.__whitelist__): | 
|---|
| 108 |                 return True | 
|---|
| 109 |             else: | 
|---|
| 110 |                 return not self.__should_skip_for(cls) | 
|---|
| 111 |      | 
|---|
| 112 |     def __should_skip_for(self, cls): | 
|---|
| 113 |         if hasattr(cls, '__requires__'): | 
|---|
| 114 |             def test_suite(): return 'ok' | 
|---|
| 115 |             for requirement in cls.__requires__: | 
|---|
| 116 |                 check = getattr(requires, requirement) | 
|---|
| 117 |                 if check(test_suite)() != 'ok': | 
|---|
| 118 |                     # The requirement will perform messaging. | 
|---|
| 119 |                     return True | 
|---|
| 120 |         if (hasattr(cls, '__unsupported_on__') and | 
|---|
| 121 |             testing.db.name in cls.__unsupported_on__): | 
|---|
| 122 |             print "'%s' unsupported on DB implementation '%s'" % ( | 
|---|
| 123 |                 cls.__class__.__name__, testing.db.name) | 
|---|
| 124 |             return True | 
|---|
| 125 |         if (getattr(cls, '__only_on__', None) not in (None, testing.db.name)): | 
|---|
| 126 |             print "'%s' unsupported on DB implementation '%s'" % ( | 
|---|
| 127 |                 cls.__class__.__name__, testing.db.name) | 
|---|
| 128 |             return True | 
|---|
| 129 |         if (getattr(cls, '__skip_if__', False)): | 
|---|
| 130 |             for c in getattr(cls, '__skip_if__'): | 
|---|
| 131 |                 if c(): | 
|---|
| 132 |                     print "'%s' skipped by %s" % ( | 
|---|
| 133 |                         cls.__class__.__name__, c.__name__) | 
|---|
| 134 |                     return True | 
|---|
| 135 |         for rule in getattr(cls, '__excluded_on__', ()): | 
|---|
| 136 |             if testing._is_excluded(*rule): | 
|---|
| 137 |                 print "'%s' unsupported on DB %s version %s" % ( | 
|---|
| 138 |                     cls.__class__.__name__, testing.db.name, | 
|---|
| 139 |                     _server_version()) | 
|---|
| 140 |                 return True | 
|---|
| 141 |         return False | 
|---|
| 142 |  | 
|---|
| 143 |     #def begin(self): | 
|---|
| 144 |         #pass | 
|---|
| 145 |  | 
|---|
| 146 |     def beforeTest(self, test): | 
|---|
| 147 |         testing.resetwarnings() | 
|---|
| 148 |  | 
|---|
| 149 |     def afterTest(self, test): | 
|---|
| 150 |         testing.resetwarnings() | 
|---|
| 151 |          | 
|---|
| 152 |     #def handleError(self, test, err): | 
|---|
| 153 |         #pass | 
|---|
| 154 |  | 
|---|
| 155 |     #def finalize(self, result=None): | 
|---|
| 156 |         #pass | 
|---|