From 4c2362b361ea17e1ace0767e40fa5fc64e0927d5 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 4 Jan 2024 04:00:09 +0100 Subject: [PATCH 1/4] tests: remove import path mangling while importing 'test._common' The import path mangling is not relevant (anymore?) for the two ways of running tests: * `python3 test/testall.py` (see CONTRIBUTING.rst): The `testall.py` script already adds the project path to `sys.path`. * `tox -e py-cov`: this command is supposed to be run from the project path. Thus, the current directory is already the first of location in `sys.path`. The previous mangling of the import path while loading a module could lead to unwanted side-effects hidden in an unexpected location. Instead, import path mangling should take place in the script being called by the user (here: `testall.py`). --- test/_common.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/_common.py b/test/_common.py index 01839e96f..665c9098b 100644 --- a/test/_common.py +++ b/test/_common.py @@ -22,8 +22,6 @@ import time import unittest from contextlib import contextmanager -# Mangle the search path to include the beets sources. -sys.path.insert(0, "..") import beets # noqa: E402 import beets.library # noqa: E402 From 7707e23456d059e00512c93ca139634501214c49 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 4 Jan 2024 04:44:36 +0100 Subject: [PATCH 2/4] tests: move reusable components from `test.test_importer` to `test.helper` `ImportHelper` and `AutotagStub` are used in many tests. Thus, they should reside in a module which is obviously used by multiple tests. --- test/helper.py | 213 +++++++++++++++++++++++++++++- test/plugins/test_edit.py | 3 +- test/plugins/test_filefilter.py | 3 +- test/plugins/test_importadded.py | 2 +- test/plugins/test_mbsubmit.py | 9 +- test/test_importer.py | 216 +------------------------------ test/test_plugins.py | 2 +- test/test_ui_importer.py | 2 +- 8 files changed, 227 insertions(+), 223 deletions(-) diff --git a/test/helper.py b/test/helper.py index 6e73990c0..495b8c7f4 100644 --- a/test/helper.py +++ b/test/helper.py @@ -47,7 +47,7 @@ from mediafile import Image, MediaFile import beets import beets.plugins -from beets import config, importer, logging, util +from beets import autotag, config, importer, logging, util from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.library import Album, Item, Library from beets.util import MoveOperation, bytestring_path, syspath @@ -496,6 +496,115 @@ class TestHelper: return path +class ImportHelper(TestHelper): + """Provides tools to setup a library, a directory containing files that are + to be imported and an import session. The class also provides stubs for the + autotagging library and several assertions for the library. + """ + + def setup_beets(self, disk=False): + super().setup_beets(disk) + self.lib.path_formats = [ + ("default", os.path.join("$artist", "$album", "$title")), + ("singleton:true", os.path.join("singletons", "$title")), + ("comp:true", os.path.join("compilations", "$album", "$title")), + ] + + def _create_import_dir(self, count=3): + """Creates a directory with media files to import. + Sets ``self.import_dir`` to the path of the directory. Also sets + ``self.import_media`` to a list :class:`MediaFile` for all the files in + the directory. + + The directory has following layout + the_album/ + track_1.mp3 + track_2.mp3 + track_3.mp3 + + :param count: Number of files to create + """ + self.import_dir = os.path.join(self.temp_dir, b"testsrcdir") + if os.path.isdir(syspath(self.import_dir)): + shutil.rmtree(syspath(self.import_dir)) + + album_path = os.path.join(self.import_dir, b"the_album") + os.makedirs(syspath(album_path)) + + resource_path = os.path.join(_common.RSRC, b"full.mp3") + + metadata = { + "artist": "Tag Artist", + "album": "Tag Album", + "albumartist": None, + "mb_trackid": None, + "mb_albumid": None, + "comp": None, + } + self.media_files = [] + for i in range(count): + # Copy files + medium_path = os.path.join( + album_path, bytestring_path("track_%d.mp3" % (i + 1)) + ) + shutil.copy(syspath(resource_path), syspath(medium_path)) + medium = MediaFile(medium_path) + + # Set metadata + metadata["track"] = i + 1 + metadata["title"] = "Tag Title %d" % (i + 1) + for attr in metadata: + setattr(medium, attr, metadata[attr]) + medium.save() + self.media_files.append(medium) + self.import_media = self.media_files + + def _setup_import_session( + self, + import_dir=None, + delete=False, + threaded=False, + copy=True, + singletons=False, + move=False, + autotag=True, + link=False, + hardlink=False, + ): + config["import"]["copy"] = copy + config["import"]["delete"] = delete + config["import"]["timid"] = True + config["threaded"] = False + config["import"]["singletons"] = singletons + config["import"]["move"] = move + config["import"]["autotag"] = autotag + config["import"]["resume"] = False + config["import"]["link"] = link + config["import"]["hardlink"] = hardlink + + self.importer = ImportSessionFixture( + self.lib, + loghandler=None, + query=None, + paths=[import_dir or self.import_dir], + ) + + def assert_file_in_lib(self, *segments): + """Join the ``segments`` and assert that this path exists in the + library directory. + """ + self.assertExists(os.path.join(self.libdir, *segments)) + + def assert_file_not_in_lib(self, *segments): + """Join the ``segments`` and assert that this path does not + exist in the library directory. + """ + self.assertNotExists(os.path.join(self.libdir, *segments)) + + def assert_lib_dir_empty(self): + self.assertEqual(len(os.listdir(syspath(self.libdir))), 0) + + class ImportSessionFixture(importer.ImportSession): """ImportSession that can be controlled programaticaly. @@ -634,3 +743,105 @@ TRACK_INFO_FIELDS = [ "data_source", "data_url", ] + + +class AutotagStub: + """Stub out MusicBrainz album and track matcher and control what the + autotagger returns. + """ + + NONE = "NONE" + IDENT = "IDENT" + GOOD = "GOOD" + BAD = "BAD" + MISSING = "MISSING" + """Generate an album match for all but one track + """ + + length = 2 + matching = IDENT + + def install(self): + self.mb_match_album = autotag.mb.match_album + self.mb_match_track = autotag.mb.match_track + self.mb_album_for_id = autotag.mb.album_for_id + self.mb_track_for_id = autotag.mb.track_for_id + + autotag.mb.match_album = self.match_album + autotag.mb.match_track = self.match_track + autotag.mb.album_for_id = self.album_for_id + autotag.mb.track_for_id = self.track_for_id + + return self + + def restore(self): + autotag.mb.match_album = self.mb_match_album + autotag.mb.match_track = self.mb_match_track + autotag.mb.album_for_id = self.mb_album_for_id + autotag.mb.track_for_id = self.mb_track_for_id + + def match_album(self, albumartist, album, tracks, extra_tags): + if self.matching == self.IDENT: + yield self._make_album_match(albumartist, album, tracks) + + elif self.matching == self.GOOD: + for i in range(self.length): + yield self._make_album_match(albumartist, album, tracks, i) + + elif self.matching == self.BAD: + for i in range(self.length): + yield self._make_album_match(albumartist, album, tracks, i + 1) + + elif self.matching == self.MISSING: + yield self._make_album_match(albumartist, album, tracks, missing=1) + + def match_track(self, artist, title): + yield TrackInfo( + title=title.replace("Tag", "Applied"), + track_id="trackid", + artist=artist.replace("Tag", "Applied"), + artist_id="artistid", + length=1, + index=0, + ) + + def album_for_id(self, mbid): + return None + + def track_for_id(self, mbid): + return None + + def _make_track_match(self, artist, album, number): + return TrackInfo( + title="Applied Title %d" % number, + track_id="match %d" % number, + artist=artist, + length=1, + index=0, + ) + + def _make_album_match(self, artist, album, tracks, distance=0, missing=0): + if distance: + id = " " + "M" * distance + else: + id = "" + if artist is None: + artist = "Various Artists" + else: + artist = artist.replace("Tag", "Applied") + id + album = album.replace("Tag", "Applied") + id + + track_infos = [] + for i in range(tracks - missing): + track_infos.append(self._make_track_match(artist, album, i + 1)) + + return AlbumInfo( + artist=artist, + album=album, + tracks=track_infos, + va=False, + album_id="albumid" + id, + artist_id="artistid" + id, + albumtype="soundtrack", + data_source="match_source", + ) diff --git a/test/plugins/test_edit.py b/test/plugins/test_edit.py index 413db7cf6..cf9aadf7f 100644 --- a/test/plugins/test_edit.py +++ b/test/plugins/test_edit.py @@ -15,8 +15,7 @@ import codecs import unittest from test import _common -from test.helper import TestHelper, control_stdin -from test.test_importer import AutotagStub, ImportHelper +from test.helper import AutotagStub, ImportHelper, TestHelper, control_stdin from test.test_ui_importer import TerminalImportSessionSetup from unittest.mock import patch diff --git a/test/plugins/test_filefilter.py b/test/plugins/test_filefilter.py index e5c0f615a..989436379 100644 --- a/test/plugins/test_filefilter.py +++ b/test/plugins/test_filefilter.py @@ -20,8 +20,7 @@ import os import shutil import unittest from test import _common -from test.helper import capture_log -from test.test_importer import ImportHelper +from test.helper import ImportHelper, capture_log from mediafile import MediaFile diff --git a/test/plugins/test_importadded.py b/test/plugins/test_importadded.py index 35292355a..3aa0152ee 100644 --- a/test/plugins/test_importadded.py +++ b/test/plugins/test_importadded.py @@ -17,7 +17,7 @@ import os import unittest -from test.test_importer import AutotagStub, ImportHelper +from test.helper import AutotagStub, ImportHelper from beets import importer from beets.util import displayable_path, syspath diff --git a/test/plugins/test_mbsubmit.py b/test/plugins/test_mbsubmit.py index e495a73a9..fc0c65eb8 100644 --- a/test/plugins/test_mbsubmit.py +++ b/test/plugins/test_mbsubmit.py @@ -14,8 +14,13 @@ import unittest -from test.helper import TestHelper, capture_stdout, control_stdin -from test.test_importer import AutotagStub, ImportHelper +from test.helper import ( + AutotagStub, + ImportHelper, + TestHelper, + capture_stdout, + control_stdin, +) from test.test_ui_importer import TerminalImportSessionSetup diff --git a/test/test_importer.py b/test/test_importer.py index 9b76d5038..4e01a7129 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -27,7 +27,8 @@ from tarfile import TarFile from tempfile import mkstemp from test import _common from test.helper import ( - ImportSessionFixture, + AutotagStub, + ImportHelper, TestHelper, capture_log, has_program, @@ -37,223 +38,12 @@ from zipfile import ZipFile from mediafile import MediaFile -from beets import autotag, config, importer, logging, util +from beets import config, importer, logging, util from beets.autotag import AlbumInfo, AlbumMatch, TrackInfo from beets.importer import albums_in_dir from beets.util import bytestring_path, displayable_path, py3_path, syspath -class AutotagStub: - """Stub out MusicBrainz album and track matcher and control what the - autotagger returns. - """ - - NONE = "NONE" - IDENT = "IDENT" - GOOD = "GOOD" - BAD = "BAD" - MISSING = "MISSING" - """Generate an album match for all but one track - """ - - length = 2 - matching = IDENT - - def install(self): - self.mb_match_album = autotag.mb.match_album - self.mb_match_track = autotag.mb.match_track - self.mb_album_for_id = autotag.mb.album_for_id - self.mb_track_for_id = autotag.mb.track_for_id - - autotag.mb.match_album = self.match_album - autotag.mb.match_track = self.match_track - autotag.mb.album_for_id = self.album_for_id - autotag.mb.track_for_id = self.track_for_id - - return self - - def restore(self): - autotag.mb.match_album = self.mb_match_album - autotag.mb.match_track = self.mb_match_track - autotag.mb.album_for_id = self.mb_album_for_id - autotag.mb.track_for_id = self.mb_track_for_id - - def match_album(self, albumartist, album, tracks, extra_tags): - if self.matching == self.IDENT: - yield self._make_album_match(albumartist, album, tracks) - - elif self.matching == self.GOOD: - for i in range(self.length): - yield self._make_album_match(albumartist, album, tracks, i) - - elif self.matching == self.BAD: - for i in range(self.length): - yield self._make_album_match(albumartist, album, tracks, i + 1) - - elif self.matching == self.MISSING: - yield self._make_album_match(albumartist, album, tracks, missing=1) - - def match_track(self, artist, title): - yield TrackInfo( - title=title.replace("Tag", "Applied"), - track_id="trackid", - artist=artist.replace("Tag", "Applied"), - artist_id="artistid", - length=1, - index=0, - ) - - def album_for_id(self, mbid): - return None - - def track_for_id(self, mbid): - return None - - def _make_track_match(self, artist, album, number): - return TrackInfo( - title="Applied Title %d" % number, - track_id="match %d" % number, - artist=artist, - length=1, - index=0, - ) - - def _make_album_match(self, artist, album, tracks, distance=0, missing=0): - if distance: - id = " " + "M" * distance - else: - id = "" - if artist is None: - artist = "Various Artists" - else: - artist = artist.replace("Tag", "Applied") + id - album = album.replace("Tag", "Applied") + id - - track_infos = [] - for i in range(tracks - missing): - track_infos.append(self._make_track_match(artist, album, i + 1)) - - return AlbumInfo( - artist=artist, - album=album, - tracks=track_infos, - va=False, - album_id="albumid" + id, - artist_id="artistid" + id, - albumtype="soundtrack", - data_source="match_source", - ) - - -class ImportHelper(TestHelper): - """Provides tools to setup a library, a directory containing files that are - to be imported and an import session. The class also provides stubs for the - autotagging library and several assertions for the library. - """ - - def setup_beets(self, disk=False): - super().setup_beets(disk) - self.lib.path_formats = [ - ("default", os.path.join("$artist", "$album", "$title")), - ("singleton:true", os.path.join("singletons", "$title")), - ("comp:true", os.path.join("compilations", "$album", "$title")), - ] - - def _create_import_dir(self, count=3): - """Creates a directory with media files to import. - Sets ``self.import_dir`` to the path of the directory. Also sets - ``self.import_media`` to a list :class:`MediaFile` for all the files in - the directory. - - The directory has following layout - the_album/ - track_1.mp3 - track_2.mp3 - track_3.mp3 - - :param count: Number of files to create - """ - self.import_dir = os.path.join(self.temp_dir, b"testsrcdir") - if os.path.isdir(syspath(self.import_dir)): - shutil.rmtree(syspath(self.import_dir)) - - album_path = os.path.join(self.import_dir, b"the_album") - os.makedirs(syspath(album_path)) - - resource_path = os.path.join(_common.RSRC, b"full.mp3") - - metadata = { - "artist": "Tag Artist", - "album": "Tag Album", - "albumartist": None, - "mb_trackid": None, - "mb_albumid": None, - "comp": None, - } - self.media_files = [] - for i in range(count): - # Copy files - medium_path = os.path.join( - album_path, bytestring_path("track_%d.mp3" % (i + 1)) - ) - shutil.copy(syspath(resource_path), syspath(medium_path)) - medium = MediaFile(medium_path) - - # Set metadata - metadata["track"] = i + 1 - metadata["title"] = "Tag Title %d" % (i + 1) - for attr in metadata: - setattr(medium, attr, metadata[attr]) - medium.save() - self.media_files.append(medium) - self.import_media = self.media_files - - def _setup_import_session( - self, - import_dir=None, - delete=False, - threaded=False, - copy=True, - singletons=False, - move=False, - autotag=True, - link=False, - hardlink=False, - ): - config["import"]["copy"] = copy - config["import"]["delete"] = delete - config["import"]["timid"] = True - config["threaded"] = False - config["import"]["singletons"] = singletons - config["import"]["move"] = move - config["import"]["autotag"] = autotag - config["import"]["resume"] = False - config["import"]["link"] = link - config["import"]["hardlink"] = hardlink - - self.importer = ImportSessionFixture( - self.lib, - loghandler=None, - query=None, - paths=[import_dir or self.import_dir], - ) - - def assert_file_in_lib(self, *segments): - """Join the ``segments`` and assert that this path exists in the - library directory. - """ - self.assertExists(os.path.join(self.libdir, *segments)) - - def assert_file_not_in_lib(self, *segments): - """Join the ``segments`` and assert that this path does not - exist in the library directory. - """ - self.assertNotExists(os.path.join(self.libdir, *segments)) - - def assert_lib_dir_empty(self): - self.assertEqual(len(os.listdir(syspath(self.libdir))), 0) - - class ScrubbedImportTest(_common.TestCase, ImportHelper): def setUp(self): self.setup_beets(disk=True) diff --git a/test/test_plugins.py b/test/test_plugins.py index 320742a18..6a63caf49 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -19,7 +19,7 @@ import shutil import unittest from test import helper from test._common import RSRC -from test.test_importer import AutotagStub, ImportHelper +from test.helper import AutotagStub, ImportHelper from test.test_ui_importer import TerminalImportSessionSetup from unittest.mock import ANY, Mock, patch diff --git a/test/test_ui_importer.py b/test/test_ui_importer.py index 0b8be163a..4bff6b16d 100644 --- a/test/test_ui_importer.py +++ b/test/test_ui_importer.py @@ -73,7 +73,7 @@ class TerminalImportSessionFixture(TerminalImportSession): class TerminalImportSessionSetup: - """Overwrites test_importer.ImportHelper to provide a terminal importer""" + """Overwrites ImportHelper._setup_import_session to provide a terminal importer""" def _setup_import_session( self, From 2b99c12430c2ae8f0ceac04639449a5103a48815 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Thu, 4 Jan 2024 05:02:51 +0100 Subject: [PATCH 3/4] tests: move `TerminalImportSessionSetup` from `tests.test_ui_importer` to `test.helper` This class is imported by some other test modules. Thus, it should reside in a module, which is obviously used by other tests. --- test/helper.py | 81 +++++++++++++++++++++++++++++++++ test/plugins/test_edit.py | 9 +++- test/plugins/test_mbsubmit.py | 2 +- test/test_plugins.py | 3 +- test/test_ui_importer.py | 85 +---------------------------------- 5 files changed, 91 insertions(+), 89 deletions(-) diff --git a/test/helper.py b/test/helper.py index 495b8c7f4..cdf794095 100644 --- a/test/helper.py +++ b/test/helper.py @@ -50,6 +50,7 @@ import beets.plugins from beets import autotag, config, importer, logging, util from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.library import Album, Item, Library +from beets.ui.commands import TerminalImportSession from beets.util import MoveOperation, bytestring_path, syspath @@ -670,6 +671,86 @@ class ImportSessionFixture(importer.ImportSession): task.should_merge_duplicates = True +class TerminalImportSessionFixture(TerminalImportSession): + def __init__(self, *args, **kwargs): + self.io = kwargs.pop("io") + super().__init__(*args, **kwargs) + self._choices = [] + + default_choice = importer.action.APPLY + + def add_choice(self, choice): + self._choices.append(choice) + + def clear_choices(self): + self._choices = [] + + def choose_match(self, task): + self._add_choice_input() + return super().choose_match(task) + + def choose_item(self, task): + self._add_choice_input() + return super().choose_item(task) + + def _add_choice_input(self): + try: + choice = self._choices.pop(0) + except IndexError: + choice = self.default_choice + + if choice == importer.action.APPLY: + self.io.addinput("A") + elif choice == importer.action.ASIS: + self.io.addinput("U") + elif choice == importer.action.ALBUMS: + self.io.addinput("G") + elif choice == importer.action.TRACKS: + self.io.addinput("T") + elif choice == importer.action.SKIP: + self.io.addinput("S") + elif isinstance(choice, int): + self.io.addinput("M") + self.io.addinput(str(choice)) + self._add_choice_input() + else: + raise Exception("Unknown choice %s" % choice) + + +class TerminalImportSessionSetup: + """Overwrites ImportHelper._setup_import_session to provide a terminal importer""" + + def _setup_import_session( + self, + import_dir=None, + delete=False, + threaded=False, + copy=True, + singletons=False, + move=False, + autotag=True, + ): + config["import"]["copy"] = copy + config["import"]["delete"] = delete + config["import"]["timid"] = True + config["threaded"] = False + config["import"]["singletons"] = singletons + config["import"]["move"] = move + config["import"]["autotag"] = autotag + config["import"]["resume"] = False + + if not hasattr(self, "io"): + self.io = _common.DummyIO() + self.io.install() + self.importer = TerminalImportSessionFixture( + self.lib, + loghandler=None, + query=None, + io=self.io, + paths=[import_dir or self.import_dir], + ) + + def generate_album_info(album_id, track_values): """Return `AlbumInfo` populated with mock data. diff --git a/test/plugins/test_edit.py b/test/plugins/test_edit.py index cf9aadf7f..c5eb59507 100644 --- a/test/plugins/test_edit.py +++ b/test/plugins/test_edit.py @@ -15,8 +15,13 @@ import codecs import unittest from test import _common -from test.helper import AutotagStub, ImportHelper, TestHelper, control_stdin -from test.test_ui_importer import TerminalImportSessionSetup +from test.helper import ( + AutotagStub, + ImportHelper, + TerminalImportSessionSetup, + TestHelper, + control_stdin, +) from unittest.mock import patch from beets.dbcore.query import TrueQuery diff --git a/test/plugins/test_mbsubmit.py b/test/plugins/test_mbsubmit.py index fc0c65eb8..b8dbdb5ea 100644 --- a/test/plugins/test_mbsubmit.py +++ b/test/plugins/test_mbsubmit.py @@ -17,11 +17,11 @@ import unittest from test.helper import ( AutotagStub, ImportHelper, + TerminalImportSessionSetup, TestHelper, capture_stdout, control_stdin, ) -from test.test_ui_importer import TerminalImportSessionSetup class MBSubmitPluginTest( diff --git a/test/test_plugins.py b/test/test_plugins.py index 6a63caf49..5fb954894 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -19,8 +19,7 @@ import shutil import unittest from test import helper from test._common import RSRC -from test.helper import AutotagStub, ImportHelper -from test.test_ui_importer import TerminalImportSessionSetup +from test.helper import AutotagStub, ImportHelper, TerminalImportSessionSetup from unittest.mock import ANY, Mock, patch from mediafile import MediaFile diff --git a/test/test_ui_importer.py b/test/test_ui_importer.py index 4bff6b16d..67dfef221 100644 --- a/test/test_ui_importer.py +++ b/test/test_ui_importer.py @@ -20,90 +20,7 @@ test_importer module. But here the test importer inherits from import unittest from test import test_importer -from test._common import DummyIO - -from beets import config, importer -from beets.ui.commands import TerminalImportSession - - -class TerminalImportSessionFixture(TerminalImportSession): - def __init__(self, *args, **kwargs): - self.io = kwargs.pop("io") - super().__init__(*args, **kwargs) - self._choices = [] - - default_choice = importer.action.APPLY - - def add_choice(self, choice): - self._choices.append(choice) - - def clear_choices(self): - self._choices = [] - - def choose_match(self, task): - self._add_choice_input() - return super().choose_match(task) - - def choose_item(self, task): - self._add_choice_input() - return super().choose_item(task) - - def _add_choice_input(self): - try: - choice = self._choices.pop(0) - except IndexError: - choice = self.default_choice - - if choice == importer.action.APPLY: - self.io.addinput("A") - elif choice == importer.action.ASIS: - self.io.addinput("U") - elif choice == importer.action.ALBUMS: - self.io.addinput("G") - elif choice == importer.action.TRACKS: - self.io.addinput("T") - elif choice == importer.action.SKIP: - self.io.addinput("S") - elif isinstance(choice, int): - self.io.addinput("M") - self.io.addinput(str(choice)) - self._add_choice_input() - else: - raise Exception("Unknown choice %s" % choice) - - -class TerminalImportSessionSetup: - """Overwrites ImportHelper._setup_import_session to provide a terminal importer""" - - def _setup_import_session( - self, - import_dir=None, - delete=False, - threaded=False, - copy=True, - singletons=False, - move=False, - autotag=True, - ): - config["import"]["copy"] = copy - config["import"]["delete"] = delete - config["import"]["timid"] = True - config["threaded"] = False - config["import"]["singletons"] = singletons - config["import"]["move"] = move - config["import"]["autotag"] = autotag - config["import"]["resume"] = False - - if not hasattr(self, "io"): - self.io = DummyIO() - self.io.install() - self.importer = TerminalImportSessionFixture( - self.lib, - loghandler=None, - query=None, - io=self.io, - paths=[import_dir or self.import_dir], - ) +from test.helper import TerminalImportSessionSetup class NonAutotaggedImportTest( From 508d28f66befd5dd027cf2d49b2200bbbed2e90d Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Fri, 5 Jan 2024 02:58:55 +0100 Subject: [PATCH 4/4] tests: move reusable test-related modules into the `beets` package External Python packages interfacing beets may want to use an in-memory beets library instance for testing beets-related code. The `TestHelper` class is very helpful for this purpose. Previously `TestHelper` was located in the `test/` directory. Now it is part of `beets` itself (`beets.test.helper.TestHelper`) and can be easily imported. --- beets/test/__init__.py | 19 +++++++++++++++++++ {test => beets/test}/_common.py | 23 ++++++++++++++++++++--- {test => beets/test}/helper.py | 4 +--- docs/changelog.rst | 4 ++++ setup.cfg | 1 + test/plugins/test_acousticbrainz.py | 2 +- test/plugins/test_advancedrewrite.py | 2 +- test/plugins/test_albumtypes.py | 2 +- test/plugins/test_art.py | 4 ++-- test/plugins/test_bareasc.py | 2 +- test/plugins/test_beatport.py | 4 ++-- test/plugins/test_bucket.py | 2 +- test/plugins/test_convert.py | 4 ++-- test/plugins/test_discogs.py | 6 +++--- test/plugins/test_edit.py | 12 ++++++------ test/plugins/test_embedart.py | 4 ++-- test/plugins/test_embyupdate.py | 2 +- test/plugins/test_export.py | 3 ++- test/plugins/test_fetchart.py | 2 +- test/plugins/test_filefilter.py | 4 ++-- test/plugins/test_ftintitle.py | 2 +- test/plugins/test_hook.py | 4 ++-- test/plugins/test_importadded.py | 2 +- test/plugins/test_info.py | 2 +- test/plugins/test_ipfs.py | 4 ++-- test/plugins/test_keyfinder.py | 2 +- test/plugins/test_lastgenre.py | 4 ++-- test/plugins/test_limit.py | 3 ++- test/plugins/test_lyrics.py | 2 +- test/plugins/test_mbsubmit.py | 3 ++- test/plugins/test_mbsync.py | 10 +++++----- test/plugins/test_mpdstats.py | 2 +- test/plugins/test_parentwork.py | 2 +- test/plugins/test_permissions.py | 4 ++-- test/plugins/test_play.py | 2 +- test/plugins/test_player.py | 2 +- test/plugins/test_playlist.py | 2 +- test/plugins/test_plexupdate.py | 2 +- test/plugins/test_plugin_mediafield.py | 2 +- test/plugins/test_random.py | 2 +- test/plugins/test_replaygain.py | 2 +- test/plugins/test_smartplaylist.py | 4 ++-- test/plugins/test_spotify.py | 4 ++-- test/plugins/test_subsonicupdate.py | 4 ++-- test/plugins/test_the.py | 2 +- test/plugins/test_thumbnails.py | 2 +- test/plugins/test_types_plugin.py | 3 ++- test/plugins/test_web.py | 2 +- test/plugins/test_zero.py | 2 +- test/test_art_resize.py | 4 ++-- test/test_autotag.py | 2 +- test/test_config_command.py | 2 +- test/test_datequery.py | 2 +- test/test_dbcore.py | 2 +- test/test_files.py | 4 ++-- test/test_importer.py | 16 ++++++++-------- test/test_library.py | 6 +++--- test/test_logging.py | 4 ++-- test/test_m3ufile.py | 2 +- test/test_mb.py | 2 +- test/test_metasync.py | 4 ++-- test/test_plugins.py | 10 +++++++--- test/test_query.py | 2 +- test/test_sort.py | 2 +- test/test_ui.py | 9 +++++++-- test/test_ui_commands.py | 2 +- test/test_ui_importer.py | 3 ++- test/test_ui_init.py | 4 ++-- test/test_util.py | 2 +- test/test_vfs.py | 2 +- 70 files changed, 164 insertions(+), 111 deletions(-) create mode 100644 beets/test/__init__.py rename {test => beets/test}/_common.py (95%) rename {test => beets/test}/helper.py (99%) diff --git a/beets/test/__init__.py b/beets/test/__init__.py new file mode 100644 index 000000000..2af37583f --- /dev/null +++ b/beets/test/__init__.py @@ -0,0 +1,19 @@ +# This file is part of beets. +# Copyright 2024, Lars Kruse +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""This module contains components of beets' test environment, which +may be of use for testing procedures of external libraries or programs. +For example the 'TestHelper' class may be useful for creating an +in-memory beets library filled with a few example items. +""" diff --git a/test/_common.py b/beets/test/_common.py similarity index 95% rename from test/_common.py rename to beets/test/_common.py index 665c9098b..0eed7170e 100644 --- a/test/_common.py +++ b/beets/test/_common.py @@ -33,12 +33,29 @@ from beets.ui import commands # noqa: E402 from beets.util import bytestring_path, syspath # noqa: E402 beetsplug.__path__ = [ - os.path.abspath(os.path.join(__file__, "..", "..", "beetsplug")) + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + os.path.pardir, + os.path.pardir, + "beetsplug", + ) + ) ] # Test resources path. -RSRC = util.bytestring_path(os.path.join(os.path.dirname(__file__), "rsrc")) -PLUGINPATH = os.path.join(os.path.dirname(__file__), "rsrc", "beetsplug") +RSRC = util.bytestring_path( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + os.path.pardir, + os.path.pardir, + "test", + "rsrc", + ) + ) +) +PLUGINPATH = os.path.join(RSRC.decode(), "beetsplug") # Propagate to root logger so the test runner can capture it log = logging.getLogger("beets") diff --git a/test/helper.py b/beets/test/helper.py similarity index 99% rename from test/helper.py rename to beets/test/helper.py index cdf794095..b12bfe7ab 100644 --- a/test/helper.py +++ b/beets/test/helper.py @@ -40,9 +40,6 @@ from enum import Enum from io import StringIO from tempfile import mkdtemp, mkstemp -# TODO Move AutotagMock here -from test import _common - from mediafile import Image, MediaFile import beets @@ -50,6 +47,7 @@ import beets.plugins from beets import autotag, config, importer, logging, util from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.library import Album, Item, Library +from beets.test import _common from beets.ui.commands import TerminalImportSession from beets.util import MoveOperation, bytestring_path, syspath diff --git a/docs/changelog.rst b/docs/changelog.rst index b3b63f1c2..c4e1be077 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -290,6 +290,10 @@ For plugin developers: overwrite the function defined by the other plugin. Now, beets will raise an exception when this happens. :bug:`5002` +* Allow reuse of some parts of beets' testing components. This may ease the + work for externally developed plugins or related software (e.g. the beets + plugin for Mopidy), if they need to create an in-memory instance of a beets + music library for their tests. For packagers: diff --git a/setup.cfg b/setup.cfg index 2e91dbdbd..aef45c084 100644 --- a/setup.cfg +++ b/setup.cfg @@ -117,6 +117,7 @@ per-file-ignores = ./beets/__main__.py:D ./beets/importer.py:D ./beets/plugins.py:D + ./beets/test/*:D ./beets/util/bluelet.py:D ./beets/util/enumeration.py:D ./beets/util/artresizer.py:D diff --git a/test/plugins/test_acousticbrainz.py b/test/plugins/test_acousticbrainz.py index 76167accc..fbf83def0 100644 --- a/test/plugins/test_acousticbrainz.py +++ b/test/plugins/test_acousticbrainz.py @@ -19,8 +19,8 @@ import json import os.path import unittest -from test._common import RSRC +from beets.test._common import RSRC from beetsplug.acousticbrainz import ABSCHEME, AcousticPlugin diff --git a/test/plugins/test_advancedrewrite.py b/test/plugins/test_advancedrewrite.py index 74d2e5db0..d21660da6 100644 --- a/test/plugins/test_advancedrewrite.py +++ b/test/plugins/test_advancedrewrite.py @@ -16,8 +16,8 @@ """ import unittest -from test.helper import TestHelper +from beets.test.helper import TestHelper from beets.ui import UserError PLUGIN_NAME = "advancedrewrite" diff --git a/test/plugins/test_albumtypes.py b/test/plugins/test_albumtypes.py index d50e2263c..532fdc69c 100644 --- a/test/plugins/test_albumtypes.py +++ b/test/plugins/test_albumtypes.py @@ -16,9 +16,9 @@ import unittest -from test.helper import TestHelper from beets.autotag.mb import VARIOUS_ARTISTS_ID +from beets.test.helper import TestHelper from beetsplug.albumtypes import AlbumTypesPlugin diff --git a/test/plugins/test_art.py b/test/plugins/test_art.py index a691fa602..b2d1d74b4 100644 --- a/test/plugins/test_art.py +++ b/test/plugins/test_art.py @@ -18,8 +18,6 @@ import os import shutil import unittest -from test import _common -from test.helper import capture_log from unittest.mock import patch import confuse @@ -27,6 +25,8 @@ import responses from beets import config, importer, library, logging, util from beets.autotag import AlbumInfo, AlbumMatch +from beets.test import _common +from beets.test.helper import capture_log from beets.util import syspath from beets.util.artresizer import ArtResizer from beetsplug import fetchart diff --git a/test/plugins/test_bareasc.py b/test/plugins/test_bareasc.py index c2357d5e7..59ac0d216 100644 --- a/test/plugins/test_bareasc.py +++ b/test/plugins/test_bareasc.py @@ -5,9 +5,9 @@ import unittest -from test.helper import TestHelper, capture_stdout from beets import logging +from beets.test.helper import TestHelper, capture_stdout class BareascPluginTest(unittest.TestCase, TestHelper): diff --git a/test/plugins/test_beatport.py b/test/plugins/test_beatport.py index 3e578e552..fd75f2154 100644 --- a/test/plugins/test_beatport.py +++ b/test/plugins/test_beatport.py @@ -17,10 +17,10 @@ import unittest from datetime import timedelta -from test import _common -from test.helper import TestHelper from beets import library +from beets.test import _common +from beets.test.helper import TestHelper from beetsplug import beatport diff --git a/test/plugins/test_bucket.py b/test/plugins/test_bucket.py index b2893ca78..4f43f5ef5 100644 --- a/test/plugins/test_bucket.py +++ b/test/plugins/test_bucket.py @@ -16,9 +16,9 @@ import unittest -from test.helper import TestHelper from beets import config, ui +from beets.test.helper import TestHelper from beetsplug import bucket diff --git a/test/plugins/test_convert.py b/test/plugins/test_convert.py index 124ca5e76..bc13b6fec 100644 --- a/test/plugins/test_convert.py +++ b/test/plugins/test_convert.py @@ -18,12 +18,12 @@ import os.path import re import sys import unittest -from test import _common, helper -from test.helper import capture_log, control_stdin from mediafile import MediaFile from beets import util +from beets.test import _common, helper +from beets.test.helper import capture_log, control_stdin from beets.util import bytestring_path, displayable_path diff --git a/test/plugins/test_discogs.py b/test/plugins/test_discogs.py index c448eddd0..6ee57dcd9 100644 --- a/test/plugins/test_discogs.py +++ b/test/plugins/test_discogs.py @@ -16,11 +16,11 @@ """ import unittest -from test import _common -from test._common import Bag -from test.helper import capture_log from beets import config +from beets.test import _common +from beets.test._common import Bag +from beets.test.helper import capture_log from beets.util.id_extractors import extract_discogs_id_regex from beetsplug.discogs import DiscogsPlugin diff --git a/test/plugins/test_edit.py b/test/plugins/test_edit.py index c5eb59507..7c1fcf0b3 100644 --- a/test/plugins/test_edit.py +++ b/test/plugins/test_edit.py @@ -14,18 +14,18 @@ import codecs import unittest -from test import _common -from test.helper import ( +from unittest.mock import patch + +from beets.dbcore.query import TrueQuery +from beets.library import Item +from beets.test import _common +from beets.test.helper import ( AutotagStub, ImportHelper, TerminalImportSessionSetup, TestHelper, control_stdin, ) -from unittest.mock import patch - -from beets.dbcore.query import TrueQuery -from beets.library import Item from beetsplug.edit import EditPlugin diff --git a/test/plugins/test_embedart.py b/test/plugins/test_embedart.py index 55dd18dc3..80d284d31 100644 --- a/test/plugins/test_embedart.py +++ b/test/plugins/test_embedart.py @@ -17,8 +17,6 @@ import os.path import shutil import tempfile import unittest -from test import _common -from test.helper import TestHelper from test.plugins.test_art import FetchImageHelper from test.test_art_resize import DummyIMBackend from unittest.mock import MagicMock, patch @@ -26,6 +24,8 @@ from unittest.mock import MagicMock, patch from mediafile import MediaFile from beets import art, config, logging, ui +from beets.test import _common +from beets.test.helper import TestHelper from beets.util import bytestring_path, displayable_path, syspath from beets.util.artresizer import ArtResizer diff --git a/test/plugins/test_embyupdate.py b/test/plugins/test_embyupdate.py index 55cebb01e..57859f5ac 100644 --- a/test/plugins/test_embyupdate.py +++ b/test/plugins/test_embyupdate.py @@ -1,8 +1,8 @@ import unittest -from test.helper import TestHelper import responses +from beets.test.helper import TestHelper from beetsplug import embyupdate diff --git a/test/plugins/test_export.py b/test/plugins/test_export.py index 1c3e5e49e..977184bb0 100644 --- a/test/plugins/test_export.py +++ b/test/plugins/test_export.py @@ -19,10 +19,11 @@ import json import re # used to test csv format import unittest -from test.helper import TestHelper from xml.etree import ElementTree from xml.etree.ElementTree import Element +from beets.test.helper import TestHelper + class ExportPluginTest(unittest.TestCase, TestHelper): def setUp(self): diff --git a/test/plugins/test_fetchart.py b/test/plugins/test_fetchart.py index 90a6570a2..c651924af 100644 --- a/test/plugins/test_fetchart.py +++ b/test/plugins/test_fetchart.py @@ -17,9 +17,9 @@ import ctypes import os import sys import unittest -from test.helper import TestHelper from beets import util +from beets.test.helper import TestHelper class FetchartCliTest(unittest.TestCase, TestHelper): diff --git a/test/plugins/test_filefilter.py b/test/plugins/test_filefilter.py index 989436379..10eed77c4 100644 --- a/test/plugins/test_filefilter.py +++ b/test/plugins/test_filefilter.py @@ -19,12 +19,12 @@ import os import shutil import unittest -from test import _common -from test.helper import ImportHelper, capture_log from mediafile import MediaFile from beets import config +from beets.test import _common +from beets.test.helper import ImportHelper, capture_log from beets.util import bytestring_path, displayable_path, syspath from beetsplug.filefilter import FileFilterPlugin diff --git a/test/plugins/test_ftintitle.py b/test/plugins/test_ftintitle.py index 10ffd4f72..60dd3668e 100644 --- a/test/plugins/test_ftintitle.py +++ b/test/plugins/test_ftintitle.py @@ -16,8 +16,8 @@ import unittest -from test.helper import TestHelper +from beets.test.helper import TestHelper from beetsplug import ftintitle diff --git a/test/plugins/test_hook.py b/test/plugins/test_hook.py index d702b9894..1364028f6 100644 --- a/test/plugins/test_hook.py +++ b/test/plugins/test_hook.py @@ -17,10 +17,10 @@ import os.path import sys import tempfile import unittest -from test import _common -from test.helper import TestHelper, capture_log from beets import config, plugins +from beets.test import _common +from beets.test.helper import TestHelper, capture_log def get_temporary_path(): diff --git a/test/plugins/test_importadded.py b/test/plugins/test_importadded.py index 3aa0152ee..647892e1d 100644 --- a/test/plugins/test_importadded.py +++ b/test/plugins/test_importadded.py @@ -17,9 +17,9 @@ import os import unittest -from test.helper import AutotagStub, ImportHelper from beets import importer +from beets.test.helper import AutotagStub, ImportHelper from beets.util import displayable_path, syspath from beetsplug.importadded import ImportAddedPlugin diff --git a/test/plugins/test_info.py b/test/plugins/test_info.py index 64308e8b1..a14424799 100644 --- a/test/plugins/test_info.py +++ b/test/plugins/test_info.py @@ -14,10 +14,10 @@ import unittest -from test.helper import TestHelper from mediafile import MediaFile +from beets.test.helper import TestHelper from beets.util import displayable_path diff --git a/test/plugins/test_ipfs.py b/test/plugins/test_ipfs.py index f4fc7c8bf..65b713101 100644 --- a/test/plugins/test_ipfs.py +++ b/test/plugins/test_ipfs.py @@ -14,11 +14,11 @@ import os import unittest -from test import _common -from test.helper import TestHelper from unittest.mock import Mock, patch from beets import library +from beets.test import _common +from beets.test.helper import TestHelper from beets.util import _fsencoding, bytestring_path from beetsplug.ipfs import IPFSPlugin diff --git a/test/plugins/test_keyfinder.py b/test/plugins/test_keyfinder.py index 4827fd3d7..84d66dfeb 100644 --- a/test/plugins/test_keyfinder.py +++ b/test/plugins/test_keyfinder.py @@ -14,11 +14,11 @@ import unittest -from test.helper import TestHelper from unittest.mock import patch from beets import util from beets.library import Item +from beets.test.helper import TestHelper @patch("beets.util.command_output") diff --git a/test/plugins/test_lastgenre.py b/test/plugins/test_lastgenre.py index ee8f1d4a2..6f250c3ba 100644 --- a/test/plugins/test_lastgenre.py +++ b/test/plugins/test_lastgenre.py @@ -16,11 +16,11 @@ import unittest -from test import _common -from test.helper import TestHelper from unittest.mock import Mock from beets import config +from beets.test import _common +from beets.test.helper import TestHelper from beetsplug import lastgenre diff --git a/test/plugins/test_limit.py b/test/plugins/test_limit.py index 2e2fb38c3..0ed6c9202 100644 --- a/test/plugins/test_limit.py +++ b/test/plugins/test_limit.py @@ -14,7 +14,8 @@ """Tests for the 'limit' plugin.""" import unittest -from test.helper import TestHelper + +from beets.test.helper import TestHelper class LimitPluginTest(unittest.TestCase, TestHelper): diff --git a/test/plugins/test_lyrics.py b/test/plugins/test_lyrics.py index 4bfeda80e..44a8edb35 100644 --- a/test/plugins/test_lyrics.py +++ b/test/plugins/test_lyrics.py @@ -19,7 +19,6 @@ import itertools import os import re import unittest -from test import _common from unittest.mock import MagicMock, patch import confuse @@ -27,6 +26,7 @@ import requests from beets import logging from beets.library import Item +from beets.test import _common from beets.util import bytestring_path from beetsplug import lyrics diff --git a/test/plugins/test_mbsubmit.py b/test/plugins/test_mbsubmit.py index b8dbdb5ea..40024bc71 100644 --- a/test/plugins/test_mbsubmit.py +++ b/test/plugins/test_mbsubmit.py @@ -14,7 +14,8 @@ import unittest -from test.helper import ( + +from beets.test.helper import ( AutotagStub, ImportHelper, TerminalImportSessionSetup, diff --git a/test/plugins/test_mbsync.py b/test/plugins/test_mbsync.py index d1c823f02..bc41b3464 100644 --- a/test/plugins/test_mbsync.py +++ b/test/plugins/test_mbsync.py @@ -14,16 +14,16 @@ import unittest -from test.helper import ( +from unittest.mock import patch + +from beets import config +from beets.library import Item +from beets.test.helper import ( TestHelper, capture_log, generate_album_info, generate_track_info, ) -from unittest.mock import patch - -from beets import config -from beets.library import Item class MbsyncCliTest(unittest.TestCase, TestHelper): diff --git a/test/plugins/test_mpdstats.py b/test/plugins/test_mpdstats.py index 76761da10..40804fabb 100644 --- a/test/plugins/test_mpdstats.py +++ b/test/plugins/test_mpdstats.py @@ -14,11 +14,11 @@ import unittest -from test.helper import TestHelper from unittest.mock import ANY, Mock, call, patch from beets import util from beets.library import Item +from beets.test.helper import TestHelper from beetsplug.mpdstats import MPDStats diff --git a/test/plugins/test_parentwork.py b/test/plugins/test_parentwork.py index 3e88823fb..377784983 100644 --- a/test/plugins/test_parentwork.py +++ b/test/plugins/test_parentwork.py @@ -17,10 +17,10 @@ import os import unittest -from test.helper import TestHelper from unittest.mock import patch from beets.library import Item +from beets.test.helper import TestHelper from beetsplug import parentwork work = { diff --git a/test/plugins/test_permissions.py b/test/plugins/test_permissions.py index d6175de5a..d10a873cd 100644 --- a/test/plugins/test_permissions.py +++ b/test/plugins/test_permissions.py @@ -4,10 +4,10 @@ import os import platform import unittest -from test._common import touch -from test.helper import TestHelper from unittest.mock import Mock, patch +from beets.test._common import touch +from beets.test.helper import TestHelper from beets.util import displayable_path from beetsplug.permissions import ( check_permissions, diff --git a/test/plugins/test_play.py b/test/plugins/test_play.py index 4049878b6..cb99f6b43 100644 --- a/test/plugins/test_play.py +++ b/test/plugins/test_play.py @@ -18,9 +18,9 @@ import os import sys import unittest -from test.helper import TestHelper, control_stdin from unittest.mock import ANY, patch +from beets.test.helper import TestHelper, control_stdin from beets.ui import UserError from beets.util import open_anything diff --git a/test/plugins/test_player.py b/test/plugins/test_player.py index 480b87691..eb38e9afc 100644 --- a/test/plugins/test_player.py +++ b/test/plugins/test_player.py @@ -25,7 +25,6 @@ import threading import time import unittest from contextlib import contextmanager -from test.helper import TestHelper # Mock GstPlayer so that the forked process doesn't attempt to import gi: from unittest import mock @@ -33,6 +32,7 @@ from unittest import mock import confuse import yaml +from beets.test.helper import TestHelper from beets.util import bluelet, py3_path from beetsplug import bpd diff --git a/test/plugins/test_playlist.py b/test/plugins/test_playlist.py index 21bf6491f..b4861dcaf 100644 --- a/test/plugins/test_playlist.py +++ b/test/plugins/test_playlist.py @@ -18,9 +18,9 @@ import shutil import tempfile import unittest from shlex import quote -from test import _common, helper import beets +from beets.test import _common, helper class PlaylistTestHelper(helper.TestHelper): diff --git a/test/plugins/test_plexupdate.py b/test/plugins/test_plexupdate.py index 2571415e7..f45ea9d8f 100644 --- a/test/plugins/test_plexupdate.py +++ b/test/plugins/test_plexupdate.py @@ -1,8 +1,8 @@ import unittest -from test.helper import TestHelper import responses +from beets.test.helper import TestHelper from beetsplug.plexupdate import get_music_section, update_plex diff --git a/test/plugins/test_plugin_mediafield.py b/test/plugins/test_plugin_mediafield.py index 004b3eaab..0e03886cf 100644 --- a/test/plugins/test_plugin_mediafield.py +++ b/test/plugins/test_plugin_mediafield.py @@ -18,12 +18,12 @@ import os import shutil import unittest -from test import _common import mediafile from beets.library import Item from beets.plugins import BeetsPlugin +from beets.test import _common from beets.util import bytestring_path, syspath field_extension = mediafile.MediaField( diff --git a/test/plugins/test_random.py b/test/plugins/test_random.py index dc9217745..b371fa832 100644 --- a/test/plugins/test_random.py +++ b/test/plugins/test_random.py @@ -19,9 +19,9 @@ import math import unittest from random import Random -from test.helper import TestHelper from beets import random +from beets.test.helper import TestHelper class RandomTest(unittest.TestCase, TestHelper): diff --git a/test/plugins/test_replaygain.py b/test/plugins/test_replaygain.py index e80c79bc6..92e3e5f65 100644 --- a/test/plugins/test_replaygain.py +++ b/test/plugins/test_replaygain.py @@ -14,11 +14,11 @@ import unittest -from test.helper import TestHelper, has_program from mediafile import MediaFile from beets import config +from beets.test.helper import TestHelper, has_program from beetsplug.replaygain import ( FatalGstreamerPluginReplayGainError, GStreamerBackend, diff --git a/test/plugins/test_smartplaylist.py b/test/plugins/test_smartplaylist.py index 921ae815e..ee3bfb8ce 100644 --- a/test/plugins/test_smartplaylist.py +++ b/test/plugins/test_smartplaylist.py @@ -17,14 +17,14 @@ import unittest from os import path, remove from shutil import rmtree from tempfile import mkdtemp -from test import _common -from test.helper import TestHelper from unittest.mock import MagicMock, Mock, PropertyMock from beets import config from beets.dbcore import OrQuery from beets.dbcore.query import FixedFieldSort, MultipleSort, NullSort from beets.library import Album, Item, parse_query_string +from beets.test import _common +from beets.test.helper import TestHelper from beets.ui import UserError from beets.util import CHAR_REPLACE, bytestring_path, py3_path, syspath from beetsplug.smartplaylist import SmartPlaylistPlugin diff --git a/test/plugins/test_spotify.py b/test/plugins/test_spotify.py index 19d1ca3e7..832e558fa 100644 --- a/test/plugins/test_spotify.py +++ b/test/plugins/test_spotify.py @@ -3,14 +3,14 @@ import os import unittest -from test import _common -from test.helper import TestHelper from urllib.parse import parse_qs, urlparse import responses from beets import config from beets.library import Item +from beets.test import _common +from beets.test.helper import TestHelper from beetsplug import spotify diff --git a/test/plugins/test_subsonicupdate.py b/test/plugins/test_subsonicupdate.py index 3d401102f..1e1f4eb43 100644 --- a/test/plugins/test_subsonicupdate.py +++ b/test/plugins/test_subsonicupdate.py @@ -2,13 +2,13 @@ import unittest -from test import _common -from test.helper import TestHelper from urllib.parse import parse_qs, urlparse import responses from beets import config +from beets.test import _common +from beets.test.helper import TestHelper from beetsplug import subsonicupdate diff --git a/test/plugins/test_the.py b/test/plugins/test_the.py index a42f1a8c4..6f8b05883 100644 --- a/test/plugins/test_the.py +++ b/test/plugins/test_the.py @@ -2,9 +2,9 @@ import unittest -from test import _common from beets import config +from beets.test import _common from beetsplug.the import FORMAT, PATTERN_A, PATTERN_THE, ThePlugin diff --git a/test/plugins/test_thumbnails.py b/test/plugins/test_thumbnails.py index 127cc67d8..951fc6e8c 100644 --- a/test/plugins/test_thumbnails.py +++ b/test/plugins/test_thumbnails.py @@ -17,9 +17,9 @@ import os.path import unittest from shutil import rmtree from tempfile import mkdtemp -from test.helper import TestHelper from unittest.mock import Mock, call, patch +from beets.test.helper import TestHelper from beets.util import bytestring_path, syspath from beetsplug.thumbnails import ( LARGE_DIR, diff --git a/test/plugins/test_types_plugin.py b/test/plugins/test_types_plugin.py index 6488ca0ab..1726fbf6f 100644 --- a/test/plugins/test_types_plugin.py +++ b/test/plugins/test_types_plugin.py @@ -16,10 +16,11 @@ import time import unittest from datetime import datetime -from test.helper import TestHelper from confuse import ConfigValueError +from beets.test.helper import TestHelper + class TypesPluginTest(unittest.TestCase, TestHelper): def setUp(self): diff --git a/test/plugins/test_web.py b/test/plugins/test_web.py index 8da6f2350..2fe35282d 100644 --- a/test/plugins/test_web.py +++ b/test/plugins/test_web.py @@ -6,10 +6,10 @@ import os.path import platform import shutil import unittest -from test import _common from beets import logging from beets.library import Album, Item +from beets.test import _common from beetsplug import web diff --git a/test/plugins/test_zero.py b/test/plugins/test_zero.py index 50b00a707..7c3337e87 100644 --- a/test/plugins/test_zero.py +++ b/test/plugins/test_zero.py @@ -2,11 +2,11 @@ import unittest -from test.helper import TestHelper, control_stdin from mediafile import MediaFile from beets.library import Item +from beets.test.helper import TestHelper, control_stdin from beets.util import syspath from beetsplug.zero import ZeroPlugin diff --git a/test/test_art_resize.py b/test/test_art_resize.py index e14ff42ef..2f426a1dd 100644 --- a/test/test_art_resize.py +++ b/test/test_art_resize.py @@ -17,10 +17,10 @@ import os import unittest -from test import _common -from test.helper import TestHelper from unittest.mock import patch +from beets.test import _common +from beets.test.helper import TestHelper from beets.util import command_output, syspath from beets.util.artresizer import IMBackend, PILBackend diff --git a/test/test_autotag.py b/test/test_autotag.py index 130b69419..44c68ce9a 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -17,12 +17,12 @@ import re import unittest -from test import _common from beets import autotag, config from beets.autotag import AlbumInfo, TrackInfo, match from beets.autotag.hooks import Distance, string_dist from beets.library import Item +from beets.test import _common from beets.util import plurality diff --git a/test/test_config_command.py b/test/test_config_command.py index 77fddee02..5ff9e6f4e 100644 --- a/test/test_config_command.py +++ b/test/test_config_command.py @@ -2,13 +2,13 @@ import os import unittest from shutil import rmtree from tempfile import mkdtemp -from test.helper import TestHelper from unittest.mock import patch import yaml from beets import config, ui from beets.library import Library +from beets.test.helper import TestHelper class ConfigCommandTest(unittest.TestCase, TestHelper): diff --git a/test/test_datequery.py b/test/test_datequery.py index 73175f9ec..2b666f0d1 100644 --- a/test/test_datequery.py +++ b/test/test_datequery.py @@ -18,7 +18,6 @@ import time import unittest from datetime import datetime, timedelta -from test import _common from beets.dbcore.query import ( DateInterval, @@ -26,6 +25,7 @@ from beets.dbcore.query import ( InvalidQueryArgumentValueError, _parse_periods, ) +from beets.test import _common def _date(string): diff --git a/test/test_dbcore.py b/test/test_dbcore.py index 541222be2..c98e9ceba 100644 --- a/test/test_dbcore.py +++ b/test/test_dbcore.py @@ -20,9 +20,9 @@ import shutil import sqlite3 import unittest from tempfile import mkstemp -from test import _common from beets import dbcore +from beets.test import _common # Fixture: concrete database and model classes. For migration tests, we # have multiple models with different numbers of fields. diff --git a/test/test_files.py b/test/test_files.py index d57c85747..e0c8a9bf1 100644 --- a/test/test_files.py +++ b/test/test_files.py @@ -20,11 +20,11 @@ import shutil import stat import unittest from os.path import join -from test import _common -from test._common import item, touch import beets.library from beets import util +from beets.test import _common +from beets.test._common import item, touch from beets.util import MoveOperation, bytestring_path, syspath diff --git a/test/test_importer.py b/test/test_importer.py index 4e01a7129..a381fc5f4 100644 --- a/test/test_importer.py +++ b/test/test_importer.py @@ -25,14 +25,6 @@ import unittest from io import StringIO from tarfile import TarFile from tempfile import mkstemp -from test import _common -from test.helper import ( - AutotagStub, - ImportHelper, - TestHelper, - capture_log, - has_program, -) from unittest.mock import Mock, patch from zipfile import ZipFile @@ -41,6 +33,14 @@ from mediafile import MediaFile from beets import config, importer, logging, util from beets.autotag import AlbumInfo, AlbumMatch, TrackInfo from beets.importer import albums_in_dir +from beets.test import _common +from beets.test.helper import ( + AutotagStub, + ImportHelper, + TestHelper, + capture_log, + has_program, +) from beets.util import bytestring_path, displayable_path, py3_path, syspath diff --git a/test/test_library.py b/test/test_library.py index bf59bed22..beb06971c 100644 --- a/test/test_library.py +++ b/test/test_library.py @@ -24,15 +24,15 @@ import sys import time import unicodedata import unittest -from test import _common -from test._common import item -from test.helper import TestHelper from mediafile import MediaFile, UnreadableFileError import beets.dbcore.query import beets.library from beets import config, plugins, util +from beets.test import _common +from beets.test._common import item +from beets.test.helper import TestHelper from beets.util import bytestring_path, syspath # Shortcut to path normalization. diff --git a/test/test_logging.py b/test/test_logging.py index 5e019786b..58be799e0 100644 --- a/test/test_logging.py +++ b/test/test_logging.py @@ -5,12 +5,12 @@ import sys import threading import unittest from io import StringIO -from test import _common, helper -from test._common import TestCase import beets.logging as blog import beetsplug from beets import plugins, ui +from beets.test import _common, helper +from beets.test._common import TestCase class LoggingTest(TestCase): diff --git a/test/test_m3ufile.py b/test/test_m3ufile.py index be0517a78..2db5cac06 100644 --- a/test/test_m3ufile.py +++ b/test/test_m3ufile.py @@ -19,8 +19,8 @@ import unittest from os import path from shutil import rmtree from tempfile import mkdtemp -from test._common import RSRC +from beets.test._common import RSRC from beets.util import bytestring_path from beets.util.m3u import EmptyPlaylistError, M3UFile diff --git a/test/test_mb.py b/test/test_mb.py index 71a0294a2..605d126f9 100644 --- a/test/test_mb.py +++ b/test/test_mb.py @@ -16,11 +16,11 @@ """ import unittest -from test import _common from unittest import mock from beets import config from beets.autotag import mb +from beets.test import _common class MBAlbumInfoTest(_common.TestCase): diff --git a/test/test_metasync.py b/test/test_metasync.py index 18493adb4..426334670 100644 --- a/test/test_metasync.py +++ b/test/test_metasync.py @@ -18,10 +18,10 @@ import platform import time import unittest from datetime import datetime -from test import _common -from test.helper import TestHelper from beets.library import Item +from beets.test import _common +from beets.test.helper import TestHelper from beets.util import py3_path diff --git a/test/test_plugins.py b/test/test_plugins.py index 5fb954894..84f689342 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -17,9 +17,6 @@ import itertools import os import shutil import unittest -from test import helper -from test._common import RSRC -from test.helper import AutotagStub, ImportHelper, TerminalImportSessionSetup from unittest.mock import ANY, Mock, patch from mediafile import MediaFile @@ -34,6 +31,13 @@ from beets.importer import ( ) from beets.library import Item from beets.plugins import MetadataSourcePlugin +from beets.test import helper +from beets.test._common import RSRC +from beets.test.helper import ( + AutotagStub, + ImportHelper, + TerminalImportSessionSetup, +) from beets.util import bytestring_path, displayable_path, syspath from beets.util.id_extractors import ( beatport_id_regex, diff --git a/test/test_query.py b/test/test_query.py index a6538f7ca..354d71dc8 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -20,7 +20,6 @@ import sys import unittest from contextlib import contextmanager from functools import partial -from test import _common, helper import beets.library from beets import dbcore, util @@ -31,6 +30,7 @@ from beets.dbcore.query import ( ParsingError, ) from beets.library import Item, Library +from beets.test import _common, helper from beets.util import syspath # Because the absolute path begins with something like C:, we diff --git a/test/test_sort.py b/test/test_sort.py index 035d5ea70..d5235cb4c 100644 --- a/test/test_sort.py +++ b/test/test_sort.py @@ -16,10 +16,10 @@ """ import unittest -from test import _common import beets.library from beets import config, dbcore +from beets.test import _common # A test case class providing a library with some dummy data and some diff --git a/test/test_ui.py b/test/test_ui.py index cae861480..811f4d96e 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -22,8 +22,6 @@ import shutil import subprocess import sys import unittest -from test import _common -from test.helper import TestHelper, capture_stdout, control_stdin, has_program from unittest.mock import Mock, patch from confuse import ConfigError @@ -31,6 +29,13 @@ from mediafile import MediaFile from beets import autotag, config, library, plugins, ui, util from beets.autotag.match import distance +from beets.test import _common +from beets.test.helper import ( + TestHelper, + capture_stdout, + control_stdin, + has_program, +) from beets.ui import commands from beets.util import MoveOperation, syspath diff --git a/test/test_ui_commands.py b/test/test_ui_commands.py index 3c53b93a3..fd1bb9243 100644 --- a/test/test_ui_commands.py +++ b/test/test_ui_commands.py @@ -19,9 +19,9 @@ import os import shutil import unittest -from test import _common from beets import library, ui +from beets.test import _common from beets.ui import commands from beets.util import syspath diff --git a/test/test_ui_importer.py b/test/test_ui_importer.py index 67dfef221..3991376ac 100644 --- a/test/test_ui_importer.py +++ b/test/test_ui_importer.py @@ -20,7 +20,8 @@ test_importer module. But here the test importer inherits from import unittest from test import test_importer -from test.helper import TerminalImportSessionSetup + +from beets.test.helper import TerminalImportSessionSetup class NonAutotaggedImportTest( diff --git a/test/test_ui_init.py b/test/test_ui_init.py index 70ed3e229..c3e45b1c3 100644 --- a/test/test_ui_init.py +++ b/test/test_ui_init.py @@ -20,10 +20,10 @@ import shutil import unittest from copy import deepcopy from random import random -from test import _common -from test.helper import control_stdin from beets import config, ui +from beets.test import _common +from beets.test.helper import control_stdin class InputMethodsTest(_common.TestCase): diff --git a/test/test_util.py b/test/test_util.py index 26da2bf89..69d4b14b2 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -20,10 +20,10 @@ import re import subprocess import sys import unittest -from test import _common from unittest.mock import Mock, patch from beets import util +from beets.test import _common class UtilTest(unittest.TestCase): diff --git a/test/test_vfs.py b/test/test_vfs.py index 28458aecf..789356157 100644 --- a/test/test_vfs.py +++ b/test/test_vfs.py @@ -15,9 +15,9 @@ """Tests for the virtual filesystem builder..""" import unittest -from test import _common from beets import library, vfs +from beets.test import _common class VFSTest(_common.TestCase):