152 lines
4.9 KiB
Python
152 lines
4.9 KiB
Python
"""Tests for CRX with dotted symbols (e.g. 'foo.bar', 'request.body').
|
|
|
|
These tests expose the fundamental bug: CRX outputs grammars where '.'
|
|
means both concatenation AND is part of symbol names. The parser splits
|
|
on all dots, breaking matches.
|
|
|
|
Every test here MUST pass after the fix. They all fail before.
|
|
"""
|
|
|
|
from bex.crx import CRX
|
|
from bex.ensemble import _matches
|
|
|
|
|
|
class TestCRXDotInSymbol:
|
|
"""CRX must handle symbols containing dots."""
|
|
|
|
def test_single_dotted_symbol(self):
|
|
"""One symbol with a dot: ['foo.bar']."""
|
|
crx = CRX()
|
|
g = crx.infer([['foo.bar']])
|
|
assert g is not None
|
|
assert _matches(g, ['foo.bar'])
|
|
|
|
def test_two_dotted_symbols(self):
|
|
"""Two different dotted symbols: ['foo.bar', 'baz.qux']."""
|
|
crx = CRX()
|
|
g = crx.infer([['foo.bar', 'baz.qux']])
|
|
assert g is not None
|
|
assert _matches(g, ['foo.bar', 'baz.qux'])
|
|
|
|
def test_dotted_symbol_repeated(self):
|
|
"""Dotted symbol appears multiple times: ['foo.bar', 'foo.bar', 'baz']."""
|
|
crx = CRX()
|
|
g = crx.infer([['foo.bar', 'foo.bar', 'baz']])
|
|
assert g is not None
|
|
assert _matches(g, ['foo.bar', 'foo.bar', 'baz'])
|
|
|
|
def test_dotted_symbols_multiple_sequences(self):
|
|
"""Multiple sequences with dotted symbols."""
|
|
crx = CRX()
|
|
seqs = [
|
|
['return.capability', 'invoke.request'],
|
|
['return.capability', 'invoke'],
|
|
]
|
|
g = crx.infer(seqs)
|
|
assert g is not None
|
|
for seq in seqs:
|
|
assert _matches(g, seq), f"Grammar {g!r} should match {seq}"
|
|
|
|
def test_dotted_symbol_alternation(self):
|
|
"""Dotted symbols in alternation: ['a.b', 'a.c']."""
|
|
crx = CRX()
|
|
g = crx.infer([['a.b'], ['a.c']])
|
|
assert g is not None
|
|
assert _matches(g, ['a.b'])
|
|
assert _matches(g, ['a.c'])
|
|
|
|
def test_dotted_symbol_optional(self):
|
|
"""Optional dotted symbol: some sequences have it, some don't."""
|
|
crx = CRX()
|
|
seqs = [
|
|
['a.b', 'c'],
|
|
['c'],
|
|
]
|
|
g = crx.infer(seqs)
|
|
assert g is not None
|
|
for seq in seqs:
|
|
assert _matches(g, seq), f"Grammar {g!r} should match {seq}"
|
|
|
|
def test_dotted_symbol_plus(self):
|
|
"""Repeated dotted symbol: ['a.b', 'a.b', 'a.b', 'c']."""
|
|
crx = CRX()
|
|
seqs = [
|
|
['a.b', 'a.b', 'a.b', 'c'],
|
|
['a.b', 'a.b', 'c'],
|
|
['a.b', 'c'],
|
|
]
|
|
g = crx.infer(seqs)
|
|
assert g is not None
|
|
for seq in seqs:
|
|
assert _matches(g, seq), f"Grammar {g!r} should match {seq}"
|
|
|
|
def test_realistic_method_chain(self):
|
|
"""Realistic: method call chains like Kotlin/Python."""
|
|
crx = CRX()
|
|
seqs = [
|
|
['request.body', 'validate', 'save'],
|
|
['request.body', 'validate', 'return'],
|
|
['request.body', 'save'],
|
|
]
|
|
g = crx.infer(seqs)
|
|
assert g is not None
|
|
for seq in seqs:
|
|
assert _matches(g, seq), f"Grammar {g!r} should match {seq}"
|
|
|
|
def test_multiple_dots_in_symbol(self):
|
|
"""Symbol with multiple dots: 'a.b.c'."""
|
|
crx = CRX()
|
|
seqs = [
|
|
['a.b.c', 'd.e.f'],
|
|
['a.b.c', 'g'],
|
|
]
|
|
g = crx.infer(seqs)
|
|
assert g is not None
|
|
for seq in seqs:
|
|
assert _matches(g, seq), f"Grammar {g!r} should match {seq}"
|
|
|
|
def test_mixed_dotted_and_plain(self):
|
|
"""Mix of dotted and plain symbols."""
|
|
crx = CRX()
|
|
seqs = [
|
|
['foo.bar', 'baz', 'qux.quux'],
|
|
['foo.bar', 'baz'],
|
|
['baz', 'qux.quux'],
|
|
]
|
|
g = crx.infer(seqs)
|
|
assert g is not None
|
|
for seq in seqs:
|
|
assert _matches(g, seq), f"Grammar {g!r} should match {seq}"
|
|
|
|
|
|
class TestEnsembleDotInSymbol:
|
|
"""Ensemble (CRX + scoring) must handle dotted symbols."""
|
|
|
|
def test_ensemble_dotted_symbols(self):
|
|
"""infer_ensemble with dotted symbols."""
|
|
from bex.ensemble import infer_ensemble
|
|
seqs = [
|
|
['request.body', 'validate', 'save'],
|
|
['request.body', 'validate', 'return'],
|
|
['request.body', 'save'],
|
|
]
|
|
result = infer_ensemble(seqs)
|
|
assert result['best'] is not None
|
|
grammar = result['best']['grammar']
|
|
for seq in seqs:
|
|
assert _matches(grammar, seq), \
|
|
f"Grammar {grammar!r} should match {seq}"
|
|
|
|
def test_scoring_dotted_symbols(self):
|
|
"""lang_size_score with dotted symbols should not crash."""
|
|
from bex.ensemble import infer_ensemble
|
|
seqs = [
|
|
['foo.bar', 'baz.qux'],
|
|
['foo.bar', 'baz'],
|
|
]
|
|
result = infer_ensemble(seqs)
|
|
assert result['best'] is not None
|
|
# Just checking it doesn't crash — the score should be finite
|
|
score = result['best']['mdl_score']
|
|
assert isinstance(score, (int, float))
|
|
assert score < float('inf')
|