WILDCARD_PAT excluded \s (all whitespace) which prevented referencing YAML keys containing spaces (e.g. __key with spaces__). Replace \s with \t\n\r to allow space characters while still rejecting tabs and newlines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
254 lines
6.2 KiB
Python
254 lines
6.2 KiB
Python
from src.parser.parser import parse
|
|
from src.parser.ast_nodes import Template, Text, Variant, Wildcard, Variable
|
|
|
|
|
|
def test_plain_text():
|
|
result = parse("hello world")
|
|
assert isinstance(result, Template)
|
|
assert len(result.parts) == 1
|
|
assert isinstance(result.parts[0], Text)
|
|
assert result.parts[0].value == "hello world"
|
|
|
|
|
|
def test_simple_variant():
|
|
result = parse("{a|b|c}")
|
|
assert len(result.parts) == 1
|
|
v = result.parts[0]
|
|
assert isinstance(v, Variant)
|
|
assert v.min_count == 1
|
|
assert v.max_count == 1
|
|
assert v.sampler == "random"
|
|
assert len(v.options) == 3
|
|
assert v.options[0].node.parts[0].value == "a"
|
|
assert v.options[1].node.parts[0].value == "b"
|
|
assert v.options[2].node.parts[0].value == "c"
|
|
|
|
|
|
def test_text_with_variant():
|
|
result = parse("A {big|small} dog")
|
|
assert len(result.parts) == 3
|
|
assert isinstance(result.parts[0], Text)
|
|
assert isinstance(result.parts[1], Variant)
|
|
assert isinstance(result.parts[2], Text)
|
|
|
|
|
|
def test_variant_default_weight():
|
|
result = parse("{a|b}")
|
|
v = result.parts[0]
|
|
assert all(opt.weight == 1.0 for opt in v.options)
|
|
|
|
|
|
def test_empty_template():
|
|
result = parse("")
|
|
assert isinstance(result, Template)
|
|
assert result.parts == []
|
|
|
|
|
|
def test_weighted_option():
|
|
result = parse("{0.5::a|0.1::b|c}")
|
|
v = result.parts[0]
|
|
assert v.options[0].weight == 0.5
|
|
assert v.options[1].weight == 0.1
|
|
assert v.options[2].weight == 1.0
|
|
|
|
|
|
def test_exact_count():
|
|
result = parse("{2$$a|b|c}")
|
|
v = result.parts[0]
|
|
assert v.min_count == 2
|
|
assert v.max_count == 2
|
|
assert v.separator == ", "
|
|
|
|
|
|
def test_range_count():
|
|
result = parse("{1-2$$a|b|c}")
|
|
v = result.parts[0]
|
|
assert v.min_count == 1
|
|
assert v.max_count == 2
|
|
|
|
|
|
def test_range_count_no_lower():
|
|
result = parse("{-2$$a|b|c}")
|
|
v = result.parts[0]
|
|
assert v.min_count == 1
|
|
assert v.max_count == 2
|
|
|
|
|
|
def test_range_count_no_upper():
|
|
result = parse("{1-$$a|b|c}")
|
|
v = result.parts[0]
|
|
assert v.min_count == 1
|
|
assert v.max_count == 1 # upper = lower when omitted
|
|
|
|
|
|
def test_custom_separator():
|
|
result = parse("{2$$ and $$a|b|c}")
|
|
v = result.parts[0]
|
|
assert v.separator == " and "
|
|
|
|
|
|
def test_random_sampler_prefix():
|
|
result = parse("{~a|b}")
|
|
v = result.parts[0]
|
|
assert v.sampler == "random"
|
|
|
|
|
|
def test_cyclical_sampler_prefix():
|
|
result = parse("{@a|b}")
|
|
v = result.parts[0]
|
|
assert v.sampler == "cyclical"
|
|
|
|
|
|
def test_nested_variant():
|
|
result = parse("{a|{b|c}}")
|
|
v = result.parts[0]
|
|
assert len(v.options) == 2
|
|
# second option contains another variant
|
|
inner = v.options[1].node.parts[0]
|
|
assert isinstance(inner, Variant)
|
|
assert len(inner.options) == 2
|
|
|
|
|
|
def test_variant_with_whitespace():
|
|
result = parse("{\n summer\n | autumn\n | winter\n}")
|
|
v = result.parts[0]
|
|
assert len(v.options) == 3
|
|
# whitespace is stripped from option values
|
|
assert v.options[0].node.parts[0].value.strip() == "summer"
|
|
|
|
|
|
# --- Wildcards ---
|
|
|
|
|
|
def test_basic_wildcard():
|
|
result = parse("__season__")
|
|
assert len(result.parts) == 1
|
|
w = result.parts[0]
|
|
assert isinstance(w, Wildcard)
|
|
assert w.pattern == "season"
|
|
assert w.sampler == "random"
|
|
assert w.params == {}
|
|
|
|
|
|
def test_wildcard_glob():
|
|
result = parse("__colours*__")
|
|
w = result.parts[0]
|
|
assert w.pattern == "colours*"
|
|
|
|
|
|
def test_wildcard_recursive_glob():
|
|
result = parse("__artists/**__")
|
|
w = result.parts[0]
|
|
assert w.pattern == "artists/**"
|
|
|
|
|
|
def test_wildcard_random_sampler():
|
|
result = parse("__~colours__")
|
|
w = result.parts[0]
|
|
assert w.sampler == "random"
|
|
|
|
|
|
def test_wildcard_cyclical_sampler():
|
|
result = parse("__@colours__")
|
|
w = result.parts[0]
|
|
assert w.sampler == "cyclical"
|
|
|
|
|
|
def test_wildcard_with_params():
|
|
result = parse("__season_clothes(season=winter)__")
|
|
w = result.parts[0]
|
|
assert w.pattern == "season_clothes"
|
|
assert w.params == {"season": "winter"}
|
|
|
|
|
|
def test_wildcard_in_text():
|
|
result = parse("A __season__ day")
|
|
assert len(result.parts) == 3
|
|
assert isinstance(result.parts[1], Wildcard)
|
|
|
|
|
|
def test_wildcard_with_spaces_in_name():
|
|
result = parse("__key with spaces__")
|
|
w = result.parts[0]
|
|
assert isinstance(w, Wildcard)
|
|
assert w.pattern == "key with spaces"
|
|
|
|
|
|
def test_wildcard_with_spaces_in_path():
|
|
result = parse("__my category/key with spaces__")
|
|
w = result.parts[0]
|
|
assert isinstance(w, Wildcard)
|
|
assert w.pattern == "my category/key with spaces"
|
|
|
|
|
|
# --- Variables ---
|
|
|
|
|
|
def test_variable_reference():
|
|
result = parse("${name}")
|
|
v = result.parts[0]
|
|
assert isinstance(v, Variable)
|
|
assert v.name == "name"
|
|
assert v.value is None
|
|
assert v.immediate is False
|
|
assert v.default is None
|
|
|
|
|
|
def test_variable_assignment():
|
|
result = parse("${x=hello}")
|
|
v = result.parts[0]
|
|
assert isinstance(v, Variable)
|
|
assert v.name == "x"
|
|
assert v.value is not None
|
|
assert v.value.parts[0].value == "hello"
|
|
assert v.immediate is False
|
|
|
|
|
|
def test_variable_immediate_assignment():
|
|
result = parse("${x=!hello}")
|
|
v = result.parts[0]
|
|
assert v.immediate is True
|
|
assert v.value is not None
|
|
|
|
|
|
def test_variable_default():
|
|
result = parse("${season:summer}")
|
|
v = result.parts[0]
|
|
assert v.name == "season"
|
|
assert v.value is None
|
|
assert v.default is not None
|
|
assert v.default.parts[0].value == "summer"
|
|
|
|
|
|
def test_variable_with_variant_value():
|
|
result = parse("${x={a|b}}")
|
|
v = result.parts[0]
|
|
assert isinstance(v, Variable)
|
|
assert v.value is not None
|
|
inner = v.value.parts[0]
|
|
assert isinstance(inner, Variant)
|
|
|
|
|
|
def test_variable_reuse():
|
|
result = parse("${x=hello} ${x}")
|
|
assert len(result.parts) == 3 # assign, text " ", reference
|
|
assert isinstance(result.parts[0], Variable)
|
|
assert isinstance(result.parts[2], Variable)
|
|
assert result.parts[2].value is None # reference, not assign
|
|
|
|
|
|
# --- Comments ---
|
|
|
|
|
|
def test_comment_stripped():
|
|
result = parse("# this is a comment\n{a|b}")
|
|
# comment is stripped, only variant remains
|
|
assert len(result.parts) == 1
|
|
assert isinstance(result.parts[0], Variant)
|
|
|
|
|
|
def test_inline_comment():
|
|
result = parse("{a\n# comment\n|b}")
|
|
v = result.parts[0]
|
|
assert len(v.options) == 2
|