xref: /trunk/test/odf-validate/fixtures/t16e.py (revision 2c0928fc86855eb408f3542a506ee816b6587f91)
1#!/usr/bin/env python3
2# usage: runpy.sh fixtures/t16e.py <port> <outdir>
3# Task 16a: runtime checks of the XML processing requirements of ODF 1.2 Part 1 section 2.4 E on hand-built Writer packages.
4# Prints "RESULT <name> <observation>" lines; the expectation of a conforming consumer is in the name or the comment.
5import sys, os, re, zipfile, uno
6from unohelp import connect, desktop, pv
7port, out = int(sys.argv[1]), os.path.abspath(sys.argv[2])
8os.makedirs(out, exist_ok=True)
9desk = desktop(connect(port))
10OFFICE = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"
11TEXT = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"
12STYLE = "urn:oasis:names:tc:opendocument:xmlns:style:1.0"
13XL = "http://www.w3.org/1999/xlink"
14MAN = ('<?xml version="1.0" encoding="UTF-8"?><manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">'
15       '<manifest:file-entry manifest:full-path="/" manifest:media-type="application/vnd.oasis.opendocument.text"/>'
16       '<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>'
17       '<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/></manifest:manifest>')
18STYLES = ('<?xml version="1.0" encoding="UTF-8"?><office:document-styles xmlns:office="%s" xmlns:style="%s" xmlns:text="%s" office:version="1.2">'
19          '<office:styles><style:style style:name="P1" style:family="paragraph"><style:text-properties style:text-position="33%% 100%%"/></style:style></office:styles>'
20          '</office:document-styles>' % (OFFICE, STYLE, TEXT))
21def pkg(name, content):
22    p = os.path.join(out, name)
23    z = zipfile.ZipFile(p, "w")
24    z.writestr("mimetype", "application/vnd.oasis.opendocument.text", compress_type=zipfile.ZIP_STORED)
25    z.writestr("META-INF/manifest.xml", MAN); z.writestr("styles.xml", STYLES); z.writestr("content.xml", content)
26    z.close(); return p
27def doc(body, root_ns=None, pre="", extra_attrs=""):
28    ns = root_ns if root_ns is not None else 'xmlns:office="%s" xmlns:text="%s" xmlns:style="%s" xmlns:xlink="%s"' % (OFFICE, TEXT, STYLE, XL)
29    return '<?xml version="1.0" encoding="UTF-8"?>%s<office:document-content %s office:version="1.2"><office:body><office:text>%s</office:text></office:body></office:document-content>' % (pre, ns, body)
30def observe(path):
31    try:
32        d = desk.loadComponentFromURL(uno.systemPathToFileUrl(path), "_blank", 0, (pv("Hidden", True),))
33    except Exception as e:
34        return {"loaded": False, "error": repr(e)[:120]}
35    if d is None:
36        return {"loaded": False, "error": "None"}
37    r = {"loaded": True}
38    try:
39        r["text"] = d.Text.String.replace("\n", "|")
40        en = d.Text.createEnumeration(); refs = []; styles = []
41        while en.hasMoreElements():
42            e = en.nextElement()
43            if e.supportsService("com.sun.star.text.Paragraph"):
44                refs.append(e.MetadataReference.Second); styles.append(e.ParaStyleName)
45        r["xmlid"] = refs; r["parastyle"] = styles
46        r["bookmarks"] = d.Bookmarks.Count
47    except Exception as e:
48        r["obs_error"] = repr(e)[:100]
49    d.close(True); return r
50def run(name, content, note=""):
51    r = observe(pkg(name + ".odt", content)); print("RESULT %-34s %s %s" % (name, r, ("# " + note) if note else ""), flush=True)
52# ---- baseline and namespace handling
53run("00-baseline", doc('<text:p xml:id="p1">one</text:p><text:p>two</text:p><text:bookmark text:name="b1" xml:id="bk1"/>'))
54# prefixes renamed throughout (expected: identical to the baseline)
55b = doc('<text:p xml:id="p1">one</text:p><text:p>two</text:p><text:bookmark text:name="b1" xml:id="bk1"/>')
56b = re.sub(r'(</?|\s)text:', r'\1t:', b); b = b.replace('xmlns:text=', 'xmlns:t=')
57b = re.sub(r'(</?|\s)office:', r'\1o:', b); b = b.replace('xmlns:office=', 'xmlns:o=')
58run("01-prefixes-renamed", b, "conforming: same as baseline")
59# default namespace for the text elements
60run("02-default-namespace", doc('<p xml:id="p1">one</p><p>two</p>', root_ns='xmlns:office="%s" xmlns="%s" xmlns:style="%s"' % (OFFICE, TEXT, STYLE)), "conforming: same text")
61# unprefixed attribute under a default namespace: no namespace per Namespaces in XML, so style-name must be ignored
62run("03-unprefixed-attr", doc('<p style-name="P1">one</p>', root_ns='xmlns:office="%s" xmlns="%s" xmlns:style="%s"' % (OFFICE, TEXT, STYLE)), "conforming: parastyle stays Standard")
63# prefix rebound to another namespace inside the element: it is then not a text:p
64run("04-prefix-rebound", doc('<text:p xmlns:text="urn:example:other">hidden</text:p><text:p>shown</text:p>'), "conforming: only 'shown'")
65# undeclared prefix: not namespace-well-formed
66run("05-undeclared-prefix", doc('<text:p>one</text:p>', root_ns='xmlns:office="%s"' % OFFICE), "conforming: load fails")
67# unknown element and attribute in the ODF namespace
68run("06-unknown-content", doc('<text:foo>x</text:foo><text:p text:bogus="1">kept</text:p>'), "tolerated: 'kept'")
69# ---- xml:id
70run("10-xmlid-whitespace", doc('<text:p xml:id="  w1  ">one</text:p>'), "xml:id spec: normalised to w1")
71run("11-xmlid-duplicate", doc('<text:p xml:id="d1">one</text:p><text:p xml:id="d1">two</text:p>'), "xml:id spec: duplicate is an error")
72run("12-xmlid-not-ncname", doc('<text:p xml:id="1bad">one</text:p>'), "xml:id spec: invalid value")
73# ---- DTD and entities
74run("20-internal-entity", doc('<text:p>&e;</text:p>', pre='<!DOCTYPE office:document-content [<!ENTITY e "ENTITYTEXT">]>'), "expanded")
75host = open("/etc/hostname").read().strip() if os.path.exists("/etc/hostname") else "zzz"
76open(os.path.join(out, "secret.txt"), "w").write("SECRETFILE")
77run("21-external-entity", doc('<text:p>a&x;b</text:p>', pre='<!DOCTYPE office:document-content [<!ENTITY x SYSTEM "file://%s/secret.txt">]>' % out), "must NOT contain SECRETFILE")
78run("22-external-dtd", doc('<text:p>one</text:p>', pre='<!DOCTYPE office:document-content SYSTEM "file://%s/secret.txt">' % out), "must load, nothing fetched")
79laugh = '<!DOCTYPE office:document-content [<!ENTITY a "lol"><!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;"><!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;"><!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;"><!ENTITY e "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;"><!ENTITY f "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;"><!ENTITY g "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;"><!ENTITY h "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;"><!ENTITY i "&h;&h;&h;&h;&h;&h;&h;&h;&h;&h;">]>'
80run("23-entity-expansion-limit", doc('<text:p>&i;</text:p>', pre=laugh), "expansion 10^8: must not hang or exhaust memory")
81print("DONE")
82