''' Tests the Database classes '''

from bootstrap_test import BootstrapBaseTest

import unittest
import transaction

from db.spatialIndex import SpatialIndex
from db.persistence import Persistent

class House(Persistent):
    def __init__(self, name):
        Persistent.__init__( self )
        self.name = name        

doLog = False
def log(*args):
    if doLog:
        for arg in args:
            print str(arg),
        print

class SpatialIndexTest(BootstrapBaseTest):
    def testAbort1(self):
        transaction.begin()
        settings = dict( dimension = 2, leaf_capacity = 20, pagesize = 4096, near_minimum_overlap_factor = 20, writethrough = False, buffering_capacity = 100 )
        self.root['spatialIndex'] = index = SpatialIndex( settings )
        log( 'PRE-SAVEPOINT' )
        sp = transaction.savepoint()
        log( 'POST-SAVEPOINT' )
        item1 = index.add( House('Mansion'), (5,5,20,10) )
        noHousesInArea = index.count( (0,0,100,100) )
        self.assertEqual( noHousesInArea, 1 )
        log( 'PRE-ABORT' )
        transaction.abort()
        log( 'POST-ABORT' )
        self.assertRaises( ValueError, index.count, (0,0,100,100) )
        
    def testAbort2(self):
        transaction.begin()
        settings = dict( dimension = 2, leaf_capacity = 20, pagesize = 4096, near_minimum_overlap_factor = 20, writethrough = False, buffering_capacity = 100 )
        self.root['spatialIndex'] = index = SpatialIndex( settings )
        transaction.commit()
        item1 = index.add( House('Mansion'), (5,5,20,10) )
        noHousesInArea = index.count( (0,0,100,100) )
        self.assertEqual( noHousesInArea, 1 )
        transaction.abort()
        noHousesInArea = index.count( (0,0,100,100) )
        self.assertEqual( noHousesInArea, 0 )

    def testMore(self):
        import struct
            
        transaction.begin()
        settings = dict( dimension = 2, leaf_capacity = 20, pagesize = 4096, near_minimum_overlap_factor = 20, writethrough = False, buffering_capacity = 100 )
        self.root['spatialIndex'] = index = SpatialIndex( settings )
        item1 = index.add( House('Mansion'), (5,5,20,10) )
        item2 = index.add( House('Cottage'), (20,20,25,25) )
        self.assertEqual( index.count( (0,0,100,100) ), 2 )
        index.clearBuffer(False)
        
        log( 'PRE-SAVEPOINT1' )
        sp = transaction.savepoint()
        log( 'POST-SAVEPOINT1')
        index.delete( item1 )
        index.delete( item2 )
        index.add( House('Cottage #2'), (9,9,25,25) )
        self.assertEqual( index.count( (0,0,100,100) ), 1 )
        log( 'PRE-SAVEPOINT2' )
        sp2 = transaction.savepoint()
        log( struct.unpack( 'I', index.pageData[0][8:12] ) )
        log( 'POST-SAVEPOINT2' )
        item3 = index.add( House('Cottage #2a'), (11,11,26,26) )
        self.assertEqual( index.count( (0,0,100,100) ), 2 )
        index.delete( item3 )
        self.assertEqual( index.count( (0,0,100,100) ), 1 )
        self.assertEqual( set(h.name for h in index.intersection( (0,0,100,100) )), set(('Cottage #2',)) )
        log( 'PRE-ROLLBACK2' )
        log( struct.unpack( 'I', index.pageData[0][8:12] ) )
        sp2.rollback()
        log( struct.unpack( 'I', index.pageData[0][8:12] ) )
        log( 'POST-ROLLBACK2' )
        log( index.count( (0,0,100,100) ) )
        for obj in index.intersection( (0,0,100,100) ):
            log( obj.name )
        self.assertEqual( index.count( (0,0,100,100) ), 1 )
        index.add( House('Cottage #77'), (10,10,25,25) )
        log( 'PRE-ROLLBACK1' )
        self.assertEqual( index.count( (0,0,100,100) ), 2 )
        sp.rollback()
        log( 'POST-ROLLBACK1' )
        
        noHousesInArea = index.count( (0,0,100,100) )
        log( 'Y', noHousesInArea, len(index.idToItem), struct.unpack( 'I', index.pageData[0][8:12] ) )
        self.assertEqual( noHousesInArea, 2 )
        transaction.commit()
        
        log( 'Z', len(index.idToItem), struct.unpack( 'I', index.pageData[0][8:12] ) )
        housesInArea = list( index.intersection ( (0,0,100,100) ) )
        log( len(housesInArea) )
        self.assertEqual( set((housesInArea[0].name, housesInArea[1].name)), set(('Mansion', 'Cottage')) )
        closestHouse = list( index.nearest( (10,10), num_results = 1 ) )[0]
        self.assertEqual( closestHouse.name, 'Mansion' )
        totalArea = index.bounds
        self.assertEqual( totalArea, [5,5,25,25] )
        transaction.commit()
        
        index.add( House('Cottage #3'), (10,10,25,25) )
        self.assertEqual( index.count( (0,0,100,100) ), 3 )
        transaction.abort()
        
        self.assertEqual( index.count( (0,0,100,100) ), 2 )
        
    def testBenchmark(self):
        transaction.begin()
        settings = dict( dimension = 2, leaf_capacity = 20, pagesize = 4096, near_minimum_overlap_factor = 20, writethrough = False, buffering_capacity = 100 )
        self.root['spatialIndex'] = index = SpatialIndex( settings )
    
        # benchmark inserting 1000 random objects
        #  note: This is not the smart way. If you want to insert many objects
        #        at once, use the initialValuesGenerator.
        #        It's nice for testing though since it will cause lots of writes.
        import random
        def generate(count, minX, minY, maxX, maxY):
            for i in range(count):
                x1, y1 = random.random() * minX, random.random() * minY
                x2, y2 = x1 + random.random() * maxX, y1 + random.random() * maxY
                yield House( 'Random house #%d' % i ), (x1, y1, x2, y2)
                
        import time
        start = time.clock()
        noObjects = 1000
        for args in generate(noObjects, 1000, 1000, 1000, 1000):
            index.add( *args )
        print time.clock() - start
        transaction.commit()
        print time.clock() - start
        
if __name__ == '__main__':
    unittest.main()
