This is an automated email from the ASF dual-hosted git repository. jimjag pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit ed39d56c1332803e6451536e3de1648181dc28d7 Author: Jim Jagielski <[email protected]> AuthorDate: Tue Nov 3 02:55:26 2026 -0500 test/odf-validate: add the ODF 1.4 extended variant and v14/x14 modes --- test/odf-validate/drive.sh | 4 ++-- test/odf-validate/gate.py | 2 +- test/odf-validate/make-controls.py | 11 ++++++++++- test/odf-validate/report.py | 2 +- test/odf-validate/selftest.py | 13 ++++++++++++- test/odf-validate/tools13/make-foreign-corpus.py | 8 ++++++-- test/odf-validate/validate.py | 18 ++++++++++-------- 7 files changed, 42 insertions(+), 16 deletions(-) diff --git a/test/odf-validate/drive.sh b/test/odf-validate/drive.sh index 77f9157e88..3c7169440c 100755 --- a/test/odf-validate/drive.sh +++ b/test/odf-validate/drive.sh @@ -19,7 +19,7 @@ # under the License. # #************************************************************** -# usage: drive.sh <ext|strict|v00|v10|v11|v13|x13> <port> <jobs.tsv> <done.tsv> (DefaultVersion 3, 4, 0 UNKNOWN, 1 = ODF 1.0, 2 = ODF 1.1, 5 = ODF 1.3, 6 = ODF 1.3 Extended) +# usage: drive.sh <ext|strict|v00|v10|v11|v13|x13|v14|x14> <port> <jobs.tsv> <done.tsv> (DefaultVersion 3, 4, 0 UNKNOWN, 1 = ODF 1.0, 2 = ODF 1.1, 5 = ODF 1.3, 6 = ODF 1.3 Extended, 7 = ODF 1.4, 8 = ODF 1.4 Extended) # No mode name may be a prefix of another: the kills below match "profile_<mode>" as a pattern (a mode ext13 died when ext finished). # Converts jobs in batches of 20. Stops on the first soffice crash; kills and resumes on a hang. source ${0:A:h}/env.sh @@ -27,7 +27,7 @@ mode=$1; port=$2; jobs=$3; done_=$4; log=${done_:r}.log prof=$mode if [[ ! -e $D/runs/profile_$prof/user/registrymodifications.xcu ]]; then mkdir -p $D/runs/profile_$prof/user - case $mode in v00) v=0;; v10) v=1;; v11) v=2;; ext) v=3;; strict) v=4;; v13) v=5;; x13) v=6;; *) echo "drive.sh: unknown mode $mode" >&2; exit 2;; esac + case $mode in v00) v=0;; v10) v=1;; v11) v=2;; ext) v=3;; strict) v=4;; v13) v=5;; x13) v=6;; v14) v=7;; x14) v=8;; *) echo "drive.sh: unknown mode $mode" >&2; exit 2;; esac cat > $D/runs/profile_$prof/user/registrymodifications.xcu <<XCU <?xml version="1.0" encoding="UTF-8"?> <oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> diff --git a/test/odf-validate/gate.py b/test/odf-validate/gate.py index 9e61f84900..d2f1c96011 100644 --- a/test/odf-validate/gate.py +++ b/test/odf-validate/gate.py @@ -275,7 +275,7 @@ class Gate: val = json.load(open(os.path.join(md, "val.json"))) r["errors"] = {v: len(e) for v, e in val["errors"].items()} r["packages"] = len(val["packages"]) - for v in ("12", "14", "12x", "13", "13x"): + for v in ("12", "14", "12x", "13", "13x", "14x"): kp = os.path.join(md, "kinds%s.json" % ("" if v == "12" else v)) run(PY + [os.path.join(D, "report.py"), os.path.join(md, "val.json"), kp, v], os.path.join(md, "report%s.log" % v)) r["kinds" + v] = len(json.load(open(kp))) diff --git a/test/odf-validate/make-controls.py b/test/odf-validate/make-controls.py index 3ef816b550..465d979e59 100644 --- a/test/odf-validate/make-controls.py +++ b/test/odf-validate/make-controls.py @@ -44,7 +44,7 @@ def mk(name, ver, body, extra=None): OFFNS = ('xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" ' 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" ' 'xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" ' - 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"') + 'xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"') def mkdoc(name, ext, mime, streams): @@ -88,3 +88,12 @@ for n, region in (("baseregion", ""), ("newregion", '<chart:coordinate-region sv for n, attrs in (("scaleone", 'style:scale-to="80%"'), ("scalexy", 'style:scale-to-X="2" style:scale-to-Y="3"'), ("scaleboth", 'style:scale-to-X="2" style:scale-to-Y="3" style:scale-to="80%"')): mkdoc(n, "odt", TEXT, {"content.xml": ("office:document-content", "<office:body><office:text><text:p>hi</text:p></office:text></office:body>"), "styles.xml": ("office:document-styles", '<office:automatic-styles><style:page-layout style:name="pm1"><style:page-layout-properties %s/></style:page-layout></office:automatic-styles>' % attrs)}) +# 1.4 (OFFICE-4142): draw:decorative on style:graphic-properties is new, invalid in 1.2 and 1.3; basedeco is the same style without it +for n, attrs in (("basedeco", ""), ("newdeco", ' draw:decorative="true"')): + mkdoc(n, "odt", TEXT, {"content.xml": ("office:document-content", "<office:body><office:text><text:p>hi</text:p></office:text></office:body>"), + "styles.xml": ("office:document-styles", '<office:automatic-styles><style:style style:name="gr1" style:family="graphic"><style:graphic-properties%s/></style:style></office:automatic-styles>' % attrs)}) +# 1.4 (OFFICE-4110): draw:handle needs the new position-x/-y or polar-pole pair; the old combined attribute alone is valid in 1.2 and 1.3 only. +# The pair itself is invalid in 1.2 and 1.3. +for n, attrs in (("handleold", 'draw:handle-position="0 0"'), ("handlenew", 'draw:handle-position-x="0" draw:handle-position-y="0"'), + ("handlepolar", 'draw:handle-polar-pole-x="0" draw:handle-polar-pole-y="0"')): + mkdoc(n, "odt", TEXT, {"content.xml": ("office:document-content", '<office:body><office:text><text:p><draw:custom-shape><draw:enhanced-geometry><draw:handle %s/></draw:enhanced-geometry></draw:custom-shape></text:p></office:text></office:body>' % attrs)}) diff --git a/test/odf-validate/report.py b/test/odf-validate/report.py index 9fdce2431c..980ad87aa0 100644 --- a/test/odf-validate/report.py +++ b/test/odf-validate/report.py @@ -19,7 +19,7 @@ # #************************************************************** -# usage: report.py <val.json> <kinds.json> [12|14|12x] -- groups errors by normalized message +# usage: report.py <val.json> <kinds.json> [12|14|12x|13|13x|14x] -- groups errors by normalized message import sys, re, json, collections diff --git a/test/odf-validate/selftest.py b/test/odf-validate/selftest.py index 007ef02374..511035ae7e 100644 --- a/test/odf-validate/selftest.py +++ b/test/odf-validate/selftest.py @@ -44,6 +44,17 @@ checks = { "13: style:scale-to-X/Y together with style:scale-to invalid in 1.3 and 1.3 extended": all(any('"style:scale-to"' in m for m in errs(v, "scaleboth.odt")) for v in ("13", "13x")), "13: style:scale-to-X/Y alone and style:scale-to alone valid in 1.3 and 1.3 extended; X/Y invalid in 1.2": not any(errs(v, p) for v in ("13", "13x") for p in ("scalexy.odt", "scaleone.odt")) and any('"style:scale-to-X"' in m for m in errs("12", "scalexy.odt")) and not errs("12", "scaleone.odt"), + "14: good.odt clean in 1.4 and 1.4 extended; a 1.3 version is rewritten like a 1.2 one": not errs("14x", "good.odt"), + "14: bad.odt flags text:bogus in 1.4 extended": any('"text:bogus"' in m for m in errs("14x", "bad.odt")), + "14: wrongver.odt (1.1) flags office:version in 1.4 extended": any("office:version" in m for m in errs("14x", "wrongver.odt")), + "14: extforeign.odt invalid as written, valid in 1.4 extended once foreign content is removed": bool(errs("14", "extforeign.odt")) and not errs("14x", "extforeign.odt"), + "14: draw:decorative invalid in 1.2 and 1.3, valid in 1.4 and 1.4 extended; the same style without it is clean in all": any('"draw:decorative"' in m for v in ("12", "13") for m in errs(v, "newdeco.odt")) + and all(errs(v, "newdeco.odt") for v in ("12", "13")) and not errs("14", "newdeco.odt") and not errs("14x", "newdeco.odt") + and not any(errs(v, "basedeco.odt") for v in ("12", "13", "14", "14x")), + "14: draw:handle with only the old combined attribute valid in 1.2 and 1.3, invalid in 1.4 and 1.4 extended": not any(errs(v, "handleold.odt") for v in ("12", "13")) + and all(errs(v, "handleold.odt") for v in ("14", "14x")), + "14: draw:handle with the new position or polar pair valid in 1.4 and 1.4 extended, invalid in 1.2 and 1.3": not any(errs(v, p) for v in ("14", "14x") for p in ("handlenew.odt", "handlepolar.odt")) + and all(errs(v, p) for v in ("12", "13") for p in ("handlenew.odt", "handlepolar.odt")), "mathobj.odt MathML object skipped": d["mathml_skipped"] == ["mathobj.odt/Object 1/content.xml"] and not errs("12", "mathobj.odt"), "no unparsed jing output": not any(e[0] == "?" for v in d["errors"] for e in d["errors"][v]), } @@ -154,7 +165,7 @@ gspec = importlib.util.spec_from_file_location("gate", os.path.join(D, "gate.py" gt = importlib.util.module_from_spec(gspec) gspec.loader.exec_module(gt) vol = gt.read_volatile() -checks["gate: gate-volatile.tsv lists the 12 documents with a reason"] = len(vol) == 12 and all(len(r) > 10 for r in vol.values()) +checks["gate: gate-volatile.tsv lists the 13 documents with a reason"] = len(vol) == 13 and all(len(r) > 10 for r in vol.values()) checks["gate: rewrite_jobs keeps sources and moves outputs"] = gt.rewrite_jobs([("/a/b.odt", "/x/0001_b.odt")], "/new") == [("/a/b.odt", "/new/0001_b.odt")] fake = {"0067_AddInChart.ods": {"ndiff": 1}, "0001_x.odt": {"ndiff": 0}, "0002_y.odt": {"ndiff": 3}} checks["gate: classify separates volatile from unexplained differences"] = gt.classify(fake, vol) == (1, ["0067_AddInChart.ods"], ["0002_y.odt"]) diff --git a/test/odf-validate/tools13/make-foreign-corpus.py b/test/odf-validate/tools13/make-foreign-corpus.py index 035bf70105..885c450afa 100644 --- a/test/odf-validate/tools13/make-foreign-corpus.py +++ b/test/odf-validate/tools13/make-foreign-corpus.py @@ -21,8 +21,8 @@ #************************************************************** # Black-box corpus builder: converts the same inputs with another office suite's headless binary once per output -# ODF version (1.2, 1.2 extended, 1.3, 1.3 extended), so an import smoke test can load each 1.3 file next to its -# 1.2 twin. Only observes output files; never reads that suite's source or installed configuration. +# ODF version (1.2, 1.2 extended, 1.3, 1.3 extended, 1.4, 1.4 extended), so an import smoke test can load each 1.3 or 1.4 +# file next to its 1.2 twin. Only observes output files; never reads that suite's source or installed configuration. # # python3 -I make-foreign-corpus.py <soffice binary> [--inputs LIST] [--root DIR] [--modes v12,v13,...] # python3 -I make-foreign-corpus.py <soffice binary> --probe WORKDIR [--probe-range 0-20] (value table only) @@ -43,6 +43,8 @@ DEFAULT_VERSION_V12 = 4 DEFAULT_VERSION_V12EXT = 8 DEFAULT_VERSION_V13 = 10 DEFAULT_VERSION_V13EXT = 11 +DEFAULT_VERSION_V14 = 12 +DEFAULT_VERSION_V14EXT = 13 # mode -> (DefaultVersion value, expected office:version, whether extension namespaces are expected) MODES = { @@ -50,6 +52,8 @@ MODES = { "v12ext": (DEFAULT_VERSION_V12EXT, "1.2", True), "v13": (DEFAULT_VERSION_V13, "1.3", False), "v13ext": (DEFAULT_VERSION_V13EXT, "1.3", True), + "v14": (DEFAULT_VERSION_V14, "1.4", False), + "v14ext": (DEFAULT_VERSION_V14EXT, "1.4", True), } OUT_EXT = {"odt": "odt", "sxw": "odt", "doc": "odt", "docx": "odt", "rtf": "odt", "ods": "ods", "sxc": "ods", "xls": "ods", "xlsx": "ods", diff --git a/test/odf-validate/validate.py b/test/odf-validate/validate.py index 89f6067add..fe05100b4d 100644 --- a/test/odf-validate/validate.py +++ b/test/odf-validate/validate.py @@ -20,9 +20,10 @@ #************************************************************** # usage: validate.py <outdir> <val.json> -# Validates every package in <outdir> against ODF 1.2 as written, against ODF 1.3 and 1.4 with the version rewritten -# ("13", "14"), and against ODF 1.2 and 1.3 after removing foreign elements and attributes ("12x", "13x"; Part 1 2.2.2 B.1; -# manifest left as written apart from the version). +# Validates every package in <outdir> against ODF 1.2 as written, against ODF 1.3 and 1.4 with an office:version or +# manifest:version of 1.2 or 1.3 rewritten to the target ("13", "14"; any other version stays and is reported), and against +# ODF 1.2, 1.3 and 1.4 after removing foreign elements and attributes ("12x", "13x", "14x"; Part 1 2.2.2 B.1; manifest left +# as written apart from the version). import sys, os, re, zipfile, subprocess, json, shutil, importlib.util D = os.path.dirname(os.path.abspath(__file__)) @@ -32,7 +33,8 @@ SCH = {"12": (D + "/schema/OpenDocument-v1.2-os-schema.rng", D + "/schema/OpenDo "14": (D + "/schema/OpenDocument-v1.4-schema.rng", D + "/schema/OpenDocument-v1.4-manifest-schema.rng")} SCH["12x"] = SCH["12"] SCH["13x"] = SCH["13"] -VERSIONS = ("12", "14", "12x", "13", "13x") +SCH["14x"] = SCH["14"] +VERSIONS = ("12", "14", "12x", "13", "13x", "14x") _spec = importlib.util.spec_from_file_location("foreign", D + "/foreign.py") foreign = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(foreign) @@ -53,7 +55,7 @@ def main(): sys.exit("validate.py: <val.json> must live under " + os.path.join(D, "runs")) shutil.rmtree(work, ignore_errors=True) files = {v: ([], []) for v in VERSIONS} - mixed = {"12x": foreign.mixed_elements(SCH["12"][0]), "13x": foreign.mixed_elements(SCH["13"][0])} + mixed = {v: foreign.mixed_elements(SCH[v][0]) for v in ("12x", "13x", "14x")} skipped, bad_zip = [], [] pkgs = sorted(p for p in os.listdir(outdir) if not p.startswith(".")) for pkg in pkgs: @@ -70,11 +72,11 @@ def main(): skipped.append(pkg + "/" + name); continue for v in VERSIONS: t = text - if v in ("12x", "13x") and not is_mani: + if v in mixed and not is_mani: t = foreign.strip_foreign(t, mixed[v]) - if v in ("13", "13x", "14"): + if v in ("13", "13x", "14", "14x"): attr = "manifest:version" if is_mani else "office:version" - t = re.sub(attr + '="1.2"', attr + '="1.%s"' % v[1], t, count=1) + t = re.sub(attr + '="1\\.[23]"', attr + '="1.%s"' % v[1], t, count=1) p = os.path.join(work, v, pkg, name) os.makedirs(os.path.dirname(p), exist_ok=True) open(p, "w", encoding="utf-8").write(t)
