root/projects/sAsync/trunk/sasync/test/test_items.py

Revision 3, 12.4 kB (checked in by edsuom, 2 years ago)

Import of trunk from old repo

Line 
1 # sAsync:
2 # An enhancement to the SQLAlchemy package that provides persistent
3 # dictionaries, text indexing and searching, and an access broker for
4 # conveniently managing database access, table setup, and
5 # transactions. Everything can be run in an asynchronous fashion using the
6 # Twisted framework and its deferred processing capabilities.
7 #
8 # Copyright (C) 2006 by Edwin A. Suominen, http://www.eepatents.com
9 #
10 # This program is free software; you can redistribute it and/or modify it under
11 # the terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 2 of the License, or (at your option) any later
13 # version.
14 #
15 # This program is distributed in the hope that it will be useful, but WITHOUT
16 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 # FOR A PARTICULAR PURPOSE.  See the file COPYING for more details.
18 #
19 # You should have received a copy of the GNU General Public License along with
20 # this program; if not, write to the Free Software Foundation, Inc., 51
21 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22
23 """
24 Unit tests for sasync.items.py.
25 """
26 import random
27 from twisted.internet.reactor import callLater
28 from twisted.internet.defer import \
29      Deferred, DeferredList, DeferredLock, succeed
30 from twisted.trial.unittest import TestCase
31
32 from sqlalchemy import *
33
34 from sasync.database import transact, AccessBroker
35 import sasync.items as items
36 import mock
37
38 GROUP_ID = 123
39 VERBOSE = False
40
41 db = 'items.db'
42    
43
44 class TestableItemsTransactor(items.Transactor):
45     @transact
46     def pre(self):
47         # Group 123
48         self.sasync_items.insert().execute(
49             group_id=123, name='foo', value='OK')
50         # Set up an experienced MockThing to have pickled
51         thing = mock.MockThing()
52         thing.method(1)
53         self.sasync_items.insert().execute(
54             group_id=123, name='bar', value=thing)
55         # Group 124
56         self.sasync_items.insert().execute(
57             group_id=124, name='foo', value='bogus')
58         self.sasync_items.insert().execute(
59             group_id=124, name='invalid', value='bogus')
60
61     @transact
62     def post(self):
63         self.sasync_items.delete().execute()
64
65
66 class ItemsMixin:
67     def tearDown(self):
68         def _tearDown():
69             si = self.i.t.sasync_items
70             si.delete(si.c.group_id == GROUP_ID).execute()
71         d = self.i.t.deferToQueue(_tearDown, niceness=20)
72         d.addCallback(lambda _: self.i.shutdown())
73         return d
74
75
76 class TestItemsTransactor(ItemsMixin, TestCase):
77     def setUp(self):
78         url = "sqlite:///%s" % db
79         self.i = items.Items(GROUP_ID, url)
80         self.i.t = TestableItemsTransactor(self.i.groupID, url)
81         return self.i.t.pre()
82
83     def tearDown(self):
84         return self.i.t.post()
85
86     def testLoad(self):
87         def gotValue(value, name):
88             if name == 'foo':
89                 self.failUnlessEqual(value, 'OK')
90             else:
91                 self.failUnless(
92                     isinstance(value, mock.MockThing),
93                     "Item 'bar' is a '%s', not an instance of 'MockThing'" \
94                     % value)
95                 self.failUnless(
96                     value.beenThereDoneThat,
97                     "Class instance wasn't properly persisted with its state")
98                 self.failUnlessEqual(
99                     value.method(2.5), 5.0,
100                     "Class instance wasn't properly persisted with its method")
101
102         dList = []
103         for name in ('foo', 'bar'):
104             dList.append(self.i.t.load(name).addCallback(gotValue, name))
105         return DeferredList(dList)
106
107     def testLoad(self):
108         def gotValue(value, name):
109             if name == 'foo':
110                 self.failUnlessEqual(value, 'OK')
111             else:
112                 self.failUnless(
113                     isinstance(value, mock.MockThing),
114                     "Item 'bar' is a '%s', not an instance of 'MockThing'" \
115                     % value)
116                 self.failUnless(
117                     value.beenThereDoneThat,
118                     "Class instance wasn't properly persisted with its state")
119                 self.failUnlessEqual(
120                     value.method(2.5), 5.0,
121                     "Class instance wasn't properly persisted with its method")
122
123         dList = []
124         for name in ('foo', 'bar'):
125             dList.append(self.i.t.load(name).addCallback(gotValue, name))
126         return DeferredList(dList)
127
128     def testLoadAbsent(self):
129         def gotValue(value):
130             self.failUnless(
131                 isinstance(value, items.Missing),
132                 "Should have returned 'Missing' object, not '%s'!" % \
133                 str(value))
134         def gotExpectedError(failure):
135             self.fail("Shouldn't have raised error on missing value")
136         return self.i.t.load('invalid').addCallbacks(
137             gotValue, gotExpectedError)
138
139     def testLoadAll(self):
140         def loaded(items):
141             itemKeys = items.keys()
142             itemKeys.sort()
143             self.failUnlessEqual(itemKeys, ['bar', 'foo'])
144         return self.i.t.loadAll().addCallback(loaded)
145
146     def insertLots(self, callback):
147         noviceThing = mock.MockThing()
148         experiencedThing = mock.MockThing()
149         experiencedThing.method(0)
150         self.whatToInsert = {
151             'alpha':5937341,
152             'bravo':'abc',
153             'charlie':-3.1415,
154             'delta':(1,2,3),
155             'echo':True,
156             'foxtrot':False,
157             'golf':noviceThing,
158             'hotel':experiencedThing,
159             'india':mock.MockThing
160             }
161         dList = []
162         for name, value in self.whatToInsert.iteritems():
163             dList.append(self.i.t.insert(name, value))
164         return DeferredList(dList).addCallback(
165             callback, self.whatToInsert.copy())
166
167     def testInsert(self):
168         def done(null, items):
169             def check():
170                 table = self.i.t.sasync_items
171                 for name, inserted in items.iteritems():
172                     value = table.select(
173                         and_(table.c.group_id == 123,
174                              table.c.name == name)
175                         ).execute().fetchone()['value']
176                     self.failUnlessEqual(
177                         value, inserted,
178                         "Inserted '%s:%s' but read '%s' back from the database!" % \
179                         (name, inserted, value))
180                     for otherName, otherValue in items.iteritems():
181                         if otherName != name and value == otherValue:
182                             self.fail(
183                                 "Inserted item '%s' is equal to item '%s'" % \
184                                 (name, otherName))
185             return self.i.t.deferToQueue(check)
186         return self.insertLots(done)
187
188     def testDeleteOne(self):
189         def gotOriginal(value):
190             self.failUnlessEqual(value, 'OK')
191             return self.i.t.delete('foo').addCallback(getAfterDeleted)
192         def getAfterDeleted(null):
193             return self.i.t.load('foo').addCallback(checkIfDeleted)
194         def checkIfDeleted(value):
195             self.failUnless(isinstance(value, items.Missing))
196         return self.i.t.load('foo').addCallback(gotOriginal)
197
198     def testDeleteMultiple(self):
199         def getAfterDeleted(null):
200             return self.i.t.loadAll().addCallback(checkIfDeleted)
201         def checkIfDeleted(values):
202             self.failUnlessEqual(values, {})
203         return self.i.t.delete('foo', 'bar').addCallback(getAfterDeleted)
204
205     def testNamesFew(self):
206         def got(names):
207             names.sort()
208             self.failUnlessEqual(names, ['bar', 'foo'])
209         return self.i.t.names().addCallback(got)
210
211     def testNamesMany(self):
212         def get(null, items):
213             return self.i.t.names().addCallback(got, items.keys())
214         def got(names, shouldHave):
215             shouldHave += ['foo', 'bar']
216             names.sort()
217             shouldHave.sort()
218             self.failUnlessEqual(names, shouldHave)
219         return self.insertLots(get)
220
221     def testUpdate(self):
222         def update(null, items):
223             return DeferredList([
224                 self.i.t.update('alpha',   1),
225                 self.i.t.update('bravo',   2),
226                 self.i.t.update('charlie', 3)
227                 ]).addCallback(check, items)
228         def check(null, items):
229             return self.i.t.loadAll().addCallback(loaded, items)
230         def loaded(loadedItems, controlItems):
231             controlItems.update({'alpha':1, 'bravo':2, 'charlie':3})
232             for name, value in controlItems.iteritems():
233                 self.failUnlessEqual(
234                     value, loadedItems.get(name, 'Impossible Value'))
235         return self.insertLots(update)
236
237    
238 class TestItems(ItemsMixin, TestCase):
239     def setUp(self):
240         self.i = items.Items(GROUP_ID, "sqlite:///%s" % db)
241    
242     def testInsertAndLoad(self):
243         nouns = ('lamp', 'rug', 'chair')
244         def first(null):
245             return self.i.loadAll().addCallback(second)
246         def second(items):
247             self.failUnlessEqual(items['Nouns'], nouns)
248         return self.i.insert('Nouns', nouns).addCallback(first)
249
250     def testInsertAndDelete(self):
251         items = {'a':0, 'b':1, 'c':2, 'd':3, 'e':4}
252
253         def first(null):
254             return self.i.delete('c').addCallback(second)
255
256         def second(null):
257             return self.i.names().addCallback(third)
258
259         def third(nameList):
260             desiredList = [x for x in items.keys() if x != 'c']
261             desiredList.sort()
262             nameList.sort()
263             self.failUnlessEqual(nameList, desiredList)
264
265         dL = []
266         for name, value in items.iteritems():
267             dL.append(self.i.insert(name, value))
268         return DeferredList(dL).addCallback(first)
269
270     def testInsertAndLoadAll(self):
271         items = {'a':0, 'b':1, 'c':2, 'd':3, 'e':4}
272         def first(null):
273             return self.i.loadAll().addCallback(second)
274         def second(loadedItems):
275             self.failUnlessEqual(loadedItems, items)
276
277         dL = []
278         for name, value in items.iteritems():
279             dL.append(self.i.insert(name, value))
280         return DeferredList(dL).addCallback(first)
281
282     def testInsertAndUpdate(self):
283         items = {'a':0, 'b':1, 'c':2, 'd':3, 'e':4}
284         def first(null):
285             return self.i.update('b', 10).addCallback(second)
286         def second(null):
287             return self.i.loadAll().addCallback(third)
288         def third(loadedItems):
289             expectedItems = {'a':0, 'b':10, 'c':2, 'd':3, 'e':4}
290             self.failUnlessEqual(loadedItems, expectedItems)
291
292         dL = []
293         for name, value in items.iteritems():
294             dL.append(self.i.insert(name, value))
295         return DeferredList(dL).addCallback(first)
296
297
298 class TestItemsIntegerNames(ItemsMixin, TestCase):
299     def setUp(self):
300         self.items = {'1':'a', 2:'b', 3:'c', '04':'d'}
301         self.i = items.Items(GROUP_ID, "sqlite:///%s" % db, nameType=int)
302
303     def insertStuff(self):
304         dL = []
305         for name, value in self.items.iteritems():
306             dL.append(self.i.insert(name, value))
307         return DeferredList(dL)
308
309     def testNames(self):
310         def first(null):
311             return self.i.names().addCallback(second)
312         def second(names):
313             names.sort()
314             self.failUnlessEqual(names, [1, 2, 3, 4])
315         return self.insertStuff().addCallback(first)
316
317     def testLoadAll(self):
318         def first(null):
319             return self.i.loadAll().addCallback(second)
320         def second(loaded):
321             self.failUnlessEqual(loaded, {1:'a', 2:'b', 3:'c', 4:'d'})
322         return self.insertStuff().addCallback(first)
323
324
325 class TestItemsStringNames(ItemsMixin, TestCase):
326     def setUp(self):
327         self.items = {'1':'a', 2:'b', u'3':'c', "4":'d'}
328         self.i = items.Items(GROUP_ID, "sqlite:///%s" % db, nameType=str)
329
330     def insertStuff(self):
331         dL = []
332         for name, value in self.items.iteritems():
333             dL.append(self.i.insert(name, value))
334         return DeferredList(dL)
335
336     def testNames(self):
337         def first(null):
338             return self.i.names().addCallback(second)
339         def second(names):
340             names.sort()
341             self.failUnlessEqual(names, ['1', '2', '3', '4'])
342         return self.insertStuff().addCallback(first)       
343
344     def testLoadAll(self):
345         def first(null):
346             return self.i.loadAll().addCallback(second)
347         def second(loaded):
348             self.failUnlessEqual(loaded, {'1':'a', '2':'b', '3':'c', '4':'d'})
349         return self.insertStuff().addCallback(first)
350        
Note: See TracBrowser for help on using the browser.