aboutsummaryrefslogtreecommitdiffstats
path: root/test/utils
diff options
context:
space:
mode:
Diffstat (limited to 'test/utils')
-rw-r--r--test/utils/__init__.py0
-rw-r--r--test/utils/test_commons.py31
-rw-r--r--test/utils/test_uri.py189
-rw-r--r--test/utils/test_uuid.py92
-rw-r--r--test/utils/testfile.t1
5 files changed, 313 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_commons.py b/test/utils/test_commons.py
new file mode 100644
index 0000000..ce73788
--- /dev/null
+++ b/test/utils/test_commons.py
@@ -0,0 +1,31 @@
+"""
+
+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 bsfs.utils.commons import typename
+
+
+## code ##
+
+class TestCommons(unittest.TestCase):
+ def test_typename(self):
+ class Foo(): pass
+ self.assertEqual(typename(Foo()), 'Foo')
+ self.assertEqual(typename('hello'), 'str')
+ self.assertEqual(typename(123), 'int')
+ self.assertEqual(typename(None), 'NoneType')
+
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##
diff --git a/test/utils/test_uri.py b/test/utils/test_uri.py
new file mode 100644
index 0000000..770e65a
--- /dev/null
+++ b/test/utils/test_uri.py
@@ -0,0 +1,189 @@
+"""
+
+Part of the tagit test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import operator
+import unittest
+
+# objects to test
+from bsfs.utils.uri import URI
+
+
+## code ##
+
+class TestURI(unittest.TestCase):
+
+ def test_new(self):
+ # cannot create an unparseable URI
+ self.assertRaises(ValueError, URI, 'http://')
+ # returns URI otherwise
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment'), URI)
+
+ def test_is_parseable(self):
+ # empty string is a parseable uri
+ self.assertTrue(URI.is_parseable(''))
+ # examples from the RFC are parseable
+ self.assertTrue(URI.is_parseable('foo://example.com:8042/over/there?name=ferret#nose'))
+ self.assertTrue(URI.is_parseable('urn:example:animal:ferret:nose'))
+ self.assertTrue(URI.is_parseable('mailto:fred@xample.com'))
+ self.assertTrue(URI.is_parseable('www.w3.org/Addressing/'))
+ self.assertTrue(URI.is_parseable('ftp://cnn.example.com&store=breaking_news@10.0.0.1/top_story.htm'))
+ self.assertTrue(URI.is_parseable('ftp://ftp.is.co.za/rfc/rfc1808.txt'))
+ self.assertTrue(URI.is_parseable('http://www.ietf.org/rfc/rfc2396.txt'))
+ self.assertTrue(URI.is_parseable('ldap://[2001:db8::7]/c=GB?objectClass?one'))
+ self.assertTrue(URI.is_parseable('mailto:John.Doe@example.com'))
+ self.assertTrue(URI.is_parseable('news:comp.infosystems.www.servers.unix'))
+ self.assertTrue(URI.is_parseable('tel:+1-816-555-1212'))
+ self.assertTrue(URI.is_parseable('telnet://192.0.2.16:80/'))
+ self.assertTrue(URI.is_parseable('urn:oasis:names:specification:docbook:dtd:xml:4.1.2'))
+
+ # uri cannot end with a scheme delimiter
+ self.assertFalse(URI.is_parseable('http://'))
+ # port must be a number
+ self.assertFalse(URI.is_parseable('http://example.com:foo/'))
+ # the double slash (//) implies a authority
+ self.assertFalse(URI.is_parseable('http:///path0/path1?query#fragment'))
+
+ def test_compose(self):
+ self.assertEqual(URI.compose('path'), '/path')
+ self.assertEqual(URI.compose('/path'), '/path') # leading slash is not repeated
+ self.assertEqual(URI.compose('path', scheme='scheme'), 'scheme:/path')
+ self.assertEqual(URI.compose('path', authority='authority'), '//authority/path')
+ self.assertEqual(URI.compose('path', host='host'), '//host/path')
+ self.assertEqual(URI.compose('path', user='user'), '/path') # user w/o host is ignored
+ self.assertEqual(URI.compose('path', host='host', user='user'), '//user@host/path')
+ self.assertEqual(URI.compose('path', port='port'), '/path') # port w/o host is ignored
+ self.assertEqual(URI.compose('path', host='host', port=1234), '//host:1234/path')
+ self.assertEqual(URI.compose('path', host='host', port='1234'), '//host:1234/path')
+ self.assertRaises(ValueError, URI.compose, 'path', host='host', port='foo') # port must be a number
+ self.assertEqual(URI.compose('path', host='host', user='foo', port='1234'), '//foo@host:1234/path')
+ self.assertEqual(URI.compose('path', query='query'), '/path?query')
+ self.assertEqual(URI.compose('path', fragment='fragment'), '/path#fragment')
+ self.assertEqual(URI.compose('path', 'scheme', 'authority', 'user', 'host', 1234, 'query', 'fragment'),
+ 'scheme://user@host:1234/path?query#fragment')
+
+ def test_get(self):
+ # get returns the respective component
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('scheme'), 'http')
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('authority'), 'user@www.example.com:1234')
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('userinfo'), 'user')
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('host'), 'www.example.com')
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('port'), 1234)
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('path'), '/path0/path1')
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('query'), 'query')
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('fragment'), 'fragment')
+ # get returns a default value if the component is missing
+ class Foo(): pass
+ foo = Foo()
+ self.assertEqual(URI('//user@www.example.com:1234/path0/path1?query#fragment').get('scheme', foo), foo)
+ self.assertEqual(URI('/path0/path1?query#fragment').get('authority', foo), foo)
+ self.assertEqual(URI('http://www.example.com:1234/path0/path1?query#fragment').get('userinfo', foo), foo)
+ self.assertEqual(URI('/path0/path1?query#fragment').get('host', foo), foo)
+ self.assertEqual(URI('http://user@www.example.com/path0/path1?query#fragment').get('port', foo), foo)
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1#fragment').get('query', foo), foo)
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query').get('fragment', foo), foo)
+ # can only get components
+ self.assertRaises(ValueError, URI('').get, 'foobar')
+
+ def test_scheme(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'http')
+ self.assertEqual(URI('ftp://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'ftp')
+ self.assertEqual(URI('myown://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'myown')
+ # empty scheme
+ self.assertRaises(ValueError, getattr, URI('www.example.com/path0/path1?query#fragment'), 'scheme')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'scheme')
+
+ def test_authority(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').authority, 'user@www.example.com:1234')
+ # empty authority
+ self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'authority')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'authority')
+
+ def test_userinfo(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'http')
+ # empty authority
+ self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'userinfo')
+ # empty userinfo
+ self.assertRaises(ValueError, getattr, URI('http://www.example.com:1234/path0/path1?query#fragment'), 'userinfo')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'userinfo')
+
+ def test_host(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').host, 'www.example.com')
+ # IPv4 host
+ self.assertEqual(URI('http://user@10.0.0.1:1234/path0/path1?query#fragment').host, '10.0.0.1')
+ # IPv6 host
+ self.assertEqual(URI('http://user@[::64]:1234/path0/path1?query#fragment').host, '[::64]')
+ # empty authority
+ self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'host')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'host')
+
+ def test_port(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').port, 1234)
+ # empty authority
+ self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'port')
+ # empty port
+ self.assertRaises(ValueError, getattr, URI('http://user@www.example.com/path0/path1?query#fragment'), 'port')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'port')
+
+ def test_path(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').path, '/path0/path1')
+ # empty path
+ self.assertEqual(URI('http://user@www.example.com:1234?query#fragment').path, '')
+ # empty URI
+ self.assertEqual(URI('').path, '')
+
+ def test_query(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').query, 'query')
+ # empty query
+ self.assertRaises(ValueError, getattr, URI('http://user@www.example.com:1234/path0/path1#fragment'), 'query')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'query')
+
+ def test_fragment(self):
+ # full URI
+ self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').fragment, 'fragment')
+ # empty fragment
+ self.assertRaises(ValueError, getattr, URI('http://user@www.example.com:1234/path0/path1?query'), 'fragment')
+ # empty URI
+ self.assertRaises(ValueError, getattr, URI(''), 'fragment')
+
+ def test_overloaded(self):
+ # composition
+ self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment') + 'hello', URI)
+ self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment') * 2, URI)
+ self.assertIsInstance(2 * URI('http://user@www.example.com:1234/{}/path1?{}#fragment'), URI) # rmul
+ self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment').join(['hello', 'world']) , URI)
+ # stripping
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').strip(), URI)
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').lstrip(), URI)
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').rstrip(), URI)
+ # case fold
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').lower(), URI)
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').upper(), URI)
+ # formatting
+ self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment').format('hello', 'world'), URI)
+ self.assertIsInstance(URI('http://user@www.example.com:1234/%s/path1?%s#fragment') % ('hello', 'world'), URI)
+ self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').replace('path0', 'pathX'), URI)
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##
diff --git a/test/utils/test_uuid.py b/test/utils/test_uuid.py
new file mode 100644
index 0000000..49176d4
--- /dev/null
+++ b/test/utils/test_uuid.py
@@ -0,0 +1,92 @@
+"""
+
+Part of the tagit test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import os
+import re
+import unittest
+
+# objects to test
+from bsfs.utils.uuid import UUID, UCID
+
+
+## code ##
+
+class TestUUID(unittest.TestCase):
+ """Test the UUID generator.
+
+ The UUID is expected to generate random strings of 64 characters(0-9, A-F, case insensitive).
+ Due to the random nature of UUIDs, we cannot actually check if an uid is 'valid' besides
+ matching the expected format.
+
+ At best, we can check if the number of collisions (values generated repeatedly) is below some
+ threshold. One would expect the number of collisions to increase with the number of generated uids.
+ Hence, we only perform an empirical test, whereas the exact test parameters (NUM_SAMPLES,
+ COLLISIONS_THRESHOLD) are subject to the application requirements. Note that this simple test
+ cannot replace a thorough statistical analysis.
+
+ """
+
+ # expected uuid string format
+ _RX_FORMAT = re.compile('[0-9A-Fa-f]{64}')
+
+ # number of uuids to generate for collisions test
+ _NUM_SAMPLES = 100_000
+
+ # number of permitted collisions (less-than test; exclusive)
+ _COLLISIONS_THRESHOLD = 2 # zero or one collisions to pass the test
+
+ def _test_format(self, uid):
+ self.assertIsInstance(uid, str)
+ self.assertTrue(self._RX_FORMAT.fullmatch(uid) is not None)
+
+ def test_call(self):
+ gen = UUID()
+ # w/o content
+ self._test_format(gen())
+ # with content
+ self._test_format(gen('hello world'))
+
+ def test_iter(self):
+ for _, uid in zip(range(1_000), iter(UUID())):
+ self._test_format(uid)
+
+ def test_next(self):
+ gen = UUID()
+ for _ in range(1_000):
+ uid = next(gen)
+ self._test_format(uid)
+
+ def test_collisions(self):
+ # generated uuids are reasonably unique.
+ # Note that we cannot guarantee no collisions.
+ uids = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID())}
+ self.assertGreater(len(uids), self._NUM_SAMPLES - self._COLLISIONS_THRESHOLD)
+ # uuids are reasonably unique across instances
+ uidA = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID())}
+ uidB = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID())}
+ self.assertLess(len(uidA & uidB), self._COLLISIONS_THRESHOLD)
+ # uuids are reasonably unique despite identical seeds.
+ uidA = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID(seed=123))}
+ uidB = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID(seed=123))}
+ self.assertLess(len(uidA & uidB), self._COLLISIONS_THRESHOLD)
+
+
+class TestUCID(unittest.TestCase):
+ def setUp(self):
+ self._checksum = 'a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447' # sha256
+ self._path = os.path.join(os.path.dirname(__file__), 'testfile.t')
+
+ def test_from_path(self):
+ self.assertEqual(UCID.from_path(self._path), self._checksum)
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##
diff --git a/test/utils/testfile.t b/test/utils/testfile.t
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/test/utils/testfile.t
@@ -0,0 +1 @@
+hello world