diff --git a/test/formats/common.py b/test/formats/common.py index 6d07e2f2b..65e1ec4fe 100644 --- a/test/formats/common.py +++ b/test/formats/common.py @@ -12,6 +12,7 @@ import picard.formats from picard.formats import ext_to_format from picard.metadata import Metadata + settings = { 'clear_existing_tags': False, 'embed_only_one_front_image': False, @@ -141,12 +142,10 @@ def skipUnlessTestfile(func): # prevent unittest to run tests in those classes class CommonTests: - class SimpleFormatsTest(PicardTestCase): - + class BaseFileTest(PicardTestCase): testfile = None testfile_ext = None testfile_path = None - expected_info = None def setUp(self): super().setUp() @@ -167,6 +166,10 @@ class CommonTests: shutil.copy(filename, copy) return copy + class SimpleFormatsTest(BaseFileTest): + + expected_info = None + @skipUnlessTestfile def test_can_open_and_save(self): metadata = Metadata() diff --git a/test/formats/coverart.py b/test/formats/coverart.py new file mode 100644 index 000000000..c0b0af151 --- /dev/null +++ b/test/formats/coverart.py @@ -0,0 +1,143 @@ +import os.path + +from PyQt5 import QtCore + +from picard import config +from picard.coverart.image import ( + CoverArtImage, + TagCoverArtImage, +) +import picard.formats +from picard.metadata import Metadata +from .common import ( + CommonTests, + skipUnlessTestfile, +) + + +def file_save_image(filename, image): + f = picard.formats.open_(filename) + metadata = Metadata() + metadata.images.append(image) + f._save(filename, metadata) + + +def load_coverart_file(filename): + with open(os.path.join('test', 'data', filename), 'rb') as f: + return f.read() + + +# prevent unittest to run tests in those classes +class CommonCoverArtTests: + + class CoverArtTest(CommonTests.BaseFileTest): + + supports_types = True + + def setUp(self): + super().setUp() + self.jpegdata = load_coverart_file('mb.jpg') + self.pngdata = load_coverart_file('mb.png') + self.addCleanup(QtCore.QObject.tagger.run_cleanup) + + @skipUnlessTestfile + def test_cover_art(self): + source_types = ["front", "booklet"] + # Use reasonable large data > 64kb. + # This checks a mutagen error with ASF files. + tests = [ + CoverArtImage(data=self.jpegdata + b"a" * 1024 * 128, types=source_types), + CoverArtImage(data=self.pngdata + b"a" * 1024 * 128, types=source_types), + ] + for test in tests: + file_save_image(self.filename, test) + f = picard.formats.open_(self.filename) + loaded_metadata = f._load(self.filename) + image = loaded_metadata.images[0] + self.assertEqual(test.mimetype, image.mimetype) + self.assertEqual(test, image) + + def test_cover_art_with_types(self): + expected = set('abcdefg'[:]) if self.supports_types else set('a') + f = picard.formats.open_(self.filename) + f._save(self.filename, self._cover_metadata()) + + f = picard.formats.open_(self.filename) + loaded_metadata = f._load(self.filename) + found = set([chr(img.data[-1]) for img in loaded_metadata.images]) + self.assertEqual(expected, found) + + @skipUnlessTestfile + def test_cover_art_types_only_one_front(self): + config.setting['embed_only_one_front_image'] = True + f = picard.formats.open_(self.filename) + f._save(self.filename, self._cover_metadata()) + + f = picard.formats.open_(self.filename) + loaded_metadata = f._load(self.filename) + self.assertEqual(1, len(loaded_metadata.images)) + self.assertEqual(ord('a'), loaded_metadata.images[0].data[-1]) + + def _cover_metadata(self): + imgdata = self.jpegdata + metadata = Metadata() + metadata.images.append( + TagCoverArtImage( + file='a', + tag='a', + data=imgdata + b'a', + support_types=True, + types=[u'booklet', u'front'], + ) + ) + metadata.images.append( + TagCoverArtImage( + file='b', + tag='b', + data=imgdata + b'b', + support_types=True, + types=[u'back'], + ) + ) + metadata.images.append( + TagCoverArtImage( + file='c', + tag='c', + data=imgdata + b'c', + support_types=True, + types=[u'front'], + ) + ) + metadata.images.append( + TagCoverArtImage( + file='d', + tag='d', + data=imgdata + b'd', + ) + ) + metadata.images.append( + TagCoverArtImage( + file='e', + tag='e', + data=imgdata + b'e', + is_front=False + ) + ) + metadata.images.append( + TagCoverArtImage( + file='f', + tag='f', + data=imgdata + b'f', + types=[u'front'] + ) + ) + metadata.images.append( + TagCoverArtImage( + file='g', + tag='g', + data=imgdata + b'g', + types=[u'back'], + is_front=True + ) + ) + return metadata diff --git a/test/formats/test_apev2.py b/test/formats/test_apev2.py index 075c91fb7..ca656dfb3 100644 --- a/test/formats/test_apev2.py +++ b/test/formats/test_apev2.py @@ -2,6 +2,7 @@ from .common import ( CommonTests, load_metadata, ) +from .coverart import CommonCoverArtTests class MonkeysAudioTest(CommonTests.TagFormatsTest): @@ -76,3 +77,8 @@ class OptimFROGDUalStreamTest(CommonTests.TagFormatsTest): def test_format(self): metadata = load_metadata(self.filename) self.assertEqual(metadata['~format'], 'OptimFROG DualStream Audio') + + +class ApeCoverArtTest(CommonCoverArtTests.CoverArtTest): + testfile = 'test.ape' + supports_types = False diff --git a/test/formats/test_asf.py b/test/formats/test_asf.py index 6962d6e6f..4a953bc0b 100644 --- a/test/formats/test_asf.py +++ b/test/formats/test_asf.py @@ -1,4 +1,5 @@ from .common import CommonTests +from .coverart import CommonCoverArtTests class ASFTest(CommonTests.TagFormatsTest): @@ -21,3 +22,11 @@ class WMATest(CommonTests.TagFormatsTest): '~sample_rate': '44100', '~bitrate': '64.0', } + + +class AsfCoverArtTest(CommonCoverArtTests.CoverArtTest): + testfile = 'test.asf' + + +class WmaCoverArtTest(CommonCoverArtTests.CoverArtTest): + testfile = 'test.wma' diff --git a/test/formats/test_coverart.py b/test/formats/test_coverart.py deleted file mode 100644 index c65450b8f..000000000 --- a/test/formats/test_coverart.py +++ /dev/null @@ -1,315 +0,0 @@ -import os.path -import shutil -from tempfile import mkstemp -from test.picardtestcase import PicardTestCase - -from PyQt5 import QtCore - -from picard import ( - config, - # log, -) -from picard.coverart.image import ( - CoverArtImage, - TagCoverArtImage, -) -import picard.formats -from picard.metadata import Metadata -from .common import ( - load_raw, - settings, -) - - -class TestCoverArt(PicardTestCase): - - def setUp(self): - super().setUp() - with open(os.path.join('test', 'data', 'mb.jpg'), 'rb') as f: - self.jpegdata = f.read() - with open(os.path.join('test', 'data', 'mb.png'), 'rb') as f: - self.pngdata = f.read() - - def _common_set_up(self, extra=None): - config.setting = settings.copy() - if extra is not None: - config.setting.update(extra) - - def _set_up(self, original, extra=None): - fd, self.filename = mkstemp(suffix=os.path.splitext(original)[1]) - os.close(fd) - shutil.copy(original, self.filename) - self._common_set_up(extra) - - def _common_tear_down(self): - QtCore.QObject.tagger.run_cleanup() - - def _tear_down(self): - os.unlink(self.filename) - self._common_tear_down() - - def test_coverartimage(self): - tests = { - 'jpg': { - 'mime': 'image/jpeg', - 'data': self.jpegdata - }, - 'png': { - 'mime': 'image/png', - 'data': self.pngdata - }, - } - tmp_files = [] - for t in tests: - imgdata = tests[t]['data'] - imgdata2 = imgdata + b'xxx' - # set data once - coverartimage = CoverArtImage( - data=imgdata2 - ) - tmp_file = coverartimage.tempfile_filename - tmp_files.append(tmp_file) - filesize = os.path.getsize(tmp_file) - # ensure file was written, and check its length - self.assertEqual(filesize, len(imgdata2)) - self.assertEqual(coverartimage.data, imgdata2) - - # set data again, with another payload - coverartimage.set_data(imgdata) - - tmp_file = coverartimage.tempfile_filename - tmp_files.append(tmp_file) - filesize = os.path.getsize(tmp_file) - # check file length again - self.assertEqual(filesize, len(imgdata)) - self.assertEqual(coverartimage.data, imgdata) - - QtCore.QObject.tagger.run_cleanup() - - def test_asf(self): - self._test_cover_art(os.path.join('test', 'data', 'test.wma')) - - def test_ape(self): - self._test_cover_art(os.path.join('test', 'data', 'test.wv')) - - def test_mp3(self): - self._test_cover_art(os.path.join('test', 'data', 'test.mp3')) - - def test_mp4(self): - self._test_cover_art(os.path.join('test', 'data', 'test.m4a')) - - def test_ogg(self): - self._test_cover_art(os.path.join('test', 'data', 'test.ogg')) - - def test_flac(self): - self._test_cover_art(os.path.join('test', 'data', 'test.flac')) - - # test for multiple images added to files, some types don't accept more than - # one, and there is no guarantee that order is preserved - def test_asf_types(self): - self._test_cover_art_types(os.path.join('test', 'data', 'test.wma'), - set('abcdefg'[:])) - - def test_ape_types(self): - self._test_cover_art_types(os.path.join('test', 'data', 'test.wv'), - set('a')) - - def test_mp3_types(self): - self._test_cover_art_types(os.path.join('test', 'data', 'test.mp3'), - set('abcdefg'[:])) - - def test_mp4_types(self): - self._test_cover_art_types(os.path.join('test', 'data', 'test.m4a'), - set('abcdefg'[:])) - - def test_ogg_types(self): - self._test_cover_art_types(os.path.join('test', 'data', 'test.ogg'), - set('abcdefg'[:])) - - def test_flac_types(self): - self._test_cover_art_types(os.path.join('test', 'data', 'test.flac'), - set('abcdefg'[:])) - - def test_asf_types_only_front(self): - self._test_cover_art_types_only_front( - os.path.join('test', 'data', 'test.wma'), - set('a')) - - def test_ape_types_only_front(self): - self._test_cover_art_types_only_front( - os.path.join('test', 'data', 'test.wv'), - set('a')) - - def test_mp3_types_only_front(self): - self._test_cover_art_types_only_front( - os.path.join('test', 'data', 'test.mp3'), - set('a')) - - def test_mp4_types_only_front(self): - self._test_cover_art_types_only_front( - os.path.join('test', 'data', 'test.m4a'), - set('a')) - - def test_ogg_types_only_front(self): - self._test_cover_art_types_only_front( - os.path.join('test', 'data', 'test.ogg'), - set('a')) - - def test_flac_types_only_front(self): - self._test_cover_art_types_only_front( - os.path.join('test', 'data', 'test.flac'), - set('a')) - - def test_flac_set_picture_dimensions(self): - self._set_up(os.path.join('test', 'data', 'test.flac')) - try: - tests = [ - CoverArtImage(data=self.jpegdata), - CoverArtImage(data=self.pngdata), - ] - for test in tests: - self._file_save_image(self.filename, test) - raw_metadata = load_raw(self.filename) - pic = raw_metadata.pictures[0] - self.assertNotEqual(pic.width, 0) - self.assertEqual(pic.width, test.width) - self.assertNotEqual(pic.height, 0) - self.assertEqual(pic.height, test.height) - finally: - self._tear_down() - - def _test_cover_art(self, filename): - self._set_up(filename) - try: - source_types = ["front", "booklet"] - # Use reasonable large data > 64kb. - # This checks a mutagen error with ASF files. - tests = [ - CoverArtImage(data=self.jpegdata + b"a" * 1024 * 128, types=source_types), - CoverArtImage(data=self.pngdata + b"a" * 1024 * 128, types=source_types), - ] - for test in tests: - self._file_save_image(self.filename, test) - f = picard.formats.open_(self.filename) - loaded_metadata = f._load(self.filename) - image = loaded_metadata.images[0] - self.assertEqual(test.mimetype, image.mimetype) - self.assertEqual(test, image) - finally: - self._tear_down() - - @staticmethod - def _file_save_image(filename, image): - f = picard.formats.open_(filename) - metadata = Metadata() - metadata.images.append(image) - f._save(filename, metadata) - - def _cover_metadata(self): - imgdata = self.jpegdata - metadata = Metadata() - metadata.images.append( - TagCoverArtImage( - file='a', - tag='a', - data=imgdata + b'a', - support_types=True, - types=[u'booklet', u'front'], - ) - ) - metadata.images.append( - TagCoverArtImage( - file='b', - tag='b', - data=imgdata + b'b', - support_types=True, - types=[u'back'], - ) - ) - metadata.images.append( - TagCoverArtImage( - file='c', - tag='c', - data=imgdata + b'c', - support_types=True, - types=[u'front'], - ) - ) - metadata.images.append( - TagCoverArtImage( - file='d', - tag='d', - data=imgdata + b'd', - ) - ) - metadata.images.append( - TagCoverArtImage( - file='e', - tag='e', - data=imgdata + b'e', - is_front=False - ) - ) - metadata.images.append( - TagCoverArtImage( - file='f', - tag='f', - data=imgdata + b'f', - types=[u'front'] - ) - ) - metadata.images.append( - TagCoverArtImage( - file='g', - tag='g', - data=imgdata + b'g', - types=[u'back'], - is_front=True - ) - ) - return metadata - - def test_is_front_image(self): - self._common_set_up() - try: - m = self._cover_metadata() - front_images = set('acdfg'[:]) - found = set() - for img in m.images: - if img.is_front_image(): - found.add(img.tag) - self.assertEqual(front_images, found) - finally: - self._common_tear_down() - - def _test_cover_art_types(self, filename, expect): - self._set_up(filename) - expect = {ord(char) for char in expect} - try: - f = picard.formats.open_(self.filename) - f._save(self.filename, self._cover_metadata()) - - f = picard.formats.open_(self.filename) - loaded_metadata = f._load(self.filename) - found = set() - for n, image in enumerate(loaded_metadata.images): - found.add(image.data[-1]) - self.assertEqual(expect, found) - finally: - self._tear_down() - - def _test_cover_art_types_only_front(self, filename, expect): - self._set_up(filename, {'embed_only_one_front_image': True}) - expect = {ord(char) for char in expect} - try: - f = picard.formats.open_(self.filename) - f._save(self.filename, self._cover_metadata()) - - f = picard.formats.open_(self.filename) - loaded_metadata = f._load(self.filename) - found = set() - for n, image in enumerate(loaded_metadata.images): - found.add(image.data[-1]) - self.assertEqual(expect, found) - finally: - self._tear_down() diff --git a/test/formats/test_id3.py b/test/formats/test_id3.py index 648867543..74f70b01c 100644 --- a/test/formats/test_id3.py +++ b/test/formats/test_id3.py @@ -12,6 +12,7 @@ from .common import ( save_metadata, skipUnlessTestfile, ) +from .coverart import CommonCoverArtTests # prevent unittest to run tests in those classes @@ -229,3 +230,7 @@ class AIFFTest(CommonId3Tests.Id3Test): '~sample_rate': '44100', '~bitrate': '1411.2', } + + +class Mp3CoverArtTest(CommonCoverArtTests.CoverArtTest): + testfile = 'test.mp3' diff --git a/test/formats/test_mp4.py b/test/formats/test_mp4.py index e21e2408e..e12fc4ef2 100644 --- a/test/formats/test_mp4.py +++ b/test/formats/test_mp4.py @@ -3,6 +3,7 @@ from .common import ( CommonTests, load_metadata, ) +from .coverart import CommonCoverArtTests class MP4Test(CommonTests.TagFormatsTest): @@ -29,3 +30,7 @@ class MP4Test(CommonTests.TagFormatsTest): def test_format(self): metadata = load_metadata(self.filename) self.assertIn('AAC LC', metadata['~format']) + + +class Mp4CoverArtTest(CommonCoverArtTests.CoverArtTest): + testfile = 'test.m4a' diff --git a/test/formats/test_vorbis.py b/test/formats/test_vorbis.py index d72da77f1..c33fac552 100644 --- a/test/formats/test_vorbis.py +++ b/test/formats/test_vorbis.py @@ -6,13 +6,19 @@ from picard import ( config, log, ) +from picard.coverart.image import CoverArtImage from picard.formats import vorbis from .common import ( CommonTests, load_metadata, + load_raw, save_and_load_metadata, skipUnlessTestfile, ) +from .coverart import ( + CommonCoverArtTests, + file_save_image, +) # prevent unittest to run tests in those classes @@ -81,7 +87,7 @@ class VorbisUtilTest(PicardTestCase): self.assertEqual(sanitized, ' }') -class FlacCoverArtTest(CommonCoverArtTests.CoverArtTestCase): +class FlacCoverArtTest(CommonCoverArtTests.CoverArtTest): testfile = 'test.flac' def test_set_picture_dimensions(self): @@ -99,5 +105,5 @@ class FlacCoverArtTest(CommonCoverArtTests.CoverArtTestCase): self.assertEqual(pic.height, test.height) -class OggCoverArtTest(CommonCoverArtTests.CoverArtTestCase): +class OggCoverArtTest(CommonCoverArtTests.CoverArtTest): testfile = 'test.ogg' diff --git a/test/test_coverart_image.py b/test/test_coverart_image.py index 05fd0d44b..483eacda4 100644 --- a/test/test_coverart_image.py +++ b/test/test_coverart_image.py @@ -1,7 +1,10 @@ #!/usr/bin/env python # coding: utf-8 +import os.path import unittest +from PyQt5 import QtCore + from test.picardtestcase import ( PicardTestCase, create_fake_png, @@ -84,6 +87,27 @@ class CoverArtImageTest(PicardTestCase): self.assertEqual(image2, image3) self.assertNotEqual(image2, image4) + def test_set_data(self): + self.addCleanup(QtCore.QObject.tagger.run_cleanup) + imgdata = data=create_fake_png(b'a') + imgdata2 = data=create_fake_png(b'xxx') + # set data once + coverartimage = CoverArtImage(data=imgdata2) + tmp_file = coverartimage.tempfile_filename + filesize = os.path.getsize(tmp_file) + # ensure file was written, and check its length + self.assertEqual(filesize, len(imgdata2)) + self.assertEqual(coverartimage.data, imgdata2) + + # set data again, with another payload + coverartimage.set_data(imgdata) + + tmp_file = coverartimage.tempfile_filename + filesize = os.path.getsize(tmp_file) + # check file length again + self.assertEqual(filesize, len(imgdata)) + self.assertEqual(coverartimage.data, imgdata) + class LocalFileCoverArtImageTest(PicardTestCase): def test_set_file_url(self):