beets/test/ui/commands/test_import.py
Sebastian Mohr a59e41a883 tests: move command tests into dedicated files
Moved tests related to ui into own folder.
Moved 'modify' command tests into own file.
Moved 'write' command tests into own file.
Moved 'fields' command tests into own file.
Moved 'do_query' test into own file.
Moved 'list' command tests into own file.
Moved 'remove' command tests into own file.
Moved 'move' command tests into own file.
Moved 'update' command tests into own file.
Moved 'show_change' test into test_import file.
Moved 'summarize_items' test into test_import file.
Moved 'completion' command test into own file.
2025-11-03 14:00:58 +01:00

256 lines
9.5 KiB
Python

import os
import re
import unittest
from unittest.mock import Mock, patch
import pytest
from beets import autotag, config, library, ui
from beets.autotag.match import distance
from beets.test import _common
from beets.test.helper import BeetsTestCase, IOMixin
from beets.ui.commands.import_ import import_files, paths_from_logfile
from beets.ui.commands.import_.display import show_change
from beets.ui.commands.import_.session import summarize_items
class ImportTest(BeetsTestCase):
def test_quiet_timid_disallowed(self):
config["import"]["quiet"] = True
config["import"]["timid"] = True
with pytest.raises(ui.UserError):
import_files(None, [], None)
def test_parse_paths_from_logfile(self):
if os.path.__name__ == "ntpath":
logfile_content = (
"import started Wed Jun 15 23:08:26 2022\n"
"asis C:\\music\\Beatles, The\\The Beatles; C:\\music\\Beatles, The\\The Beatles\\CD 01; C:\\music\\Beatles, The\\The Beatles\\CD 02\n" # noqa: E501
"duplicate-replace C:\\music\\Bill Evans\\Trio '65\n"
"skip C:\\music\\Michael Jackson\\Bad\n"
"skip C:\\music\\Soulwax\\Any Minute Now\n"
)
expected_paths = [
"C:\\music\\Beatles, The\\The Beatles",
"C:\\music\\Michael Jackson\\Bad",
"C:\\music\\Soulwax\\Any Minute Now",
]
else:
logfile_content = (
"import started Wed Jun 15 23:08:26 2022\n"
"asis /music/Beatles, The/The Beatles; /music/Beatles, The/The Beatles/CD 01; /music/Beatles, The/The Beatles/CD 02\n" # noqa: E501
"duplicate-replace /music/Bill Evans/Trio '65\n"
"skip /music/Michael Jackson/Bad\n"
"skip /music/Soulwax/Any Minute Now\n"
)
expected_paths = [
"/music/Beatles, The/The Beatles",
"/music/Michael Jackson/Bad",
"/music/Soulwax/Any Minute Now",
]
logfile = os.path.join(self.temp_dir, b"logfile.log")
with open(logfile, mode="w") as fp:
fp.write(logfile_content)
actual_paths = list(paths_from_logfile(logfile))
assert actual_paths == expected_paths
class ShowChangeTest(IOMixin, unittest.TestCase):
def setUp(self):
super().setUp()
self.items = [_common.item()]
self.items[0].track = 1
self.items[0].path = b"/path/to/file.mp3"
self.info = autotag.AlbumInfo(
album="the album",
album_id="album id",
artist="the artist",
artist_id="artist id",
tracks=[
autotag.TrackInfo(
title="the title", track_id="track id", index=1
)
],
)
def _show_change(
self,
items=None,
info=None,
color=False,
cur_artist="the artist",
cur_album="the album",
dist=0.1,
):
"""Return an unicode string representing the changes"""
items = items or self.items
info = info or self.info
mapping = dict(zip(items, info.tracks))
config["ui"]["color"] = color
config["import"]["detail"] = True
change_dist = distance(items, info, mapping)
change_dist._penalties = {"album": [dist], "artist": [dist]}
show_change(
cur_artist,
cur_album,
autotag.AlbumMatch(change_dist, info, mapping, set(), set()),
)
return self.io.getoutput().lower()
def test_null_change(self):
msg = self._show_change()
assert "match (90.0%)" in msg
assert "album, artist" in msg
def test_album_data_change(self):
msg = self._show_change(
cur_artist="another artist", cur_album="another album"
)
assert "another artist -> the artist" in msg
assert "another album -> the album" in msg
def test_item_data_change(self):
self.items[0].title = "different"
msg = self._show_change()
assert "different" in msg
assert "the title" in msg
def test_item_data_change_with_unicode(self):
self.items[0].title = "caf\xe9"
msg = self._show_change()
assert "caf\xe9" in msg
assert "the title" in msg
def test_album_data_change_with_unicode(self):
msg = self._show_change(cur_artist="caf\xe9", cur_album="another album")
assert "caf\xe9" in msg
assert "the artist" in msg
def test_item_data_change_title_missing(self):
self.items[0].title = ""
msg = re.sub(r" +", " ", self._show_change())
assert "file.mp3" in msg
assert "the title" in msg
def test_item_data_change_title_missing_with_unicode_filename(self):
self.items[0].title = ""
self.items[0].path = "/path/to/caf\xe9.mp3".encode()
msg = re.sub(r" +", " ", self._show_change())
assert "caf\xe9.mp3" in msg or "caf.mp3" in msg
def test_colorize(self):
assert "test" == ui.uncolorize("test")
txt = ui.uncolorize("\x1b[31mtest\x1b[39;49;00m")
assert "test" == txt
txt = ui.uncolorize("\x1b[31mtest\x1b[39;49;00m test")
assert "test test" == txt
txt = ui.uncolorize("\x1b[31mtest\x1b[39;49;00mtest")
assert "testtest" == txt
txt = ui.uncolorize("test \x1b[31mtest\x1b[39;49;00m test")
assert "test test test" == txt
def test_color_split(self):
exp = ("test", "")
res = ui.color_split("test", 5)
assert exp == res
exp = ("\x1b[31mtes\x1b[39;49;00m", "\x1b[31mt\x1b[39;49;00m")
res = ui.color_split("\x1b[31mtest\x1b[39;49;00m", 3)
assert exp == res
def test_split_into_lines(self):
# Test uncolored text
txt = ui.split_into_lines("test test test", [5, 5, 5])
assert txt == ["test", "test", "test"]
# Test multiple colored texts
colored_text = "\x1b[31mtest \x1b[39;49;00m" * 3
split_txt = [
"\x1b[31mtest\x1b[39;49;00m",
"\x1b[31mtest\x1b[39;49;00m",
"\x1b[31mtest\x1b[39;49;00m",
]
txt = ui.split_into_lines(colored_text, [5, 5, 5])
assert txt == split_txt
# Test single color, multi space text
colored_text = "\x1b[31m test test test \x1b[39;49;00m"
txt = ui.split_into_lines(colored_text, [5, 5, 5])
assert txt == split_txt
# Test single color, different spacing
colored_text = "\x1b[31mtest\x1b[39;49;00mtest test test"
# ToDo: fix color_len to handle mid-text color escapes, and thus
# split colored texts over newlines (potentially with dashes?)
split_txt = ["\x1b[31mtest\x1b[39;49;00mt", "est", "test", "test"]
txt = ui.split_into_lines(colored_text, [5, 5, 5])
assert txt == split_txt
def test_album_data_change_wrap_newline(self):
# Patch ui.term_width to force wrapping
with patch("beets.ui.term_width", return_value=30):
# Test newline layout
config["ui"]["import"]["layout"] = "newline"
long_name = f"another artist with a{' very' * 10} long name"
msg = self._show_change(
cur_artist=long_name, cur_album="another album"
)
assert "artist: another artist" in msg
assert " -> the artist" in msg
assert "another album -> the album" not in msg
def test_item_data_change_wrap_column(self):
# Patch ui.term_width to force wrapping
with patch("beets.ui.term_width", return_value=54):
# Test Column layout
config["ui"]["import"]["layout"] = "column"
long_title = f"a track with a{' very' * 10} long name"
self.items[0].title = long_title
msg = self._show_change()
assert "(#1) a track (1:00) -> (#1) the title (0:00)" in msg
def test_item_data_change_wrap_newline(self):
# Patch ui.term_width to force wrapping
with patch("beets.ui.term_width", return_value=30):
config["ui"]["import"]["layout"] = "newline"
long_title = f"a track with a{' very' * 10} long name"
self.items[0].title = long_title
msg = self._show_change()
assert "(#1) a track with" in msg
assert " -> (#1) the title (0:00)" in msg
@patch("beets.library.Item.try_filesize", Mock(return_value=987))
class SummarizeItemsTest(unittest.TestCase):
def setUp(self):
super().setUp()
item = library.Item()
item.bitrate = 4321
item.length = 10 * 60 + 54
item.format = "F"
self.item = item
def test_summarize_item(self):
summary = summarize_items([], True)
assert summary == ""
summary = summarize_items([self.item], True)
assert summary == "F, 4kbps, 10:54, 987.0 B"
def test_summarize_items(self):
summary = summarize_items([], False)
assert summary == "0 items"
summary = summarize_items([self.item], False)
assert summary == "1 items, F, 4kbps, 10:54, 987.0 B"
# make a copy of self.item
i2 = self.item.copy()
summary = summarize_items([self.item, i2], False)
assert summary == "2 items, F, 4kbps, 21:48, 1.9 KiB"
i2.format = "G"
summary = summarize_items([self.item, i2], False)
assert summary == "2 items, F 1, G 1, 4kbps, 21:48, 1.9 KiB"
summary = summarize_items([self.item, i2, i2], False)
assert summary == "3 items, G 2, F 1, 4kbps, 32:42, 2.9 KiB"