xref: /aosp_15_r20/external/libxml2/python/tests/input_callback.py (revision 7c5688314b92172186c154356a6374bf7684c3ca)
1*7c568831SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*7c568831SAndroid Build Coastguard Worker#
3*7c568831SAndroid Build Coastguard Worker# This tests custom input callbacks
4*7c568831SAndroid Build Coastguard Worker#
5*7c568831SAndroid Build Coastguard Workerimport sys
6*7c568831SAndroid Build Coastguard Workerimport setup_test
7*7c568831SAndroid Build Coastguard Workerimport libxml2
8*7c568831SAndroid Build Coastguard Workertry:
9*7c568831SAndroid Build Coastguard Worker    import StringIO
10*7c568831SAndroid Build Coastguard Worker    str_io = StringIO.StringIO
11*7c568831SAndroid Build Coastguard Workerexcept:
12*7c568831SAndroid Build Coastguard Worker    import io
13*7c568831SAndroid Build Coastguard Worker    str_io = io.StringIO
14*7c568831SAndroid Build Coastguard Worker
15*7c568831SAndroid Build Coastguard Worker# We implement a new scheme, py://strings/ that will reference this dictionary
16*7c568831SAndroid Build Coastguard Workerpystrings = {
17*7c568831SAndroid Build Coastguard Worker    'catalogs/catalog.xml' :
18*7c568831SAndroid Build Coastguard Worker'''<?xml version="1.0" encoding="utf-8"?>
19*7c568831SAndroid Build Coastguard Worker<!DOCTYPE catalog PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN" "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
20*7c568831SAndroid Build Coastguard Worker<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
21*7c568831SAndroid Build Coastguard Worker  <rewriteSystem systemIdStartString="http://example.com/dtds/" rewritePrefix="../dtds/"/>
22*7c568831SAndroid Build Coastguard Worker</catalog>''',
23*7c568831SAndroid Build Coastguard Worker
24*7c568831SAndroid Build Coastguard Worker    'xml/sample.xml' :
25*7c568831SAndroid Build Coastguard Worker'''<?xml version="1.0" encoding="utf-8"?>
26*7c568831SAndroid Build Coastguard Worker<!DOCTYPE root SYSTEM "http://example.com/dtds/sample.dtd">
27*7c568831SAndroid Build Coastguard Worker<root>&sample.entity;</root>''',
28*7c568831SAndroid Build Coastguard Worker
29*7c568831SAndroid Build Coastguard Worker    'dtds/sample.dtd' :
30*7c568831SAndroid Build Coastguard Worker'''
31*7c568831SAndroid Build Coastguard Worker<!ELEMENT root (#PCDATA)>
32*7c568831SAndroid Build Coastguard Worker<!ENTITY sample.entity "replacement text">'''
33*7c568831SAndroid Build Coastguard Worker}
34*7c568831SAndroid Build Coastguard Worker
35*7c568831SAndroid Build Coastguard Workerprefix = "py://strings/"
36*7c568831SAndroid Build Coastguard WorkerstartURL = prefix + "xml/sample.xml"
37*7c568831SAndroid Build Coastguard WorkercatURL = prefix + "catalogs/catalog.xml"
38*7c568831SAndroid Build Coastguard Worker
39*7c568831SAndroid Build Coastguard Workerdef my_input_cb(URI):
40*7c568831SAndroid Build Coastguard Worker    if not(URI.startswith(prefix)):
41*7c568831SAndroid Build Coastguard Worker        return None
42*7c568831SAndroid Build Coastguard Worker    path = URI[len(prefix):]
43*7c568831SAndroid Build Coastguard Worker    if path not in pystrings:
44*7c568831SAndroid Build Coastguard Worker        return None
45*7c568831SAndroid Build Coastguard Worker    return str_io(pystrings[path])
46*7c568831SAndroid Build Coastguard Worker
47*7c568831SAndroid Build Coastguard Worker
48*7c568831SAndroid Build Coastguard Workerdef run_test(desc, docpath, catalog, exp_status="verified", exp_err=[], test_callback=None,
49*7c568831SAndroid Build Coastguard Worker        root_name="root", root_content="replacement text"):
50*7c568831SAndroid Build Coastguard Worker    opts = libxml2.XML_PARSE_DTDLOAD | libxml2.XML_PARSE_NONET | libxml2.XML_PARSE_COMPACT
51*7c568831SAndroid Build Coastguard Worker    actual_err = []
52*7c568831SAndroid Build Coastguard Worker
53*7c568831SAndroid Build Coastguard Worker    def my_global_error_cb(ctx, msg):
54*7c568831SAndroid Build Coastguard Worker        actual_err.append((-1, msg))
55*7c568831SAndroid Build Coastguard Worker    def my_ctx_error_cb(arg, msg, severity, reserved):
56*7c568831SAndroid Build Coastguard Worker        actual_err.append((severity, msg))
57*7c568831SAndroid Build Coastguard Worker
58*7c568831SAndroid Build Coastguard Worker    libxml2.registerErrorHandler(my_global_error_cb, None)
59*7c568831SAndroid Build Coastguard Worker    try:
60*7c568831SAndroid Build Coastguard Worker        parser = libxml2.createURLParserCtxt(docpath, opts)
61*7c568831SAndroid Build Coastguard Worker        parser.setErrorHandler(my_ctx_error_cb, None)
62*7c568831SAndroid Build Coastguard Worker        if catalog is not None:
63*7c568831SAndroid Build Coastguard Worker            parser.addLocalCatalog(catalog)
64*7c568831SAndroid Build Coastguard Worker        if test_callback is not None:
65*7c568831SAndroid Build Coastguard Worker            test_callback()
66*7c568831SAndroid Build Coastguard Worker        parser.parseDocument()
67*7c568831SAndroid Build Coastguard Worker        doc = parser.doc()
68*7c568831SAndroid Build Coastguard Worker        actual_status = "loaded"
69*7c568831SAndroid Build Coastguard Worker        e = doc.getRootElement()
70*7c568831SAndroid Build Coastguard Worker        if e.name == root_name and e.content == root_content:
71*7c568831SAndroid Build Coastguard Worker            actual_status = "verified"
72*7c568831SAndroid Build Coastguard Worker        doc.freeDoc()
73*7c568831SAndroid Build Coastguard Worker    except libxml2.parserError:
74*7c568831SAndroid Build Coastguard Worker        actual_status = "not loaded"
75*7c568831SAndroid Build Coastguard Worker
76*7c568831SAndroid Build Coastguard Worker    if actual_status != exp_status:
77*7c568831SAndroid Build Coastguard Worker        print("Test '%s' failed: expect status '%s', actual '%s'" % (desc, exp_status, actual_status))
78*7c568831SAndroid Build Coastguard Worker        sys.exit(1)
79*7c568831SAndroid Build Coastguard Worker    elif actual_err != exp_err:
80*7c568831SAndroid Build Coastguard Worker        print("Test '%s' failed" % desc)
81*7c568831SAndroid Build Coastguard Worker        print("Expect errors:")
82*7c568831SAndroid Build Coastguard Worker        for s,m in exp_err: print("  [%2d] '%s'" % (s,m))
83*7c568831SAndroid Build Coastguard Worker        print("Actual errors:")
84*7c568831SAndroid Build Coastguard Worker        for s,m in actual_err: print("  [%2d] '%s'" % (s,m))
85*7c568831SAndroid Build Coastguard Worker        sys.exit(1)
86*7c568831SAndroid Build Coastguard Worker
87*7c568831SAndroid Build Coastguard Worker
88*7c568831SAndroid Build Coastguard Worker# Check that we cannot read custom schema without custom callback
89*7c568831SAndroid Build Coastguard Workerrun_test(desc="Loading entity without custom callback",
90*7c568831SAndroid Build Coastguard Worker        docpath=startURL, catalog=None,
91*7c568831SAndroid Build Coastguard Worker        exp_status="not loaded", exp_err=[
92*7c568831SAndroid Build Coastguard Worker            (-1, "I/O "),
93*7c568831SAndroid Build Coastguard Worker            (-1, "warning : "),
94*7c568831SAndroid Build Coastguard Worker            (-1, "failed to load \"py://strings/xml/sample.xml\": No such file or directory\n")
95*7c568831SAndroid Build Coastguard Worker            ])
96*7c568831SAndroid Build Coastguard Worker
97*7c568831SAndroid Build Coastguard Worker# Register handler and try to load the same entity
98*7c568831SAndroid Build Coastguard Workerlibxml2.registerInputCallback(my_input_cb)
99*7c568831SAndroid Build Coastguard Workerrun_test(desc="Loading entity with custom callback",
100*7c568831SAndroid Build Coastguard Worker        docpath=startURL, catalog=None,
101*7c568831SAndroid Build Coastguard Worker        exp_status="loaded", exp_err=[
102*7c568831SAndroid Build Coastguard Worker            (  4, 'failed to load "http://example.com/dtds/sample.dtd": Attempt to load network entity\n'),
103*7c568831SAndroid Build Coastguard Worker            (  4, "Entity 'sample.entity' not defined\n")
104*7c568831SAndroid Build Coastguard Worker            ])
105*7c568831SAndroid Build Coastguard Worker
106*7c568831SAndroid Build Coastguard Worker# Register a catalog (also accessible via pystr://) and retry
107*7c568831SAndroid Build Coastguard Workerrun_test(desc="Loading entity with custom callback and catalog",
108*7c568831SAndroid Build Coastguard Worker        docpath=startURL, catalog=catURL)
109*7c568831SAndroid Build Coastguard Worker
110*7c568831SAndroid Build Coastguard Worker# Unregister custom callback when parser is already created
111*7c568831SAndroid Build Coastguard Workerrun_test(desc="Loading entity and unregistering callback",
112*7c568831SAndroid Build Coastguard Worker        docpath=startURL, catalog=catURL,
113*7c568831SAndroid Build Coastguard Worker        test_callback=lambda: libxml2.popInputCallbacks(),
114*7c568831SAndroid Build Coastguard Worker        exp_status="loaded", exp_err=[
115*7c568831SAndroid Build Coastguard Worker            ( 3, "failed to load \"py://strings/dtds/sample.dtd\": No such file or directory\n"),
116*7c568831SAndroid Build Coastguard Worker            ( 4, "Entity 'sample.entity' not defined\n")
117*7c568831SAndroid Build Coastguard Worker            ])
118*7c568831SAndroid Build Coastguard Worker
119*7c568831SAndroid Build Coastguard Worker# Try to load the document again
120*7c568831SAndroid Build Coastguard Workerrun_test(desc="Retry loading document after unregistering callback",
121*7c568831SAndroid Build Coastguard Worker        docpath=startURL, catalog=catURL,
122*7c568831SAndroid Build Coastguard Worker        exp_status="not loaded", exp_err=[
123*7c568831SAndroid Build Coastguard Worker            (-1, "I/O "),
124*7c568831SAndroid Build Coastguard Worker            (-1, "warning : "),
125*7c568831SAndroid Build Coastguard Worker            (-1, "failed to load \"py://strings/xml/sample.xml\": No such file or directory\n")
126*7c568831SAndroid Build Coastguard Worker            ])
127*7c568831SAndroid Build Coastguard Worker
128*7c568831SAndroid Build Coastguard Worker# But should be able to read standard I/O yet...
129*7c568831SAndroid Build Coastguard Workerrun_test(desc="Loading using standard i/o after unregistering callback",
130*7c568831SAndroid Build Coastguard Worker        docpath="tst.xml", catalog=None,
131*7c568831SAndroid Build Coastguard Worker        root_name='doc', root_content='bar')
132*7c568831SAndroid Build Coastguard Worker
133*7c568831SAndroid Build Coastguard Worker# Now pop ALL input callbacks, should fail to load even standard I/O
134*7c568831SAndroid Build Coastguard Workertry:
135*7c568831SAndroid Build Coastguard Worker    while True:
136*7c568831SAndroid Build Coastguard Worker        libxml2.popInputCallbacks()
137*7c568831SAndroid Build Coastguard Workerexcept IndexError:
138*7c568831SAndroid Build Coastguard Worker    pass
139*7c568831SAndroid Build Coastguard Worker
140*7c568831SAndroid Build Coastguard Workerrun_test(desc="Loading using standard i/o after unregistering all callbacks",
141*7c568831SAndroid Build Coastguard Worker        docpath="tst.xml", catalog=None,
142*7c568831SAndroid Build Coastguard Worker        exp_status="not loaded", exp_err=[
143*7c568831SAndroid Build Coastguard Worker            (-1, "I/O "),
144*7c568831SAndroid Build Coastguard Worker            (-1, "warning : "),
145*7c568831SAndroid Build Coastguard Worker            (-1, "failed to load \"tst.xml\": No such file or directory\n")
146*7c568831SAndroid Build Coastguard Worker            ])
147*7c568831SAndroid Build Coastguard Worker
148*7c568831SAndroid Build Coastguard Workerprint("OK")
149*7c568831SAndroid Build Coastguard Workersys.exit(0);
150