root/trunk/geniusql/test/zoo_fixture.py
| Revision 5 (checked in by fumanchu, 6 years ago) | |
|---|---|
| |
| Line | |
|---|---|
| 1 | """Test fixture for Storage Managers.""" |
| 2 | |
| 3 | import datetime |
| 4 | import os |
| 5 | thisdir = os.path.dirname(__file__) |
| 6 | logname = os.path.join(thisdir, "geniusqltest.log") |
| 7 | |
| 8 | |
| 9 | try: |
| 10 | import pythoncom |
| 11 | except ImportError: |
| 12 | pythoncom = None |
| 13 | |
| 14 | try: |
| 15 | set |
| 16 | except NameError: |
| 17 | from sets import Set as set |
| 18 | |
| 19 | import sys |
| 20 | import threading |
| 21 | import time |
| 22 | import traceback |
| 23 | import unittest |
| 24 | import warnings |
| 25 | |
| 26 | |
| 27 | import geniusql |
| 28 | from geniusql import errors, typerefs |
| 29 | from geniusql.test import tools |
| 30 | |
| 31 | from dejavu import logic |
| 32 | |
| 33 | |
| 34 | Jan_1_2001 = datetime.date(2001, 1, 1) |
| 35 | every13days = [Jan_1_2001 + datetime.timedelta(x * 13) for x in range(20)] |
| 36 | every17days = [Jan_1_2001 + datetime.timedelta(x * 17) for x in range(20)] |
| 37 | del x |
| 38 | |
| 39 | |
| 40 | class ZooTests(unittest.TestCase): |
| 41 | |
| 42 | def test_1_create_tables(self): |
| 43 | Animal = db.table('Animal') |
| 44 | Animal['ID'] = db.column(int, autoincrement=True, key=True) |
| 45 | Animal.add_index('ID') |
| 46 | Animal['ZooID'] = db.column(int) |
| 47 | Animal['Species'] = db.column(hints={'bytes': 100}) |
| 48 | Animal['Legs'] = db.column(int, default=4) |
| 49 | Animal['PreviousZoos'] = db.column(list, hints={'bytes': 8000}) |
| 50 | Animal['LastEscape'] = db.column(datetime.datetime) |
| 51 | Animal['Lifespan'] = db.column(float, hints={'precision': 4}) |
| 52 | Animal['Age'] = db.column(float, default=1, hints={'precision': 4}) |
| 53 | Animal['MotherID'] = db.column(int) |
| 54 | Animal['PreferredFoodID'] = db.column(int) |
| 55 | Animal['AlternateFoodID'] = db.column(int) |
| 56 | Animal.add_index('ZooID') |
| 57 | Animal.references['Animal'] = ('ID', 'Animal', 'MotherID') |
| 58 | Animal.references['Visit'] = ('ID', 'Visit', 'AnimalID') |
| 59 | db['Animal'] = Animal |
| 60 | |
| 61 | Zoo = db.table('Zoo') |
| 62 | Zoo['ID'] = db.column(int, autoincrement=True, key=True) |
| 63 | Zoo.add_index('ID') |
| 64 | Zoo['Name'] = db.column() |
| 65 | Zoo['Founded'] = db.column(datetime.date) |
| 66 | Zoo['Opens'] = db.column(datetime.time) |
| 67 | Zoo['LastEscape'] = db.column(datetime.datetime) |
| 68 | |
| 69 | if typerefs.fixedpoint: |
| 70 | # Explicitly set precision and scale so test_msaccess |
| 71 | # can test CURRENCY type |
| 72 | Zoo['Admission'] = db.column(typerefs.fixedpoint.FixedPoint, |
| 73 | hints={'precision': 4, 'scale': 2}) |
| 74 | else: |
| 75 | Zoo['Admission'] = db.column(float) |
| 76 | |
| 77 | Zoo.references['Animal'] = ('ID', 'Animal', 'ZooID') |
| 78 | db['Zoo'] = Zoo |
| 79 | |
| 80 | Food = db.table('Food') |
| 81 | Food['ID'] = db.column(int, autoincrement=True, key=True) |
| 82 | Food.add_index('ID') |
| 83 | Food['Name'] = db.column() |
| 84 | Food['NutritionValue'] = db.column(int) |
| 85 | Food.references['Animal'] = ('ID', 'Animal', 'PreferredFoodID') |
| 86 | Animal.references['Alternate Food'] = ('AlternateFoodID', 'Food', 'ID') |
| 87 | db['Food'] = Food |
| 88 | |
| 89 | Vet = db.table('Vet') |
| 90 | Vet['ID'] = c = db.column(int, autoincrement=True, key=True) |
| 91 | c.initial = 200 |
| 92 | Vet.add_index('ID') |
| 93 | Vet['Name'] = db.column() |
| 94 | Vet['ZooID'] = db.column(int) |
| 95 | Vet.add_index('ZooID') |
| 96 | Vet['City'] = db.column() |
| 97 | Vet.references['Zoo'] = ('ZooID', 'Zoo', 'ID') |
| 98 | Vet.references['Visit'] = ('ID', 'Visit', 'VetID') |
| 99 | db['Vet'] = Vet |
| 100 | |
| 101 | Visit = db.table('Visit') |
| 102 | Visit['ID'] = db.column(int, autoincrement=True, key=True) |
| 103 | Visit.add_index('ID') |
| 104 | Visit['VetID'] = db.column(int) |
| 105 | Visit.add_index('VetID') |
| 106 | Visit['ZooID'] = db.column(int) |
| 107 | Visit.add_index('ZooID') |
| 108 | Visit['AnimalID'] = db.column(int) |
| 109 | Visit.add_index('AnimalID') |
| 110 | Visit['Date'] = db.column(datetime.date) |
| 111 | db['Visit'] = Visit |
| 112 | |
| 113 | Exhibit = db.table('Exhibit') |
| 114 | # Make this a string to help test vs unicode. |
| 115 | Exhibit['Name'] = db.column(str, key=True) |
| 116 | Exhibit.add_index('Name') |
| 117 | Exhibit['ZooID'] = db.column(int, key=True) |
| 118 | Exhibit.add_index('ZooID') |
| 119 | Exhibit['Animals'] = db.column(list) |
| 120 | Exhibit['PettingAllowed'] = db.column(bool) |
| 121 | Exhibit['Creators'] = db.column(tuple) |
| 122 | |
| 123 | if typerefs.decimal: |
| 124 | Exhibit['Acreage'] = db.column(typerefs.decimal.Decimal) |
| 125 | else: |
| 126 | Exhibit['Acreage'] = db.column(float) |
| 127 | |
| 128 | Exhibit.references['Zoo'] = ('ZooID', 'Zoo', 'ID') |
| 129 | db['Exhibit'] = Exhibit |
| 130 | |
| 131 | t = db.table('NothingToDoWithZoos') |
| 132 | t['ALong'] = db.column(long, hints={'precision': 1}) |
| 133 | t['AFloat'] = db.column(float, hints={'precision': 1}) |
| 134 | if typerefs.decimal: |
| 135 | t['ADecimal'] = db.column(typerefs.decimal.Decimal, |
| 136 | hints={'precision': 1, 'scale': 1}) |
| 137 | if typerefs.fixedpoint: |
| 138 | t['AFixed'] = db.column(typerefs.fixedpoint.FixedPoint, |
| 139 | hints={'precision': 1, 'scale': 1}) |
| 140 | db['NothingToDoWithZoos'] = t |
| 141 | |
| 142 | def test_2_populate(self): |
| 143 | newids = db.insert('Zoo', Name='Wild Animal Park', |
| 144 | Founded=datetime.date(2000, 1, 1), |
| 145 | # 59 can give rounding errors with divmod, which |
| 146 | # AdapterFromADO needs to correct. |
| 147 | Opens=datetime.time(8, 15, 59), |
| 148 | LastEscape=datetime.datetime(2004, 7, 29, 5, 6, 7), |
| 149 | Admission=4.95, |
| 150 | ) |
| 151 | wap = int(newids['ID']) |
| 152 | |
| 153 | newids = db.insert('Zoo', Name = 'San Diego Zoo', |
| 154 | # This early date should play havoc with a number |
| 155 | # of implementations. |
| 156 | Founded = datetime.date(1835, 9, 13), |
| 157 | Opens = datetime.time(9, 0, 0), |
| 158 | Admission = 0, |
| 159 | ) |
| 160 | sdz = int(newids['ID']) |
| 161 | |
| 162 | db.insert('Zoo', Name = u'Montr\xe9al Biod\xf4me', |
| 163 | Founded = datetime.date(1992, 6, 19), |
| 164 | Opens = datetime.time(9, 0, 0), |
| 165 | Admission = 11.75, |
| 166 | ) |
| 167 | |
| 168 | newids = db.insert('Zoo', Name = 'Sea_World', Admission = 60) |
| 169 | seaworld = int(newids['ID']) |
| 170 | |
| 171 | # Animals |
| 172 | newids = db.insert('Animal', Species='Leopard', Lifespan=73.5) |
| 173 | leopardid = int(newids['ID']) |
| 174 | self.assertEqual(leopardid, 1) |
| 175 | db.save('Animal', ID=leopardid, ZooID=wap, |
| 176 | LastEscape=datetime.datetime(2004, 12, 21, 8, 15, 0, 999907)) |
| 177 | |
| 178 | lion = db.insert('Animal', Species='Lion', ZooID=wap)['ID'] |
| 179 | db.insert('Animal', Species='Slug', Legs=1, Lifespan=.75, |
| 180 | # Test our 8000-byte limit |
| 181 | PreviousZoos=["f" * (8000 - 14)]) |
| 182 | |
| 183 | newids = db.insert('Animal', Species='Tiger', ZooID=sdz, |
| 184 | PreviousZoos=['animal\\universe']) |
| 185 | tiger = int(newids['ID']) |
| 186 | |
| 187 | # Override Legs.default with itself just to make sure it works. |
| 188 | db.insert('Animal', Species='Bear', Legs=4) |
| 189 | # Notice that ostrich.PreviousZoos is [], whereas leopard is None. |
| 190 | db.insert('Animal', Species='Ostrich', Legs=2, PreviousZoos=[], |
| 191 | Lifespan=103.2) |
| 192 | db.insert('Animal', Species='Centipede', Legs=100) |
| 193 | |
| 194 | emp = db.insert('Animal', Species='Emperor Penguin', Legs=2, ZooID=seaworld) |
| 195 | emp = int(emp['ID']) |
| 196 | adelie = db.insert('Animal', Species='Adelie Penguin', Legs=2, ZooID=seaworld) |
| 197 | adelie = int(adelie['ID']) |
| 198 | |
| 199 | db.insert('Animal', Species='Millipede', Legs=1000000, ZooID=sdz, |
| 200 | PreviousZoos=['Wild Animal Park']) |
| 201 | |
| 202 | ## # Add a mother and child to test relationships |
| 203 | ## bai_yun = Animal(Species='Ape', Legs=2) |
| 204 | ## box.memorize(bai_yun) # ID = 11 |
| 205 | ## self.assertEqual(bai_yun.ID, 11) |
| 206 | ## hua_mei = Animal(Species='Ape', Legs=2, MotherID=bai_yun.ID) |
| 207 | ## box.memorize(hua_mei) # ID = 12 |
| 208 | ## self.assertEqual(hua_mei.ID, 12) |
| 209 | |
| 210 | # Exhibits |
| 211 | db.insert('Exhibit', Name = 'The Penguin Encounter', |
| 212 | ZooID = seaworld, |
| 213 | Animals = [emp, adelie], |
| 214 | PettingAllowed = True, |
| 215 | Acreage = 3.1, |
| 216 | # See ticket #45 |
| 217 | Creators = (u'Richard F\xfcrst', u'Sonja Martin'), |
| 218 | ) |
| 219 | |
| 220 | db.insert('Exhibit', Name = 'Tiger River', |
| 221 | ZooID = sdz, |
| 222 | Animals = [tiger], |
| 223 | PettingAllowed = False, |
| 224 | Acreage = 4, |
| 225 | ) |
| 226 | |
| 227 | # Vets |
| 228 | newids = db.insert('Vet', Name = 'Charles Schroeder', ZooID = sdz) |
| 229 | cs = int(newids['ID']) |
| 230 | self.assertEqual(cs, db['Vet']['ID'].initial) |
| 231 | |
| 232 | newids = db.insert('Vet', Name = 'Jim McBain', ZooID = seaworld) |
| 233 | jm = int(newids['ID']) |
| 234 | |
| 235 | # Visits |
| 236 | for d in every13days: |
| 237 | db.insert('Visit', VetID=cs, AnimalID=tiger, Date=d) |
| 238 | for d in every17days: |
| 239 | db.insert('Visit', VetID=jm, AnimalID=emp, Date=d) |
| 240 | |
| 241 | # Foods |
| 242 | dead_fish = db.insert('Food', Name="Dead Fish", Nutrition=5)['ID'] |
| 243 | live_fish = db.insert('Food', Name="Live Fish", Nutrition=10)['ID'] |
| 244 | bunnies = db.insert('Food', Name="Live Bunny Wabbit", Nutrition=10)['ID'] |
| 245 | steak = db.insert('Food', Name="T-Bone", Nutrition=7)['ID'] |
| 246 | |
| 247 | # Foods --> add preferred and alternate foods |
| 248 | db.save('Animal', ID=lion, |
| 249 | PreferredFoodID=steak, AlternateFoodID=bunnies) |
| 250 | db.save('Animal', ID=tiger, |
| 251 | PreferredFoodID=bunnies, AlternateFoodID=steak) |
| 252 | db.save('Animal', ID=emp, |
| 253 | PreferredFoodID=live_fish, AlternateFoodID=dead_fish) |
| 254 | db.save('Animal', ID=adelie, |
| 255 | PreferredFoodID=live_fish, AlternateFoodID=dead_fish) |
| 256 | |
| 257 | def test_3_Properties(self): |
| 258 | # Zoos |
| 259 | sel = db.select(db['Zoo'], ('Founded', 'Opens', 'Admission'), |
| 260 | logic.Expression(lambda z: z.Name == 'Wild Animal Park')) |
| 261 | data, _ = db.fetch(sel.sql(), db.get_transaction()) |
| 262 | self.assertEqual(data[0][0], datetime.date(2000, 1, 1)) |
| 263 | self.assertEqual(data[0][1], datetime.time(8, 15, 59)) |
| 264 | # This should have been updated when leopard.LastEscape was set. |
| 265 | ## self.assertEqual(WAP.LastEscape, |
| 266 | ## datetime.datetime(2004, 12, 21, 8, 15, 0, 999907)) |
| 267 | self.assertEqual(data[0][2], 4.95) |
| 268 | ## |
| 269 | ## SDZ = box.unit(Zoo, Founded=datetime.date(1835, 9, 13)) |
| 270 | ## self.assertNotEqual(SDZ, None) |
| 271 | ## self.assertEqual(SDZ.Founded, datetime.date(1835, 9, 13)) |
| 272 | ## self.assertEqual(SDZ.Opens, datetime.time(9, 0, 0)) |
| 273 | ## self.assertEqual(SDZ.LastEscape, None) |
| 274 | ## self.assertEqual(float(SDZ.Admission), 0) |
| 275 | ## |
| 276 | ## # Try a magic Sandbox recaller method |
| 277 | ## Biodome = box.Zoo(Name = u'Montr\xe9al Biod\xf4me') |
| 278 | ## self.assertNotEqual(Biodome, None) |
| 279 | ## self.assertEqual(Biodome.Name, u'Montr\xe9al Biod\xf4me') |
| 280 | ## self.assertEqual(Biodome.Founded, datetime.date(1992, 6, 19)) |
| 281 | ## self.assertEqual(Biodome.Opens, datetime.time(9, 0, 0)) |
| 282 | ## self.assertEqual(Biodome.LastEscape, None) |
| 283 | ## self.assertEqual(float(Biodome.Admission), 11.75) |
| 284 | ## |
| 285 | ## if typerefs.fixedpoint: |
| 286 | ## seaworld = box.unit(Zoo, Admission = typerefs.fixedpoint.FixedPoint(60)) |
| 287 | ## else: |
| 288 | ## seaworld = box.unit(Zoo, Admission = float(60)) |
| 289 | ## self.assertNotEqual(seaworld, None) |
| 290 | ## self.assertEqual(seaworld.Name, u'Sea_World') |
| 291 | ## |
| 292 | ## # Animals |
| 293 | ## leopard = box.unit(Animal, Species='Leopard') |
| 294 | ## self.assertEqual(leopard.Species, 'Leopard') |
| 295 | ## self.assertEqual(leopard.Legs, 4) |
| 296 | ## self.assertEqual(leopard.Lifespan, 73.5) |
| 297 | ## self.assertEqual(leopard.ZooID, WAP.ID) |
| 298 | ## self.assertEqual(leopard.PreviousZoos, None) |
| 299 | ## ## self.assertEqual(leopard.LastEscape, |
| 300 | ## ## datetime.datetime(2004, 12, 21, 8, 15, 0, 999907)) |
| 301 | ## |
| 302 | ## ostrich = box.unit(Animal, Species='Ostrich') |
| 303 | ## self.assertEqual(ostrich.Species, 'Ostrich') |
| 304 | ## self.assertEqual(ostrich.Legs, 2) |
| 305 | ## self.assertEqual(ostrich.ZooID, None) |
| 306 | ## self.assertEqual(ostrich.PreviousZoos, []) |
| 307 | ## self.assertEqual(ostrich.LastEscape, None) |
| 308 | ## |
| 309 | ## millipede = box.unit(Animal, Legs=1000000) |
| 310 | ## self.assertEqual(millipede.Species, 'Millipede') |
| 311 | ## self.assertEqual(millipede.Legs, 1000000) |
| 312 | ## self.assertEqual(millipede.ZooID, SDZ.ID) |
| 313 | ## self.assertEqual(millipede.PreviousZoos, [WAP.Name]) |
| 314 | ## self.assertEqual(millipede.LastEscape, None) |
| 315 | ## |
| 316 | ## # Test that strings in a list get decoded correctly. |
| 317 | ## # See http://projects.amor.org/dejavu/ticket/50 |
| 318 | ## tiger = box.unit(Animal, Species='Tiger') |
| 319 | ## self.assertEqual(tiger.PreviousZoos, ["animal\\universe"]) |
| 320 | ## |
| 321 | ## # Test our 8000-byte limit. |
| 322 | ## # len(pickle.dumps(["f" * (8000 - 14)]) == 8000 |
| 323 | ## slug = box.unit(Animal, Species='Slug') |
| 324 | ## self.assertEqual(len(slug.PreviousZoos[0]), 8000 - 14) |
| 325 | ## |
| 326 | ## # Exhibits |
| 327 | ## exes = box.recall(Exhibit) |
| 328 | ## self.assertEqual(len(exes), 2) |
| 329 | ## if exes[0].Name == 'The Penguin Encounter': |
| 330 | ## pe = exes[0] |
| 331 | ## tr = exes[1] |
| 332 | ## else: |
| 333 | ## pe = exes[1] |
| 334 | ## tr = exes[0] |
| 335 | ## self.assertEqual(pe.ZooID, seaworld.ID) |
| 336 | ## self.assertEqual(len(pe.Animals), 2) |
| 337 | ## self.assertEqual(float(pe.Acreage), 3.1) |
| 338 | ## self.assertEqual(pe.PettingAllowed, True) |
| 339 | ## self.assertEqual(pe.Creators, (u'Richard F\xfcrst', u'Sonja Martin')) |
| 340 | ## |
| 341 | ## self.assertEqual(tr.ZooID, SDZ.ID) |
| 342 | ## self.assertEqual(len(tr.Animals), 1) |
| 343 | ## self.assertEqual(float(tr.Acreage), 4) |
| 344 | ## self.assertEqual(tr.PettingAllowed, False) |
| 345 | ## |
| 346 | ## finally: |
| 347 | ## box.flush_all() |
| 348 | ## |
| 349 | ## def test_4_Expressions(self): |
| 350 | ## box = arena.new_sandbox() |
| 351 | ## try: |
| 352 | ## def matches(lam, cls=Animal): |
| 353 | ## # We flush_all to ensure a DB hit each time. |
| 354 | ## box.flush_all() |
| 355 | ## return len(box.recall(cls, (lam))) |
| 356 | ## |
| 357 | ## zoos = box.recall(Zoo) |
| 358 | ## self.assertEqual(zoos[0].dirty(), False) |
| 359 | ## self.assertEqual(len(zoos), 4) |
| 360 | ## self.assertEqual(matches(lambda x: True), 12) |
| 361 | ## self.assertEqual(matches(lambda x: x.Legs == 4), 4) |
| 362 | ## self.assertEqual(matches(lambda x: x.Legs == 2), 5) |
| 363 | ## self.assertEqual(matches(lambda x: x.Legs >= 2 and x.Legs < 20), 9) |
| 364 | ## self.assertEqual(matches(lambda x: x.Legs > 10), 2) |
| 365 | ## self.assertEqual(matches(lambda x: x.Lifespan > 70), 2) |
| 366 | ## self.assertEqual(matches(lambda x: x.Species.startswith('L')), 2) |
| 367 | ## self.assertEqual(matches(lambda x: x.Species.endswith('pede')), 2) |
| 368 | ## self.assertEqual(matches(lambda x: x.LastEscape != None), 1) |
| 369 | ## self.assertEqual(matches(lambda x: x.LastEscape is not None), 1) |
| 370 | ## self.assertEqual(matches(lambda x: None == x.LastEscape), 11) |
| 371 | ## |
| 372 | ## # In operator (containedby) |
| 373 | ## self.assertEqual(matches(lambda x: 'pede' in x.Species), 2) |
| 374 | ## self.assertEqual(matches(lambda x: x.Species in ('Lion', 'Tiger', 'Bear')), 3) |
| 375 | ## |
| 376 | ## # Try In with cell references |
| 377 | ## class thing(object): pass |
| 378 | ## pet, pet2 = thing(), thing() |
| 379 | ## pet.Name, pet2.Name = 'Slug', 'Ostrich' |
| 380 | ## self.assertEqual(matches(lambda x: x.Species in (pet.Name, pet2.Name)), 2) |
| 381 | ## |
| 382 | ## # logic and other functions |
| 383 | ## self.assertEqual(matches(lambda x: dejavu.ieq(x.Species, 'slug')), 1) |
| 384 | ## self.assertEqual(matches(lambda x: dejavu.icontains(x.Species, 'PEDE')), 2) |
| 385 | ## self.assertEqual(matches(lambda x: dejavu.icontains(('Lion', 'Banana'), x.Species)), 1) |
| 386 | ## f = lambda x: dejavu.icontainedby(x.Species, ('Lion', 'Bear', 'Leopard')) |
| 387 | ## self.assertEqual(matches(f), 3) |
| 388 | ## name = 'Lion' |
| 389 | ## self.assertEqual(matches(lambda x: len(x.Species) == len(name)), 3) |
| 390 | ## |
| 391 | ## # This broke sometime in 2004. Rev 32 seems to have fixed it. |
| 392 | ## self.assertEqual(matches(lambda x: 'i' in x.Species), 7) |
| 393 | ## |
| 394 | ## # Test now(), today(), year(), month(), day() |
| 395 | ## self.assertEqual(matches(lambda x: x.Founded != None |
| 396 | ## and x.Founded < dejavu.today(), Zoo), 3) |
| 397 | ## self.assertEqual(matches(lambda x: x.LastEscape == dejavu.now()), 0) |
| 398 | ## self.assertEqual(matches(lambda x: dejavu.year(x.LastEscape) == 2004), 1) |
| 399 | ## self.assertEqual(matches(lambda x: dejavu.month(x.LastEscape) == 12), 1) |
| 400 | ## self.assertEqual(matches(lambda x: dejavu.day(x.LastEscape) == 21), 1) |
| 401 | ## |
| 402 | ## |
| 403 | ## # Test AND, OR with cannot_represent. |
| 404 | ## # Notice that we reference a method ('count') which no |
| 405 | ## # known SM handles, so it will default back to Expr.eval(). |
| 406 | ## self.assertEqual(matches(lambda x: 'p' in x.Species |
| 407 | ## and x.Species.count('e') > 1), 3) |
| 408 | ## |
| 409 | ## # This broke in MSAccess (storeado) in April 2005, due to a bug in |
| 410 | ## # db.SQLDecompiler.visit_CALL_FUNCTION (append TOS, not replace!). |
| 411 | ## box.flush_all() |
| 412 | ## e = logic.Expression(lambda x, **kw: x.LastEscape != None |
| 413 | ## and x.LastEscape >= datetime.datetime(kw['Year'], 12, 1) |
| 414 | ## and x.LastEscape < datetime.datetime(kw['Year'], 12, 31) |
| 415 | ## ) |
| 416 | ## e.bind_args(Year=2004) |
| 417 | ## units = box.recall(Animal, e) |
| 418 | ## self.assertEqual(len(units), 1) |
| 419 | ## |
| 420 | ## # Test wildcards in LIKE. This fails with SQLite <= 3.0.8, |
| 421 | ## # so make sure it's always at the end of this method so |
| 422 | ## # it doesn't preclude running the other tests. |
| 423 | ## box.flush_all() |
| 424 | ## units = box.recall(Zoo, lambda x: "_" in x.Name) |
| 425 | ## self.assertEqual(len(units), 1) |
| 426 | ## finally: |
| 427 | ## box.flush_all() |
| 428 | ## |
| 429 | ## def test_5_Aggregates(self): |
| 430 | ## box = arena.new_sandbox() |
| 431 | ## try: |
| 432 | ## # views |
| 433 | ## legs = [x[0] for x in box.view(Animal, ['Legs'])] |
| 434 | ## legs.sort() |
| 435 | ## self.assertEqual(legs, [1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 100, 1000000]) |
| 436 | ## |
| 437 | ## expected = {'Leopard': 73.5, |
| 438 | ## 'Slug': .75, |
| 439 | ## 'Tiger': None, |
| 440 | ## 'Lion': None, |
| 441 | ## 'Bear': None, |
| 442 | ## 'Ostrich': 103.2, |
| 443 | ## 'Centipede': None, |
| 444 | ## 'Emperor Penguin': None, |
| 445 | ## 'Adelie Penguin': None, |
| 446 | ## 'Millipede': None, |
| 447 | ## 'Ape': None, |
| 448 | ## } |
| 449 | ## for species, lifespan in box.view(Animal, ['Species', 'Lifespan']): |
| 450 | ## if expected[species] is None: |
| 451 | ## self.assertEqual(lifespan, None) |
| 452 | ## else: |
| 453 | ## self.assertAlmostEqual(expected[species], lifespan, places=5) |
| 454 | ## |
| 455 | ## expected = [u'Montr\xe9al Biod\xf4me', 'Wild Animal Park'] |
| 456 | ## e = (lambda x: x.Founded != None |
| 457 | ## and x.Founded <= dejavu.today() |
| 458 | ## and x.Founded >= datetime.date(1990, 1, 1)) |
| 459 | ## values = [val[0] for val in box.view(Zoo, ['Name'], e)] |
| 460 | ## for name in expected: |
| 461 | ## self.assert_(name in values) |
| 462 | ## |
| 463 | ## # distinct |
| 464 | ## legs = box.distinct(Animal, ['Legs']) |
| 465 | ## legs.sort() |
| 466 | ## self.assertEqual(legs, [1, 2, 4, 100, 1000000]) |
| 467 | ## |
| 468 | ## # This may raise a warning on some DB's. |
| 469 | ## f = (lambda x: x.Species == 'Lion') |
| 470 | ## escapees = box.distinct(Animal, ['Legs'], f) |
| 471 | ## self.assertEqual(escapees, [4]) |
| 472 | ## |
| 473 | ## # range should return a sorted list |
| 474 | ## legs = box.range(Animal, 'Legs', lambda x: x.Legs <= 100) |
| 475 | ## self.assertEqual(legs, range(1, 101)) |
| 476 | ## topics = box.range(Exhibit, 'Name') |
| 477 | ## self.assertEqual(topics, ['The Penguin Encounter', 'Tiger River']) |
| 478 | ## vets = box.range(Vet, 'Name') |
| 479 | ## self.assertEqual(vets, ['Charles Schroeder', 'Jim McBain']) |
| 480 | ## finally: |
| 481 | ## box.flush_all() |
| 482 | ## |
| 483 | ## def test_6_Editing(self): |
| 484 | ## # Edit |
| 485 | ## box = arena.new_sandbox() |
| 486 | ## try: |
| 487 | ## SDZ = box.unit(Zoo, Name='San Diego Zoo') |
| 488 | ## SDZ.Name = 'The San Diego Zoo' |
| 489 | ## SDZ.Founded = datetime.date(1900, 1, 1) |
| 490 | ## SDZ.Opens = datetime.time(7, 30, 0) |
| 491 | ## SDZ.Admission = "35.00" |
| 492 | ## finally: |
| 493 | ## box.flush_all() |
| 494 | ## |
| 495 | ## # Test edits |
| 496 | ## box = arena.new_sandbox() |
| 497 | ## try: |
| 498 | ## SDZ = box.unit(Zoo, Name='The San Diego Zoo') |
| 499 | ## self.assertEqual(SDZ.Name, 'The San Diego Zoo') |
| 500 | ## self.assertEqual(SDZ.Founded, datetime.date(1900, 1, 1)) |
| 501 | ## self.assertEqual(SDZ.Opens, datetime.time(7, 30, 0)) |
| 502 | ## if typerefs.fixedpoint: |
| 503 | ## self.assertEqual(SDZ.Admission, typerefs.fixedpoint.FixedPoint(35, 2)) |
| 504 | ## else: |
| 505 | ## self.assertEqual(SDZ.Admission, 35.0) |
| 506 | ## finally: |
| 507 | ## box.flush_all() |
| 508 | ## |
| 509 | ## # Change it back |
| 510 | ## box = arena.new_sandbox() |
| 511 | ## try: |
| 512 | ## SDZ = box.unit(Zoo, Name='The San Diego Zoo') |
| 513 | ## SDZ.Name = 'San Diego Zoo' |
| 514 | ## SDZ.Founded = datetime.date(1835, 9, 13) |
| 515 | ## SDZ.Opens = datetime.time(9, 0, 0) |
| 516 | ## SDZ.Admission = "0" |
| 517 | ## finally: |
| 518 | ## box.flush_all() |
| 519 | ## |
| 520 | ## # Test re-edits |
| 521 | ## box = arena.new_sandbox() |
| 522 | ## try: |
| 523 | ## SDZ = box.unit(Zoo, Name='San Diego Zoo') |
| 524 | ## self.assertEqual(SDZ.Name, 'San Diego Zoo') |
| 525 | ## self.assertEqual(SDZ.Founded, datetime.date(1835, 9, 13)) |
| 526 | ## self.assertEqual(SDZ.Opens, datetime.time(9, 0, 0)) |
| 527 | ## if typerefs.fixedpoint: |
| 528 | ## self.assertEqual(SDZ.Admission, typerefs.fixedpoint.FixedPoint(0, 2)) |
| 529 | ## else: |
| 530 | ## self.assertEqual(SDZ.Admission, 0.0) |
| 531 | ## finally: |
| 532 | ## box.flush_all() |
| 533 | ## |
| 534 | ## def test_7_Multirecall(self): |
| 535 | ## box = arena.new_sandbox() |
| 536 | ## try: |
| 537 | ## f = (lambda z, a: z.Name == 'San Diego Zoo') |
| 538 | ## zooed_animals = box.recall(Zoo & Animal, f) |
| 539 | ## self.assertEqual(len(zooed_animals), 2) |
| 540 | ## |
| 541 | ## SDZ = box.unit(Zoo, Name='San Diego Zoo') |
| 542 | ## aid = 0 |
| 543 | ## for z, a in zooed_animals: |
| 544 | ## self.assertEqual(id(z), id(SDZ)) |
| 545 | ## self.assertNotEqual(id(a), aid) |
| 546 | ## aid = id(a) |
| 547 | ## |
| 548 | ## # Assert that multirecalls with no matching related units returns |
| 549 | ## # no matches for the initial class, since all joins are INNER. |
| 550 | ## # We're also going to test that you can combine a one-arg expr |
| 551 | ## # with a two-arg expr. |
| 552 | ## sdexpr = logic.filter(Name='San Diego Zoo') |
| 553 | ## leo = lambda z, a: a.Species == 'Leopard' |
| 554 | ## zooed_animals = box.recall(Zoo & Animal, sdexpr + leo) |
| 555 | ## self.assertEqual(len(zooed_animals), 0) |
| 556 | ## |
| 557 | ## # Now try the same expr with INNER, LEFT, and RIGHT JOINs. |
| 558 | ## zooed_animals = box.recall(Zoo & Animal) |
| 559 | ## self.assertEqual(len(zooed_animals), 6) |
| 560 | ## self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]), |
| 561 | ## set([("Wild Animal Park", "Leopard"), |
| 562 | ## ("Wild Animal Park", "Lion"), |
| 563 | ## ("San Diego Zoo", "Tiger"), |
| 564 | ## ("San Diego Zoo", "Millipede"), |
| 565 | ## ("Sea_World", "Emperor Penguin"), |
| 566 | ## ("Sea_World", "Adelie Penguin")])) |
| 567 | ## |
| 568 | ## zooed_animals = box.recall(Zoo >> Animal) |
| 569 | ## self.assertEqual(len(zooed_animals), 12) |
| 570 | ## self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]), |
| 571 | ## set([("Wild Animal Park", "Leopard"), |
| 572 | ## ("Wild Animal Park", "Lion"), |
| 573 | ## ("San Diego Zoo", "Tiger"), |
| 574 | ## ("San Diego Zoo", "Millipede"), |
| 575 | ## ("Sea_World", "Emperor Penguin"), |
| 576 | ## ("Sea_World", "Adelie Penguin"), |
| 577 | ## (None, "Slug"), |
| 578 | ## (None, "Bear"), |
| 579 | ## (None, "Ostrich"), |
| 580 | ## (None, "Centipede"), |
| 581 | ## (None, "Ape"), |
| 582 | ## (None, "Ape"), |
| 583 | ## ])) |
| 584 | ## |
| 585 | ## zooed_animals = box.recall(Zoo << Animal) |
| 586 | ## self.assertEqual(len(zooed_animals), 7) |
| 587 | ## self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]), |
| 588 | ## set([("Wild Animal Park", "Leopard"), |
| 589 | ## ("Wild Animal Park", "Lion"), |
| 590 | ## ("San Diego Zoo", "Tiger"), |
| 591 | ## ("San Diego Zoo", "Millipede"), |
| 592 | ## ("Sea_World", "Emperor Penguin"), |
| 593 | ## ("Sea_World", "Adelie Penguin"), |
| 594 | ## (u'Montr\xe9al Biod\xf4me', None), |
| 595 | ## ])) |
| 596 | ## |
| 597 | ## # Try a multiple-arg expression |
| 598 | ## f = (lambda a, z: a.Legs >= 4 and z.Admission < 10) |
| 599 | ## animal_zoos = box.recall(Animal & Zoo, f) |
| 600 | ## self.assertEqual(len(animal_zoos), 4) |
| 601 | ## names = [a.Species for a, z in animal_zoos] |
| 602 | ## names.sort() |
| 603 | ## self.assertEqual(names, ['Leopard', 'Lion', 'Millipede', 'Tiger']) |
| 604 | ## |
| 605 | ## # Let's try three joined classes just for the sadistic fun of it. |
| 606 | ## tree = (Animal >> Zoo) >> Vet |
| 607 | ## f = (lambda a, z, v: z.Name == 'Sea_World') |
| 608 | ## self.assertEqual(len(box.recall(tree, f)), 2) |
| 609 | ## |
| 610 | ## # MSAccess can't handle an INNER JOIN nested in an OUTER JOIN. |
| 611 | ## # Test that this fails for MSAccess, but works for other SM's. |
| 612 | ## trees = [] |
| 613 | ## def make_tree(): |
| 614 | ## trees.append( (Animal & Zoo) >> Vet ) |
| 615 | ## warnings.filterwarnings("ignore", category=errors.FeatureWarning) |
| 616 | ## try: |
| 617 | ## make_tree() |
| 618 | ## finally: |
| 619 | ## warnings.filters.pop(0) |
| 620 | ## |
| 621 | ## azv = [] |
| 622 | ## def set_azv(): |
| 623 | ## f = (lambda a, z, v: z.Name == 'Sea_World') |
| 624 | ## azv.append(box.recall(trees[0], f)) |
| 625 | ## |
| 626 | ## smname = arena.stores['testSM'].__class__.__name__ |
| 627 | ## if smname in ("StorageManagerADO_MSAccess",): |
| 628 | ## self.assertRaises(pythoncom.com_error, set_azv) |
| 629 | ## else: |
| 630 | ## set_azv() |
| 631 | ## self.assertEqual(len(azv[0]), 2) |
| 632 | ## |
| 633 | ## # Try mentioning the same class twice. |
| 634 | ## tree = (Animal << Animal) |
| 635 | ## f = (lambda anim, mother: mother.ID != None) |
| 636 | ## animals = [mother.ID for anim, mother in box.recall(tree, f)] |
| 637 | ## self.assertEqual(animals, [11]) |
| 638 | ## finally: |
| 639 | ## box.flush_all() |
| 640 | ## |
| 641 | ## def test_8_CustomAssociations(self): |
| 642 | ## box = arena.new_sandbox() |
| 643 | ## try: |
| 644 | ## # Try different association paths |
| 645 | ## std_expected = ['Live Bunny Wabbit', 'Live Fish', 'Live Fish', 'T-Bone'] |
| 646 | ## cus_expected = ['Dead Fish', 'Dead Fish', 'Live Bunny Wabbit', 'T-Bone'] |
| 647 | ## uj = Animal & Food |
| 648 | ## for path, expected in [# standard path |
| 649 | ## (None, std_expected), |
| 650 | ## # custom path |
| 651 | ## ('Alternate Food', cus_expected)]: |
| 652 | ## |
| 653 | ## uj.path = path |
| 654 | ## foods = [food for animal, food in box.recall(uj)] |
| 655 | ## foods.sort(dejavu.sort('Name')) |
| 656 | ## self.assertEqual([f.Name for f in foods], expected) |
| 657 | ## |
| 658 | ## # Test the magic association methods |
| 659 | ## tiger = box.unit(Animal, Species='Tiger') |
| 660 | ## self.assertEqual(tiger.Food().Name, 'Live Bunny Wabbit') |
| 661 | ## self.assertEqual(tiger.AlternateFood().Name, 'T-Bone') |
| 662 | ## |
| 663 | ## finally: |
| 664 | ## box.flush_all() |
| 665 | ## |
| 666 | ## def test_9_arena_views(self): |
| 667 | ## # views |
| 668 | ## legs = [x[0] for x in arena.view(Animal, ['Legs'])] |
| 669 | ## legs.sort() |
| 670 | ## self.assertEqual(legs, [1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 100, 1000000]) |
| 671 | ## |
| 672 | ## expected = {'Leopard': 73.5, |
| 673 | ## 'Slug': .75, |
| 674 | ## 'Tiger': None, |
| 675 | ## 'Lion': None, |
| 676 | ## 'Bear': None, |
| 677 | ## 'Ostrich': 103.2, |
| 678 | ## 'Centipede': None, |
| 679 | ## 'Emperor Penguin': None, |
| 680 | ## 'Adelie Penguin': None, |
| 681 | ## 'Millipede': None, |
| 682 | ## 'Ape': None, |
| 683 | ## } |
| 684 | ## for species, lifespan in arena.view(Animal, ['Species', 'Lifespan']): |
| 685 | ## if expected[species] is None: |
| 686 | ## self.assertEqual(lifespan, None) |
| 687 | ## else: |
| 688 | ## self.assertAlmostEqual(expected[species], lifespan, places=5) |
| 689 | ## |
| 690 | ## expected = [u'Montr\xe9al Biod\xf4me', 'Wild Animal Park'] |
| 691 | ## e = (lambda x: x.Founded != None |
| 692 | ## and x.Founded <= dejavu.today() |
| 693 | ## and x.Founded >= datetime.date(1990, 1, 1)) |
| 694 | ## values = [val[0] for val in arena.view(Zoo, ['Name'], e)] |
| 695 | ## for name in expected: |
| 696 | ## self.assert_(name in values) |
| 697 | ## |
| 698 | ## # distinct |
| 699 | ## legs = arena.distinct(Animal, ['Legs']) |
| 700 | ## legs.sort() |
| 701 | ## self.assertEqual(legs, [1, 2, 4, 100, 1000000]) |
| 702 | ## |
| 703 | ## # This may raise a warning on some DB's. |
| 704 | ## f = (lambda x: x.Species == 'Lion') |
| 705 | ## escapees = arena.distinct(Animal, ['Legs'], f) |
| 706 | ## self.assertEqual(escapees, [4]) |
| 707 | ## |
| 708 | ## # range should return a sorted list |
| 709 | ## legs = arena.range(Animal, 'Legs', lambda x: x.Legs <= 100) |
| 710 | ## self.assertEqual(legs, range(1, 101)) |
| 711 | ## topics = arena.range(Exhibit, 'Name') |
| 712 | ## self.assertEqual(topics, ['The Penguin Encounter', 'Tiger River']) |
| 713 | ## vets = arena.range(Vet, 'Name') |
| 714 | ## self.assertEqual(vets, ['Charles Schroeder', 'Jim McBain']) |
| 715 | ## |
| 716 | ## def test_Iteration(self): |
| 717 | ## box = arena.new_sandbox() |
| 718 | ## try: |
| 719 | ## # Test box.unit inside of xrecall |
| 720 | ## for visit in box.xrecall(Visit, VetID=1): |
| 721 | ## firstvisit = box.unit(Visit, VetID=1, Date=Jan_1_2001) |
| 722 | ## self.assertEqual(firstvisit.VetID, 1) |
| 723 | ## self.assertEqual(visit.VetID, 1) |
| 724 | ## |
| 725 | ## # Test recall inside of xrecall |
| 726 | ## for visit in box.xrecall(Visit, VetID=1): |
| 727 | ## f = (lambda x: x.VetID == 1 and x.ID != visit.ID) |
| 728 | ## othervisits = box.recall(Visit, f) |
| 729 | ## self.assertEqual(len(othervisits), len(every13days) - 1) |
| 730 | ## |
| 731 | ## # Test far associations inside of xrecall |
| 732 | ## for visit in box.xrecall(Visit, VetID=1): |
| 733 | ## # visit.Vet is a ToOne association, so will return a unit or None. |
| 734 | ## vet = visit.Vet() |
| 735 | ## self.assertEqual(vet.ID, 1) |
| 736 | ## finally: |
| 737 | ## box.flush_all() |
| 738 | ## |
| 739 | ## def test_Subclassing(self): |
| 740 | ## box = arena.new_sandbox() |
| 741 | ## try: |
| 742 | ## box.memorize(Visit(VetID=21, ZooID=1, AnimalID=1)) |
| 743 | ## box.memorize(Visit(VetID=21, ZooID=1, AnimalID=2)) |
| 744 | ## box.memorize(Visit(VetID=32, ZooID=1, AnimalID=3)) |
| 745 | ## box.memorize(Lecture(VetID=21, ZooID=1, Topic='Cage Cleaning')) |
| 746 | ## box.memorize(Lecture(VetID=21, ZooID=1, Topic='Ape Mating Habits')) |
| 747 | ## box.memorize(Lecture(VetID=32, ZooID=3, Topic='Your Tiger and Steroids')) |
| 748 | ## |
| 749 | ## visits = box.recall(Visit, inherit=True, ZooID=1) |
| 750 | ## self.assertEqual(len(visits), 5) |
| 751 | ## |
| 752 | ## box.flush_all() |
| 753 | ## |
| 754 | ## box = arena.new_sandbox() |
| 755 | ## visits = box.recall(Visit, inherit=True, VetID=21) |
| 756 | ## self.assertEqual(len(visits), 4) |
| 757 | ## cc = [x for x in visits |
| 758 | ## if getattr(x, "Topic", None) == "Cage Cleaning"] |
| 759 | ## self.assertEqual(len(cc), 1) |
| 760 | ## |
| 761 | ## # Checking for non-existent attributes in/from subclasses |
| 762 | ## # isn't supported yet. |
| 763 | ## ## f = logic.filter(AnimalID=2) |
| 764 | ## ## self.assertEqual(len(box.recall(Visit, f)), 1) |
| 765 | ## ## self.assertEqual(len(box.recall(Lecture, f)), 0) |
| 766 | ## finally: |
| 767 | ## box.flush_all() |
| 768 | ## |
| 769 | ## def test_DB_Introspection(self): |
| 770 | ## s = arena.stores.values()[0] |
| 771 | ## if not hasattr(s, "db"): |
| 772 | ## print "not a db (skipped) ", |
| 773 | ## return |
| 774 | ## |
| 775 | ## zootable = s.db['Zoo'] |
| 776 | ## cols = zootable |
| 777 | ## self.assertEqual(len(cols), 6) |
| 778 | ## idcol = cols['ID'] |
| 779 | ## self.assertEqual(s.db.python_type(idcol.dbtype), int) |
| 780 | ## for prop in Zoo.properties: |
| 781 | ## self.assertEqual(cols[prop].key, |
| 782 | ## prop in Zoo.identifiers) |
| 783 | ## |
| 784 | ## def test_zzz_Schema_Upgrade(self): |
| 785 | ## # Must run last. |
| 786 | ## zs = ZooSchema(arena) |
| 787 | ## |
| 788 | ## # In this first upgrade, we simulate the case where the code was |
| 789 | ## # upgraded, and the database schema upgrade performed afterward. |
| 790 | ## # The Schema.latest property is set, and upgrade() is called with |
| 791 | ## # no argument (which should upgrade us to "latest"). |
| 792 | ## Animal.set_property("ExhibitID") |
| 793 | ## # Test numeric default (see hack in storeado for MS Access). |
| 794 | ## prop = Animal.set_property("Stomachs", int) |
| 795 | ## prop.default = 1 |
| 796 | ## zs.latest = 2 |
| 797 | ## zs.upgrade() |
| 798 | ## |
| 799 | ## # In this example, we simulate the developer who wants to put |
| 800 | ## # model changes inline with database changes (see upgrade_to_3). |
| 801 | ## # We do not set latest, but instead supply an arg to upgrade(). |
| 802 | ## zs.upgrade(3) |
| 803 | ## |
| 804 | ## # Test that Animals have a new "Family" property, and an ExhibitID. |
| 805 | ## box = arena.new_sandbox() |
| 806 | ## try: |
| 807 | ## emp = box.unit(Animal, Family='Emperor Penguin') |
| 808 | ## self.assertEqual(emp.ExhibitID, 'The Penguin Encounter') |
| 809 | ## finally: |
| 810 | ## box.flush_all() |
| 811 | |
| 812 | |
| 813 | class IsolationTests(unittest.TestCase): |
| 814 | |
| 815 | verbose = False |
| 816 | _boxid = 0 |
| 817 | |
| 818 | def setUp(self): |
| 819 | s = arena.stores.values()[0] |
| 820 | if hasattr(s, "db"): |
| 821 | self.db = s.db |
| 822 | else: |
| 823 | self.db = None |
| 824 | |
| 825 | try: |
| 826 | self.old_implicit = s.db.implicit_trans |
| 827 | s.db.implicit_trans = False |
| 828 | self.old_tkey = s.db.transaction_key |
| 829 | # Use an explicit 'boxid' for the transaction key |
| 830 | s.db.transaction_key = lambda: self.boxid |
| 831 | except AttributeError: |
| 832 | self.old_implicit = None |
| 833 | |
| 834 | |
| 835 | def tearDown(self): |
| 836 | if self.db and self.old_implicit is not None: |
| 837 | self.db.implicit_trans = self.old_implicit |
| 838 | self.db.transaction_key = self.old_tkey |
| 839 | |
| 840 | def restore(self): |
| 841 | self.boxid = 0 |
| 842 | box = arena.new_sandbox() |
| 843 | box.start() |
| 844 | jim = box.unit(Vet, Name = 'Jim McBain') |
| 845 | jim.City = None |
| 846 | box.flush_all() |
| 847 | |
| 848 | def cleanup_boxes(self): |
| 849 | try: |
| 850 | self.boxid = 1 |
| 851 | self.box1.rollback() |
| 852 | except: pass |
| 853 | try: |
| 854 | self.boxid = 2 |
| 855 | self.box2.rollback() |
| 856 | except: pass |
| 857 | |
| 858 | # Destroy refs so the conns can go back in the pool. |
| 859 | del self.box1, self.box2 |
| 860 | |
| 861 | def attempt(self, testfunc, anomaly_name, level): |
| 862 | self.restore() |
| 863 | |
| 864 | self.boxid = 1 |
| 865 | self.box1 = arena.new_sandbox() |
| 866 | self.box1.start(level) |
| 867 | |
| 868 | self.boxid = 2 |
| 869 | self.box2 = arena.new_sandbox() |
| 870 | self.box2.start(level) |
| 871 | |
| 872 | try: |
| 873 | testfunc(level) |
| 874 | except AssertionError: |
| 875 | self.cleanup_boxes() |
| 876 | if level.forbids(anomaly_name): |
| 877 | warnings.warn("%r allowed anomaly %r." % |
| 878 | (level, anomaly_name)) |
| 879 | except: |
| 880 | if self.db.is_lock_error(sys.exc_info()[1]): |
| 881 | self.cleanup_boxes() |
| 882 | if not level.forbids(anomaly_name): |
| 883 | warnings.warn("%r prevented anomaly %r with an error." % |
| 884 | (level, anomaly_name)) |
| 885 | else: |
| 886 | self.cleanup_boxes() |
| 887 | raise |
| 888 | else: |
| 889 | self.cleanup_boxes() |
| 890 | if not level.forbids(anomaly_name): |
| 891 | warnings.warn("%r prevented anomaly %r." % |
| 892 | (level, anomaly_name)) |
| 893 | |
| 894 | def _get_boxid(self): |
| 895 | return self._boxid |
| 896 | def _set_boxid(self, val): |
| 897 | if self.verbose: |
| 898 | print val, |
| 899 | self._boxid = val |
| 900 | boxid = property(_get_boxid, _set_boxid) |
| 901 | |
| 902 | def test_dirty_read(self): |
| 903 | def dirty_read(level): |
| 904 | # Write City 1 |
| 905 | self.boxid = 1 |
| 906 | jim1 = self.box1.unit(Vet, Name = 'Jim McBain') |
| 907 | jim1.City = "Addis Ababa" |
| 908 | self.box1.repress(jim1) |
| 909 | |
| 910 | # Read City 2. |
| 911 | self.boxid = 2 |
| 912 | jim2 = self.box2.unit(Vet, Name = 'Jim McBain') |
| 913 | # If READ UNCOMMITTED or lower, this should fail |
| 914 | assert jim2.City is None |
| 915 | |
| 916 | for level in geniusql.levels: |
| 917 | if self.verbose: |
| 918 | |
| 919 | print level, |
| 920 | if level.name in self.db.isolation_levels: |
| 921 | self.attempt(dirty_read, "Dirty Read", level) |
| 922 | |
| 923 | def test_nonrepeatable_read(self): |
| 924 | def nonrepeatable_read(level): |
| 925 | # Read City 1 |
| 926 | self.boxid = 1 |
| 927 | jim1 = self.box1.unit(Vet, Name = 'Jim McBain') |
| 928 | val1 = jim1.City |
| 929 | assert val1 is None |
| 930 | self.box1.repress(jim1) |
| 931 | |
| 932 | # Write City 2. |
| 933 | self.boxid = 2 |
| 934 | jim2 = self.box2.unit(Vet, Name = 'Jim McBain') |
| 935 | jim2.City = "Tehachapi" |
| 936 | self.box2.flush_all() |
| 937 | |
| 938 | # Re-read City 1 |
| 939 | self.boxid = 1 |
| 940 | jim1 = self.box1.unit(Vet, Name = 'Jim McBain') |
| 941 | # If READ COMMITTED or lower, this should fail |
| 942 | assert jim1.City == val1 |
| 943 | |
| 944 | for level in geniusql.levels: |
| 945 | if self.verbose: |
| 946 | |
| 947 | print level, |
| 948 | if level.name in self.db.isolation_levels: |
| 949 | self.attempt(nonrepeatable_read, "Nonrepeatable Read", level) |
| 950 | |
| 951 | def test_phantom(self): |
| 952 | def phantom(level): |
| 953 | # Read City 1 |
| 954 | self.boxid = 1 |
| 955 | pvets = self.box1.recall(Vet, City = 'Poughkeepsie') |
| 956 | assert len(pvets) == 0 |
| 957 | |
| 958 | # Write City 2. |
| 959 | self.boxid = 2 |
| 960 | jim2 = self.box2.unit(Vet, Name = 'Jim McBain') |
| 961 | jim2.City = "Poughkeepsie" |
| 962 | self.box2.flush_all() |
| 963 | |
| 964 | # Re-read City 1 |
| 965 | self.boxid = 1 |
| 966 | pvets = self.box1.recall(Vet, City = 'Poughkeepsie') |
| 967 | # If REPEATABLE READ or lower, this should fail |
| 968 | assert len(pvets) == 0 |
| 969 | |
| 970 | for level in geniusql.levels: |
| 971 | if self.verbose: |
| 972 | |
| 973 | print level, |
| 974 | if level.name in self.db.isolation_levels: |
| 975 | self.attempt(phantom, "Phantom", level) |
| 976 | |
| 977 | |
| 978 | class ConcurrencyTests(unittest.TestCase): |
| 979 | |
| 980 | def test_Multithreading(self): |
| 981 | ## print "skipped ", |
| 982 | ## return |
| 983 | |
| 984 | s = arena.stores.values()[0] |
| 985 | |
| 986 | # Test threads overlapping on separate sandboxes |
| 987 | f = (lambda x: x.Legs == 4) |
| 988 | def box_per_thread(): |
| 989 | # Notice that, although we write changes in each thread, |
| 990 | # we only assert the unchanged data, since the order of |
| 991 | # thread execution can not be guaranteed. |
| 992 | box = arena.new_sandbox() |
| 993 | try: |
| 994 | quadrupeds = box.recall(Animal, f) |
| 995 | self.assertEqual(len(quadrupeds), 4) |
| 996 | quadrupeds[0].Age += 1.0 |
| 997 | finally: |
| 998 | box.flush_all() |
| 999 | ts = [] |
| 1000 | # PostgreSQL, for example, has a default max_connections of 100. |
| 1001 | for x in range(99): |
| 1002 | t = threading.Thread(target=box_per_thread) |
| 1003 | t.start() |
| 1004 | ts.append(t) |
| 1005 | for t in ts: |
| 1006 | t.join() |
| 1007 | |
| 1008 | def test_Implicit_Transactions(self): |
| 1009 | zoostore = arena.storage(Zoo) |
| 1010 | |
| 1011 | if not hasattr(zoostore, "db"): |
| 1012 | print "not a db (skipped) ", |
| 1013 | return |
| 1014 | |
| 1015 | old_implicit = zoostore.db.implicit_trans |
| 1016 | try: |
| 1017 | def commit_test(): |
| 1018 | """Test transaction commit.""" |
| 1019 | now = datetime.time(8, 18, 28) |
| 1020 | |
| 1021 | box = arena.new_sandbox() |
| 1022 | try: |
| 1023 | WAP = box.unit(Zoo, Name='Wild Animal Park') |
| 1024 | WAP.Opens = now |
| 1025 | box.flush_all() |
| 1026 | WAP = box.unit(Zoo, Name='Wild Animal Park') |
| 1027 | self.assertEqual(WAP.Opens, now) |
| 1028 | finally: |
| 1029 | box.flush_all() |
| 1030 | |
| 1031 | def rollback_test(): |
| 1032 | """Test transaction rollback.""" |
| 1033 | box = arena.new_sandbox() |
| 1034 | try: |
| 1035 | SDZ = box.unit(Zoo, Name='San Diego Zoo') |
| 1036 | SDZ.Name = 'The One and Only San Diego Zoo' |
| 1037 | SDZ.Founded = datetime.date(2039, 9, 13) |
| 1038 | box.rollback() |
| 1039 | SDZ = box.unit(Zoo, Name='San Diego Zoo') |
| 1040 | self.assertEqual(SDZ.Name, 'San Diego Zoo') |
| 1041 | self.assertEqual(SDZ.Founded, datetime.date(1835, 9, 13)) |
| 1042 | finally: |
| 1043 | box.flush_all() |
| 1044 | |
| 1045 | zoostore.db.implicit_trans = True |
| 1046 | commit_test() |
| 1047 | if zoostore.rollback: |
| 1048 | rollback_test() |
| 1049 | |
| 1050 | zoostore.db.implicit_trans = False |
| 1051 | zoostore.start() |
| 1052 | commit_test() |
| 1053 | if zoostore.rollback: |
| 1054 | zoostore.start() |
| 1055 | rollback_test() |
| 1056 | finally: |
| 1057 | zoostore.db.implicit_trans = old_implicit |
| 1058 | |
| 1059 | def test_ContextManagement(self): |
| 1060 | # Test context management using Python 2.5 'with ... as' |
| 1061 | try: |
| 1062 | from dejavu.test import test_context |
| 1063 | except SyntaxError: |
| 1064 | print "'with ... as' not supported (skipped) ", |
| 1065 | else: |
| 1066 | test_context.test_with_context(arena) |
| 1067 | |
| 1068 | |
| 1069 | class NumericTests(unittest.TestCase): |
| 1070 | |
| 1071 | def test_numbers(self): |
| 1072 | ## print "skipped ", |
| 1073 | ## return |
| 1074 | |
| 1075 | float_prec = 53 |
| 1076 | box = arena.new_sandbox() |
| 1077 | try: |
| 1078 | print "precision:", |
| 1079 | # PostgreSQL should be able to go up to 1000 decimal digits (~= 2 ** 10), |
| 1080 | # but SQL constants don't actually overflow until 2 ** 15. Meh. |
| 1081 | db = getattr(arena.stores['testSM'], "db", None) |
| 1082 | if db: |
| 1083 | import math |
| 1084 | maxprec = db.typeadapter.numeric_max_precision |
| 1085 | if maxprec == 0: |
| 1086 | # SQLite, for example, must always use TEXT. |
| 1087 | # So we might as well try... oh... how about 3? |
| 1088 | overflow_prec = 3 |
| 1089 | else: |
| 1090 | overflow_prec = int(math.log(maxprec, 2)) + 1 |
| 1091 | else: |
| 1092 | overflow_prec = 8 |
| 1093 | |
| 1094 | dc = typerefs.decimal.getcontext() |
| 1095 | |
| 1096 | for prec in xrange(overflow_prec + 1): |
| 1097 | p = 2 ** prec |
| 1098 | print p, |
| 1099 | if p > dc.prec: |
| 1100 | dc.prec = p |
| 1101 | |
| 1102 | # We don't need to test <type long> at different 'scales'. |
| 1103 | long_done = False |
| 1104 | # Test scales at both extremes and the median |
| 1105 | for s in (0, int(prec/2), max(prec-1, 0)): |
| 1106 | s = 2 ** s |
| 1107 | |
| 1108 | # Modify the model and storage |
| 1109 | if not long_done: |
| 1110 | arena.drop_property(NothingToDoWithZoos, 'ALong') |
| 1111 | NothingToDoWithZoos.ALong.hints['bytes'] = p |
| 1112 | arena.add_property(NothingToDoWithZoos, 'ALong') |
| 1113 | if p <= float_prec: |
| 1114 | arena.drop_property(NothingToDoWithZoos, 'AFloat') |
| 1115 | NothingToDoWithZoos.AFloat.hints['precision'] = p |
| 1116 | arena.add_property(NothingToDoWithZoos, 'AFloat') |
| 1117 | if typerefs.decimal: |
| 1118 | arena.drop_property(NothingToDoWithZoos, 'ADecimal') |
| 1119 | NothingToDoWithZoos.ADecimal.hints['precision'] = p |
| 1120 | NothingToDoWithZoos.ADecimal.hints['scale'] = s |
| 1121 | arena.add_property(NothingToDoWithZoos, 'ADecimal') |
| 1122 | if typerefs.fixedpoint: |
| 1123 | arena.drop_property(NothingToDoWithZoos, 'AFixed') |
| 1124 | NothingToDoWithZoos.AFixed.hints['precision'] = p |
| 1125 | NothingToDoWithZoos.AFixed.hints['scale'] = s |
| 1126 | arena.add_property(NothingToDoWithZoos, 'AFixed') |
| 1127 | |
| 1128 | # Create an instance and set the specified precision/scale |
| 1129 | nothing = NothingToDoWithZoos() |
| 1130 | if not long_done: |
| 1131 | Lval = (16 ** p) - 1 |
| 1132 | setattr(nothing, 'ALong', Lval) |
| 1133 | if p <= float_prec: |
| 1134 | fval = float(((2 ** p) - 1) / (2 ** s)) |
| 1135 | setattr(nothing, 'AFloat', fval) |
| 1136 | nval = "1" * p |
| 1137 | nval = nval[:-s] + "." + nval[-s:] |
| 1138 | if typerefs.decimal: |
| 1139 | dval = typerefs.decimal.Decimal(nval) |
| 1140 | setattr(nothing, 'ADecimal', dval) |
| 1141 | if typerefs.fixedpoint: |
| 1142 | # fixedpoint uses "precision" where we use "scale"; |
| 1143 | # that is, number of digits after the decimal point. |
| 1144 | fpval = typerefs.fixedpoint.FixedPoint(nval, s) |
| 1145 | setattr(nothing, 'AFixed', fpval) |
| 1146 | box.memorize(nothing) |
| 1147 | |
| 1148 | # Flush and retrieve the object. Use comparisons to test |
| 1149 | # decompilation of imperfect_type when using large numbers. |
| 1150 | if not long_done: |
| 1151 | box.flush_all() |
| 1152 | nothing = box.unit(NothingToDoWithZoos, ALong=Lval) |
| 1153 | if nothing is None: |
| 1154 | self.fail("Unit not found by long property. " |
| 1155 | "prec=%s scale=%s" % (p, s)) |
| 1156 | if p <= float_prec: |
| 1157 | box.flush_all() |
| 1158 | nothing = box.unit(NothingToDoWithZoos, AFloat=fval) |
| 1159 | if nothing is None: |
| 1160 | self.fail("Unit not found by float property. " |
| 1161 | "prec=%s scale=%s" % (p, s)) |
| 1162 | if typerefs.decimal: |
| 1163 | box.flush_all() |
| 1164 | nothing = box.unit(NothingToDoWithZoos, ADecimal=dval) |
| 1165 | if nothing is None: |
| 1166 | self.fail("Unit not found by decimal property. " |
| 1167 | "prec=%s scale=%s" % (p, s)) |
| 1168 | if typerefs.fixedpoint: |
| 1169 | box.flush_all() |
| 1170 | nothing = box.unit(NothingToDoWithZoos, AFixed=fpval) |
| 1171 | if nothing is None: |
| 1172 | self.fail("Unit not found by fixedpoint property. " |
| 1173 | "prec=%s scale=%s" % (p, s)) |
| 1174 | |
| 1175 | # Test retrieved values. |
| 1176 | if not long_done: |
| 1177 | if nothing.ALong != Lval: |
| 1178 | self.fail("%s != %s prec=%s scale=%s" % |
| 1179 | (`nothing.ALong`, `Lval`, p, s)) |
| 1180 | if p <= float_prec: |
| 1181 | if nothing.AFloat != fval: |
| 1182 | self.fail("%s != %s prec=%s scale=%s" % |
| 1183 | (`nothing.AFloat`, `fval`, p, s)) |
| 1184 | if typerefs.decimal: |
| 1185 | if nothing.ADecimal != dval: |
| 1186 | self.fail("%s != %s prec=%s scale=%s" % |
| 1187 | (`nothing.ADecimal`, `dval`, p, s)) |
| 1188 | if typerefs.fixedpoint: |
| 1189 | if nothing.AFixed != fpval: |
| 1190 | self.fail("%s != %s prec=%s scale=%s" % |
| 1191 | (`nothing.AFixed`, `fpval`, p, s)) |
| 1192 | nothing.forget() |
| 1193 | box.flush_all() |
| 1194 | long_done = True |
| 1195 | finally: |
| 1196 | box.flush_all() |
| 1197 | |
| 1198 | |
| 1199 | ##class ZooSchema(dejavu.Schema): |
| 1200 | ## |
| 1201 | ## # We set "latest" to 1 so we can test upgrading manually. |
| 1202 | ## latest = 1 |
| 1203 | ## |
| 1204 | ## def upgrade_to_2(self): |
| 1205 | ## self.arena.add_property(Animal, "Stomachs") |
| 1206 | ## self.arena.add_property(Animal, "ExhibitID") |
| 1207 | ## box = self.arena.new_sandbox() |
| 1208 | ## for exhibit in box.recall(Exhibit): |
| 1209 | ## for animalID in exhibit.Animals: |
| 1210 | ## # Use the Sandbox magic recaller method. |
| 1211 | ## a = box.Animal(animalID) |
| 1212 | ## if a: |
| 1213 | ## # Exhibits are identified by ZooID and Name |
| 1214 | ## a.ZooID = exhibit.ZooID |
| 1215 | ## a.ExhibitID = exhibit.Name |
| 1216 | ## box.flush_all() |
| 1217 | ## |
| 1218 | ## def upgrade_to_3(self): |
| 1219 | ## Animal.remove_property("Species") |
| 1220 | ## Animal.set_property("Family") |
| 1221 | ## |
| 1222 | ## # Note that we drop this column in a separate step from step 2. |
| 1223 | ## # If we had mixed model properties and SM properties in step 2, |
| 1224 | ## # we could have done this all in one step. But this is a better |
| 1225 | ## # demonstration of the possibilities. ;) |
| 1226 | ## Exhibit.remove_property("Animals") |
| 1227 | ## self.arena.drop_property(Exhibit, "Animals") |
| 1228 | ## |
| 1229 | ## self.arena.rename_property(Animal, "Species", "Family") |
| 1230 | |
| 1231 | |
| 1232 | db = None |
| 1233 | |
| 1234 | def _geniusqllog(message): |
| 1235 | """Dejavu logger (writes to error.log).""" |
| 1236 | if isinstance(message, unicode): |
| 1237 | message = message.encode('utf8') |
| 1238 | s = "%s %s" % (datetime.datetime.now().isoformat(), message) |
| 1239 | f = open(logname, 'ab') |
| 1240 | f.write(s + '\n') |
| 1241 | f.close() |
| 1242 | |
| 1243 | def setup(DB_class, name, opts): |
| 1244 | """Set up storage for Zoo classes.""" |
| 1245 | global db |
| 1246 | db = geniusql.db(DB_class, name, opts) |
| 1247 | db.log = _geniusqllog |
| 1248 | print db.version() |
| 1249 | db.create_database() |
| 1250 | |
| 1251 | def teardown(): |
| 1252 | """Tear down storage for Zoo classes.""" |
| 1253 | try: |
| 1254 | db.drop_database() |
| 1255 | except (AttributeError, NotImplementedError): |
| 1256 | pass |
| 1257 | db.disconnect() |
| 1258 | |
| 1259 | def run(DB_class, name, opts): |
| 1260 | """Run the zoo fixture.""" |
| 1261 | try: |
| 1262 | try: |
| 1263 | setup(DB_class, name, opts) |
| 1264 | loader = unittest.TestLoader().loadTestsFromTestCase |
| 1265 | |
| 1266 | # Run the ZooTests and time it. |
| 1267 | zoocase = loader(ZooTests) |
| 1268 | startTime = datetime.datetime.now() |
| 1269 | tools.TestRunner.run(zoocase) |
| 1270 | print "Ran zoo cases in:", datetime.datetime.now() - startTime |
| 1271 | ## |
| 1272 | ## # Run the other cases. |
| 1273 | ## tools.TestRunner.run(loader(NumericTests)) |
| 1274 | ## tools.TestRunner.run(loader(ConcurrencyTests)) |
| 1275 | ## s = arena.stores.values()[0] |
| 1276 | ## if hasattr(s, "db"): |
| 1277 | ## tools.TestRunner.run(loader(IsolationTests)) |
| 1278 | ## tools.TestRunner.run(loader(DiscoveryTests)) |
| 1279 | except: |
| 1280 | traceback.print_exc() |
| 1281 | finally: |
| 1282 | teardown() |
Note: See TracBrowser for help on using the browser.
