aboutsummaryrefslogtreecommitdiff
path: root/libxml2-2.9.10/python/tests/input_callback.py
blob: 495ab62f924d1567b50185951aa8240f8fce3883 (plain)
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
#!/usr/bin/python -u
#
# This tests custom input callbacks
#
import sys
import libxml2
try:
    import StringIO
    str_io = StringIO.StringIO
except:
    import io
    str_io = io.StringIO

# We implement a new scheme, py://strings/ that will reference this dictionary
pystrings = {
    'catalogs/catalog.xml' :
'''<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE catalog PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN" "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
  <rewriteSystem systemIdStartString="http://example.com/dtds/" rewritePrefix="../dtds/"/>
</catalog>''',

    'xml/sample.xml' :
'''<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root SYSTEM "http://example.com/dtds/sample.dtd">
<root>&sample.entity;</root>''',

    'dtds/sample.dtd' :
'''
<!ELEMENT root (#PCDATA)>
<!ENTITY sample.entity "replacement text">'''
}

prefix = "py://strings/"
startURL = prefix + "xml/sample.xml"
catURL = prefix + "catalogs/catalog.xml"

def my_input_cb(URI):
    if not(URI.startswith(prefix)):
        return None
    path = URI[len(prefix):]
    if path not in pystrings:
        return None
    return str_io(pystrings[path])


def run_test(desc, docpath, catalog, exp_status="verified", exp_err=[], test_callback=None,
        root_name="root", root_content="replacement text"):
    opts = libxml2.XML_PARSE_DTDLOAD | libxml2.XML_PARSE_NONET | libxml2.XML_PARSE_COMPACT
    actual_err = []

    def my_global_error_cb(ctx, msg):
        actual_err.append((-1, msg))
    def my_ctx_error_cb(arg, msg, severity, reserved):
        actual_err.append((severity, msg))

    libxml2.registerErrorHandler(my_global_error_cb, None)
    try:
        parser = libxml2.createURLParserCtxt(docpath, opts)
        parser.setErrorHandler(my_ctx_error_cb, None)
        if catalog is not None:
            parser.addLocalCatalog(catalog)
        if test_callback is not None:
            test_callback()
        parser.parseDocument()
        doc = parser.doc()
        actual_status = "loaded"
        e = doc.getRootElement()
        if e.name == root_name and e.content == root_content:
            actual_status = "verified"
        doc.freeDoc()
    except libxml2.parserError:
        actual_status = "not loaded"

    if actual_status != exp_status:
        print("Test '%s' failed: expect status '%s', actual '%s'" % (desc, exp_status, actual_status))
        sys.exit(1)
    elif actual_err != exp_err:
        print("Test '%s' failed" % desc)
        print("Expect errors:")
        for s,m in exp_err: print("  [%2d] '%s'" % (s,m))
        print("Actual errors:")
        for s,m in actual_err: print("  [%2d] '%s'" % (s,m))
        sys.exit(1)


# Check that we cannot read custom schema without custom callback
run_test(desc="Loading entity without custom callback",
        docpath=startURL, catalog=None,
        exp_status="not loaded", exp_err=[
            (-1, "I/O "),
            (-1, "warning : "),
            (-1, "failed to load external entity \"py://strings/xml/sample.xml\"\n")
            ])

# Register handler and try to load the same entity
libxml2.registerInputCallback(my_input_cb)
run_test(desc="Loading entity with custom callback",
        docpath=startURL, catalog=None,
        exp_status="loaded", exp_err=[
            (-1, "Attempt to load network entity http://example.com/dtds/sample.dtd"),
            ( 4, "Entity 'sample.entity' not defined\n")
            ])

# Register a catalog (also accessible via pystr://) and retry
run_test(desc="Loading entity with custom callback and catalog",
        docpath=startURL, catalog=catURL)

# Unregister custom callback when parser is already created
run_test(desc="Loading entity and unregistering callback",
        docpath=startURL, catalog=catURL,
        test_callback=lambda: libxml2.popInputCallbacks(),
        exp_status="loaded", exp_err=[
            ( 3, "failed to load external entity \"py://strings/dtds/sample.dtd\"\n"),
            ( 4, "Entity 'sample.entity' not defined\n")
            ])

# Try to load the document again
run_test(desc="Retry loading document after unregistering callback",
        docpath=startURL, catalog=catURL,
        exp_status="not loaded", exp_err=[
            (-1, "I/O "),
            (-1, "warning : "),
            (-1, "failed to load external entity \"py://strings/xml/sample.xml\"\n")
            ])

# But should be able to read standard I/O yet...
run_test(desc="Loading using standard i/o after unregistering callback",
        docpath="tst.xml", catalog=None,
        root_name='doc', root_content='bar')

# Now pop ALL input callbacks, should fail to load even standard I/O
try:
    while True:
        libxml2.popInputCallbacks()
except IndexError:
    pass

run_test(desc="Loading using standard i/o after unregistering all callbacks",
        docpath="tst.xml", catalog=None,
        exp_status="not loaded", exp_err=[
            (-1, "I/O "),
            (-1, "warning : "),
            (-1, "failed to load external entity \"tst.xml\"\n")
            ])

print("OK")
sys.exit(0);