MoinLight

Annotated tests/test_parser.py

315:171156732536
2022-06-03 Paul Boddie Fixed the table of contents macro to handle an absence of section entries.
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@158 3
from os import listdir
paul@104 4
from os.path import abspath, split
paul@44 5
import sys
paul@44 6
paul@104 7
# Locate and import the moinformat package.
paul@104 8
paul@44 9
dirname = split(abspath(sys.argv[0]))[0]
paul@44 10
parent = split(dirname)[0]
paul@44 11
paul@44 12
try:
paul@44 13
    import moinformat
paul@44 14
except ImportError:
paul@158 15
    if "moinformat" in listdir(parent):
paul@44 16
        sys.path.append(parent)
paul@44 17
paul@104 18
# Import specific objects.
paul@104 19
paul@165 20
from moinformat import Metadata, make_input, make_output, make_parser, \
paul@165 21
                       make_serialiser, parse, serialise
paul@83 22
from moinformat.tree.moin import Container
paul@16 23
paul@65 24
def test_input(d, s):
paul@65 25
paul@65 26
    "Compare serialised output from 'd' with its original form 's'."
paul@65 27
paul@212 28
    metadata = Metadata({
paul@212 29
        "pagename" : "TestPage",
paul@212 30
        })
paul@165 31
paul@226 32
    # Encode the input.
paul@226 33
paul@165 34
    output = make_output(metadata)
paul@104 35
    expected = output.encode(s)
paul@12 36
paul@226 37
    # Obtain and encode the output.
paul@226 38
paul@165 39
    result = serialise(d, make_serialiser(metadata))
paul@226 40
    result = output.encode(result)
paul@226 41
paul@226 42
    # Test encoded input and output.
paul@226 43
paul@104 44
    identical = result == expected
paul@38 45
paul@38 46
    if quiet:
paul@60 47
        return identical
paul@38 48
paul@60 49
    # Show output versus input comparison result.
paul@60 50
paul@60 51
    print identical
paul@26 52
    print "-" * 60
paul@104 53
    print result
paul@65 54
    if not identical:
paul@26 55
        print "-" * 60
paul@104 56
        print expected
paul@26 57
    print "-" * 60
paul@120 58
paul@120 59
    # Show HTML serialisation.
paul@120 60
paul@165 61
    metadata.set("output_format", "html")
paul@171 62
    metadata.set("mapping", {"MoinMoin" : "https://moinmo.in/"})
paul@165 63
paul@165 64
    print serialise(d, make_serialiser(metadata))
paul@26 65
    print "-" * 60
paul@26 66
    print
paul@20 67
paul@60 68
    return identical
paul@60 69
paul@65 70
def test_tree(d, t, ts):
paul@65 71
paul@65 72
    "Compare tree structure 'd' with simplified, expected form 't' from 'ts'."
paul@65 73
paul@65 74
    failing = t.test(d)
paul@65 75
paul@65 76
    if quiet:
paul@65 77
        return not failing
paul@65 78
paul@65 79
    # Show tree versus expected forms.
paul@65 80
paul@65 81
    print not failing
paul@65 82
    print "-" * 60
paul@65 83
    print d.prettyprint()
paul@65 84
    if failing:
paul@68 85
        print "-" * 60
paul@68 86
        print ts
paul@68 87
        simple, tree, error = failing
paul@68 88
        print "-" * 60
paul@68 89
        print error
paul@68 90
        print repr(simple)
paul@68 91
        print repr(tree)
paul@65 92
        print "-" * 60
paul@65 93
        print tree.prettyprint()
paul@65 94
        print "-" * 60
paul@65 95
        print simple.prettyprint()
paul@65 96
    print "-" * 60
paul@65 97
    print
paul@65 98
paul@65 99
    return not failing
paul@65 100
paul@65 101
class Node:
paul@65 102
paul@65 103
    "A simplified tree node representation."
paul@65 104
paul@65 105
    def __init__(self, name):
paul@65 106
        self.name = name
paul@65 107
        self.nodes = []
paul@65 108
paul@65 109
    def __repr__(self):
paul@68 110
        return "Node(%r, %r)" % (self.name, self.nodes)
paul@65 111
paul@65 112
    def prettyprint(self, indent=""):
paul@68 113
        l = []
paul@68 114
        l.append("%s%s%s" % (indent, self.name, len(self.nodes) and " nodes=%d" % len(self.nodes) or ""))
paul@65 115
        for node in self.nodes:
paul@65 116
            l.append(node.prettyprint(indent + "  "))
paul@65 117
        return "\n".join(l)
paul@65 118
paul@65 119
    def append(self, node):
paul@65 120
        self.nodes.append(node)
paul@65 121
paul@65 122
    def test(self, other):
paul@65 123
paul@65 124
        """
paul@65 125
        Test whether this node is considered equivalent to 'other', where
paul@65 126
        'other' is a moinparser.tree node.
paul@65 127
paul@65 128
        Return any failing tree nodes or None.
paul@65 129
        """
paul@65 130
paul@65 131
        if other.__class__.__name__ != self.name:
paul@68 132
            return self, other, "name"
paul@65 133
paul@65 134
        if isinstance(other, Container):
paul@65 135
            for node, other_node in map(None, self.nodes, other.nodes):
paul@65 136
                if node is None or other_node is None:
paul@68 137
                    return self, other, node is None and "simple" or "document"
paul@68 138
                t = node.test(other_node)
paul@68 139
                if t:
paul@68 140
                    return t
paul@68 141
        elif self.nodes:
paul@68 142
            return self, other, "empty"
paul@65 143
paul@65 144
        return None
paul@65 145
paul@65 146
def parse_tree(s):
paul@65 147
paul@65 148
    "Parse the tree structure representation in 's'."
paul@65 149
paul@65 150
    indent = 0
paul@65 151
    branches = []
paul@65 152
paul@65 153
    for line in s.split("\n"):
paul@65 154
        line = line.rstrip()
paul@65 155
        if not line:
paul@65 156
            continue
paul@65 157
paul@65 158
        new_indent = line.rfind(" ") + 1
paul@65 159
        node = Node(line[new_indent:])
paul@65 160
paul@65 161
        # Establish a branch to add nodes to.
paul@65 162
paul@65 163
        if not branches:
paul@65 164
            branches.append(node)
paul@65 165
        else:
paul@65 166
            # Note the current node as outermost branch.
paul@65 167
paul@65 168
            if new_indent > indent:
paul@65 169
                branches.append(node)
paul@65 170
            else:
paul@65 171
                # Reduced indent involves obtaining an inner branch again.
paul@65 172
paul@65 173
                while indent > new_indent:
paul@65 174
                    del branches[-1]
paul@65 175
                    indent -= 2
paul@65 176
paul@65 177
                # Note the current node as outermost branch.
paul@65 178
paul@65 179
                branches[-1] = node
paul@65 180
paul@65 181
            # Append the current node to the parent branch.
paul@65 182
paul@65 183
            branches[-2].append(node)
paul@65 184
paul@65 185
        indent = new_indent
paul@65 186
paul@65 187
    return branches[0]
paul@65 188
paul@165 189
def get_filename(filename):
paul@165 190
paul@165 191
    "Using 'filename', return the core text filename and any encoding."
paul@165 192
paul@165 193
    t = filename.split(".")
paul@165 194
    if len(t) > 2:
paul@165 195
        text_filename = ".".join(t[:2])
paul@165 196
        encoding = t[2]
paul@165 197
    else:
paul@165 198
        text_filename = filename
paul@165 199
        encoding = None
paul@165 200
paul@165 201
    return text_filename, encoding
paul@165 202
paul@163 203
def get_tree(input, tree_filename):
paul@163 204
paul@163 205
    "Using 'input', return (text, tree) for 'tree_filename'."
paul@163 206
paul@163 207
    if input.dir.exists(tree_filename):
paul@163 208
        ts = input.readfile(tree_filename)
paul@163 209
        return ts, parse_tree(ts)
paul@163 210
    else:
paul@163 211
        return None, None
paul@163 212
paul@26 213
if __name__ == "__main__":
paul@38 214
    args = sys.argv[1:]
paul@65 215
paul@210 216
    if "--help" in args:
paul@210 217
        print >>sys.stderr, """\
paul@210 218
Usage: %s [ -q ] [ <filename>... ]
paul@210 219
paul@210 220
Run the test suite or, if filenames are indicated, specific test files.
paul@210 221
The following options are supported:
paul@210 222
paul@210 223
-q          Suppress test output, reporting only success or failure
paul@210 224
--quiet     Equivalent to -q
paul@210 225
"""
paul@210 226
        sys.exit(1)
paul@210 227
paul@210 228
    for arg in ["-q", "--quiet"]:
paul@210 229
        if arg in args:
paul@210 230
            del args[args.index(arg)]
paul@210 231
            quiet = True
paul@210 232
            break
paul@210 233
    else:
paul@210 234
        quiet = False
paul@65 235
paul@165 236
    metadata = Metadata({
paul@165 237
        "input_context"     : "directory",
paul@165 238
        "input_filename"    : dirname,
paul@165 239
        })
paul@165 240
paul@104 241
    # Make an input context.
paul@104 242
paul@165 243
    input = make_input(metadata)
paul@104 244
paul@104 245
    # Obtain input filenames.
paul@104 246
paul@145 247
    filenames = args or input.dir.select_files("test*.txt*")
paul@26 248
    filenames.sort()
paul@0 249
paul@104 250
    # Process each filename, obtaining a corresponding tree definition.
paul@104 251
paul@26 252
    for filename in filenames:
paul@165 253
        text_filename, encoding = get_filename(filename)
paul@65 254
paul@165 255
        # Identify any tree-related filenames.
paul@104 256
paul@163 257
        basename = text_filename.rsplit(".", 1)[0]
paul@163 258
        tree_filename = "%s.tree" % basename
paul@163 259
        tree_exp_filename = "%s.tree-exp" % basename
paul@104 260
paul@104 261
        # Read and parse the input.
paul@104 262
paul@261 263
        s = input.readfile(filename, encoding)
paul@165 264
        p = make_parser(metadata)
paul@163 265
        d = parse(s, p)
paul@104 266
paul@163 267
        # Read and parse any tree definitions.
paul@163 268
paul@163 269
        ts, t = get_tree(input, tree_filename)
paul@163 270
        tsexp, texp = get_tree(input, tree_exp_filename)
paul@65 271
paul@104 272
        # Report the test results.
paul@104 273
paul@65 274
        if not quiet:
paul@65 275
            print filename
paul@65 276
paul@65 277
        identical = test_input(d, s)
paul@65 278
        tree_identical = ts and test_tree(d, t, ts)
paul@65 279
paul@163 280
        if tsexp:
paul@163 281
            p.evaluate_macros()
paul@163 282
            tree_exp_identical = test_tree(d, texp, tsexp)
paul@163 283
        else:
paul@163 284
            tree_exp_identical = None
paul@163 285
paul@65 286
        if quiet:
paul@163 287
            print "%s %s %s: %s" % (identical, tree_identical, tree_exp_identical, filename)
paul@3 288
paul@0 289
# vim: tabstop=4 expandtab shiftwidth=4