Anna Schumaker
afb599dcf4
I extract the artist, length, mbid, mtime, tracknumber, and title from the tags to use when constructing Tracks. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
284 lines
12 KiB
Python
284 lines
12 KiB
Python
# Copyright 2022 (c) Anna Schumaker
|
|
"""Tests our Mutagen wrapper."""
|
|
import pathlib
|
|
import threading
|
|
import unittest.mock
|
|
import emmental.db.tagger
|
|
import tests.util
|
|
|
|
|
|
class TestTags(tests.util.TestCase):
|
|
"""Test the Tags object."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
super().setUp()
|
|
self.file = pathlib.Path("/a/b/c/track.ogg")
|
|
|
|
def make_tags(self, raw_tags: dict) -> emmental.db.tagger.Tags:
|
|
"""Set up and return our Tags object."""
|
|
audio_tags = emmental.audio.tagger._Tags(self.file, raw_tags)
|
|
return emmental.db.tagger.Tags(self.sql, audio_tags)
|
|
|
|
def test_init(self):
|
|
"""Test that the Tags object was set up properly."""
|
|
tags = self.make_tags({})
|
|
self.assertEqual(tags.db, self.sql)
|
|
|
|
self.assertIsNone(tags.album)
|
|
self.assertIsNone(tags.decade)
|
|
self.assertIsNone(tags.medium)
|
|
self.assertIsNone(tags.year)
|
|
|
|
self.assertListEqual(tags.album_artists, [])
|
|
self.assertListEqual(tags.artists, [])
|
|
self.assertListEqual(tags.genres, [])
|
|
|
|
def test_album(self):
|
|
"""Test that the album was tagged properly."""
|
|
raw_tags = {"album": ["Album Name"],
|
|
"musicbrainz_releasegroupid": ["ab-cd-ef"],
|
|
"albumartist": ["Album Artist"],
|
|
"date": ["1988-06"]}
|
|
|
|
with unittest.mock.patch.object(pathlib.Path, "is_file",
|
|
return_value=True):
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertIsInstance(tags.album, emmental.db.albums.Album)
|
|
self.assertEqual(tags.album.name, "Album Name")
|
|
self.assertEqual(tags.album.mbid, "ab-cd-ef")
|
|
self.assertEqual(tags.album.artist, "Album Artist")
|
|
self.assertEqual(tags.album.release, "1988-06")
|
|
self.assertEqual(tags.album.cover, pathlib.Path("/a/b/c/cover.jpg"))
|
|
|
|
self.assertEqual(self.make_tags(raw_tags).album, tags.album)
|
|
self.assertIsNone(tags.album.cover)
|
|
|
|
def test_album_artists(self):
|
|
"""Test that album artists were tagged and updated properly."""
|
|
raw_tags = {"album": ["Album Name"],
|
|
"musicbrainz_releasegroupid": ["ab-cd-ef"],
|
|
"albumartist": ["Artist 1", "Artist 2"],
|
|
"musicbrainz_albumartistid": ["gh-ij-kl", "mn-op-qr"],
|
|
"date": ["1988-06"]}
|
|
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertEqual(len(tags.album_artists), 2)
|
|
for i, artist in enumerate(tags.album_artists):
|
|
with self.subTest(i=i):
|
|
self.assertIsInstance(artist, emmental.db.artists.Artist)
|
|
self.assertEqual(artist.name, f"Artist {i+1}")
|
|
self.assertEqual(artist.mbid,
|
|
"gh-ij-kl" if i == 0 else "mn-op-qr")
|
|
|
|
self.assertListEqual(tags.album.get_artists(), tags.album_artists)
|
|
self.assertListEqual(self.make_tags(raw_tags).album_artists,
|
|
tags.album_artists)
|
|
|
|
raw_tags["albumartist"] = ["Artist 1", "Artist 3"]
|
|
raw_tags["musicbrainz_albumartistid"] = ["gh-ij-kl", "st-uv-wx"]
|
|
updated = self.make_tags(raw_tags)
|
|
self.assertEqual(updated.album, tags.album)
|
|
self.assertListEqual(tags.album.get_artists(), updated.album_artists)
|
|
|
|
def test_artists(self):
|
|
"""Test that artists were tagged properly."""
|
|
raw_tags = {"artists": ["Artist 1", "Artist 2"],
|
|
"albumartist": ["Artist 3"]}
|
|
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertEqual(len(tags.artists), 3)
|
|
for i, artist in enumerate(tags.artists):
|
|
with self.subTest(i=i):
|
|
self.assertIsInstance(artist, emmental.db.artists.Artist)
|
|
self.assertEqual(artist.name, f"Artist {i+1}")
|
|
|
|
self.assertListEqual(self.make_tags(raw_tags).artists, tags.artists)
|
|
|
|
def test_decade(self):
|
|
"""Test that the decade was tagged properly."""
|
|
raw_tags = {"date": ["1988-06-17"]}
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertIsInstance(tags.decade, emmental.db.decades.Decade)
|
|
self.assertEqual(tags.decade.decade, 1980)
|
|
|
|
self.assertEqual(self.make_tags(raw_tags).decade, tags.decade)
|
|
|
|
def test_genres(self):
|
|
"""Test that genres were tagged properly."""
|
|
raw_tags = {"genre": ["Genre 1", "Genre 2"]}
|
|
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertEqual(len(tags.genres), 2)
|
|
for i, genre in enumerate(tags.genres):
|
|
with self.subTest(i=i):
|
|
self.assertIsInstance(genre, emmental.db.genres.Genre)
|
|
self.assertEqual(genre.name, f"Genre {i+1}")
|
|
|
|
self.assertListEqual(self.make_tags(raw_tags).genres, tags.genres)
|
|
|
|
def test_medium(self):
|
|
"""Test that the medium was tagged properly."""
|
|
raw_tags = {"album": ["Album Name"],
|
|
"musicbrainz_releasegroupid": ["ab-cd-ef"],
|
|
"albumartist": ["Album Artist"],
|
|
"date": ["1988-06"],
|
|
"discnumber": ["2"],
|
|
"discsubtitle": ["Subtitle"],
|
|
"media": ["CD"]}
|
|
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertIsInstance(tags.medium, emmental.db.media.Medium)
|
|
self.assertEqual(tags.medium.number, 2)
|
|
self.assertEqual(tags.medium.name, "Subtitle")
|
|
self.assertEqual(tags.medium.type, "CD")
|
|
|
|
raw_tags["discsubtitle"] = ["New Subtitle"]
|
|
self.assertEqual(self.make_tags(raw_tags).medium, tags.medium)
|
|
self.assertEqual(tags.medium.name, "New Subtitle")
|
|
|
|
def test_year(self):
|
|
"""Test that the year was tagged properly."""
|
|
raw_tags = {"date": ["1988-06-17"]}
|
|
tags = self.make_tags(raw_tags)
|
|
self.assertIsInstance(tags.year, emmental.db.years.Year)
|
|
self.assertEqual(tags.year.year, 1988)
|
|
|
|
self.assertEqual(self.make_tags(raw_tags).year, tags.year)
|
|
|
|
|
|
@unittest.mock.patch("emmental.audio.tagger.tag_file")
|
|
class TestTaggerThread(tests.util.TestCase):
|
|
"""Test the tagger thread behavior."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
super().setUp()
|
|
self.tagger = emmental.db.tagger.Thread()
|
|
self.tags = dict()
|
|
|
|
def tearDown(self):
|
|
"""Clean up."""
|
|
super().tearDown()
|
|
self.tagger.stop()
|
|
|
|
def make_tags(self, tags: dict) -> emmental.db.tagger.Tags:
|
|
"""Set up and return our Tags object."""
|
|
return emmental.audio.tagger._Tags(pathlib.Path("/a/b/c.ogg"), tags)
|
|
|
|
def test_init(self, mock_file: unittest.mock.Mock):
|
|
"""Test that the tagger thread was initialized properly."""
|
|
self.assertIsInstance(self.tagger, threading.Thread)
|
|
self.assertIsInstance(self.tagger._condition, threading.Condition)
|
|
self.assertTrue(self.tagger.is_alive())
|
|
|
|
def test_stop(self, mock_file: unittest.mock.Mock):
|
|
"""Test the stop function."""
|
|
mock_connection = unittest.mock.Mock()
|
|
mock_connection.close = unittest.mock.Mock()
|
|
|
|
self.tagger._file = "abcde"
|
|
self.tagger._connection = mock_connection
|
|
|
|
with unittest.mock.patch.object(self.tagger._condition, "notify",
|
|
wraps=self.tagger._condition.notify) \
|
|
as mock_notify:
|
|
self.tagger.stop()
|
|
self.assertIsNone(self.tagger._file)
|
|
mock_notify.assert_called()
|
|
|
|
self.assertFalse(self.tagger.is_alive())
|
|
self.assertIsNone(self.tagger._connection)
|
|
mock_connection.close.assert_called()
|
|
|
|
def test_tag_file(self, mock_file: unittest.mock.Mock):
|
|
"""Test asking the thread to tag a file."""
|
|
self.assertIsInstance(self.tagger.ready, threading.Event)
|
|
self.assertIsNone(self.tagger._file)
|
|
self.assertIsNone(self.tagger._tags)
|
|
self.assertTrue(self.tagger.ready.is_set())
|
|
|
|
mock_file.return_value = None
|
|
self.tagger.ready.set()
|
|
self.tagger._tags = 12345
|
|
self.tagger.tag_file(pathlib.Path("/a/b/c.ogg"))
|
|
self.assertFalse(self.tagger.ready.is_set())
|
|
self.assertEqual(self.tagger._file, pathlib.Path("/a/b/c.ogg"))
|
|
self.assertIsNone(self.tagger._tags)
|
|
|
|
self.tagger.ready.wait()
|
|
self.assertIsNone(self.tagger._tags)
|
|
mock_file.assert_called_with(pathlib.Path("/a/b/c.ogg"), None)
|
|
|
|
mock_file.return_value = self.tags
|
|
self.tagger.tag_file(pathlib.Path("/a/b/c.ogg"))
|
|
self.tagger.ready.wait()
|
|
self.assertIsNotNone(self.tagger._tags)
|
|
|
|
def test_get_result(self, mock_file: unittest.mock.Mock):
|
|
"""Test creating a Tags structure after tagging."""
|
|
mock_file.return_value = None
|
|
self.tagger.tag_file(pathlib.Path("/a/b/c.ogg"))
|
|
self.assertTupleEqual(self.tagger.get_result(self.sql), (None, None))
|
|
|
|
self.tagger.ready.wait()
|
|
self.assertTupleEqual(self.tagger.get_result(self.sql),
|
|
(pathlib.Path("/a/b/c.ogg"), None))
|
|
self.assertIsNone(self.tagger._file)
|
|
|
|
mock_file.return_value = self.make_tags(dict())
|
|
self.tagger.tag_file(pathlib.Path("/a/b/c.ogg"))
|
|
self.tagger.ready.wait()
|
|
(file, tags) = self.tagger.get_result(self.sql)
|
|
self.assertEqual(file, pathlib.Path("/a/b/c.ogg"))
|
|
self.assertIsInstance(tags, emmental.db.tagger.Tags)
|
|
self.assertIsNone(self.tagger._file)
|
|
self.assertIsNone(self.tagger._tags)
|
|
|
|
@unittest.mock.patch("emmental.db.connection.Connection.__call__")
|
|
@unittest.mock.patch("musicbrainzngs.get_artist_by_id")
|
|
def test_tag_file_lookup_mbid(self, mock_get_artist: unittest.mock.Mock,
|
|
mock_connection: unittest.mock.Mock,
|
|
mock_file: unittest.mock.Mock):
|
|
"""Test looking up artists with an MBID but no name after tagging."""
|
|
audio_tags = self.make_tags({"albumartist": ["No Artist"],
|
|
"musicbrainz_albumartistid":
|
|
["ab-cd-ef", "gh-ij-kl"]})
|
|
mock_file.return_value = audio_tags
|
|
mock_get_artist.return_value = {"artist": {"name": "Some Artist"}}
|
|
|
|
mock_cursor = unittest.mock.Mock()
|
|
mock_cursor.fetchone = unittest.mock.Mock(return_value=None)
|
|
mock_connection.return_value = mock_cursor
|
|
|
|
self.tagger.tag_file(pathlib.Path("/a/b/c.ogg"))
|
|
self.tagger.ready.wait()
|
|
self.assertEqual(audio_tags.artists[0].name, "Some Artist")
|
|
self.assertEqual(audio_tags.artists[1].name, "Some Artist")
|
|
|
|
@unittest.mock.patch("emmental.db.connection.Connection.__call__")
|
|
@unittest.mock.patch("musicbrainzngs.get_artist_by_id")
|
|
def test_tag_file_lookup_sql(self, mock_get_artist: unittest.mock.Mock,
|
|
mock_connection: unittest.mock.Mock,
|
|
mock_file: unittest.mock.Mock):
|
|
"""Test looking up unnamed artists in the database."""
|
|
audio_tags = self.make_tags({"albumartist": ["No Artist"],
|
|
"musicbrainz_albumartistid":
|
|
["ab-cd-ef", "gh-ij-kl"]})
|
|
mock_file.return_value = audio_tags
|
|
mock_get_artist.return_value = {"artist": {"name": None}}
|
|
|
|
mock_row = {"name": "Some Artist"}
|
|
mock_cursor = unittest.mock.Mock()
|
|
mock_cursor.fetchone = unittest.mock.Mock(return_value=mock_row)
|
|
mock_connection.return_value = mock_cursor
|
|
|
|
self.assertIsNone(self.tagger._connection)
|
|
|
|
self.tagger.tag_file(pathlib.Path("/a/b/c.ogg"))
|
|
self.tagger.ready.wait()
|
|
self.assertIsInstance(self.tagger._connection,
|
|
emmental.db.connection.Connection)
|
|
self.assertEqual(audio_tags.artists[0].name, "Some Artist")
|
|
self.assertEqual(audio_tags.artists[1].name, "Some Artist")
|