]> kaliko git repositories - mpd-sima.git/blob - tests/test_simadb.py
Remove bad heuristic to infer artist aliases
[mpd-sima.git] / tests / test_simadb.py
1 # coding: utf-8
2
3 import datetime
4 import unittest
5 import os
6
7 from sima.lib.simadb import SimaDB
8 from sima.lib.track import Track
9 from sima.lib.meta import Album, Artist, MetaContainer
10
11
12 DEVOLT = {
13     'album': 'Grey',
14     'albumartist': 'Devolt',
15     'albumartistsort': 'Devolt',
16     'artist': 'Devolt',
17     'genre': ['Rock'],
18     'date': '2011-12-01',
19     'disc': '1/1',
20     'file': 'music/Devolt/2011-Grey/03-Devolt - Crazy.mp3',
21     'last-modified': '2012-04-02T20:48:59Z',
22     'musicbrainz_albumartistid': 'd8e7e3e2-49ab-4f7c-b148-fc946d521f99',
23     'musicbrainz_albumid': 'ea2ef2cf-59e1-443a-817e-9066e3e0be4b',
24     'musicbrainz_artistid': 'd8e7e3e2-49ab-4f7c-b148-fc946d521f99',
25     'musicbrainz_trackid': 'fabf8fc9-2ae5-49c9-8214-a839c958d872',
26     'time': '220',
27     'duration': '220.000',
28     'title': 'Crazy',
29     'track': '3/6'}
30
31 DB_FILE = 'file::memory:?cache=shared'
32 KEEP_FILE = True  # File db in file to ease debug
33 if KEEP_FILE:
34     DB_FILE = '/dev/shm/unittest.sqlite'
35 CURRENT = datetime.datetime.utcnow()
36 IN_THE_PAST = CURRENT - datetime.timedelta(hours=1)
37
38
39 class Main(unittest.TestCase):
40     """Deal with database creation and purge between tests"""
41
42     @classmethod
43     def setUpClass(self):
44         self.db = SimaDB(db_path=DB_FILE)
45
46     def setUp(self):
47         # Maintain a connection to keep the database (when stored in memory)
48         self.conn = self.db.get_database_connection()
49         self.db.drop_all()
50         self.db.create_db()
51
52     def tearDown(self):
53         if not KEEP_FILE:
54             self.db.drop_all()
55         self.conn.close()
56
57     @classmethod
58     def tearDownClass(self):
59         if KEEP_FILE:
60             return
61         if os.path.isfile(DB_FILE):
62             os.unlink(DB_FILE)
63
64
65 class Test_00DB(Main):
66
67     def test_00_recreation(self):
68         self.db.create_db()
69
70     def test_01_add_track(self):
71         trk = Track(**DEVOLT)
72         trk_id = self.db.get_track(trk)
73         self.assertEqual(trk_id, self.db.get_track(trk, add=False),
74                          'Same track, same record')
75
76     def test_02_history(self):
77         # set records in the past to ease purging then
78         last = CURRENT - datetime.timedelta(seconds=5)
79         trk = Track(**DEVOLT)
80         self.db.add_history(trk, date=last)
81         self.db.add_history(trk, date=last)
82         hist = self.db.fetch_history()
83         self.assertEqual(len(hist), 1, 'same track results in a single record')
84
85         trk_foo = Track(file="/foo/bar/baz.flac")
86         self.db.add_history(trk_foo, date=last)
87         hist = self.db.fetch_history()
88         self.assertEqual(len(hist), 2)
89
90         self.db.add_history(trk, date=last)
91         hist = self.db.fetch_history()
92         self.assertEqual(len(hist), 2)
93         self.db.purge_history(duration=0)
94         hist = self.db.fetch_history()
95         self.assertEqual(len(hist), 0)
96
97         # Controls we got history in the right order
98         # recent first, oldest last
99         hist = list()
100         for i in range(1, 5):  # starts at 1 to ensure records are in the past
101             trk = Track(file=f'/foo/bar.{i}', name=f'{i}-baz',
102                         album='foolbum', genre=f'{i}')
103             hist.append(trk)
104             last = CURRENT - datetime.timedelta(minutes=i)
105             self.db.add_history(trk, date=last)
106         hist_records = self.db.fetch_history()
107         self.assertEqual(hist, hist_records)
108         self.db.purge_history(duration=0)
109
110     def test_history_to_tracks(self):
111         tr = dict(**DEVOLT)
112         tr.pop('file')
113         trk01 = Track(file='01', **tr)
114         self.db.add_history(trk01, CURRENT-datetime.timedelta(minutes=1))
115         #
116         tr.pop('musicbrainz_artistid')
117         trk02 = Track(file='02', **tr)
118         self.db.add_history(trk02, CURRENT-datetime.timedelta(minutes=2))
119         #
120         tr.pop('musicbrainz_albumid')
121         trk03 = Track(file='03', **tr)
122         self.db.add_history(trk03, CURRENT-datetime.timedelta(minutes=3))
123         #
124         tr.pop('musicbrainz_albumartistid')
125         trk04 = Track(file='04', **tr)
126         self.db.add_history(trk04, CURRENT-datetime.timedelta(minutes=4))
127         #
128         tr.pop('musicbrainz_trackid')
129         trk05 = Track(file='05', **tr)
130         self.db.add_history(trk05, CURRENT-datetime.timedelta(minutes=5))
131         history = self.db.fetch_history()
132         self.assertEqual(len(history), 5)
133         # Controls history ordering, recent first
134         self.assertEqual(history, [trk01, trk02, trk03, trk04, trk05])
135
136     def test_history_to_artists(self):
137         tr = dict(**DEVOLT)
138         tr.pop('file')
139         tr.pop('musicbrainz_artistid')
140         #
141         trk01 = Track(file='01', **tr)
142         self.db.add_history(trk01, CURRENT-datetime.timedelta(hours=1))
143         #
144         trk02 = Track(file='02', **tr)
145         self.db.add_history(trk02, CURRENT-datetime.timedelta(hours=1))
146         self.db.add_history(trk02, CURRENT-datetime.timedelta(hours=1))
147         #
148         trk03 = Track(file='03', **tr)
149         self.db.add_history(trk03, CURRENT-datetime.timedelta(hours=1))
150         # got multiple tracks, same artist/album, got artist/album history len = 1
151         art_history = self.db.fetch_artists_history()
152         alb_history = self.db.fetch_albums_history()
153         self.assertEqual(len(art_history), 1)
154         self.assertEqual(len(alb_history), 1)
155         self.assertEqual(art_history, [trk01.Artist])
156
157         # Now add new artist to history
158         trk04 = Track(file='04', artist='New Art')
159         trk05 = Track(file='05', artist='New² Art')
160         self.db.add_history(trk04, CURRENT-datetime.timedelta(minutes=3))
161         self.db.add_history(trk03, CURRENT-datetime.timedelta(minutes=2))
162         self.db.add_history(trk05, CURRENT-datetime.timedelta(minutes=1))
163         art_history = self.db.fetch_artists_history()
164         # Now we should have 4 artists in history
165         self.assertEqual(len(art_history), 4)
166         # Controling order, recent first
167         self.assertEqual([trk05.artist, trk03.artist,
168                          trk04.artist, trk03.artist],
169                          art_history)
170
171     def test_albums_history(self):
172         # set records in the past to ease purging then
173         last = CURRENT - datetime.timedelta(seconds=5)
174         track = {
175             'album': 'album', 'title': 'title',
176             'albumartist': 'albumartist',
177             'artist': 'artist',
178             'file': 'foo/bar.flac',
179             'musicbrainz_albumartistid': 'd8e7e3e2-49ab-4f7c-b148-fc946d521f99',
180             'musicbrainz_albumid': 'ea2ef2cf-59e1-443a-817e-9066e3e0be4b',
181             'musicbrainz_artistid': 'd8e7e3e2-49ab-4f7c-b148-fc946d521f99',}
182         self.db.purge_history(duration=0)
183         self.db.add_history(Track(**track), last)
184         alb_history = self.db.fetch_albums_history()
185         alb = alb_history[0]
186         self.assertEqual(alb.Artist.name, track.get('albumartist'))
187         # Fetching Album history for "artist" should return "album"
188         artist = Artist(track.get('artist'), mbid=track.get('musicbrainz_artistid'))
189         alb_history = self.db.fetch_albums_history(artist)
190         self.assertTrue(alb_history)
191         # Falls back to album and MBID when albumartist and
192         # musicbrainz_albumartistid are not set
193         self.db.purge_history(duration=0)
194         track = {
195             'album': 'no albumartist set', 'title': 'title',
196             'artist': 'no album artist set',
197             'file': 'bar/foo.flac',
198             'musicbrainz_artistid': 'dddddddd-49ab-4f7c-b148-dddddddddddd',}
199         self.db.add_history(Track(**track), last)
200         alb_history = self.db.fetch_albums_history()
201         album = alb_history[0]
202         self.assertEqual(album.albumartist, track['artist'])
203         self.assertEqual(album.Artist.mbid, track['musicbrainz_artistid'])
204
205     def test_04_filtering_history(self):
206         # Controls artist history filtering
207         for i in range(0, 5):
208             trk = Track(file=f'/foo/bar.{i}', name=f'{i}-baz',
209                         artist=f'{i}-art', album=f'{i}-lbum')
210             last = CURRENT - datetime.timedelta(minutes=i)
211             self.db.add_history(trk, date=last)
212             if i == 5:  # bounce latest record
213                 self.db.add_history(trk, date=last)
214         art_history = self.db.fetch_artists_history()
215         # Already checked but to be sure, we should have 5 artists in history
216         self.assertEqual(len(art_history), 5)
217         for needle in ['4-art', Artist(name='4-art')]:
218             art_history = self.db.fetch_artists_history(needle=needle)
219             self.assertEqual(art_history, [needle])
220         needle = Artist(name='not-art')
221         art_history = self.db.fetch_artists_history(needle=needle)
222         self.assertEqual(art_history, [])
223         # Controls artists history filtering
224         #   for a list of Artist objects
225         needle_list = [Artist(name='3-art'), Artist(name='4-art')]
226         art_history = self.db.fetch_artists_history(needle=needle_list)
227         self.assertEqual(art_history, needle_list)
228         #   for a MetaContainer
229         needle_meta = MetaContainer(needle_list)
230         art_history = self.db.fetch_artists_history(needle=needle_meta)
231         self.assertEqual(art_history, needle_list)
232         #   for a list of string (Meta object handles Artist/str comparison)
233         art_history = self.db.fetch_artists_history(['3-art', '4-art'])
234         self.assertEqual(art_history, needle_list)
235
236         # Controls album history filtering
237         needle = Artist(name='4-art')
238         alb_history = self.db.fetch_albums_history(needle=needle)
239         self.assertEqual(alb_history, [Album(name='4-lbum')])
240
241     def test_05_triggers(self):
242         self.db.purge_history(duration=0)
243         tracks_ids = list()
244         #  Add a first track
245         track = Track(file='/baz/bar.baz', name='baz', artist='fooart',
246                       albumartist='not-same', album='not-same',)
247         self.db.get_track(track)
248         # Set 6 more records from same artist but not same album
249         for i in range(1, 6):  # starts at 1 to ensure records are in the past
250             trk = Track(file=f'/foo/{i}', name=f'{i}', artist='fooart',
251                         albumartist='fooalbart', album='foolbum',)
252             # Add track, save its DB id
253             tracks_ids.append(self.db.get_track(trk))
254             # set records in the past to ease purging then
255             last = CURRENT - datetime.timedelta(minutes=i)
256             self.db.add_history(trk, date=last)  # Add to history
257         conn = self.db.get_database_connection()
258         # for tid in tracks_ids:
259         for tid in tracks_ids[:-1]:
260             # Delete lastest record
261             conn.execute('DELETE FROM history WHERE history.track = ?',
262                          (tid,))
263             c = conn.execute('SELECT albums.name FROM albums;')
264             # There are still albums records (still a history using it)
265             self.assertIn((trk.album,), c.fetchall())
266         # purging last entry in history for album == trk.album
267         conn.execute('DELETE FROM history WHERE history.track = ?',
268                      (tracks_ids[-1],))
269         # triggers purge other tables if possible
270         conn.execute('SELECT albums.name FROM albums;')
271         albums = c.fetchall()
272         # No more "foolbum" in the table albums
273         self.assertNotIn(('foolbum',), albums)
274         # There is still "fooart" though
275         c = conn.execute('SELECT artists.name FROM artists;')
276         artists = c.fetchall()
277         # No more "foolbum" in the table albums
278         self.assertIn(('fooart',), artists)
279         conn.close()
280
281     def test_06_add_album(self):
282         pass
283
284 class Test_01BlockList(Main):
285
286     def test_blocklist_addition(self):
287         trk = Track(file='/foo/bar/baz', name='title')
288         self.assertIs(self.db.get_bl_track(trk, add=False), None)
289         tracks_ids = list()
290         # Set 6 records, same album
291         for i in range(1, 6):  # starts at 1 to ensure records are in the past
292             trk = Track(file=f'/foo/{i}', name=f'{i}', artist='fooart',
293                         albumartist='fooalbart', album='foolbum',)
294             # Add track, save its DB id
295             tracks_ids.append(self.db.get_track(trk))
296             if i == 1:
297                 self.db.get_bl_track(trk)
298                 self.assertIsNot(self.db.get_bl_track(trk, add=False), None)
299             if i == 2:
300                 self.db.get_bl_album(Album(name=trk.album))
301             if i == 3:
302                 self.db.get_bl_artist(trk.Artist)
303             if i == 4:
304                 self.assertIs(self.db.get_bl_track(trk, add=False), None)
305
306
307     def test_blocklist_triggers_00(self):
308         trk01 = Track(file='01', name='01', artist='artist A', album='album A')
309         blart01_id = self.db.get_bl_artist(trk01.Artist)
310         blalb01_id = self.db.get_bl_album(Album(name=trk01.album, mbid=trk01.musicbrainz_albumid))
311         conn = self.db.get_database_connection()
312         self.db._remove_blocklist_id(blart01_id, with_connection=conn)
313         self.db._remove_blocklist_id(blalb01_id, with_connection=conn)
314         albums = conn.execute('SELECT albums.name FROM albums;').fetchall()
315         artists = conn.execute('SELECT artists.name FROM artists;').fetchall()
316         conn.close()
317         self.assertNotIn((trk01.album,), albums)
318         self.assertNotIn((trk01.artist,), artists)
319
320     def test_blocklist_triggers_01(self):
321         trk01 = Track(file='01', name='01', artist='artist A', album='album A')
322         trk02 = Track(file='02', name='01', artist='artist A', album='album B')
323         trk01_id = self.db.get_bl_track(trk01)
324         trk02_id = self.db.get_bl_track(trk02)
325         self.db.add_history(trk01, IN_THE_PAST)
326         self.db._remove_blocklist_id(trk01_id)
327         # bl trk01 removed:
328         # albums/artists table not affected since trk01_id still in history
329         conn = self.db.get_database_connection()
330         albums = conn.execute('SELECT albums.name FROM albums;').fetchall()
331         artists = conn.execute('SELECT artists.name FROM artists;').fetchall()
332         self.assertIn(('album A',), albums)
333         self.assertIn(('artist A',), artists)
334         self.db.purge_history(0)
335         # remove last reference to trk01
336         albums = conn.execute('SELECT albums.name FROM albums;').fetchall()
337         self.assertNotIn(('album A',), albums)
338         self.assertIn(('artist A',), artists)
339         # remove trk02
340         self.db._remove_blocklist_id(trk02_id)
341         albums = conn.execute('SELECT albums.name FROM albums;').fetchall()
342         artists = conn.execute('SELECT artists.name FROM artists;').fetchall()
343         self.assertNotIn(('album B',), albums)
344         self.assertNotIn(('artist A'), artists)
345         conn.close()
346
347
348 class Test_02Genre(Main):
349
350     def test_genre(self):
351         conn = self.db.get_database_connection()
352         self.db.get_genre('Post-Rock', with_connection=conn)
353         genres = list()
354         for i in range(1, 15):  # starts at 1 to ensure records are in the past
355             trk = Track(file=f'/foo/bar.{i}', name=f'{i}-baz',
356                         album='foolbum', artist=f'{i}-art', genre=f'{i}')
357             genres.append(f'{i}')
358             last = CURRENT - datetime.timedelta(minutes=i)
359             self.db.add_history(trk, date=last)
360         genre_hist = self.db.fetch_genres_history(limit=10)
361         self.assertEqual([g[0] for g in genre_hist], genres[:10])
362
363     def test_null_genres(self):
364         conn = self.db.get_database_connection()
365         genres = list()
366         for i in range(1, 2):  # starts at 1 to ensure records are in the past
367             trk = Track(file=f'/foo/bar.{i}', name=f'{i}-baz',
368                         album='foolbum', artist=f'{i}-art')
369             last = CURRENT - datetime.timedelta(minutes=i)
370             self.db.add_history(trk, date=last)
371         genre_hist = self.db.fetch_genres_history(limit=10)
372         self.assertEqual(genre_hist, [])
373
374 # VIM MODLINE
375 # vim: ai ts=4 sw=4 sts=4 expandtab fileencoding=utf8