aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--test/utils/__init__.py0
-rw-r--r--test/utils/test_builder.py173
-rw-r--r--test/utils/test_frame.py168
-rw-r--r--test/utils/test_time.py159
4 files changed, 500 insertions, 0 deletions
diff --git a/test/utils/__init__.py b/test/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/utils/__init__.py
diff --git a/test/utils/test_builder.py b/test/utils/test_builder.py
new file mode 100644
index 0000000..53a22d0
--- /dev/null
+++ b/test/utils/test_builder.py
@@ -0,0 +1,173 @@
+"""
+
+Part of the tagit test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+from functools import partial
+import unittest
+
+# objects to test
+from tagit.utils.builder import BuilderBase, InvalidFactoryName
+
+
+## code ##
+
+class Foo(object):
+ """Description Foo"""
+ def __init__(self, *args, **kwargs):
+ self.args = args
+ self.kwargs = kwargs
+ def __eq__(self, other):
+ return self.args == other.args and self.kwargs == other.kwargs
+
+def bar(*args, **kwargs):
+ """Description bar"""
+ return args, kwargs
+
+class BuilderStub(BuilderBase):
+ _factories = {
+ 'foo': Foo,
+ 'bar': bar,
+ }
+
+class TestBuilderBase(unittest.TestCase):
+ def test_magics(self):
+ builder = BuilderStub()
+ # contains
+ self.assertIn('foo', builder)
+ self.assertIn('bar', builder)
+ self.assertNotIn('foobar', builder)
+ # iter
+ self.assertEqual(set(builder), {'foo', 'bar'})
+ self.assertCountEqual(list(builder), ['foo', 'bar'])
+ self.assertEqual(set(BuilderBase()), set())
+ # len
+ self.assertEqual(len(builder), 2)
+ self.assertEqual(len(BuilderBase()), 0)
+ # eq
+ self.assertEqual(BuilderStub(), BuilderStub())
+ self.assertNotEqual(BuilderBase(), BuilderStub())
+ # hash
+ self.assertEqual(hash(BuilderStub()), hash(BuilderStub()))
+ self.assertEqual(hash(BuilderBase()), hash(BuilderBase()))
+
+ def test_get(self):
+ builder = BuilderStub()
+ # get
+ self.assertEqual(builder.get('foo'), Foo)
+ self.assertEqual(builder.get('bar'), bar)
+ self.assertRaises(InvalidFactoryName, builder.get, 'foobar')
+ # getitem
+ self.assertEqual(builder['foo'], Foo)
+ self.assertEqual(builder['bar'], bar)
+ self.assertRaises(InvalidFactoryName, builder.__getitem__, 'foobar')
+
+ def test_keys(self):
+ self.assertEqual(set(BuilderStub().keys()), {'foo', 'bar'})
+ self.assertEqual(set(BuilderBase().keys()), set())
+
+ def test_describe(self):
+ builder = BuilderStub()
+ self.assertEqual(builder.describe('foo'), 'Description Foo')
+ self.assertEqual(builder.describe('bar'), 'Description bar')
+ self.assertRaises(InvalidFactoryName, builder.describe, 'foobar')
+
+ def test_prepare(self):
+ builder = BuilderStub()
+ # empty args
+ # foo
+ part = builder.prepare('foo')
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords), (Foo, (), {}))
+ # bar
+ part = builder.prepare('bar')
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords), (bar, (), {}))
+ # foobar
+ self.assertRaises(InvalidFactoryName, builder.prepare, 'foobar')
+
+ # args
+ # foo
+ part = builder.prepare('foo', 1, 2, 3)
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords), (Foo, (1, 2, 3), {}))
+ # bar
+ part = builder.prepare('bar', 1, 2, 3)
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords), (bar, (1, 2, 3), {}))
+ # foobar
+ self.assertRaises(InvalidFactoryName, builder.prepare, 'foobar', 1, 2, 3)
+
+ # kwargs
+ # foo
+ part = builder.prepare('foo', arg1='hello', arg2='world')
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords),
+ (Foo, (), {'arg1': 'hello', 'arg2': 'world'}))
+ # bar
+ part = builder.prepare('bar', arg1='hello', arg2='world')
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords),
+ (bar, (), {'arg1': 'hello', 'arg2': 'world'}))
+ # foobar
+ self.assertRaises(InvalidFactoryName, builder.prepare, 'foobar',
+ arg1='hello', arg2='world')
+
+ # mixed
+ # foo
+ part = builder.prepare('foo', 1, 2, 3, arg1='hello', arg2='world')
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords),
+ (Foo, (1, 2, 3), {'arg1': 'hello', 'arg2': 'world'}))
+ # bar
+ part = builder.prepare('bar', 1, 2, 3, arg1='hello', arg2='world')
+ self.assertIsInstance(part, partial)
+ self.assertEqual((part.func, part.args, part.keywords),
+ (bar, (1, 2, 3), {'arg1': 'hello', 'arg2': 'world'}))
+ # foobar
+ self.assertRaises(InvalidFactoryName, builder.prepare, 'foobar',
+ 1, 2, 3, arg1='hello', arg2='world')
+
+ def test_build(self):
+ builder = BuilderStub()
+ # empty args
+ self.assertEqual(builder.build('foo'), Foo())
+ self.assertEqual(builder.build('bar'), bar())
+ self.assertRaises(InvalidFactoryName, builder.build, 'foobar')
+ # args
+ self.assertEqual(builder.build('foo', 1, 2, 3), Foo(1, 2, 3))
+ self.assertEqual(builder.build('bar', 1, 2, 3), bar(1, 2, 3))
+ self.assertRaises(InvalidFactoryName, builder.build, 'foobar', 1, 2, 3)
+ # kwargs
+ self.assertEqual(builder.build('foo', arg1='hello', arg2='world'),
+ Foo(arg1='hello', arg2='world'))
+ self.assertEqual(builder.build('bar', arg1='hello', arg2='world'),
+ bar(arg1='hello', arg2='world'))
+ self.assertRaises(InvalidFactoryName, builder.build, 'foobar',
+ arg1='hello', arg2='world')
+ # mixed
+ self.assertEqual(
+ builder.build('foo', 1, 2, 3, arg1='hello', arg2='world'),
+ Foo(1, 2, 3, arg1='hello', arg2='world'))
+ self.assertEqual(
+ builder.build('bar', 1, 2, 3, arg1='hello', arg2='world'),
+ bar(1, 2, 3, arg1='hello', arg2='world'))
+ self.assertRaises(InvalidFactoryName, builder.build, 'foobar',
+ 1, 2, 3, arg1='hello', arg2='world')
+
+ def test_key_from_instance(self):
+ builder = BuilderStub()
+ self.assertEqual(builder.key_from_instance(Foo()), 'foo')
+ self.assertEqual(builder.key_from_instance(bar), 'bar')
+ self.assertRaises(KeyError, builder.key_from_instance, 'foobar')
+ self.assertRaises(KeyError, builder.key_from_instance, BuilderStub())
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##
diff --git a/test/utils/test_frame.py b/test/utils/test_frame.py
new file mode 100644
index 0000000..79bfa3b
--- /dev/null
+++ b/test/utils/test_frame.py
@@ -0,0 +1,168 @@
+"""
+
+Part of the tagit test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import unittest
+
+# objects to test
+from tagit.utils.frame import Frame
+
+
+## code ##
+
+class EntityStub(object):
+ def __init__(self, guid):
+ self.guid = guid
+ def __eq__(self, other):
+ return self.guid == other.guid
+
+class LibraryStub(object):
+ def __init__(self, guids):
+ self.guids = guids
+ def entity(self, guid):
+ if guid in self.guids:
+ return EntityStub(guid)
+ else:
+ raise KeyError(guid)
+
+class TestFrame(unittest.TestCase):
+ def setUp(self):
+ self.ent0 = EntityStub('ent0')
+ self.ent1 = EntityStub('ent1')
+ self.ent2 = EntityStub('ent2')
+ self.ent3 = EntityStub('ent3')
+ self.ent4 = EntityStub('ent4')
+ self.ent5 = EntityStub('ent5')
+ self.lib = LibraryStub(['ent0', 'ent1', 'ent2', 'ent3'])
+
+ def test_properties(self):
+ # plain test
+ frame = Frame(self.ent0, [self.ent1, self.ent2], 123)
+ self.assertEqual(frame, {
+ 'cursor': self.ent0,
+ 'selection': [self.ent1, self.ent2],
+ 'offset': 123,
+ })
+ self.assertEqual(frame.cursor, self.ent0)
+ self.assertEqual(frame.selection, [self.ent1, self.ent2])
+ self.assertEqual(frame.offset, 123)
+
+ # empty selection
+ frame = Frame(self.ent0, [], 123)
+ self.assertEqual(frame, {
+ 'cursor': self.ent0,
+ 'selection': [],
+ 'offset': 123,
+ })
+ self.assertEqual(frame.cursor, self.ent0)
+ self.assertEqual(frame.selection, [])
+ self.assertEqual(frame.offset, 123)
+
+ # no cursor
+ frame = Frame(None, [self.ent0], 123)
+ self.assertEqual(frame, {
+ 'cursor': None,
+ 'selection': [self.ent0],
+ 'offset': 123,
+ })
+ self.assertEqual(frame.cursor, None)
+ self.assertEqual(frame.selection, [self.ent0])
+ self.assertEqual(frame.offset, 123)
+
+ # no selection
+ frame = Frame(self.ent0, None, 123)
+ self.assertEqual(frame, {
+ 'cursor': self.ent0,
+ 'selection': [],
+ 'offset': 123
+ })
+ self.assertEqual(frame.cursor, self.ent0)
+ self.assertEqual(frame.selection, [])
+ self.assertEqual(frame.offset, 123)
+
+ # Not tested: different list-like selection formats (tuple, list)
+ # This seems ok since such formats would be admissible.
+
+ def test_copy(self):
+ frameA = Frame(self.ent0, [self.ent1, self.ent2], 123)
+ self.assertEqual(frameA, {
+ 'cursor': self.ent0,
+ 'selection': [self.ent1, self.ent2],
+ 'offset': 123
+ })
+
+ frameB = frameA.copy()
+ self.assertEqual(frameB, {
+ 'cursor': self.ent0,
+ 'selection': [self.ent1, self.ent2],
+ 'offset': 123
+ })
+
+ # robust against frame changes
+ frameA['cursor'] = self.ent3
+ self.assertEqual(frameB.cursor, self.ent0)
+ frameA['selection'].append(self.ent3)
+ self.assertEqual(frameB.selection, [self.ent1, self.ent2, self.ent3])
+ frameA['selection'] = [self.ent4, self.ent5]
+ self.assertEqual(frameB.selection, [self.ent1, self.ent2, self.ent3])
+ frameA['offset'] = 321
+ self.assertEqual(frameB.offset, 123)
+
+ # ignorant to object changes
+ self.ent0.guid = 'abc'
+ self.assertEqual(frameA.cursor.guid, 'ent3')
+ self.assertEqual(frameB.cursor.guid, 'abc')
+
+ def test_serialization(self):
+ # empty frame
+ frame = Frame()
+ self.assertEqual(frame,
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=False))
+ # with cursor
+ frame = Frame(self.ent1)
+ self.assertEqual(frame,
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=False))
+
+ # with selection
+ frame = Frame(selection=[self.ent0, self.ent3])
+ self.assertEqual(frame,
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=False))
+
+ # with offset
+ frame = Frame(offset=554)
+ self.assertEqual(frame,
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=False))
+
+ # full frame
+ frame = Frame(self.ent1, [self.ent0, self.ent2], 482)
+ self.assertEqual(frame,
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=False))
+
+ # with invalid values
+ frame = Frame(self.ent5, [self.ent0, self.ent2], 482)
+ self.assertRaises(KeyError,
+ Frame.from_serialized, self.lib, frame.serialize(), ignore_errors=False)
+ frame = Frame(self.ent1, [self.ent5, self.ent2], 482)
+ self.assertRaises(KeyError,
+ Frame.from_serialized, self.lib, frame.serialize(), ignore_errors=False)
+
+ # ignoring invalid values
+ frame = Frame(self.ent5, [self.ent0, self.ent2], 482)
+ self.assertEqual(
+ Frame(None, [self.ent0, self.ent2], 482),
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=True))
+ frame = Frame(self.ent1, [self.ent5, self.ent2], 482)
+ self.assertEqual(
+ Frame(self.ent1, [self.ent2], 482),
+ Frame.from_serialized(self.lib, frame.serialize(), ignore_errors=True))
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##
diff --git a/test/utils/test_time.py b/test/utils/test_time.py
new file mode 100644
index 0000000..a4ffeac
--- /dev/null
+++ b/test/utils/test_time.py
@@ -0,0 +1,159 @@
+"""
+
+Part of the tagit test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# standard imports
+from datetime import datetime, timezone
+import math
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+# external imports
+import pyexiv2
+
+# inner-module imports
+from tagit.parsing import parse_datetime
+
+# objects to test
+from tagit.shared import time as ttime
+
+# constants
+sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
+from testdata import IMAGE_VALID
+
+
+## code ##
+
+class TestTTime(unittest.TestCase):
+ def setUp(self):
+ self.img = tempfile.mkstemp(prefix='tagit_')[1]
+ shutil.copy(IMAGE_VALID['path'], self.img)
+ self.empty = tempfile.mkstemp(prefix='tagit_')[1]
+ # ensure correct file date (fixed, in utc)
+ #os.system('touch -d "29 Oct 2015 14:20:56" {}'.format(self.empty))
+ os.utime(self.empty, (1446124856, 1446124856))
+
+ def tearDown(self):
+ if os.path.exists(self.img): os.unlink(self.img)
+ if os.path.exists(self.empty): os.unlink(self.empty)
+
+ def test_conversions(self):
+ """
+ Files, via os.stat (Timestamp in UTC, Local timezone)
+ Images, from Exif (Timestamp in camera local time, No timezone)
+ Images, from Xmp (Timestamp in camera local time, Timezone from Xmp)
+ now (Timestamp in UTC, Local timezone)
+ Database (timestamp in UTC, timestamp in local time)
+ """
+ # prepare
+ stat = os.stat(self.empty)
+ meta = pyexiv2.ImageMetadata(self.img)
+ meta.read()
+ # Create time objects in the proper format
+
+ # Manually defined
+ dt_manual = datetime(2015, 10, 29, 14, 20, 56)
+
+ # os.stat
+ dt_stat = datetime.fromtimestamp(stat.st_mtime)
+
+ # exif
+ dt_exif = meta['Exif.Photo.DateTimeOriginal'].value.replace(tzinfo=ttime.NoTimeZone)
+
+ # xmp
+ dt_xmp = meta['Xmp.exif.DateTimeOriginal'].value
+
+ # user-specified
+ dt_user = parse_datetime("29.10.2015, 14:20:56")
+
+ # now
+ dt_now = datetime.now()
+
+ # UTC offset
+ offset_ref = dt_manual.replace(tzinfo=timezone.utc).timestamp() - dt_manual.timestamp()
+ offset_ref /= 3600
+ offset_now = dt_now.replace(tzinfo=timezone.utc).timestamp() - dt_now.timestamp()
+ offset_now /= 3600
+
+ # Conversions
+ # Comparable local time
+ self.assertEqual(math.floor(ttime.timestamp_loc(dt_manual) % (3600 * 24) / 3600), 14 )
+ self.assertEqual(math.ceil( ttime.timestamp_loc(dt_manual) % (3600 * 24) / 3600), 15 )
+ self.assertEqual(ttime.timestamp_loc(dt_manual) % (3600 * 24), 51656 )
+ self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_stat) )
+ self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_exif) )
+ self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_xmp) )
+ self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_user) )
+
+ # UTC offset
+ self.assertEqual(ttime.utcoffset(dt_manual), offset_ref)
+ self.assertEqual(ttime.utcoffset(dt_stat), offset_ref)
+ self.assertEqual(ttime.utcoffset(dt_exif), None)
+ self.assertEqual(ttime.utcoffset(dt_xmp), 11)
+ self.assertEqual(ttime.utcoffset(dt_user), offset_ref)
+ self.assertEqual(ttime.utcoffset(dt_now), offset_now)
+
+ # Comparable UTC
+ self.assertEqual(ttime.timestamp_utc(dt_now),
+ ttime.timestamp_loc(dt_now) - 3600 * offset_now)
+ self.assertEqual(ttime.timestamp_utc(dt_manual),
+ ttime.timestamp_loc(dt_manual) - 3600 * offset_ref )
+ self.assertEqual(ttime.timestamp_utc(dt_stat),
+ ttime.timestamp_loc(dt_stat) - 3600 * offset_ref)
+ self.assertEqual(ttime.timestamp_utc(dt_user),
+ ttime.timestamp_loc(dt_user) - 3600 * offset_ref)
+ self.assertEqual(ttime.timestamp_utc(dt_exif),
+ ttime.timestamp_loc(dt_exif))
+ self.assertEqual(ttime.timestamp_utc(dt_xmp),
+ ttime.timestamp_loc(dt_xmp) - 3600 * ttime.utcoffset(dt_xmp))
+
+ # Conversion back
+ self.assertEqual(ttime.from_timestamp_utc(
+ ttime.timestamp_utc(dt_now)).timestamp(), dt_now.timestamp())
+ self.assertEqual(ttime.from_timestamp_utc(
+ ttime.timestamp_utc(dt_manual)).timestamp(), dt_manual.timestamp())
+ self.assertEqual(ttime.from_timestamp_utc(
+ ttime.timestamp_utc(dt_stat)).timestamp(), dt_stat.timestamp())
+ self.assertEqual(ttime.from_timestamp_utc(
+ ttime.timestamp_utc(dt_user)).timestamp(), dt_user.timestamp())
+ self.assertEqual(ttime.from_timestamp_utc(
+ ttime.timestamp_utc(dt_exif)).timestamp(), dt_exif.timestamp())
+ self.assertEqual(ttime.from_timestamp_utc(
+ ttime.timestamp_utc(dt_xmp)).timestamp(), dt_xmp.timestamp())
+
+ self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc(
+ ttime.timestamp_loc(dt_now))), ttime.timestamp_loc(dt_now))
+ self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc(
+ ttime.timestamp_loc(dt_manual))), ttime.timestamp_loc(dt_manual))
+ self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc(
+ ttime.timestamp_loc(dt_stat))), ttime.timestamp_loc(dt_stat))
+ self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc(
+ ttime.timestamp_loc(dt_user))), ttime.timestamp_loc(dt_user))
+ self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc(
+ ttime.timestamp_loc(dt_exif))), ttime.timestamp_loc(dt_exif))
+ self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc(
+ ttime.timestamp_loc(dt_xmp))), ttime.timestamp_loc(dt_xmp))
+
+ # Conversion to local time + TZO for storage.library
+ # This is used to store file infos
+
+ # Retrieval in local time
+ # This is used to search for files in a given time range
+
+ # Retrieval in local time + TZO from storage.library
+
+ # Retrieve in UTC from storage.library
+ # This is used to compare files (e.g. syncing) across platforms
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##