Commit 4dd72eb9 authored by Stefan Behnel's avatar Stefan Behnel

test support for parse tree path assertions

parent d0722d34
......@@ -163,9 +163,14 @@ class Context(object):
module_node.scope.utility_code_list.extend(scope.utility_code_list)
return module_node
test_support = []
if options.evaluate_tree_assertions:
from Cython.TestUtils import TreeAssertVisitor
test_support.append(TreeAssertVisitor())
return ([
create_parse(self),
] + self.create_pipeline(pxd=False, py=py) + [
] + self.create_pipeline(pxd=False, py=py) + test_support + [
inject_pxd_code,
abort_on_errors,
generate_pyx_code,
......@@ -592,6 +597,7 @@ class CompilationOptions(object):
verbose boolean Always print source names being compiled
quiet boolean Don't print source names in recursive mode
compiler_directives dict Overrides for pragma options (see Options.py)
evaluate_tree_assertions boolean Test support: evaluate parse tree assertions
Following options are experimental and only used on MacOSX:
......@@ -780,6 +786,7 @@ default_options = dict(
verbose = 0,
quiet = 0,
compiler_directives = {},
evaluate_tree_assertions = False,
emit_linenums = False,
)
if sys.platform == "mac":
......
......@@ -68,7 +68,11 @@ option_defaults = {
'c99_complex' : False, # Don't use macro wrappers for complex arith, not sure what to name this...
'callspec' : "",
'profile': False,
'doctesthack': False
'doctesthack': False,
# test support
'testAssertPathExists' : [],
'testFailIfPathExists' : [],
}
# Override types possibilities above, if needed
......@@ -80,7 +84,9 @@ for key, val in option_defaults.items():
option_scopes = { # defaults to available everywhere
# 'module', 'function', 'class', 'with statement'
'doctesthack' : ('module',)
'doctesthack' : ('module',),
'testAssertPathExists' : ('function',),
'testFailIfPathExists' : ('function',),
}
def parse_option_value(name, value):
......
......@@ -457,6 +457,11 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
raise PostParseError(dec.function.pos,
'The %s option takes no prepositional arguments' % optname)
return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
elif optiontype is list:
if kwds and len(kwds) != 0:
raise PostParseError(dec.function.pos,
'The %s option takes no keyword arguments' % optname)
return optname, [ str(arg.value) for arg in args ]
else:
assert False
......@@ -499,10 +504,16 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
legal_scopes = Options.option_scopes.get(name, None)
if not self.check_directive_scope(node.pos, name, 'function'):
continue
if name in optdict and isinstance(optdict[name], dict):
# only keywords can be merged, everything else
# overrides completely
optdict[name].update(value)
if name in optdict:
old_value = optdict[name]
# keywords and arg lists can be merged, everything
# else overrides completely
if isinstance(old_value, dict):
old_value.update(value)
elif isinstance(old_value, list):
old_value.extend(value)
else:
optdict[name] = value
else:
optdict[name] = value
body = StatListNode(node.pos, stats=[node])
......
......@@ -4,7 +4,8 @@ import unittest
from Cython.Compiler.ModuleNode import ModuleNode
import Cython.Compiler.Main as Main
from Cython.Compiler.TreeFragment import TreeFragment, strip_common_indent
from Cython.Compiler.Visitor import TreeVisitor
from Cython.Compiler.Visitor import TreeVisitor, VisitorTransform
from Cython.Compiler import TreePath
class NodeTypeWriter(TreeVisitor):
def __init__(self):
......@@ -74,6 +75,10 @@ class CythonTest(unittest.TestCase):
self.assertEqual(len(result_lines), len(expected_lines),
"Unmatched lines. Got:\n%s\nExpected:\n%s" % ("\n".join(result_lines), expected))
def assertNodeExists(self, path, result_tree):
self.assertNotEqual(TreePath.find_first(result_tree, path), None,
"Path '%s' not found in result tree" % path)
def fragment(self, code, pxds={}, pipeline=[]):
"Simply create a tree fragment using the name of the test-case in parse errors."
name = self.id()
......@@ -136,3 +141,28 @@ class TransformTest(CythonTest):
tree = T(tree)
return tree
class TreeAssertVisitor(VisitorTransform):
# actually, a TreeVisitor would be enough, but this needs to run
# as part of the compiler pipeline
def visit_CompilerDirectivesNode(self, node):
directives = node.directives
if 'testAssertPathExists' in directives:
for path in directives['testAssertPathExists']:
if TreePath.find_first(node, path) is None:
Errors.error(
node.pos,
"Expected path '%s' not found in result tree of node %r" % (
path, node.body))
if 'testFailIfPathExists' in directives:
for path in directives['testFailIfPathExists']:
if TreePath.find_first(node, path) is not None:
Errors.error(
node.pos,
"Unexpected path '%s' found in result tree of node %r" % (
path, node.body))
self.visitchildren(node)
return node
visit_Node = VisitorTransform.recurse_to_children
......@@ -279,7 +279,9 @@ class CythonCompileTestCase(unittest.TestCase):
annotate = annotate,
use_listing_file = False,
cplus = self.language == 'cpp',
generate_pxi = False)
generate_pxi = False,
evaluate_tree_assertions = True,
)
cython_compile(source, options=options,
full_module_name=module)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment