xref: /trunk/test/aoo-test/tests/smoke/test_convert.py (revision 2cb6d6f119bbe2f78742f1c5c8538cb0d4e72ede)
1*2cb6d6f1SJim Jagielski################################################################
2*2cb6d6f1SJim Jagielski#
3*2cb6d6f1SJim Jagielski#  Licensed to the Apache Software Foundation (ASF) under one
4*2cb6d6f1SJim Jagielski#  or more contributor license agreements.  See the NOTICE file
5*2cb6d6f1SJim Jagielski#  distributed with this work for additional information
6*2cb6d6f1SJim Jagielski#  regarding copyright ownership.  The ASF licenses this file
7*2cb6d6f1SJim Jagielski#  to you under the Apache License, Version 2.0 (the
8*2cb6d6f1SJim Jagielski#  "License"); you may not use this file except in compliance
9*2cb6d6f1SJim Jagielski#  with the License.  You may obtain a copy of the License at
10*2cb6d6f1SJim Jagielski#
11*2cb6d6f1SJim Jagielski#    http://www.apache.org/licenses/LICENSE-2.0
12*2cb6d6f1SJim Jagielski#
13*2cb6d6f1SJim Jagielski#  Unless required by applicable law or agreed to in writing,
14*2cb6d6f1SJim Jagielski#  software distributed under the License is distributed on an
15*2cb6d6f1SJim Jagielski#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*2cb6d6f1SJim Jagielski#  KIND, either express or implied.  See the License for the
17*2cb6d6f1SJim Jagielski#  specific language governing permissions and limitations
18*2cb6d6f1SJim Jagielski#  under the License.
19*2cb6d6f1SJim Jagielski#
20*2cb6d6f1SJim Jagielski################################################################
21*2cb6d6f1SJim Jagielski
22*2cb6d6f1SJim Jagielski"""The worker's convert op: ODF and foreign formats out, OOXML in, properties passed through, and the document always closed."""
23*2cb6d6f1SJim Jagielskiimport zipfile
24*2cb6d6f1SJim Jagielski
25*2cb6d6f1SJim Jagielskiimport pytest
26*2cb6d6f1SJim Jagielski
27*2cb6d6f1SJim Jagielskifrom aootest.client import WorkerError
28*2cb6d6f1SJim Jagielskifrom aootest.odfversion import MODES, odf_versions
29*2cb6d6f1SJim Jagielski
30*2cb6d6f1SJim Jagielskipytestmark = pytest.mark.smoke
31*2cb6d6f1SJim Jagielski
32*2cb6d6f1SJim JagielskiDOCX = {
33*2cb6d6f1SJim Jagielski    "[Content_Types].xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
34*2cb6d6f1SJim Jagielski    '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
35*2cb6d6f1SJim Jagielski    '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
36*2cb6d6f1SJim Jagielski    '<Default Extension="xml" ContentType="application/xml"/>'
37*2cb6d6f1SJim Jagielski    '<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
38*2cb6d6f1SJim Jagielski    '</Types>',
39*2cb6d6f1SJim Jagielski    "_rels/.rels": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
40*2cb6d6f1SJim Jagielski    '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
41*2cb6d6f1SJim Jagielski    '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>'
42*2cb6d6f1SJim Jagielski    '</Relationships>',
43*2cb6d6f1SJim Jagielski    "word/document.xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
44*2cb6d6f1SJim Jagielski    '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>'
45*2cb6d6f1SJim Jagielski    '<w:p><w:r><w:t>Imported from OOXML</w:t></w:r></w:p></w:body></w:document>',
46*2cb6d6f1SJim Jagielski}
47*2cb6d6f1SJim Jagielski
48*2cb6d6f1SJim Jagielski
49*2cb6d6f1SJim Jagielski@pytest.fixture
50*2cb6d6f1SJim Jagielskidef office(aoo_offices):
51*2cb6d6f1SJim Jagielski    return aoo_offices.get(None)
52*2cb6d6f1SJim Jagielski
53*2cb6d6f1SJim Jagielski
54*2cb6d6f1SJim Jagielski@pytest.fixture
55*2cb6d6f1SJim Jagielskidef source(office, tmp_path):
56*2cb6d6f1SJim Jagielski    """A small Writer document with known text."""
57*2cb6d6f1SJim Jagielski    path = tmp_path / "source.odt"
58*2cb6d6f1SJim Jagielski    office.call("new_and_store", {"app": "writer", "dst": str(path), "filter": "writer8", "text": "Hello Convert"}, timeout=120)
59*2cb6d6f1SJim Jagielski    return path
60*2cb6d6f1SJim Jagielski
61*2cb6d6f1SJim Jagielski
62*2cb6d6f1SJim Jagielskidef open_documents(office):
63*2cb6d6f1SJim Jagielski    return office.call("open_documents")["documents"]
64*2cb6d6f1SJim Jagielski
65*2cb6d6f1SJim Jagielski
66*2cb6d6f1SJim Jagielskidef test_odf_to_odf_keeps_the_text_and_leaves_nothing_open(office, source, tmp_path):
67*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.odt"
68*2cb6d6f1SJim Jagielski    assert office.call("convert", {"src": str(source), "dst": str(dst), "filter": "writer8"}, timeout=120) == {"dst": str(dst)}
69*2cb6d6f1SJim Jagielski    with zipfile.ZipFile(dst) as z:
70*2cb6d6f1SJim Jagielski        assert "Hello Convert" in z.read("content.xml").decode("utf-8")
71*2cb6d6f1SJim Jagielski    assert open_documents(office) == []
72*2cb6d6f1SJim Jagielski
73*2cb6d6f1SJim Jagielski
74*2cb6d6f1SJim Jagielskidef test_text_export(office, source, tmp_path):
75*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.txt"
76*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(source), "dst": str(dst), "filter": "Text"}, timeout=120)
77*2cb6d6f1SJim Jagielski    assert "Hello Convert" in dst.read_text(encoding="utf-8", errors="replace")
78*2cb6d6f1SJim Jagielski
79*2cb6d6f1SJim Jagielski
80*2cb6d6f1SJim Jagielskidef test_pdf_export(office, source, tmp_path):
81*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.pdf"
82*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(source), "dst": str(dst), "filter": "writer_pdf_Export"}, timeout=180)
83*2cb6d6f1SJim Jagielski    data = dst.read_bytes()
84*2cb6d6f1SJim Jagielski    assert data.startswith(b"%PDF-") and b"%%EOF" in data[-1024:]
85*2cb6d6f1SJim Jagielski
86*2cb6d6f1SJim Jagielski
87*2cb6d6f1SJim Jagielskidef test_binary_word_export(office, source, tmp_path):
88*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.doc"
89*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(source), "dst": str(dst), "filter": "MS Word 97"}, timeout=180)
90*2cb6d6f1SJim Jagielski    assert dst.read_bytes().startswith(bytes.fromhex("D0CF11E0A1B11AE1"))
91*2cb6d6f1SJim Jagielski
92*2cb6d6f1SJim Jagielski
93*2cb6d6f1SJim Jagielskidef test_spreadsheet_to_csv(office, tmp_path):
94*2cb6d6f1SJim Jagielski    ods = tmp_path / "s.ods"
95*2cb6d6f1SJim Jagielski    office.call("new_and_store", {"app": "calc", "dst": str(ods), "filter": "calc8", "text": "cell one"}, timeout=120)
96*2cb6d6f1SJim Jagielski    csv = tmp_path / "s.csv"
97*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(ods), "dst": str(csv), "filter": "Text - txt - csv (StarCalc)"}, timeout=120)
98*2cb6d6f1SJim Jagielski    assert "cell one" in csv.read_text(encoding="utf-8", errors="replace")
99*2cb6d6f1SJim Jagielski
100*2cb6d6f1SJim Jagielski
101*2cb6d6f1SJim Jagielskidef test_ooxml_is_imported_by_type_detection(office, tmp_path):
102*2cb6d6f1SJim Jagielski    docx = tmp_path / "in.docx"
103*2cb6d6f1SJim Jagielski    with zipfile.ZipFile(docx, "w", zipfile.ZIP_DEFLATED) as z:
104*2cb6d6f1SJim Jagielski        for name, text in DOCX.items():
105*2cb6d6f1SJim Jagielski            z.writestr(name, text)
106*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.odt"
107*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(docx), "dst": str(dst), "filter": "writer8"}, timeout=180)
108*2cb6d6f1SJim Jagielski    with zipfile.ZipFile(dst) as z:
109*2cb6d6f1SJim Jagielski        assert "Imported from OOXML" in z.read("content.xml").decode("utf-8")
110*2cb6d6f1SJim Jagielski
111*2cb6d6f1SJim Jagielski
112*2cb6d6f1SJim Jagielski@pytest.mark.parametrize("mode", [3, 4])
113*2cb6d6f1SJim Jagielskidef test_the_save_mode_applies_to_a_conversion(aoo_offices, source, tmp_path, mode):
114*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.odt"
115*2cb6d6f1SJim Jagielski    aoo_offices.get(mode).call("convert", {"src": str(source), "dst": str(dst), "filter": "writer8"}, timeout=120)
116*2cb6d6f1SJim Jagielski    assert odf_versions(dst) == (MODES[mode], MODES[mode])
117*2cb6d6f1SJim Jagielski
118*2cb6d6f1SJim Jagielski
119*2cb6d6f1SJim Jagielskidef test_store_properties_reach_the_filter(office, source, tmp_path):
120*2cb6d6f1SJim Jagielski    enc = tmp_path / "enc.odt"
121*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(source), "dst": str(enc), "filter": "writer8", "store_props": {"Password": "secret"}}, timeout=120)
122*2cb6d6f1SJim Jagielski    with zipfile.ZipFile(enc) as z:
123*2cb6d6f1SJim Jagielski        assert b"encryption-data" in z.read("META-INF/manifest.xml")
124*2cb6d6f1SJim Jagielski        assert "Hello Convert" not in z.read("content.xml").decode("latin-1")
125*2cb6d6f1SJim Jagielski    # without the password the load fails cleanly; with it the text comes back
126*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
127*2cb6d6f1SJim Jagielski        office.call("convert", {"src": str(enc), "dst": str(tmp_path / "nopw.odt"), "filter": "writer8"}, timeout=120)
128*2cb6d6f1SJim Jagielski    assert e.value.kind == "uno"
129*2cb6d6f1SJim Jagielski    back = tmp_path / "back.odt"
130*2cb6d6f1SJim Jagielski    office.call("convert", {"src": str(enc), "dst": str(back), "filter": "writer8", "load_props": {"Password": "secret"}}, timeout=120)
131*2cb6d6f1SJim Jagielski    with zipfile.ZipFile(back) as z:
132*2cb6d6f1SJim Jagielski        assert "Hello Convert" in z.read("content.xml").decode("utf-8")
133*2cb6d6f1SJim Jagielski    assert open_documents(office) == []
134*2cb6d6f1SJim Jagielski
135*2cb6d6f1SJim Jagielski
136*2cb6d6f1SJim Jagielskidef test_a_missing_source_is_a_uno_error_and_the_office_stays_usable(office, tmp_path):
137*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
138*2cb6d6f1SJim Jagielski        office.call("convert", {"src": str(tmp_path / "nothing.odt"), "dst": str(tmp_path / "o.odt"), "filter": "writer8"}, timeout=60)
139*2cb6d6f1SJim Jagielski    assert e.value.kind == "uno"
140*2cb6d6f1SJim Jagielski    assert office.call("ping") == {"pong": True} and open_documents(office) == []
141*2cb6d6f1SJim Jagielski
142*2cb6d6f1SJim Jagielski
143*2cb6d6f1SJim Jagielskidef test_an_unknown_filter_closes_the_document_it_loaded(office, source, tmp_path):
144*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
145*2cb6d6f1SJim Jagielski        office.call("convert", {"src": str(source), "dst": str(tmp_path / "o.x"), "filter": "no such filter"}, timeout=60)
146*2cb6d6f1SJim Jagielski    assert e.value.kind == "uno"
147*2cb6d6f1SJim Jagielski    assert open_documents(office) == []
148*2cb6d6f1SJim Jagielski
149*2cb6d6f1SJim Jagielski
150*2cb6d6f1SJim Jagielski@pytest.mark.parametrize("content", [b"", b"this is not a zip file at all\n"], ids=["empty", "garbage"])
151*2cb6d6f1SJim Jagielskidef test_a_damaged_input_ends_cleanly(office, tmp_path, content):
152*2cb6d6f1SJim Jagielski    """Either a uno error or a converted document is fine; a hang, a crash, a leaked document or a dead office is not."""
153*2cb6d6f1SJim Jagielski    src = tmp_path / "damaged.odt"
154*2cb6d6f1SJim Jagielski    src.write_bytes(content)
155*2cb6d6f1SJim Jagielski    dst = tmp_path / "out.odt"
156*2cb6d6f1SJim Jagielski    try:
157*2cb6d6f1SJim Jagielski        office.call("convert", {"src": str(src), "dst": str(dst), "filter": "writer8"}, timeout=60)
158*2cb6d6f1SJim Jagielski    except WorkerError as e:
159*2cb6d6f1SJim Jagielski        assert e.kind == "uno"
160*2cb6d6f1SJim Jagielski    else:
161*2cb6d6f1SJim Jagielski        assert zipfile.is_zipfile(dst)
162*2cb6d6f1SJim Jagielski    assert office.call("ping") == {"pong": True} and open_documents(office) == []
163