Leave a single source of truth for importer setup

This commit is contained in:
Šarūnas Nejus 2024-07-12 22:11:48 +01:00
parent 8065ff0461
commit f042f5ad32
No known key found for this signature in database
GPG key ID: DD28F6704DBE3435
13 changed files with 212 additions and 251 deletions

View file

@ -53,6 +53,7 @@ import beets
import beets.plugins import beets.plugins
from beets import autotag, config, importer, logging, util from beets import autotag, config, importer, logging, util
from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.autotag.hooks import AlbumInfo, TrackInfo
from beets.importer import ImportSession
from beets.library import Album, Item, Library from beets.library import Album, Item, Library
from beets.test import _common from beets.test import _common
from beets.ui.commands import TerminalImportSession from beets.ui.commands import TerminalImportSession
@ -498,13 +499,26 @@ class PluginTestCase(PluginMixin, BeetsTestCase):
pass pass
class ImportHelper: class ImportHelper(TestHelper):
"""Provides tools to setup a library, a directory containing files that are """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 to be imported and an import session. The class also provides stubs for the
autotagging library and several assertions for the library. autotagging library and several assertions for the library.
""" """
importer: importer.ImportSession resource_path = syspath(os.path.join(_common.RSRC, b"full.mp3"))
default_import_config = {
"autotag": True,
"copy": True,
"hardlink": False,
"link": False,
"move": False,
"resume": False,
"singletons": False,
"timid": True,
}
lib: Library
importer: ImportSession
@cached_property @cached_property
def import_dir(self): def import_dir(self):
@ -512,47 +526,28 @@ class ImportHelper:
os.makedirs(syspath(import_dir), exist_ok=True) os.makedirs(syspath(import_dir), exist_ok=True)
return import_dir return import_dir
def setup_beets(self): def setUp(self):
super().setup_beets() super().setUp()
self.import_media = []
self.lib.path_formats = [ self.lib.path_formats = [
("default", os.path.join("$artist", "$album", "$title")), ("default", os.path.join("$artist", "$album", "$title")),
("singleton:true", os.path.join("singletons", "$title")), ("singleton:true", os.path.join("singletons", "$title")),
("comp:true", os.path.join("compilations", "$album", "$title")), ("comp:true", os.path.join("compilations", "$album", "$title")),
] ]
def prepare_album_for_import(self, count=3): def prepare_track_for_import(
"""Creates a directory with media files to import. self,
Sets ``self.import_dir`` to the path of the directory. Also sets track_id: int,
``self.import_media`` to a list :class:`MediaFile` for all the files in album_path: bytes,
the directory. album_id: int | None = None,
) -> MediaFile:
The directory has following layout filename = bytestring_path(f"track_{track_id}.mp3")
the_album/ medium_path = os.path.join(album_path, filename)
track_1.mp3 shutil.copy(self.resource_path, syspath(medium_path))
track_2.mp3
track_3.mp3
:param count: Number of files to create
"""
album_path = os.path.join(self.import_dir, b"the_album")
os.makedirs(syspath(album_path), exist_ok=True)
resource_path = os.path.join(_common.RSRC, b"full.mp3")
album = bytestring_path("album")
album_path = os.path.join(self.import_dir, album)
os.makedirs(syspath(album_path), exist_ok=True)
self.import_media = []
for track_id in range(1, count + 1):
medium_path = os.path.join(
album_path, bytestring_path(f"track_{track_id}.mp3")
)
shutil.copy(syspath(resource_path), syspath(medium_path))
medium = MediaFile(medium_path) medium = MediaFile(medium_path)
medium.update( medium.update(
{ {
"album": "Tag Album", "album": "Tag Album" + (f" {album_id}" if album_id else ""),
"albumartist": None, "albumartist": None,
"mb_albumid": None, "mb_albumid": None,
"comp": None, "comp": None,
@ -563,79 +558,55 @@ class ImportHelper:
} }
) )
medium.save() medium.save()
self.import_media.append(medium) return medium
def _get_import_session(self, import_dir: str) -> None: def prepare_album_for_import(
self.importer = ImportSessionFixture( self, item_count: int, album_id: int | None = None
) -> None:
"""Create an album directory with media files to import.
The directory has following layout
album/
track_1.mp3
track_2.mp3
track_3.mp3
"""
album_path = os.path.join(
self.import_dir,
bytestring_path(f"album_{album_id}" if album_id else "album"),
)
os.makedirs(syspath(album_path), exist_ok=True)
mediums = [
self.prepare_track_for_import(tid, album_path, album_id=album_id)
for tid in range(1, item_count + 1)
]
self.import_media.extend(mediums)
def prepare_albums_for_import(self, count: int = 1) -> None:
album_dirs = Path(os.fsdecode(self.import_dir)).glob("album_*")
base_idx = int(str(max(album_dirs, default="0")).split("_")[-1]) + 1
for album_id in range(base_idx, count + base_idx):
self.prepare_album_for_import(1, album_id=album_id)
def _get_import_session(self, import_dir: str) -> ImportSession:
return ImportSessionFixture(
self.lib, self.lib,
loghandler=None, loghandler=None,
query=None, query=None,
paths=[import_dir], paths=[import_dir],
) )
def _setup_import_session( def setup_importer(
self, self, import_dir: str | None = None, **kwargs
import_dir=None, ) -> ImportSession:
singletons=False, config["import"].set_args({**self.default_import_config, **kwargs})
move=False, self.importer = self._get_import_session(import_dir or self.import_dir)
autotag=True, return self.importer
):
config["import"]["copy"] = True
config["import"]["delete"] = False
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"] = False
config["import"]["hardlink"] = False
self._get_import_session(import_dir or self.import_dir) def setup_singleton_importer(self, **kwargs) -> ImportSession:
return self.setup_importer(singletons=True, **kwargs)
def create_importer(self, item_count=1, album_count=1):
"""Create files to import and return corresponding session.
Copies the specified number of files to a subdirectory of
`self.temp_dir` and creates a `ImportSessionFixture` for this path.
"""
resource_path = os.path.join(_common.RSRC, b"full.mp3")
album_dirs = Path(os.fsdecode(self.import_dir)).glob("album_*")
base_idx = int(str(max(album_dirs, default="0")).split("_")[-1]) + 1
for album_id in range(base_idx, album_count + base_idx):
album = bytestring_path(f"album_{album_id}")
album_path = os.path.join(self.import_dir, album)
os.makedirs(syspath(album_path), exist_ok=True)
for track_id in range(1, item_count + 1):
medium_path = os.path.join(
album_path, bytestring_path(f"track_{track_id}.mp3")
)
shutil.copy(syspath(resource_path), syspath(medium_path))
medium = MediaFile(medium_path)
medium.update(
{
"album": f"Tag Album {album_id}",
"albumartist": None,
"mb_albumid": None,
"comp": None,
"artist": "Tag Artist",
"title": f"Tag Track {track_id}",
"track": track_id,
"mb_trackid": None,
}
)
medium.save()
config["import"]["quiet"] = True
config["import"]["autotag"] = False
config["import"]["resume"] = False
return ImportSessionFixture(
self.lib, loghandler=None, query=None, paths=[self.import_dir]
)
def assert_file_in_lib(self, *segments): def assert_file_in_lib(self, *segments):
"""Join the ``segments`` and assert that this path exists in the """Join the ``segments`` and assert that this path exists in the
@ -657,7 +628,7 @@ class ImportTestCase(ImportHelper, BeetsTestCase):
pass pass
class ImportSessionFixture(importer.ImportSession): class ImportSessionFixture(ImportSession):
"""ImportSession that can be controlled programaticaly. """ImportSession that can be controlled programaticaly.
>>> lib = Library(':memory:') >>> lib = Library(':memory:')
@ -771,9 +742,11 @@ class TerminalImportSessionFixture(TerminalImportSession):
class TerminalImportMixin(ImportHelper): class TerminalImportMixin(ImportHelper):
"""Provides_a terminal importer for the import session.""" """Provides_a terminal importer for the import session."""
def _get_import_session(self, import_dir: str) -> None: io: _common.DummyIO
def _get_import_session(self, import_dir: str) -> importer.ImportSession:
self.io.install() self.io.install()
self.importer = TerminalImportSessionFixture( return TerminalImportSessionFixture(
self.lib, self.lib,
loghandler=None, loghandler=None,
query=None, query=None,

View file

@ -434,7 +434,7 @@ def displayable_path(
return path.decode("utf-8", "ignore") return path.decode("utf-8", "ignore")
def syspath(path: Bytes_or_String, prefix: bool = True) -> Bytes_or_String: def syspath(path: Bytes_or_String, prefix: bool = True) -> str:
"""Convert a path for use by the operating system. In particular, """Convert a path for use by the operating system. In particular,
paths on Windows must receive a magic prefix and must be converted paths on Windows must receive a magic prefix and must be converted
to Unicode before they are sent to the OS. To disable the magic to Unicode before they are sent to the OS. To disable the magic

View file

@ -98,7 +98,8 @@ class ConvertTestCase(ConvertMixin, PluginTestCase):
class ImportConvertTest(ImportHelper, ConvertTestCase): class ImportConvertTest(ImportHelper, ConvertTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.importer = self.create_importer() self.prepare_album_for_import(1)
self.importer = self.setup_importer(autotag=False)
self.config["convert"] = { self.config["convert"] = {
"dest": os.path.join(self.temp_dir, b"convert"), "dest": os.path.join(self.temp_dir, b"convert"),

View file

@ -13,7 +13,6 @@
# included in all copies or substantial portions of the Software. # included in all copies or substantial portions of the Software.
import codecs import codecs
from typing import ClassVar
from unittest.mock import patch from unittest.mock import patch
from beets.dbcore.query import TrueQuery from beets.dbcore.query import TrueQuery
@ -329,17 +328,14 @@ class EditDuringImporterTestCase(
"""TODO""" """TODO"""
IGNORED = ["added", "album_id", "id", "mtime", "path"] IGNORED = ["added", "album_id", "id", "mtime", "path"]
singletons: ClassVar[bool]
def setUp(self): def setUp(self):
super().setUp() super().setUp()
# Create some mediafiles, and store them for comparison. # Create some mediafiles, and store them for comparison.
self.prepare_album_for_import() self.prepare_album_for_import(1)
self._setup_import_session(singletons=self.singletons)
self.items_orig = [Item.from_path(f.path) for f in self.import_media] self.items_orig = [Item.from_path(f.path) for f in self.import_media]
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self.matcher.matching = AutotagStub.GOOD self.matcher.matching = AutotagStub.GOOD
self.config["import"]["timid"] = True
def tearDown(self): def tearDown(self):
EditPlugin.listeners = None EditPlugin.listeners = None
@ -349,7 +345,9 @@ class EditDuringImporterTestCase(
@_common.slow_test() @_common.slow_test()
class EditDuringImporterNonSingletonTest(EditDuringImporterTestCase): class EditDuringImporterNonSingletonTest(EditDuringImporterTestCase):
singletons = False def setUp(self):
super().setUp()
self.importer = self.setup_importer()
def test_edit_apply_asis(self): def test_edit_apply_asis(self):
"""Edit the album field for all items in the library, apply changes, """Edit the album field for all items in the library, apply changes,
@ -497,7 +495,9 @@ class EditDuringImporterNonSingletonTest(EditDuringImporterTestCase):
@_common.slow_test() @_common.slow_test()
class EditDuringImporterSingletonTest(EditDuringImporterTestCase): class EditDuringImporterSingletonTest(EditDuringImporterTestCase):
singletons = True def setUp(self):
super().setUp()
self.importer = self.setup_singleton_importer()
def test_edit_apply_asis_singleton(self): def test_edit_apply_asis_singleton(self):
"""Edit the album field for all items in the library, apply changes, """Edit the album field for all items in the library, apply changes,

View file

@ -18,7 +18,6 @@
import os import os
import shutil import shutil
from typing import ClassVar
from mediafile import MediaFile from mediafile import MediaFile
@ -30,17 +29,9 @@ from beetsplug.filefilter import FileFilterPlugin
class FileFilterPluginMixin(ImportTestCase): class FileFilterPluginMixin(ImportTestCase):
singletons: ClassVar[bool]
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.__create_import_dir(2) self.__create_import_dir(2)
self._setup_import_session()
config["import"]["pretend"] = True
import_files = [self.import_dir]
self._setup_import_session(singletons=self.singletons)
self.importer.paths = import_files
def tearDown(self): def tearDown(self):
self.unload_plugins() self.unload_plugins()
@ -112,7 +103,9 @@ class FileFilterPluginMixin(ImportTestCase):
class FileFilterPluginNonSingletonTest(FileFilterPluginMixin): class FileFilterPluginNonSingletonTest(FileFilterPluginMixin):
singletons = False def setUp(self):
super().setUp()
self.importer = self.setup_importer(pretend=True)
def test_import_default(self): def test_import_default(self):
"""The default configuration should import everything.""" """The default configuration should import everything."""
@ -189,7 +182,9 @@ class FileFilterPluginNonSingletonTest(FileFilterPluginMixin):
class FileFilterPluginSingletonTest(FileFilterPluginMixin): class FileFilterPluginSingletonTest(FileFilterPluginMixin):
singletons = True def setUp(self):
super().setUp()
self.importer = self.setup_singleton_importer(pretend=True)
def test_import_global(self): def test_import_global(self):
config["filefilter"]["path"] = ".*track_1.*\\.mp3" config["filefilter"]["path"] = ".*track_1.*\\.mp3"

View file

@ -56,7 +56,7 @@ class ImportAddedTest(PluginMixin, ImportTestCase):
) )
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self.matcher.macthin = AutotagStub.GOOD self.matcher.macthin = AutotagStub.GOOD
self._setup_import_session() self.importer = self.setup_importer()
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)
def tearDown(self): def tearDown(self):
@ -113,7 +113,7 @@ class ImportAddedTest(PluginMixin, ImportTestCase):
# Newer Item path mtimes as if Beets had modified them # Newer Item path mtimes as if Beets had modified them
modify_mtimes(items_added_before.keys(), offset=10000) modify_mtimes(items_added_before.keys(), offset=10000)
# Reimport # Reimport
self._setup_import_session(import_dir=album.path) self.setup_importer(import_dir=self.libdir)
self.importer.run() self.importer.run()
# Verify the reimported items # Verify the reimported items
album = self.lib.albums().get() album = self.lib.albums().get()
@ -154,8 +154,7 @@ class ImportAddedTest(PluginMixin, ImportTestCase):
# Newer Item path mtimes as if Beets had modified them # Newer Item path mtimes as if Beets had modified them
modify_mtimes(items_added_before.keys(), offset=10000) modify_mtimes(items_added_before.keys(), offset=10000)
# Reimport # Reimport
import_dir = os.path.dirname(list(items_added_before.keys())[0]) self.setup_importer(import_dir=self.libdir, singletons=True)
self._setup_import_session(import_dir=import_dir, singletons=True)
self.importer.run() self.importer.run()
# Verify the reimported items # Verify the reimported items
items_added_after = {item.path: item.added for item in self.lib.items()} items_added_after = {item.path: item.added for item in self.lib.items()}

View file

@ -39,8 +39,8 @@ class KeyFinderTest(PluginMixin, ImportTestCase):
def test_add_key_on_import(self, command_output): def test_add_key_on_import(self, command_output):
command_output.return_value = util.CommandOutput(b"dbm", b"") command_output.return_value = util.CommandOutput(b"dbm", b"")
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
item = self.lib.items().get() item = self.lib.items().get()
self.assertEqual(item["initial_key"], "C#m") self.assertEqual(item["initial_key"], "C#m")

View file

@ -29,7 +29,7 @@ class MBSubmitPluginTest(PluginMixin, TerminalImportMixin, ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(2) self.prepare_album_for_import(2)
self._setup_import_session() self.setup_importer()
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
def tearDown(self): def tearDown(self):

View file

@ -22,6 +22,7 @@ class PermissionsPluginTest(PluginMixin, ImportTestCase):
super().setUp() super().setUp()
self.config["permissions"] = {"file": "777", "dir": "777"} self.config["permissions"] = {"file": "777", "dir": "777"}
self.prepare_album_for_import(1)
def test_permissions_on_album_imported(self): def test_permissions_on_album_imported(self):
self.do_thing(True) self.do_thing(True)
@ -44,10 +45,10 @@ class PermissionsPluginTest(PluginMixin, ImportTestCase):
& 0o777 & 0o777
) )
self.importer = self.create_importer() self.importer = self.setup_importer(autotag=False)
typs = ["file", "dir"] typs = ["file", "dir"]
track_file = (b"album_1", b"track_1.mp3") track_file = (b"album", b"track_1.mp3")
self.exp_perms = { self.exp_perms = {
True: { True: {
k: convert_perm(self.config["permissions"][k].get()) k: convert_perm(self.config["permissions"][k].get())
@ -93,8 +94,7 @@ class PermissionsPluginTest(PluginMixin, ImportTestCase):
def do_set_art(self, expect_success): def do_set_art(self, expect_success):
if platform.system() == "Windows": if platform.system() == "Windows":
self.skipTest("permissions not available on Windows") self.skipTest("permissions not available on Windows")
self.importer = self.create_importer() self.setup_importer(autotag=False).run()
self.importer.run()
album = self.lib.albums().get() album = self.lib.albums().get()
artpath = os.path.join(self.temp_dir, b"cover.jpg") artpath = os.path.join(self.temp_dir, b"cover.jpg")
touch(artpath) touch(artpath)

View file

@ -68,7 +68,8 @@ class ReplayGainTestCase(ImportTestCase):
except Exception: except Exception:
self.tearDown() self.tearDown()
self.importer = self.create_importer() self.prepare_album_for_import(1)
self.importer = self.setup_importer(autotag=False)
def tearDown(self): def tearDown(self):
self.unload_plugins() self.unload_plugins()

View file

@ -51,8 +51,8 @@ class ScrubbedImportTest(PluginMixin, ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(2) self.prepare_album_for_import(1)
self._setup_import_session(autotag=False) self.setup_importer(autotag=False)
def test_tags_not_scrubbed(self): def test_tags_not_scrubbed(self):
config["plugins"] = ["scrub"] config["plugins"] = ["scrub"]
@ -103,8 +103,8 @@ class NonAutotaggedImportTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(2) self.prepare_album_for_import(1)
self._setup_import_session(autotag=False) self.setup_importer(autotag=False)
def test_album_created_with_track_artist(self): def test_album_created_with_track_artist(self):
self.importer.run() self.importer.run()
@ -285,7 +285,7 @@ class RmTempTest(ImportTestCase):
archive_task = importer.ArchiveImportTask(zip_path) archive_task = importer.ArchiveImportTask(zip_path)
archive_task.extract() archive_task.extract()
tmp_path = archive_task.toppath tmp_path = archive_task.toppath
self._setup_import_session(autotag=False, import_dir=tmp_path) self.setup_importer(autotag=False, import_dir=tmp_path)
self.assertExists(tmp_path) self.assertExists(tmp_path)
archive_task.finalize(self) archive_task.finalize(self)
self.assertNotExists(tmp_path) self.assertNotExists(tmp_path)
@ -297,7 +297,7 @@ class ImportZipTest(ImportTestCase):
self.assertEqual(len(self.lib.items()), 0) self.assertEqual(len(self.lib.items()), 0)
self.assertEqual(len(self.lib.albums()), 0) self.assertEqual(len(self.lib.albums()), 0)
self._setup_import_session(autotag=False, import_dir=zip_path) self.setup_importer(autotag=False, import_dir=zip_path)
self.importer.run() self.importer.run()
self.assertEqual(len(self.lib.items()), 1) self.assertEqual(len(self.lib.items()), 1)
self.assertEqual(len(self.lib.albums()), 1) self.assertEqual(len(self.lib.albums()), 1)
@ -341,8 +341,7 @@ class ImportSingletonTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(1) self.prepare_album_for_import(1)
self._setup_import_session() self.setup_importer(singletons=True)
config["import"]["singletons"] = True
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
def tearDown(self): def tearDown(self):
@ -410,7 +409,7 @@ class ImportSingletonTest(ImportTestCase):
os.path.join(self.import_dir, b"album"), os.path.join(self.import_dir, b"album"),
single_path, single_path,
] ]
self._setup_import_session(singletons=False) self.setup_importer()
self.importer.paths = import_files self.importer.paths = import_files
self.importer.add_choice(importer.action.ASIS) self.importer.add_choice(importer.action.ASIS)
@ -462,7 +461,7 @@ class ImportTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(1) self.prepare_album_for_import(1)
self._setup_import_session() self.setup_importer()
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self.matcher.macthin = AutotagStub.GOOD self.matcher.macthin = AutotagStub.GOOD
@ -581,7 +580,7 @@ class ImportTest(ImportTestCase):
def test_empty_directory_warning(self): def test_empty_directory_warning(self):
import_dir = os.path.join(self.temp_dir, b"empty") import_dir = os.path.join(self.temp_dir, b"empty")
self.touch(b"non-audio", dir=import_dir) self.touch(b"non-audio", dir=import_dir)
self._setup_import_session(import_dir=import_dir) self.setup_importer(import_dir=import_dir)
with capture_log() as logs: with capture_log() as logs:
self.importer.run() self.importer.run()
@ -591,7 +590,7 @@ class ImportTest(ImportTestCase):
def test_empty_directory_singleton_warning(self): def test_empty_directory_singleton_warning(self):
import_dir = os.path.join(self.temp_dir, b"empty") import_dir = os.path.join(self.temp_dir, b"empty")
self.touch(b"non-audio", dir=import_dir) self.touch(b"non-audio", dir=import_dir)
self._setup_import_session(import_dir=import_dir, singletons=True) self.setup_importer(import_dir=import_dir, singletons=True)
with capture_log() as logs: with capture_log() as logs:
self.importer.run() self.importer.run()
@ -672,7 +671,7 @@ class ImportTracksTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(1) self.prepare_album_for_import(1)
self._setup_import_session() self.setup_importer()
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
def tearDown(self): def tearDown(self):
@ -706,7 +705,7 @@ class ImportCompilationTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(3) self.prepare_album_for_import(3)
self._setup_import_session() self.setup_importer()
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
def tearDown(self): def tearDown(self):
@ -827,55 +826,52 @@ class ImportExistingTest(ImportTestCase):
self.prepare_album_for_import(1) self.prepare_album_for_import(1)
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self._setup_import_session() self.reimporter = self.setup_importer(import_dir=self.libdir)
self.setup_importer = self.importer self.importer = self.setup_importer()
self.setup_importer.default_choice = importer.action.APPLY
self._setup_import_session(import_dir=self.libdir)
def tearDown(self): def tearDown(self):
super().tearDown() super().tearDown()
self.matcher.restore() self.matcher.restore()
def test_does_not_duplicate_item(self): def test_does_not_duplicate_item(self):
self.setup_importer.run() self.importer.run()
self.assertEqual(len(self.lib.items()), 1) self.assertEqual(len(self.lib.items()), 1)
self.importer.add_choice(importer.action.APPLY) self.reimporter.add_choice(importer.action.APPLY)
self.importer.run() self.reimporter.run()
self.assertEqual(len(self.lib.items()), 1) self.assertEqual(len(self.lib.items()), 1)
def test_does_not_duplicate_album(self): def test_does_not_duplicate_album(self):
self.setup_importer.run()
self.assertEqual(len(self.lib.albums()), 1)
self.importer.add_choice(importer.action.APPLY)
self.importer.run() self.importer.run()
self.assertEqual(len(self.lib.albums()), 1) self.assertEqual(len(self.lib.albums()), 1)
def test_does_not_duplicate_singleton_track(self): self.reimporter.add_choice(importer.action.APPLY)
self.setup_importer.add_choice(importer.action.TRACKS) self.reimporter.run()
self.setup_importer.add_choice(importer.action.APPLY) self.assertEqual(len(self.lib.albums()), 1)
self.setup_importer.run()
self.assertEqual(len(self.lib.items()), 1)
def test_does_not_duplicate_singleton_track(self):
self.importer.add_choice(importer.action.TRACKS) self.importer.add_choice(importer.action.TRACKS)
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)
self.importer.run() self.importer.run()
self.assertEqual(len(self.lib.items()), 1) self.assertEqual(len(self.lib.items()), 1)
self.reimporter.add_choice(importer.action.TRACKS)
self.reimporter.add_choice(importer.action.APPLY)
self.reimporter.run()
self.assertEqual(len(self.lib.items()), 1)
def test_asis_updates_metadata(self): def test_asis_updates_metadata(self):
self.setup_importer.run() self.importer.run()
medium = MediaFile(self.lib.items().get().path) medium = MediaFile(self.lib.items().get().path)
medium.title = "New Title" medium.title = "New Title"
medium.save() medium.save()
self.importer.add_choice(importer.action.ASIS) self.reimporter.add_choice(importer.action.ASIS)
self.importer.run() self.reimporter.run()
self.assertEqual(self.lib.items().get().title, "New Title") self.assertEqual(self.lib.items().get().title, "New Title")
def test_asis_updated_moves_file(self): def test_asis_updated_moves_file(self):
self.setup_importer.run() self.importer.run()
medium = MediaFile(self.lib.items().get().path) medium = MediaFile(self.lib.items().get().path)
medium.title = "New Title" medium.title = "New Title"
medium.save() medium.save()
@ -885,15 +881,15 @@ class ImportExistingTest(ImportTestCase):
) )
self.assert_file_in_lib(old_path) self.assert_file_in_lib(old_path)
self.importer.add_choice(importer.action.ASIS) self.reimporter.add_choice(importer.action.ASIS)
self.importer.run() self.reimporter.run()
self.assert_file_in_lib( self.assert_file_in_lib(
b"Applied Artist", b"Applied Album", b"New Title.mp3" b"Applied Artist", b"Applied Album", b"New Title.mp3"
) )
self.assert_file_not_in_lib(old_path) self.assert_file_not_in_lib(old_path)
def test_asis_updated_without_copy_does_not_move_file(self): def test_asis_updated_without_copy_does_not_move_file(self):
self.setup_importer.run() self.importer.run()
medium = MediaFile(self.lib.items().get().path) medium = MediaFile(self.lib.items().get().path)
medium.title = "New Title" medium.title = "New Title"
medium.save() medium.save()
@ -904,8 +900,8 @@ class ImportExistingTest(ImportTestCase):
self.assert_file_in_lib(old_path) self.assert_file_in_lib(old_path)
config["import"]["copy"] = False config["import"]["copy"] = False
self.importer.add_choice(importer.action.ASIS) self.reimporter.add_choice(importer.action.ASIS)
self.importer.run() self.reimporter.run()
self.assert_file_not_in_lib( self.assert_file_not_in_lib(
b"Applied Artist", b"Applied Album", b"New Title.mp3" b"Applied Artist", b"Applied Album", b"New Title.mp3"
) )
@ -913,15 +909,14 @@ class ImportExistingTest(ImportTestCase):
def test_outside_file_is_copied(self): def test_outside_file_is_copied(self):
config["import"]["copy"] = False config["import"]["copy"] = False
self.setup_importer.run() self.importer.run()
self.assert_equal_path( self.assert_equal_path(
self.lib.items().get().path, self.import_media[0].path self.lib.items().get().path, self.import_media[0].path
) )
config["import"]["copy"] = True self.reimporter = self.setup_importer()
self._setup_import_session() self.reimporter.add_choice(importer.action.APPLY)
self.importer.add_choice(importer.action.APPLY) self.reimporter.run()
self.importer.run()
new_path = os.path.join( new_path = os.path.join(
b"Applied Artist", b"Applied Album", b"Applied Track 1.mp3" b"Applied Artist", b"Applied Album", b"Applied Track 1.mp3"
) )
@ -933,14 +928,14 @@ class ImportExistingTest(ImportTestCase):
def test_outside_file_is_moved(self): def test_outside_file_is_moved(self):
config["import"]["copy"] = False config["import"]["copy"] = False
self.setup_importer.run() self.importer.run()
self.assert_equal_path( self.assert_equal_path(
self.lib.items().get().path, self.import_media[0].path self.lib.items().get().path, self.import_media[0].path
) )
self._setup_import_session(move=True) self.reimporter = self.setup_importer(move=True)
self.importer.add_choice(importer.action.APPLY) self.reimporter.add_choice(importer.action.APPLY)
self.importer.run() self.reimporter.run()
self.assertNotExists(self.import_media[0].path) self.assertNotExists(self.import_media[0].path)
@ -950,7 +945,7 @@ class GroupAlbumsImportTest(ImportTestCase):
self.prepare_album_for_import(3) self.prepare_album_for_import(3)
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self.matcher.matching = AutotagStub.NONE self.matcher.matching = AutotagStub.NONE
self._setup_import_session() self.setup_importer()
# Split tracks into two albums and use both as-is # Split tracks into two albums and use both as-is
self.importer.add_choice(importer.action.ALBUMS) self.importer.add_choice(importer.action.ALBUMS)
@ -1020,7 +1015,7 @@ class ChooseCandidateTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.prepare_album_for_import(1) self.prepare_album_for_import(1)
self._setup_import_session() self.setup_importer()
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self.matcher.matching = AutotagStub.BAD self.matcher.matching = AutotagStub.BAD
@ -1163,9 +1158,10 @@ class ImportDuplicateAlbumTest(ImportTestCase):
self.add_album_fixture(albumartist="artist", album="album") self.add_album_fixture(albumartist="artist", album="album")
# Create import session # Create import session
self.importer = self.create_importer() self.prepare_album_for_import(1)
config["import"]["autotag"] = True self.importer = self.setup_importer(
config["import"]["duplicate_keys"]["album"] = "albumartist album" duplicate_keys={"album": "albumartist album"}
)
def test_remove_duplicate_album(self): def test_remove_duplicate_album(self):
item = self.lib.items().get() item = self.lib.items().get()
@ -1190,7 +1186,7 @@ class ImportDuplicateAlbumTest(ImportTestCase):
# Imported item has the same artist and album as the one in the # Imported item has the same artist and album as the one in the
# library. # library.
import_file = os.path.join( import_file = os.path.join(
self.importer.paths[0], b"album_1", b"track_1.mp3" self.importer.paths[0], b"album", b"track_1.mp3"
) )
import_file = MediaFile(import_file) import_file = MediaFile(import_file)
import_file.artist = item["artist"] import_file.artist = item["artist"]
@ -1238,7 +1234,7 @@ class ImportDuplicateAlbumTest(ImportTestCase):
item = self.lib.items().get() item = self.lib.items().get()
import_file = MediaFile( import_file = MediaFile(
os.path.join(self.importer.paths[0], b"album_1", b"track_1.mp3") os.path.join(self.importer.paths[0], b"album", b"track_1.mp3")
) )
import_file.artist = item["artist"] import_file.artist = item["artist"]
import_file.albumartist = item["artist"] import_file.albumartist = item["artist"]
@ -1284,10 +1280,10 @@ class ImportDuplicateSingletonTest(ImportTestCase):
) )
# Import session # Import session
self.importer = self.create_importer() self.prepare_album_for_import(1)
config["import"]["autotag"] = True self.importer = self.setup_importer(
config["import"]["singletons"] = True duplicate_keys={"album": "artist title"}, singletons=True
config["import"]["duplicate_keys"]["item"] = "artist title" )
def test_remove_duplicate(self): def test_remove_duplicate(self):
item = self.lib.items().get() item = self.lib.items().get()
@ -1363,8 +1359,8 @@ class TagLogTest(BeetsTestCase):
class ResumeImportTest(ImportTestCase): class ResumeImportTest(ImportTestCase):
@patch("beets.plugins.send") @patch("beets.plugins.send")
def test_resume_album(self, plugins_send): def test_resume_album(self, plugins_send):
self.importer = self.create_importer(album_count=2) self.prepare_albums_for_import(2)
self.config["import"]["resume"] = True self.importer = self.setup_importer(autotag=False, resume=True)
# Aborts import after one album. This also ensures that we skip # Aborts import after one album. This also ensures that we skip
# the first album in the second try. # the first album in the second try.
@ -1384,9 +1380,10 @@ class ResumeImportTest(ImportTestCase):
@patch("beets.plugins.send") @patch("beets.plugins.send")
def test_resume_singleton(self, plugins_send): def test_resume_singleton(self, plugins_send):
self.importer = self.create_importer(item_count=2) self.prepare_album_for_import(2)
self.config["import"]["resume"] = True self.importer = self.setup_importer(
self.config["import"]["singletons"] = True autotag=False, resume=True, singletons=True
)
# Aborts import after one track. This also ensures that we skip # Aborts import after one track. This also ensures that we skip
# the first album in the second try. # the first album in the second try.
@ -1408,10 +1405,10 @@ class ResumeImportTest(ImportTestCase):
class IncrementalImportTest(ImportTestCase): class IncrementalImportTest(ImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.config["import"]["incremental"] = True self.prepare_album_for_import(1)
def test_incremental_album(self): def test_incremental_album(self):
importer = self.create_importer(album_count=1) importer = self.setup_importer(autotag=False, incremental=True)
importer.run() importer.run()
# Change album name so the original file would be imported again # Change album name so the original file would be imported again
@ -1420,13 +1417,13 @@ class IncrementalImportTest(ImportTestCase):
album["album"] = "edited album" album["album"] = "edited album"
album.store() album.store()
importer = self.create_importer(album_count=1)
importer.run() importer.run()
self.assertEqual(len(self.lib.albums()), 2) self.assertEqual(len(self.lib.albums()), 2)
def test_incremental_item(self): def test_incremental_item(self):
self.config["import"]["singletons"] = True importer = self.setup_importer(
importer = self.create_importer(item_count=1) autotag=False, incremental=True, singletons=True
)
importer.run() importer.run()
# Change track name so the original file would be imported again # Change track name so the original file would be imported again
@ -1435,12 +1432,11 @@ class IncrementalImportTest(ImportTestCase):
item["artist"] = "edited artist" item["artist"] = "edited artist"
item.store() item.store()
importer = self.create_importer(item_count=1)
importer.run() importer.run()
self.assertEqual(len(self.lib.items()), 2) self.assertEqual(len(self.lib.items()), 2)
def test_invalid_state_file(self): def test_invalid_state_file(self):
importer = self.create_importer() importer = self.setup_importer(autotag=False, incremental=True)
with open(self.config["statefile"].as_filename(), "wb") as f: with open(self.config["statefile"].as_filename(), "wb") as f:
f.write(b"000") f.write(b"000")
importer.run() importer.run()
@ -1648,7 +1644,7 @@ class ReimportTest(ImportTestCase):
self.matcher.restore() self.matcher.restore()
def _setup_session(self, singletons=False): def _setup_session(self, singletons=False):
self._setup_import_session(self._album().path, singletons=singletons) self.setup_importer(import_dir=self.libdir, singletons=singletons)
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)
def _album(self): def _album(self):
@ -1732,8 +1728,7 @@ class ImportPretendTest(ImportTestCase):
super().setUp() super().setUp()
self.__create_import_dir() self.__create_import_dir()
self.__create_empty_import_dir() self.__create_empty_import_dir()
self._setup_import_session() self.setup_importer(pretend=True)
config["import"]["pretend"] = True
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
self.io.install() self.io.install()
@ -1763,7 +1758,7 @@ class ImportPretendTest(ImportTestCase):
self.empty_path = path self.empty_path = path
def __run(self, import_paths, singletons=True): def __run(self, import_paths, singletons=True):
self._setup_import_session(singletons=singletons) self.setup_importer(singletons=singletons)
self.importer.paths = import_paths self.importer.paths = import_paths
with capture_log() as logs: with capture_log() as logs:
@ -1929,21 +1924,21 @@ class ImportMusicBrainzIdTest(ImportTestCase):
self.prepare_album_for_import(1) self.prepare_album_for_import(1)
def test_one_mbid_one_album(self): def test_one_mbid_one_album(self):
self.config["import"]["search_ids"] = [ self.setup_importer(
self.MB_RELEASE_PREFIX + self.ID_RELEASE_0 search_ids=[self.MB_RELEASE_PREFIX + self.ID_RELEASE_0]
] )
self._setup_import_session()
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)
self.importer.run() self.importer.run()
self.assertEqual(self.lib.albums().get().album, "VALID_RELEASE_0") self.assertEqual(self.lib.albums().get().album, "VALID_RELEASE_0")
def test_several_mbid_one_album(self): def test_several_mbid_one_album(self):
self.config["import"]["search_ids"] = [ self.setup_importer(
search_ids=[
self.MB_RELEASE_PREFIX + self.ID_RELEASE_0, self.MB_RELEASE_PREFIX + self.ID_RELEASE_0,
self.MB_RELEASE_PREFIX + self.ID_RELEASE_1, self.MB_RELEASE_PREFIX + self.ID_RELEASE_1,
] ]
self._setup_import_session() )
self.importer.add_choice(2) # Pick the 2nd best match (release 1). self.importer.add_choice(2) # Pick the 2nd best match (release 1).
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)
@ -1951,21 +1946,23 @@ class ImportMusicBrainzIdTest(ImportTestCase):
self.assertEqual(self.lib.albums().get().album, "VALID_RELEASE_1") self.assertEqual(self.lib.albums().get().album, "VALID_RELEASE_1")
def test_one_mbid_one_singleton(self): def test_one_mbid_one_singleton(self):
self.config["import"]["search_ids"] = [ self.setup_importer(
self.MB_RECORDING_PREFIX + self.ID_RECORDING_0 search_ids=[self.MB_RECORDING_PREFIX + self.ID_RECORDING_0],
] singletons=True,
self._setup_import_session(singletons=True) )
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)
self.importer.run() self.importer.run()
self.assertEqual(self.lib.items().get().title, "VALID_RECORDING_0") self.assertEqual(self.lib.items().get().title, "VALID_RECORDING_0")
def test_several_mbid_one_singleton(self): def test_several_mbid_one_singleton(self):
self.config["import"]["search_ids"] = [ self.setup_importer(
search_ids=[
self.MB_RECORDING_PREFIX + self.ID_RECORDING_0, self.MB_RECORDING_PREFIX + self.ID_RECORDING_0,
self.MB_RECORDING_PREFIX + self.ID_RECORDING_1, self.MB_RECORDING_PREFIX + self.ID_RECORDING_1,
] ],
self._setup_import_session(singletons=True) singletons=True,
)
self.importer.add_choice(2) # Pick the 2nd best match (recording 1). self.importer.add_choice(2) # Pick the 2nd best match (recording 1).
self.importer.add_choice(importer.action.APPLY) self.importer.add_choice(importer.action.APPLY)

View file

@ -135,8 +135,8 @@ class LoggingLevelTest(PluginMixin, ImportTestCase):
def test_import_stage_level0(self): def test_import_stage_level0(self):
self.config["verbose"] = 0 self.config["verbose"] = 0
with helper.capture_log() as logs: with helper.capture_log() as logs:
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
self.assertIn("dummy: warning import_stage", logs) self.assertIn("dummy: warning import_stage", logs)
self.assertNotIn("dummy: info import_stage", logs) self.assertNotIn("dummy: info import_stage", logs)
self.assertNotIn("dummy: debug import_stage", logs) self.assertNotIn("dummy: debug import_stage", logs)
@ -144,8 +144,8 @@ class LoggingLevelTest(PluginMixin, ImportTestCase):
def test_import_stage_level1(self): def test_import_stage_level1(self):
self.config["verbose"] = 1 self.config["verbose"] = 1
with helper.capture_log() as logs: with helper.capture_log() as logs:
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
self.assertIn("dummy: warning import_stage", logs) self.assertIn("dummy: warning import_stage", logs)
self.assertIn("dummy: info import_stage", logs) self.assertIn("dummy: info import_stage", logs)
self.assertNotIn("dummy: debug import_stage", logs) self.assertNotIn("dummy: debug import_stage", logs)
@ -153,8 +153,8 @@ class LoggingLevelTest(PluginMixin, ImportTestCase):
def test_import_stage_level2(self): def test_import_stage_level2(self):
self.config["verbose"] = 2 self.config["verbose"] = 2
with helper.capture_log() as logs: with helper.capture_log() as logs:
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
self.assertIn("dummy: warning import_stage", logs) self.assertIn("dummy: warning import_stage", logs)
self.assertIn("dummy: info import_stage", logs) self.assertIn("dummy: info import_stage", logs)
self.assertIn("dummy: debug import_stage", logs) self.assertIn("dummy: debug import_stage", logs)
@ -264,20 +264,20 @@ class ConcurrentEventsTest(ImportTestCase):
blog.getLogger("beets").set_global_level(blog.WARNING) blog.getLogger("beets").set_global_level(blog.WARNING)
with helper.capture_log() as logs: with helper.capture_log() as logs:
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
self.assertEqual(logs, []) self.assertEqual(logs, [])
blog.getLogger("beets").set_global_level(blog.INFO) blog.getLogger("beets").set_global_level(blog.INFO)
with helper.capture_log() as logs: with helper.capture_log() as logs:
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
for l in logs: for l in logs:
self.assertIn("import", l) self.assertIn("import", l)
self.assertIn("album", l) self.assertIn("album", l)
blog.getLogger("beets").set_global_level(blog.DEBUG) blog.getLogger("beets").set_global_level(blog.DEBUG)
with helper.capture_log() as logs: with helper.capture_log() as logs:
importer = self.create_importer() self.prepare_album_for_import(1)
importer.run() self.setup_importer(autotag=False).run()
self.assertIn("Sending event: database_change", logs) self.assertIn("Sending event: database_change", logs)

View file

@ -160,12 +160,9 @@ class ItemTypeConflictTest(PluginLoaderTestCase):
class EventsTest(PluginImportTestCase): class EventsTest(PluginImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
config["import"]["pretend"] = True
def test_import_task_created(self): def test_import_task_created(self):
import_files = [self.import_dir] self.importer = self.setup_importer(pretend=True)
self._setup_import_session(singletons=False)
self.importer.paths = import_files
with helper.capture_log() as logs: with helper.capture_log() as logs:
self.importer.run() self.importer.run()
@ -212,9 +209,7 @@ class EventsTest(PluginImportTestCase):
to_singleton_plugin = ToSingletonPlugin to_singleton_plugin = ToSingletonPlugin
self.register_plugin(to_singleton_plugin) self.register_plugin(to_singleton_plugin)
import_files = [self.import_dir] self.importer = self.setup_importer(pretend=True)
self._setup_import_session(singletons=False)
self.importer.paths = import_files
with helper.capture_log() as logs: with helper.capture_log() as logs:
self.importer.run() self.importer.run()
@ -371,7 +366,7 @@ class ListenersTest(PluginLoaderTestCase):
class PromptChoicesTest(TerminalImportMixin, PluginImportTestCase): class PromptChoicesTest(TerminalImportMixin, PluginImportTestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self._setup_import_session() self.setup_importer()
self.matcher = AutotagStub().install() self.matcher = AutotagStub().install()
# keep track of ui.input_option() calls # keep track of ui.input_option() calls
self.input_options_patcher = patch( self.input_options_patcher = patch(