gazprea-fuzzer-python/ast_generator/test/test_ast_generator.py
ayrton 9f915bd80e Fixed conditionals rarely evaluating
Added the setting in the yaml for truthiness, and added a timeout error to the code generation.

Need to fix the generation error that prevents a large amount of code from being generated...

Took 1 hour 47 minutes
2023-11-25 11:40:46 -07:00

418 lines
14 KiB
Python

import unittest
import xml
import xml.dom.minidom
import yaml
from ast_generator.ast_generator import *
from ast_generator.utils import Variable
def reachable_return(block):
return True #TODO we actually need to check this
class TestGeneration(unittest.TestCase):
@classmethod
def setUpClass(cls):
with open("config.yaml", 'r') as stream:
props = yaml.safe_load(stream)
cls.ast_gen = AstGenerator(props)
def setUp(self):
self.ast_gen.current_nesting_depth = 0
self.ast_gen.current_control_flow_nesting_depth = 0
def test_generate_literal(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_literal('int')
self.assertIsNotNone(self.ast_gen.ast.find(GAZ_LIT_TAG))
self.assertEqual("int", self.ast_gen.ast.find(GAZ_LIT_TAG).get("type"))
self.assertIsNotNone(self.ast_gen.ast.find(GAZ_LIT_TAG).get("value"))
self.assertIsNotNone(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
def test_generate_variable(self):
out: Variable = self.ast_gen.generate_variable('int')
self.assertEqual("int", out.xml.get("type"))
self.assertIsNotNone(out.xml.get("name"))
self.assertIsNotNone(out.xml.get("mut"))
self.assertIsNotNone(ET.tostring(out.xml, 'utf-8').decode('utf-8'))
def test_generate_declaration(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_declaration()
self.assertIsNotNone(self.ast_gen.ast.find("declaration"))
decl = self.ast_gen.ast.find("declaration")
self.assertIsNotNone(decl.find("variable"))
self.assertIsNotNone(decl.find("rhs"))
# print(ET.tostring(decl, 'utf-8').decode('utf-8'))
def test_generate_assignment(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_assignment()
self.assertIsNotNone(self.ast_gen.ast.find("assignment"))
decl = self.ast_gen.ast.find("assignment")
# print(ET.tostring(decl, 'utf-8').decode('utf-8'))
self.assertIsNotNone(decl.find("variable"))
self.assertIsNotNone(decl.find("rhs"))
def test_generate_bin_operation(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_binary('+', 'int')
self.assertIsNotNone(self.ast_gen.ast.find("operator"))
operator = self.ast_gen.ast.find("operator")
self.assertEqual('+', operator.get("op"))
self.assertEqual('int', operator.get("type"))
def test_generate_unary_operation(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_unary('-', 'int')
self.assertIsNotNone(self.ast_gen.ast.find("unary"))
operator = self.ast_gen.ast.find("unary")
self.assertEqual('-', operator.get("op"))
self.assertEqual('int', operator.get("type"))
def test_generate_stream(self):
for type in ["std_input", "std_output"]:
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_in_stream()
self.assertIsNotNone(self.ast_gen.ast.find("stream"))
in_stream = self.ast_gen.ast.find("stream")
self.assertEqual("std_input", in_stream.get("type"))
lad = None
for child in in_stream.iter():
lad = child.attrib
self.assertIsNotNone(lad)
def test_generate_block(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_block()
self.assertIsNotNone(self.ast_gen.ast.find("block"))
elem = None
for child in self.ast_gen.ast.iter():
elem = child.attrib
self.assertIsNotNone(elem)
def test_generate_conditional(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_conditional()
# print(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
self.assertIsNotNone(self.ast_gen.current_ast_element.find("conditional"))
conditional = self.ast_gen.ast.find("conditional")
print(ET.tostring(conditional, 'utf-8').decode('utf-8'))
self.has_child(conditional)
block = conditional.findall("block")
self.assertEqual(2, len(block))
def has_child(self, conditional):
opts = ['operator', 'unary_operator', 'literal', 'brackets']
res = []
for i in opts:
res.append(conditional.find(i))
res_list = list(filter(lambda x: x is not None, res))
self.assertGreater(len(res_list), 0)
def test_generate_loop(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_loop()
self.assertIsNotNone(self.ast_gen.ast.find("loop"))
loop = self.ast_gen.ast.find("loop")
# print(ET.tostring(loop, 'utf-8').decode('utf-8'))
self.has_child(loop)
block = loop.findall("block")
self.assertEqual(1, len(block))
def test_generate_routine(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_routine()
self.assertIsNotNone(self.ast_gen.ast.find("procedure") or self.ast_gen.ast.find("function"))
routine = self.ast_gen.ast.find("procedure") or self.ast_gen.ast.find("function")
# print(ET.tostring(routine, 'utf-8').decode('utf-8'))
self.assertIsNotNone(routine.find("block"))
self.assertIsNotNone(routine.find("argument"))
def test_generate_function_ASSERT_RETURNS(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_routine(routine_type="function")
self.assertIsNotNone(self.ast_gen.ast.find("function"))
routine = self.ast_gen.ast.find("function")
# print(ET.tostring(routine, 'utf-8').decode('utf-8'))
self.assertIsNotNone(routine.find("block"))
self.assertIsNotNone(routine.find("argument"))
block = routine.find("block")
# print(ET.tostring(block, 'utf-8').decode('utf-8'))
rets = block.find("return")
# print(rets)
self.assertLess(0, len(rets))
self.assertTrue(reachable_return(block))
def test_generate_main(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_main()
self.assertIsNotNone(self.ast_gen.ast.find("procedure"))
out = self.ast_gen.ast.find("procedure")
# print(ET.tostring(out, 'utf-8').decode('utf-8'))
self.assertEqual("main", out.get("name"))
self.assertEqual("int", out.get("return_type"))
self.assertIsNotNone(out.find("block"))
block = out.find("block")
self.assertTrue(reachable_return(block))
self.assertIsNone(out.find("argument"))
def test_generate_ast(self):
self.ast_gen.generate_ast()
self.assertIsNotNone(self.ast_gen.ast)
print(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
procedures = self.ast_gen.ast.findall("procedure")
self.assertLess(0, len(procedures))
main = False
for proc in procedures:
if proc.get("name") == "main":
main = True
self.assertTrue(main)
def test_no_op_operation(self):
for l in range(1000):
# print("iteration: " + str(l))
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_int_expr()
# self.write_ast()
if self.ast_gen.ast.find("operator") is None:
l -= 1
continue
operator = self.ast_gen.ast.find("operator")
self.assertFalse(self.is_no_op(operator))
def test_create_global(self):
element = ET.Element("block")
self.ast_gen.ast = element
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_main()
global_block = element
global_scope = self.ast_gen.current_scope.get_top_scope()
self.assertIsNotNone(self.ast_gen.ast.find("procedure"))
self.ast_gen.current_ast_element = self.ast_gen.ast.find("procedure")
self.ast_gen.generate_global()
self.assertGreater(len(global_scope.symbols), 0)
self.assertIsNotNone(global_block.find("declaration"))
def test_generate_assignment_no_declaration(self):
for l in range(1000):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_assignment()
self.assertIsNotNone(self.ast_gen.ast.find("assignment"))
decl = self.ast_gen.ast.find("assignment")
self.assertIsNone(decl.find("declaration"))
def test_failing_assignment(self):
self.ast_gen.ast = ET.Element("block")
self.ast_gen.current_ast_element = self.ast_gen.ast
with self.assertRaises(ValueError):
self.ast_gen.generate_assignment()
print(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
self.assertIsNone(self.ast_gen.ast.find("assignment"))
def is_no_op(self, operator):
"""
recursively check if operator is a no-op
@param operator:
@return:
"""
res = False
if operator.get("op") == '':
return True
else:
lhs = operator.find("lhs")
rhs = operator.find("rhs")
if lhs is None:
if rhs.find("operator") is not None:
res = self.is_no_op(rhs.find("operator"))
elif rhs.find("unary") is not None:
res = self.is_no_op(rhs.find("unary"))
else:
if lhs.find("operator") is not None:
res = self.is_no_op(lhs.find("operator"))
elif lhs.find("unary") is not None:
res = self.is_no_op(lhs.find("unary"))
elif rhs.find("operator") is not None:
res = self.is_no_op(rhs.find("operator"))
elif rhs.find("unary") is not None:
res = self.is_no_op(rhs.find("unary"))
return res
def test_print_all_current_scope_vars(self):
element = ET.Element("block")
self.ast_gen.ast = element
self.ast_gen.current_ast_element = element
self.ast_gen.current_scope = Scope(None)
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.generate_declaration(mut='var')
self.ast_gen.print_all_current_scope_vars()
# print(ET.tostring(self.ast_gen.ast))
streams = self.ast_gen.ast.findall("stream")
self.assertEqual(4, len(streams))
def test_conditional_test_1(self):
element = ET.fromstring('<literal type="bool" value="False"/>')
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertFalse(res)
def test_conditional_test_2(self):
element = ET.fromstring('<literal type="bool" value="True"/>')
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertTrue(res)
def test_conditional_test_3(self):
with open("xml/conditional.xml", 'r') as f:
element = ET.fromstring(f.read())
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertTrue(res)
def test_conditional_test_4(self):
with open("xml/conditional_2.xml", 'r') as f:
element = ET.fromstring(f.read())
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertFalse(res)
def test_conditional_test_5(self):
with open("xml/conditional_3.xml", 'r') as f:
element = ET.fromstring(f.read())
self.assertIsNotNone(element)
res = self.ast_gen.truth_check(element, True)
self.assertTrue(res)
# def test_conditional_test_variable(self):
# element = ET.fromstring('<variable name="harold" type="int" mut="var"/>')
#
# var = Variable("harold", "int", "var")
# var.decl_xml = ET.fromstring('<literal type="bool" value="True"/>')
# self.ast_gen.current_scope.append("harold", element)
# self.assertIsNotNone(element)
#
# res = self.ast_gen.truth_check(element, True)
# self.assertTrue(res)
def write_ast(self):
dom = xml.dom.minidom.parseString(ET.tostring(self.ast_gen.ast).decode('utf-8'))
pretty: str = dom.toprettyxml()
randint = random.randint(0, 1000)
print(randint)
with open("debug/ast/debug_{}.xml".format(randint), 'w') as f:
f.write(pretty)
if __name__ == '__main__':
with open("config.yaml", 'r') as stream:
props = yaml.safe_load(stream)
ast_gen = AstGenerator(props)
for a in range(20):
ast_gen.generate_ast()
ast = ast_gen.ast
with open(f"xml/ast{a}.xml", 'x') as t:
dom = xml.dom.minidom.parseString(ET.tostring(ast).decode('utf-8'))
pretty: str = dom.toprettyxml()
repretty = ""
for line in pretty.split('\n'):
if line.startswith("<?xml"):
pass
else:
repretty += (line + '\n')
t.write(repretty)