Rename test files to test_*.py
[joel/kofoto.git] / src / test / test_shelf.py
1 #! /usr/bin/env python
2
3 import gc
4 import os
5 import shutil
6 import sys
7 import threading
8 import unittest
9
10 if __name__ == "__main__":
11     cwd = os.getcwd()
12     libdir = unicode(os.path.realpath(
13         os.path.join(os.path.dirname(sys.argv[0]), "..", "packages")))
14     os.chdir(libdir)
15     sys.path.insert(0, libdir)
16 from kofoto.shelf import \
17     Shelf, \
18     computeImageHash, \
19     makeValidTag, \
20     verifyValidAlbumTag, \
21     verifyValidCategoryTag
22 from kofoto.albumtype import AlbumType
23 from kofoto.imageversiontype import ImageVersionType
24 from kofoto.shelfexceptions import \
25     AlbumDoesNotExistError, \
26     AlbumExistsError, \
27     BadAlbumTagError, \
28     BadCategoryTagError, \
29     CategoriesAlreadyConnectedError, \
30     CategoryDoesNotExistError, \
31     CategoryExistsError, \
32     CategoryLoopError, \
33     CategoryPresentError, \
34     FailedWritingError, \
35     ImageDoesNotExistError, \
36     ImageVersionDoesNotExistError, \
37     ImageVersionExistsError, \
38     ShelfLockedError, \
39     ShelfNotFoundError, \
40     UndeletableAlbumError, \
41     UndeletableAlbumError, \
42     UnsettableChildrenError, \
43     UnsupportedShelfError
44
45 PICDIR = unicode(os.path.realpath(
46     os.path.join("..", "reference_pictures", "working")))
47
48 ######################################################################
49
50 db = "shelf.tmp"
51
52 def removeTmpDb():
53     for x in [db, db + "-journal"]:
54         if os.path.exists(x):
55             os.unlink(x)
56
57 class LockerThread(threading.Thread):
58     def __init__(self, mContinue, ltContinue):
59         threading.Thread.__init__(self)
60         self.mContinue = mContinue
61         self.ltContinue = ltContinue
62
63     def run(self):
64         s = Shelf(db)
65         s.create()
66         s.begin()
67         self.mContinue.set()
68         self.ltContinue.wait()
69         s.rollback()
70
71 ######################################################################
72
73 class TestPublicShelfFunctions(unittest.TestCase):
74     def test_computeImageHash(self):
75         s = computeImageHash(os.path.join(PICDIR, "arlaharen.png"))
76         assert s == "39a1266d2689f53d48b09a5e0ca0af1f"
77         try:
78             computeImageHash("nonexisting")
79         except IOError:
80             pass
81         else:
82             assert False, s
83
84     def test_verifyValidAlbumTag(self):
85         # Valid tags.
86         for x in ("foo", "1foo", "and", "exactly", "not", "or"):
87             try:
88                 verifyValidAlbumTag(x)
89             except:
90                 assert False, x
91         # Invalid tags.
92         for x in (None, 1, 1L, "1" "foo " "@foo"):
93             try:
94                 verifyValidAlbumTag(x)
95             except BadAlbumTagError:
96                 pass
97             else:
98                 assert False, x
99
100     def test_verifyValidCategoryTag(self):
101         # Valid tags.
102         for x in ("foo", "1foo"):
103             try:
104                 verifyValidCategoryTag(x)
105             except:
106                 assert False, x
107         # Invalid tags.
108         for x in (None, 1, 1L, "1" "foo " "@foo",
109                   "and", "exactly", "not", "or"):
110             try:
111                 verifyValidCategoryTag(x)
112             except BadCategoryTagError:
113                 pass
114             else:
115                 assert False, x
116
117     def test_makeValidTag(self):
118         for tag, validTag in [("", "_"),
119                               ("@", "_"),
120                               (" ", "_"),
121                               ("1", "1_"),
122                               ("@foo_", "foo_"),
123                               ("fo@o __", "fo@o__")]:
124             assert makeValidTag(tag) == validTag, (tag, validTag)
125
126 class TestNegativeShelfOpens(unittest.TestCase):
127     def tearDown(self):
128         removeTmpDb()
129
130     def test_NonexistingShelf(self):
131         try:
132             s = Shelf(db)
133             s.begin()
134         except ShelfNotFoundError:
135             pass
136         else:
137             assert False
138         assert not os.path.exists(db)
139
140     def test_BadShelf(self):
141         file(db, "w") # Create empty file.
142         try:
143             s = Shelf(db)
144             s.begin()
145         except UnsupportedShelfError:
146             pass
147         else:
148             assert False
149
150     def test_LockedShelf(self):
151         mContinue = threading.Event()
152         ltContinue = threading.Event()
153         lt = LockerThread(mContinue, ltContinue)
154         lt.start()
155         mContinue.wait()
156         try:
157             try:
158                 s = Shelf(db)
159                 s.begin()
160             except ShelfLockedError:
161                 pass
162             else:
163                 assert False
164         finally:
165             ltContinue.set()
166             lt.join()
167
168 class TestShelfCreation(unittest.TestCase):
169     def tearDown(self):
170         removeTmpDb()
171
172     def test_CreateShelf1(self):
173         s = Shelf(db)
174         s.create()
175         assert os.path.exists(db)
176
177     def test_CreateShelf2(self):
178         file(db, "w") # Create empty file.
179         s = Shelf(db)
180         try:
181             s.create()
182         except FailedWritingError:
183             pass
184         else:
185             assert False
186
187 class TestShelfMemoryLeakage(unittest.TestCase):
188     def setUp(self):
189         # Ignore any previous leaks.
190         gc.collect()
191
192     def tearDown(self):
193         removeTmpDb()
194
195     def test_MemoryLeak1(self):
196         s = Shelf(db)
197         s.create()
198         assert gc.collect() == 0
199
200     def test_MemoryLeak2(self):
201         s = Shelf(db)
202         s.create()
203         s.begin()
204         s.getObject(0)
205         s.rollback()
206         assert gc.collect() == 0
207
208     def test_MemoryLeak3(self):
209         s = Shelf(db)
210         s.create()
211         s.begin()
212         s.getObject(0)
213         s.commit()
214         assert gc.collect() == 0
215
216 class TestShelfTransactions(unittest.TestCase):
217     def tearDown(self):
218         removeTmpDb()
219
220     def test_commit(self):
221         s = Shelf(db)
222         s.create()
223         s.begin()
224         s.createAlbum(u"foo")
225         assert s.getAlbumByTag(u"foo")
226         s.commit()
227         s = Shelf(db)
228         s.begin()
229         assert s.getAlbumByTag(u"foo")
230         s.rollback()
231
232     def test_rollback(self):
233         s = Shelf(db)
234         s.create()
235         s.begin()
236         s.createAlbum(u"foo")
237         s.rollback()
238         s.begin()
239         try:
240             s.getAlbumByTag(u"foo")
241         except AlbumDoesNotExistError:
242             pass
243         else:
244             assert False
245
246     def test_isModified(self):
247         s = Shelf(db)
248         s.create()
249         s.begin()
250         assert not s.isModified()
251         s.createAlbum(u"foo")
252         assert s.isModified()
253         s.rollback()
254         s.begin()
255         assert not s.isModified()
256         s.rollback()
257
258     def test_registerModificationCallback(self):
259         res = [False]
260         def f(x):
261             res[0] = True
262         s = Shelf(db)
263         s.create()
264         s.begin()
265         s.registerModificationCallback(f)
266         assert not res[0]
267         s.createAlbum(u"foo")
268         assert res[0]
269         s.rollback()
270         res[0] = False
271         s.begin()
272         assert not res[0]
273         s.unregisterModificationCallback(f)
274         s.createAlbum(u"foo")
275         assert not res[0]
276         s.rollback()
277
278 class TestShelfFixture(unittest.TestCase):
279     def setUp(self):
280         self.shelf = Shelf(db)
281         self.shelf.create()
282         self.shelf.begin()
283         root = self.shelf.getRootAlbum()
284         alpha = self.shelf.createAlbum(u"alpha")
285         beta = self.shelf.createAlbum(u"beta")
286         children = [beta]
287         for x in os.listdir(PICDIR):
288             loc = os.path.join(PICDIR, x)
289             if not os.path.isfile(loc):
290                 continue
291             image = self.shelf.createImage()
292             imageversion = self.shelf.createImageVersion(
293                 image, loc, ImageVersionType.Original)
294             children.append(image)
295         del children[-1] # The last image becomes orphaned.
296         alpha.setChildren(children)
297         beta.setChildren(list(beta.getChildren()) + [children[-1]])
298         root.setChildren(list(root.getChildren()) + [alpha, beta])
299         self.shelf.createAlbum(u"epsilon", AlbumType.Plain) # Orphaned album.
300         zeta = self.shelf.createAlbum(u"zeta", AlbumType.Search)
301         zeta.setAttribute(u"query", u"a")
302         root.setChildren(list(root.getChildren()) + [zeta])
303
304         cat_a = self.shelf.createCategory(u"a", u"A")
305         cat_b = self.shelf.createCategory(u"b", u"B")
306         cat_c = self.shelf.createCategory(u"c", u"C")
307         cat_d = self.shelf.createCategory(u"d", u"D")
308         cat_a.connectChild(cat_b)
309         cat_a.connectChild(cat_c)
310         cat_b.connectChild(cat_d)
311         cat_c.connectChild(cat_d)
312
313         self.shelf.flushObjectCache()
314         self.shelf.flushCategoryCache()
315
316     def tearDown(self):
317         self.shelf.rollback()
318         removeTmpDb()
319
320 class TestShelfMethods(TestShelfFixture):
321     def test_flushCaches(self):
322         self.shelf.flushCategoryCache()
323         self.shelf.flushObjectCache()
324
325     def test_getStatistics(self):
326         s = self.shelf.getStatistics()
327         assert s["nalbums"] == 6
328         assert s["nimages"] == 11
329         assert s["nimageversions"] == 11
330
331     def test_createdObjects(self):
332         root = self.shelf.getRootAlbum()
333         children = list(root.getChildren())
334         assert len(children) == 4
335         orphans, alpha, beta, zeta = children
336         assert self.shelf.getAlbum(alpha.getId()) == alpha
337         assert self.shelf.getAlbumByTag(u"beta") == beta
338         assert len(list(alpha.getChildren())) == 11
339         assert len(list(beta.getChildren())) == 1
340
341     def test_createdAttributes(self):
342         imageversion = self.shelf.getImageVersionByLocation(
343             os.path.join(PICDIR, "Canon_Digital_IXUS.jpg"))
344         image = imageversion.getImage()
345         assert image.getAttribute(u"captured") == "2002-02-02 22:20:51"
346         assert image.getAttribute(u"cameramake") == "Canon"
347         assert image.getAttribute(u"cameramodel") == "Canon DIGITAL IXUS"
348
349     def test_negativeAlbumCreation(self):
350         try:
351             self.shelf.createAlbum(u"beta")
352         except AlbumExistsError:
353             pass
354         else:
355             assert False
356
357     def test_getAlbum(self):
358         album = self.shelf.getAlbum(0)
359         assert album == self.shelf.getRootAlbum()
360
361     def test_negativeGetAlbum(self):
362         try:
363             self.shelf.getAlbum(12345678)
364         except AlbumDoesNotExistError:
365             pass
366         else:
367             assert False
368
369     def test_getAlbumByTag(self):
370         album = self.shelf.getAlbumByTag(u"root")
371         assert album == self.shelf.getRootAlbum()
372
373     def test_negativeGetAlbumByTag(self):
374         try:
375             self.shelf.getAlbum(u"12345678")
376         except AlbumDoesNotExistError:
377             pass
378         else:
379             assert False
380
381     def test_getRootAlbum(self):
382         root = self.shelf.getRootAlbum()
383         assert root.getId() == 0
384         assert root == self.shelf.getAlbumByTag(u"root")
385
386     def test_getAllAlbums(self):
387         albums = list(self.shelf.getAllAlbums())
388         assert len(albums) == 6
389
390     def test_getAllImageVersions(self):
391         imageversions = list(self.shelf.getAllImageVersions())
392         assert len(imageversions) == 11
393
394     def test_getImageVersionsInDirectory(self):
395         self.shelf.flushImageVersionCache()
396
397         imageversions = list(self.shelf.getImageVersionsInDirectory(u"."))
398         assert len(imageversions) == 0
399
400         # Image versions not in cache.
401         imageversions = list(self.shelf.getImageVersionsInDirectory(PICDIR))
402         assert len(imageversions) == 11
403
404         # Image versions in cache.
405         imageversions = list(self.shelf.getImageVersionsInDirectory(PICDIR))
406         assert len(imageversions) == 11
407
408     def test_deleteAlbum(self):
409         album = self.shelf.getAlbumByTag(u"beta")
410         self.shelf.deleteAlbum(album.getId())
411
412     def test_negativeRootAlbumDeletion(self):
413         root = self.shelf.getRootAlbum()
414         try:
415             self.shelf.deleteAlbum(root.getId())
416         except UndeletableAlbumError:
417             pass
418         else:
419             assert False
420
421     def test_negativeAlbumDeletion(self):
422         try:
423             self.shelf.deleteAlbum(12345678)
424         except AlbumDoesNotExistError:
425             pass
426         else:
427             assert False
428
429     def test_negativeImageCreation(self):
430         try:
431             image = self.shelf.createImage()
432             self.shelf.createImageVersion(
433                 image,
434                 os.path.join(PICDIR, "arlaharen.png"),
435                 ImageVersionType.Original)
436         except ImageVersionExistsError:
437             pass
438         else:
439             assert False
440
441     def test_getImage(self):
442         imageversion = self.shelf.getImageVersionByLocation(
443             os.path.join(PICDIR, "arlaharen.png"))
444         image = imageversion.getImage()
445         assert self.shelf.getImage(image.getId()) == image
446
447     def test_negativeGetImage(self):
448         try:
449             self.shelf.getImage(12345678)
450         except ImageDoesNotExistError:
451             pass
452         else:
453             assert False
454
455     def test_getImageVersion(self):
456         imageversion = self.shelf.getImageVersionByLocation(
457             os.path.join(PICDIR, "arlaharen.png"))
458         assert self.shelf.getImageVersion(imageversion.getId()) == imageversion
459
460     def test_negativeGetImageVersion(self):
461         try:
462             self.shelf.getImageVersion(12345678)
463         except ImageVersionDoesNotExistError:
464             pass
465         else:
466             assert False
467
468     def test_getImageVersionByHash(self):
469         imageversion = self.shelf.getImageVersionByLocation(
470             os.path.join(PICDIR, "arlaharen.png"))
471         assert self.shelf.getImageVersionByHash(
472             imageversion.getHash()) == imageversion
473
474     def test_negativeGetImageVersionByHash(self):
475         try:
476             self.shelf.getImageVersion(u"badhash")
477         except ImageVersionDoesNotExistError:
478             pass
479         else:
480             assert False
481
482     def test_getImageVersionByLocation(self):
483         imageversion1 = self.shelf.getImageVersionByLocation(
484             os.path.join(PICDIR, u"arlaharen.png"))
485         currentDir = os.getcwd()
486         try:
487             os.chdir(PICDIR)
488             imageversion2 = self.shelf.getImageVersionByLocation(
489                 u"arlaharen.png")
490         finally:
491             os.chdir(currentDir)
492         assert imageversion1 == imageversion2
493
494     def test_negativeGetImageVersionByHash(self):
495         try:
496             self.shelf.getImageVersionByLocation(u"/bad/location")
497         except ImageVersionDoesNotExistError:
498             pass
499         else:
500             assert False
501
502     def test_deleteImage(self):
503         imageversion = self.shelf.getImageVersionByLocation(
504             os.path.join(PICDIR, "arlaharen.png"))
505         imageid = imageversion.getImage().getId()
506         self.shelf.deleteImage(imageid)
507         try:
508             self.shelf.getImageVersionByLocation(
509                 os.path.join(PICDIR, "arlaharen.png"))
510         except ImageVersionDoesNotExistError:
511             pass
512         else:
513             assert False
514
515     def test_negativeImageDeletion(self):
516         try:
517             self.shelf.deleteImage(12345678)
518         except ImageDoesNotExistError:
519             pass
520         else:
521             assert False
522
523     def test_deleteImageVersion(self):
524         imageversion = self.shelf.getImageVersionByLocation(
525             os.path.join(PICDIR, "arlaharen.png"))
526         self.shelf.deleteImageVersion(imageversion.getId())
527
528     def test_negativeImageVersionDeletion(self):
529         try:
530             self.shelf.deleteImageVersion(12345678)
531         except ImageVersionDoesNotExistError:
532             pass
533         else:
534             assert False
535
536     def test_getObject(self):
537         rootalbum = self.shelf.getRootAlbum()
538         album = self.shelf.getObject(rootalbum.getId())
539         assert album == rootalbum
540
541     def test_deleteObject(self):
542         albumid = self.shelf.getAlbumByTag(u"beta").getId()
543         imageversion = self.shelf.getImageVersionByLocation(
544             os.path.join(PICDIR, "arlaharen.png"))
545         imageid = imageversion.getImage().getId()
546         self.shelf.deleteObject(albumid)
547         self.shelf.deleteObject(imageid)
548         try:
549             self.shelf.getAlbum(albumid)
550         except AlbumDoesNotExistError:
551             pass
552         else:
553             assert False
554         try:
555             self.shelf.getImage(imageid)
556         except ImageDoesNotExistError:
557             pass
558         else:
559             assert False
560
561     def test_getAllAttributeNames(self):
562         attrnames = sorted(self.shelf.getAllAttributeNames())
563         assert attrnames == [
564             "cameramake", "cameramodel", "captured", "description",
565             "digitalzoom", "exposurebias", "exposureprogram", "exposuretime",
566             "flash", "fnumber", "focallength", "iso", "orientation", "query",
567             "title"
568             ], attrnames
569
570     def test_getCategory(self):
571         category = self.shelf.getCategory(1)
572         assert category.getId() == 1
573
574     def test_getCategoryByTag(self):
575         category = self.shelf.getCategoryByTag(u"a")
576         assert category.getTag() == u"a"
577
578     def test_negativeCreateCategory(self):
579         try:
580             self.shelf.createCategory(u"a", u"Foo")
581         except CategoryExistsError:
582             pass
583         else:
584             assert False
585
586     def test_deleteCategory(self):
587         category = self.shelf.getCategoryByTag(u"a")
588         self.shelf.deleteCategory(category.getId())
589
590     def test_negativeDeleteCategory(self):
591         try:
592             self.shelf.deleteCategory(12345678)
593         except CategoryDoesNotExistError:
594             pass
595         else:
596             assert False
597
598     def test_getRootCategories(self):
599         categories = sorted(
600             self.shelf.getRootCategories(), key=lambda x: x.getTag())
601         cat_a = self.shelf.getCategoryByTag(u"a")
602         cat_events = self.shelf.getCategoryByTag(u"events")
603         cat_locations = self.shelf.getCategoryByTag(u"locations")
604         cat_people = self.shelf.getCategoryByTag(u"people")
605         assert categories == [cat_a, cat_events, cat_locations, cat_people], \
606                categories
607
608 class TestCategory(TestShelfFixture):
609     def test_categoryMethods(self):
610         cat_a = self.shelf.getCategoryByTag(u"a")
611         cat_b = self.shelf.getCategoryByTag(u"b")
612         cat_c = self.shelf.getCategoryByTag(u"c")
613         cat_d = self.shelf.getCategoryByTag(u"d")
614
615         assert self.shelf.getCategory(cat_a.getId()) == cat_a
616         cat_a.setTag(u"foo")
617         assert self.shelf.getCategoryByTag(u"foo") == cat_a
618
619         assert cat_a.getDescription() == "A"
620         cat_a.setDescription(u"foo")
621         assert cat_a.getDescription() == "foo"
622
623         a_children = sorted(cat_a.getChildren(), key=lambda x: x.getId())
624         assert a_children == [cat_b, cat_c]
625         b_children = list(cat_b.getChildren())
626         assert b_children == [cat_d]
627         d_children = list(cat_d.getChildren())
628         assert d_children == []
629
630         a_parents = list(cat_a.getParents())
631         assert a_parents == []
632         b_parents = list(cat_b.getParents())
633         assert b_parents == [cat_a]
634         d_parents = sorted(cat_d.getParents(), key=lambda x: x.getTag())
635         assert d_parents == [cat_b, cat_c]
636
637         assert not cat_a.isChildOf(cat_a)
638         assert cat_b.isChildOf(cat_a)
639         assert cat_c.isChildOf(cat_a)
640         assert not cat_d.isChildOf(cat_a)
641         assert cat_a.isChildOf(cat_a, recursive=True)
642         assert cat_b.isChildOf(cat_a, recursive=True)
643         assert cat_c.isChildOf(cat_a, recursive=True)
644         assert cat_d.isChildOf(cat_a, recursive=True)
645
646         assert not cat_d.isParentOf(cat_d)
647         assert cat_b.isParentOf(cat_d)
648         assert cat_c.isParentOf(cat_d)
649         assert not cat_a.isParentOf(cat_d)
650         assert cat_d.isParentOf(cat_d, recursive=True)
651         assert cat_b.isParentOf(cat_d, recursive=True)
652         assert cat_c.isParentOf(cat_d, recursive=True)
653         assert cat_a.isParentOf(cat_d, recursive=True)
654
655     def test_negativeCategoryConnectChild(self):
656         cat_a = self.shelf.getCategoryByTag(u"a")
657         cat_b = self.shelf.getCategoryByTag(u"b")
658         try:
659             cat_a.connectChild(cat_b)
660         except CategoriesAlreadyConnectedError:
661             pass
662         else:
663             assert False
664         try:
665             cat_b.connectChild(cat_a)
666         except CategoryLoopError:
667             pass
668         else:
669             assert False
670
671     def test_categoryDisconnectChild(self):
672         cat_a = self.shelf.getCategoryByTag(u"a")
673         cat_b = self.shelf.getCategoryByTag(u"b")
674         cat_a.disconnectChild(cat_b)
675         assert not cat_a.isParentOf(cat_b)
676
677     def test_negativeCategoryDisconnectChild(self):
678         cat_a = self.shelf.getCategoryByTag(u"a")
679         cat_d = self.shelf.getCategoryByTag(u"d")
680         cat_a.disconnectChild(cat_d) # No exception.
681
682 class TestObject(TestShelfFixture):
683     def test_getParents(self):
684         root = self.shelf.getRootAlbum()
685         alpha = self.shelf.getAlbumByTag(u"alpha")
686         beta = self.shelf.getAlbumByTag(u"beta")
687         parents = sorted(beta.getParents(), key=lambda x: x.getTag())
688         assert parents == [alpha, root]
689
690     def test_getAttribute(self):
691         orphans = self.shelf.getAlbumByTag(u"orphans")
692         assert orphans.getAttribute(u"title")
693         assert orphans.getAttribute(u"description")
694         orphans.getAttributeMap() # Just populate the cache.
695         assert not orphans.getAttribute(u"nonexisting")
696         assert u"nonexisting" not in orphans.getAttributeMap()
697
698     def test_getAttributeMap(self):
699         orphans = self.shelf.getAlbumByTag(u"orphans")
700         map = orphans.getAttributeMap()
701         assert "description" in map
702         assert "title" in map
703
704     def test_getAttributeNames(self):
705         orphans = self.shelf.getAlbumByTag(u"orphans")
706         names = sorted(orphans.getAttributeNames())
707         assert names == ["description", "title"]
708
709     def test_setAttribute(self):
710         orphans = self.shelf.getAlbumByTag(u"orphans")
711         orphans.setAttribute(u"foo", u"fie") # New.
712         assert orphans.getAttribute(u"foo") == u"fie"
713         assert u"foo" in orphans.getAttributeMap()
714         assert u"foo" in orphans.getAttributeNames()
715         assert orphans.getAttribute(u"title")
716         orphans.setAttribute(u"title", u"gazonk") # Existing
717         assert orphans.getAttribute(u"title") == u"gazonk"
718         assert u"foo" in orphans.getAttributeMap()
719         assert u"foo" in orphans.getAttributeNames()
720
721     def test_deleteAttribute(self):
722         orphans = self.shelf.getAlbumByTag(u"orphans")
723         orphans.deleteAttribute(u"nonexisting") # No exception.
724         assert orphans.getAttribute(u"title")
725         orphans.deleteAttribute(u"title")
726         orphans.getAttributeMap() # Just populate the cache.
727         assert not orphans.getAttribute(u"title")
728         assert u"title" not in orphans.getAttributeMap()
729
730     def test_addCategory(self):
731         orphans = self.shelf.getAlbumByTag(u"orphans")
732         cat_a = self.shelf.getCategoryByTag(u"a")
733         assert list(orphans.getCategories()) == []
734         orphans.addCategory(cat_a)
735         assert list(orphans.getCategories()) == [cat_a]
736         try:
737             orphans.addCategory(cat_a)
738         except CategoryPresentError:
739             pass
740         else:
741             assert False
742         assert list(orphans.getCategories()) == [cat_a]
743
744     def test_removeCategory(self):
745         orphans = self.shelf.getAlbumByTag(u"orphans")
746         assert list(orphans.getCategories()) == []
747         cat_a = self.shelf.getCategoryByTag(u"a")
748         orphans.addCategory(cat_a)
749         assert list(orphans.getCategories()) == [cat_a]
750         orphans.removeCategory(cat_a)
751         assert list(orphans.getCategories()) == []
752
753     def test_deleteCategoryInvalidatesCategoryCache(self):
754         orphans = self.shelf.getAlbumByTag(u"orphans")
755         assert list(orphans.getCategories()) == []
756         cat_a = self.shelf.getCategoryByTag(u"a")
757         orphans.addCategory(cat_a)
758         assert list(orphans.getCategories()) == [cat_a]
759         self.shelf.deleteCategory(cat_a.getId())
760         assert list(orphans.getCategories()) == []
761
762 class TestAlbum(TestShelfFixture):
763     def test_getType(self):
764         alpha = self.shelf.getAlbumByTag(u"alpha")
765         assert alpha.getType() == AlbumType.Plain
766
767     def test_isMutable(self):
768         alpha = self.shelf.getAlbumByTag(u"alpha")
769         assert alpha.isMutable()
770
771     def test_getTag(self):
772         alpha = self.shelf.getAlbumByTag(u"alpha")
773         assert alpha.getTag() == u"alpha"
774
775     def test_setTag(self):
776         alpha = self.shelf.getAlbumByTag(u"alpha")
777         alpha.setTag(u"alfa")
778         assert alpha.getTag() == u"alfa"
779
780     def test_getAlbumParents(self):
781         root = self.shelf.getRootAlbum()
782         alpha = self.shelf.getAlbumByTag(u"alpha")
783         parents = sorted(alpha.getAlbumParents(), key=lambda x: x.getTag())
784         assert parents == [root]
785
786     def test_isAlbum(self):
787         assert self.shelf.getRootAlbum().isAlbum()
788
789 class TestPlainAlbum(TestShelfFixture):
790     def test_getType(self):
791         alpha = self.shelf.getAlbumByTag(u"alpha")
792         assert alpha.getType() == AlbumType.Plain
793
794     def test_isMutable(self):
795         alpha = self.shelf.getAlbumByTag(u"alpha")
796         assert alpha.isMutable()
797
798     def test_getChildren(self):
799         epsilon = self.shelf.getAlbumByTag(u"epsilon")
800         alpha = self.shelf.getAlbumByTag(u"alpha")
801         beta = self.shelf.getAlbumByTag(u"beta")
802         assert list(epsilon.getChildren()) == []
803         alphaChildren = list(alpha.getChildren())
804         assert list(beta.getChildren()) == [alphaChildren[-1]]
805
806     def test_getAlbumChildren(self):
807         alpha = self.shelf.getAlbumByTag(u"alpha")
808         beta = self.shelf.getAlbumByTag(u"beta")
809         epsilon = self.shelf.getAlbumByTag(u"epsilon")
810         alphaAlbumChildren = list(alpha.getAlbumChildren())
811         assert alphaAlbumChildren == [beta]
812         assert list(epsilon.getAlbumChildren()) == []
813
814     def test_setChildren(self):
815         root = self.shelf.getRootAlbum()
816         beta = self.shelf.getAlbumByTag(u"beta")
817         assert list(beta.getChildren()) != []
818         beta.setChildren([beta, root])
819         assert list(beta.getChildren()) == [beta, root]
820         beta.setChildren([]) # Break the cycle.
821         assert list(beta.getChildren()) == []
822
823 class TestImage(TestShelfFixture):
824     def test_isAlbum(self):
825         imageversion = self.shelf.getImageVersionByLocation(
826             os.path.join(PICDIR, "arlaharen.png"))
827         assert not imageversion.getImage().isAlbum()
828
829     def test_getImageVersions(self):
830         imageversion = self.shelf.getImageVersionByLocation(
831             os.path.join(PICDIR, "arlaharen.png"))
832         image = imageversion.getImage()
833         assert list(image.getImageVersions()) == [imageversion]
834         imageversion2 = self.shelf.getImageVersionByLocation(
835             os.path.join(PICDIR, "Canon_Digital_IXUS.jpg"))
836         imageversion2.setImage(image)
837         imageversions = set(image.getImageVersions())
838         assert set(image.getImageVersions()) == imageversions
839         self.shelf.deleteImageVersion(imageversion.getId())
840         self.shelf.deleteImageVersion(imageversion2.getId())
841         assert list(image.getImageVersions()) == []
842
843     def test_getPrimaryVersion(self):
844         imageversion = self.shelf.getImageVersionByLocation(
845             os.path.join(PICDIR, "arlaharen.png"))
846         image = imageversion.getImage()
847         assert image.getPrimaryVersion() == imageversion
848         imageversion2 = self.shelf.getImageVersionByLocation(
849             os.path.join(PICDIR, "Canon_Digital_IXUS.jpg"))
850         imageversion2.setImage(image)
851         assert image.getPrimaryVersion() == imageversion
852         imageversion2.makePrimary()
853         assert image.getPrimaryVersion() == imageversion2
854
855         newImage = self.shelf.createImage()
856         lastImageVersion = list(image.getImageVersions())[-1]
857         lastImageVersion.setImage(newImage)
858         assert image.getPrimaryVersion() != lastImageVersion
859         assert newImage.getPrimaryVersion() == lastImageVersion
860         lastImageVersion.setImage(image)
861
862         self.shelf.deleteImageVersion(imageversion2.getId())
863         assert image.getPrimaryVersion() == imageversion
864         self.shelf.deleteImageVersion(imageversion.getId())
865         assert image.getPrimaryVersion() == None
866
867 class TestImageVersion(TestShelfFixture):
868     def test_getType(self):
869         imageversion = self.shelf.getImageVersionByLocation(
870             os.path.join(PICDIR, "arlaharen.png"))
871         assert imageversion.getType() == ImageVersionType.Original
872         imageversion.setType(ImageVersionType.Important)
873         assert imageversion.getType() == ImageVersionType.Important
874         imageversion.setType(ImageVersionType.Other)
875         assert imageversion.getType() == ImageVersionType.Other
876         imageversion.setType(ImageVersionType.Original)
877         assert imageversion.getType() == ImageVersionType.Original
878
879     # ImageVersion.makePrimary tested in TestImage.test_getPrimaryVersion.
880     # ImageVersion.setImage tested in TestImage.test_getPrimaryVersion.
881     # ImageVersion.setType tested in TestImageVersion.test_getType.
882     # ImageVersion.setComment tested in TestImageVersion.test_getComment.
883
884     def test_getComment(self):
885         imageversion = self.shelf.getImageVersionByLocation(
886             os.path.join(PICDIR, "arlaharen.png"))
887         assert imageversion.getComment() == ""
888         imageversion.setComment(u"a comment")
889         assert imageversion.getComment() == u"a comment"
890
891     def test_getHash(self):
892         imageversion = self.shelf.getImageVersionByLocation(
893             os.path.join(PICDIR, "arlaharen.png"))
894         assert imageversion.getHash() == "39a1266d2689f53d48b09a5e0ca0af1f"
895
896     def test_getLocation(self):
897         location = os.path.join(PICDIR, "arlaharen.png")
898         imageversion = self.shelf.getImageVersionByLocation(location)
899         assert imageversion.getLocation() == os.path.realpath(location)
900
901     def test_getModificationTime(self):
902         imageversion = self.shelf.getImageVersionByLocation(
903             os.path.join(PICDIR, "arlaharen.png"))
904         t = imageversion.getModificationTime()
905         assert isinstance(t, int)
906         assert t > 0
907
908     def test_getSize(self):
909         imageversion = self.shelf.getImageVersionByLocation(
910             os.path.join(PICDIR, "arlaharen.png"))
911         assert imageversion.getSize() == (304, 540)
912
913     def test_contentChanged(self):
914         path = os.path.join(PICDIR, "arlaharen.png")
915         imageversion = self.shelf.getImageVersionByLocation(path)
916         self.shelf.deleteImageVersion(imageversion.getId())
917         newpath = u"tmp.png"
918         try:
919             shutil.copy2(path, newpath)
920             newimage = self.shelf.createImage()
921             newimageversion = self.shelf.createImageVersion(
922                 newimage, newpath, ImageVersionType.Original)
923             oldmtime = imageversion.getModificationTime()
924             f = open(newpath, "a")
925             f.write("foo")
926             f.close()
927             newimageversion.contentChanged()
928             assert newimageversion.getHash() == "b27312d9739c0edfd115f824be244b75"
929             assert newimageversion.getModificationTime() > oldmtime
930         finally:
931             try:
932                 os.unlink(newpath)
933             except OSError:
934                 pass
935
936     def test_locationChanged(self):
937         location = os.path.join(PICDIR, "arlaharen.png")
938         imageversion = self.shelf.getImageVersionByLocation(location)
939         imageversion.locationChanged(u"/foo/../bar")
940         assert imageversion.getLocation() == "/bar"
941
942     def test_importExifTags(self):
943         imageversion = self.shelf.getImageVersionByLocation(
944             os.path.join(PICDIR, "arlaharen.png"))
945         imageversion.importExifTags(True) # TODO: Test more.
946         imageversion.importExifTags(False) # TODO: Test more.
947
948 class TestOrphansAlbum(TestShelfFixture):
949     def test_getType(self):
950         orphans = self.shelf.getAlbumByTag(u"orphans")
951         assert orphans.getType() == AlbumType.Orphans
952
953     def test_isMutable(self):
954         orphans = self.shelf.getAlbumByTag(u"orphans")
955         assert not orphans.isMutable()
956
957     def test_getChildren(self):
958         orphans = self.shelf.getAlbumByTag(u"orphans")
959         assert len(list(orphans.getChildren())) == 2
960
961     def test_getAlbumChildren(self):
962         orphans = self.shelf.getAlbumByTag(u"orphans")
963         epsilon = self.shelf.getAlbumByTag(u"epsilon")
964         assert list(orphans.getAlbumChildren()) == [epsilon]
965
966     def test_setChildren(self):
967         orphans = self.shelf.getAlbumByTag(u"orphans")
968         try:
969             orphans.setChildren([])
970         except UnsettableChildrenError:
971             pass
972         else:
973             assert False
974
975     def test_isAlbum(self):
976         assert self.shelf.getAlbumByTag(u"orphans").isAlbum()
977
978 class TestSearchAlbum(TestShelfFixture):
979     def test_getType(self):
980         zeta = self.shelf.getAlbumByTag(u"zeta")
981         assert zeta.getType() == AlbumType.Search
982
983     def test_isMutable(self):
984         zeta = self.shelf.getAlbumByTag(u"zeta")
985         assert not zeta.isMutable()
986
987     def test_getChildren(self):
988         alpha = self.shelf.getAlbumByTag(u"alpha")
989         image1, image2 = list(alpha.getChildren())[0:2]
990         cat_a = self.shelf.getCategoryByTag(u"a")
991         cat_b = self.shelf.getCategoryByTag(u"b")
992         image1.addCategory(cat_a)
993         image2.addCategory(cat_b)
994         zeta = self.shelf.getAlbumByTag(u"zeta")
995         assert zeta
996         zeta.setAttribute(u"query", u"b")
997         children = zeta.getChildren()
998         assert list(children) == [image2]
999
1000     def test_getAlbumChildren(self):
1001         alpha = self.shelf.getAlbumByTag(u"alpha")
1002         image1, image2 = list(alpha.getChildren())[0:2]
1003         cat_a = self.shelf.getCategoryByTag(u"a")
1004         cat_b = self.shelf.getCategoryByTag(u"b")
1005         image1.addCategory(cat_a)
1006         image2.addCategory(cat_b)
1007         zeta = self.shelf.getAlbumByTag(u"zeta")
1008         assert zeta
1009         zeta.setAttribute(u"query", u"b")
1010         children = zeta.getAlbumChildren()
1011         l = list(children)
1012         assert list(children) == []
1013
1014     def test_setChildren(self):
1015         zeta = self.shelf.getAlbumByTag(u"zeta")
1016         try:
1017             zeta.setChildren([])
1018         except UnsettableChildrenError:
1019             pass
1020         else:
1021             assert False
1022
1023     def test_isAlbum(self):
1024         assert self.shelf.getAlbumByTag(u"zeta").isAlbum()
1025
1026 ######################################################################
1027
1028 removeTmpDb()
1029 if __name__ == "__main__":
1030     unittest.main()