from typing import Optional
from django.test import TestCase
from ..models import Role
from ..parsers import parse_artist
PART_EXAMPLES: list[tuple[str, list[tuple[bool, str]]]] = [
(
'Oranges and Lemons',
[
(True, 'Oranges and Lemons'),
],
),
(
'The Very Angry Dolphins',
[
(True, 'The Very Angry Dolphins'),
],
),
(
(
'Date Arisa, Yoshimura Haruka, Matsuda Satsumi, Nakatsu Mariko and '
'Kotobuki Minako'
),
[
(True, 'Date Arisa'),
(False, ', '),
(True, 'Yoshimura Haruka'),
(False, ', '),
(True, 'Matsuda Satsumi'),
(False, ', '),
(True, 'Nakatsu Mariko'),
(False, ' and '),
(True, 'Kotobuki Minako'),
],
),
(
'Takagi-san (CV: Takahashi Rie)',
[
(True, 'Takagi-san'),
(False, ' (CV: '),
(True, 'Takahashi Rie'),
(False, ')'),
],
),
(
'Miho and Kana from AIKATSU☆STARS!',
[
(True, 'Miho'),
(False, ' and '),
(True, 'Kana'),
(False, ' from '),
(True, 'AIKATSU☆STARS!'),
],
),
(
'Moe, Sunao from STAR☆ANIS',
[
(True, 'Moe'),
(False, ', '),
(True, 'Sunao'),
(False, ' from '),
(True, 'STAR☆ANIS'),
],
),
(
'Alicia from BEST FRIENDS!, Remi and Nanase',
[
(True, 'Alicia'),
(False, ' from '),
(True, 'BEST FRIENDS!'),
(False, ', '),
(True, 'Remi'),
(False, ' and '),
(True, 'Nanase'),
],
),
(
'Laala and Mirei (CV: Himika Akaneya and Yu Serizawa from i☆Ris)',
[
(True, 'Laala'),
(False, ' and '),
(True, 'Mirei'),
(False, ' (CV: '),
(True, 'Himika Akaneya'),
(False, ' and '),
(True, 'Yu Serizawa'),
(False, ' from '),
(True, 'i☆Ris'),
(False, ')'),
],
),
(
'765PRO&876PRO ALLSTARS',
[
(True, '765PRO&876PRO ALLSTARS'),
],
),
(
'Ranka Lee=Nakajima Megumi',
[
(True, 'Ranka Lee'),
(False, '='),
(True, 'Nakajima Megumi'),
],
),
(
'char = cv',
[
(True, 'char'),
(False, ' = '),
(True, 'cv'),
],
),
(
'char + cv',
[
(True, 'char'),
(False, ' + '),
(True, 'cv'),
],
),
(
'Yamagami Lucy (…) (CV: Kayano Ai) Miyoshi Saya (CV: Nakahara Mai)',
[
(True, 'Yamagami Lucy (…)'),
(False, ' (CV: '),
(True, 'Kayano Ai'),
(False, ') '),
(True, 'Miyoshi Saya'),
(False, ' (CV: '),
(True, 'Nakahara Mai'),
(False, ')'),
],
),
(
'Team.Nekokan [Neko] featuring. Amaoto Junca',
[
(True, 'Team.Nekokan [Neko]'),
(False, ' featuring. '),
(True, 'Amaoto Junca'),
],
),
(
'Lillian Weinberg (Performed by Laura Pitt-Pulford)',
[
(True, 'Lillian Weinberg'),
(False, ' (Performed by '),
(True, 'Laura Pitt-Pulford'),
(False, ')'),
],
),
(
'SawanoHiroyuki[nZk]:collab',
[
(True, 'SawanoHiroyuki[nZk]'),
(False, ':'),
(True, 'collab'),
],
),
(
'SawanoHiroyuki[nZk]:Tielle&Gemie',
[
(True, 'SawanoHiroyuki[nZk]'),
(False, ':'),
(True, 'Tielle'),
(False, '&'),
(True, 'Gemie'),
],
),
(
'SawanoHiroyuki[nZk]:someone:else',
[
(True, 'SawanoHiroyuki[nZk]'),
(False, ':'),
(True, 'someone:else'),
],
),
(
'SawanoHiroyuki[nZk]:someone&someone:else&yet another person',
[
(True, 'SawanoHiroyuki[nZk]'),
(False, ':'),
(True, 'someone'),
(False, '&'),
(True, 'someone:else'),
(False, '&'),
(True, 'yet another person'),
],
),
(
'FLOWxGRANRODEO',
[
(True, 'FLOW'),
(False, 'x'),
(True, 'GRANRODEO'),
],
),
(
'FLOWxORANGE RANGE',
[
(True, 'FLOW'),
(False, 'x'),
(True, 'ORANGE RANGE'),
],
),
(
'fu_mou (Hifumi,inc.)',
[
(True, 'fu_mou'),
(False, ' ('),
(True, 'Hifumi,inc.'),
(False, ')'),
],
),
(
'Morinaka Kazaki meets ▽▲TRiNITY▲▽',
[
(True, 'Morinaka Kazaki'),
(False, ' meets '),
(True, '▽▲TRiNITY▲▽'),
],
),
(
'Eunsol(1008), NA.ZU.NA, Ookubo Yuki',
[
(True, 'Eunsol(1008)'),
(False, ', '),
(True, 'NA.ZU.NA'),
(False, ', '),
(True, 'Ookubo Yuki'),
],
),
(
'(K)NoW_NAME:NIKIIE&Tachibana Ayaka',
[
(True, '(K)NoW_NAME'),
(False, ':'),
(True, 'NIKIIE'),
(False, '&'),
(True, 'Tachibana Ayaka'),
],
),
]
GROUP_EXAMPLES = [
('This is a group (but it only has one paren section)', False),
('This is a group (the parens (are nested) (but do not end) until the end)', True),
('This is not a group; there are no parens', False),
('This is not a group (there are parens here) but not here', False),
('This is not a group (the parens) (do not reach to the end)', False),
('This is not a group (the parens (open too many times)', False),
]
[docs]
class ArtistParserTests(TestCase):
[docs]
def test_artist_parts(self) -> None:
for string, expected_result in PART_EXAMPLES:
self.assertEqual(
[(a.is_artist, a.text) for a in parse_artist(string).chunks],
expected_result,
)
[docs]
def test_group_detection(self) -> None:
for string, first_part_should_be_group in GROUP_EXAMPLES:
parsed = parse_artist(string)
if first_part_should_be_group:
self.assertTrue(parsed.should_collapse, msg=string)
else:
self.assertFalse(parsed.should_collapse, msg=string)
ROLE_EXAMPLES: dict[str, tuple[Optional[str], Optional[str], str, str, str]] = {
'': (None, None, '', '', ''),
'unmatchable': (None, None, '', 'unmatchable', 'unmatchable'),
'Mobile Suit Gundam SEED ED3': ('Mobile Suit Gundam SEED', None, 'ED', '3', 'ED3'),
"Vivy -Fluorite Eye's Song- Character Song": (
"Vivy -Fluorite Eye's Song-",
None,
'Character Song',
'',
'Character Song',
),
'ab image song - gdm': ('ab', None, 'image song', 'gdm', 'image song - gdm'),
'Pop Team Epic Rebroadcast OP': ('Pop Team Epic', 'Rebroadcast ', 'OP', '', 'OP'),
'takt op.Destiny ED': ('takt op.Destiny', None, 'ED', '', 'ED'),
'Im OP Even at lv1 OP': ('Im OP Even at lv1', None, 'OP', '', 'OP'),
'Im OP Even at lv1 OP 1': ('Im OP Even at lv1', None, 'OP', '1', 'OP 1'),
'sasami@ganbont OP B-sIdE': ('sasami@ganbont', None, 'OP', 'B-sIdE', 'OP B-sIdE'),
'twgok ED EP07': ('twgok', None, 'ED', 'EP07', 'ED EP07'),
'a show ep4 insert': ('a show', None, '', 'ep4 insert', 'ep4 insert'),
'idfs ED EP01-03': ('idfs', None, 'ED', 'EP01-03', 'ED EP01-03'),
'idfs insert song EP01-03': (
'idfs',
None,
'insert song',
'EP01-03',
'insert song EP01-03',
),
}
[docs]
class RoleParserTests(TestCase):
[docs]
def test_role_parsing(self) -> None:
for full_tag, expected_result in ROLE_EXAMPLES.items():
role = Role(full_tag)
self.assertEqual(
(
role.anime,
role.caveat,
role.kind,
role.specifics,
role.full_role,
),
expected_result,
)