xref: /trunk/test/odf-validate/fixtures/p8/numfmt_check.py (revision 0e16a31d78a652c56c5c0fc79c752f48b9048df9)
1#**************************************************************
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20#**************************************************************
21
22# usage: runpy.sh fixtures/p8/numfmt_check.py <port> <outdir>
23# Loads an ODS whose cells use number styles with number:min-decimal-places and number:fill-character (ODF 1.3) and
24# compares the format code and the displayed text of each cell with the expected ones. Rows marked "control" must give the
25# same result before and after the reader change. Exit code 1 when any row differs.
26import os, sys, zipfile
27import uno
28from unohelp import connect, desktop, load
29
30port, out = int(sys.argv[1]), os.path.abspath(sys.argv[2])
31os.makedirs(out, exist_ok=True)
32
33NS = ('xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '
34      'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" '
35      'xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" '
36      'xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"')
37
38# name, number-style content, cell value, expected format code (None: only checked by the regex below), expected text
39# (suffix match); the number-style is always an en-US one
40# sci-min1-of-3-display: the format code is right (ODF wants 1.5E+05), but the scientific output of SvNumberformat shows the optional
41# digits as zeros; the row pins that known limit
42CASES = [
43    ("min1-of-3",          '<number:number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1"/>', 1.5,    "0.0##",     "1.5"),
44    ("min1-of-3-integer",  '<number:number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1"/>', 2,      "0.0##",     "2.0"),
45    ("min1-of-3-full",     '<number:number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1"/>', 1.2345, "0.0##",     "1.235"),
46    ("min0-of-3",          '<number:number number:decimal-places="3" number:min-decimal-places="0" number:min-integer-digits="1"/>', 1.5,    "0.###",     "1.5"),
47    ("min-equals-places",  '<number:number number:decimal-places="2" number:min-decimal-places="2" number:min-integer-digits="1"/>', 1.5,    "0.00",      "1.50"),
48    ("control-no-min",     '<number:number number:decimal-places="2" number:min-integer-digits="1"/>',                               1.5,    "0.00",      "1.50"),
49    ("control-var-dec",    '<number:number number:decimal-places="2" number:decimal-replacement="" number:min-integer-digits="1"/>', 1.5,    "0.##",      "1.5"),
50    ("var-dec-and-min1",   '<number:number number:decimal-places="3" number:decimal-replacement="" number:min-decimal-places="1" number:min-integer-digits="1"/>', 1.5, "0.0##", "1.5"),
51    ("min-above-places",   '<number:number number:decimal-places="2" number:min-decimal-places="5" number:min-integer-digits="1"/>', 1.5,    "0.00",      "1.50"),
52    ("control-dashes",     '<number:number number:decimal-places="2" number:decimal-replacement="-" number:min-integer-digits="1"/>', 2,     "0.--",      "2.--"),
53    ("dashes-and-min1",    '<number:number number:decimal-places="2" number:decimal-replacement="-" number:min-decimal-places="1" number:min-integer-digits="1"/>', 2, "0.--", "2.--"),
54    ("sci-min1-of-3-display",      '<number:scientific-number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1" number:min-exponent-digits="2"/>', 150000, "0.0##E+00", "1.500E+05"),
55    ("control-sci",        '<number:scientific-number number:decimal-places="3" number:min-integer-digits="1" number:min-exponent-digits="2"/>', 150000, "0.000E+00", "1.500E+05"),
56    ("fill-before",        '<number:fill-character>.</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "*.0.00", "1.50"),
57    ("fill-after-text",    '<number:text>$</number:text><number:fill-character>-</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, '"$"*-0.00', "1.50"),
58    ("fill-twice",         '<number:fill-character>.</number:fill-character><number:fill-character>-</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "*.0.00", "1.50"),
59    ("fill-two-chars",     '<number:fill-character>ab</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"),
60    ("fill-empty",         '<number:fill-character/><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"),
61]
62
63styles, cells = [], []
64for i, (name, content, value, _, _) in enumerate(CASES):
65    styles.append('<number:number-style style:name="N%d" number:language="en" number:country="US">%s</number:number-style>' % (i, content))
66    styles.append('<style:style style:name="ce%d" style:family="table-cell" style:data-style-name="N%d"/>' % (i, i))
67    cells.append('<table:table-row><table:table-cell table:style-name="ce%d" office:value-type="float" office:value="%s"/></table:table-row>' % (i, value))
68
69content = ('<?xml version="1.0" encoding="UTF-8"?>\n<office:document-content %s office:version="1.3"><office:automatic-styles>%s'
70           '</office:automatic-styles><office:body><office:spreadsheet><table:table table:name="T">%s</table:table>'
71           '</office:spreadsheet></office:body></office:document-content>' % (NS, "".join(styles), "".join(cells)))
72manifest = ('<?xml version="1.0" encoding="UTF-8"?>\n<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" '
73            'manifest:version="1.3"><manifest:file-entry manifest:full-path="/" manifest:version="1.3" '
74            'manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>'
75            '<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/></manifest:manifest>')
76path = os.path.join(out, "numfmt.ods")
77with zipfile.ZipFile(path, "w") as z:
78    z.writestr("mimetype", "application/vnd.oasis.opendocument.spreadsheet", zipfile.ZIP_STORED)
79    z.writestr("META-INF/manifest.xml", manifest, zipfile.ZIP_DEFLATED)
80    z.writestr("content.xml", content, zipfile.ZIP_DEFLATED)
81
82ctx = connect(port)
83doc = load(desktop(ctx), path)
84sheet = doc.Sheets.getByIndex(0)
85formats = doc.NumberFormats
86fails = 0
87for i, (name, _, _, want_fmt, want_txt) in enumerate(CASES):
88    cell = sheet.getCellByPosition(0, i)
89    fmt = formats.getByKey(cell.NumberFormat).FormatString
90    txt = cell.String
91    ok = (fmt == want_fmt) and txt.endswith(want_txt)
92    fails += not ok
93    print("%-4s %-20s format=%-12r text=%-12r want %r / %r" % ("ok" if ok else "FAIL", name, fmt, txt, want_fmt, want_txt))
94doc.close(True)
95print("%d of %d rows ok" % (len(CASES) - fails, len(CASES)))
96sys.exit(1 if fails else 0)
97