Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

root/tags/1.4.0/test/zoo_fixture.py

Revision 163 (checked in by fumanchu, 6 years ago)

Reverted a special-case test for caching. Not sure why it was there in the first place.

  • Property svn:eol-style set to native
Line 
1 """Test fixture for Storage Managers."""
2
3 import datetime
4 import os
5 try:
6     import pythoncom
7 except ImportError:
8     pythoncom = None
9
10 try:
11     set
12 except NameError:
13     from sets import Set as set
14
15 import threading
16 import time
17 import unittest
18 import warnings
19
20 try:
21     import decimal
22 except ImportError:
23     decimal = None
24
25 try:
26     import fixedpoint
27 except ImportError:
28     fixedpoint = None
29
30 __all__ = ['Animal', 'Exhibit', 'Lecture', 'Vet', 'Visit', 'Zoo',
31            # Don't export the ZooTests class--it will break e.g. test_dejavu.
32            'arena', 'init', 'run', 'setup', 'teardown']
33
34 import dejavu
35 from dejavu import logic
36 from dejavu import Unit, UnitProperty, ToOne, ToMany
37 from dejavu.test import tools
38 from dejavu import engines
39
40
41 class EscapeProperty(UnitProperty):
42     def __set__(self, unit, value):
43         UnitProperty.__set__(self, unit, value)
44         # Zoo is a ToOne association, so it will return a unit or None.
45         z = unit.Zoo()
46         if z:
47             z.LastEscape = unit.LastEscape
48
49
50 class Animal(Unit):
51     Species = UnitProperty(hints={'bytes': 100})
52     ZooID = UnitProperty(int, index=True)
53     Legs = UnitProperty(int, default=4)
54     PreviousZoos = UnitProperty(list, hints={'bytes': 1000})
55     LastEscape = EscapeProperty(datetime.datetime)
56     Lifespan = UnitProperty(float, hints={'bytes': 4})
57     MotherID = UnitProperty(int)
58
59 Animal.many_to_one('ID', Animal, 'MotherID')
60
61
62 class Zoo(Unit):
63     Name = UnitProperty()
64     Founded = UnitProperty(datetime.date)
65     Opens = UnitProperty(datetime.time)
66     LastEscape = UnitProperty(datetime.datetime)
67    
68     if fixedpoint:
69         Admission = UnitProperty(fixedpoint.FixedPoint)
70     else:
71         Admission = UnitProperty(float)
72
73 Zoo.one_to_many('ID', Animal, 'ZooID')
74
75
76 class Vet(Unit):
77     """A Veterinarian."""
78     Name = UnitProperty()
79     ZooID = UnitProperty(int, index=True)
80
81 Vet.many_to_one('ZooID', Zoo, 'ID')
82
83
84 class Visit(Unit):
85     """Work done by a Veterinarian on an Animal."""
86     VetID = UnitProperty(int, index=True)
87     ZooID = UnitProperty(int, index=True)
88     AnimalID = UnitProperty(int, index=True)
89     Date = UnitProperty(datetime.date)
90
91 Vet.one_to_many('ID', Visit, 'VetID')
92 Animal.one_to_many('ID', Visit, 'AnimalID')
93
94
95 class Lecture(Visit):
96     """A Visit by a Vet to train staff (rather than visit an Animal)."""
97     AnimalID = None
98     Topic = UnitProperty()
99
100
101 class Exhibit(Unit):
102     # Make this a string to help test vs unicode.
103     Name = UnitProperty(str)
104     ZooID = UnitProperty(int)
105     Animals = UnitProperty(list)
106     PettingAllowed = UnitProperty(bool)
107     if decimal:
108         Acreage = UnitProperty(decimal.Decimal)
109     else:
110         Acreage = UnitProperty(float)
111    
112     # Remove the ID property (inherited from Unit) from the Exhibit class.
113     ID = None
114     sequencer = dejavu.UnitSequencerNull()
115     identifiers = (ZooID, Name)
116
117 Zoo.one_to_many('ID', Exhibit, 'ZooID')
118
119
120 Jan_1_2001 = datetime.date(2001, 1, 1)
121 every13days = [Jan_1_2001 + datetime.timedelta(x * 13) for x in range(20)]
122 every17days = [Jan_1_2001 + datetime.timedelta(x * 17) for x in range(20)]
123 del x
124
125 class ZooTests(unittest.TestCase):
126    
127     def test_1_model(self):
128         self.assertEqual(Zoo.Animal.__class__, dejavu.ToMany)
129         self.assertEqual(Zoo.Animal.nearClass, Zoo)
130         self.assertEqual(Zoo.Animal.nearKey, 'ID')
131         self.assertEqual(Zoo.Animal.farClass, Animal)
132         self.assertEqual(Zoo.Animal.farKey, 'ZooID')
133        
134         self.assertEqual(Animal.Zoo.__class__, dejavu.ToOne)
135         self.assertEqual(Animal.Zoo.nearClass, Animal)
136         self.assertEqual(Animal.Zoo.nearKey, 'ZooID')
137         self.assertEqual(Animal.Zoo.farClass, Zoo)
138         self.assertEqual(Animal.Zoo.farKey, 'ID')
139    
140     def test_2_populate(self):
141         box = arena.new_sandbox()
142        
143         # Notice this also tests that: a Unit which is only
144         # dirtied via __init__ is still saved.
145         WAP = Zoo(Name = 'Wild Animal Park',
146                   Founded = datetime.date(2000, 1, 1),
147                   # 59 can give rounding errors with divmod, which
148                   # AdapterFromADO needs to correct.
149                   Opens = datetime.time(8, 15, 59),
150                   LastEscape = datetime.datetime(2004, 7, 29, 5, 6, 7),
151                   Admission = "4.95",
152                   )
153         box.memorize(WAP)
154         # The object should get an ID automatically.
155         self.assertNotEqual(WAP.ID, None)
156        
157         SDZ = Zoo(Name = 'San Diego Zoo',
158                   # This early date should play havoc with a number
159                   # of implementations.
160                   Founded = datetime.date(1835, 9, 13),
161                   Opens = datetime.time(9, 0, 0),
162                   Admission = "0",
163                   )
164         box.memorize(SDZ)
165         # The object should get an ID automatically.
166         self.assertNotEqual(SDZ.ID, None)
167        
168         Biodome = Zoo(Name = u'Montr\xe9al Biod\xf4me',
169                       Founded = datetime.date(1992, 6, 19),
170                       Opens = datetime.time(9, 0, 0),
171                       Admission = "11.75",
172                       )
173         box.memorize(Biodome)
174        
175         seaworld = Zoo(Name = 'Sea_World', Admission = "60")
176         box.memorize(seaworld)
177        
178         # Animals
179         leopard = Animal(Species='Leopard', Lifespan=73.5)
180         self.assertEqual(leopard.PreviousZoos, None)
181         box.memorize(leopard)
182         leopard.add(WAP)
183         leopard.LastEscape = datetime.datetime(2004, 12, 21, 8, 15, 0)
184        
185         lion = Animal(Species='Lion', ZooID=WAP.ID)
186         box.memorize(lion)
187        
188         box.memorize(Animal(Species='Slug', Legs=1, Lifespan=.75))
189         tiger = Animal(Species='Tiger')
190         box.memorize(tiger)
191        
192         # Override Legs.default with itself just to make sure it works.
193         box.memorize(Animal(Species='Bear', Legs=4))
194         # Notice that ostrich.PreviousZoos is [], whereas leopard is None.
195         box.memorize(Animal(Species='Ostrich', Legs=2, PreviousZoos=[],
196                             Lifespan=103.2))
197         box.memorize(Animal(Species='Centipede', Legs=100))
198        
199         emp = Animal(Species='Emperor Penguin', Legs=2)
200         box.memorize(emp)
201         adelie = Animal(Species='Adelie Penguin', Legs=2)
202         box.memorize(adelie)
203        
204         seaworld.add(emp, adelie)
205        
206         millipede = Animal(Species='Millipede', Legs=1000000)
207         millipede.PreviousZoos = [WAP.ID]
208         box.memorize(millipede)
209        
210         SDZ.add(tiger, millipede)
211        
212         # Add a mother and child to test relationships
213         bai_yun = Animal(Species='Ape', Legs=2)
214         box.memorize(bai_yun)   # ID = 11
215         hua_mei = Animal(Species='Ape', Legs=2, MotherID=bai_yun.ID)
216         box.memorize(hua_mei)   # ID = 12
217        
218         # Exhibits
219         pe = Exhibit(Name = 'The Penguin Encounter',
220                      ZooID = seaworld.ID,
221                      Animals = [emp.ID, adelie.ID],
222                      PettingAllowed = True,
223                      Acreage = "3.21",
224                      )
225         box.memorize(pe)
226        
227         tr = Exhibit(Name = 'Tiger River',
228                      ZooID = SDZ.ID,
229                      Animals = [tiger.ID],
230                      PettingAllowed = False,
231                      Acreage = "4",
232                      )
233         box.memorize(tr)
234        
235         # Vets
236         cs = Vet(Name = 'Charles Schroeder', ZooID = SDZ.ID)
237         box.memorize(cs)
238         jm = Vet(Name = 'Jim McBain', ZooID = seaworld.ID)
239         box.memorize(jm)
240        
241         # Visits
242         for d in every13days:
243             box.memorize(Visit(VetID=cs.ID, AnimalID=tiger.ID, Date=d))
244         for d in every17days:
245             box.memorize(Visit(VetID=jm.ID, AnimalID=emp.ID, Date=d))
246        
247         box.flush_all()
248    
249     def test_3_Properties(self):
250         box = arena.new_sandbox()
251        
252         # Zoos
253         WAP = box.unit(Zoo, Name='Wild Animal Park')
254         self.assertNotEqual(WAP, None)
255         self.assertEqual(WAP.Founded, datetime.date(2000, 1, 1))
256         self.assertEqual(WAP.Opens, datetime.time(8, 15, 59))
257         # This should have been updated when leopard.LastEscape was set.
258         self.assertEqual(WAP.LastEscape,
259                          datetime.datetime(2004, 12, 21, 8, 15, 0))
260         self.assertEqual(str(WAP.Admission), "4.95")
261        
262         SDZ = box.unit(Zoo, Founded=datetime.date(1835, 9, 13))
263         self.assertNotEqual(SDZ, None)
264         self.assertEqual(SDZ.Founded, datetime.date(1835, 9, 13))
265         self.assertEqual(SDZ.Opens, datetime.time(9, 0, 0))
266         self.assertEqual(SDZ.LastEscape, None)
267         self.assertEqual(float(SDZ.Admission), 0)
268        
269         # Try a magic Sandbox recaller method
270         Biodome = box.Zoo(Name = u'Montr\xe9al Biod\xf4me')
271         self.assertNotEqual(Biodome, None)
272         self.assertEqual(Biodome.Name, u'Montr\xe9al Biod\xf4me')
273         self.assertEqual(Biodome.Founded, datetime.date(1992, 6, 19))
274         self.assertEqual(Biodome.Opens, datetime.time(9, 0, 0))
275         self.assertEqual(Biodome.LastEscape, None)
276         self.assertEqual(float(Biodome.Admission), 11.75)
277        
278         if fixedpoint:
279             seaworld = box.unit(Zoo, Admission = fixedpoint.FixedPoint(60))
280         else:
281             seaworld = box.unit(Zoo, Admission = float(60))
282         self.assertNotEqual(seaworld, None)
283         self.assertEqual(seaworld.Name, u'Sea_World')
284        
285         # Animals
286         leopard = box.unit(Animal, Species='Leopard')
287         self.assertEqual(leopard.Species, 'Leopard')
288         self.assertEqual(leopard.Legs, 4)
289         self.assertEqual(leopard.Lifespan, 73.5)
290         self.assertEqual(leopard.ZooID, WAP.ID)
291         self.assertEqual(leopard.PreviousZoos, None)
292         self.assertEqual(leopard.LastEscape,
293                          datetime.datetime(2004, 12, 21, 8, 15, 0))
294        
295         ostrich = box.unit(Animal, Species='Ostrich')
296         self.assertEqual(ostrich.Species, 'Ostrich')
297         self.assertEqual(ostrich.Legs, 2)
298         self.assertEqual(ostrich.ZooID, None)
299         self.assertEqual(ostrich.PreviousZoos, [])
300         self.assertEqual(ostrich.LastEscape, None)
301        
302         millipede = box.unit(Animal, Legs=1000000)
303         self.assertEqual(millipede.Species, 'Millipede')
304         self.assertEqual(millipede.Legs, 1000000)
305         self.assertEqual(millipede.ZooID, SDZ.ID)
306         self.assertEqual(millipede.PreviousZoos, [WAP.ID])
307         self.assertEqual(millipede.LastEscape, None)
308        
309         # Exhibits
310         exes = box.recall(Exhibit)
311         self.assertEqual(len(exes), 2)
312         if exes[0].Name == 'The Penguin Encounter':
313             pe = exes[0]
314             tr = exes[1]
315         else:
316             pe = exes[1]
317             tr = exes[0]
318         self.assertEqual(pe.ZooID, seaworld.ID)
319         self.assertEqual(len(pe.Animals), 2)
320         self.assertEqual(float(pe.Acreage), 3.21)
321         self.assertEqual(pe.PettingAllowed, True)
322        
323         self.assertEqual(tr.ZooID, SDZ.ID)
324         self.assertEqual(len(tr.Animals), 1)
325         self.assertEqual(float(tr.Acreage), 4)
326         self.assertEqual(tr.PettingAllowed, False)
327        
328         box.flush_all()
329    
330     def test_Expressions(self):
331         box = arena.new_sandbox()
332        
333         def matches(lam, cls=Animal):
334             # We flush_all to ensure a DB hit each time.
335             box.flush_all()
336             return len(box.recall(cls, logic.Expression(lam)))
337        
338         zoos = box.recall(Zoo)
339         self.assertEqual(zoos[0].dirty(), False)
340         self.assertEqual(len(zoos), 4)
341         self.assertEqual(matches(lambda x: True), 12)
342         self.assertEqual(matches(lambda x: x.Legs == 4), 4)
343         self.assertEqual(matches(lambda x: x.Legs == 2), 5)
344         self.assertEqual(matches(lambda x: x.Legs >= 2 and x.Legs < 20), 9)
345         self.assertEqual(matches(lambda x: x.Legs > 10), 2)
346         self.assertEqual(matches(lambda x: x.Lifespan > 70), 2)
347         self.assertEqual(matches(lambda x: x.Species.startswith('L')), 2)
348         self.assertEqual(matches(lambda x: x.Species.endswith('pede')), 2)
349         self.assertEqual(matches(lambda x: x.LastEscape != None), 1)
350         self.assertEqual(matches(lambda x: None == x.LastEscape), 11)
351        
352         # In operator (containedby)
353         self.assertEqual(matches(lambda x: 'pede' in x.Species), 2)
354         self.assertEqual(matches(lambda x: x.Species in ('Lion', 'Tiger', 'Bear')), 3)
355        
356         # Try In with cell references
357         class thing(object): pass
358         pet, pet2 = thing(), thing()
359         pet.Name, pet2.Name = 'Slug', 'Ostrich'
360         self.assertEqual(matches(lambda x: x.Species in (pet.Name, pet2.Name)), 2)
361        
362         # logic and other functions
363         self.assertEqual(matches(lambda x: dejavu.ieq(x.Species, 'slug')), 1)
364         self.assertEqual(matches(lambda x: dejavu.icontains(x.Species, 'PEDE')), 2)
365         self.assertEqual(matches(lambda x: dejavu.icontains(('Lion', 'Banana'), x.Species)), 1)
366         f = lambda x: dejavu.icontainedby(x.Species, ('Lion', 'Bear', 'Leopard'))
367         self.assertEqual(matches(f), 3)
368         name = 'Lion'
369         self.assertEqual(matches(lambda x: len(x.Species) == len(name)), 3)
370        
371         # This broke sometime in 2004. Rev 32 seems to have fixed it.
372         self.assertEqual(matches(lambda x: 'i' in x.Species), 7)
373        
374         # Test now(), today(), year()
375         self.assertEqual(matches(lambda x: x.Founded != None
376                                  and x.Founded < dejavu.today(), Zoo), 3)
377         self.assertEqual(matches(lambda x: x.LastEscape == dejavu.now()), 0)
378         self.assertEqual(matches(lambda x: dejavu.year(x.LastEscape) == 2004), 1)
379        
380         # Test AND, OR with cannot_represent.
381         # Notice that we reference a method ('count') which no
382         # known SM handles, so it will default back to Expr.eval().
383         self.assertEqual(matches(lambda x: 'p' in x.Species
384                                  and x.Species.count('e') > 1), 3)
385        
386         # This broke in MSAccess (storeado) in April 2005, due to a bug in
387         # db.SQLDecompiler.visit_CALL_FUNCTION (append TOS, not replace!).
388         box.flush_all()
389         e = logic.Expression(lambda x, **kw: x.LastEscape != None
390                              and x.LastEscape >= datetime.datetime(kw['Year'], 12, 1)
391                              and x.LastEscape < datetime.datetime(kw['Year'], 12, 31)
392                              )
393         e.bind_args(Year=2004)
394         units = box.recall(Animal, e)
395         self.assertEqual(len(units), 1)
396        
397         # Test wildcards in LIKE. This fails with SQLite <= 3.0.8,
398         # so make sure it's always at the end of this method so
399         # it doesn't preclude running the other tests.
400         box.flush_all()
401         units = box.recall(Zoo, logic.Expression(lambda x: "_" in x.Name))
402         self.assertEqual(len(units), 1)
403    
404     def test_Aggregates(self):
405         box = arena.new_sandbox()
406        
407         # views
408         legs = [x[0] for x in box.view(Animal, ['Legs'])]
409         legs.sort()
410         self.assertEqual(legs, [1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 100, 1000000])
411        
412         expected = {'Leopard': 73.5,
413                     'Slug': .75,
414                     'Tiger': None,
415                     'Lion': None,
416                     'Bear': None,
417                     'Ostrich': 103.2,
418                     'Centipede': None,
419                     'Emperor Penguin': None,
420                     'Adelie Penguin': None,
421                     'Millipede': None,
422                     'Ape': None,
423                     }
424         for species, lifespan in box.view(Animal, ['Species', 'Lifespan']):
425             if expected[species] is None:
426                 self.assertEqual(lifespan, None)
427             else:
428                 self.assertAlmostEqual(expected[species], lifespan, places=5)
429        
430         # distinct
431         legs = box.distinct(Animal, ['Legs'])
432         legs.sort()
433         self.assertEqual(legs, [1, 2, 4, 100, 1000000])
434        
435         # This may raise a warning on some DB's.
436         f = logic.Expression(lambda x: x.Species == 'Lion')
437         escapees = box.distinct(Animal, ['Legs'], f)
438         self.assertEqual(escapees, [4])
439    
440     def test_Multirecall(self):
441         # Multirecall isn't designed with caching proxies in mind.
442         # If we use any, sweep out all their units before proceeding.
443         for store in arena.stores.itervalues():
444             if hasattr(store, "sweep_all"):
445                 store.sweep_all()
446        
447         box = arena.new_sandbox()
448        
449         f = logic.Expression(lambda z, a: z.Name == 'San Diego Zoo')
450         zooed_animals = box.recall(Zoo & Animal, f)
451         self.assertEqual(len(zooed_animals), 2)
452        
453         SDZ = box.unit(Zoo, Name='San Diego Zoo')
454         aid = 0
455         for z, a in zooed_animals:
456             self.assertEqual(id(z), id(SDZ))
457             self.assertNotEqual(id(a), aid)
458             aid = id(a)
459        
460         # Assert that multirecalls with no matching related units returns
461         # no matches for the initial class, since all joins are INNER.
462         # We're also going to test that you can combine a one-arg expr
463         # with a two-arg expr.
464         sdexpr = logic.filter(Name='San Diego Zoo')
465         leo = logic.Expression(lambda z, a: a.Species == 'Leopard')
466         zooed_animals = box.recall(Zoo & Animal, sdexpr + leo)
467         self.assertEqual(len(zooed_animals), 0)
468        
469         # Now try the same expr with INNER, LEFT, and RIGHT JOINs.
470         zooed_animals = box.recall(Zoo & Animal)
471         self.assertEqual(len(zooed_animals), 6)
472         self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]),
473                          set([("Wild Animal Park", "Leopard"),
474                               ("Wild Animal Park", "Lion"),
475                               ("San Diego Zoo", "Tiger"),
476                               ("San Diego Zoo", "Millipede"),
477                               ("Sea_World", "Emperor Penguin"),
478                               ("Sea_World", "Adelie Penguin")]))
479        
480         zooed_animals = box.recall(Zoo >> Animal)
481         self.assertEqual(len(zooed_animals), 12)
482         self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]),
483                          set([("Wild Animal Park", "Leopard"),
484                               ("Wild Animal Park", "Lion"),
485                               ("San Diego Zoo", "Tiger"),
486                               ("San Diego Zoo", "Millipede"),
487                               ("Sea_World", "Emperor Penguin"),
488                               ("Sea_World", "Adelie Penguin"),
489                               (None, "Slug"),
490                               (None, "Bear"),
491                               (None, "Ostrich"),
492                               (None, "Centipede"),
493                               (None, "Ape"),
494                               (None, "Ape"),
495                               ]))
496        
497         zooed_animals = box.recall(Zoo << Animal)
498         self.assertEqual(len(zooed_animals), 7)
499         self.assertEqual(set([(z.Name, a.Species) for z, a in zooed_animals]),
500                          set([("Wild Animal Park", "Leopard"),
501                               ("Wild Animal Park", "Lion"),
502                               ("San Diego Zoo", "Tiger"),
503                               ("San Diego Zoo", "Millipede"),
504                               ("Sea_World", "Emperor Penguin"),
505                               ("Sea_World", "Adelie Penguin"),
506                               (u'Montr\xe9al Biod\xf4me', None),
507                               ]))
508        
509         # Try a multiple-arg expression
510         f = logic.Expression(lambda a, z: a.Legs >= 4 and z.Admission < 10)
511         animal_zoos = box.recall(Animal & Zoo, f)
512         self.assertEqual(len(animal_zoos), 4)
513         names = [a.Species for a, z in animal_zoos]
514         names.sort()
515         self.assertEqual(names, ['Leopard', 'Lion', 'Millipede', 'Tiger'])
516        
517         # Let's try three joined classes just for the sadistic fun of it.
518         tree = (Animal >> Zoo) >> Vet
519         f = logic.Expression(lambda a, z, v: z.Name == 'Sea_World')
520         self.assertEqual(len(box.recall(tree, f)), 2)
521        
522         # MSAccess can't handle an INNER JOIN nested in an OUTER JOIN.
523         # Test that this fails for MSAccess, but works for other SM's.
524         trees = []
525         def make_tree():
526             trees.append( (Animal & Zoo) >> Vet )
527         warnings.filterwarnings("ignore", category=dejavu.StorageWarning)
528         try:
529             make_tree()
530         finally:
531             warnings.filters.pop(0)
532        
533         azv = []
534         def set_azv():
535             f = logic.Expression(lambda a, z, v: z.Name == 'Sea_World')
536             azv.append(box.recall(trees[0], f))
537        
538         smname = arena.stores['testSM'].__class__.__name__
539         if smname in ("StorageManagerADO_MSAccess",):
540             self.assertRaises(pythoncom.com_error, set_azv)
541         else:
542             set_azv()
543             self.assertEqual(len(azv[0]), 2)
544        
545         # Try mentioning the same class twice.
546         tree = (Animal << Animal)
547         f = logic.Expression(lambda anim, mother: mother.ID != None)
548         animals = [mother.ID for anim, mother in box.recall(tree, f)]
549         self.assertEqual(animals, [11])
550    
551     def test_Editing(self):
552         # Edit
553         box = arena.new_sandbox()
554         SDZ = box.unit(Zoo, Name='San Diego Zoo')
555         SDZ.Name = 'The San Diego Zoo'
556         SDZ.Founded = datetime.date(1900, 1, 1)
557         SDZ.Opens = datetime.time(7, 30, 0)
558         SDZ.Admission = "35.00"
559         box.flush_all()
560        
561         # Test edits
562         box = arena.new_sandbox()
563         SDZ = box.unit(Zoo, Name='The San Diego Zoo')
564         self.assertEqual(SDZ.Name, 'The San Diego Zoo')
565         self.assertEqual(SDZ.Founded, datetime.date(1900, 1, 1))
566         self.assertEqual(SDZ.Opens, datetime.time(7, 30, 0))
567         if fixedpoint:
568             self.assertEqual(SDZ.Admission, fixedpoint.FixedPoint(35, 2))
569         else:
570             self.assertEqual(SDZ.Admission, 35.0)
571         box.flush_all()
572        
573         # Change it back
574         box = arena.new_sandbox()
575         SDZ = box.unit(Zoo, Name='The San Diego Zoo')
576         SDZ.Name = 'San Diego Zoo'
577         SDZ.Founded = datetime.date(1835, 9, 13)
578         SDZ.Opens = datetime.time(9, 0, 0)
579         SDZ.Admission = "0"
580         box.flush_all()
581        
582         # Test re-edits
583         box = arena.new_sandbox()
584         SDZ = box.unit(Zoo, Name='San Diego Zoo')
585         self.assertEqual(SDZ.Name, 'San Diego Zoo')
586         self.assertEqual(SDZ.Founded, datetime.date(1835, 9, 13))
587         self.assertEqual(SDZ.Opens, datetime.time(9, 0, 0))
588         if fixedpoint:
589             self.assertEqual(SDZ.Admission, fixedpoint.FixedPoint(0, 2))
590         else:
591             self.assertEqual(SDZ.Admission, 0.0)
592    
593     def test_Multithreading(self):
594         f = logic.Expression(lambda x: x.Legs == 4)
595         def thread_recall():
596             # Notice we only do reads in this thread, not writes, since
597             # the order of thread execution can not be guaranteed.
598             box = arena.new_sandbox()
599             quadrupeds = box.recall(Animal, f)
600             self.assertEqual(len(quadrupeds), 4)
601        
602         ts = []
603         # PostgreSQL, for example, has a default max_connections of 100.
604         for x in range(99):
605             t = threading.Thread(target=thread_recall)
606             t.start()
607             ts.append(t)
608         for t in ts:
609             t.join()
610    
611     def test_Iteration(self):
612         box = arena.new_sandbox()
613        
614         # Test box.unit inside of xrecall
615         for visit in box.xrecall(Visit, logic.filter(VetID=1)):
616             firstvisit = box.unit(Visit, VetID=1, Date=Jan_1_2001)
617             self.assertEqual(firstvisit.VetID, 1)
618             self.assertEqual(visit.VetID, 1)
619        
620         # Test recall inside of xrecall
621         for visit in box.xrecall(Visit, logic.filter(VetID=1)):
622             f = logic.Expression(lambda x: x.VetID == 1 and x.ID != visit.ID)
623             othervisits = box.recall(Visit, f)
624             self.assertEqual(len(othervisits), len(every13days) - 1)
625        
626         # Test far associations inside of xrecall
627         for visit in box.xrecall(Visit, logic.filter(VetID=1)):
628             # visit.Vet is a ToOne association, so will return a unit or None.
629             vet = visit.Vet()
630             self.assertEqual(vet.ID, 1)
631    
632     def test_Engines(self):
633         box = arena.new_sandbox()
634        
635         quadrupeds = box.recall(Animal, logic.filter(Legs=4))
636         self.assertEqual(len(quadrupeds), 4)
637        
638         eng = engines.UnitEngine()
639         box.memorize(eng)
640         eng.add_rule('CREATE', 1, "Animal")
641         eng.add_rule('FILTER', 1, logic.filter(Legs=4))
642         self.assertEqual(eng.FinalClassName, "Animal")
643        
644         qcoll = eng.take_snapshot()
645         self.assertEqual(len(qcoll), 4)
646         self.assertEqual(qcoll.EngineID, eng.ID)
647        
648         eng.add_rule('TRANSFORM', 1, "Zoo")
649         self.assertEqual(eng.FinalClassName, "Zoo")
650        
651         # Sleep for a second so the Timestamps are different.
652         time.sleep(1)
653         qcoll = eng.take_snapshot()
654         self.assertEqual(len(qcoll), 2)
655         zoos = qcoll.units()
656         zoos.sort(dejavu.sort('Name'))
657        
658         SDZ = box.unit(Zoo, Name='San Diego Zoo')
659         WAP = box.unit(Zoo, Name='Wild Animal Park')
660         self.assertEqual(zoos, [SDZ, WAP])
661        
662         # Flush and start over
663         box.flush_all()
664         box = arena.new_sandbox()
665        
666         # Use the Sandbox magic recaller method
667         eng = box.UnitEngine(1)
668         self.assertEqual(len(eng.rules()), 3)
669         snaps = eng.snapshots()
670         self.assertEqual(len(snaps), 2)
671        
672         self.assertEqual(snaps[0].Type, "Animal")
673         self.assertEqual(len(snaps[0]), 4)
674        
675         self.assertEqual(snaps[1].Type, "Zoo")
676         self.assertEqual(len(snaps[1]), 2)
677         self.assertEqual(eng.last_snapshot(), snaps[1])
678        
679         # Remove the last TRANSFORM rule to see if finalclass reverts.
680         self.assertEqual(eng.FinalClassName, "Zoo")
681         eng.rules()[-1].forget()
682         self.assertEqual(eng.FinalClassName, "Animal")
683    
684     def test_Subclassing(self):
685         box = arena.new_sandbox()
686         box.memorize(Visit(VetID=21, ZooID=1, AnimalID=1))
687         box.memorize(Visit(VetID=21, ZooID=1, AnimalID=2))
688         box.memorize(Visit(VetID=32, ZooID=1, AnimalID=3))
689         box.memorize(Lecture(VetID=21, ZooID=1, Topic='Cage Cleaning'))
690         box.memorize(Lecture(VetID=21, ZooID=1, Topic='Ape Mating Habits'))
691         box.memorize(Lecture(VetID=32, ZooID=3, Topic='Your Tiger and Steroids'))
692        
693         visits = box.recall(Visit, logic.filter(ZooID=1))
694         self.assertEqual(len(visits), 5)
695        
696         box.flush_all()
697        
698         box = arena.new_sandbox()
699         visits = box.recall(Visit, logic.filter(VetID=21))
700         self.assertEqual(len(visits), 4)
701         cc = [x for x in visits
702               if getattr(x, "Topic", None) == "Cage Cleaning"]
703         self.assertEqual(len(cc), 1)
704        
705         # Checking for non-existent attributes in/from subclasses
706         # isn't supported yet.
707 ##        f = logic.filter(AnimalID=2)
708 ##        self.assertEqual(len(box.recall(Visit, f)), 1)
709 ##        self.assertEqual(len(box.recall(Lecture, f)), 0)
710    
711 ##    def test_Transactions(self):
712 ##        box = arena.new_sandbox()
713 ##        box.mark("rollback point name")
714 ##        lion = box.unit(Animal, Species=Lion)
715 ##        lion.LastEscape = datetime.datetime(2005, 12, 25, 8, 14)
716 ##        box.commit_since("rollback point name")
717    
718     def testzzzz_Schema_Upgrade(self):
719         # Must run last.
720         zs = ZooSchema(arena)
721        
722         # In this first upgrade, we simulate the case where the code was
723         # upgraded, and the database schema upgrade performed afterward.
724         # The Schema.latest property is set, and upgrade() is called with
725         # no argument (which should upgrade us to "latest").
726         Animal.set_property("ExhibitID")
727         zs.latest = 2
728         zs.upgrade()
729        
730         # In this example, we simulate the developer who wants to put
731         # model changes inline with database changes (see upgrade_to_3).
732         # We do not set latest, but instead supply an arg to upgrade().
733         zs.upgrade(3)
734        
735         # Test that Animals have a new "Family" property, and an ExhibitID.
736         box = arena.new_sandbox()
737         emp = box.unit(Animal, Family='Emperor Penguin')
738         self.assertEqual(emp.ExhibitID, 'The Penguin Encounter')
739
740
741 arena = dejavu.Arena()
742
743 def _djvlog(message, flag):
744     """Dejavu logger (writes to error.log)."""
745     if flag & arena.logflags:
746         s = "%s %s" % (datetime.datetime.now().isoformat(),
747                        message.encode('utf8'))
748         fname = os.path.join(os.path.dirname(__file__), "djvtest.log")
749         f = open(fname, 'ab')
750         f.write(s + '\n')
751         f.close()
752
753 def init():
754     global arena
755     arena = dejavu.Arena()
756     arena.log = _djvlog
757     arena.logflags = dejavu.LOGSQL
758
759
760 class ZooSchema(dejavu.Schema):
761    
762     # We set "latest" to 1 so we can test upgrading.
763     latest = 1
764    
765     def upgrade_to_2(self):
766         self.arena.add_property(Animal, "ExhibitID")
767         box = self.arena.new_sandbox()
768         for exhibit in box.recall(Exhibit):
769             for animalID in exhibit.Animals:
770                 # Use the Sandbox magic recaller method.
771                 a = box.Animal(animalID)
772                 if a:
773                     # Exhibits are identified by ZooID and Name
774                     a.ZooID = exhibit.ZooID
775                     a.ExhibitID = exhibit.Name
776         box.flush_all()
777    
778     def upgrade_to_3(self):
779         Animal.remove_property("Species")
780         Animal.set_property("Family")
781        
782         # Note that we drop this column in a separate step from step 2.
783         # If we had mixed model properties and SM properties in step 2,
784         # we could have done this all in one step. But this is a better
785         # demonstration of the possibilities. ;)
786         Exhibit.remove_property("Animals")
787         self.arena.drop_property(Exhibit, "Animals")
788        
789         self.arena.rename_property(Animal, "Species", "Family")
790
791
792 def setup(SM_class, opts):
793     """setup(SM_class, opts). Set up storage for Zoo classes."""
794     global arena
795     arena.add_store('testSM', SM_class, opts)
796     v = getattr(arena.stores['testSM'], "version", None)
797     if v:
798         print v()
799     arena.stores['testSM'].create_database()
800    
801     arena.register_all(globals())
802     engines.register_classes(arena)
803    
804     zs = ZooSchema(arena)
805     zs.upgrade()
806     zs.assert_storage()
807
808
809 def teardown():
810     """Tear down storage for Zoo classes."""
811     global arena
812     arena.shutdown()
813     for store in arena.stores.values():
814         try:
815             store.drop_database()
816         except (AttributeError, NotImplementedError):
817             pass
818     arena.stores = {}
819     arena.defaultStore = None
820
821 def run(SM_class, opts):
822     """Run the zoo fixture."""
823     try:
824         setup(SM_class, opts)
825         suite = unittest.TestLoader().loadTestsFromTestCase(ZooTests)
826         startTime = datetime.datetime.now()
827         tools.djvTestRunner.run(suite)
828         print "Ran zoo suite in:", (datetime.datetime.now() - startTime)
829     finally:
830         teardown()
831
Note: See TracBrowser for help on using the browser.