mirror of
https://github.com/beetbox/beets.git
synced 2025-12-07 17:16:07 +01:00
lastgenre: Test blacklist feature
- test blacklist patterns - test file format - test complex regex patterns - test invalid regex gets escaped
This commit is contained in:
parent
fb65d852c5
commit
5d333dca3b
1 changed files with 161 additions and 0 deletions
|
|
@ -14,12 +14,17 @@
|
||||||
|
|
||||||
"""Tests for the 'lastgenre' plugin."""
|
"""Tests for the 'lastgenre' plugin."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import tempfile
|
||||||
|
from collections import defaultdict
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from beets.test import _common
|
from beets.test import _common
|
||||||
from beets.test.helper import BeetsTestCase
|
from beets.test.helper import BeetsTestCase
|
||||||
|
from beets.ui import UserError
|
||||||
from beetsplug import lastgenre
|
from beetsplug import lastgenre
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -544,3 +549,159 @@ def test_get_genre(config_values, item_genre, mock_genres, expected_result):
|
||||||
# Run
|
# Run
|
||||||
res = plugin._get_genre(item)
|
res = plugin._get_genre(item)
|
||||||
assert res == expected_result
|
assert res == expected_result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"blacklist_dict, artist, genre, expected_forbidden",
|
||||||
|
[
|
||||||
|
# Global blacklist - simple word
|
||||||
|
({"*": ["spoken word"]}, "Any Artist", "spoken word", True),
|
||||||
|
({"*": ["spoken word"]}, "Any Artist", "jazz", False),
|
||||||
|
# Global blacklist - regex pattern
|
||||||
|
({"*": [".*electronic.*"]}, "Any Artist", "ambient electronic", True),
|
||||||
|
({"*": [".*electronic.*"]}, "Any Artist", "jazz", False),
|
||||||
|
# Artist-specific blacklist
|
||||||
|
({"metallica": ["metal"]}, "Metallica", "metal", True),
|
||||||
|
({"metallica": ["metal"]}, "Iron Maiden", "metal", False),
|
||||||
|
# Case insensitive matching
|
||||||
|
({"metallica": ["metal"]}, "METALLICA", "METAL", True),
|
||||||
|
# Artist-specific blacklist - exact match
|
||||||
|
({"metallica": ["^Heavy Metal$"]}, "Metallica", "classic metal", False),
|
||||||
|
# Combined global and artist-specific
|
||||||
|
(
|
||||||
|
{"*": ["spoken word"], "metallica": ["metal"]},
|
||||||
|
"Metallica",
|
||||||
|
"spoken word",
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"*": ["spoken word"], "metallica": ["metal"]},
|
||||||
|
"Metallica",
|
||||||
|
"metal",
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
# Complex regex pattern with multiple features (raw string)
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"fracture": [
|
||||||
|
r"^(heavy|black|power|death)?\s?(metal|rock)$|\w+-metal\d*$"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Fracture",
|
||||||
|
"power metal",
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
# Complex regex pattern with multiple features (regular string)
|
||||||
|
(
|
||||||
|
{"amon tobin": ["d(rum)?[ n/]*b(ass)?"]},
|
||||||
|
"Amon Tobin",
|
||||||
|
"dnb",
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
# Empty blacklist
|
||||||
|
({}, "Any Artist", "any genre", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_blacklist_patterns(blacklist_dict, artist, genre, expected_forbidden):
|
||||||
|
"""Test blacklist pattern matching logic directly."""
|
||||||
|
|
||||||
|
# Initialize plugin
|
||||||
|
plugin = lastgenre.LastGenrePlugin()
|
||||||
|
|
||||||
|
# Set up compiled blacklist directly (skipping file parsing)
|
||||||
|
compiled_blacklist = defaultdict(list)
|
||||||
|
for artist_name, patterns in blacklist_dict.items():
|
||||||
|
compiled_blacklist[artist_name.lower()] = [
|
||||||
|
re.compile(pattern) for pattern in patterns
|
||||||
|
]
|
||||||
|
|
||||||
|
plugin.blacklist = compiled_blacklist
|
||||||
|
|
||||||
|
# Test the _is_forbidden method
|
||||||
|
result = plugin._is_forbidden(genre, artist)
|
||||||
|
assert result == expected_forbidden
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"file_content, expected_blacklist",
|
||||||
|
[
|
||||||
|
# Basic artist with pattern
|
||||||
|
("metallica:\n metal", {"metallica": ["metal"]}),
|
||||||
|
# Global blacklist
|
||||||
|
("*:\n spoken word", {"*": ["spoken word"]}),
|
||||||
|
# Multiple patterns per artist
|
||||||
|
(
|
||||||
|
"metallica:\n metal\n .*rock.*",
|
||||||
|
{"metallica": ["metal", ".*rock.*"]},
|
||||||
|
),
|
||||||
|
# Comments and empty lines ignored
|
||||||
|
(
|
||||||
|
"# comment\n*:\n spoken word\n\nmetallica:\n metal",
|
||||||
|
{"*": ["spoken word"], "metallica": ["metal"]},
|
||||||
|
),
|
||||||
|
# Case insensitive artist names
|
||||||
|
("METALLICA:\n METAL", {"metallica": ["metal"]}),
|
||||||
|
# Invalid regex pattern that gets escaped
|
||||||
|
("artist:\n [invalid(regex", {"artist": ["\\[invalid\\(regex"]}),
|
||||||
|
# Empty file
|
||||||
|
("", {}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_blacklist_file_format(file_content, expected_blacklist):
|
||||||
|
"""Test blacklist file format parsing."""
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".txt", delete=False, encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
f.write(file_content)
|
||||||
|
blacklist_file = f.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
plugin = lastgenre.LastGenrePlugin()
|
||||||
|
plugin.config["blacklist"] = blacklist_file
|
||||||
|
blacklist_result = plugin._load_blacklist()
|
||||||
|
|
||||||
|
# Convert compiled regex patterns back to strings for comparison
|
||||||
|
string_blacklist = {}
|
||||||
|
for artist, compiled_patterns in blacklist_result.items():
|
||||||
|
string_blacklist[artist] = [
|
||||||
|
pattern.pattern for pattern in compiled_patterns
|
||||||
|
]
|
||||||
|
|
||||||
|
assert string_blacklist == expected_blacklist
|
||||||
|
|
||||||
|
finally:
|
||||||
|
os.unlink(blacklist_file)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"invalid_content, expected_error_message",
|
||||||
|
[
|
||||||
|
# Missing colon
|
||||||
|
("metallica\n metal", "Malformed blacklist section header"),
|
||||||
|
# Pattern before section
|
||||||
|
(" metal\nmetallica:\n heavy metal", "before any section header"),
|
||||||
|
# Unindented pattern
|
||||||
|
("metallica:\nmetal", "Malformed blacklist section header"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_blacklist_file_format_errors(invalid_content, expected_error_message):
|
||||||
|
"""Test blacklist file format error handling."""
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".txt", delete=False, encoding="utf-8"
|
||||||
|
) as f:
|
||||||
|
f.write(invalid_content)
|
||||||
|
blacklist_file = f.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
plugin = lastgenre.LastGenrePlugin()
|
||||||
|
plugin.config["blacklist"] = blacklist_file
|
||||||
|
|
||||||
|
with pytest.raises(UserError) as exc_info:
|
||||||
|
plugin._load_blacklist()
|
||||||
|
|
||||||
|
assert expected_error_message in str(exc_info.value)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
os.unlink(blacklist_file)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue