test(pyargus): add better testing for parsing expressions

- Limit the size of the identifiers, and prevent ambiguity in the
identifiers.
This commit is contained in:
Anand Balakrishnan 2023-10-12 16:22:03 -07:00
parent 5da441db42
commit 4f33300d1c
No known key found for this signature in database
7 changed files with 31 additions and 16 deletions

View file

@ -101,7 +101,7 @@ def ruff(session: nox.Session):
@nox.session(tags=["lint", "python"]) @nox.session(tags=["lint", "python"])
def mypy(session: nox.Session): def mypy(session: nox.Session):
session.conda_install("mypy", "typing-extensions", "pytest", "hypothesis") session.conda_install("mypy", "typing-extensions", "pytest", "hypothesis", "lark")
session.env.update(ENV) session.env.update(ENV)
with session.chdir(CURRENT_DIR / "pyargus"): with session.chdir(CURRENT_DIR / "pyargus"):

View file

@ -1,24 +1,34 @@
"""Hypothesis strategies to generate Argus expressions """Hypothesis strategies to generate Argus expressions
""" """
import hypothesis.strategies as st import hypothesis.strategies as st
import lark
from hypothesis.extra.lark import from_lark from hypothesis.extra.lark import from_lark
from lark import Lark, Transformer
class T(Transformer): class Transformer(lark.Transformer):
def INT(self, tok): # noqa: N802,ANN # type: ignore def INT(self, tok: lark.Token) -> lark.Token: # noqa: N802
"Convert the value of `tok` from string to int, while maintaining line number & column." """Convert the value of `tok` from string to int, while maintaining line number & column.
Performs wrapping conversion for 64-bit integers
"""
return tok.update(value=int(tok) // 2**64) return tok.update(value=int(tok) // 2**64)
ARGUS_EXPR_GRAMMAR = Lark( ARGUS_EXPR_GRAMMAR = lark.Lark(
r""" r"""
TRUE: "true" | "TRUE" TRUE: "true" | "TRUE"
FALSE: "false" | "FALSE" FALSE: "false" | "FALSE"
BOOLEAN: TRUE | FALSE BOOLEAN: TRUE | FALSE
IDENT: ESCAPED_STRING | CNAME KEYWORD: "X" | "G" | "F" | "U" | BOOLEAN
ESCAPED_STRING: "\"" /(\w|[\t ]){1,20}/ "\""
NUM_IDENT: ESCAPED_STRING
| "num_" CNAME
BOOL_IDENT: ESCAPED_STRING
| "bool_" CNAME
num_expr: num_expr "*" num_expr num_expr: num_expr "*" num_expr
| num_expr "/" num_expr | num_expr "/" num_expr
@ -26,7 +36,7 @@ num_expr: num_expr "*" num_expr
| num_expr "-" num_expr | num_expr "-" num_expr
| "-" num_expr | "-" num_expr
| NUMBER | NUMBER
| IDENT | NUM_IDENT
| "(" num_expr ")" | "(" num_expr ")"
cmp_expr: num_expr ">=" num_expr cmp_expr: num_expr ">=" num_expr
@ -49,12 +59,11 @@ bool_expr: bool_expr "&&" bool_expr
| "F" WS_INLINE INTERVAL? bool_expr | "F" WS_INLINE INTERVAL? bool_expr
| cmp_expr | cmp_expr
| BOOLEAN | BOOLEAN
| IDENT | BOOL_IDENT
| "(" bool_expr ")" | "(" bool_expr ")"
phi: bool_expr phi: bool_expr
%import common.ESCAPED_STRING
%import common.CNAME %import common.CNAME
%import common.NUMBER %import common.NUMBER
%import common.INT %import common.INT
@ -64,10 +73,17 @@ phi: bool_expr
""", """,
start="phi", start="phi",
parser="lalr",
transformer=Transformer(),
) )
@st.composite @st.composite
def argus_expr(draw: st.DrawFn) -> str: def argus_expr(draw: st.DrawFn) -> str:
"""Strategy to generate an Argus STL expression from a pre-defined grammar""" """Strategy to generate an Argus STL expression from a pre-defined grammar"""
return draw(from_lark(ARGUS_EXPR_GRAMMAR, start="phi")) return draw(
from_lark(
ARGUS_EXPR_GRAMMAR,
start="phi",
)
)

View file

@ -1,13 +1,13 @@
import logging import logging
from hypothesis import given from hypothesis import HealthCheck, given, settings
import argus import argus
from argus.test_utils.expr_gen import argus_expr
from .utils.expr_gen import argus_expr
@given(spec=argus_expr()) @given(spec=argus_expr())
@settings(suppress_health_check=[HealthCheck.too_slow])
def test_correct_expr(spec: str) -> None: def test_correct_expr(spec: str) -> None:
try: try:
_ = argus.parse_expr(spec) _ = argus.parse_expr(spec)

View file

@ -3,8 +3,7 @@ from hypothesis import assume, given
from hypothesis import strategies as st from hypothesis import strategies as st
import argus import argus
from argus.test_utils.signals_gen import (
from .utils.signals_gen import (
constant_signal, constant_signal,
draw_index, draw_index,
empty_signal, empty_signal,