LibreOffice Module unotest (master) 1
unotest.py
Go to the documentation of this file.
1# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
2#
3# This file is part of the LibreOffice project.
4#
5# This Source Code Form is subject to the terms of the Mozilla Public
6# License, v. 2.0. If a copy of the MPL was not distributed with this
7# file, You can obtain one at http://mozilla.org/MPL/2.0/.
8#
9
10import pathlib
11import subprocess
12import time
13import uuid
14import argparse
15import os
16import shutil
17import urllib.parse
18import urllib.request
19
20try:
21 import pyuno
22 import uno
23except ImportError:
24 print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables")
25 print("to something like:")
26 print(" PYTHONPATH=/installation/opt/program")
27 print(" URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
28 raise
29
30try:
31 from com.sun.star.document import XDocumentEventListener
32except ImportError:
33 print("UNO API class not found: try to set URE_BOOTSTRAP variable")
34 print("to something like:")
35 print(" URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
36 raise
37
38try:
39 from urllib.parse import quote
40except ImportError:
41 from urllib import quote
42
43
44
45def mkPropertyValue(name, value):
46 return uno.createUnoStruct("com.sun.star.beans.PropertyValue", name, 0, value, 0)
47
48def mkPropertyValues(**kwargs):
49 '''mkPropertyValues(Name=Value, Name=Value,...) -> (PropertyValue, PropertyValue,...)
50 ex. : mkPropertyValues(Hidden=True, ReadOnly=False)'''
51 from com.sun.star.beans import PropertyValue
52 return tuple(PropertyValue(k,0,kwargs[k],0) for k in kwargs)
53
55 return pyuno.fileUrlToSystemPath(url)
56
57def systemPathToFileUrl(systemPath):
58 return pyuno.systemPathToFileUrl(systemPath)
59
60
61
62class OfficeConnection(object):
63
64 def __init__(self, args):
65 self.args = args
66 self.soffice = None
67 self.xContext = None
68 self.channel = None
69
70 def setUp(self):
71 try:
72 self.verbose = self.args["verbose"]
73 except KeyError:
74 self.verbose = False
75 try:
76 prog = self.args["program"]
77 except KeyError:
78 prog = os.getenv("SOFFICE_BIN")
79 if not (prog):
80 raise Exception("SOFFICE_BIN must be set")
81 channel = "pipe,name=pytest" + str(uuid.uuid1())
82 try:
83 userdir = self.args["userdir"]
84 except KeyError:
85 userdir = "file:///tmp"
86 if not(userdir.startswith("file://")):
87 raise Exception("--userdir must be file URL")
88 self.soffice = self.bootstrap(prog, userdir, channel)
89 return self.connect(channel)
90
91 def bootstrap(self, soffice, userdir, channel):
92 argv = [ soffice, "--accept=" + channel + ";urp",
93 "-env:UserInstallation=" + userdir,
94 "--quickstart=no",
95 "--norestore", "--nologo", "--headless"]
96 if "--valgrind" in self.args:
97 argv.append("--valgrind")
98 if self.verbose:
99 print ("starting LibreOffice with channel: ", channel)
100 return subprocess.Popen(argv)
101
102 def connect(self, channel):
103 xLocalContext = uno.getComponentContext()
104 xUnoResolver = xLocalContext.ServiceManager.createInstanceWithContext(
105 "com.sun.star.bridge.UnoUrlResolver", xLocalContext)
106 url = ("uno:%s;urp;StarOffice.ComponentContext" % channel)
107 if self.verbose:
108 print("Connecting to: ", url)
109 while True:
110 try:
111 self.xContext = xUnoResolver.resolve(url)
112 return self.xContext
113# except com.sun.star.connection.NoConnectException
114 except pyuno.getClass("com.sun.star.connection.NoConnectException"):
115 print("WARN: NoConnectException: sleeping...")
116 time.sleep(1)
117
118 def tearDown(self):
119 if self.soffice:
120 if self.xContext:
121 try:
122 if self.verbose:
123 print("tearDown: calling terminate()...")
124 xMgr = self.xContext.ServiceManager
125 xDesktop = xMgr.createInstanceWithContext(
126 "com.sun.star.frame.Desktop", self.xContext)
127 xDesktop.terminate()
128 if self.verbose:
129 print("...done")
130# except com.sun.star.lang.DisposedException:
131 except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"):
132 print("caught UnknownPropertyException")
133 pass # ignore, also means disposed
134 except pyuno.getClass("com.sun.star.lang.DisposedException"):
135 print("caught DisposedException")
136 pass # ignore
137 else:
138 self.soffice.terminate()
139 ret = self.soffice.wait()
140 self.xContext = None
141 self.socket = None
142 self.soffice = None
143 if ret != 0:
144 raise Exception("Exit status indicates failure: " + str(ret))
145
146 def getContext(self):
147 return self.xContext
148
150 def __init__(self, args):
151 self.args = args
152 self.connection = None
153 def getContext(self):
154 return self.connection.xContext
155 def getDoc(self):
156 return self.xDoc
157 def setUp(self):
158 conn = OfficeConnection(self.args)
159 conn.setUp()
160 self.connection = conn
162 assert(self.connection)
163 smgr = self.getContext().ServiceManager
164 desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.getContext())
165 props = [("Hidden", True), ("ReadOnly", False)]
166 loadProps = tuple([mkPropertyValue(name, value) for (name, value) in props])
167 self.xDoc = desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, loadProps)
168 return self.xDoc
169
170 def checkProperties(self, obj, dict, test):
171 for k,v in dict.items():
172 obj.setPropertyValue(k, v)
173 value = obj.getPropertyValue(k)
174 test.assertEqual(value, v)
175
176 def postTest(self):
177 assert(self.connection)
178 def tearDown(self):
179 if self.connection:
180 try:
181 self.connection.tearDown()
182 finally:
183 self.connection = None
184
185havePonies = False
186
188 def getContext(self):
189 return self.xContext
190 def getDoc(self):
191 return self.xDoc
192 def setUp(self):
193 self.xContext = pyuno.getComponentContext()
194 global havePonies
195 if not(havePonies):
196 pyuno.private_initTestEnvironment()
197 havePonies = True
198
200 return self.openEmptyDoc("private:factory/swriter")
201
203 return self.openEmptyDoc("private:factory/scalc")
204
205 def openEmptyDoc(self, url, bHidden = True, bReadOnly = False):
206 props = [("Hidden", bHidden), ("ReadOnly", bReadOnly)]
207 return self.__openDocFromURL(url, props)
208
209 def openTemplateFromTDOC(self, file):
210 return self.openDocFromTDOC(file, True)
211
212 def openDocFromTDOC(self, file, asTemplate = False):
213 path = makeCopyFromTDOC(file)
214 return self.openDocFromAbsolutePath(path, asTemplate)
215
216 def openDocFromAbsolutePath(self, file, asTemplate = False):
217 return self.openDocFromURL(pathlib.Path(file).as_uri(), asTemplate)
218
219 def openDocFromURL(self, url, asTemplate = False):
220 props = [("Hidden", True), ("ReadOnly", False), ("AsTemplate", asTemplate)]
221 return self.__openDocFromURL(url, props)
222
223 def __openDocFromURL(self, url, props):
224 assert(self.xContext)
225 smgr = self.getContext().ServiceManager
226 desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.getContext())
227 loadProps = tuple([mkPropertyValue(name, value) for (name, value) in props])
228 self.xDoc = desktop.loadComponentFromURL(url, "_blank", 0, loadProps)
229 assert(self.xDoc)
230 return self.xDoc
231
232 def checkProperties(self, obj, dict, test):
233 for k,v in dict.items():
234 obj.setPropertyValue(k, v)
235 value = obj.getPropertyValue(k)
236 test.assertEqual(value, v)
237
238 def setProperties(self, obj, dict):
239 for k,v in dict.items():
240 obj.setPropertyValue(k, v)
241
242 def postTest(self):
243 assert(self.xContext)
244 def tearDown(self):
245 if hasattr(self, 'xDoc'):
246 if self.xDoc:
247 self.xDoc.close(True)
248 # HACK in case self.xDoc holds a UNO proxy to an SwXTextDocument (whose dtor calls
249 # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be
250 # garbage-collected after VCL has already been deinitialized:
251 self.xDoc = None
252
253def simpleInvoke(connection, test):
254 try:
255 connection.preTest()
256 test.run(connection.getContext())
257 finally:
258 connection.postTest()
259
260def retryInvoke(connection, test):
261 tries = 5
262 while tries > 0:
263 try:
264 tries -= 1
265 try:
266 connection.preTest()
267 test.run(connection.getContext())
268 return
269 finally:
270 connection.postTest()
271 except KeyboardInterrupt:
272 raise # Ctrl+C should work
273 except:
274 print("retryInvoke: caught exception")
275 raise Exception("FAILED retryInvoke")
276
277def runConnectionTests(connection, invoker, tests):
278 try:
279 connection.setUp()
280 for test in tests:
281 invoker(connection, test)
282 finally:
283 connection.tearDown()
284
286 src = os.getenv("TDOC")
287 assert(src is not None)
288 src = os.path.join(src, file)
289 dst = os.getenv("TestUserDir")
290 assert(dst is not None)
291 uri = urllib.parse.urlparse(dst)
292 assert(uri.scheme.casefold() == "file")
293 assert(uri.netloc == "" or uri.netloc.casefold() == "localhost")
294 assert(uri.params == "")
295 assert(uri.query == "")
296 assert(uri.fragment == "")
297 dst = urllib.request.url2pathname(uri.path)
298 dst = os.path.join(dst, "tmp", file)
299 os.makedirs(os.path.dirname(dst), exist_ok=True)
300 try:
301 os.remove(dst)
302 except FileNotFoundError:
303 pass
304 shutil.copyfile(src, dst)
305 return dst
306
307
308
309if __name__ == "__main__":
310 parser = argparse.ArgumentParser("Help utilities for testing LibreOffice")
311 group = parser.add_mutually_exclusive_group()
312 group.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
313 #parser.add_argument("p", type=str, help="program name")
314 args = parser.parse_args()
315 if args.verbose:
316 verbose = True
317 con = PersistentConnection({"verbose" : args.verbose})
318 print("starting soffice ... ", end="")
319 con.setUp()
320 print("done")
321 con.get
322 print ("shutting down ... ", end="")
323 con.tearDown()
324 print("done")
325
326# vim: set shiftwidth=4 softtabstop=4 expandtab:
def bootstrap(self, soffice, userdir, channel)
Definition: unotest.py:91
def openTemplateFromTDOC(self, file)
Definition: unotest.py:209
def openDocFromTDOC(self, file, asTemplate=False)
Definition: unotest.py:212
def checkProperties(self, obj, dict, test)
Definition: unotest.py:232
def setProperties(self, obj, dict)
Definition: unotest.py:238
def openDocFromURL(self, url, asTemplate=False)
Definition: unotest.py:219
def openDocFromAbsolutePath(self, file, asTemplate=False)
Definition: unotest.py:216
def __openDocFromURL(self, url, props)
Definition: unotest.py:223
def openEmptyDoc(self, url, bHidden=True, bReadOnly=False)
Definition: unotest.py:205
def checkProperties(self, obj, dict, test)
Definition: unotest.py:170
def mkPropertyValues(**kwargs)
Definition: unotest.py:48
def systemPathToFileUrl(systemPath)
Definition: unotest.py:57
def mkPropertyValue(name, value)
utilities ###
Definition: unotest.py:45
def makeCopyFromTDOC(file)
Definition: unotest.py:285
def retryInvoke(connection, test)
Definition: unotest.py:260
def fileUrlToSystemPath(url)
Definition: unotest.py:54
def simpleInvoke(connection, test)
Definition: unotest.py:253
def runConnectionTests(connection, invoker, tests)
Definition: unotest.py:277
PyRef getClass(const OUString &name, const Runtime &runtime)
def getComponentContext()
def createUnoStruct(typeName, *args, **kwargs)