Author: ianb
Date: 2005-02-01 07:59:43 +0000 (Tue, 01 Feb 2005)
New Revision: 565
Added:
trunk/SQLObject/sqlobject/tests/
trunk/SQLObject/sqlobject/tests/__init__.py
trunk/SQLObject/sqlobject/tests/dbtest.py
trunk/SQLObject/sqlobject/tests/test_auto.py
trunk/SQLObject/sqlobject/tests/test_basic.py
trunk/SQLObject/sqlobject/tests/test_blob.py
trunk/SQLObject/sqlobject/tests/test_cache.py
trunk/SQLObject/sqlobject/tests/test_constraints.py
trunk/SQLObject/sqlobject/tests/test_converters.py
trunk/SQLObject/sqlobject/tests/test_datetime.py
trunk/SQLObject/sqlobject/tests/test_delete.py
trunk/SQLObject/sqlobject/tests/test_distinct.py
trunk/SQLObject/sqlobject/tests/test_enum.py
trunk/SQLObject/sqlobject/tests/test_expire.py
trunk/SQLObject/sqlobject/tests/test_indexes.py
trunk/SQLObject/sqlobject/tests/test_inheritance.py
trunk/SQLObject/sqlobject/tests/test_joins.py
trunk/SQLObject/sqlobject/tests/test_lazy.py
trunk/SQLObject/sqlobject/tests/test_picklecol.py
trunk/SQLObject/sqlobject/tests/test_select.py
trunk/SQLObject/sqlobject/tests/test_slice.py
trunk/SQLObject/sqlobject/tests/test_sorting.py
trunk/SQLObject/sqlobject/tests/test_stringid.py
trunk/SQLObject/sqlobject/tests/test_style.py
trunk/SQLObject/sqlobject/tests/test_transactions.py
trunk/SQLObject/sqlobject/tests/test_unicode.py
trunk/SQLObject/sqlobject/tests/test_validation.py
Modified:
trunk/SQLObject/docs/DeveloperGuide.txt
Log:
Refactored tests into py.test framework; included documentation
about how to use the new tests.
Modified: trunk/SQLObject/docs/DeveloperGuide.txt
===================================================================
--- trunk/SQLObject/docs/DeveloperGuide.txt 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/docs/DeveloperGuide.txt 2005-02-01 07:59:43 UTC (rev 565)
@@ -26,16 +26,19 @@
* I don't stress too much on line length. But try to break lines up
by grouping with parenthesis instead of with backslashes (if you
- can).
+ can). Do asserts like::
+ assert some_condition(a, b), (
+ "Some condition failed, %r isn't right!" % a)
+
* But if you are having problems with line length, maybe you should
just break the expression up into multiple statements.
* Blank lines between methods, unless they are very small and closely
bound to each other.
-* Never use the form ``condition and trueValue or falseValue``. Break
- it out and use a variable.
+* *Never* use the form ``condition and trueValue or falseValue``.
+ Break it out and use a variable.
* Careful of namespace pollution. SQLObject does allow for ``from
sqlobject import *`` so names should be fairly distinct, or they
@@ -122,27 +125,42 @@
Tests are important. Tests keep everything from falling apart. All
new additions should have tests.
-Right now all the tests are in one big file ``tests.py``. Which is
-unfortunate, but that's the way it is. They may seem complex, but
-they aren't so bad really. They all subclass from ``SQLObjectTest``.
+Testing uses `py.test`__, an alternative to ``unittest``. It is
+available via subversion at http://codespeak.net/svn/py/dist. Read
+its `getting started`_ document for more.
-The ``classes`` attribute is special in a test class. This is a list
-of `SQLObject` subclasses that this test uses. `SQLObjectTest` will
-create the tables before the tests are run, and destroy them after.
+.. __: http://codespeak.net/py/current/doc/test.html
+.. _getting started: http://codespeak.net/py/current/doc/getting_started.html
-You may also define an ``.inserts()`` method. This method sets up the
-basic data. When doing verbose input (``-vv``) you won't see these
-inserts, since they may be overwhelming. Use the command-line options
-``--inserts`` to show them (as well as the create statement.
+To actually run the test, you have to give it a database to connect
+to. You do this with the ``TESTDB`` environmental variable (right now
+py.test doesn't have a better way to add custom options). You can
+give the complete URI to access your test database, or you can give it
+a shortcut like ``mysql`` (these shortcuts are defined in the top of
+``tests/dbtest.py``.
-When running tests, use ``-ddbname`` to test with ``dbname`` (e.g.,
-``-dmysql``, ``-dpostgres``, etc), or ``-dall`` to use Postgres,
-MySQL, Firebird, and SQLite (all the core supported databases;
-everything I have installed on my computer). Please try to test as
-many databases as you can. At least SQLite and one other should be
-easy, though if you can test both Postgres and MySQL that would be
-much better.
+All the tests are modules in ``sqlobject/tests``. Each module tests
+one kind of feature, more or less. If you are testing a module, call
+the test module ``tests/test_modulename.py`` -- only modules that
+start with ``test_`` will be picked up by py.test.
+The "framework" for testing is in ``tests/dbtest``. There's a couple
+important functions:
+
+``setupClass(soClass)`` creates the tables for the class. It tries to
+avoid recreating tables if not necessary.
+
+``supports(featureName)`` checks if the database backend supports the
+named feature. What backends support what is defined at the top of
+``dbtest``.
+
+If you ``import *`` you'll also get py.test's version of raises_, an
+``inserts`` function that can create instances for you, and a couple
+miscellaneous functions.
+
+.. _raises: http://codespeak.net/py/current/doc/test.html#id4
+
+
If you submit a patch or implement a feature without a test, I'll be
forced to write the test. That's no fun for me, to just be writing
tests. So please, write tests; everything at least needs to be
Added: trunk/SQLObject/sqlobject/tests/__init__.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/__init__.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/__init__.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1 @@
+#
Added: trunk/SQLObject/sqlobject/tests/dbtest.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/dbtest.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/dbtest.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,218 @@
+"""
+The framework for making database tests.
+"""
+
+import os
+import re
+import sqlobject
+from py.test import raises
+
+connectionShortcuts = {
+ 'mysql': 'mysql://test@localhost/test',
+ 'dbm': 'dbm:///data',
+ 'postgres': 'postgres:///test',
+ 'postgresql': 'postgres:///test',
+ 'pygresql': 'pygresql://localhost/test',
+ 'sqlite': 'sqlite:///%s/data/sqlite.data' % os.getcwd(),
+ 'sybase': 'sybase://test:test123@sybase/test?autoCommit=0',
+ 'firebird': 'firebird://sysdba:masterkey@localhost/var/lib/firebird/data/test.gdb',
+ }
+
+"""
+supportsMatrix defines what database backends support what features.
+Each feature has a name, if you see a key like '+featureName' then
+only the databases listed support the feature. Conversely,
+'-featureName' means all databases *except* the ones listed support
+the feature. The databases are given by their SQLObject string name,
+separated by spaces.
+
+The function supports(featureName) returns True or False based on this,
+and you can use it like::
+
+ def test_featureX():
+ if not supports('featureX'):
+ return
+"""
+supportsMatrix = {
+ '+restrictedEnum': 'postgres',
+ '-transactions': 'mysql',
+ '-dropTableCascade': 'sybase',
+ '-dynamicColumn': 'sqlite sybase',
+ '-fromDatabase': 'sqlite sybase firebird',
+ '-expressionIndex': 'mysql sqlite firebird',
+ }
+
+
+def setupClass(soClass):
+ """
+ Makes sure the class has a corresponding and correct table.
+ This won't recreate the table if it already exists. It will
+ check that the table is properly defined (in case you change
+ your table definition).
+ """
+ connection = getConnection()
+ soClass._connection = connection
+ installOrClear(soClass)
+ return soClass
+
+installedDBFilename = os.path.join(os.getcwd(), 'dbs_data.tmp')
+
+installedDBTracker = sqlobject.connectionForURI(
+ 'sqlite:///' + installedDBFilename)
+
+def getConnection():
+ name = os.environ.get('TESTDB')
+ assert name, 'You must set $TESTDB to do database operations'
+ if connectionShortcuts.has_key(name):
+ name = connectionShortcuts[name]
+ return sqlobject.connectionForURI(name)
+
+connection = getConnection()
+
+class InstalledTestDatabase(sqlobject.SQLObject):
+ """
+ This table is set up in SQLite (always, regardless of $TESTDB) and
+ tracks what tables have been set up in the 'real' database. This
+ way we don't keep recreating the tables over and over when there
+ are multiple tests that use a table.
+ """
+
+ _connection = installedDBTracker
+ tableName = sqlobject.StringCol(notNull=True)
+ createSQL = sqlobject.StringCol(notNull=True)
+ connectionURI = sqlobject.StringCol(notNull=True)
+
+ def installOrClear(cls, soClass):
+ cls.setup()
+ table = soClass._table
+ if not soClass._connection.tableExists(table):
+ cls.install(soClass)
+ items = list(cls.selectBy(
+ tableName=table,
+ connectionURI=soClass._connection.uri()))
+ if items:
+ instance = items[0]
+ sql = instance.createSQL
+ else:
+ sql = None
+ newSQL = soClass.createTableSQL()
+ if sql != newSQL:
+ if sql is not None:
+ instance.destroySelf()
+ cls.drop(soClass)
+ cls.install(soClass)
+ else:
+ cls.clear(soClass)
+ installOrClear = classmethod(installOrClear)
+
+ def install(cls, soClass):
+ """
+ Creates the given table in its database.
+ """
+ sql = getattr(soClass, soClass._connection.dbName + 'Create',
+ None)
+ if sql:
+ soClass._connection.query(sql)
+ else:
+ sql = soClass.createTableSQL()
+ soClass.createTable()
+ cls(tableName=soClass._table,
+ createSQL=sql,
+ connectionURI=soClass._connection.uri())
+ install = classmethod(install)
+
+ def drop(cls, soClass):
+ """
+ Drops a the given table from its database
+ """
+ sql = getattr(soClass, soClass._connection.dbName + 'Drop', None)
+ if sql:
+ soClass._connection.query(sql)
+ else:
+ soClass.dropTable()
+ drop = classmethod(drop)
+
+ def clear(cls, soClass):
+ """
+ Removes all the rows from a table.
+ """
+ soClass.clearTable()
+ clear = classmethod(clear)
+
+ def setup(cls):
+ """
+ This sets up *this* table.
+ """
+ if not cls._connection.tableExists(cls._table):
+ cls.createTable()
+ setup = classmethod(setup)
+
+installOrClear = InstalledTestDatabase.installOrClear
+
+class Dummy(object):
+
+ """
+ Used for creating fake objects; a really poor 'mock object'.
+ """
+
+ def __init__(self, **kw):
+ for name, value in kw.items():
+ setattr(self, name, value)
+
+def d(**kw):
+ """
+ Because dict(**kw) doesn't work in Python 2.2, this is a
+ replacement.
+ """
+ return kw
+
+def inserts(cls, data, schema=None):
+ """
+ Creates a bunch of rows. You can use it like::
+
+ inserts(Person, [{'fname': 'blah', 'lname': 'doe'}, ...])
+
+ Or::
+
+ inserts(Person, [('blah', 'doe')], schema=
+ ['fname', 'lname'])
+
+ If you give a single string for the `schema` then it'll split
+ that string to get the list of column names.
+ """
+ if schema:
+ if isinstance(schema, str):
+ schema = schema.split()
+ keywordData = []
+ for item in data:
+ itemDict = {}
+ for name, value in zip(schema, item):
+ itemDict[name] = value
+ keywordData.append(itemDict)
+ data = keywordData
+ results = []
+ for args in data:
+ results.append(cls(**args))
+ return results
+
+def supports(feature):
+ dbName = connection.dbName
+ support = supportsMatrix.get('+' + feature, None)
+ notSupport = supportsMatrix.get('-' + feature, None)
+ if support is not None and dbName in support.split():
+ return True
+ else:
+ return False
+ if notSupport is not None and dbName in notSupport.split():
+ return False
+ else:
+ return True
+ assert notSupport is not None or support is not None, (
+ "The supportMatrix does not list this feature: %r"
+ % feature)
+
+# To avoid name clashes:
+_inserts = inserts
+
+__all__ = ['getConnection', 'setupClass', 'Dummy', 'raises',
+ 'd', 'inserts', 'supports']
Added: trunk/SQLObject/sqlobject/tests/test_auto.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_auto.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_auto.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,136 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+from sqlobject import classregistry
+
+########################################
+## Dynamic column tests
+########################################
+
+class Person(SQLObject):
+
+ _columns = [StringCol('name', length=100, dbName='name_col')]
+ _defaultOrder = 'name'
+
+class Phone(SQLObject):
+
+ _columns = [StringCol('phone', length=12)]
+ _defaultOrder = 'phone'
+
+class TestPeople:
+
+ def setup_method(self, meth):
+ setupClass(Person)
+ setupClass(Phone)
+ for n in ['jane', 'tim', 'bob', 'jake']:
+ Person(name=n)
+ for p in ['555-555-5555', '555-394-2930',
+ '444-382-4854']:
+ Phone(phone=p)
+
+ def test_defaultOrder(self):
+ assert (list(Person.select('all')) ==
+ list(Person.select('all', orderBy=Person._defaultOrder)))
+
+ def test_dynamicColumn(self):
+ if not supports('dynamicColumn'):
+ return
+ nickname = StringCol('nickname', length=10)
+ Person.addColumn(nickname, changeSchema=True)
+ n = Person(name='robert', nickname='bob')
+ assert ([p.name for p in Person.select('all')]
+ == ['bob', 'jake', 'jane', 'robert', 'tim'])
+ Person.delColumn(nickname, changeSchema=True)
+
+ def test_dynamicJoin(self):
+ if not supports('dynamicColumn'):
+ return
+ col = KeyCol('personID', foreignKey='Person')
+ Phone.addColumn(col, changeSchema=True)
+ join = MultipleJoin('Phone')
+ Person.addJoin(join)
+ for phone in Phone.select('all'):
+ if phone.phone.startswith('555'):
+ phone.person = Person.selectBy(name='tim')[0]
+ else:
+ phone.person = Person.selectBy(name='bob')[0]
+ l = [p.phone for p in Person.selectBy(name='tim')[0].phones]
+ l.sort()
+ assert l == ['555-394-2930', '555-555-5555']
+ Phone.delColumn(col, changeSchema=True)
+ Person.delJoin(join)
+
+########################################
+## Auto class generation
+########################################
+
+class TestAuto:
+
+ mysqlCreate = """
+ CREATE TABLE IF NOT EXISTS auto_test (
+ auto_id INT AUTO_INCREMENT PRIMARY KEY,
+ first_name VARCHAR(100),
+ last_name VARCHAR(200) NOT NULL,
+ age INT DEFAULT NULL,
+ created DATETIME NOT NULL,
+ happy char(1) DEFAULT 'Y' NOT NULL,
+ wannahavefun TINYINT DEFAULT 0 NOT NULL
+ )
+ """
+
+ postgresCreate = """
+ CREATE TABLE auto_test (
+ auto_id SERIAL PRIMARY KEY,
+ first_name VARCHAR(100),
+ last_name VARCHAR(200) NOT NULL,
+ age INT DEFAULT 0,
+ created VARCHAR(40) NOT NULL,
+ happy char(1) DEFAULT 'Y' NOT NULL,
+ wannahavefun BOOL DEFAULT FALSE NOT NULL
+ )
+ """
+
+ sybaseCreate = """
+ CREATE TABLE auto_test (
+ auto_id integer,
+ first_name VARCHAR(100),
+ last_name VARCHAR(200) NOT NULL,
+ age INT DEFAULT 0,
+ created VARCHAR(40) NOT NULL,
+ happy char(1) DEFAULT 'Y' NOT NULL
+ )
+ """
+
+ mysqlDrop = """
+ DROP TABLE IF EXISTS auto_test
+ """
+
+ postgresDrop = """
+ DROP TABLE auto_test
+ """
+
+ sybaseDrop = """
+ DROP TABLE auto_test
+ """
+
+ _table = 'auto_test'
+
+ def test_classCreate(self):
+ if not supports('fromDatabase'):
+ return
+ class AutoTest(SQLObject):
+ _fromDatabase = True
+ _idName = 'auto_id'
+ _connection = connection()
+ john = AutoTest(firstName='john',
+ lastName='doe',
+ age=10,
+ created=now(),
+ wannahavefun=False)
+ jane = AutoTest(firstName='jane',
+ lastName='doe',
+ happy='N',
+ created=now(),
+ wannahavefun=True)
+ assert not john.wannahavefun
+ assert jane.wannahavefun
+ del classregistry.registry(AutoTest._registry).classes['AutoTest']
Added: trunk/SQLObject/sqlobject/tests/test_basic.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_basic.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_basic.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,230 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+class TestSO1(SQLObject):
+
+ name = StringCol(length=50, dbName='name_col')
+ _cacheValues = False
+ _columns = [
+ StringCol('passwd', length=10),
+ ]
+
+ def _set_passwd(self, passwd):
+ self._SO_set_passwd(passwd.encode('rot13'))
+
+def setupGetters(cls):
+ setupClass(cls)
+ inserts(cls, [('bob', 'god'), ('sally', 'sordid'),
+ ('dave', 'dremel'), ('fred', 'forgo')],
+ 'name passwd')
+
+def test_case1():
+ setupGetters(TestSO1)
+ bob = TestSO1.selectBy(name='bob')[0]
+ assert bob.name == 'bob'
+ assert bob.passwd == 'god'.encode('rot13')
+
+def test_newline():
+ setupGetters(TestSO1)
+ bob = TestSO1.selectBy(name='bob')[0]
+ testString = 'hey\nyou\\can\'t you see me?\t'
+ bob.name = testString
+ bob.expire()
+ assert bob.name == testString
+
+def test_count():
+ setupGetters(TestSO1)
+ assert TestSO1.selectBy(name='bob').count() == 1
+ assert TestSO1.select(TestSO1.q.name == 'bob').count() == 1
+ assert TestSO1.select().count() == len(list(TestSO1.select()))
+
+def test_getset():
+ setupGetters(TestSO1)
+ bob = TestSO1.selectBy(name='bob')[0]
+ assert bob.name == 'bob'
+ bob.name = 'joe'
+ assert bob.name == 'joe'
+
+class TestSO2(SQLObject):
+ name = StringCol(length=50, dbName='name_col')
+ passwd = StringCol(length=10)
+
+ def _set_passwd(self, passwd):
+ self._SO_set_passwd(passwd.encode('rot13'))
+
+def test_case2():
+ setupGetters(TestSO2)
+ bob = TestSO2.selectBy(name='bob')[0]
+ assert bob.name == 'bob'
+ assert bob.passwd == 'god'.encode('rot13')
+
+class Student(SQLObject):
+ is_smart = BoolCol()
+
+def test_boolCol():
+ setupClass(Student)
+ student = Student(is_smart=False)
+ assert student.is_smart == False
+ student2 = Student(is_smart='false')
+ assert student2.is_smart == True
+
+class TestSO3(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO4', default=None)
+ other2 = KeyCol(foreignKey='TestSO4', default=None)
+
+class TestSO4(SQLObject):
+ me = StringCol(length=10)
+
+def test_foreignKey():
+ setupClass(TestSO3)
+ setupClass(TestSO4)
+ tc3 = TestSO3(name='a')
+ assert tc3.other is None
+ assert tc3.other2 is None
+ assert tc3.otherID is None
+ assert tc3.other2ID is None
+ tc4a = TestSO4(me='1')
+ tc3.other = tc4a
+ assert tc3.other == tc4a
+ assert tc3.otherID == tc4a.id
+ tc4b = TestSO4(me='2')
+ tc3.other = tc4b.id
+ assert tc3.other == tc4b
+ assert tc3.otherID == tc4b.id
+ tc4c = TestSO4(me='3')
+ tc3.other2 = tc4c
+ assert tc3.other2 == tc4c
+ assert tc3.other2ID == tc4c.id
+ tc4d = TestSO4(me='4')
+ tc3.other2 = tc4d.id
+ assert tc3.other2 == tc4d
+ assert tc3.other2ID == tc4d.id
+ tcc = TestSO3(name='b', other=tc4a)
+ assert tcc.other == tc4a
+ tcc2 = TestSO3(name='c', other=tc4a.id)
+ assert tcc2.other == tc4a
+
+class TestSO5(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO6', default=None, cascade=True)
+ another = ForeignKey('TestSO7', default=None, cascade=True)
+
+class TestSO6(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO7', default=None, cascade=True)
+
+class TestSO7(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+
+def test_foreignKeyDestroySelfCascade():
+ setupClass(TestSO7)
+ setupClass(TestSO6)
+ setupClass(TestSO5)
+
+ tc5 = TestSO5(name='a')
+ tc6a = TestSO6(name='1')
+ tc5.other = tc6a
+ tc7a = TestSO7(name='2')
+ tc6a.other = tc7a
+ tc5.another = tc7a
+ assert tc5.other == tc6a
+ assert tc5.otherID == tc6a.id
+ assert tc6a.other == tc7a
+ assert tc6a.otherID == tc7a.id
+ assert tc5.other.other == tc7a
+ assert tc5.other.otherID == tc7a.id
+ assert tc5.another == tc7a
+ assert tc5.anotherID == tc7a.id
+ assert tc5.other.other == tc5.another
+ assert TestSO5.select().count() == 1
+ assert TestSO6.select().count() == 1
+ assert TestSO7.select().count() == 1
+ tc6b = TestSO6(name='3')
+ tc6c = TestSO6(name='4')
+ tc7b = TestSO7(name='5')
+ tc6b.other = tc7b
+ tc6c.other = tc7b
+ assert TestSO5.select().count() == 1
+ assert TestSO6.select().count() == 3
+ assert TestSO7.select().count() == 2
+ tc6b.destroySelf()
+ assert TestSO5.select().count() == 1
+ assert TestSO6.select().count() == 2
+ assert TestSO7.select().count() == 2
+ tc7b.destroySelf()
+ assert TestSO5.select().count() == 1
+ assert TestSO6.select().count() == 1
+ assert TestSO7.select().count() == 1
+ tc7a.destroySelf()
+ assert TestSO5.select().count() == 0
+ assert TestSO6.select().count() == 0
+ assert TestSO7.select().count() == 0
+
+def testForeignKeyDropTableCascade():
+ if not supports('dropTableCascade'):
+ return
+ setupClass(TestSO7)
+ setupClass(TestSO6)
+ setupClass(TestSO5)
+
+ tc5a = TestSO5(name='a')
+ tc6a = TestSO6(name='1')
+ tc5a.other = tc6a
+ tc7a = TestSO7(name='2')
+ tc6a.other = tc7a
+ tc5a.another = tc7a
+ tc5b = TestSO5(name='b')
+ tc5c = TestSO5(name='c')
+ tc6b = TestSO6(name='3')
+ tc5c.other = tc6b
+ assert TestSO5.select().count() == 3
+ assert TestSO6.select().count() == 2
+ assert TestSO7.select().count() == 1
+ TestSO7.dropTable(cascade=True)
+ assert TestSO5.select().count() == 3
+ assert TestSO6.select().count() == 2
+ tc6a.destroySelf()
+ assert TestSO5.select().count() == 2
+ assert TestSO6.select().count() == 1
+ tc6b.destroySelf()
+ assert TestSO5.select().count() == 1
+ assert TestSO6.select().count() == 0
+ assert iter(TestSO5.select()).next() == tc5b
+ tc6c = TestSO6(name='3')
+ tc5b.other = tc6c
+ assert TestSO5.select().count() == 1
+ assert TestSO6.select().count() == 1
+ tc6c.destroySelf()
+ assert TestSO5.select().count() == 0
+ assert TestSO6.select().count() == 0
+
+class TestSO8(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+ other = ForeignKey('TestSO9', default=None, cascade=False)
+
+class TestSO9(SQLObject):
+ name = StringCol(length=10, dbName='name_col')
+
+def testForeignKeyDestroySelfRestrict():
+ setupClass(TestSO9)
+ setupClass(TestSO8)
+
+ tc8a = TestSO8(name='a')
+ tc9a = TestSO9(name='1')
+ tc8a.other = tc9a
+ tc8b = TestSO8(name='b')
+ tc9b = TestSO9(name='2')
+ assert tc8a.other == tc9a
+ assert tc8a.otherID == tc9a.id
+ assert TestSO8.select().count() == 2
+ assert TestSO9.select().count() == 2
+ raises(Exception, tc9a.destroySelf)
+ tc9b.destroySelf()
+ assert TestSO8.select().count() == 2
+ assert TestSO9.select().count() == 1
+ tc8a.destroySelf()
+ tc8b.destroySelf()
+ tc9a.destroySelf()
+ assert TestSO8.select().count() == 0
+ assert TestSO9.select().count() == 0
Added: trunk/SQLObject/sqlobject/tests/test_blob.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_blob.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_blob.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,22 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## BLOB columns
+########################################
+
+class ImageData(SQLObject):
+ image = BLOBCol(default='emptydata', length=65535)
+
+def test_BLOBCol():
+ setupClass(ImageData)
+ data = ''.join([chr(x) for x in range(256)])
+
+ prof = ImageData()
+ prof.image = data
+ iid = prof.id
+
+ ImageData._connection.cache.clear()
+
+ prof2 = ImageData.get(iid)
+ assert prof2.image == data
Copied: trunk/SQLObject/sqlobject/tests/test_cache.py (from rev 560, trunk/SQLObject/tests/test_cache.py)
===================================================================
--- trunk/SQLObject/tests/test_cache.py 2005-01-31 22:57:35 UTC (rev 560)
+++ trunk/SQLObject/sqlobject/tests/test_cache.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,21 @@
+from sqlobject.cache import CacheSet
+
+class Something(object):
+ pass
+
+def test_purge1():
+ x = CacheSet()
+ y = Something()
+ obj = x.get(1, y.__class__)
+ assert obj is None
+ x.put(1, y.__class__, y)
+ x.finishPut(y.__class__)
+ j = x.get(1, y.__class__)
+ assert j == y
+ x.expire(1, y.__class__)
+ j = x.get(1, y.__class__)
+ assert j == None
+ x.finishPut(y.__class__)
+ j = x.get(1, y.__class__)
+ assert j == None
+ x.finishPut(y.__class__)
Added: trunk/SQLObject/sqlobject/tests/test_constraints.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_constraints.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_constraints.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,34 @@
+from sqlobject.constraints import *
+from sqlobject.tests.dbtest import *
+
+def test_constraints():
+ obj = 'Test object'
+ col = Dummy(name='col')
+ isString(obj, col, 'blah')
+ raises(BadValue, isString, obj, col, 1)
+ # @@: Should this really be an error?
+ raises(BadValue, isString, obj, col, u'test!')
+ #isString(obj, col, u'test!')
+
+ raises(BadValue, notNull, obj, col, None)
+ raises(BadValue, isInt, obj, col, 1.1)
+ isInt(obj, col, 1)
+ isInt(obj, col, 1L)
+ isFloat(obj, col, 1)
+ isFloat(obj, col, 1L)
+ isFloat(obj, col, 1.2)
+ raises(BadValue, isFloat, obj, col, '1.0')
+
+ # @@: Should test isBool, but I don't think isBool is right
+
+ lst = InList(('a', 'b', 'c'))
+ lst(obj, col, 'a')
+ raises(BadValue, lst, obj, col, ('a', 'b', 'c'))
+ raises(BadValue, lst, obj, col, 'A')
+
+ maxlen = MaxLength(2)
+ raises(BadValue, maxlen, obj, col, '123')
+ maxlen(obj, col, '12')
+ maxlen(obj, col, (1,))
+ raises(BadValue, maxlen, obj, col, 1)
+
Copied: trunk/SQLObject/sqlobject/tests/test_converters.py (from rev 560, trunk/SQLObject/tests/test_converters.py)
===================================================================
--- trunk/SQLObject/tests/test_converters.py 2005-01-31 22:57:35 UTC (rev 560)
+++ trunk/SQLObject/sqlobject/tests/test_converters.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,139 @@
+from sqlobject.sqlbuilder import sqlrepr, TRUE, FALSE
+from sqlobject.sqlbuilder import SQLExpression, SQLObjectField, \
+ Select, Insert, Update, Delete, Replace, \
+ SQLTrueClauseClass, SQLConstant, SQLPrefix, SQLCall, SQLOp
+from sqlobject.converters import registerConverter
+
+class TestClass:
+
+ def __repr__(self):
+ return '<TestClass>'
+
+def TestClassConverter(value, db):
+ return repr(value)
+
+registerConverter(TestClass, TestClassConverter)
+
+class NewTestClass:
+
+ __metaclass__ = type
+
+ def __repr__(self):
+ return '<NewTestClass>'
+
+def NewTestClassConverter(value, db):
+ return repr(value)
+
+registerConverter(NewTestClass, NewTestClassConverter)
+
+def _sqlrepr(self, db):
+ return '<%s>' % self.__class__.__name__
+
+SQLExpression.__sqlrepr__ = _sqlrepr
+
+############################################################
+## Tests
+############################################################
+
+def test_simple_string():
+ assert sqlrepr('A String', 'firebird') == "'A String'"
+
+def test_string_newline():
+ assert sqlrepr('A String\nAnother', 'postgres') == "'A String\\nAnother'"
+ assert sqlrepr('A String\nAnother', 'sqlite') == "'A String\nAnother'"
+
+def test_string_tab():
+ assert sqlrepr('A String\tAnother', 'postgres') == "'A String\\tAnother'"
+
+def test_string_r():
+ assert sqlrepr('A String\rAnother', 'postgres') == "'A String\\rAnother'"
+
+def test_string_b():
+ assert sqlrepr('A String\bAnother', 'postgres') == "'A String\\bAnother'"
+
+def test_string_000():
+ assert sqlrepr('A String\000Another', 'postgres') == "'A String\\0Another'"
+
+def test_string_():
+ assert sqlrepr('A String\'Another', 'postgres') == "'A String\\'Another'"
+ assert sqlrepr('A String\'Another', 'firebird') == "'A String''Another'"
+
+def test_simple_unicode():
+ assert sqlrepr(u'A String', 'postgres') == "'A String'"
+
+def test_integer():
+ assert sqlrepr(10) == "10"
+
+def test_float():
+ assert sqlrepr(10.01) == "10.01"
+
+def test_none():
+ assert sqlrepr(None) == "NULL"
+
+def test_list():
+ assert sqlrepr(['one','two','three'], 'postgres') == "('one', 'two', 'three')"
+
+def test_tuple():
+ assert sqlrepr(('one','two','three'), 'postgres') == "('one', 'two', 'three')"
+
+def test_bool():
+ assert sqlrepr(TRUE, 'postgres') == "'t'"
+ assert sqlrepr(FALSE, 'postgres') == "'f'"
+ assert sqlrepr(TRUE, 'mysql') == "1"
+ assert sqlrepr(FALSE, 'mysql') == "0"
+
+def test_instance():
+ instance = TestClass()
+ assert sqlrepr(instance) == repr(instance)
+
+def test_newstyle():
+ instance = NewTestClass()
+ assert sqlrepr(instance) == repr(instance)
+
+def test_sqlexpr():
+ instance = SQLExpression()
+ assert sqlrepr(instance) == repr(instance)
+
+def test_sqlobjectfield():
+ instance = SQLObjectField('test', 'test', 'test')
+ assert sqlrepr(instance) == repr(instance)
+
+def test_select():
+ instance = Select('test')
+ assert sqlrepr(instance, 'mysql') == "SELECT 'test'"
+
+def test_insert():
+ instance = Insert('test', [('test',)])
+ assert sqlrepr(instance, 'mysql') == "INSERT INTO test VALUES ('test')"
+
+def test_update():
+ instance = Update('test', {'test':'test'})
+ assert sqlrepr(instance, 'mysql') == "UPDATE test SET test='test'"
+
+def test_delete():
+ instance = Delete('test', None)
+ assert sqlrepr(instance, 'mysql') == "DELETE FROM test"
+
+def test_replace():
+ instance = Replace('test', {'test':'test'})
+ assert sqlrepr(instance, 'mysql') == "REPLACE test SET test='test'"
+
+def test_trueclause():
+ instance = SQLTrueClauseClass()
+ assert sqlrepr(instance) == repr(instance)
+
+def test_op():
+ instance = SQLOp('and', 'this', 'that')
+ assert sqlrepr(instance, 'mysql') == "('this' AND 'that')"
+
+def test_call():
+ instance = SQLCall('test', ('test',))
+ assert sqlrepr(instance, 'mysql') == "'test'('test')"
+
+def test_constant():
+ instance = SQLConstant('test')
+ assert sqlrepr(instance) == repr(instance)
+
+def test_prefix():
+ instance = SQLPrefix('test', 'test')
+ assert sqlrepr(instance, 'mysql') == "test 'test'"
Added: trunk/SQLObject/sqlobject/tests/test_datetime.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_datetime.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_datetime.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,52 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+from sqlobject import col
+if default_datetime_implementation == DATETIME_IMPLEMENTATION:
+ from datetime import datetime
+ now = datetime.now
+elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
+ from mx.DateTime import now
+
+########################################
+## Date/time columns
+########################################
+
+if datetime_available:
+ col.default_datetime_implementation = DATETIME_IMPLEMENTATION
+ from datetime import date, datetime
+
+ class DateTime1(SQLObject):
+ col1 = DateCol()
+ col2 = DateTimeCol()
+
+ def test_dateTime():
+ setupClass(DateTime1)
+ _now = now()
+ dt1 = DateTime1(col1=_now, col2=_now)
+ assert isinstance(dt1.col1, date)
+ assert isinstance(dt1.col2, datetime)
+
+ today_str = _now.strftime("%Y-%m-%d")
+ now_str = _now.strftime("%Y-%m-%d %T")
+ assert str(dt1.col1) == today_str
+ assert str(dt1.col2) == now_str
+
+if mxdatetime_available:
+ col.default_datetime_implementation = MXDATETIME_IMPLEMENTATION
+
+ class DateTime2(SQLObject):
+ col1 = DateCol()
+ col2 = DateTimeCol()
+
+ def test_mxDateTime():
+ setupClass(DateTime2)
+ _now = now()
+ dt2 = DateTime2(col1=_now, col2=_now)
+ assert isinstance(dt2.col1, col.DateTimeType)
+ assert isinstance(dt2.col2, col.DateTimeType)
+
+ today_str = _now.strftime("%Y-%m-%d 00:00:00.00")
+ now_str = _now.strftime("%Y-%m-%d %T.00")
+ assert str(dt2.col1) == today_str
+ assert str(dt2.col2) == now_str
+
Added: trunk/SQLObject/sqlobject/tests/test_delete.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_delete.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_delete.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,29 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+from test_basic import TestSO1, setupGetters
+
+########################################
+## Delete during select
+########################################
+
+def testSelect():
+ setupGetters(TestSO1)
+ for obj in TestSO1.select('all'):
+ obj.destroySelf()
+ assert list(TestSO1.select('all')) == []
+
+########################################
+## Delete without caching
+########################################
+
+class NoCache(SQLObject):
+ name = StringCol()
+
+def testDestroySelf():
+ setupClass(NoCache)
+ old = NoCache._connection.cache
+ NoCache._connection.cache = cache.CacheSet(cache=False)
+ value = NoCache(name='test')
+ value.destroySelf()
+ NoCache._connection.cache = old
+
Added: trunk/SQLObject/sqlobject/tests/test_distinct.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_distinct.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_distinct.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,32 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Distinct
+########################################
+
+class Distinct1(SQLObject):
+ n = IntCol()
+
+class Distinct2(SQLObject):
+ other = ForeignKey('Distinct1')
+
+def count(select):
+ result = {}
+ for ob in select:
+ result[int(ob.n)] = result.get(int(ob.n), 0)+1
+ return result
+
+def test_distinct():
+ setupClass(Distinct1)
+ setupClass(Distinct2)
+ obs = [Distinct1(n=i) for i in range(3)]
+ Distinct2(other=obs[0])
+ Distinct2(other=obs[0])
+ Distinct2(other=obs[1])
+
+ query = (Distinct2.q.otherID==Distinct1.q.id)
+ sel = Distinct1.select(query)
+ assert count(sel) == {0: 2, 1: 1}
+ sel = Distinct1.select(query, distinct=True)
+ assert count(sel) == {0: 1, 1:1}
Added: trunk/SQLObject/sqlobject/tests/test_enum.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_enum.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_enum.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,19 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Enum test
+########################################
+
+class Enum1(SQLObject):
+
+ _columns = [
+ EnumCol('l', enumValues=['a', 'bcd', 'e']),
+ ]
+
+def testBad():
+ setupClass(Enum1)
+ for l in ['a', 'bcd', 'a', 'e']:
+ Enum1(l=l)
+ if supports('restrictedEnum'):
+ raises(Enum1._connection.module.IntegrityError, Enum1, l='b')
Added: trunk/SQLObject/sqlobject/tests/test_expire.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_expire.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_expire.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,26 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Expiring, syncing
+########################################
+
+class SyncTest(SQLObject):
+ name = StringCol(length=50, alternateID=True, dbName='name_col')
+
+def test_expire():
+ setupClass(SyncTest)
+ SyncTest(name='bob')
+ SyncTest(name='tim')
+
+ conn = SyncTest._connection
+ b = SyncTest.byName('bob')
+ conn.query("UPDATE sync_test SET name_col = 'robert' WHERE id = %i"
+ % b.id)
+ assert b.name == 'bob'
+ b.expire()
+ assert b.name == 'robert'
+ conn.query("UPDATE sync_test SET name_col = 'bobby' WHERE id = %i"
+ % b.id)
+ b.sync()
+ assert b.name == 'bobby'
Added: trunk/SQLObject/sqlobject/tests/test_indexes.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_indexes.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_indexes.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,42 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Indexes
+########################################
+
+class SOIndex1(SQLObject):
+ name = StringCol(length=100)
+ number = IntCol()
+
+ nameIndex = DatabaseIndex('name', unique=True)
+ nameIndex2 = DatabaseIndex(name, number)
+ nameIndex3 = DatabaseIndex({'column': name,
+ 'length': 3})
+
+class SOIndex2(SQLObject):
+
+ name = StringCol()
+
+ nameIndex = DatabaseIndex({'expression': 'lower(name)'})
+
+def test_1():
+ setupClass(SOIndex1)
+ n = 0
+ for name in 'blah blech boring yep yort snort'.split():
+ n += 1
+ SOIndex1(name=name, number=n)
+ mod = SOIndex1._connection.module
+ try:
+ SOIndex1(name='blah', number=0)
+ except (mod.ProgrammingError, mod.IntegrityError):
+ # expected
+ pass
+ else:
+ assert 0, "Exception expected."
+
+def test_2():
+ if not supports('expressionIndex'):
+ return
+ setupClass(SOIndex2)
+ SOIndex2(name='')
Added: trunk/SQLObject/sqlobject/tests/test_inheritance.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_inheritance.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_inheritance.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,31 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Inheritance
+########################################
+
+class Super(SQLObject):
+
+ _columns = [StringCol('name', length=10)]
+
+class Sub(Super):
+
+ _columns = Super._columns + [StringCol('name2', length=10)]
+
+def test_super():
+ setupClass(Super)
+ setupClass(Sub)
+ s1 = Super(name='one')
+ s2 = Super(name='two')
+ s3 = Super.get(s1.id)
+ assert s1 == s3
+
+def test_sub():
+ setupClass(Super)
+ setupClass(Sub)
+ s1 = Sub(name='one', name2='1')
+ s2 = Sub(name='two', name2='2')
+ s3 = Sub.get(s1.id)
+ assert s1 == s3
+
Added: trunk/SQLObject/sqlobject/tests/test_joins.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_joins.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_joins.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,106 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Joins
+########################################
+
+class PersonJoiner(SQLObject):
+
+ _columns = [StringCol('name', length=40, alternateID=True)]
+ _joins = [RelatedJoin('AddressJoiner')]
+
+class AddressJoiner(SQLObject):
+
+ _columns = [StringCol('zip', length=5, alternateID=True)]
+ _joins = [RelatedJoin('PersonJoiner')]
+
+class ImplicitJoiningSO(SQLObject):
+ foo = RelatedJoin('Bar')
+
+class ExplicitJoiningSO(SQLObject):
+ _joins = [MultipleJoin('Bar', joinMethodName='foo')]
+
+class TestJoin:
+
+ def setup_method(self, meth):
+ setupClass(PersonJoiner)
+ setupClass(AddressJoiner)
+ for n in ['bob', 'tim', 'jane', 'joe', 'fred', 'barb']:
+ PersonJoiner(name=n)
+ for z in ['11111', '22222', '33333', '44444']:
+ AddressJoiner(zip=z)
+
+ def test_join(self):
+ b = PersonJoiner.byName('bob')
+ assert b.addressJoiners == []
+ z = AddressJoiner.byZip('11111')
+ b.addAddressJoiner(z)
+ self.assertZipsEqual(b.addressJoiners, ['11111'])
+ self.assertNamesEqual(z.personJoiners, ['bob'])
+ z2 = AddressJoiner.byZip('22222')
+ b.addAddressJoiner(z2)
+ self.assertZipsEqual(b.addressJoiners, ['11111', '22222'])
+ self.assertNamesEqual(z2.personJoiners, ['bob'])
+ b.removeAddressJoiner(z)
+ self.assertZipsEqual(b.addressJoiners, ['22222'])
+ self.assertNamesEqual(z.personJoiners, [])
+
+ def assertZipsEqual(self, zips, dest):
+ assert [a.zip for a in zips] == dest
+
+ def assertNamesEqual(self, people, dest):
+ assert [p.name for p in people] == dest
+
+ def test_joinAttributeWithUnderscores(self):
+ # Make sure that the implicit setting of joinMethodName works
+ assert hasattr(ImplicitJoiningSO, 'foo')
+ assert not hasattr(ImplicitJoiningSO, 'bars')
+
+ # And make sure explicit setting also works
+ assert hasattr(ExplicitJoiningSO, 'foo')
+ assert not hasattr(ExplicitJoiningSO, 'bars')
+
+class PersonJoiner2(SQLObject):
+
+ _columns = [StringCol('name', length=40, alternateID=True)]
+ _joins = [MultipleJoin('AddressJoiner2')]
+
+class AddressJoiner2(SQLObject):
+
+ _columns = [StringCol('zip', length=5),
+ StringCol('plus4', length=4, default=None),
+ ForeignKey('PersonJoiner2')]
+ _defaultOrder = ['-zip', 'plus4']
+
+class TestJoin2:
+
+ def setup_method(self, meth):
+ setupClass(PersonJoiner2)
+ setupClass(AddressJoiner2)
+ p1 = PersonJoiner2(name='bob')
+ p2 = PersonJoiner2(name='sally')
+ for z in ['11111', '22222', '33333']:
+ a = AddressJoiner2(zip=z, personJoiner2=p1)
+ #p1.addAddressJoiner2(a)
+ AddressJoiner2(zip='00000', personJoiner2=p2)
+
+ def test_basic(self):
+ bob = PersonJoiner2.byName('bob')
+ sally = PersonJoiner2.byName('sally')
+ assert len(bob.addressJoiner2s) == 3
+ assert len(sally.addressJoiner2s) == 1
+ bob.addressJoiner2s[0].destroySelf()
+ assert len(bob.addressJoiner2s) == 2
+ z = bob.addressJoiner2s[0]
+ z.zip = 'xxxxx'
+ id = z.id
+ del z
+ z = AddressJoiner2.get(id)
+ assert z.zip == 'xxxxx'
+
+ def test_defaultOrder(self):
+ p1 = PersonJoiner2.byName('bob')
+ assert ([i.zip for i in p1.addressJoiner2s]
+ == ['33333', '22222', '11111'])
+
Added: trunk/SQLObject/sqlobject/tests/test_lazy.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_lazy.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_lazy.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,161 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Lazy updates
+########################################
+
+class Lazy(SQLObject):
+
+ _lazyUpdate = True
+ name = StringCol()
+ other = StringCol(default='nothing')
+ third = StringCol(default='third')
+
+class TestLazyTest:
+
+ def setup_method(self, meth):
+ # All this stuff is so that we can track when the connection
+ # does an actual update; we put in a new _SO_update method
+ # that calls the original and sets an instance variable that
+ # we can later check.
+ setupClass(Lazy)
+ self.conn = Lazy._connection
+ self.conn.didUpdate = False
+ self._oldUpdate = self.conn._SO_update
+ newUpdate = (
+ lambda so, values, s=self, c=self.conn, o=self._oldUpdate:
+ self._alternateUpdate(so, values, c, o))
+ self.conn._SO_update = newUpdate
+
+ def teardown_method(self, meth):
+ self.conn._SO_update = self._oldUpdate
+ del self._oldUpdate
+
+ def _alternateUpdate(self, so, values, conn, oldUpdate):
+ conn.didUpdate = True
+ return oldUpdate(so, values)
+
+ def test_lazy(self):
+ assert not self.conn.didUpdate
+ obj = Lazy(name='tim')
+ # We just did an insert, but not an update:
+ assert not self.conn.didUpdate
+ obj.set(name='joe')
+ assert obj.dirty
+ assert obj.name == 'joe'
+ assert not self.conn.didUpdate
+ obj.syncUpdate()
+ assert obj.name == 'joe'
+ assert self.conn.didUpdate
+ assert not obj.dirty
+ assert obj.name == 'joe'
+ self.conn.didUpdate = False
+
+ obj = Lazy(name='frank')
+ obj.name = 'joe'
+ assert not self.conn.didUpdate
+ assert obj.dirty
+ assert obj.name == 'joe'
+ obj.name = 'joe2'
+ assert not self.conn.didUpdate
+ assert obj.dirty
+ assert obj.name == 'joe2'
+ obj.syncUpdate()
+ assert obj.name == 'joe2'
+ assert not obj.dirty
+ assert self.conn.didUpdate
+ self.conn.didUpdate = False
+
+ obj = Lazy(name='loaded')
+ assert not obj.dirty
+ assert not self.conn.didUpdate
+ assert obj.name == 'loaded'
+ obj.name = 'unloaded'
+ assert obj.dirty
+ assert obj.name == 'unloaded'
+ assert not self.conn.didUpdate
+ obj.sync()
+ assert not obj.dirty
+ assert obj.name == 'unloaded'
+ assert self.conn.didUpdate
+ self.conn.didUpdate = False
+ obj.name = 'whatever'
+ assert obj.dirty
+ assert obj.name == 'whatever'
+ assert not self.conn.didUpdate
+ obj._SO_loadValue('name')
+ assert obj.dirty
+ assert obj.name == 'whatever'
+ assert not self.conn.didUpdate
+ obj._SO_loadValue('other')
+ assert obj.name == 'whatever'
+ assert not self.conn.didUpdate
+ obj.syncUpdate()
+ assert self.conn.didUpdate
+ self.conn.didUpdate = False
+
+ # Now, check that get() doesn't screw
+ # cached objects' validator state.
+ obj_id = obj.id
+ old_state = obj._SO_validatorState
+ obj = Lazy.get(obj_id)
+ assert not obj.dirty
+ assert not self.conn.didUpdate
+ assert obj._SO_validatorState is old_state
+ assert obj.name == 'whatever'
+ obj.name = 'unloaded'
+ assert obj.name == 'unloaded'
+ assert obj.dirty
+ assert not self.conn.didUpdate
+ # Fetch the object again with get() and
+ # make sure dirty is still set, as the
+ # object should come from the cache.
+ obj = Lazy.get(obj_id)
+ assert obj.dirty
+ assert not self.conn.didUpdate
+ assert obj.name == 'unloaded'
+ obj.syncUpdate()
+ assert self.conn.didUpdate
+ assert not obj.dirty
+ self.conn.didUpdate = False
+
+ # Then clear the cache, and try a get()
+ # again, to make sure stuf like _SO_createdValues
+ # is properly initialized.
+ self.conn.cache.clear()
+ obj = Lazy.get(obj_id)
+ assert not obj.dirty
+ assert not self.conn.didUpdate
+ assert obj.name == 'unloaded'
+ obj.name = 'spongebob'
+ assert obj.name == 'spongebob'
+ assert obj.dirty
+ assert not self.conn.didUpdate
+ obj.syncUpdate()
+ assert self.conn.didUpdate
+ assert not obj.dirty
+ self.conn.didUpdate = False
+
+ obj = Lazy(name='last')
+ assert not obj.dirty
+ obj.syncUpdate()
+ assert not self.conn.didUpdate
+ assert not obj.dirty
+ # Check that setting multiple values
+ # actually works. This was broken
+ # and just worked because we were testing
+ # only one value at a time, so 'name'
+ # had the right value after the for loop *wink*
+ # Also, check that passing a name that is not
+ # a valid column doesn't break, but instead
+ # just does a plain setattr.
+ obj.set(name='first', other='who', third='yes')
+ assert obj.name == 'first'
+ assert obj.other == 'who'
+ assert obj.third == 'yes'
+ assert obj.dirty
+ assert not self.conn.didUpdate
+ obj.syncUpdate()
+ assert self.conn.didUpdate
+ assert not obj.dirty
Added: trunk/SQLObject/sqlobject/tests/test_picklecol.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_picklecol.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_picklecol.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,36 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Pickle columns
+########################################
+
+class PickleData:
+ pi = 3.14156
+ def __init__(self):
+ self.question = 'The Ulimate Question of Life, the Universe and Everything'
+ self.answer = 42
+
+class PickleContainer(SQLObject):
+ pickledata = PickleCol(default=None, length=65535)
+
+def test_pickleCol():
+ setupClass(PickleContainer)
+ mypickledata = PickleData()
+
+ ctnr = PickleContainer(pickledata=mypickledata)
+ iid = ctnr.id
+
+ PickleContainer._connection.cache.clear()
+
+ ctnr2 = PickleContainer.get(iid)
+ s2 = ctnr2.pickledata
+
+ assert isinstance(s2, PickleData)
+ assert isinstance(s2.pi, float)
+ assert isinstance(s2.question, str)
+ assert isinstance(s2.answer, int)
+ assert s2.pi == mypickledata.pi
+ assert s2.question == mypickledata.question
+ assert s2.answer == mypickledata.answer
+
Added: trunk/SQLObject/sqlobject/tests/test_select.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_select.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_select.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,101 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+try:
+ enumerate
+except NameError:
+ def enumerate(iterable):
+ i = 0
+ for obj in iterable:
+ yield i, obj
+ i += 1
+
+class IterTest(SQLObject):
+ name = StringCol(dbName='name_col')
+
+names = ('a', 'b', 'c')
+def setupIter():
+ setupClass(IterTest)
+ for n in names:
+ IterTest(name=n)
+
+def test_00_normal():
+ setupIter()
+ count = 0
+ for test in IterTest.select():
+ count += 1
+ assert count == len(names)
+
+def test_01_turn_to_list():
+ count = 0
+ for test in list(IterTest.select()):
+ count += 1
+ assert count == len(names)
+
+def test_02_generator():
+ all = IterTest.select()
+ count = 0
+ for i, test in enumerate(all):
+ count += 1
+ assert count == len(names)
+
+def test_03_ranged_indexed():
+ all = IterTest.select()
+ count = 0
+ for i in range(all.count()):
+ test = all[i]
+ count += 1
+ assert count == len(names)
+
+def test_04_indexed_ended_by_exception():
+ all = IterTest.select()
+ count = 0
+ try:
+ while 1:
+ test = all[count]
+ count = count+1
+ # Stop the test if it's gone on too long
+ if count > len(names):
+ break
+ except IndexError:
+ pass
+ assert count == len(names)
+
+
+
+
+class Counter2(SQLObject):
+
+ _columns = [
+ IntCol('n1', notNull=True),
+ IntCol('n2', notNull=True),
+ ]
+
+class TestSelect:
+
+ def setup_method(self, meth):
+ setupClass(Counter2)
+ for i in range(10):
+ for j in range(10):
+ Counter2(n1=i, n2=j)
+
+ def counterEqual(self, counters, value):
+ assert [(c.n1, c.n2) for c in counters] == value
+
+ def accumulateEqual(self, func, counters, value):
+ assert func([c.n1 for c in counters]) == value
+
+ def test_1(self):
+ try:
+ sum
+ except NameError:
+ def sum(sequence):
+ s = 0
+ for item in sequence:
+ s = s + item
+ return s
+ self.accumulateEqual(sum,Counter2.select(orderBy='n1'),
+ sum(range(10)) * 10)
+
+ def test_2(self):
+ self.accumulateEqual(len,Counter2.select('all'), 100)
Added: trunk/SQLObject/sqlobject/tests/test_slice.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_slice.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_slice.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,51 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## Slicing tests
+########################################
+
+class Counter(SQLObject):
+
+ _columns = [
+ IntCol('number', notNull=True),
+ ]
+
+class TestSlice:
+
+ def setup_method(self, meth):
+ setupClass(Counter)
+ for i in range(100):
+ Counter(number=i)
+
+ def counterEqual(self, counters, value):
+ assert [c.number for c in counters] == value
+
+ def test_1(self):
+ self.counterEqual(
+ Counter.select(None, orderBy='number'), range(100))
+
+ def test_2(self):
+ self.counterEqual(
+ Counter.select(None, orderBy='number')[10:20],
+ range(10, 20))
+
+ def test_3(self):
+ self.counterEqual(
+ Counter.select(None, orderBy='number')[20:30][:5],
+ range(20, 25))
+
+ def test_4(self):
+ self.counterEqual(
+ Counter.select(None, orderBy='number')[:-10],
+ range(0, 90))
+
+ def test_5(self):
+ self.counterEqual(
+ Counter.select(None, orderBy='number', reversed=True),
+ range(99, -1, -1))
+
+ def test_6(self):
+ self.counterEqual(
+ Counter.select(None, orderBy='-number'),
+ range(99, -1, -1))
Added: trunk/SQLObject/sqlobject/tests/test_sorting.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_sorting.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_sorting.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,62 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+class Names(SQLObject):
+
+ _table = 'names_table'
+
+ firstName = StringCol(length=30)
+ lastName = StringCol(length=30)
+
+ _defaultOrder = ['lastName', 'firstName']
+
+def setupNames():
+ setupClass(Names)
+ inserts(Names, [('aj', 'baker'), ('joe', 'robbins'),
+ ('tim', 'jackson'), ('joe', 'baker'),
+ ('zoe', 'robbins')],
+ schema='firstName lastName')
+
+def nameList(names):
+ result = []
+ for name in names:
+ result.append('%s %s' % (name.firstName, name.lastName))
+ return result
+
+def firstList(names):
+ return [n.firstName for n in names]
+
+def test_defaultOrder():
+ setupNames()
+ assert nameList(Names.select()) == \
+ ['aj baker', 'joe baker',
+ 'tim jackson', 'joe robbins',
+ 'zoe robbins']
+
+def test_otherOrder():
+ setupNames()
+ assert nameList(Names.select().orderBy(['firstName', 'lastName'])) == \
+ ['aj baker', 'joe baker',
+ 'joe robbins', 'tim jackson',
+ 'zoe robbins']
+
+def test_untranslatedColumnOrder():
+ setupNames()
+ assert nameList(Names.select().orderBy(['first_name', 'last_name'])) == \
+ ['aj baker', 'joe baker',
+ 'joe robbins', 'tim jackson',
+ 'zoe robbins']
+
+def test_singleUntranslatedColumnOrder():
+ setupNames()
+ assert firstList(Names.select().orderBy('firstName')) == \
+ ['aj', 'joe', 'joe', 'tim', 'zoe']
+ assert firstList(Names.select().orderBy('first_name')) == \
+ ['aj', 'joe', 'joe', 'tim', 'zoe']
+ assert firstList(Names.select().orderBy('-firstName')) == \
+ ['zoe', 'tim', 'joe', 'joe', 'aj']
+ assert firstList(Names.select().orderBy('-first_name')) == \
+ ['zoe', 'tim', 'joe', 'joe', 'aj']
+ assert firstList(Names.select().orderBy(Names.q.firstName)) == \
+ ['aj', 'joe', 'joe', 'tim', 'zoe']
+
Added: trunk/SQLObject/sqlobject/tests/test_stringid.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_stringid.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_stringid.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,63 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+
+########################################
+## String ID test
+########################################
+
+class SOStringID(SQLObject):
+
+ _table = 'so_string_id'
+ _idType = str
+ val = StringCol(alternateID=True)
+
+ mysqlCreate = """
+ CREATE TABLE IF NOT EXISTS so_string_id (
+ id VARCHAR(50) PRIMARY KEY,
+ val TEXT
+ )
+ """
+
+ postgresCreate = """
+ CREATE TABLE so_string_id (
+ id VARCHAR(50) PRIMARY KEY,
+ val TEXT
+ )
+ """
+
+ sybaseCreate = """
+ CREATE TABLE so_string_id (
+ id VARCHAR(50) UNIQUE,
+ val VARCHAR(50) NULL
+ )
+ """
+
+ firebirdCreate = """
+ CREATE TABLE so_string_id (
+ id VARCHAR(50) NOT NULL PRIMARY KEY,
+ val BLOB SUB_TYPE TEXT
+ )
+ """
+
+ sqliteCreate = postgresCreate
+
+ mysqlDrop = """
+ DROP TABLE IF EXISTS so_string_id
+ """
+
+ postgresDrop = """
+ DROP TABLE so_string_id
+ """
+
+ sqliteDrop = postgresDrop
+ firebirdDrop = postgresDrop
+
+
+def test_stringID():
+ setupClass(SOStringID)
+ t = SOStringID(id='hey', val='whatever')
+ t2 = SOStringID.byVal('whatever')
+ assert t == t2
+ t3 = SOStringID(id='you', val='nowhere')
+ t4 = SOStringID.get('you')
+ assert t3 == t4
Added: trunk/SQLObject/sqlobject/tests/test_style.py
===================================================================
--- trunk/SQLObject/sqlobject/tests/test_style.py 2005-02-01 07:58:02 UTC (rev 564)
+++ trunk/SQLObject/sqlobject/tests/test_style.py 2005-02-01 07:59:43 UTC (rev 565)
@@ -0,0 +1,28 @@
+from sqlobject import *
+from sqlobject.tests.dbtest import *
+from sqlobject import styles
+
+class Another...
[truncated message content] |