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)

Reply via email to