mirror of
https://github.com/beetbox/beets.git
synced 2025-12-07 00:53:08 +01:00
258 lines
9.6 KiB
Python
258 lines
9.6 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
|
|
item_info_pairs = list(zip(items, info.tracks))
|
|
config["ui"]["color"] = color
|
|
config["import"]["detail"] = True
|
|
change_dist = distance(items, info, item_info_pairs)
|
|
change_dist._penalties = {"album": [dist], "artist": [dist]}
|
|
show_change(
|
|
cur_artist,
|
|
cur_album,
|
|
autotag.AlbumMatch(
|
|
change_dist, info, dict(item_info_pairs), 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"
|