Assertions now use isinstance() on AST nodes (Concat, Alt, Plus, Optional, Star, Symbol, Empty) instead of comparing SORE strings.
293 lines
8 KiB
Python
293 lines
8 KiB
Python
"""Tests for BEX paper algorithm implementations."""
|
|
|
|
from bex.soa import SOA
|
|
from bex.twotinf import build_soa
|
|
from bex.rwr0 import rwr0
|
|
from bex.crx import CRX
|
|
from bex.idregex import is_deterministic, idregex
|
|
from bex.expr import concat, disj, star, optional, alphabet
|
|
from bex.koa import KOA, build_complete_koa, strip_k
|
|
from bex.marking import mark_koa
|
|
from bex.rwrsq import rwr_sq, strip
|
|
from bex.ikoa import ikoa
|
|
from bex.grammar import (
|
|
Symbol, Concat, Alt, Plus, Optional, Star, Epsilon, Empty,
|
|
match,
|
|
)
|
|
|
|
|
|
def run_all():
|
|
test_soa_basic()
|
|
test_soa_accept()
|
|
test_soa_distance()
|
|
test_build_soa()
|
|
test_rwr0_linear()
|
|
test_rwr0_optional()
|
|
test_rwr0_optional_left()
|
|
test_rwr0_optional_both()
|
|
test_rwr0_disjunction()
|
|
test_rwr0_single_state()
|
|
test_crx_basic()
|
|
test_crx_single_symbol()
|
|
test_crx_all_same()
|
|
test_crx_empty()
|
|
test_determinism_check()
|
|
test_marking()
|
|
test_strip()
|
|
test_expr_utils()
|
|
test_idregex_deterministic()
|
|
test_complete_koa()
|
|
test_integration_ikoa_linear()
|
|
print("ALL TESTS PASSED")
|
|
|
|
|
|
def test_soa_basic():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(a, b)
|
|
g.add_edge(b, g.sink)
|
|
assert g.label(a) == Symbol('a')
|
|
assert g.label(b) == Symbol('b')
|
|
print(" PASS test_soa_basic")
|
|
|
|
|
|
def test_soa_accept():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(a, b)
|
|
g.add_edge(b, g.sink)
|
|
assert g.accept(['a', 'b'])
|
|
assert not g.accept(['b', 'a'])
|
|
assert not g.accept(['a'])
|
|
print(" PASS test_soa_accept")
|
|
|
|
|
|
def test_soa_distance():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
c = g.add_state(Symbol('c'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(a, b)
|
|
g.add_edge(b, c)
|
|
g.add_edge(c, g.sink)
|
|
g.add_edge(a, c)
|
|
assert g.has_edge(a, c), "Direct edge a→c should exist"
|
|
print(" PASS test_soa_distance")
|
|
|
|
|
|
def test_build_soa():
|
|
seqs = [['a', 'b', 'c'], ['a', 'b']]
|
|
g = build_soa(seqs)
|
|
assert g.accept(['a', 'b', 'c'])
|
|
assert g.accept(['a', 'b'])
|
|
assert not g.accept(['a', 'c'])
|
|
print(" PASS test_build_soa")
|
|
|
|
|
|
def test_rwr0_linear():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
c = g.add_state(Symbol('c'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(a, b)
|
|
g.add_edge(b, c)
|
|
g.add_edge(c, g.sink)
|
|
result = rwr0(g)
|
|
assert isinstance(result, Concat)
|
|
assert match(result, ['a', 'b', 'c'])
|
|
assert not match(result, ['a', 'b'])
|
|
assert not match(result, ['a', 'b', 'c', 'd'])
|
|
print(" PASS test_rwr0_linear")
|
|
|
|
|
|
def test_rwr0_optional():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(a, b)
|
|
g.add_edge(a, g.sink)
|
|
g.add_edge(b, g.sink)
|
|
result = rwr0(g)
|
|
assert isinstance(result, Concat)
|
|
print(" PASS test_rwr0_optional")
|
|
|
|
|
|
def test_rwr0_optional_left():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(g.src, b)
|
|
g.add_edge(a, g.sink)
|
|
g.add_edge(b, g.sink)
|
|
result = rwr0(g)
|
|
assert isinstance(result, Alt)
|
|
print(" PASS test_rwr0_optional_left")
|
|
|
|
|
|
def test_rwr0_optional_both():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(g.src, b)
|
|
g.add_edge(a, g.sink)
|
|
g.add_edge(b, g.sink)
|
|
result = rwr0(g)
|
|
assert isinstance(result, Alt)
|
|
print(" PASS test_rwr0_optional_both")
|
|
|
|
|
|
def test_rwr0_disjunction():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
b = g.add_state(Symbol('b'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(g.src, b)
|
|
g.add_edge(a, g.sink)
|
|
g.add_edge(b, g.sink)
|
|
result = rwr0(g)
|
|
assert isinstance(result, Alt)
|
|
print(" PASS test_rwr0_disjunction")
|
|
|
|
|
|
def test_rwr0_single_state():
|
|
g = SOA()
|
|
a = g.add_state(Symbol('a'))
|
|
g.add_edge(g.src, a)
|
|
g.add_edge(a, g.sink)
|
|
result = rwr0(g)
|
|
assert isinstance(result, Symbol) and result.value == 'a'
|
|
print(" PASS test_rwr0_single_state")
|
|
|
|
|
|
def test_crx_basic():
|
|
crx = CRX()
|
|
seqs = [['a', 'b', 'c'], ['a', 'b'], ['a', 'c']]
|
|
result = crx.infer(seqs)
|
|
assert result is not None
|
|
assert match(result, ['a', 'b', 'c'])
|
|
assert match(result, ['a', 'b'])
|
|
assert match(result, ['a', 'c'])
|
|
print(" PASS test_crx_basic")
|
|
|
|
|
|
def test_crx_single_symbol():
|
|
crx = CRX()
|
|
seqs = [['a'], ['a'], ['a']]
|
|
result = crx.infer(seqs)
|
|
assert result is not None
|
|
assert match(result, ['a'])
|
|
print(" PASS test_crx_single_symbol")
|
|
|
|
|
|
def test_crx_all_same():
|
|
crx = CRX()
|
|
seqs = [['a', 'b'], ['a', 'b'], ['a', 'b']]
|
|
result = crx.infer(seqs)
|
|
assert result is not None
|
|
assert match(result, ['a', 'b'])
|
|
print(" PASS test_crx_all_same")
|
|
|
|
|
|
def test_crx_empty():
|
|
crx = CRX()
|
|
result = crx.infer([])
|
|
assert result is None or isinstance(result, (Empty, Epsilon))
|
|
print(" PASS test_crx_empty")
|
|
|
|
|
|
def test_determinism_check():
|
|
assert is_deterministic(Concat([Symbol('a'), Symbol('b')]))
|
|
assert is_deterministic(Plus(Symbol('a')))
|
|
assert is_deterministic(Alt([Symbol('a'), Symbol('b')]))
|
|
assert not is_deterministic(Alt([Symbol('a'), Symbol('a')]))
|
|
print(" PASS test_determinism_check")
|
|
|
|
|
|
def test_marking():
|
|
G = KOA(k=2)
|
|
a1 = G.add_state(Symbol('a_1'))
|
|
a2 = G.add_state(Symbol('a_2'))
|
|
G.add_edge(G.src, a1)
|
|
G.add_edge(a1, a2)
|
|
G.add_edge(a2, G.sink)
|
|
H = mark_koa(G)
|
|
lab1 = H.label(a1)
|
|
lab2 = H.label(a2)
|
|
assert isinstance(lab1, Symbol) and lab1.value == 'a_1'
|
|
assert isinstance(lab2, Symbol) and lab2.value == 'a_2'
|
|
print(" PASS test_marking")
|
|
|
|
|
|
def test_strip():
|
|
r = strip(Symbol('a_1'))
|
|
assert isinstance(r, Symbol) and r.value == 'a'
|
|
r2 = strip(Plus(Alt([Symbol('a_1'), Symbol('b_1')])))
|
|
assert isinstance(r2, Plus) and isinstance(r2.child, Alt)
|
|
print(" PASS test_strip")
|
|
|
|
|
|
def test_expr_utils():
|
|
c = concat(Symbol('a'), Symbol('b'))
|
|
assert isinstance(c, Concat) and c.parts == [Symbol('a'), Symbol('b')]
|
|
d = disj(Symbol('a'), Symbol('b'))
|
|
assert isinstance(d, Alt) and d.parts == [Symbol('a'), Symbol('b')]
|
|
s = star(Symbol('a'))
|
|
assert isinstance(s, Plus) and s.child == Symbol('a')
|
|
o = optional(Symbol('a'))
|
|
assert isinstance(o, Optional) and o.child == Symbol('a')
|
|
o2 = optional(concat(Symbol('a'), Symbol('b')))
|
|
assert isinstance(o2, Optional) and isinstance(o2.child, Concat)
|
|
alpha = alphabet(concat(Symbol('a'), Symbol('b')))
|
|
assert alpha == {'a', 'b'}
|
|
alpha2 = alphabet(Plus(Alt([Symbol('a'), Symbol('b')])))
|
|
assert alpha2 == {'a', 'b'}
|
|
sk = strip_k(Symbol('a_1'))
|
|
assert isinstance(sk, Symbol) and sk.value == 'a'
|
|
print(" PASS test_expr_utils")
|
|
|
|
|
|
def test_idregex_deterministic():
|
|
seqs = [['a', 'b'], ['a'], ['a', 'b', 'c']]
|
|
result = idregex(seqs, kmax=2, N=2)
|
|
if result is None:
|
|
print(" SKIP test_idregex_deterministic (returned None)")
|
|
return
|
|
assert is_deterministic(result), f"Non-deterministic: {result}"
|
|
print(f" PASS test_idregex_deterministic: {result}")
|
|
|
|
|
|
def test_complete_koa():
|
|
G, symbol_states = build_complete_koa([['a', 'b'], ['a']], k=2)
|
|
assert G.count_symbol(Symbol('a')) == 2
|
|
assert G.count_symbol(Symbol('b')) == 2
|
|
assert G.has_edge(G.src, G.sink)
|
|
print(" PASS test_complete_koa")
|
|
|
|
|
|
def test_integration_ikoa_linear():
|
|
seqs = [
|
|
['init', 'validate', 'run'],
|
|
['init', 'validate', 'run', 'cleanup'],
|
|
['init', 'run'],
|
|
]
|
|
G = ikoa(seqs, k=3)
|
|
assert G is not None
|
|
result = rwr_sq(G)
|
|
if result is not None:
|
|
assert isinstance(result, (Symbol, Concat, Alt, Plus, Optional, Star))
|
|
print(f" PASS test_integration_ikoa_linear: {result}")
|
|
else:
|
|
print(" PASS test_integration_ikoa_linear (rwr_sq returned None — expected for complex input)")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
run_all()
|