Skip to content

Commit

Permalink
CAST test upgrades (#619)
Browse files Browse the repository at this point in the history
## CAST test upgrades
This PR includes some test utilities for use when testing CAST output,
so common tests such as an assignment or an expression can be done as
single function calls. Some debug scripts are now executables. Minor
edits and code cleanup

## What's New
- Added `skema/program_analysis/CAST/matlab/tests/utils.py` with
functions for common testing tasks
- Added 3 conditional logic tests:
          `if`
          `if else`
          `if elseif else`

## Relevant issues
Related to PR #613 and #583

---------

Co-authored-by: Joseph Astier <[email protected]>
  • Loading branch information
jastier and Joseph Astier authored Nov 1, 2023
1 parent a66fba6 commit 77c96cb
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
import json
import sys
from skema.program_analysis.CAST.matlab.matlab_to_cast import MatlabToCast
Expand Down
11 changes: 4 additions & 7 deletions skema/program_analysis/CAST/matlab/matlab_to_cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,6 @@ def visit_elseif_clause(self, node):
""" return a ModelIf with comparison and body nodes. """
# get ModelIf with body nodes
mi = self.visit_else_clause(node)

# addd comparison operator
comp: Operator = get_first_child_by_type(node, "comparison_operator")
mi.expr = self.visit(comp)
Expand All @@ -630,17 +629,15 @@ def visit_elseif_clause(self, node):

def visit_else_clause(self, node):
""" Return a ModelIf with body nodes only. """
mi = ModelIf()

# get the top level body nodes
mi = ModelIf()
block = get_first_child_by_type(node, "block")
body_nodes = list()
for child in block.children:
body_node = self.visit(child)
if body_node:
body_nodes.append(body_node)
if len(body_nodes) > 0:
mi.body = body_nodes
if not mi.body:
mi.body = list()
mi.body.append(body_node)

return mi

Expand Down
36 changes: 9 additions & 27 deletions skema/program_analysis/CAST/matlab/tests/test_assignment.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
from skema.program_analysis.CAST.matlab.matlab_to_cast import MatlabToCast
from skema.program_analysis.CAST2FN.model.cast import (
Assignment,
LiteralValue,
Name,
Var
from skema.program_analysis.CAST.matlab.tests.utils import (
assert_assignment,
first_cast_node
)

# Test the CAST returned by processing the simplest MATLAB assignment

def test_assignment():
""" Tests parser assignment CAST """
""" Test CAST from MATLAB 'assignment' statement."""

source = 'x = 5;'
source = 'x = 5'

cast = MatlabToCast(source = source).out_cast
# The root of the CAST should be Assignment
assignment = first_cast_node(source)

# there should only be one CAST object in the cast output list
assert len(cast) == 1

# The root of the CAST should be assignment
node = cast[0].nodes[0].body[0]
assert isinstance(node, Assignment)

# Left branch of an assignment node is the variable
left = node.left
assert isinstance(left, Var)
assert isinstance(left.val, Name)
assert left.val.name == "x"

# Right branch of this assignment is an Integer literal
right = node.right
assert isinstance(right, LiteralValue)
assert right.value_type == "Integer"
assert right.value == "5"
# The module body should contain a single assignment node
assert_assignment(assignment, left = "x", right = "5")
48 changes: 15 additions & 33 deletions skema/program_analysis/CAST/matlab/tests/test_binary_operation.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,23 @@
from skema.program_analysis.CAST.matlab.matlab_to_cast import MatlabToCast
from skema.program_analysis.CAST2FN.model.cast import (
Assignment,
LiteralValue,
Name,
Operator,
Var
from skema.program_analysis.CAST.matlab.tests.utils import (
assert_var,
assert_expression,
first_cast_node
)
from skema.program_analysis.CAST2FN.model.cast import Assignment

# Test the CAST returned by processing the simplest MATLAB binary assignment
# Test the CAST returned by processing the simplest MATLAB binary operation

def test_binary_operation():
""" Tests parser binary operation CAST """
""" Test CAST from MATLAB binary operation statement."""

source = 'z = x + y;'
source = 'z = x + y'

cast = MatlabToCast(source = source).out_cast
# The root of the CAST should be Assignment
assignment = first_cast_node(source)
assert isinstance(assignment, Assignment)

# there should only be one CAST object in the cast output list
assert len(cast) == 1
# Left operand of this assignment node is the variable
assert_var(assignment.left, name = "z")

# The root of the CAST should be assignment
node = cast[0].nodes[0].body[0]
assert isinstance(node, Assignment)

# Left branch of an assignment node is the variable
left = node.left
assert isinstance(left, Var)
assert isinstance(left.val, Name)
assert left.val.name == "z"

# right branch of this assignment node is a binary operator
right = node.right
assert right.op == "+"
assert isinstance(right, Operator)
assert isinstance(right.operands[0], Var)
assert isinstance(right.operands[0].val, Name)
assert right.operands[0].val.name == "x"
assert isinstance(right.operands[1], Var)
assert isinstance(right.operands[1].val, Name)
assert right.operands[1].val.name == "y"
# right operand of this assignment node is a binary expression
assert_expression(assignment.right, op = "+", left = "x", right = "y")
66 changes: 66 additions & 0 deletions skema/program_analysis/CAST/matlab/tests/test_conditional_logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from skema.program_analysis.CAST.matlab.tests.utils import (
assert_assignment,
assert_expression,
first_cast_node
)
from skema.program_analysis.CAST2FN.model.cast import ModelIf

def test_if():
""" Test CAST from MATLAB 'if' conditional logic."""

source = """
if x > 5
y = 6
end
"""

mi = first_cast_node(source)
# if
assert isinstance(mi, ModelIf)
assert_expression(mi.expr, op = ">", left = "x", right = "5")
assert_assignment(mi.body[0], left="y", right = "6")

def test_if_else():
""" Test CAST from MATLAB 'if else' conditional logic."""

source = """
if x > 5
y = 6
else
y = x
end
"""

mi = first_cast_node(source)
# if
assert isinstance(mi, ModelIf)
assert_expression(mi.expr, op = ">", left = "x", right = "5")
assert_assignment(mi.body[0], left="y", right = "6")
# else
assert_assignment(mi.orelse[0], left="y", right = "x")

def test_if_elseif_else():
""" Test CAST from MATLAB 'if elseif else' conditional logic."""

source = """
if x > 5
y = 6
elseif x > 0
y = x
else
y = 0
end
"""

mi = first_cast_node(source)
# if
assert isinstance(mi, ModelIf)
assert_expression(mi.expr, op = ">", left = "x", right = "5")
assert_assignment(mi.body[0], left="y", right = "6")
# elseif
assert isinstance(mi.orelse[0], ModelIf)
assert_expression(mi.orelse[0].expr, op = ">", left = "x", right = "0")
assert_assignment(mi.orelse[0].body[0], left="y", right = "x")
# else
assert_assignment(mi.orelse[1], left="y", right = "0")

58 changes: 58 additions & 0 deletions skema/program_analysis/CAST/matlab/tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from skema.program_analysis.CAST.matlab.matlab_to_cast import MatlabToCast
from skema.program_analysis.CAST2FN.model.cast import (
Assignment,
LiteralValue,
Operator,
Module,
Name,
Var
)

def assert_var(var, name = ""):
""" Test the Var for correct type and name. """
assert isinstance(var, Var)
assert isinstance(var.val, Name)
assert var.val.name == name

def assert_literal_value(literal_value, value = ""):
""" Test the LiteralValue for correct type and value. """
assert isinstance(literal_value, LiteralValue)
assert literal_value.value == value

def assert_operand(operand, value = ""):
""" Test a Var or LiteralValue operand for correct type and value. """
if isinstance(operand, Var):
assert_var(operand, value)
elif isinstance(operand, LiteralValue):
assert_literal_value(operand, value)
else:
assert(False)

def assert_assignment(assignment, left = "", right = ""):
""" Test an Assignment correct type and operands. """
assert isinstance(assignment, Assignment)
assert_operand(assignment.left, left)
assert_operand(assignment.right, right)

def assert_expression(expression, op = "", left = "", right = ""):
""" Test an Operator for correct type, operation, and operands. """
assert isinstance(expression, Operator)
assert expression.op == op
assert_operand(expression.operands[0], left)
assert_operand(expression.operands[1], right)

def first_cast_node(source):
""" Return the first node from the first Module of MatlabToCast output """

# there should only be one CAST object in the cast output list
cast = MatlabToCast(source = source).out_cast
assert len(cast) == 1

# there should be one module in the CAST object
assert len(cast[0].nodes) == 1
module = cast[0].nodes[0]
assert isinstance(module, Module)

# currently we support one node per module. This may change
assert len(module.body) == 1
return module.body[0]
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
import sys
from tree_sitter import Language, Node, Parser
from pathlib import Path
Expand All @@ -10,8 +11,12 @@
def print_tree(node: Node, indent = ''):
"""Display the node branch in pretty format"""
for child in node.children:
print(f"{indent} node: {child.type}")
print_tree(child, indent + ' ')
if child.type == "\n":
# print(f"{indent} <nl>")
pass
else:
print(f"{indent} {child.type}")
print_tree(child, indent + ' ')

if __name__ == "__main__":
if len(sys.argv) > 1:
Expand Down

0 comments on commit 77c96cb

Please sign in to comment.