class TestTieCore(TestCase): def setUp(self): _remove_index() self.exif = ee.ExifEditor(Configuration()) self.files_base_path = _path_to_linkname(os.path.abspath("../res")) self.index = Index(TEST_INDEX_LOCATION, self.exif) self.tie_core = TieCoreImpl(self.exif, self.index) def tearDown(self): _remove_index() def test_query_all(self): self._prepare_query_test() files_2_3 = self.tie_core.query( Query([QUERY_TAG_2, QUERY_TAG_3], MatchType.all)) self.assertEqual( [os.path.abspath(QUERY_FILE_2)], files_2_3, "Query all with 2 tags did not find the expected files") files_3 = self.tie_core.query(Query([QUERY_TAG_3], MatchType.all)) self.assertEqual( [os.path.abspath(QUERY_FILE_2), os.path.abspath(QUERY_FILE_3)], files_3, "Query all with 1 tag did not find the expected files") _clean_after_query_test() def test_query_all_empty_tags_list(self): self._prepare_query_test() result = self.tie_core.query(Query([], MatchType.all)) self.assertEqual([], result, "Query with no tags") _clean_after_query_test() def test_query_all_no_match(self): self._prepare_query_test() result = self.tie_core.query( Query([QUERY_TAG_2, "some random non existant tag"], MatchType.all)) self.assertEqual([], result, "Query with no result") _clean_after_query_test() def test_query_any(self): self._prepare_query_test() files = self.tie_core.query( Query([QUERY_TAG_2, QUERY_TAG_3], MatchType.any)) self.assertEqual([ os.path.abspath(QUERY_FILE_1), os.path.abspath(QUERY_FILE_2), os.path.abspath(QUERY_FILE_3) ], files, "Query any did not find the expected files") _clean_after_query_test() def _prepare_query_test(self): cli.run_cmd(["cp", READ_FILE_MD, QUERY_FILE_1]) cli.run_cmd(["cp", READ_FILE_MD, QUERY_FILE_2]) cli.run_cmd(["cp", READ_FILE_MD, QUERY_FILE_3]) cli.run_cmd(["cp", READ_FILE_MD, QUERY_FILE_4]) self.exif.set_meta_data(QUERY_FILE_1, md.MetaData([QUERY_TAG_1, QUERY_TAG_2])) self.exif.set_meta_data(QUERY_FILE_2, md.MetaData([QUERY_TAG_2, QUERY_TAG_3])) self.exif.set_meta_data(QUERY_FILE_3, md.MetaData([QUERY_TAG_3, QUERY_TAG_4])) self.exif.set_meta_data(QUERY_FILE_4, md.MetaData([QUERY_TAG_4])) self.index.update(QUERY_FILE_1) self.index.update(QUERY_FILE_2) self.index.update(QUERY_FILE_3) self.index.update(QUERY_FILE_4) def test_list(self): tags = self.tie_core.list([READ_FILE_MD]) self.assertEqual([TEST_READ_TAG_1.lower(), TEST_READ_TAG_2.lower()], tags, "listed tags do not match") def test_list_multiple_files(self): tags = self.tie_core.list([READ_FILE_MD, READ_FILE_MD_2]) self.assertEqual( sorted([ TEST_READ_TAG_1.lower(), TEST_READ_TAG_2.lower(), TEST_READ_TAG_3.lower() ]), tags, "listed tags do not match") def test_list_empty_raw(self): cli.run_cmd(["cp", READ_FILE, WRITE_FILE]) self.exif._write_exif_field("Exif.Photo.UserComment", "", WRITE_FILE) tags = self.tie_core.list([WRITE_FILE]) self.assertEqual([], tags, "listed tags do not match empty list") os.remove(WRITE_FILE) def test_list_empty_md(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) self.tie_core.clear(WRITE_FILE_MD) tags = self.tie_core.list([WRITE_FILE_MD]) self.assertEqual([], tags, "listed tags do not match empty list") os.remove(WRITE_FILE_MD) def test_list_invalid(self): self.assertRaises(InvalidMetaDataError, lambda: self.tie_core.list([READ_FILE])) def test_list_not_found(self): self.assertRaises( FileNotFoundError, lambda: self.tie_core.list(["../res/foobar.not.existant"])) def test_tag(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) added_tag1 = "New tag Ä" added_tag2 = "Other tag Ä" self.tie_core.tag(WRITE_FILE_MD, [added_tag1, added_tag2]) self.assertEqual( sorted([ TEST_READ_TAG_1.lower(), TEST_READ_TAG_2.lower(), added_tag1.lower(), added_tag2.lower() ]), self.tie_core.list([WRITE_FILE_MD]), "Tags after adding did not match") os.remove(WRITE_FILE_MD) def test_tag_empty(self): cli.run_cmd(["cp", READ_FILE_NO_EXIF, WRITE_FILE_NO_EXIF]) tag = "foo bar" self.tie_core.tag(WRITE_FILE_NO_EXIF, [tag]) self.assertEqual([tag], self.tie_core.list([WRITE_FILE_NO_EXIF]), "File without exif data not tagged") os.remove(WRITE_FILE_NO_EXIF) def test_tag_invalid(self): self.assertRaises( InvalidMetaDataError, lambda: self.tie_core.tag(READ_FILE, [TEST_READ_TAG_1])) def test_list_no_exif_data(self): res = self.tie_core.list([READ_FILE_NO_EXIF]) self.assertEqual([], res, "File without exif data does not show empty tag list") def test_tag_duplicate(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) added_tag1 = "New tag Ä" self.tie_core.tag(WRITE_FILE_MD, [added_tag1, added_tag1, TEST_READ_TAG_1]) self.assertEqual( sorted([ TEST_READ_TAG_1.lower(), TEST_READ_TAG_2.lower(), added_tag1.lower() ]), self.tie_core.list([WRITE_FILE_MD]), "Tags after duplicate adding did not match") os.remove(WRITE_FILE_MD) def test_tag_not_found(self): self.assertRaises( FileNotFoundError, lambda: self.tie_core.tag( "../res/foobar.not.existant", [TEST_READ_TAG_1])) def test_untag(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) self.tie_core.untag(WRITE_FILE_MD, [TEST_READ_TAG_1]) self.assertEqual([TEST_READ_TAG_2.lower()], self.tie_core.list([WRITE_FILE_MD]), "Tags after removing did not match") os.remove(WRITE_FILE_MD) def test_untag_duplicate(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) self.tie_core.untag(WRITE_FILE_MD, [TEST_READ_TAG_1]) self.tie_core.untag(WRITE_FILE_MD, [TEST_READ_TAG_1]) self.tie_core.untag(WRITE_FILE_MD, [TEST_READ_TAG_1, TEST_READ_TAG_1]) self.assertEqual([TEST_READ_TAG_2.lower()], self.tie_core.list([WRITE_FILE_MD]), "Tags after duplicate removing did not match") os.remove(WRITE_FILE_MD) def test_untag_invalid(self): self.assertRaises( InvalidMetaDataError, lambda: self.tie_core.untag(READ_FILE, [TEST_READ_TAG_1])) def test_untag_not_found(self): self.assertRaises( FileNotFoundError, lambda: self.tie_core.untag( "../res/foobar.not.existant", [TEST_READ_TAG_1])) def test_clear(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) self.tie_core.clear(WRITE_FILE_MD) tags_after_clear = self.tie_core.list([WRITE_FILE_MD]) self.assertEqual([], tags_after_clear, "Tag list was not empty after clear") os.remove(WRITE_FILE_MD) def test_clear_invalid(self): cli.run_cmd(["cp", READ_FILE, WRITE_FILE]) self.tie_core.clear(WRITE_FILE) tags_after_clear = self.tie_core.list([WRITE_FILE]) self.assertEqual([], tags_after_clear, "Tag list was not empty after clearing corrupt file") os.remove(WRITE_FILE) def test_clear_not_found(self): self.assertRaises( FileNotFoundError, lambda: self.tie_core.clear("../res/foobar.not.existant")) def test_index(self): self.tie_core.update_index(READ_FILE_MD) link_name = self.files_base_path + ":" + "read_md.jpg" self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name)), "no link in tagdir 1") self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name)), "no link in tagdir 2") def test_list_all_tags(self): cli.run_cmd(["cp", READ_FILE_MD, WRITE_FILE_MD]) self.tie_core.update_index(WRITE_FILE_MD) tags = self.tie_core.list_all_tags() self.assertEqual( sorted([TEST_READ_TAG_1.lower(), TEST_READ_TAG_2.lower()]), sorted(tags)) os.remove(WRITE_FILE_MD)
class TestIndex(TestCase): def setUp(self): _remove_index() self.index = Index(TEST_INDEX_LOCATION, ee.ExifEditor(Configuration())) self.files_base_path = _path_to_linkname(os.path.abspath("../res")) def tearDown(self): _remove_index() def test_initialization(self): self.assertTrue(os.path.isdir(TEST_INDEX_LOCATION)) self.assertTrue( os.path.isdir(os.path.join(TEST_INDEX_LOCATION, "tags"))) def test_update_file(self): self.index.update("../res/read_md.jpg") link_name = self.files_base_path + ":" + "read_md.jpg" self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name)), "no link in tagdir 1") self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name)), "no link in tagdir 2") def test_list_tags(self): cli.run_cmd( ["mkdir", "-p", os.path.join(TEST_INDEX_LOCATION, "tags", "foo")]) cli.run_cmd( ["mkdir", "-p", os.path.join(TEST_INDEX_LOCATION, "tags", "bar")]) tags = self.index.list_tags() self.assertEqual(["bar", "foo"], tags, "tags list incorrect") def test_list_files(self): self.index.update(READ_FILE_MD) files = self.index.list_files(TEST_READ_TAG_1) self.assertEqual([os.path.abspath(READ_FILE_MD)], files, "files list incorrect") def test_update_file_invalid_md(self): img = os.path.abspath("../res/read.jpg") linkname = _path_to_linkname(img) removed_tag_dir = os.path.join(TEST_INDEX_LOCATION, "tags", "removed tag") link_path = os.path.join(removed_tag_dir, linkname) cli.run_cmd(["mkdir", "-p", removed_tag_dir]) cli.run_cmd(["ln", "-sf", img, link_path]) self.index.update("../res/read.jpg") self.assertFalse(os.path.islink(link_path), "tag was not removed!") def test_update_vanished_file(self): vanished_file = "../res/vanished_file.jpg" link_path = self._prepare_vanished_file(vanished_file, TEST_READ_TAG_2) self.index.update(vanished_file) self.assertFalse(os.path.islink(link_path), "tag of vanished file was not removed!") def test_update_vanished_file_with_implicit_update(self): vanished_file = "../res/vanished_file.jpg" link_path = self._prepare_vanished_file(vanished_file, TEST_READ_TAG_2) self.index.update("../res/") self.assertFalse(os.path.islink(link_path), "tag of vanished file was not removed!") def test_list_vanished_file(self): vanished_file = "../res/vanished_file.jpg" link_path = self._prepare_vanished_file(vanished_file, TEST_READ_TAG_2) self.index.list_files(TEST_READ_TAG_2) self.assertFalse(os.path.islink(link_path), "tag of vanished file was not removed!") def test_update_vanished_directory(self): vanished_directory = "../res/vanished_directory" vanished_file = os.path.join(vanished_directory, "some_subdir/vanished_file.jpg") link_path = self._prepare_vanished_file(vanished_file, TEST_READ_TAG_2) self.index.update(vanished_directory) self.assertFalse(os.path.islink(link_path), "tag of file in vanished directory was not removed!") def test_list_vanished_directory(self): vanished_directory = "../res/vanished_directory" vanished_file = os.path.join(vanished_directory, "some_subdir/vanished_file.jpg") link_path = self._prepare_vanished_file(vanished_file, TEST_READ_TAG_2) self.index.list_files(TEST_READ_TAG_2) self.assertFalse(os.path.islink(link_path), "tag of file in vanished directory was not removed!") def _prepare_vanished_file(self, path: str, tag_name: str) -> str: img = os.path.abspath(path) linkname = _path_to_linkname(img) tag_dir = os.path.join(TEST_INDEX_LOCATION, "tags", tag_name) link_path = os.path.join(tag_dir, linkname) cli.run_cmd(["mkdir", "-p", tag_dir]) cli.run_cmd(["ln", "-sf", img, link_path]) return link_path def test_update_dir(self): self.index.update("../res/recursive.d") link_name_root = self.files_base_path + ":recursive.d:" + "read_md.jpg" link_name_lvl1 = self.files_base_path + ":recursive.d:level1:" + "read_md.jpg" link_name_lvl2 = self.files_base_path + ":recursive.d:level1:level2:" + "read_md.jpg" for link_name in [link_name_root, link_name_lvl1, link_name_lvl2]: self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name)), "Link " + link_name + " should exist in tagdir 1 but does not") self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name)), "Link " + link_name + " should exist in tagdir 2 but does not") def test_removed_tag(self): img = os.path.abspath("../res/read_md.jpg") linkname = _path_to_linkname(img) removed_tag_dir = os.path.join(TEST_INDEX_LOCATION, "tags", "removed tag") link_path = os.path.join(removed_tag_dir, linkname) cli.run_cmd(["mkdir", "-p", removed_tag_dir]) cli.run_cmd(["ln", "-sf", img, link_path]) self.index.update("../res/read_md.jpg") self.assertFalse(os.path.islink(link_path), "tag was not removed!") # Execute again to test if this (incorrectly) raises a FileNotFoundError self.index.update("../res/read_md.jpg") def test_auto_remove_empty_tag_dir(self): empty_tag_dir = os.path.join(TEST_INDEX_LOCATION, "tags", "removed tag") cli.run_cmd(["mkdir", "-p", empty_tag_dir]) self.index.update("../res/read_md.jpg") self.assertFalse(os.path.isdir(empty_tag_dir), "empty tag dir was not removed!") def test_symlinks_followed(self): self.index.update("../res/recursive.d/level1_symlinked/read_md.jpg") link_name = self.files_base_path + ":recursive.d:level1:read_md.jpg" self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name)), "no link in tagdir 1") self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name)), "no link in tagdir 2") def test_unfollowed_symlinks_corrected(self): link_name_right = self.files_base_path + ":recursive.d:level1:read_md.jpg" link_name_wrong = self.files_base_path + ":recursive.d:level1_symlinked:read_md.jpg" cli.run_cmd([ "mkdir", "-p", os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower()) ]) cli.run_cmd([ "mkdir", "-p", os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower()) ]) cli.run_cmd([ "ln", "-sf", "../res/recursive.d/level1_symlinked/read_md.jpg", os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name_wrong) ]) cli.run_cmd([ "ln", "-sf", "../res/recursive.d/level1_symlinked/read_md.jpg", os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name_wrong) ]) self.index.update("../res/recursive.d/level1_symlinked/read_md.jpg") self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name_right)), "no link in tagdir 1") self.assertTrue( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name_right)), "no link in tagdir 2") self.assertFalse( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_1.lower(), link_name_wrong)), "wrong link in tagdir 1") self.assertFalse( os.path.islink( os.path.join(TEST_INDEX_LOCATION, "tags", TEST_READ_TAG_2.lower(), link_name_wrong)), "wrong link in tagdir 2")