1# -*- coding: utf-8 -*- 2# This file is part of beets. 3# Copyright 2016, Adrian Sampson. 4# 5# Permission is hereby granted, free of charge, to any person obtaining 6# a copy of this software and associated documentation files (the 7# "Software"), to deal in the Software without restriction, including 8# without limitation the rights to use, copy, modify, merge, publish, 9# distribute, sublicense, and/or sell copies of the Software, and to 10# permit persons to whom the Software is furnished to do so, subject to 11# the following conditions: 12# 13# The above copyright notice and this permission notice shall be 14# included in all copies or substantial portions of the Software. 15 16"""Tests for autotagging functionality. 17""" 18from __future__ import division, absolute_import, print_function 19 20import re 21import copy 22import unittest 23 24from test import _common 25from beets import autotag 26from beets.autotag import match 27from beets.autotag.hooks import Distance, string_dist 28from beets.library import Item 29from beets.util import plurality 30from beets.autotag import AlbumInfo, TrackInfo 31from beets import config 32 33 34class PluralityTest(_common.TestCase): 35 def test_plurality_consensus(self): 36 objs = [1, 1, 1, 1] 37 obj, freq = plurality(objs) 38 self.assertEqual(obj, 1) 39 self.assertEqual(freq, 4) 40 41 def test_plurality_near_consensus(self): 42 objs = [1, 1, 2, 1] 43 obj, freq = plurality(objs) 44 self.assertEqual(obj, 1) 45 self.assertEqual(freq, 3) 46 47 def test_plurality_conflict(self): 48 objs = [1, 1, 2, 2, 3] 49 obj, freq = plurality(objs) 50 self.assertTrue(obj in (1, 2)) 51 self.assertEqual(freq, 2) 52 53 def test_plurality_empty_sequence_raises_error(self): 54 with self.assertRaises(ValueError): 55 plurality([]) 56 57 def test_current_metadata_finds_pluralities(self): 58 items = [Item(artist='The Beetles', album='The White Album'), 59 Item(artist='The Beatles', album='The White Album'), 60 Item(artist='The Beatles', album='Teh White Album')] 61 likelies, consensus = match.current_metadata(items) 62 self.assertEqual(likelies['artist'], 'The Beatles') 63 self.assertEqual(likelies['album'], 'The White Album') 64 self.assertFalse(consensus['artist']) 65 66 def test_current_metadata_artist_consensus(self): 67 items = [Item(artist='The Beatles', album='The White Album'), 68 Item(artist='The Beatles', album='The White Album'), 69 Item(artist='The Beatles', album='Teh White Album')] 70 likelies, consensus = match.current_metadata(items) 71 self.assertEqual(likelies['artist'], 'The Beatles') 72 self.assertEqual(likelies['album'], 'The White Album') 73 self.assertTrue(consensus['artist']) 74 75 def test_albumartist_consensus(self): 76 items = [Item(artist='tartist1', album='album', 77 albumartist='aartist'), 78 Item(artist='tartist2', album='album', 79 albumartist='aartist'), 80 Item(artist='tartist3', album='album', 81 albumartist='aartist')] 82 likelies, consensus = match.current_metadata(items) 83 self.assertEqual(likelies['artist'], 'aartist') 84 self.assertFalse(consensus['artist']) 85 86 def test_current_metadata_likelies(self): 87 fields = ['artist', 'album', 'albumartist', 'year', 'disctotal', 88 'mb_albumid', 'label', 'catalognum', 'country', 'media', 89 'albumdisambig'] 90 items = [Item(**dict((f, '%s_%s' % (f, i or 1)) for f in fields)) 91 for i in range(5)] 92 likelies, _ = match.current_metadata(items) 93 for f in fields: 94 self.assertEqual(likelies[f], '%s_1' % f) 95 96 97def _make_item(title, track, artist=u'some artist'): 98 return Item(title=title, track=track, 99 artist=artist, album=u'some album', 100 length=1, 101 mb_trackid='', mb_albumid='', mb_artistid='') 102 103 104def _make_trackinfo(): 105 return [ 106 TrackInfo(u'one', None, artist=u'some artist', length=1, index=1), 107 TrackInfo(u'two', None, artist=u'some artist', length=1, index=2), 108 TrackInfo(u'three', None, artist=u'some artist', length=1, index=3), 109 ] 110 111 112def _clear_weights(): 113 """Hack around the lazy descriptor used to cache weights for 114 Distance calculations. 115 """ 116 Distance.__dict__['_weights'].computed = False 117 118 119class DistanceTest(_common.TestCase): 120 def tearDown(self): 121 super(DistanceTest, self).tearDown() 122 _clear_weights() 123 124 def test_add(self): 125 dist = Distance() 126 dist.add('add', 1.0) 127 self.assertEqual(dist._penalties, {'add': [1.0]}) 128 129 def test_add_equality(self): 130 dist = Distance() 131 dist.add_equality('equality', 'ghi', ['abc', 'def', 'ghi']) 132 self.assertEqual(dist._penalties['equality'], [0.0]) 133 134 dist.add_equality('equality', 'xyz', ['abc', 'def', 'ghi']) 135 self.assertEqual(dist._penalties['equality'], [0.0, 1.0]) 136 137 dist.add_equality('equality', 'abc', re.compile(r'ABC', re.I)) 138 self.assertEqual(dist._penalties['equality'], [0.0, 1.0, 0.0]) 139 140 def test_add_expr(self): 141 dist = Distance() 142 dist.add_expr('expr', True) 143 self.assertEqual(dist._penalties['expr'], [1.0]) 144 145 dist.add_expr('expr', False) 146 self.assertEqual(dist._penalties['expr'], [1.0, 0.0]) 147 148 def test_add_number(self): 149 dist = Distance() 150 # Add a full penalty for each number of difference between two numbers. 151 152 dist.add_number('number', 1, 1) 153 self.assertEqual(dist._penalties['number'], [0.0]) 154 155 dist.add_number('number', 1, 2) 156 self.assertEqual(dist._penalties['number'], [0.0, 1.0]) 157 158 dist.add_number('number', 2, 1) 159 self.assertEqual(dist._penalties['number'], [0.0, 1.0, 1.0]) 160 161 dist.add_number('number', -1, 2) 162 self.assertEqual(dist._penalties['number'], [0.0, 1.0, 1.0, 1.0, 163 1.0, 1.0]) 164 165 def test_add_priority(self): 166 dist = Distance() 167 dist.add_priority('priority', 'abc', 'abc') 168 self.assertEqual(dist._penalties['priority'], [0.0]) 169 170 dist.add_priority('priority', 'def', ['abc', 'def']) 171 self.assertEqual(dist._penalties['priority'], [0.0, 0.5]) 172 173 dist.add_priority('priority', 'gh', ['ab', 'cd', 'ef', 174 re.compile('GH', re.I)]) 175 self.assertEqual(dist._penalties['priority'], [0.0, 0.5, 0.75]) 176 177 dist.add_priority('priority', 'xyz', ['abc', 'def']) 178 self.assertEqual(dist._penalties['priority'], [0.0, 0.5, 0.75, 179 1.0]) 180 181 def test_add_ratio(self): 182 dist = Distance() 183 dist.add_ratio('ratio', 25, 100) 184 self.assertEqual(dist._penalties['ratio'], [0.25]) 185 186 dist.add_ratio('ratio', 10, 5) 187 self.assertEqual(dist._penalties['ratio'], [0.25, 1.0]) 188 189 dist.add_ratio('ratio', -5, 5) 190 self.assertEqual(dist._penalties['ratio'], [0.25, 1.0, 0.0]) 191 192 dist.add_ratio('ratio', 5, 0) 193 self.assertEqual(dist._penalties['ratio'], [0.25, 1.0, 0.0, 0.0]) 194 195 def test_add_string(self): 196 dist = Distance() 197 sdist = string_dist(u'abc', u'bcd') 198 dist.add_string('string', u'abc', u'bcd') 199 self.assertEqual(dist._penalties['string'], [sdist]) 200 self.assertNotEqual(dist._penalties['string'], [0]) 201 202 def test_add_string_none(self): 203 dist = Distance() 204 dist.add_string('string', None, 'string') 205 self.assertEqual(dist._penalties['string'], [1]) 206 207 def test_add_string_both_none(self): 208 dist = Distance() 209 dist.add_string('string', None, None) 210 self.assertEqual(dist._penalties['string'], [0]) 211 212 def test_distance(self): 213 config['match']['distance_weights']['album'] = 2.0 214 config['match']['distance_weights']['medium'] = 1.0 215 _clear_weights() 216 217 dist = Distance() 218 dist.add('album', 0.5) 219 dist.add('media', 0.25) 220 dist.add('media', 0.75) 221 self.assertEqual(dist.distance, 0.5) 222 223 # __getitem__() 224 self.assertEqual(dist['album'], 0.25) 225 self.assertEqual(dist['media'], 0.25) 226 227 def test_max_distance(self): 228 config['match']['distance_weights']['album'] = 3.0 229 config['match']['distance_weights']['medium'] = 1.0 230 _clear_weights() 231 232 dist = Distance() 233 dist.add('album', 0.5) 234 dist.add('medium', 0.0) 235 dist.add('medium', 0.0) 236 self.assertEqual(dist.max_distance, 5.0) 237 238 def test_operators(self): 239 config['match']['distance_weights']['source'] = 1.0 240 config['match']['distance_weights']['album'] = 2.0 241 config['match']['distance_weights']['medium'] = 1.0 242 _clear_weights() 243 244 dist = Distance() 245 dist.add('source', 0.0) 246 dist.add('album', 0.5) 247 dist.add('medium', 0.25) 248 dist.add('medium', 0.75) 249 self.assertEqual(len(dist), 2) 250 self.assertEqual(list(dist), [('album', 0.2), ('medium', 0.2)]) 251 self.assertTrue(dist == 0.4) 252 self.assertTrue(dist < 1.0) 253 self.assertTrue(dist > 0.0) 254 self.assertEqual(dist - 0.4, 0.0) 255 self.assertEqual(0.4 - dist, 0.0) 256 self.assertEqual(float(dist), 0.4) 257 258 def test_raw_distance(self): 259 config['match']['distance_weights']['album'] = 3.0 260 config['match']['distance_weights']['medium'] = 1.0 261 _clear_weights() 262 263 dist = Distance() 264 dist.add('album', 0.5) 265 dist.add('medium', 0.25) 266 dist.add('medium', 0.5) 267 self.assertEqual(dist.raw_distance, 2.25) 268 269 def test_items(self): 270 config['match']['distance_weights']['album'] = 4.0 271 config['match']['distance_weights']['medium'] = 2.0 272 _clear_weights() 273 274 dist = Distance() 275 dist.add('album', 0.1875) 276 dist.add('medium', 0.75) 277 self.assertEqual(dist.items(), [('medium', 0.25), ('album', 0.125)]) 278 279 # Sort by key if distance is equal. 280 dist = Distance() 281 dist.add('album', 0.375) 282 dist.add('medium', 0.75) 283 self.assertEqual(dist.items(), [('album', 0.25), ('medium', 0.25)]) 284 285 def test_update(self): 286 dist1 = Distance() 287 dist1.add('album', 0.5) 288 dist1.add('media', 1.0) 289 290 dist2 = Distance() 291 dist2.add('album', 0.75) 292 dist2.add('album', 0.25) 293 dist2.add('media', 0.05) 294 295 dist1.update(dist2) 296 297 self.assertEqual(dist1._penalties, {'album': [0.5, 0.75, 0.25], 298 'media': [1.0, 0.05]}) 299 300 301class TrackDistanceTest(_common.TestCase): 302 def test_identical_tracks(self): 303 item = _make_item(u'one', 1) 304 info = _make_trackinfo()[0] 305 dist = match.track_distance(item, info, incl_artist=True) 306 self.assertEqual(dist, 0.0) 307 308 def test_different_title(self): 309 item = _make_item(u'foo', 1) 310 info = _make_trackinfo()[0] 311 dist = match.track_distance(item, info, incl_artist=True) 312 self.assertNotEqual(dist, 0.0) 313 314 def test_different_artist(self): 315 item = _make_item(u'one', 1) 316 item.artist = u'foo' 317 info = _make_trackinfo()[0] 318 dist = match.track_distance(item, info, incl_artist=True) 319 self.assertNotEqual(dist, 0.0) 320 321 def test_various_artists_tolerated(self): 322 item = _make_item(u'one', 1) 323 item.artist = u'Various Artists' 324 info = _make_trackinfo()[0] 325 dist = match.track_distance(item, info, incl_artist=True) 326 self.assertEqual(dist, 0.0) 327 328 329class AlbumDistanceTest(_common.TestCase): 330 def _mapping(self, items, info): 331 out = {} 332 for i, t in zip(items, info.tracks): 333 out[i] = t 334 return out 335 336 def _dist(self, items, info): 337 return match.distance(items, info, self._mapping(items, info)) 338 339 def test_identical_albums(self): 340 items = [] 341 items.append(_make_item(u'one', 1)) 342 items.append(_make_item(u'two', 2)) 343 items.append(_make_item(u'three', 3)) 344 info = AlbumInfo( 345 artist=u'some artist', 346 album=u'some album', 347 tracks=_make_trackinfo(), 348 va=False, 349 album_id=None, 350 artist_id=None, 351 ) 352 self.assertEqual(self._dist(items, info), 0) 353 354 def test_incomplete_album(self): 355 items = [] 356 items.append(_make_item(u'one', 1)) 357 items.append(_make_item(u'three', 3)) 358 info = AlbumInfo( 359 artist=u'some artist', 360 album=u'some album', 361 tracks=_make_trackinfo(), 362 va=False, 363 album_id=None, 364 artist_id=None, 365 ) 366 dist = self._dist(items, info) 367 self.assertNotEqual(dist, 0) 368 # Make sure the distance is not too great 369 self.assertTrue(dist < 0.2) 370 371 def test_global_artists_differ(self): 372 items = [] 373 items.append(_make_item(u'one', 1)) 374 items.append(_make_item(u'two', 2)) 375 items.append(_make_item(u'three', 3)) 376 info = AlbumInfo( 377 artist=u'someone else', 378 album=u'some album', 379 tracks=_make_trackinfo(), 380 va=False, 381 album_id=None, 382 artist_id=None, 383 ) 384 self.assertNotEqual(self._dist(items, info), 0) 385 386 def test_comp_track_artists_match(self): 387 items = [] 388 items.append(_make_item(u'one', 1)) 389 items.append(_make_item(u'two', 2)) 390 items.append(_make_item(u'three', 3)) 391 info = AlbumInfo( 392 artist=u'should be ignored', 393 album=u'some album', 394 tracks=_make_trackinfo(), 395 va=True, 396 album_id=None, 397 artist_id=None, 398 ) 399 self.assertEqual(self._dist(items, info), 0) 400 401 def test_comp_no_track_artists(self): 402 # Some VA releases don't have track artists (incomplete metadata). 403 items = [] 404 items.append(_make_item(u'one', 1)) 405 items.append(_make_item(u'two', 2)) 406 items.append(_make_item(u'three', 3)) 407 info = AlbumInfo( 408 artist=u'should be ignored', 409 album=u'some album', 410 tracks=_make_trackinfo(), 411 va=True, 412 album_id=None, 413 artist_id=None, 414 ) 415 info.tracks[0].artist = None 416 info.tracks[1].artist = None 417 info.tracks[2].artist = None 418 self.assertEqual(self._dist(items, info), 0) 419 420 def test_comp_track_artists_do_not_match(self): 421 items = [] 422 items.append(_make_item(u'one', 1)) 423 items.append(_make_item(u'two', 2, u'someone else')) 424 items.append(_make_item(u'three', 3)) 425 info = AlbumInfo( 426 artist=u'some artist', 427 album=u'some album', 428 tracks=_make_trackinfo(), 429 va=True, 430 album_id=None, 431 artist_id=None, 432 ) 433 self.assertNotEqual(self._dist(items, info), 0) 434 435 def test_tracks_out_of_order(self): 436 items = [] 437 items.append(_make_item(u'one', 1)) 438 items.append(_make_item(u'three', 2)) 439 items.append(_make_item(u'two', 3)) 440 info = AlbumInfo( 441 artist=u'some artist', 442 album=u'some album', 443 tracks=_make_trackinfo(), 444 va=False, 445 album_id=None, 446 artist_id=None, 447 ) 448 dist = self._dist(items, info) 449 self.assertTrue(0 < dist < 0.2) 450 451 def test_two_medium_release(self): 452 items = [] 453 items.append(_make_item(u'one', 1)) 454 items.append(_make_item(u'two', 2)) 455 items.append(_make_item(u'three', 3)) 456 info = AlbumInfo( 457 artist=u'some artist', 458 album=u'some album', 459 tracks=_make_trackinfo(), 460 va=False, 461 album_id=None, 462 artist_id=None, 463 ) 464 info.tracks[0].medium_index = 1 465 info.tracks[1].medium_index = 2 466 info.tracks[2].medium_index = 1 467 dist = self._dist(items, info) 468 self.assertEqual(dist, 0) 469 470 def test_per_medium_track_numbers(self): 471 items = [] 472 items.append(_make_item(u'one', 1)) 473 items.append(_make_item(u'two', 2)) 474 items.append(_make_item(u'three', 1)) 475 info = AlbumInfo( 476 artist=u'some artist', 477 album=u'some album', 478 tracks=_make_trackinfo(), 479 va=False, 480 album_id=None, 481 artist_id=None, 482 ) 483 info.tracks[0].medium_index = 1 484 info.tracks[1].medium_index = 2 485 info.tracks[2].medium_index = 1 486 dist = self._dist(items, info) 487 self.assertEqual(dist, 0) 488 489 490class AssignmentTest(unittest.TestCase): 491 def item(self, title, track): 492 return Item( 493 title=title, track=track, 494 mb_trackid='', mb_albumid='', mb_artistid='', 495 ) 496 497 def test_reorder_when_track_numbers_incorrect(self): 498 items = [] 499 items.append(self.item(u'one', 1)) 500 items.append(self.item(u'three', 2)) 501 items.append(self.item(u'two', 3)) 502 trackinfo = [] 503 trackinfo.append(TrackInfo(u'one', None)) 504 trackinfo.append(TrackInfo(u'two', None)) 505 trackinfo.append(TrackInfo(u'three', None)) 506 mapping, extra_items, extra_tracks = \ 507 match.assign_items(items, trackinfo) 508 self.assertEqual(extra_items, []) 509 self.assertEqual(extra_tracks, []) 510 self.assertEqual(mapping, { 511 items[0]: trackinfo[0], 512 items[1]: trackinfo[2], 513 items[2]: trackinfo[1], 514 }) 515 516 def test_order_works_with_invalid_track_numbers(self): 517 items = [] 518 items.append(self.item(u'one', 1)) 519 items.append(self.item(u'three', 1)) 520 items.append(self.item(u'two', 1)) 521 trackinfo = [] 522 trackinfo.append(TrackInfo(u'one', None)) 523 trackinfo.append(TrackInfo(u'two', None)) 524 trackinfo.append(TrackInfo(u'three', None)) 525 mapping, extra_items, extra_tracks = \ 526 match.assign_items(items, trackinfo) 527 self.assertEqual(extra_items, []) 528 self.assertEqual(extra_tracks, []) 529 self.assertEqual(mapping, { 530 items[0]: trackinfo[0], 531 items[1]: trackinfo[2], 532 items[2]: trackinfo[1], 533 }) 534 535 def test_order_works_with_missing_tracks(self): 536 items = [] 537 items.append(self.item(u'one', 1)) 538 items.append(self.item(u'three', 3)) 539 trackinfo = [] 540 trackinfo.append(TrackInfo(u'one', None)) 541 trackinfo.append(TrackInfo(u'two', None)) 542 trackinfo.append(TrackInfo(u'three', None)) 543 mapping, extra_items, extra_tracks = \ 544 match.assign_items(items, trackinfo) 545 self.assertEqual(extra_items, []) 546 self.assertEqual(extra_tracks, [trackinfo[1]]) 547 self.assertEqual(mapping, { 548 items[0]: trackinfo[0], 549 items[1]: trackinfo[2], 550 }) 551 552 def test_order_works_with_extra_tracks(self): 553 items = [] 554 items.append(self.item(u'one', 1)) 555 items.append(self.item(u'two', 2)) 556 items.append(self.item(u'three', 3)) 557 trackinfo = [] 558 trackinfo.append(TrackInfo(u'one', None)) 559 trackinfo.append(TrackInfo(u'three', None)) 560 mapping, extra_items, extra_tracks = \ 561 match.assign_items(items, trackinfo) 562 self.assertEqual(extra_items, [items[1]]) 563 self.assertEqual(extra_tracks, []) 564 self.assertEqual(mapping, { 565 items[0]: trackinfo[0], 566 items[2]: trackinfo[1], 567 }) 568 569 def test_order_works_when_track_names_are_entirely_wrong(self): 570 # A real-world test case contributed by a user. 571 def item(i, length): 572 return Item( 573 artist=u'ben harper', 574 album=u'burn to shine', 575 title=u'ben harper - Burn to Shine {0}'.format(i), 576 track=i, 577 length=length, 578 mb_trackid='', mb_albumid='', mb_artistid='', 579 ) 580 items = [] 581 items.append(item(1, 241.37243007106997)) 582 items.append(item(2, 342.27781704375036)) 583 items.append(item(3, 245.95070222338137)) 584 items.append(item(4, 472.87662515485437)) 585 items.append(item(5, 279.1759535763187)) 586 items.append(item(6, 270.33333768012)) 587 items.append(item(7, 247.83435613222923)) 588 items.append(item(8, 216.54504531525072)) 589 items.append(item(9, 225.72775379800484)) 590 items.append(item(10, 317.7643606963552)) 591 items.append(item(11, 243.57001238834192)) 592 items.append(item(12, 186.45916150485752)) 593 594 def info(index, title, length): 595 return TrackInfo(title, None, length=length, index=index) 596 trackinfo = [] 597 trackinfo.append(info(1, u'Alone', 238.893)) 598 trackinfo.append(info(2, u'The Woman in You', 341.44)) 599 trackinfo.append(info(3, u'Less', 245.59999999999999)) 600 trackinfo.append(info(4, u'Two Hands of a Prayer', 470.49299999999999)) 601 trackinfo.append(info(5, u'Please Bleed', 277.86599999999999)) 602 trackinfo.append(info(6, u'Suzie Blue', 269.30599999999998)) 603 trackinfo.append(info(7, u'Steal My Kisses', 245.36000000000001)) 604 trackinfo.append(info(8, u'Burn to Shine', 214.90600000000001)) 605 trackinfo.append(info(9, u'Show Me a Little Shame', 224.0929999999999)) 606 trackinfo.append(info(10, u'Forgiven', 317.19999999999999)) 607 trackinfo.append(info(11, u'Beloved One', 243.733)) 608 trackinfo.append(info(12, u'In the Lord\'s Arms', 186.13300000000001)) 609 610 mapping, extra_items, extra_tracks = \ 611 match.assign_items(items, trackinfo) 612 self.assertEqual(extra_items, []) 613 self.assertEqual(extra_tracks, []) 614 for item, info in mapping.items(): 615 self.assertEqual(items.index(item), trackinfo.index(info)) 616 617 618class ApplyTestUtil(object): 619 def _apply(self, info=None, per_disc_numbering=False, artist_credit=False): 620 info = info or self.info 621 mapping = {} 622 for i, t in zip(self.items, info.tracks): 623 mapping[i] = t 624 config['per_disc_numbering'] = per_disc_numbering 625 config['artist_credit'] = artist_credit 626 autotag.apply_metadata(info, mapping) 627 628 629class ApplyTest(_common.TestCase, ApplyTestUtil): 630 def setUp(self): 631 super(ApplyTest, self).setUp() 632 633 self.items = [] 634 self.items.append(Item({})) 635 self.items.append(Item({})) 636 trackinfo = [] 637 trackinfo.append(TrackInfo( 638 u'oneNew', 639 u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', 640 medium=1, 641 medium_index=1, 642 medium_total=1, 643 index=1, 644 artist_credit='trackArtistCredit', 645 artist_sort='trackArtistSort', 646 )) 647 trackinfo.append(TrackInfo( 648 u'twoNew', 649 u'40130ed1-a27c-42fd-a328-1ebefb6caef4', 650 medium=2, 651 medium_index=1, 652 index=2, 653 medium_total=1, 654 )) 655 self.info = AlbumInfo( 656 tracks=trackinfo, 657 artist=u'artistNew', 658 album=u'albumNew', 659 album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', 660 artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', 661 artist_credit=u'albumArtistCredit', 662 artist_sort=u'albumArtistSort', 663 albumtype=u'album', 664 va=False, 665 mediums=2, 666 ) 667 668 def test_titles_applied(self): 669 self._apply() 670 self.assertEqual(self.items[0].title, 'oneNew') 671 self.assertEqual(self.items[1].title, 'twoNew') 672 673 def test_album_and_artist_applied_to_all(self): 674 self._apply() 675 self.assertEqual(self.items[0].album, 'albumNew') 676 self.assertEqual(self.items[1].album, 'albumNew') 677 self.assertEqual(self.items[0].artist, 'artistNew') 678 self.assertEqual(self.items[1].artist, 'artistNew') 679 680 def test_track_index_applied(self): 681 self._apply() 682 self.assertEqual(self.items[0].track, 1) 683 self.assertEqual(self.items[1].track, 2) 684 685 def test_track_total_applied(self): 686 self._apply() 687 self.assertEqual(self.items[0].tracktotal, 2) 688 self.assertEqual(self.items[1].tracktotal, 2) 689 690 def test_disc_index_applied(self): 691 self._apply() 692 self.assertEqual(self.items[0].disc, 1) 693 self.assertEqual(self.items[1].disc, 2) 694 695 def test_disc_total_applied(self): 696 self._apply() 697 self.assertEqual(self.items[0].disctotal, 2) 698 self.assertEqual(self.items[1].disctotal, 2) 699 700 def test_per_disc_numbering(self): 701 self._apply(per_disc_numbering=True) 702 self.assertEqual(self.items[0].track, 1) 703 self.assertEqual(self.items[1].track, 1) 704 705 def test_per_disc_numbering_track_total(self): 706 self._apply(per_disc_numbering=True) 707 self.assertEqual(self.items[0].tracktotal, 1) 708 self.assertEqual(self.items[1].tracktotal, 1) 709 710 def test_artist_credit(self): 711 self._apply(artist_credit=True) 712 self.assertEqual(self.items[0].artist, 'trackArtistCredit') 713 self.assertEqual(self.items[1].artist, 'albumArtistCredit') 714 self.assertEqual(self.items[0].albumartist, 'albumArtistCredit') 715 self.assertEqual(self.items[1].albumartist, 'albumArtistCredit') 716 717 def test_artist_credit_prefers_artist_over_albumartist_credit(self): 718 self.info.tracks[0].artist = 'oldArtist' 719 self.info.tracks[0].artist_credit = None 720 self._apply(artist_credit=True) 721 self.assertEqual(self.items[0].artist, 'oldArtist') 722 723 def test_artist_credit_falls_back_to_albumartist(self): 724 self.info.artist_credit = None 725 self._apply(artist_credit=True) 726 self.assertEqual(self.items[1].artist, 'artistNew') 727 728 def test_mb_trackid_applied(self): 729 self._apply() 730 self.assertEqual(self.items[0].mb_trackid, 731 'dfa939ec-118c-4d0f-84a0-60f3d1e6522c') 732 self.assertEqual(self.items[1].mb_trackid, 733 '40130ed1-a27c-42fd-a328-1ebefb6caef4') 734 735 def test_mb_albumid_and_artistid_applied(self): 736 self._apply() 737 for item in self.items: 738 self.assertEqual(item.mb_albumid, 739 '7edb51cb-77d6-4416-a23c-3a8c2994a2c7') 740 self.assertEqual(item.mb_artistid, 741 'a6623d39-2d8e-4f70-8242-0a9553b91e50') 742 743 def test_albumtype_applied(self): 744 self._apply() 745 self.assertEqual(self.items[0].albumtype, 'album') 746 self.assertEqual(self.items[1].albumtype, 'album') 747 748 def test_album_artist_overrides_empty_track_artist(self): 749 my_info = copy.deepcopy(self.info) 750 self._apply(info=my_info) 751 self.assertEqual(self.items[0].artist, 'artistNew') 752 self.assertEqual(self.items[1].artist, 'artistNew') 753 754 def test_album_artist_overridden_by_nonempty_track_artist(self): 755 my_info = copy.deepcopy(self.info) 756 my_info.tracks[0].artist = 'artist1!' 757 my_info.tracks[1].artist = 'artist2!' 758 self._apply(info=my_info) 759 self.assertEqual(self.items[0].artist, 'artist1!') 760 self.assertEqual(self.items[1].artist, 'artist2!') 761 762 def test_artist_credit_applied(self): 763 self._apply() 764 self.assertEqual(self.items[0].albumartist_credit, 'albumArtistCredit') 765 self.assertEqual(self.items[0].artist_credit, 'trackArtistCredit') 766 self.assertEqual(self.items[1].albumartist_credit, 'albumArtistCredit') 767 self.assertEqual(self.items[1].artist_credit, 'albumArtistCredit') 768 769 def test_artist_sort_applied(self): 770 self._apply() 771 self.assertEqual(self.items[0].albumartist_sort, 'albumArtistSort') 772 self.assertEqual(self.items[0].artist_sort, 'trackArtistSort') 773 self.assertEqual(self.items[1].albumartist_sort, 'albumArtistSort') 774 self.assertEqual(self.items[1].artist_sort, 'albumArtistSort') 775 776 def test_full_date_applied(self): 777 my_info = copy.deepcopy(self.info) 778 my_info.year = 2013 779 my_info.month = 12 780 my_info.day = 18 781 self._apply(info=my_info) 782 783 self.assertEqual(self.items[0].year, 2013) 784 self.assertEqual(self.items[0].month, 12) 785 self.assertEqual(self.items[0].day, 18) 786 787 def test_date_only_zeros_month_and_day(self): 788 self.items = [] 789 self.items.append(Item(year=1, month=2, day=3)) 790 self.items.append(Item(year=4, month=5, day=6)) 791 792 my_info = copy.deepcopy(self.info) 793 my_info.year = 2013 794 self._apply(info=my_info) 795 796 self.assertEqual(self.items[0].year, 2013) 797 self.assertEqual(self.items[0].month, 0) 798 self.assertEqual(self.items[0].day, 0) 799 800 def test_missing_date_applies_nothing(self): 801 self.items = [] 802 self.items.append(Item(year=1, month=2, day=3)) 803 self.items.append(Item(year=4, month=5, day=6)) 804 805 self._apply() 806 807 self.assertEqual(self.items[0].year, 1) 808 self.assertEqual(self.items[0].month, 2) 809 self.assertEqual(self.items[0].day, 3) 810 811 def test_data_source_applied(self): 812 my_info = copy.deepcopy(self.info) 813 my_info.data_source = 'MusicBrainz' 814 self._apply(info=my_info) 815 816 self.assertEqual(self.items[0].data_source, 'MusicBrainz') 817 818 819class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): 820 def setUp(self): 821 super(ApplyCompilationTest, self).setUp() 822 823 self.items = [] 824 self.items.append(Item({})) 825 self.items.append(Item({})) 826 trackinfo = [] 827 trackinfo.append(TrackInfo( 828 u'oneNew', 829 u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', 830 artist=u'artistOneNew', 831 artist_id=u'a05686fc-9db2-4c23-b99e-77f5db3e5282', 832 index=1, 833 )) 834 trackinfo.append(TrackInfo( 835 u'twoNew', 836 u'40130ed1-a27c-42fd-a328-1ebefb6caef4', 837 artist=u'artistTwoNew', 838 artist_id=u'80b3cf5e-18fe-4c59-98c7-e5bb87210710', 839 index=2, 840 )) 841 self.info = AlbumInfo( 842 tracks=trackinfo, 843 artist=u'variousNew', 844 album=u'albumNew', 845 album_id='3b69ea40-39b8-487f-8818-04b6eff8c21a', 846 artist_id='89ad4ac3-39f7-470e-963a-56509c546377', 847 albumtype=u'compilation', 848 ) 849 850 def test_album_and_track_artists_separate(self): 851 self._apply() 852 self.assertEqual(self.items[0].artist, 'artistOneNew') 853 self.assertEqual(self.items[1].artist, 'artistTwoNew') 854 self.assertEqual(self.items[0].albumartist, 'variousNew') 855 self.assertEqual(self.items[1].albumartist, 'variousNew') 856 857 def test_mb_albumartistid_applied(self): 858 self._apply() 859 self.assertEqual(self.items[0].mb_albumartistid, 860 '89ad4ac3-39f7-470e-963a-56509c546377') 861 self.assertEqual(self.items[1].mb_albumartistid, 862 '89ad4ac3-39f7-470e-963a-56509c546377') 863 self.assertEqual(self.items[0].mb_artistid, 864 'a05686fc-9db2-4c23-b99e-77f5db3e5282') 865 self.assertEqual(self.items[1].mb_artistid, 866 '80b3cf5e-18fe-4c59-98c7-e5bb87210710') 867 868 def test_va_flag_cleared_does_not_set_comp(self): 869 self._apply() 870 self.assertFalse(self.items[0].comp) 871 self.assertFalse(self.items[1].comp) 872 873 def test_va_flag_sets_comp(self): 874 va_info = copy.deepcopy(self.info) 875 va_info.va = True 876 self._apply(info=va_info) 877 self.assertTrue(self.items[0].comp) 878 self.assertTrue(self.items[1].comp) 879 880 881class StringDistanceTest(unittest.TestCase): 882 def test_equal_strings(self): 883 dist = string_dist(u'Some String', u'Some String') 884 self.assertEqual(dist, 0.0) 885 886 def test_different_strings(self): 887 dist = string_dist(u'Some String', u'Totally Different') 888 self.assertNotEqual(dist, 0.0) 889 890 def test_punctuation_ignored(self): 891 dist = string_dist(u'Some String', u'Some.String!') 892 self.assertEqual(dist, 0.0) 893 894 def test_case_ignored(self): 895 dist = string_dist(u'Some String', u'sOME sTring') 896 self.assertEqual(dist, 0.0) 897 898 def test_leading_the_has_lower_weight(self): 899 dist1 = string_dist(u'XXX Band Name', u'Band Name') 900 dist2 = string_dist(u'The Band Name', u'Band Name') 901 self.assertTrue(dist2 < dist1) 902 903 def test_parens_have_lower_weight(self): 904 dist1 = string_dist(u'One .Two.', u'One') 905 dist2 = string_dist(u'One (Two)', u'One') 906 self.assertTrue(dist2 < dist1) 907 908 def test_brackets_have_lower_weight(self): 909 dist1 = string_dist(u'One .Two.', u'One') 910 dist2 = string_dist(u'One [Two]', u'One') 911 self.assertTrue(dist2 < dist1) 912 913 def test_ep_label_has_zero_weight(self): 914 dist = string_dist(u'My Song (EP)', u'My Song') 915 self.assertEqual(dist, 0.0) 916 917 def test_featured_has_lower_weight(self): 918 dist1 = string_dist(u'My Song blah Someone', u'My Song') 919 dist2 = string_dist(u'My Song feat Someone', u'My Song') 920 self.assertTrue(dist2 < dist1) 921 922 def test_postfix_the(self): 923 dist = string_dist(u'The Song Title', u'Song Title, The') 924 self.assertEqual(dist, 0.0) 925 926 def test_postfix_a(self): 927 dist = string_dist(u'A Song Title', u'Song Title, A') 928 self.assertEqual(dist, 0.0) 929 930 def test_postfix_an(self): 931 dist = string_dist(u'An Album Title', u'Album Title, An') 932 self.assertEqual(dist, 0.0) 933 934 def test_empty_strings(self): 935 dist = string_dist(u'', u'') 936 self.assertEqual(dist, 0.0) 937 938 def test_solo_pattern(self): 939 # Just make sure these don't crash. 940 string_dist(u'The ', u'') 941 string_dist(u'(EP)', u'(EP)') 942 string_dist(u', An', u'') 943 944 def test_heuristic_does_not_harm_distance(self): 945 dist = string_dist(u'Untitled', u'[Untitled]') 946 self.assertEqual(dist, 0.0) 947 948 def test_ampersand_expansion(self): 949 dist = string_dist(u'And', u'&') 950 self.assertEqual(dist, 0.0) 951 952 def test_accented_characters(self): 953 dist = string_dist(u'\xe9\xe1\xf1', u'ean') 954 self.assertEqual(dist, 0.0) 955 956 957class EnumTest(_common.TestCase): 958 """ 959 Test Enum Subclasses defined in beets.util.enumeration 960 """ 961 def test_ordered_enum(self): 962 OrderedEnumClass = match.OrderedEnum('OrderedEnumTest', ['a', 'b', 'c']) # noqa 963 self.assertLess(OrderedEnumClass.a, OrderedEnumClass.b) 964 self.assertLess(OrderedEnumClass.a, OrderedEnumClass.c) 965 self.assertLess(OrderedEnumClass.b, OrderedEnumClass.c) 966 self.assertGreater(OrderedEnumClass.b, OrderedEnumClass.a) 967 self.assertGreater(OrderedEnumClass.c, OrderedEnumClass.a) 968 self.assertGreater(OrderedEnumClass.c, OrderedEnumClass.b) 969 970 971def suite(): 972 return unittest.TestLoader().loadTestsFromName(__name__) 973 974if __name__ == '__main__': 975 unittest.main(defaultTest='suite') 976