Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 195 lines (165 sloc) 7.517 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
import Cython.Compiler.Errors as Errors
from Cython.CodeWriter import CodeWriter
from Cython.Compiler.TreeFragment import TreeFragment, strip_common_indent
from Cython.Compiler.Visitor import TreeVisitor, VisitorTransform
from Cython.Compiler import TreePath

import unittest
import os, sys
import tempfile

class NodeTypeWriter(TreeVisitor):
    def __init__(self):
        super(NodeTypeWriter, self).__init__()
        self._indents = 0
        self.result = []
    def visit_Node(self, node):
        if len(self.access_path) == 0:
            name = u"(root)"
        else:
            tip = self.access_path[-1]
            if tip[2] is not None:
                name = u"%s[%d]" % tip[1:3]
            else:
                name = tip[1]

        self.result.append(u" " * self._indents +
                           u"%s: %s" % (name, node.__class__.__name__))
        self._indents += 1
        self.visitchildren(node)
        self._indents -= 1

def treetypes(root):
    """Returns a string representing the tree by class names.
There's a leading and trailing whitespace so that it can be
compared by simple string comparison while still making test
cases look ok."""
    w = NodeTypeWriter()
    w.visit(root)
    return u"\n".join([u""] + w.result + [u""])

class CythonTest(unittest.TestCase):

    def setUp(self):
        self.listing_file = Errors.listing_file
        self.echo_file = Errors.echo_file
        Errors.listing_file = Errors.echo_file = None

    def tearDown(self):
        Errors.listing_file = self.listing_file
        Errors.echo_file = self.echo_file

    def assertLines(self, expected, result):
        "Checks that the given strings or lists of strings are equal line by line"
        if not isinstance(expected, list): expected = expected.split(u"\n")
        if not isinstance(result, list): result = result.split(u"\n")
        for idx, (expected_line, result_line) in enumerate(zip(expected, result)):
            self.assertEqual(expected_line, result_line, "Line %d:\nExp: %s\nGot: %s" % (idx, expected_line, result_line))
        self.assertEqual(len(expected), len(result),
            "Unmatched lines. Got:\n%s\nExpected:\n%s" % ("\n".join(expected), u"\n".join(result)))

    def codeToLines(self, tree):
        writer = CodeWriter()
        writer.write(tree)
        return writer.result.lines

    def codeToString(self, tree):
        return "\n".join(self.codeToLines(tree))

    def assertCode(self, expected, result_tree):
        result_lines = self.codeToLines(result_tree)

        expected_lines = strip_common_indent(expected.split("\n"))

        for idx, (line, expected_line) in enumerate(zip(result_lines, expected_lines)):
            self.assertEqual(expected_line, line, "Line %d:\nGot: %s\nExp: %s" % (idx, line, expected_line))
        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()
        if name.startswith("__main__."): name = name[len("__main__."):]
        name = name.replace(".", "_")
        return TreeFragment(code, name, pxds, pipeline=pipeline)

    def treetypes(self, root):
        return treetypes(root)

    def should_fail(self, func, exc_type=Exception):
        """Calls "func" and fails if it doesn't raise the right exception
(any exception by default). Also returns the exception in question.
"""
        try:
            func()
            self.fail("Expected an exception of type %r" % exc_type)
        except exc_type, e:
            self.assert_(isinstance(e, exc_type))
            return e

    def should_not_fail(self, func):
        """Calls func and succeeds if and only if no exception is raised
(i.e. converts exception raising into a failed testcase). Returns
the return value of func."""
        try:
            return func()
        except:
            self.fail(str(sys.exc_info()[1]))

class TransformTest(CythonTest):
    """
Utility base class for transform unit tests. It is based around constructing
test trees (either explicitly or by parsing a Cython code string); running
the transform, serialize it using a customized Cython serializer (with
special markup for nodes that cannot be represented in Cython),
and do a string-comparison line-by-line of the result.

To create a test case:
- Call run_pipeline. The pipeline should at least contain the transform you
are testing; pyx should be either a string (passed to the parser to
create a post-parse tree) or a node representing input to pipeline.
The result will be a transformed result.

- Check that the tree is correct. If wanted, assertCode can be used, which
takes a code string as expected, and a ModuleNode in result_tree
(it serializes the ModuleNode to a string and compares line-by-line).

All code strings are first stripped for whitespace lines and then common
indentation.

Plans: One could have a pxd dictionary parameter to run_pipeline.
"""


    def run_pipeline(self, pipeline, pyx, pxds={}):
        tree = self.fragment(pyx, pxds).root
        # Run pipeline
        for T in pipeline:
            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 'test_assert_path_exists' in directives:
            for path in directives['test_assert_path_exists']:
                if TreePath.find_first(node, path) is None:
                    Errors.error(
                        node.pos,
                        "Expected path '%s' not found in result tree" % path)
        if 'test_fail_if_path_exists' in directives:
            for path in directives['test_fail_if_path_exists']:
                if TreePath.find_first(node, path) is not None:
                    Errors.error(
                        node.pos,
                        "Unexpected path '%s' found in result tree" % path)
        self.visitchildren(node)
        return node

    visit_Node = VisitorTransform.recurse_to_children

def unpack_source_tree(tree_file, dir=None):
    if dir is None:
        dir = tempfile.mkdtemp()
    header = []
    cur_file = None
    f = open(tree_file)
    try:
        lines = f.readlines()
    finally:
        f.close()
    f = None
    for line in lines:
        if line[:5] == '#####':
            filename = line.strip().strip('#').strip().replace('/', os.path.sep)
            path = os.path.join(dir, filename)
            if not os.path.exists(os.path.dirname(path)):
                os.makedirs(os.path.dirname(path))
            if cur_file is not None:
                cur_file.close()
            cur_file = open(path, 'w')
        elif cur_file is not None:
            cur_file.write(line)
        elif line.strip() and not line.lstrip().startswith('#'):
            header.append(line)
    if cur_file is not None:
        cur_file.close()
    return dir, ''.join(header)
Something went wrong with that request. Please try again.