Author: johannes Date: 2007-09-26 08:35:03 -0500 (Wed, 26 Sep 2007) New Revision: 9784
Modified: trunk/gnue-appserver/src/data.py trunk/gnue-appserver/tests/data.py Log: Fixed dirty reads ?! Modified: trunk/gnue-appserver/src/data.py =================================================================== --- trunk/gnue-appserver/src/data.py 2007-09-26 12:44:15 UTC (rev 9783) +++ trunk/gnue-appserver/src/data.py 2007-09-26 13:35:03 UTC (rev 9784) @@ -1264,17 +1264,17 @@ next_add = None for backend_record in [self.__buildRecords(r) for r in self.__rawdata]: - # if there are any uncommitted new records, insert them at the # correct position if next_add is not None: gnue_id = backend_record._row - while next_add < SortRecord(gnue_id, + while next_add and next_add < SortRecord(gnue_id, self.__getSortSequence(gnue_id)): self.__add.pop(0) self.__added += 1 yield record(self.__cache, self.__connections, - self.__database, self.__getMasterTable(), next_add) + self.__database, self.__getMasterTable(), + next_add.row) if self.__add: next_add = SortRecord(self.__add[0], self.__getSortSequence(self.__add[0])) @@ -1287,7 +1287,17 @@ yield backend_record + # If there new records left yield them as well + while self.__add: + next = self.__add[0] + self.__add.pop(0) + self.__added += 1 + yield record(self.__cache, self.__connections, + self.__database, self.__getMasterTable(), next) + + + # --------------------------------------------------------------------------- # Close the record set # --------------------------------------------------------------------------- @@ -1650,16 +1660,17 @@ result = [] append = result.append - for element in self.__order: - field = element ['name'] - direction = element.get ('descending') or False - ignorecase = element.get ('ignorecase') or False - value = self.__getPathValue (row, self.__getPropertyPath (field)) + if self.__order: + for element in self.__order: + field = element ['name'] + direction = element.get ('descending') or False + ignorecase = element.get ('ignorecase') or False + value = self.__getPathValue (row, self.__getPropertyPath (field)) - if ignorecase and hasattr (value, 'lower'): - value = value.lower () + if ignorecase and hasattr (value, 'lower'): + value = value.lower () - append ((value, direction)) + append ((value, direction)) return result Modified: trunk/gnue-appserver/tests/data.py =================================================================== --- trunk/gnue-appserver/tests/data.py 2007-09-26 12:44:15 UTC (rev 9783) +++ trunk/gnue-appserver/tests/data.py 2007-09-26 13:35:03 UTC (rev 9784) @@ -31,674 +31,683 @@ # TestCase for _cache class # ============================================================================= -class CachingTestCase (unittest.TestCase): - """ - TestCase for the low-level class _cache - """ - - # --------------------------------------------------------------------------- - # Test Case Setup - # --------------------------------------------------------------------------- - - def setUp (self): +class CachingTestCase(unittest.TestCase): """ - Create a new instance of the cache + TestCase for the low-level class _cache """ - self.cache = data._cache () + # ------------------------------------------------------------------------- + # Test Case Setup + # ------------------------------------------------------------------------- + def setUp(self): + """ + Create a new instance of the cache + """ - # --------------------------------------------------------------------------- - # Is the cache cleaned well ? - # --------------------------------------------------------------------------- + self.cache = data._cache() - def test_cacheClean (self): - """ - Test if the cache is cleaned well - """ - table = u'FOO' - rows = [u'100', u'200'] + # ------------------------------------------------------------------------- + # Is the cache cleaned well ? + # ------------------------------------------------------------------------- - # Write data into clean cache. The cache should not be dirty at all - for row in rows: - self.cache.write_clean (table, row, [(u'foo', 1)]) + def test_cacheClean(self): + """ + Test if the cache is cleaned well + """ - result = self.__orderTables (self.cache.dirtyTables ()) - self.assertEqual (result, []) - self.assertEqual (self.cache.inserted, {}) - self.assertEqual (self.cache.deleted, {}) + table = u'FOO' + rows = [u'100', u'200'] - # Now write data to dirty cache, which will render the records as 'changed' - for row in rows: - self.cache.write_dirty (table, row, u'foo', 1) + # Write data into clean cache. The cache should not be dirty at all + for row in rows: + self.cache.write_clean(table, row, [(u'foo', 1)]) - self.cache.insertRecord (u'BAR', u'200') - self.cache.initialized (u'BAR', u'200') - self.cache.insertRecord (u'FOO', u'771') - self.cache.initialized (u'FOO', u'771') - self.cache.write_dirty (u'FOO', u'771', u'val', 5) + result = self.__orderTables(self.cache.dirtyTables()) + self.assertEqual(result, []) + self.assertEqual(self.cache.inserted, {}) + self.assertEqual(self.cache.deleted, {}) - result = self.__orderTables (self.cache.dirtyTables ()) - exp = [(u'BAR', u'200', 'initialized', [(u'gnue_id', u'200')]), - (u'FOO', u'100', 'changed', [(u'foo', 1)]), - (u'FOO', u'200', 'changed', [(u'foo', 1)]), - (u'FOO', u'771', 'inserted', [(u'gnue_id', u'771'), (u'val', 5)])] + # Now write data to dirty cache, which will render the records as + # 'changed' + for row in rows: + self.cache.write_dirty(table, row, u'foo', 1) - self.assertEqual (result, exp) - self.assertEqual (self.cache.inserted, {u'BAR-200': (u'BAR', u'200'), - u'FOO-771': (u'FOO', u'771')}) - self.assertEqual (self.cache.deleted, {}) + self.cache.insertRecord(u'BAR', u'200') + self.cache.initialized(u'BAR', u'200') + self.cache.insertRecord(u'FOO', u'771') + self.cache.initialized(u'FOO', u'771') + self.cache.write_dirty(u'FOO', u'771', u'val', 5) - # Cancelling the current atomic operation should make the cache clean again - self.cache.cancel () + result = self.__orderTables(self.cache.dirtyTables()) + exp = [(u'BAR', u'200', 'initialized', [(u'gnue_id', u'200')]), + (u'FOO', u'100', 'changed', [(u'foo', 1)]), + (u'FOO', u'200', 'changed', [(u'foo', 1)]), + (u'FOO', u'771', 'inserted', [(u'gnue_id', u'771'),(u'val', 5)])] - result = self.__orderTables (self.cache.dirtyTables ()) - self.assertEqual (result, []) - self.assertEqual (self.cache.inserted, {}) - self.assertEqual (self.cache.deleted, {}) + self.assertEqual(result, exp) + self.assertEqual(self.cache.inserted, {u'BAR-200': (u'BAR', u'200'), + u'FOO-771': (u'FOO', u'771')}) + self.assertEqual(self.cache.deleted, {}) - # Insert another three records. One is changed into 'inserted' state, - # another one is left at 'initialized' and the last one is left at - # 'initializing' - self.cache.clear () + # Cancelling the current atomic operation should make the cache clean + # again + self.cache.cancel() - self.cache.insertRecord (table, u'1') - self.cache.initialized (table, u'1') - self.cache.write_dirty (table, u'1', u'foo', 1) + result = self.__orderTables(self.cache.dirtyTables()) + self.assertEqual(result, []) + self.assertEqual(self.cache.inserted, {}) + self.assertEqual(self.cache.deleted, {}) - self.cache.insertRecord (table, u'2') - self.cache.initialized (table, u'2') + # Insert another three records. One is changed into 'inserted' state, + # another one is left at 'initialized' and the last one is left at + # 'initializing' + self.cache.clear() - self.cache.insertRecord (table, u'3') + self.cache.insertRecord(table, u'1') + self.cache.initialized(table, u'1') + self.cache.write_dirty(table, u'1', u'foo', 1) - exp = [(table, u'1', 'inserted', [(u'foo', 1), (u'gnue_id', u'1')]), - (table, u'2', 'initialized', [(u'gnue_id', u'2')]), - (table, u'3', 'initializing', [(u'gnue_id', u'3')])] - self.assertEqual (exp, self.__orderTables (self.cache.dirtyTables ())) + self.cache.insertRecord(table, u'2') + self.cache.initialized(table, u'2') - self.cache.makeClean (table, u'1') + self.cache.insertRecord(table, u'3') - # Now clear all 'clean' stuff - exp = [(table, u'2', 'initialized', [(u'gnue_id', u'2')]), - (table, u'3', 'initializing', [(u'gnue_id', u'3')])] - self.cache.clear (True) - self.assertEqual (exp, self.__orderTables (self.cache.dirtyTables ())) + exp = [(table, u'1', 'inserted', [(u'foo', 1), (u'gnue_id', u'1')]), + (table, u'2', 'initialized', [(u'gnue_id', u'2')]), + (table, u'3', 'initializing', [(u'gnue_id', u'3')])] + self.assertEqual(exp, self.__orderTables(self.cache.dirtyTables())) - # And now everything should really go away - self.cache.clear () - self.assertEqual (self.cache.dirtyTables (), {}) + self.cache.makeClean(table, u'1') + # Now clear all 'clean' stuff + exp = [(table, u'2', 'initialized', [(u'gnue_id', u'2')]), + (table, u'3', 'initializing', [(u'gnue_id', u'3')])] + self.cache.clear(True) + self.assertEqual(exp, self.__orderTables(self.cache.dirtyTables())) - - # --------------------------------------------------------------------------- - # Perform insertions - # --------------------------------------------------------------------------- + # And now everything should really go away + self.cache.clear() + self.assertEqual(self.cache.dirtyTables(), {}) - def test_cacheInsertions (self): - """ - Perform variouse test regarding data insertion - """ - self.cache.clear () - table = u'table' - row = u'100' - row2 = u'200' + # ------------------------------------------------------------------------- + # Perform insertions + # ------------------------------------------------------------------------- - # We cannot insert a record if it's already available - self.cache.write_dirty (table, row, u'foo', 1) - self.assertRaises (data.DuplicateRecordError, self.cache.insertRecord, - table, row) + def test_cacheInsertions(self): + """ + Perform variouse test regarding data insertion + """ - self.cache.clear () + self.cache.clear() - # Insert a record, change and confirm it - self.cache.insertRecord (table, row) - self.cache.initialized (table, row) - self.cache.write_dirty (table, row, u'foo', 1) - self.cache.confirm () + table = u'table' + row = u'100' + row2 = u'200' - result = self.__orderTables (self.cache.dirtyTables ()) - exp = [(table, row, 'inserted', [(u'foo', 1), (u'gnue_id', row)])] - self.assertEqual (result, exp) - self.assertEqual (self.cache.inserted, {u'table-100': (table, row)}) - self.assertEqual (self.cache.deleted, {}) + # We cannot insert a record if it's already available + self.cache.write_dirty(table, row, u'foo', 1) + self.assertRaises(data.DuplicateRecordError, self.cache.insertRecord, + table, row) - # change it again, and insert another row. We won't set the latter one to - # initialized so it does not get into 'inserted' state - self.cache.write_dirty (table, row, u'foo', 2) - self.cache.insertRecord (table, row2) - self.cache.write_dirty (table, row2, u'bar', 2) + self.cache.clear() - result = self.__orderTables (self.cache.dirtyTables ()) - exp = [(table, row, 'inserted', [(u'foo', 2), (u'gnue_id', row)]), - (table, row2, 'initializing', [(u'bar', 2), (u'gnue_id', row2)])] - self.assertEqual (result, exp) - self.assertEqual (self.cache.inserted, {u'table-100': (table, row), - u'table-200': (table, row2)}) - self.assertEqual (self.cache.deleted, {}) + # Insert a record, change and confirm it + self.cache.insertRecord(table, row) + self.cache.initialized(table, row) + self.cache.write_dirty(table, row, u'foo', 1) + self.cache.confirm() - # Now delete the second record - self.cache.deleteRecord (table, row2) + result = self.__orderTables(self.cache.dirtyTables()) + exp = [(table, row, 'inserted', [(u'foo', 1), (u'gnue_id', row)])] + self.assertEqual(result, exp) + self.assertEqual(self.cache.inserted, {u'table-100': (table, row)}) + self.assertEqual(self.cache.deleted, {}) - # After deleting the previously inserted record the only record left should - # be the inserted one - result = self.__orderTables (self.cache.dirtyTables ()) - exp = [(table, row, 'inserted', [(u'foo', 2), (u'gnue_id', row)]), - (table, row2, 'deleted', [(u'bar', 2), (u'gnue_id', None)])] - self.assertEqual (result, exp) - self.assertEqual (self.cache.inserted, {u'table-100': (table, row)}) - self.assertEqual (self.cache.deleted, {}) + # change it again, and insert another row. We won't set the latter one to + # initialized so it does not get into 'inserted' state + self.cache.write_dirty(table, row, u'foo', 2) + self.cache.insertRecord(table, row2) + self.cache.write_dirty(table, row2, u'bar', 2) - # cancel the atomic operation. This should give us the state from the last - # call of confirm () - self.cache.cancel () + result = self.__orderTables(self.cache.dirtyTables()) + exp = [(table, row, 'inserted', [(u'foo', 2), (u'gnue_id', row)]), + (table, row2, 'initializing', [(u'bar', 2), (u'gnue_id', row2)])] + self.assertEqual(result, exp) + self.assertEqual(self.cache.inserted, {u'table-100': (table, row), + u'table-200': (table, row2)}) + self.assertEqual(self.cache.deleted, {}) + # Now delete the second record + self.cache.deleteRecord(table, row2) - result = self.__orderTables (self.cache.dirtyTables ()) - exp = [(table, row, 'inserted', [(u'foo', 1), (u'gnue_id', row)])] - self.assertEqual (result, exp) - self.assertEqual (self.cache.inserted, {u'table-100': (table, row)}) - self.assertEqual (self.cache.deleted, {}) + # After deleting the previously inserted record the only record left + # should be the inserted one + result = self.__orderTables(self.cache.dirtyTables()) + exp = [(table, row, 'inserted', [(u'foo', 2), (u'gnue_id', row)]), + (table, row2, 'deleted', [(u'bar', 2), (u'gnue_id', None)])] + self.assertEqual(result, exp) + self.assertEqual(self.cache.inserted, {u'table-100': (table, row)}) + self.assertEqual(self.cache.deleted, {}) - # Now simulate a commit which uses makeClean () to make a given record - # clean - self.cache.makeClean (table, row) - self.assertEqual (self.__orderTables (self.cache.dirtyTables ()), []) - self.assertEqual (self.cache.inserted, {}) - self.assertEqual (self.cache.deleted, {}) + # cancel the atomic operation. This should give us the state from the + # last call of confirm() + self.cache.cancel() - # Add a new record, confirm it and then delete it - self.cache.clear () - self.cache.insertRecord (table, row) - self.cache.initialized (table, row) - self.cache.write_dirty (table, row, u'name', 'foobar') - self.cache.confirm () + result = self.__orderTables(self.cache.dirtyTables()) + exp = [(table, row, 'inserted', [(u'foo', 1), (u'gnue_id', row)])] + self.assertEqual(result, exp) + self.assertEqual(self.cache.inserted, {u'table-100': (table, row)}) + self.assertEqual(self.cache.deleted, {}) - exp = [(table, row, 'inserted', [(u'gnue_id', row), (u'name', 'foobar')])] - self.assertEqual (self.__orderTables (self.cache.dirtyTables ()), exp) - self.assertEqual (self.cache.inserted, {u'table-100': (table, row)}) + # Now simulate a commit which uses makeClean() to make a given record + # clean + self.cache.makeClean(table, row) + self.assertEqual(self.__orderTables(self.cache.dirtyTables()), []) + self.assertEqual(self.cache.inserted, {}) + self.assertEqual(self.cache.deleted, {}) - self.cache.deleteRecord (table, row) + # Add a new record, confirm it and then delete it + self.cache.clear() - exp = [(table, row, 'deleted', [(u'gnue_id', None), (u'name', 'foobar')])] - self.assertEqual (self.__orderTables (self.cache.dirtyTables ()), exp) - self.assertEqual (self.cache.inserted, {}) - self.assertEqual (self.cache.deleted, {}) + self.cache.insertRecord(table, row) + self.cache.initialized(table, row) + self.cache.write_dirty(table, row, u'name', 'foobar') + self.cache.confirm() + exp = [(table, row, 'inserted', [(u'gnue_id', row), (u'name', 'foobar')])] + self.assertEqual(self.__orderTables(self.cache.dirtyTables()), exp) + self.assertEqual(self.cache.inserted, {u'table-100': (table, row)}) + self.cache.deleteRecord(table, row) + exp = [(table, row, 'deleted', + [(u'gnue_id', None), (u'name', 'foobar')])] + self.assertEqual(self.__orderTables(self.cache.dirtyTables()), exp) + self.assertEqual(self.cache.inserted, {}) + self.assertEqual(self.cache.deleted, {}) - # --------------------------------------------------------------------------- - # Perform tests with modifications of records - # --------------------------------------------------------------------------- - def test_cacheModification (self): - """ - Perfrom tests with mixed modification operations - """ + # ------------------------------------------------------------------------- + # Perform tests with modifications of records + # ------------------------------------------------------------------------- - self.cache.clear () + def test_cacheModification(self): + """ + Perfrom tests with mixed modification operations + """ - table = u'FOO' - row = u'100' + self.cache.clear() - # First add some clean data to the cache - self.cache.write_clean (table, row, [(u'foo', 1)]) + table = u'FOO' + row = u'100' - # Now change an 'existing' record and cancel that operation - self.cache.write_dirty (table, row, u'bar', 2) - self.cache.write_dirty (table, row, u'foo', 100) - self.cache.cancel () + # First add some clean data to the cache + self.cache.write_clean(table, row, [(u'foo', 1)]) - # The cached (current) value of 'foo' should be still the clean value, and - # there must not be any 'dirty' data atm - self.assertEqual (self.cache.read (table, row, u'foo'), 1) - self.assertEqual (self.cache.dirtyTables (), {}) + # Now change an 'existing' record and cancel that operation + self.cache.write_dirty(table, row, u'bar', 2) + self.cache.write_dirty(table, row, u'foo', 100) + self.cache.cancel() - # Change the record again; now it should have the state 'changed' - self.cache.write_dirty (table, row, u'foo', 100) - result = self.__orderTables (self.cache.dirtyTables ()) - exp = [(table, row, 'changed', [(u'foo', 100)])] - self.assertEqual (result, exp) - self.cache.confirm () + # The cached (current) value of 'foo' should be still the clean value, + # and there must not be any 'dirty' data atm + self.assertEqual(self.cache.read(table, row, u'foo'), 1) + self.assertEqual(self.cache.dirtyTables(), {}) - # Calling initialized () for a record which wasn't inserted before must - # raise an exception - self.assertRaises (data.StateChangeError, self.cache.initialized, - table, row) + # Change the record again; now it should have the state 'changed' + self.cache.write_dirty(table, row, u'foo', 100) + result = self.__orderTables(self.cache.dirtyTables()) + exp = [(table, row, 'changed', [(u'foo', 100)])] + self.assertEqual(result, exp) + self.cache.confirm() - # Now delete the record. It will change it's state and shows up in the - # deletion queue - self.cache.deleteRecord (table, row) - exp2 = [(table, row, 'deleted', [(u'foo', 100), (u'gnue_id', None)])] - self.assertEqual (self.__orderTables (self.cache.dirtyTables ()), exp2) - self.assertEqual (self.cache.deleted, {u'FOO-100': (table, row)}) + # Calling initialized() for a record which wasn't inserted before must + # raise an exception + self.assertRaises(data.StateChangeError, self.cache.initialized, + table, row) - # Cancel the delete operation; we should be again at the point of the last - # confirmation - self.cache.cancel () - result = self.__orderTables (self.cache.dirtyTables ()) - self.assertEqual (result, exp) - self.assertEqual (self.cache.deleted, {}) + # Now delete the record. It will change it's state and shows up in the + # deletion queue + self.cache.deleteRecord(table, row) + exp2 = [(table, row, 'deleted', [(u'foo', 100), (u'gnue_id', None)])] + self.assertEqual(self.__orderTables(self.cache.dirtyTables()), exp2) + self.assertEqual(self.cache.deleted, {u'FOO-100': (table, row)}) - # Make the changed record a clean record - self.cache.makeClean (table, row) - self.assertEqual (self.cache.dirtyTables (), {}) + # Cancel the delete operation; we should be again at the point of the + # last confirmation + self.cache.cancel() + result = self.__orderTables(self.cache.dirtyTables()) + self.assertEqual(result, exp) + self.assertEqual(self.cache.deleted, {}) - # Delting a record and afterwards adding the same key points out a bug in - # appserver - self.cache.deleteRecord (u'frog', u'dummy') - self.assertRaises (data.DuplicateRecordError, self.cache.insertRecord, - u'frog', u'dummy') - expected = [(u'frog', u'dummy', 'deleted', [(u'gnue_id', None)])] - self.assertEqual (self.__orderTables (self.cache.dirtyTables ()), expected) - self.assertEqual (self.cache.inserted, {}) - self.assertEqual (self.cache.deleted, {u'frog-dummy': (u'frog', u'dummy')}) + # Make the changed record a clean record + self.cache.makeClean(table, row) + self.assertEqual(self.cache.dirtyTables(), {}) - self.cache.cancel () - self.assertEqual (self.cache.dirtyTables (), {}) - self.assertEqual (self.cache.inserted, {}) - self.assertEqual (self.cache.deleted, {}) + # Delting a record and afterwards adding the same key points out a bug + # in appserver + self.cache.deleteRecord(u'frog', u'dummy') + self.assertRaises(data.DuplicateRecordError, self.cache.insertRecord, + u'frog', u'dummy') + expected = [(u'frog', u'dummy', 'deleted', [(u'gnue_id', None)])] + self.assertEqual(self.__orderTables(self.cache.dirtyTables()), expected) + self.assertEqual(self.cache.inserted, {}) + self.assertEqual(self.cache.deleted, + {u'frog-dummy': (u'frog', u'dummy')}) + self.cache.cancel() + self.assertEqual(self.cache.dirtyTables(), {}) + self.assertEqual(self.cache.inserted, {}) + self.assertEqual(self.cache.deleted, {}) - # --------------------------------------------------------------------------- - # Prepare the given cache-ditctionary - # --------------------------------------------------------------------------- - def __orderTables (self, tables): - """ - Convert the given cache-dictionary into a comparable order. This function - creates a sequence of quadruples built from tablename, rowid, current state - of that row in the cache and an ordered sequence of (field, value) pairs. - This sequence is sorted as well as the sequence of field-values. + # ------------------------------------------------------------------------- + # Prepare the given cache-ditctionary + # ------------------------------------------------------------------------- - @param tables: cache-dictionary of the form 'dict [table] [row] [field]' - @return: ordered sequence [(table, row, state, [(field, value), ...]), ... ] - """ + def __orderTables(self, tables): + """ + Convert the given cache-dictionary into a comparable order. This + function creates a sequence of quadruples built from tablename, rowid, + current state of that row in the cache and an ordered sequence of + (field, value) pairs. This sequence is sorted as well as the sequence + of field-values. - result = [] - - for (table, rows) in tables.items (): - for (row, fields) in rows.items (): - fseq = fields.items () - fseq.sort () + @param tables: cache-dictionary of the form 'dict [table] [row] [field]' + @return: ordered sequence [(table, row, state, [(field, value), ...]), + ... ] + """ - result.append ((table, row, self.cache.state (table, row), fseq)) + result = [] - result.sort () + for (table, rows) in tables.items(): + for (row, fields) in rows.items(): + fseq = fields.items() + fseq.sort() - return result + result.append((table, row, self.cache.state(table, row), fseq)) + result.sort() + return result + + # ============================================================================= # TestCase for connection class # ============================================================================= -class ConnectionTestCase (unittest.TestCase): - """ - TestCase for the connection class (= low level layer for sessions) - """ - - # --------------------------------------------------------------------------- - # Prepare the test case - # --------------------------------------------------------------------------- - - def setUp (self): +class ConnectionTestCase(unittest.TestCase): """ - Create two seperate connections instances (so we can simulate two - concurrent sessions) and log into both (using 'gnue' as username and - password). + TestCase for the connection class (= low level layer for sessions) """ - app = GClientApp.GClientApp (application = 'appserver', - defaults = geasConfiguration.ConfigOptions) + # ------------------------------------------------------------------------- + # Prepare the test case + # ------------------------------------------------------------------------- - # Fake the login to the default connection - c = app.connections.getConnection ('gnue') - c.parameters ['_username'] = 'gnue' - c.parameters ['_password'] = 'gnue' + def setUp(self): + """ + Create two seperate connections instances (so we can simulate two + concurrent sessions) and log into both (using 'gnue' as username and + password). + """ - conn = GConnections.GConnections (app.connections._location, - app.connections._loginHandler, - app.connections._loginOptions) - c2 = conn.getConnection ('gnue') - c2.parameters ['_username'] = 'gnue' - c2.parameters ['_password'] = 'gnue' + app = GClientApp.GClientApp(application = 'appserver', + defaults = geasConfiguration.ConfigOptions) - self.connection = data.connection (app.connections, 'gnue') - self.concurrent = data.connection (conn, 'gnue') + # Fake the login to the default connection + c = app.connections.getConnection('gnue') + c.parameters ['_username'] = 'gnue' + c.parameters ['_password'] = 'gnue' - self.remove = [] + conn = GConnections.GConnections(app.connections._location, + app.connections._loginHandler, + app.connections._loginOptions) + c2 = conn.getConnection('gnue') + c2.parameters ['_username'] = 'gnue' + c2.parameters ['_password'] = 'gnue' + self.connection = data.connection(app.connections, 'gnue') + self.concurrent = data.connection(conn, 'gnue') - # --------------------------------------------------------------------------- - # Run some tests on the connection instance - # --------------------------------------------------------------------------- + self.remove = [] - def testConnection (self): - """ - Perform all tests - """ - # Run an unordered query - content = {None: (u'address_person', None, None, [u'address_name'])} - rs = self.connection.query (content, None, None) + # ------------------------------------------------------------------------- + # Run some tests on the connection instance + # ------------------------------------------------------------------------- - self.assertEqual (len(rs), 4) - e = [u'James Tiberius Kirk', u'Spock', u'Leonard H. McCoy', - u'Pavel Andreievich Chekov'] - e.sort () - self.assertEqual (self.__rsToSeq (rs, [u'address_name'], True), e) + def testConnection(self): + """ + Perform all tests + """ - # Run an ordered query - content = {None: (u'address_person', None, None, [u'address_name'])} - rs = self.connection.query (content, None, [{'name': u'address_name'}]) + # Run an unordered query + content = {None: (u'address_person', None, None, [u'address_name'])} + rs = self.connection.query(content, None, None) - self.assertEqual (len(rs), 4) - e = [u'James Tiberius Kirk', u'Leonard H. McCoy', - u'Pavel Andreievich Chekov', u'Spock'] - self.assertEqual (self.__rsToSeq (rs, [u'address_name']), e) + self.assertEqual(len(rs), 4) + e = [u'James Tiberius Kirk', u'Spock', u'Leonard H. McCoy', + u'Pavel Andreievich Chekov'] + e.sort() + self.assertEqual(self.__rsToSeq(rs, [u'address_name'], True), e) - # Run the same query again, and using a non-prefetched field - rs = self.connection.query (content, None, [{'name': u'address_name', - 'descending': True}]) - result = [] - for rec in rs: - result.append ((rec.getField (u'address_name'), rec.getField - (u'address_zip'))) - rs.close () + # Run an ordered query + content = {None: (u'address_person', None, None, [u'address_name'])} + rs = self.connection.query(content, None, [{'name': u'address_name'}]) - e = [(u'Spock', u'4711'), - (u'Pavel Andreievich Chekov', u'195251'), - (u'Leonard H. McCoy', u'39216'), - (u'James Tiberius Kirk', u'2002')] + self.assertEqual(len(rs), 4) + e = [u'James Tiberius Kirk', u'Leonard H. McCoy', + u'Pavel Andreievich Chekov', u'Spock'] + self.assertEqual(self.__rsToSeq(rs, [u'address_name']), e) - self.assertEqual (result, e) + # Run the same query again, and using a non-prefetched field + rs = self.connection.query(content, None, [{'name': u'address_name', + 'descending': True}]) + result = [] + for rec in rs: + result.append((rec.getField(u'address_name'), + rec.getField(u'address_zip'))) + rs.close() - print "Simple condition ..." - # Run the query with a simple condition - cond = ['like', ['field', u'address_zip'], ['const', '%1']] - rs = self.connection.query (content, cond, [{'name': u'address_name'}]) + e = [(u'Spock', u'4711'), + (u'Pavel Andreievich Chekov', u'195251'), + (u'Leonard H. McCoy', u'39216'), + (u'James Tiberius Kirk', u'2002')] - result = self.__rsToSeq (rs, [u'address_name']) - e = [u'Pavel Andreievich Chekov', u'Spock'] - self.assertEqual (result, e) + self.assertEqual(result, e) - # ------------------------------------------------------------------------- - # Checking dirty reads + # Run the query with a simple condition + cond = ['like', ['field', u'address_zip'], ['const', '%1']] + rs = self.connection.query(content, cond, [{'name': u'address_name'}]) - print "Dirty reads ..." - (orgCrew, orgData) = self.__getCrewMembers () - expected = [(u'James Tiberius Kirk', u'US'), - (u'Leonard H. McCoy', u'US'), - (u'Pavel Andreievich Chekov', u'RU'), - (u'Spock', None)] + result = self.__rsToSeq(rs, [u'address_name']) + e = [u'Pavel Andreievich Chekov', u'Spock'] + self.assertEqual(result, e) - self.assertEqual (orgData, expected) + # --------------------------------------------------------------------- + # Checking dirty reads - # Now, Doc McCoy moves to Austria - austria = u'0000000000000000000000200000000F' - orgCrew [1].putField (u'address_country', austria) - - # Spock will leave (for the moment) - spocksId = orgCrew [-1].getField (u'gnue_id') - self.connection.deleteRecord (u'address_person', spocksId) + (orgCrew, orgData) = self.__get_crew_members() + expected = [(u'James Tiberius Kirk', u'US'), + (u'Leonard H. McCoy', u'US'), + (u'Pavel Andreievich Chekov', u'RU'), + (u'Spock', None)] - # And a new crew member signs on - new = self.connection.insertRecord (u'address_person') - new.initialized () - new.putField (u'address_name', u'Foobar') - new.putField (u'address_zip', u'6890') - new.putField (u'address_country', austria) + self.assertEqual(orgData, expected) - # So running the previous query again - (newCrew, newData) = self.__getCrewMembers () - expected = [(u'Foobar', u'AT'), - (u'James Tiberius Kirk', u'US'), - (u'Leonard H. McCoy', u'AT'), - (u'Pavel Andreievich Chekov', u'RU')] - self.assertEqual (expected, newData) + # Now, Doc McCoy moves to Austria + austria = u'0000000000000000000000200000000F' + orgCrew[1].putField(u'address_country', austria) - # Now, there's another session doing the same request - (secCrew, secData) = self.__getCrewMembers (self.concurrent) + # Spock will leave (for the moment) + spocksId = orgCrew[-1].getField(u'gnue_id') + self.connection.deleteRecord(u'address_person', spocksId) - # Since the changes made before are not yet commited the second connection - # should still get the original result - self.assertEqual (orgData, secData) + # And a new crew member signs on + new = self.connection.insertRecord(u'address_person') + new.initialized() + new.putField(u'address_name', u'Foobar') + new.putField(u'address_zip', u'6890') + new.putField(u'address_country', austria) - # Cancelling the running atomic operation should also yield the former - # situation - self.connection.cancelChanges () - (newCrew, newData) = self.__getCrewMembers () + # So running the previous query again + (newCrew, newData) = self.__get_crew_members() - self.assertEqual (orgData, newData) + expected = [(u'Foobar', u'AT'), + (u'James Tiberius Kirk', u'US'), + (u'Leonard H. McCoy', u'AT'), + (u'Pavel Andreievich Chekov', u'RU')] + self.assertEqual(expected, newData) - # --- - # and again, Doc McCoy moves to Austria - austria = u'0000000000000000000000200000000F' - orgCrew [1].putField (u'address_country', austria) - - # Spock will leave (for the moment) - spocksId = orgCrew [-1].getField (u'gnue_id') - self.connection.deleteRecord (u'address_person', spocksId) - # And the captain becomes the senior of Checkov - orgCrew [-2].putField (u'address_senior', - u'00000000000000000000000000001100') + # Now, there's another session doing the same request + (secCrew, secData) = self.__get_crew_members(self.concurrent) - # And a new crew member signs on - new = self.connection.insertRecord (u'address_person') - new.initialized () - new.putField (u'address_name', u'Foobar') - new.putField (u'address_zip', u'6890') - new.putField (u'address_country', austria) + # Since the changes made before are not yet commited the second + # connection should still get the original result + self.assertEqual(orgData, secData) - oz = self.connection.insertRecord (u'address_country') - oz.initialized () - oz.putField (u'address_code', u'OZ') - oz.putField (u'address_name', u'Oz') + # Cancelling the running atomic operation should also yield the former + # situation + self.connection.cancelChanges() - self.connection.confirmChanges () + (newCrew, newData) = self.__get_crew_members() - # make some more changes - new.putField (u'address_name', u'buzzing the foobar') - new.putField (u'address_children', 2) + self.assertEqual(orgData, newData) - self.connection.deleteRecord (u'address_country', oz.getField (u'gnue_id')) + # --- + # and again, Doc McCoy moves to Austria + austria = u'0000000000000000000000200000000F' + orgCrew [1].putField(u'address_country', austria) - # change zip of James T. Kirk - orgCrew [0].putField (u'address_zip', u'0815') + # Spock will leave (for the moment) + spocksId = orgCrew [-1].getField(u'gnue_id') + self.connection.deleteRecord(u'address_person', spocksId) - # and now cancel all that changes - self.connection.cancelChanges () + # And the captain becomes the senior of Checkov + orgCrew [-2].putField(u'address_senior', + u'00000000000000000000000000001100') - (newCrew, newData) = self.__getCrewMembers () - expected = [(u'Foobar', u'AT'), - (u'James Tiberius Kirk', u'US'), - (u'Leonard H. McCoy', u'AT'), - (u'Pavel Andreievich Chekov', u'RU')] - self.assertEqual (expected, newData) + # And a new crew member signs on + new = self.connection.insertRecord(u'address_person') + new.initialized() + new.putField(u'address_name', u'Foobar') + new.putField(u'address_zip', u'6890') + new.putField(u'address_country', austria) - # The address_country table should again contain the new country record - content = {None: (u'address_country', None, None, [u'address_name'])} - condition = {u'address_code': u'OZ'} - resultSet = self.connection.query (content, condition, None) - self.assertEqual ([u'Oz'], self.__rsToSeq (resultSet, [u'address_name'])) + oz = self.connection.insertRecord(u'address_country') + oz.initialized() + oz.putField(u'address_code', u'OZ') + oz.putField(u'address_name', u'Oz') - new.putField (u'address_country', oz.getField (u'gnue_id')) + self.connection.confirmChanges() - # Doing a commit here means storing all changes, but also shows if data.py - # does a proper sorting of insertions. This is because we've created the - # new person before the new country (which is in fact a master record for - # the person and theirfor must be added *before* the person). - self.connection.commit () + # make some more changes + new.putField(u'address_name', u'buzzing the foobar') + new.putField(u'address_children', 2) - # Since the second session had no commit until now, no changes made by the - # first session should be visible at all. - (secCrew, secData) = self.__getCrewMembers (self.concurrent) - expected = [(u'James Tiberius Kirk', u'US'), - (u'Leonard H. McCoy', u'US'), - (u'Pavel Andreievich Chekov', u'RU'), - (u'Spock', None)] - self.assertEqual (expected, secData) + self.connection.deleteRecord(u'address_country', + oz.getField(u'gnue_id')) - # After closing the second session's transaction the query should now - # return all changes made by the first session - self.concurrent.rollback () + # change zip of James T. Kirk + orgCrew [0].putField(u'address_zip', u'0815') - (secCrew, secData) = self.__getCrewMembers (self.concurrent) - expected = [(u'Foobar', u'OZ'), - (u'James Tiberius Kirk', u'US'), - (u'Leonard H. McCoy', u'AT'), - (u'Pavel Andreievich Chekov', u'RU')] - self.assertEqual (expected, secData) + # and now cancel all that changes + self.connection.cancelChanges() - self.connection.setConstraints (u'address_person', - {u'address_country': {u'address_country': True}, - u'address_person': {u'address_senior': True}}) + (newCrew, newData) = self.__get_crew_members() + expected = [(u'Foobar', u'AT'), + (u'James Tiberius Kirk', u'US'), + (u'Leonard H. McCoy', u'AT'), + (u'Pavel Andreievich Chekov', u'RU')] + self.assertEqual(expected, newData) - self.remove = [(u'address_person', new.getField (u'gnue_id')), - (u'address_country', oz.getField (u'gnue_id'))] + # The address_country table should again contain the new country record + content = {None: (u'address_country', None, None, [u'address_name'])} + condition = {u'address_code': u'OZ'} + resultSet = self.connection.query(content, condition, None) + self.assertEqual([u'Oz'], self.__rsToSeq(resultSet, [u'address_name'])) + new.putField(u'address_country', oz.getField(u'gnue_id')) + # Doing a commit here means storing all changes, but also shows if + # data.py does a proper sorting of insertions. This is because we've + # created the new person before the new country (which is in fact a + # master record for the person and theirfor must be added *before* the + # person). + self.connection.commit() - # --------------------------------------------------------------------------- - # Restore to the initial situation in the test database - # --------------------------------------------------------------------------- + # Since the second session had no commit until now, no changes made by + # the first session should be visible at all. + (secCrew, secData) = self.__get_crew_members(self.concurrent) + expected = [(u'James Tiberius Kirk', u'US'), + (u'Leonard H. McCoy', u'US'), + (u'Pavel Andreievich Chekov', u'RU'), + (u'Spock', None)] + self.assertEqual(expected, secData) - def tearDown (self): - """ - Undo all changes made by the testcase - """ + # After closing the second session's transaction the query should now + # return all changes made by the first session + self.concurrent.rollback() - self.connection.rollback () + (secCrew, secData) = self.__get_crew_members(self.concurrent) + expected = [(u'Foobar', u'OZ'), + (u'James Tiberius Kirk', u'US'), + (u'Leonard H. McCoy', u'AT'), + (u'Pavel Andreievich Chekov', u'RU')] + self.assertEqual(expected, secData) - # First we have to re-add the record for Spock - spock = self.connection.insertRecord (u'address_person') - spock.initialized () - spock.putField (u'address_name', u'Spock') - spock.putField (u'address_street', u'Vulc Lane 1') - spock.putField (u'address_zip', u'4711') - spock.putField (u'address_city', u'Vulcane') - spock.putField (u'address_children', 0) - spock.putField (u'address_weight', 78.8) - spock.putField (u'address_born', u'2230-01-01') - spock.putField (u'address_meettime', u'6:30') - spock.putField (u'address_lastmeeting', u'2270-05-17 08:00') - spock.putField (u'address_human', False) - spock.putField (u'address_senior', u'00000000000000000000000000001100') - # This is a bit hackish cause we're changing the primary key - spock.putField (u'gnue_id', u'00000000000000000000000000001101') + self.connection.setConstraints(u'address_person', + {u'address_country': {u'address_country': True}, + u'address_person': {u'address_senior': True}}) - # Reset the country for McCoy to the US - mccoy = self.connection.findRecord (u'address_person', - u'00000000000000000000000000001102', [u'address_country']) - mccoy.putField (u'address_country', u'000000000000000000000020000000E3') + self.remove = [(u'address_person', new.getField(u'gnue_id')), + (u'address_country', oz.getField(u'gnue_id'))] - # Reassign Spock as Checkov's senior - checkov = self.connection.findRecord (u'address_person', - u'00000000000000000000000000001103', [u'address_senior']) - checkov.putField (u'address_senior', u'00000000000000000000000000001101') - # And remove newly added records - for (table, row) in self.remove: - self.connection.deleteRecord (table, row) - self.connection.commit () + # ------------------------------------------------------------------------- + # Restore to the initial situation in the test database + # ------------------------------------------------------------------------- + def tearDown(self): + """ + Undo all changes made by the testcase + """ + self.connection.rollback() - # --------------------------------------------------------------------------- - # Create a list of records and it's data for all crew members - # --------------------------------------------------------------------------- + # First we have to re-add the record for Spock + spock = self.connection.insertRecord(u'address_person') + spock.initialized() + spock.putField(u'address_name', u'Spock') + spock.putField(u'address_street', u'Vulc Lane 1') + spock.putField(u'address_zip', u'4711') + spock.putField(u'address_city', u'Vulcane') + spock.putField(u'address_children', 0) + spock.putField(u'address_weight', 78.8) + spock.putField(u'address_born', u'2230-01-01') + spock.putField(u'address_meettime', u'6:30') + spock.putField(u'address_lastmeeting', u'2270-05-17 08:00') + spock.putField(u'address_human', False) + spock.putField(u'address_senior', u'00000000000000000000000000001100') + # This is a bit hackish cause we're changing the primary key + spock.putField(u'gnue_id', u'00000000000000000000000000001101') - def __getCrewMembers (self, connection = None): - """ - Create a list of records and it's data for all crew members + # Reset the country for McCoy to the US + mccoy = self.connection.findRecord(u'address_person', + u'00000000000000000000000000001102', + [u'address_country']) + mccoy.putField(u'address_country', u'000000000000000000000020000000E3') - @param connection: if given this connection instance will be used. It - defaults to 'self.connection'. + # Reassign Spock as Checkov's senior + checkov = self.connection.findRecord(u'address_person', + u'00000000000000000000000000001103', [u'address_senior']) + checkov.putField(u'address_senior', u'00000000000000000000000000001101') - @return: tuple (recordlist, datalist), where recordlist is a sequence of - data.record instances, and datalist is a sequence of tuples - (name, country-code), one per record found. - """ + # And remove newly added records + for (table, row) in self.remove: + self.connection.deleteRecord(table, row) - if connection is None: - connection = self.connection + self.connection.commit() - # The following content definition drives 'address_person' as master table - # and joins 'address_country' in a left outer join, where 'address_name' is - # the onyle prefetched field for both tables. This forces data.py to do - # refetching for the joined table (cause we're accessing 'address_code'). - content = {u't0': (u'address_person', None, None, [u'address_name']), - u't1': (u'address_country', u't0', u'address_country', - [u'address_name'])} - sortOrder = [{'name': u't0.address_name'}] - resultSet = connection.query (content, None, sortOrder) - recList = [] - recData = [] - for rec in resultSet: - country = rec.getField (u'address_country') - if country is not None: - jRec = connection.findRecord (u'address_country', country, []) - cCode = jRec.getField (u'address_code') - else: - cCode = None + # ------------------------------------------------------------------------- + # Create a list of records and it's data for all crew members + # ------------------------------------------------------------------------- - recData.append ((rec.getField (u'address_name'), cCode)) + def __get_crew_members(self, connection=None): + """ + Create a list of records and it's data for all crew members - recList.append (rec) + @param connection: if given this connection instance will be used. It + defaults to 'self.connection'. - resultSet.close () + @return: tuple (recordlist, datalist), where recordlist is a sequence + of data.record instances, and datalist is a sequence of tuples + (name, country-code), one per record found. + """ - return (recList, recData) + if connection is None: + connection = self.connection + # The following content definition drives 'address_person' as master + # table and joins 'address_country' in a left outer join, where + # 'address_name' is the only prefetched field for both tables. This + # forces data.py to do refetching for the joined table (cause we're + # accessing 'address_code'). + content = {u't0': (u'address_person', None, None, [u'address_name']), + u't1': (u'address_country', u't0', u'address_country', + [u'address_name'])} - # --------------------------------------------------------------------------- - # Convert a resultSet into a flat sequence - # --------------------------------------------------------------------------- + sortOrder = [{'name': u't0.address_name'}] + resultSet = connection.query(content, None, sortOrder) + recList = [] + recData = [] - def __rsToSeq (self, rs, fields, reorder = False): - """ - Flatten a resultSet into a sequence of tuples holding the requested field - values. + for rec in resultSet: + country = rec.getField(u'address_country') + if country is not None: + jRec = connection.findRecord(u'address_country', country, []) + cCode = jRec.getField(u'address_code') + else: + cCode = None - @param rs: the resultset to iterate - @param fields: sequence of fieldnames to extract - @param reorder: if True, the resulting sequence will be sorted + recData.append((rec.getField(u'address_name'), cCode)) - @return: sequence of value-tuples, one per record. If only one field is - requested, the result is a sequence of field-values (without tuples) - """ + recList.append(rec) - result = [] - append = len (fields) == 1 and result.extend or result.append + resultSet.close() - for rec in rs: - append ([rec.getField (f) for f in fields]) + return (recList, recData) - rs.close () - if reorder: - result.sort () + # ------------------------------------------------------------------------- + # Convert a resultSet into a flat sequence + # ------------------------------------------------------------------------- - return result + def __rsToSeq(self, rs, fields, reorder = False): + """ + Flatten a resultSet into a sequence of tuples holding the requested + field values. + @param rs: the resultset to iterate + @param fields: sequence of fieldnames to extract + @param reorder: if True, the resulting sequence will be sorted + @return: sequence of value-tuples, one per record. If only one field is + requested, the result is a sequence of field-values (without tuples) + """ + + result = [] + append = len(fields) == 1 and result.extend or result.append + + for rec in rs: + append([rec.getField(f) for f in fields]) + + rs.close() + + if reorder: + result.sort() + + return result + + # ============================================================================= # Main program # ============================================================================= if __name__ == '__main__': - suite1 = unittest.makeSuite (CachingTestCase) - suite2 = unittest.makeSuite (ConnectionTestCase) - alltests = unittest.TestSuite ((suite1, suite2)) + suite1 = unittest.makeSuite(CachingTestCase) + suite2 = unittest.makeSuite(ConnectionTestCase) + alltests = unittest.TestSuite((suite1, suite2)) - unittest.TextTestRunner (verbosity = 2).run (alltests) + unittest.TextTestRunner(verbosity = 2).run(alltests) _______________________________________________ commit-gnue mailing list commit-gnue@gnu.org http://lists.gnu.org/mailman/listinfo/commit-gnue