Commits

Mike Bayer committed 120dcee

reorganized unit tests into subdirectories

Comments (0)

Files changed (97)

 - restored global_connect() function, attaches to a DynamicMetaData
 instance called "default_metadata".  leaving MetaData arg to Table
 out will use the default metadata.
+- fixes to session cascade behavior, entity_name propigation
+- reorganized unittests into subdirectories
+
 0.2.1
 - "pool" argument to create_engine() properly propigates
 - fixes to URL, raises exception if not parsed, does not pass blank
+To run unit tests (assuming unix-style commandline, adjust as needed for windows):
+
+cd into the SQLAlchemy distribution directory.
+
+Set up the PYTHONPATH:
+
+    export PYTHONPATH=./lib/:./test/
+
+To run all tests:
+
+    python test/alltests.py
+
+Help is available via:
+
+    python test/alltests.py --help
+
+    usage: alltests.py [options] files...
+    
+    options:
+      -h, --help            show this help message and exit
+      --dburi=DBURI         database uri (overrides --db)
+      --db=DB               prefab database uri (sqlite, sqlite_file, postgres,
+    			mysql, oracle, oracle8, mssql)
+      --mockpool            use mock pool
+      --verbose             full debug echoing
+      --noecho              Disable SQL statement echoing
+      --quiet               be totally quiet
+      --nothreadlocal       dont use thread-local mod
+      --enginestrategy=ENGINESTRATEGY
+    			engine strategy (plain or threadlocal, defaults to SA
+    			default)
+    
+
+Any unittest module can be run directly from the module file (same commandline options):
+
+    python test/orm/mapper.py
+
+Additionally, to run a speciic test within the module, specify it as ClassName.methodname:
+
+    python test/orm/mapper.py MapperTest.testget
+
+

test/activemapper.py

-from sqlalchemy.ext.activemapper           import ActiveMapper, column, one_to_many, one_to_one, objectstore
-from sqlalchemy             import and_, or_, clear_mappers
-from sqlalchemy             import ForeignKey, String, Integer, DateTime
-from datetime               import datetime
-
-import unittest
-import sqlalchemy.ext.activemapper as activemapper
-
-import testbase
-
-class testcase(testbase.PersistTest):
-    def setUpAll(self):
-        global Person, Preferences, Address
-        
-        class Person(ActiveMapper):
-            class mapping:
-                id          = column(Integer, primary_key=True)
-                full_name   = column(String)
-                first_name  = column(String)
-                middle_name = column(String)
-                last_name   = column(String)
-                birth_date  = column(DateTime)
-                ssn         = column(String)
-                gender      = column(String)
-                home_phone  = column(String)
-                cell_phone  = column(String)
-                work_phone  = column(String)
-                prefs_id    = column(Integer, foreign_key=ForeignKey('preferences.id'))
-                addresses   = one_to_many('Address', colname='person_id', backref='person')
-                preferences = one_to_one('Preferences', colname='pref_id', backref='person')
-
-            def __str__(self):
-                s =  '%s\n' % self.full_name
-                s += '  * birthdate: %s\n' % (self.birth_date or 'not provided')
-                s += '  * fave color: %s\n' % (self.preferences.favorite_color or 'Unknown')
-                s += '  * personality: %s\n' % (self.preferences.personality_type or 'Unknown')
-
-                for address in self.addresses:
-                    s += '  * address: %s\n' % address.address_1
-                    s += '             %s, %s %s\n' % (address.city, address.state, address.postal_code)
-
-                return s
-
-        class Preferences(ActiveMapper):
-            class mapping:
-                __table__        = 'preferences'
-                id               = column(Integer, primary_key=True)
-                favorite_color   = column(String)
-                personality_type = column(String)
-
-        class Address(ActiveMapper):
-            class mapping:
-                id          = column(Integer, primary_key=True)
-                type        = column(String)
-                address_1   = column(String)
-                city        = column(String)
-                state       = column(String)
-                postal_code = column(String)
-                person_id   = column(Integer, foreign_key=ForeignKey('person.id'))
-
-        activemapper.metadata.connect(testbase.db)
-        activemapper.create_tables()
-
-    def tearDownAll(self):
-        clear_mappers()
-        activemapper.drop_tables()
-        
-    def tearDown(self):
-        for t in activemapper.metadata.table_iterator(reverse=True):
-            t.delete().execute()
-        #people = Person.select()
-        #for person in people: person.delete()
-        
-        #addresses = Address.select()
-        #for address in addresses: address.delete()
-        
-        #preferences = Preferences.select()
-        #for preference in preferences: preference.delete()
-        
-        #objectstore.flush()
-        #objectstore.clear()
-    
-    def create_person_one(self):
-        # create a person
-        p1 = Person(
-                full_name='Jonathan LaCour',
-                birth_date=datetime(1979, 10, 12),
-                preferences=Preferences(
-                                favorite_color='Green',
-                                personality_type='ENTP'
-                            ),
-                addresses=[
-                    Address(
-                        address_1='123 Some Great Road.',
-                        city='Atlanta',
-                        state='GA',
-                        postal_code='30338'
-                    ),
-                    Address(
-                        address_1='435 Franklin Road.',
-                        city='Atlanta',
-                        state='GA',
-                        postal_code='30342'
-                    )
-                ]
-             )
-        return p1
-    
-    
-    def create_person_two(self):
-        p2 = Person(
-                full_name='Lacey LaCour',
-                addresses=[
-                    Address(
-                        address_1='123 Some Great Road.',
-                        city='Atlanta',
-                        state='GA',
-                        postal_code='30338'
-                    ),
-                    Address(
-                        address_1='200 Main Street',
-                        city='Roswell',
-                        state='GA',
-                        postal_code='30075'
-                    )
-                ]
-             )
-        # I don't like that I have to do this... and putting
-        # a "self.preferences = Preferences()" into the __init__
-        # of Person also doens't seem to fix this
-        p2.preferences = Preferences()
-        
-        return p2
-    
-    
-    def test_create(self):
-        p1 = self.create_person_one()
-        objectstore.flush()
-        objectstore.clear()
-        
-        results = Person.select()
-        
-        self.assertEquals(len(results), 1)
-        
-        person = results[0]
-        self.assertEquals(person.id, p1.id)
-        self.assertEquals(len(person.addresses), 2)
-        self.assertEquals(person.addresses[0].postal_code, '30338')
-    
-    
-    def test_delete(self):
-        p1 = self.create_person_one()
-        
-        objectstore.flush()
-        objectstore.clear()
-        
-        results = Person.select()
-        self.assertEquals(len(results), 1)
-        
-        results[0].delete()
-        objectstore.flush()
-        objectstore.clear()
-        
-        results = Person.select()
-        self.assertEquals(len(results), 0)
-    
-    
-    def test_multiple(self):
-        p1 = self.create_person_one()
-        p2 = self.create_person_two()
-        
-        objectstore.flush()
-        objectstore.clear()
-        
-        # select and make sure we get back two results
-        people = Person.select()
-        self.assertEquals(len(people), 2)
-                
-        # make sure that our backwards relationships work
-        self.assertEquals(people[0].addresses[0].person.id, p1.id)
-        self.assertEquals(people[1].addresses[0].person.id, p2.id)
-        
-        # try a more complex select
-        results = Person.select(
-            or_(
-                and_(
-                    Address.c.person_id == Person.c.id,
-                    Address.c.postal_code.like('30075')
-                ),
-                and_(
-                    Person.c.prefs_id == Preferences.c.id,
-                    Preferences.c.favorite_color == 'Green'
-                )
-            )
-        )
-        self.assertEquals(len(results), 2)
-        
-    
-    def test_oneway_backref(self):
-        # FIXME: I don't know why, but it seems that my backwards relationship
-        #        on preferences still ends up being a list even though I pass
-        #        in uselist=False...
-        # FIXED: the backref is a new PropertyLoader which needs its own "uselist".
-        # uses a function which I dont think existed when you first wrote ActiveMapper.
-        p1 = self.create_person_one()
-        self.assertEquals(p1.preferences.person, p1)
-        p1.delete()
-        
-        objectstore.flush()
-        objectstore.clear()
-    
-    
-    def test_select_by(self):
-        # FIXME: either I don't understand select_by, or it doesn't work.
-        # FIXED (as good as we can for now): yup....everyone thinks it works that way....it only
-        # generates joins for keyword arguments, not ColumnClause args.  would need a new layer of
-        # "MapperClause" objects to use properties in expressions. (MB)
-        
-        p1 = self.create_person_one()
-        p2 = self.create_person_two()
-        
-        objectstore.flush()
-        objectstore.clear()
-        
-        results = Person.select(
-            Address.c.postal_code.like('30075') &
-            Person.join_to('addresses')
-        )
-        self.assertEquals(len(results), 1)
-
-
-    
-if __name__ == '__main__':
-    # go ahead and setup the database connection, and create the tables
-    
-    # launch the unit tests
-    unittest.main()
 import testbase
 import unittest
 
-def suite():
-    modules_to_test = (
-        # core utilities
-        'historyarray',
-        'attributes', 
-        'dependency',
-
-        # connectivity, execution
-        'pool', 
-        'transaction',
-        
-        # schema/tables
-        'reflection', 
-        'testtypes',
-        'indexes',
+import orm.alltests as orm
+import base.alltests as base
+import sql.alltests as sql
+import engine.alltests as engine
+import ext.alltests as ext
 
-        # SQL syntax
-        'select',
-        'selectable',
-        'case_statement', 
-        
-        # assorted round-trip tests
-        'query',
-        
-        # defaults, sequences (postgres/oracle)
-        'defaults',
-        
-        # ORM selecting
-        'mapper',
-        'selectresults',
-        'lazytest1',
-        'eagertest1',
-        'eagertest2',
-        
-        # ORM persistence
-        'sessioncontext', 
-        'objectstore',
-        'cascade',
-        'relationships',
-        'association',
-        
-        # cyclical ORM persistence
-        'cycles',
-        'poly_linked_list',
-        
-        # more select/persistence, backrefs
-        'entity',
-        'manytomany',
-        'onetoone',
-        'inheritance',
-        'inheritance2',
-	'inheritance3',
-        'polymorph',
-        
-        # extensions
-        'proxy_engine',
-        'activemapper',
-        'sqlsoup'
-        
-        #'wsgi_test',
-        
-        )
+def suite():
     alltests = unittest.TestSuite()
-    for module in map(__import__, modules_to_test):
-        alltests.addTest(unittest.findTestCases(module, suiteClass=None))
+    for suite in (base, engine, sql, orm, ext):
+        alltests.addTest(suite.suite())
     return alltests
 
 

test/association.py

-import testbase
-
-from sqlalchemy import *
-
-
-class AssociationTest(testbase.PersistTest):
-    def setUpAll(self):
-        global items, item_keywords, keywords, metadata, Item, Keyword, KeywordAssociation
-        metadata = BoundMetaData(testbase.db)
-        items = Table('items', metadata, 
-            Column('item_id', Integer, primary_key=True),
-            Column('name', String(40)),
-            )
-        item_keywords = Table('item_keywords', metadata,
-            Column('item_id', Integer, ForeignKey('items.item_id')),
-            Column('keyword_id', Integer, ForeignKey('keywords.keyword_id')),
-            Column('data', String(40))
-            )
-        keywords = Table('keywords', metadata,
-            Column('keyword_id', Integer, primary_key=True),
-            Column('name', String(40))
-            )
-        metadata.create_all()
-        
-        class Item(object):
-            def __init__(self, name):
-                self.name = name
-            def __repr__(self):
-                return "Item id=%d name=%s keywordassoc=%s" % (self.item_id, self.name, repr(self.keywords))
-        class Keyword(object):
-            def __init__(self, name):
-                self.name = name
-            def __repr__(self):
-                return "Keyword id=%d name=%s" % (self.keyword_id, self.name)
-        class KeywordAssociation(object):
-            def __init__(self, keyword, data):
-                self.keyword = keyword
-                self.data = data
-            def __repr__(self):
-                return "KeywordAssociation itemid=%d keyword=%s data=%s" % (self.item_id, repr(self.keyword), self.data)
-        
-        mapper(Keyword, keywords)
-        mapper(KeywordAssociation, item_keywords, properties={
-            'keyword':relation(Keyword, lazy=False)
-        }, primary_key=[item_keywords.c.item_id, item_keywords.c.keyword_id], order_by=[item_keywords.c.data])
-        mapper(Item, items, properties={
-            'keywords' : relation(KeywordAssociation, association=Keyword)
-        })
-        
-    def tearDown(self):
-        for t in metadata.table_iterator(reverse=True):
-            t.delete().execute()
-    def tearDownAll(self):
-        clear_mappers()
-        metadata.drop_all()
-        
-    def testinsert(self):
-        sess = create_session()
-        item1 = Item('item1')
-        item2 = Item('item2')
-        item1.keywords.append(KeywordAssociation(Keyword('blue'), 'blue_assoc'))
-        item1.keywords.append(KeywordAssociation(Keyword('red'), 'red_assoc'))
-        item2.keywords.append(KeywordAssociation(Keyword('green'), 'green_assoc'))
-        sess.save(item1)
-        sess.save(item2)
-        sess.flush()
-        saved = repr([item1, item2])
-        sess.clear()
-        l = sess.query(Item).select()
-        loaded = repr(l)
-        print saved
-        print loaded
-        self.assert_(saved == loaded)
-
-    def testreplace(self):
-        sess = create_session()
-        item1 = Item('item1')
-        item1.keywords.append(KeywordAssociation(Keyword('blue'), 'blue_assoc'))
-        item1.keywords.append(KeywordAssociation(Keyword('red'), 'red_assoc'))
-        sess.save(item1)
-        sess.flush()
-        
-        red_keyword = item1.keywords[1].keyword
-        del item1.keywords[1]
-        item1.keywords.append(KeywordAssociation(red_keyword, 'new_red_assoc'))
-        sess.flush()
-        saved = repr([item1])
-        sess.clear()
-        l = sess.query(Item).select()
-        loaded = repr(l)
-        print saved
-        print loaded
-        self.assert_(saved == loaded)
-
-    def testmodify(self):
-        sess = create_session()
-        item1 = Item('item1')
-        item2 = Item('item2')
-        item1.keywords.append(KeywordAssociation(Keyword('blue'), 'blue_assoc'))
-        item1.keywords.append(KeywordAssociation(Keyword('red'), 'red_assoc'))
-        item2.keywords.append(KeywordAssociation(Keyword('green'), 'green_assoc'))
-        sess.save(item1)
-        sess.save(item2)
-        sess.flush()
-        
-        red_keyword = item1.keywords[1].keyword
-        del item1.keywords[0]
-        del item1.keywords[0]
-        purple_keyword = Keyword('purple')
-        item1.keywords.append(KeywordAssociation(red_keyword, 'new_red_assoc'))
-        item2.keywords.append(KeywordAssociation(purple_keyword, 'purple_item2_assoc'))
-        item1.keywords.append(KeywordAssociation(purple_keyword, 'purple_item1_assoc'))
-        item1.keywords.append(KeywordAssociation(Keyword('yellow'), 'yellow_assoc'))
-        
-        sess.flush()
-        saved = repr([item1, item2])
-        sess.clear()
-        l = sess.query(Item).select()
-        loaded = repr(l)
-        print saved
-        print loaded
-        self.assert_(saved == loaded)
-
-    def testdelete(self):
-        sess = create_session()
-        item1 = Item('item1')
-        item2 = Item('item2')
-        item1.keywords.append(KeywordAssociation(Keyword('blue'), 'blue_assoc'))
-        item1.keywords.append(KeywordAssociation(Keyword('red'), 'red_assoc'))
-        item2.keywords.append(KeywordAssociation(Keyword('green'), 'green_assoc'))
-        sess.save(item1)
-        sess.save(item2)
-        sess.flush()
-        self.assert_(item_keywords.count().scalar() == 3)
-
-        sess.delete(item1)
-        sess.delete(item2)
-        sess.flush()
-        self.assert_(item_keywords.count().scalar() == 0)
-
-        
-if __name__ == "__main__":
-    testbase.main()        

test/attributes.py

-from testbase import PersistTest
-import sqlalchemy.util as util
-import sqlalchemy.attributes as attributes
-import unittest, sys, os
-import pickle
-
-
-class MyTest(object):pass
-    
-class AttributesTest(PersistTest):
-    """tests for the attributes.py module, which deals with tracking attribute changes on an object."""
-    def testbasic(self):
-        class User(object):pass
-        manager = attributes.AttributeManager()
-        manager.register_attribute(User, 'user_id', uselist = False)
-        manager.register_attribute(User, 'user_name', uselist = False)
-        manager.register_attribute(User, 'email_address', uselist = False)
-        
-        u = User()
-        print repr(u.__dict__)
-        
-        u.user_id = 7
-        u.user_name = 'john'
-        u.email_address = 'lala@123.com'
-        
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'lala@123.com')
-        manager.commit(u)
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'lala@123.com')
-
-        u.user_name = 'heythere'
-        u.email_address = 'foo@bar.com'
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'heythere' and u.email_address == 'foo@bar.com')
-        
-        manager.rollback(u)
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'lala@123.com')
-
-    def testpickleness(self):
-        manager = attributes.AttributeManager()
-        manager.register_attribute(MyTest, 'user_id', uselist = False)
-        manager.register_attribute(MyTest, 'user_name', uselist = False)
-        manager.register_attribute(MyTest, 'email_address', uselist = False)
-        x = MyTest()
-        x.user_id=7
-        pickle.dumps(x)
-
-    def testlist(self):
-        class User(object):pass
-        class Address(object):pass
-        manager = attributes.AttributeManager()
-        manager.register_attribute(User, 'user_id', uselist = False)
-        manager.register_attribute(User, 'user_name', uselist = False)
-        manager.register_attribute(User, 'addresses', uselist = True)
-        manager.register_attribute(Address, 'address_id', uselist = False)
-        manager.register_attribute(Address, 'email_address', uselist = False)
-        
-        u = User()
-        print repr(u.__dict__)
-
-        u.user_id = 7
-        u.user_name = 'john'
-        u.addresses = []
-        a = Address()
-        a.address_id = 10
-        a.email_address = 'lala@123.com'
-        u.addresses.append(a)
-
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
-        manager.commit(u, a)
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
-
-        u.user_name = 'heythere'
-        a = Address()
-        a.address_id = 11
-        a.email_address = 'foo@bar.com'
-        u.addresses.append(a)
-        print repr(u.__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'heythere' and u.addresses[0].email_address == 'lala@123.com' and u.addresses[1].email_address == 'foo@bar.com')
-
-        manager.rollback(u, a)
-        print repr(u.__dict__)
-        print repr(u.addresses[0].__dict__)
-        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
-        self.assert_(len(u.addresses.unchanged_items()) == 1)
-
-    def testbackref(self):
-        class Student(object):pass
-        class Course(object):pass
-        manager = attributes.AttributeManager()
-        manager.register_attribute(Student, 'courses', uselist=True, extension=attributes.GenericBackrefExtension('students'))
-        manager.register_attribute(Course, 'students', uselist=True, extension=attributes.GenericBackrefExtension('courses'))
-        
-        s = Student()
-        c = Course()
-        s.courses.append(c)
-        self.assert_(c.students == [s])
-        s.courses.remove(c)
-        self.assert_(c.students == [])
-        
-        (s1, s2, s3) = (Student(), Student(), Student())
-        c.students = [s1, s2, s3]
-        self.assert_(s2.courses == [c])
-        self.assert_(s1.courses == [c])
-        s1.courses.remove(c)
-        self.assert_(c.students == [s2,s3])
-        
-        
-        class Post(object):pass
-        class Blog(object):pass
-        
-        manager.register_attribute(Post, 'blog', uselist=False, extension=attributes.GenericBackrefExtension('posts'))
-        manager.register_attribute(Blog, 'posts', uselist=True, extension=attributes.GenericBackrefExtension('blog'))
-        b = Blog()
-        (p1, p2, p3) = (Post(), Post(), Post())
-        b.posts.append(p1)
-        b.posts.append(p2)
-        b.posts.append(p3)
-        self.assert_(b.posts == [p1, p2, p3])
-        self.assert_(p2.blog is b)
-        
-        p3.blog = None
-        self.assert_(b.posts == [p1, p2])
-        p4 = Post()
-        p4.blog = b
-        self.assert_(b.posts == [p1, p2, p4])
-        
-
-        class Port(object):pass
-        class Jack(object):pass
-        manager.register_attribute(Port, 'jack', uselist=False, extension=attributes.GenericBackrefExtension('port'))
-        manager.register_attribute(Jack, 'port', uselist=False, extension=attributes.GenericBackrefExtension('jack'))
-        p = Port()
-        j = Jack()
-        p.jack = j
-        self.assert_(j.port is p)
-        self.assert_(p.jack is not None)
-        
-        j.port = None
-        self.assert_(p.jack is None)
-
-    def testinheritance(self):
-        """tests that attributes are polymorphic"""
-        class Foo(object):pass
-        class Bar(Foo):pass
-        
-        manager = attributes.AttributeManager()
-        
-        def func1():
-            return "this is the foo attr"
-        def func2():
-            return "this is the bar attr"
-        def func3():
-            return "this is the shared attr"
-        manager.register_attribute(Foo, 'element', uselist=False, callable_=lambda o:func1)
-        manager.register_attribute(Foo, 'element2', uselist=False, callable_=lambda o:func3)
-        manager.register_attribute(Bar, 'element', uselist=False, callable_=lambda o:func2)
-        
-        x = Foo()
-        y = Bar()
-        assert x.element == 'this is the foo attr'
-        assert y.element == 'this is the bar attr'
-        assert x.element2 == 'this is the shared attr'
-        assert y.element2 == 'this is the shared attr'
-        
-if __name__ == "__main__":
-    unittest.main()

test/autoconnect_engine.py

-from sqlalchemy import *
-from sqlalchemy.ext.proxy import AutoConnectEngine
-
-from testbase import PersistTest
-import testbase
-import os
-
-#
-# Define an engine, table and mapper at the module level, to show that the
-# table and mapper can be used with different real engines in multiple threads
-#
-
-
-module_engine = AutoConnectEngine( testbase.db_uri )
-users = Table('users', module_engine, 
-              Column('user_id', Integer, primary_key=True),
-              Column('user_name', String(16)),
-              Column('password', String(20))
-              )
-
-class User(object):
-    pass
-
-
-class AutoConnectEngineTest1(PersistTest):
-
-    def setUp(self):
-        clear_mappers()
-        objectstore.clear()
-        
-    def test_engine_connect(self):
-        users.create()
-        assign_mapper(User, users)
-        try:
-            trans = objectstore.begin()
-
-            user = User()
-            user.user_name='fred'
-            user.password='*'
-            trans.commit()
-
-            # select
-            sqluser = User.select_by(user_name='fred')[0]
-            assert sqluser.user_name == 'fred'
-
-            # modify
-            sqluser.user_name = 'fred jones'
-
-            # commit - saves everything that changed
-            objectstore.commit()
-        
-            allusers = [ user.user_name for user in User.select() ]
-            assert allusers == [ 'fred jones' ]
-        finally:
-            users.drop()
-
-
-        
-        
-if __name__ == "__main__":
-    testbase.main()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

test/base/__init__.py

Empty file added.

test/base/alltests.py

+import testbase
+import unittest
+
+def suite():
+    modules_to_test = (
+        # core utilities
+        'base.historyarray',
+        'base.attributes', 
+        'base.dependency',
+        )
+    alltests = unittest.TestSuite()
+    for name in modules_to_test:
+        mod = __import__(name)
+        for token in name.split('.')[1:]:
+            mod = getattr(mod, token)
+        alltests.addTest(unittest.findTestCases(mod, suiteClass=None))
+    return alltests
+
+
+if __name__ == '__main__':
+    testbase.runTests(suite())

test/base/attributes.py

+from testbase import PersistTest
+import sqlalchemy.util as util
+import sqlalchemy.attributes as attributes
+import unittest, sys, os
+import pickle
+
+
+class MyTest(object):pass
+    
+class AttributesTest(PersistTest):
+    """tests for the attributes.py module, which deals with tracking attribute changes on an object."""
+    def testbasic(self):
+        class User(object):pass
+        manager = attributes.AttributeManager()
+        manager.register_attribute(User, 'user_id', uselist = False)
+        manager.register_attribute(User, 'user_name', uselist = False)
+        manager.register_attribute(User, 'email_address', uselist = False)
+        
+        u = User()
+        print repr(u.__dict__)
+        
+        u.user_id = 7
+        u.user_name = 'john'
+        u.email_address = 'lala@123.com'
+        
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'lala@123.com')
+        manager.commit(u)
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'lala@123.com')
+
+        u.user_name = 'heythere'
+        u.email_address = 'foo@bar.com'
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'heythere' and u.email_address == 'foo@bar.com')
+        
+        manager.rollback(u)
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'lala@123.com')
+
+    def testpickleness(self):
+        manager = attributes.AttributeManager()
+        manager.register_attribute(MyTest, 'user_id', uselist = False)
+        manager.register_attribute(MyTest, 'user_name', uselist = False)
+        manager.register_attribute(MyTest, 'email_address', uselist = False)
+        x = MyTest()
+        x.user_id=7
+        pickle.dumps(x)
+
+    def testlist(self):
+        class User(object):pass
+        class Address(object):pass
+        manager = attributes.AttributeManager()
+        manager.register_attribute(User, 'user_id', uselist = False)
+        manager.register_attribute(User, 'user_name', uselist = False)
+        manager.register_attribute(User, 'addresses', uselist = True)
+        manager.register_attribute(Address, 'address_id', uselist = False)
+        manager.register_attribute(Address, 'email_address', uselist = False)
+        
+        u = User()
+        print repr(u.__dict__)
+
+        u.user_id = 7
+        u.user_name = 'john'
+        u.addresses = []
+        a = Address()
+        a.address_id = 10
+        a.email_address = 'lala@123.com'
+        u.addresses.append(a)
+
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
+        manager.commit(u, a)
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
+
+        u.user_name = 'heythere'
+        a = Address()
+        a.address_id = 11
+        a.email_address = 'foo@bar.com'
+        u.addresses.append(a)
+        print repr(u.__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'heythere' and u.addresses[0].email_address == 'lala@123.com' and u.addresses[1].email_address == 'foo@bar.com')
+
+        manager.rollback(u, a)
+        print repr(u.__dict__)
+        print repr(u.addresses[0].__dict__)
+        self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
+        self.assert_(len(u.addresses.unchanged_items()) == 1)
+
+    def testbackref(self):
+        class Student(object):pass
+        class Course(object):pass
+        manager = attributes.AttributeManager()
+        manager.register_attribute(Student, 'courses', uselist=True, extension=attributes.GenericBackrefExtension('students'))
+        manager.register_attribute(Course, 'students', uselist=True, extension=attributes.GenericBackrefExtension('courses'))
+        
+        s = Student()
+        c = Course()
+        s.courses.append(c)
+        self.assert_(c.students == [s])
+        s.courses.remove(c)
+        self.assert_(c.students == [])
+        
+        (s1, s2, s3) = (Student(), Student(), Student())
+        c.students = [s1, s2, s3]
+        self.assert_(s2.courses == [c])
+        self.assert_(s1.courses == [c])
+        s1.courses.remove(c)
+        self.assert_(c.students == [s2,s3])
+        
+        
+        class Post(object):pass
+        class Blog(object):pass
+        
+        manager.register_attribute(Post, 'blog', uselist=False, extension=attributes.GenericBackrefExtension('posts'))
+        manager.register_attribute(Blog, 'posts', uselist=True, extension=attributes.GenericBackrefExtension('blog'))
+        b = Blog()
+        (p1, p2, p3) = (Post(), Post(), Post())
+        b.posts.append(p1)
+        b.posts.append(p2)
+        b.posts.append(p3)
+        self.assert_(b.posts == [p1, p2, p3])
+        self.assert_(p2.blog is b)
+        
+        p3.blog = None
+        self.assert_(b.posts == [p1, p2])
+        p4 = Post()
+        p4.blog = b
+        self.assert_(b.posts == [p1, p2, p4])
+        
+
+        class Port(object):pass
+        class Jack(object):pass
+        manager.register_attribute(Port, 'jack', uselist=False, extension=attributes.GenericBackrefExtension('port'))
+        manager.register_attribute(Jack, 'port', uselist=False, extension=attributes.GenericBackrefExtension('jack'))
+        p = Port()
+        j = Jack()
+        p.jack = j
+        self.assert_(j.port is p)
+        self.assert_(p.jack is not None)
+        
+        j.port = None
+        self.assert_(p.jack is None)
+
+    def testinheritance(self):
+        """tests that attributes are polymorphic"""
+        class Foo(object):pass
+        class Bar(Foo):pass
+        
+        manager = attributes.AttributeManager()
+        
+        def func1():
+            return "this is the foo attr"
+        def func2():
+            return "this is the bar attr"
+        def func3():
+            return "this is the shared attr"
+        manager.register_attribute(Foo, 'element', uselist=False, callable_=lambda o:func1)
+        manager.register_attribute(Foo, 'element2', uselist=False, callable_=lambda o:func3)
+        manager.register_attribute(Bar, 'element', uselist=False, callable_=lambda o:func2)
+        
+        x = Foo()
+        y = Bar()
+        assert x.element == 'this is the foo attr'
+        assert y.element == 'this is the bar attr'
+        assert x.element2 == 'this is the shared attr'
+        assert y.element2 == 'this is the shared attr'
+        
+if __name__ == "__main__":
+    unittest.main()

test/base/dependency.py

+from testbase import PersistTest
+import sqlalchemy.orm.topological as topological
+import unittest, sys, os
+
+
+# TODO:  need assertion conditions in this suite
+
+
+class DependencySorter(topological.QueueDependencySorter):pass
+    
+class thingy(object):
+    def __init__(self, name):
+        self.name = name
+    def __repr__(self):
+        return "thingy(%d, %s)" % (id(self), self.name)
+    def __str__(self):
+        return repr(self)
+        
+class DependencySortTest(PersistTest):
+    def testsort(self):
+        rootnode = thingy('root')
+        node2 = thingy('node2')
+        node3 = thingy('node3')
+        node4 = thingy('node4')
+        subnode1 = thingy('subnode1')
+        subnode2 = thingy('subnode2')
+        subnode3 = thingy('subnode3')
+        subnode4 = thingy('subnode4')
+        subsubnode1 = thingy('subsubnode1')
+        tuples = [
+            (subnode3, subsubnode1),
+            (node2, subnode1),
+            (node2, subnode2),
+            (rootnode, node2),
+            (rootnode, node3),
+            (rootnode, node4),
+            (node4, subnode3),
+            (node4, subnode4)
+        ]
+        head = DependencySorter(tuples, []).sort()
+        print "\n" + str(head)
+
+    def testsort2(self):
+        node1 = thingy('node1')
+        node2 = thingy('node2')
+        node3 = thingy('node3')
+        node4 = thingy('node4')
+        node5 = thingy('node5')
+        node6 = thingy('node6')
+        node7 = thingy('node7')
+        tuples = [
+            (node1, node2),
+            (node3, node4),
+            (node4, node5),
+            (node5, node6),
+            (node6, node2)
+        ]
+        head = DependencySorter(tuples, [node7]).sort()
+        print "\n" + str(head)
+
+    def testsort3(self):
+        ['Mapper|Keyword|keywords,Mapper|IKAssociation|itemkeywords', 'Mapper|Item|items,Mapper|IKAssociation|itemkeywords']
+        node1 = thingy('keywords')
+        node2 = thingy('itemkeyowrds')
+        node3 = thingy('items')
+        tuples = [
+            (node1, node2),
+            (node3, node2),
+            (node1,node3)
+        ]
+        head1 = DependencySorter(tuples, [node1, node2, node3]).sort()
+        head2 = DependencySorter(tuples, [node3, node1, node2]).sort()
+        head3 = DependencySorter(tuples, [node3, node2, node1]).sort()
+        
+        # TODO: figure out a "node == node2" function
+        #self.assert_(str(head1) == str(head2) == str(head3))
+        print "\n" + str(head1)
+        print "\n" + str(head2)
+        print "\n" + str(head3)
+
+    def testsort4(self):
+        node1 = thingy('keywords')
+        node2 = thingy('itemkeyowrds')
+        node3 = thingy('items')
+        node4 = thingy('hoho')
+        tuples = [
+            (node1, node2),
+            (node4, node1),
+            (node1, node3),
+            (node3, node2)
+        ]
+        head = DependencySorter(tuples, []).sort()
+        print "\n" + str(head)
+
+    def testsort5(self):
+        # this one, depenending on the weather, 
+#    thingy(5780972, node4)  (idself=5781292, idparent=None)
+#     thingy(5780876, node1)  (idself=5781068, idparent=5781292)
+#      thingy(5780908, node2)  (idself=5781164, idparent=5781068)
+#       thingy(5780940, node3)  (idself=5781228, idparent=5781164)
+   
+        node1 = thingy('node1') #thingy('00B94190')
+        node2 = thingy('node2') #thingy('00B94990')
+        node3 = thingy('node3') #thingy('00B9A9B0')
+        node4 = thingy('node4') #thingy('00B4F210')
+        tuples = [
+            (node4, node1),
+            (node1, node2),
+            (node4, node3),
+            (node2, node3),
+            (node4, node2),
+            (node3, node3)
+        ]
+        allitems = [
+            node1,
+            node2,
+            node3,
+            node4
+        ]
+        head = DependencySorter(tuples, allitems).sort()
+        print "\n" + str(head)
+
+    def testcircular(self):
+        node1 = thingy('node1')
+        node2 = thingy('node2')
+        node3 = thingy('node3')
+        node4 = thingy('node4')
+        node5 = thingy('node5')
+        tuples = [
+            (node4, node5),
+            (node5, node4),
+            (node1, node2),
+            (node2, node3),
+            (node3, node1),
+            (node4, node1)
+        ]
+        head = DependencySorter(tuples, []).sort(allow_all_cycles=True)
+        print "\n" + str(head)
+        
+
+if __name__ == "__main__":
+    unittest.main()

test/base/historyarray.py

+from testbase import PersistTest
+import sqlalchemy.util as util
+import unittest, sys, os
+
+class HistoryArrayTest(PersistTest):
+    def testadd(self):
+        a = util.HistoryArraySet()
+        a.append('hi')
+        self.assert_(a == ['hi'])
+        self.assert_(a.added_items() == ['hi'])
+    
+    def testremove(self):
+        a = util.HistoryArraySet()
+        a.append('hi')
+        a.commit()
+        self.assert_(a == ['hi'])
+        self.assert_(a.added_items() == [])
+        a.remove('hi')
+        self.assert_(a == [])
+        self.assert_(a.deleted_items() == ['hi'])
+        
+    def testremoveadded(self):
+        a = util.HistoryArraySet()
+        a.append('hi')
+        a.remove('hi')
+        self.assert_(a.added_items() == [])
+        self.assert_(a.deleted_items() == [])
+        self.assert_(a == [])
+
+    def testaddedremoved(self):
+        a = util.HistoryArraySet()
+        a.append('hi')
+        a.commit()
+        a.remove('hi')
+        self.assert_(a.deleted_items() == ['hi'])
+        a.append('hi')
+        self.assert_(a.added_items() == [])
+        self.assert_(a.deleted_items() == [])
+        self.assert_(a == ['hi'])
+    
+    def testrollback(self):
+        a = util.HistoryArraySet()
+        a.append('hi')
+        a.append('there')
+        a.append('yo')
+        a.commit()
+        before = repr(a.data)
+        print repr(a.data)
+        a.remove('there')
+        a.append('lala')
+        a.remove('yo')
+        a.append('yo')
+        after = repr(a.data)
+        print repr(a.data)
+        a.rollback()
+        print repr(a.data)
+        self.assert_(before == repr(a.data))
+        
+    def testarray(self):
+        a = util.HistoryArraySet()
+        a.append('hi')
+        a.append('there')
+        self.assert_(a[0] == 'hi' and a[1] == 'there')
+        del a[1]
+        self.assert_(a == ['hi'])
+        a.append('hi')
+        a.append('there')
+        a[3:4] = ['yo', 'hi']
+        self.assert_(a == ['hi', 'there', 'yo'])    
+if __name__ == "__main__":
+    unittest.main()

test/cascade.py

-import testbase, tables
-import unittest, sys, datetime
-
-from sqlalchemy.ext.sessioncontext import SessionContext
-from sqlalchemy import *
-
-class O2MCascadeTest(testbase.AssertMixin):
-    def tearDown(self):
-        ctx.current.clear()
-        tables.delete()
-
-    def tearDownAll(self):
-        clear_mappers()
-        tables.drop()
-
-    def setUpAll(self):
-        global ctx, data
-        ctx = SessionContext(lambda: create_session(echo_uow=True))
-        tables.create()
-        mapper(tables.User, tables.users, properties = dict(
-            address = relation(mapper(tables.Address, tables.addresses), lazy = False, uselist = False, private = True),
-            orders = relation(
-                mapper(tables.Order, tables.orders, properties = dict (
-                    items = relation(mapper(tables.Item, tables.orderitems), lazy = False, uselist =True, private = True)
-                )), 
-                lazy = True, uselist = True, private = True)
-        ))
-
-    def setUp(self):
-        global data
-        data = [tables.User,
-            {'user_name' : 'ed', 
-                'address' : (tables.Address, {'email_address' : 'foo@bar.com'}),
-                'orders' : (tables.Order, [
-                    {'description' : 'eds 1st order', 'items' : (tables.Item, [{'item_name' : 'eds o1 item'}, {'item_name' : 'eds other o1 item'}])}, 
-                    {'description' : 'eds 2nd order', 'items' : (tables.Item, [{'item_name' : 'eds o2 item'}, {'item_name' : 'eds other o2 item'}])}
-                 ])
-            },
-            {'user_name' : 'jack', 
-                'address' : (tables.Address, {'email_address' : 'jack@jack.com'}),
-                'orders' : (tables.Order, [
-                    {'description' : 'jacks 1st order', 'items' : (tables.Item, [{'item_name' : 'im a lumberjack'}, {'item_name' : 'and im ok'}])}
-                 ])
-            },
-            {'user_name' : 'foo', 
-                'address' : (tables.Address, {'email_address': 'hi@lala.com'}),
-                'orders' : (tables.Order, [
-                    {'description' : 'foo order', 'items' : (tables.Item, [])}, 
-                    {'description' : 'foo order 2', 'items' : (tables.Item, [{'item_name' : 'hi'}])}, 
-                    {'description' : 'foo order three', 'items' : (tables.Item, [{'item_name' : 'there'}])}
-                ])
-            }        
-        ]
-
-        for elem in data[1:]:
-            u = tables.User()
-            ctx.current.save(u)
-            u.user_name = elem['user_name']
-            u.address = tables.Address()
-            u.address.email_address = elem['address'][1]['email_address']
-            u.orders = []
-            for order in elem['orders'][1]:
-                o = tables.Order()
-                o.isopen = None
-                o.description = order['description']
-                u.orders.append(o)
-                o.items = []
-                for item in order['items'][1]:
-                    i = tables.Item()
-                    i.item_name = item['item_name']
-                    o.items.append(i)
-
-        ctx.current.flush()
-        ctx.current.clear()
-
-        
-    def testdelete(self):
-        l = ctx.current.query(tables.User).select()
-        for u in l:
-            self.echo( repr(u.orders))
-        self.assert_result(l, data[0], *data[1:])
-
-        self.echo("\n\n\n")
-        ids = (l[0].user_id, l[2].user_id)
-        ctx.current.delete(l[0])
-        ctx.current.delete(l[2])
-
-        ctx.current.flush()
-        self.assert_(tables.orders.count(tables.orders.c.user_id.in_(*ids)).scalar() == 0)
-        self.assert_(tables.orderitems.count(tables.orders.c.user_id.in_(*ids)  &(tables.orderitems.c.order_id==tables.orders.c.order_id)).scalar() == 0)
-        self.assert_(tables.addresses.count(tables.addresses.c.user_id.in_(*ids)).scalar() == 0)
-        self.assert_(tables.users.count(tables.users.c.user_id.in_(*ids)).scalar() == 0)
-    
-
-    def testorphan(self):
-        l = ctx.current.query(tables.User).select()
-        jack = l[1]
-        jack.orders[:] = []
-
-        ids = [jack.user_id]
-        self.assert_(tables.orders.count(tables.orders.c.user_id.in_(*ids)).scalar() == 1)
-        self.assert_(tables.orderitems.count(tables.orders.c.user_id.in_(*ids)  &(tables.orderitems.c.order_id==tables.orders.c.order_id)).scalar() == 2)
-
-        ctx.current.flush()
-
-        self.assert_(tables.orders.count(tables.orders.c.user_id.in_(*ids)).scalar() == 0)
-        self.assert_(tables.orderitems.count(tables.orders.c.user_id.in_(*ids)  &(tables.orderitems.c.order_id==tables.orders.c.order_id)).scalar() == 0)
-
-
-class M2OCascadeTest(testbase.AssertMixin):
-    def tearDown(self):
-        ctx.current.clear()
-        for t in metadata.table_iterator(reverse=True):
-            t.delete().execute()
-            
-    def tearDownAll(self):
-        clear_mappers()
-        metadata.drop_all()
-        
-    def setUpAll(self):
-        global ctx, data, metadata, User, Pref
-        ctx = SessionContext(create_session)
-        metadata = BoundMetaData(testbase.db)
-        prefs = Table('prefs', metadata, 
-            Column('prefs_id', Integer, Sequence('prefs_id_seq', optional=True), primary_key=True),
-            Column('prefs_data', String(40)))
-            
-        users = Table('users', metadata,
-            Column('user_id', Integer, Sequence('user_id_seq', optional=True), primary_key = True),
-            Column('user_name', String(40)),
-            Column('pref_id', Integer, ForeignKey('prefs.prefs_id'))
-        )
-        class User(object):
-            pass
-        class Pref(object):
-            pass
-        metadata.create_all()
-        mapper(User, users, properties = dict(
-            pref = relation(mapper(Pref, prefs), lazy=False, cascade="all, delete-orphan")
-        ))
-
-    def setUp(self):
-        global data
-        data = [User,
-            {'user_name' : 'ed', 
-                'pref' : (Pref, {'prefs_data' : 'pref 1'}),
-            },
-            {'user_name' : 'jack', 
-                'pref' : (Pref, {'prefs_data' : 'pref 2'}),
-            },
-            {'user_name' : 'foo', 
-                'pref' : (Pref, {'prefs_data' : 'pref 3'}),
-            }        
-        ]
-
-        for elem in data[1:]:
-            u = User()
-            ctx.current.save(u)
-            u.user_name = elem['user_name']
-            u.pref = Pref()
-            u.pref.prefs_data = elem['pref'][1]['prefs_data']
-
-        ctx.current.flush()
-        ctx.current.clear()
-
-    def testorphan(self):
-        l = ctx.current.query(User).select()
-        jack = l[1]
-        jack.pref = None
-        ctx.current.flush()
-
-if __name__ == "__main__":
-    testbase.main()        

test/case_statement.py

-import sys
-import testbase
-from sqlalchemy import *
-
-
-class CaseTest(testbase.PersistTest):
-
-    def setUpAll(self):
-        global info_table
-        info_table = Table('infos', testbase.db,
-        	Column('pk', Integer, primary_key=True),
-        	Column('info', String))
-
-        info_table.create()
-
-        info_table.insert().execute(
-        	{'pk':1, 'info':'pk_1_data'},
-        	{'pk':2, 'info':'pk_2_data'},
-        	{'pk':3, 'info':'pk_3_data'},
-        	{'pk':4, 'info':'pk_4_data'},
-    	    {'pk':5, 'info':'pk_5_data'})
-    def tearDownAll(self):
-        info_table.drop()
-    
-    def testcase(self):
-        inner = select([case([[info_table.c.pk < 3, literal('lessthan3', type=String)],
-        	[info_table.c.pk >= 3, literal('gt3', type=String)]]).label('x'),
-        	info_table.c.pk, info_table.c.info], from_obj=[info_table]).alias('q_inner')
-
-        inner_result = inner.execute().fetchall()
-
-        # Outputs:
-        # lessthan3 1 pk_1_data
-        # lessthan3 2 pk_2_data
-        # gt3 3 pk_3_data
-        # gt3 4 pk_4_data
-        # gt3 5 pk_5_data
-        assert inner_result == [
-            ('lessthan3', 1, 'pk_1_data'),
-            ('lessthan3', 2, 'pk_2_data'),
-            ('gt3', 3, 'pk_3_data'),
-            ('gt3', 4, 'pk_4_data'),
-            ('gt3', 5, 'pk_5_data'),
-        ]
-
-        outer = select([inner])
-
-        outer_result = outer.execute().fetchall()
-
-        assert outer_result == [
-            ('lessthan3', 1, 'pk_1_data'),
-            ('lessthan3', 2, 'pk_2_data'),
-            ('gt3', 3, 'pk_3_data'),
-            ('gt3', 4, 'pk_4_data'),
-            ('gt3', 5, 'pk_5_data'),
-        ]
-
-if __name__ == "__main__":
-    testbase.main()

test/cycles.py

-from testbase import PersistTest, AssertMixin
-import unittest, sys, os
-from sqlalchemy import *
-import StringIO
-import testbase
-
-from tables import *
-import tables
-
-# TODO: need assertion conditions in this suite
-
-
-"""test cyclical mapper relationships.  No assertions yet, but run it with postgres and the 
-foreign key checks alone will usually not work if something is wrong"""
-class Tester(object):
-    def __init__(self, data=None):
-        self.data = data
-        print repr(self) + " (%d)" % (id(self))
-    def __repr__(self):
-        return "%s(%s)" % (self.__class__.__name__, repr(self.data))
-        
-class SelfReferentialTest(AssertMixin):
-    """tests a self-referential mapper, with an additional list of child objects."""
-    def setUpAll(self):
-        global t1, t2, metadata
-        metadata = BoundMetaData(testbase.db)
-        t1 = Table('t1', metadata, 
-            Column('c1', Integer, primary_key=True),
-            Column('parent_c1', Integer, ForeignKey('t1.c1')),
-            Column('data', String(20))
-        )
-        t2 = Table('t2', metadata,
-            Column('c1', Integer, primary_key=True),
-            Column('c1id', Integer, ForeignKey('t1.c1')),
-            Column('data', String(20))
-        )
-        metadata.create_all()
-    def tearDownAll(self):
-        metadata.drop_all()
-    def setUp(self):
-        clear_mappers()
-    
-    def testsingle(self):
-        class C1(Tester):
-            pass
-        m1 = mapper(C1, t1, properties = {
-            'c1s':relation(C1, private=True),
-            'parent':relation(C1, primaryjoin=t1.c.parent_c1==t1.c.c1, foreignkey=t1.c.c1, lazy=True, uselist=False)
-        })
-        a = C1('head c1')
-        a.c1s.append(C1('another c1'))
-        sess = create_session(echo_uow=False)
-        sess.save(a)
-        sess.flush()
-        sess.delete(a)
-        sess.flush()
-        
-    def testcycle(self):
-        class C1(Tester):
-            pass
-        class C2(Tester):
-            pass
-        
-        m1 = mapper(C1, t1, properties = {
-            'c1s' : relation(C1, private=True),
-            'c2s' : relation(mapper(C2, t2), private=True)
-        })
-
-        a = C1('head c1')
-        a.c1s.append(C1('child1'))
-        a.c1s.append(C1('child2'))
-        a.c1s[0].c1s.append(C1('subchild1'))
-        a.c1s[0].c1s.append(C1('subchild2'))
-        a.c1s[1].c2s.append(C2('child2 data1'))
-        a.c1s[1].c2s.append(C2('child2 data2'))
-        sess = create_session(echo_uow=False)
-        sess.save(a)
-        sess.flush()
-        
-        sess.delete(a)
-        sess.flush()
-        
-class BiDirectionalOneToManyTest(AssertMixin):
-    """tests two mappers with a one-to-many relation to each other."""
-    def setUpAll(self):
-        global t1, t2, metadata
-        metadata = BoundMetaData(testbase.db)
-        t1 = Table('t1', metadata, 
-            Column('c1', Integer, primary_key=True),
-            Column('c2', Integer, ForeignKey('t2.c1'))
-        )
-        t2 = Table('t2', metadata,
-            Column('c1', Integer, primary_key=True),
-            Column('c2', Integer)
-        )
-        metadata.create_all()
-        t2.c.c2.append_item(ForeignKey('t1.c1'))
-    def tearDownAll(self):
-        t1.drop()
-        t2.drop()
-        #metadata.drop_all()
-    def tearDown(self):
-        clear_mappers()
-    def testcycle(self):
-        class C1(object):pass
-        class C2(object):pass
-        
-        m2 = mapper(C2, t2)
-        m1 = mapper(C1, t1, properties = {
-            'c2s' : relation(m2, primaryjoin=t1.c.c2==t2.c.c1, uselist=True)
-        })
-        m2.add_property('c1s', relation(m1, primaryjoin=t2.c.c2==t1.c.c1, uselist=True))
-        a = C1()
-        b = C2()
-        c = C1()
-        d = C2()
-        e = C2()
-        f = C2()
-        a.c2s.append(b)
-        d.c1s.append(c)
-        b.c1s.append(c)
-        sess = create_session()
-        [sess.save(x) for x in [a,b,c,d,e,f]]
-        sess.flush()
-
-class BiDirectionalOneToManyTest2(AssertMixin):
-    """tests two mappers with a one-to-many relation to each other, with a second one-to-many on one of the mappers"""
-    def setUpAll(self):
-        global t1, t2, t3, metadata
-        metadata = BoundMetaData(testbase.db)
-        t1 = Table('t1', metadata, 
-            Column('c1', Integer, primary_key=True),
-            Column('c2', Integer, ForeignKey('t2.c1')),
-        )
-        t2 = Table('t2', metadata,
-            Column('c1', Integer, primary_key=True),
-            Column('c2', Integer),
-        )
-        t2.create()
-        t1.create()
-        t2.c.c2.append_item(ForeignKey('t1.c1'))
-        t3 = Table('t1_data', metadata, 
-            Column('c1', Integer, primary_key=True),
-            Column('t1id', Integer, ForeignKey('t1.c1')),
-            Column('data', String(20)))
-        t3.create()
-        
-    def tearDown(self):
-        clear_mappers()
-
-    def tearDownAll(self):
-        t3.drop()
-        t1.drop()
-        t2.drop()
-        
-    def testcycle(self):
-        class C1(object):pass
-        class C2(object):pass
-        class C1Data(object):
-            def __init__(self, data=None):
-                self.data = data
-                
-        m2 = mapper(C2, t2)
-        m1 = mapper(C1, t1, properties = {
-            'c2s' : relation(m2, primaryjoin=t1.c.c2==t2.c.c1, uselist=True),
-            'data' : relation(mapper(C1Data, t3))
-        })
-        m2.add_property('c1s', relation(m1, primaryjoin=t2.c.c2==t1.c.c1, uselist=True))
-        
-        a = C1()
-        b = C2()
-        c = C1()
-        d = C2()
-        e = C2()
-        f = C2()
-        a.c2s.append(b)
-        d.c1s.append(c)
-        b.c1s.append(c)
-        a.data.append(C1Data('c1data1'))
-        a.data.append(C1Data('c1data2'))
-        c.data.append(C1Data('c1data3'))
-        sess = create_session()
-        [sess.save(x) for x in [a,b,c,d,e,f]]
-        sess.flush()
-
-        sess.delete(d)
-        sess.delete(c)
-        sess.flush()
-
-class OneToManyManyToOneTest(AssertMixin):
-    """tests two mappers, one has a one-to-many on the other mapper, the other has a separate many-to-one relationship to the first.
-    two tests will have a row for each item that is dependent on the other.  without the "post_update" flag, such relationships
-    raise an exception when dependencies are sorted."""
-    def setUpAll(self):
-        global metadata
-        metadata = BoundMetaData(testbase.db)
-        global person    
-        global ball
-        ball = Table('ball', metadata,
-         Column('id', Integer, Sequence('ball_id_seq', optional=True), primary_key=True),
-         Column('person_id', Integer),
-         )
-        person = Table('person', metadata,
-         Column('id', Integer, Sequence('person_id_seq', optional=True), primary_key=True),
-         Column('favoriteBall_id', Integer, ForeignKey('ball.id')),
-#         Column('favoriteBall_id', Integer),
-         )
-
-        ball.create()
-        person.create()
-#        person.c.favoriteBall_id.append_item(ForeignKey('ball.id'))
-        ball.c.person_id.append_item(ForeignKey('person.id'))
-        
-        # make the test more complete for postgres
-        if db.engine.__module__.endswith('postgres'):
-            db.execute("alter table ball add constraint fk_ball_person foreign key (person_id) references person(id)", {})
-    def tearDownAll(self):
-        if db.engine.__module__.endswith('postgres'):
-            db.execute("alter table ball drop constraint fk_ball_person", {})
-        person.drop()
-        ball.drop()
-        
-    def tearDown(self):
-        clear_mappers()
-
-    def testcycle(self):
-        """this test has a peculiar aspect in that it doesnt create as many dependent 
-        relationships as the other tests, and revealed a small glitch in the circular dependency sorting."""
-        class Person(object):
-         pass
-
-        class Ball(object):
-         pass
-
-        Ball.mapper = mapper(Ball, ball)
-        Person.mapper = mapper(Person, person, properties= dict(
-         balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, foreignkey=ball.c.person_id),
-         favorateBall = relation(Ball.mapper, primaryjoin=person.c.favoriteBall_id==ball.c.id, foreignkey=person.c.favoriteBall_id),
-         )
-        )
-
-        print str(Person.mapper.props['balls'].primaryjoin)
-        
-        b = Ball()
-        p = Person()
-        p.balls.append(b)
-        sess = create_session()
-        sess.save(b)
-        sess.save(b)
-        sess.flush()
-
-    def testpostupdate_m2o(self):
-        """tests a cycle between two rows, with a post_update on the many-to-one"""
-        class Person(object):
-         pass
-
-        class Ball(object):
-         pass
-
-        Ball.mapper = mapper(Ball, ball)
-        Person.mapper = mapper(Person, person, properties= dict(
-         balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, foreignkey=ball.c.person_id, post_update=False, private=True),
-         favorateBall = relation(Ball.mapper, primaryjoin=person.c.favoriteBall_id==ball.c.id, foreignkey=person.c.favoriteBall_id, post_update=True),
-         )
-        )
-
-        print str(Person.mapper.props['balls'].primaryjoin)
-
-        b = Ball()
-        p = Person()
-        p.balls.append(b)
-        p.balls.append(Ball())
-        p.balls.append(Ball())
-        p.balls.append(Ball())
-        p.favorateBall = b
-        sess = create_session()
-        sess.save(b)
-        sess.save(p)
-        
-        self.assert_sql(db, lambda: sess.flush(), [
-            (
-                "INSERT INTO person (favoriteBall_id) VALUES (:favoriteBall_id)",
-                {'favoriteBall_id': None}
-            ),
-            (
-                "INSERT INTO ball (person_id) VALUES (:person_id)",
-                lambda ctx:{'person_id':p.id}
-            ),
-            (
-                "INSERT INTO ball (person_id) VALUES (:person_id)",
-                lambda ctx:{'person_id':p.id}
-            ),
-            (
-                "INSERT INTO ball (person_id) VALUES (:person_id)",
-                lambda ctx:{'person_id':p.id}
-            ),
-            (
-                "INSERT INTO ball (person_id) VALUES (:person_id)",
-                lambda ctx:{'person_id':p.id}
-            ),
-            (
-                "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id",
-                lambda ctx:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id}
-            )
-        ], 
-        with_sequences= [
-                (
-                    "INSERT INTO person (id, favoriteBall_id) VALUES (:id, :favoriteBall_id)",
-                    lambda ctx:{'id':ctx.last_inserted_ids()[0], 'favoriteBall_id': None}
-                ),
-                (
-                    "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                    lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id}
-                ),
-                (
-                    "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                    lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id}
-                ),
-                (
-                    "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                    lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id}
-                ),
-                (
-                    "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                    lambda ctx:{'id':ctx.last_inserted_ids()[0],'person_id':p.id}
-                ),
-                # heres the post update 
-                (
-                    "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id",
-                    lambda ctx:{'favoriteBall_id':p.favorateBall.id,'person_id':p.id}
-                )
-            ])
-        sess.delete(p)
-        self.assert_sql(db, lambda: sess.flush(), [
-            # heres the post update (which is a pre-update with deletes)
-            (
-                "UPDATE person SET favoriteBall_id=:favoriteBall_id WHERE person.id = :person_id",
-                lambda ctx:{'person_id': p.id, 'favoriteBall_id': None}
-            ),
-            (
-                "DELETE FROM ball WHERE ball.id = :id",
-                None
-                # order cant be predicted, but something like:
-                #lambda ctx:[{'id': 1L}, {'id': 4L}, {'id': 3L}, {'id': 2L}]
-            ),
-            (
-                "DELETE FROM person WHERE person.id = :id",
-                lambda ctx:[{'id': p.id}]
-            )
-
-
-        ])
-        
-    def testpostupdate_o2m(self):
-        """tests a cycle between two rows, with a post_update on the one-to-many"""
-        class Person(object):
-         pass
-
-        class Ball(object):
-         pass
-
-        Ball.mapper = mapper(Ball, ball)
-        Person.mapper = mapper(Person, person, properties= dict(
-         balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, foreignkey=ball.c.person_id, private=True, post_update=True),
-         favorateBall = relation(Ball.mapper, primaryjoin=person.c.favoriteBall_id==ball.c.id, foreignkey=person.c.favoriteBall_id),
-         )
-        )
-
-        print str(Person.mapper.props['balls'].primaryjoin)
-
-        b = Ball()
-        p = Person()
-        p.balls.append(b)
-        b2 = Ball()
-        p.balls.append(b2)
-        b3 = Ball()
-        p.balls.append(b3)
-        b4 = Ball()
-        p.balls.append(b4)
-        p.favorateBall = b
-        sess = create_session()
-        [sess.save(x) for x in [b,p,b2,b3,b4]]
-
-        self.assert_sql(db, lambda: sess.flush(), [
-                (
-                    "INSERT INTO ball (person_id) VALUES (:person_id)",
-                    {'person_id':None}
-                ),
-                (
-                    "INSERT INTO ball (person_id) VALUES (:person_id)",
-                    {'person_id':None}
-                ),
-                (
-                    "INSERT INTO ball (person_id) VALUES (:person_id)",
-                    {'person_id':None}
-                ),
-                (
-                    "INSERT INTO ball (person_id) VALUES (:person_id)",
-                    {'person_id':None}
-                ),
-                (
-                    "INSERT INTO person (favoriteBall_id) VALUES (:favoriteBall_id)",
-                    lambda ctx:{'favoriteBall_id':b.id}
-                ),
-                # heres the post update on each one-to-many item
-                (
-                    "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                    lambda ctx:{'person_id':p.id,'ball_id':b.id}
-                ),
-                (
-                    "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                    lambda ctx:{'person_id':p.id,'ball_id':b2.id}
-                ),
-                (
-                    "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                    lambda ctx:{'person_id':p.id,'ball_id':b3.id}
-                ),
-                (
-                    "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                    lambda ctx:{'person_id':p.id,'ball_id':b4.id}
-                ),
-        ],
-        with_sequences=[
-            (
-                "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None}
-            ),
-            (
-                "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None}
-            ),
-            (
-                "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None}
-            ),
-            (
-                "INSERT INTO ball (id, person_id) VALUES (:id, :person_id)",
-                lambda ctx:{'id':ctx.last_inserted_ids()[0], 'person_id':None}
-            ),
-            (
-                "INSERT INTO person (id, favoriteBall_id) VALUES (:id, :favoriteBall_id)",
-                lambda ctx:{'id':ctx.last_inserted_ids()[0], 'favoriteBall_id':b.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id':p.id,'ball_id':b.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id':p.id,'ball_id':b2.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id':p.id,'ball_id':b3.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id':p.id,'ball_id':b4.id}
-            ),
-        ])
-
-        sess.delete(p)
-        self.assert_sql(db, lambda: sess.flush(), [
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id': None, 'ball_id': b.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id': None, 'ball_id': b2.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id': None, 'ball_id': b3.id}
-            ),
-            (
-                "UPDATE ball SET person_id=:person_id WHERE ball.id = :ball_id",
-                lambda ctx:{'person_id': None, 'ball_id': b4.id}
-            ),
-            (
-                "DELETE FROM person WHERE person.id = :id",
-                lambda ctx:[{'id':p.id}]
-            ),
-            (
-                "DELETE FROM ball WHERE ball.id = :id",
-                None
-                # the order of deletion is not predictable, but its roughly:
-                # lambda ctx:[{'id': b.id}, {'id': b2.id}, {'id': b3.id}, {'id': b4.id}]
-            )
-        ])
-        
-if __name__ == "__main__":
-    testbase.main()        
-

test/defaults.py

-from testbase import PersistTest
-import sqlalchemy.util as util
-import unittest, sys, os
-import sqlalchemy.schema as schema
-import testbase
-from sqlalchemy import *
-import sqlalchemy
-
-db = testbase.db
-
-class DefaultTest(PersistTest):
-
-    def setUpAll(self):
-        global t, f, f2, ts, currenttime
-        x = {'x':50}
-        def mydefault():
-            x['x'] += 1
-            return x['x']
-
-        use_function_defaults = db.engine.name == 'postgres' or db.engine.name == 'oracle'
-        is_oracle = db.engine.name == 'oracle'
- 
-        # select "count(1)" returns different results on different DBs
-        # also correct for "current_date" compatible as column default, value differences
-        currenttime = func.current_date(type=Date, engine=db);
-        if is_oracle:
-            ts = db.func.trunc(func.sysdate(), column("'DAY'")).scalar()
-            f = select([func.count(1) + 5], engine=db).scalar()
-            f2 = select([func.count(1) + 14], engine=db).scalar()
-            # TODO: engine propigation across nested functions not working
-            currenttime = func.trunc(currenttime, column("'DAY'"), engine=db)
-            def1 = currenttime
-            def2 = func.trunc(text("sysdate"), column("'DAY'"))
-            deftype = Date
-        elif use_function_defaults:
-            f = select([func.count(1) + 5], engine=db).scalar()
-            f2 = select([func.count(1) + 14], engine=db).scalar()
-            def1 = currenttime
-            def2 = text("current_date")
-            deftype = Date
-            ts = db.func.current_date().scalar()
-        else:
-            f = select([func.count(1) + 5], engine=db).scalar()
-            f2 = select([func.count(1) + 14], engine=db).scalar()
-            def1 = def2 = "3"
-            ts = 3
-            deftype = Integer
-            
-        t = Table('default_test1', db,
-            # python function
-            Column('col1', Integer, primary_key=True, default=mydefault),
-            
-            # python literal
-            Column('col2', String(20), default="imthedefault", onupdate="im the update"),
-            
-            # preexecute expression
-            Column('col3', Integer, default=func.count(1) + 5, onupdate=func.count(1) + 14),
-            
-            # SQL-side default from sql expression
-            Column('col4', deftype, PassiveDefault(def1)),
-            
-            # SQL-side default from literal expression
-            Column('col5', deftype, PassiveDefault(def2)),
-            
-            # preexecute + update timestamp
-            Column('col6', Date, default=currenttime, onupdate=currenttime)
-        )
-        t.create()
-
-    def tearDownAll(self):
-        t.drop()
-    
-    def tearDown(self):
-        t.delete().execute()
-        
-    def teststandalone(self):
-        c = db.engine.contextual_connect()
-        x = c.execute(t.c.col1.default)
-        y = t.c.col2.default.execute()
-        z = c.execute(t.c.col3.default)
-        self.assert_(50 <= x <= 57)
-        self.assert_(y == 'imthedefault')
-        self.assert_(z == f)
-        # mysql/other db's return 0 or 1 for count(1)
-        self.assert_(5 <= z <= 6)
-        
-    def testinsert(self):
-        r = t.insert().execute()
-        self.assert_(r.lastrow_has_defaults())
-        t.insert().execute()
-        t.insert().execute()
-
-        ctexec = currenttime.scalar()
-        self.echo("Currenttime "+ repr(ctexec))
-        l = t.select().execute()
-        self.assert_(l.fetchall() == [(51, 'imthedefault', f, ts, ts, ctexec), (52, 'imthedefault', f, ts, ts, ctexec), (53, 'imthedefault', f, ts, ts, ctexec)])
-
-    def testinsertvalues(self):
-        t.insert(values={'col3':50}).execute()
-        l = t.select().execute()
-        self.assert_(l.fetchone()['col3'] == 50)
-        
-        
-    def testupdate(self):
-        r = t.insert().execute()
-        pk = r.last_inserted_ids()[0]
-        t.update(t.c.col1==pk).execute(col4=None, col5=None)
-        ctexec = currenttime.scalar()
-        self.echo("Currenttime "+ repr(ctexec))
-        l = t.select(t.c.col1==pk).execute()
-        l = l.fetchone()
-        self.assert_(l == (pk, 'im the update', f2, None, None, ctexec))
-        # mysql/other db's return 0 or 1 for count(1)
-        self.assert_(14 <= f2 <= 15)
-
-    def testupdatevalues(self):
-        r = t.insert().execute()
-        pk = r.last_inserted_ids()[0]
-        t.update(t.c.col1==pk, values={'col3': 55}).execute()
-        l = t.select(t.c.col1==pk).execute()
-        l = l.fetchone()
-        self.assert_(l['col3'] == 55)
-        
-class SequenceTest(PersistTest):
-
-    def setUpAll(self):
-        if testbase.db.engine.name != 'postgres' and testbase.db.engine.name != 'oracle':
-            return
-        global cartitems
-        cartitems = Table("cartitems", db, 
-            Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True),
-            Column("description", String(40)),
-            Column("createdate", DateTime())
-        )
-        
-        cartitems.create()
-    
-    @testbase.supported('postgres', 'oracle')
-    def testsequence(self):
-        cartitems.insert().execute(description='hi')
-        cartitems.insert().execute(description='there')
-        cartitems.insert().execute(description='lala')
-        
-        cartitems.select().execute().fetchall()
-   
-   
-    @testbase.supported('postgres', 'oracle')
-    def teststandalone(self):
-        s = Sequence("my_sequence", metadata=testbase.db)
-        s.create()
-        try:
-            x = s.execute()
-            self.assert_(x == 1)
-        finally:
-            s.drop()
-    
-    @testbase.supported('postgres', 'oracle')
-    def teststandalone2(self):
-        x = cartitems.c.cart_id.sequence.execute()
-        self.assert_(1 <= x <= 4)
-        
-    def tearDownAll(self): 
-        if testbase.db.engine.name != 'postgres' and testbase.db.engine.name != 'oracle':
-            return
-        cartitems.drop()
-