diff options
Diffstat (limited to '')
48 files changed, 7523 insertions, 0 deletions
diff --git a/sw/qa/python/check_bookmarks.py b/sw/qa/python/check_bookmarks.py new file mode 100644 index 000000000..fba145ebc --- /dev/null +++ b/sw/qa/python/check_bookmarks.py @@ -0,0 +1,156 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file incorporates work covered by the following license notice: + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0. + +import unittest +import random +import os.path + +from hashlib import sha1 +from tempfile import TemporaryDirectory +from org.libreoffice.unotest import UnoInProcess, mkPropertyValues, systemPathToFileUrl +from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK + +class CheckBookmarks(unittest.TestCase): + expectedHashes = { + 'nSetupHash': 0x8f88ee1a13a55d6024f58f470723b5174dfa21bb, + 'nInsertRandomHash': 0x5f27e87e16d2cb3ff0bcb24237aa30da3b84cf24, + 'nDeleteRandomHash': 0x1bdaa7773cbfc73a4dc0bb3e0f801b98f648e8e7, + 'nLinebreakHash': 0x3f30a35f195efcfe0373a3e439de05087a2ad37c, + 'nOdfReloadHash': 0x3f30a35f195efcfe0373a3e439de05087a2ad37c, + 'nMsWordReloadHash': 0x3f30a35f195efcfe0373a3e439de05087a2ad37c, + } + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._xDoc = cls._uno.openEmptyWriterDoc() + smgr = cls._uno.xContext.ServiceManager + cls._desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", + cls._uno.xContext) + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + # HACK in case cls._xDoc holds a UNO proxy to an SwXTextDocument (whose dtor calls + # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be + # garbage-collected after VCL has already been deinitialized: + cls._xDoc = None + + def test_bookmarks(self): + self.xDoc = self.__class__._xDoc + self.xText = self.xDoc.getText() + + ## setting and testing bookmarks + self.setupBookmarks() + self.assertEqual(self.expectedHashes['nSetupHash'], + self.getBookmarksHash(self.xDoc)) + + ## modifying bookmarks and testing again + self.insertRandomParts(200177) + self.assertEqual(self.expectedHashes['nInsertRandomHash'], + self.getBookmarksHash(self.xDoc)) + + ## modifying bookmarks and testing again + self.deleteRandomParts(4711) + self.assertEqual(self.expectedHashes['nDeleteRandomHash'], + self.getBookmarksHash(self.xDoc)) + + ## adding line breaks and testing again + self.insertLinebreaks(7) + self.assertEqual(self.expectedHashes['nLinebreakHash'], + self.getBookmarksHash(self.xDoc)) + + ## reloading document and testing again + with TemporaryDirectory() as tempdir: + xOdfReloadedDoc = self.reloadFrom(tempdir, "writer8", "odt") + self.assertEqual(self.expectedHashes['nOdfReloadHash'], + self.getBookmarksHash(xOdfReloadedDoc)) + xOdfReloadedDoc.close(True) + + ## reloading document as MS Word 97 doc and testing again + ## MsWord Hash is unstable over different systems + # xMsWordReloadedDoc = self.reloadFrom(tempdir, "MS Word 97", "doc") + # self.assertEqual(self.expectedHashes['nMsWordReloadHash'], + # self.getBookmarksHash(xMsWordReloadedDoc)) + # xMsWordReloadedDoc.close(True) + + print('tests ok') + + def setupBookmarks(self): + xCursor = self.xText.createTextCursor() + for nPara in range(10): + for nBookmark in range(100): + s = "P{}word{}".format(nPara, nBookmark) + xCursor.gotoEnd(False) + xCursor.setString(s) + xBookmark = self.xDoc.createInstance("com.sun.star.text.Bookmark") + xBookmark.setName(s) + self.xText.insertTextContent(xCursor, xBookmark, True) + xCursor.End.setString(" ") + + self.xText.insertControlCharacter(xCursor.End, PARAGRAPH_BREAK, False) + + def getBookmarksHash(self, doc): + _hash = sha1() + xBookmarks = doc.getBookmarks() + for xBookmark in xBookmarks: + s = '{}:{};'.format(xBookmark.Name, + xBookmark.getAnchor().getString().replace("\r\n", "\n")) + + _hash.update(str.encode(s)) + + return int(_hash.hexdigest(), 16) + + def insertRandomParts(self, seed): + random.seed(seed) + xCursor = self.xText.createTextCursor() + + for i in range(600): + xCursor.goRight(random.randrange(100), False) + xCursor.setString(str(random.getrandbits(64))) + + def deleteRandomParts(self, seed): + random.seed(seed) + xCursor = self.xText.createTextCursor() + + for i in range(600): + xCursor.goRight(random.randrange(100), False) + xCursor.goRight(random.randrange(20), True) + xCursor.setString("") + + def insertLinebreaks(self, seed): + random.seed(seed) + xCursor = self.xText.createTextCursor() + + for i in range(30): + xCursor.goRight(random.randrange(300), False) + self.xText.insertControlCharacter(xCursor, PARAGRAPH_BREAK, False) + + def reloadFrom(self, tempdir, sFilter, sExtension): + sFileUrl = os.path.join(tempdir, "Bookmarktest.{}".format(sExtension)) + sFileUrl = systemPathToFileUrl(sFileUrl) + store_props = mkPropertyValues(Override=True, FilterName=sFilter) + self.xDoc.storeToURL(sFileUrl, store_props) + desktop = self.__class__._desktop + load_props = mkPropertyValues(Hidden=True, ReadOnly=False) + return desktop.loadComponentFromURL(sFileUrl, "_default", 0, load_props) + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/check_change_color.py b/sw/qa/python/check_change_color.py new file mode 100644 index 000000000..07b622031 --- /dev/null +++ b/sw/qa/python/check_change_color.py @@ -0,0 +1,100 @@ +''' + This file is part of the LibreOffice project. + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + This file incorporates work covered by the following license notice: + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 . +''' + +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK +from com.sun.star.awt.FontUnderline import SINGLE + +class CheckChangeColor(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls.RED = 0xFF0000 + cls.BLUE = 0x0000FF + cls.GREEN = 0x008000 + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_change_color(self): + xDoc = CheckChangeColor._uno.openEmptyWriterDoc() + xPageStyles = xDoc.StyleFamilies["PageStyles"] + xPageStyle = xPageStyles["Standard"] + + self.assertEqual(xPageStyle.BackColor, -1) + self.assertEqual(xPageStyle.IsLandscape, False) + + xPageStyle.BackColor = self.RED + xPageStyle.IsLandscape = True + self.assertEqual(xPageStyle.BackColor, self.RED) + self.assertEqual(xPageStyle.IsLandscape, True) + + xPageStyle.GridColor = self.GREEN + self.assertEqual(xPageStyle.GridColor, self.GREEN) + + xPageStyle.FootnoteLineColor = self.BLUE + self.assertEqual(xPageStyle.FootnoteLineColor, self.BLUE) + + xDoc.dispose() + + def test_change_text_color(self): + xDoc = CheckChangeColor._uno.openEmptyWriterDoc() + cursor = xDoc.Text.createTextCursor() + + cursor.CharColor = self.RED + self.assertEqual(cursor.CharColor, self.RED) + + cursor.CharBackColor = self.BLUE + self.assertEqual(cursor.CharBackColor, self.BLUE) + + self.assertEqual(cursor.CharUnderlineHasColor, False) + cursor.CharUnderlineHasColor = True + cursor.CharUnderline = SINGLE + cursor.CharUnderlineColor = self.GREEN + self.assertEqual(cursor.CharUnderlineColor, self.GREEN) + self.assertEqual(cursor.CharUnderline, SINGLE) + self.assertEqual(cursor.CharUnderlineHasColor, True) + + xDoc.Text.insertString(cursor, "One foo to rule them all", 0) + + self.assertEqual(xDoc.getText().createTextCursor().CharColor, self.RED) + self.assertEqual(xDoc.getText().createTextCursor().CharBackColor, self.BLUE) + self.assertEqual(xDoc.getText().createTextCursor().CharUnderlineColor, self.GREEN) + + xDoc.dispose() + + def test_change_paragraph_color(self): + xDoc = CheckChangeColor._uno.openEmptyWriterDoc() + cursor = xDoc.Text.createTextCursor() + + cursor.ParaBackColor = self.RED + self.assertEqual(cursor.ParaBackColor, self.RED) + + xDoc.Text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False) + xDoc.Text.insertString(cursor, "One foo to find them all", 0) + + self.assertEqual(xDoc.getText().createTextCursor().ParaBackColor, self.RED) + + xDoc.dispose() + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/check_cross_references.py b/sw/qa/python/check_cross_references.py new file mode 100644 index 000000000..7778ff5f2 --- /dev/null +++ b/sw/qa/python/check_cross_references.py @@ -0,0 +1,222 @@ +''' + This file is part of the LibreOffice project. + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + This file incorporates work covered by the following license notice: + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 . +''' +import unittest +from com.sun.star.text.ReferenceFieldPart import (NUMBER, NUMBER_NO_CONTEXT, NUMBER_FULL_CONTEXT, TEXT) +from com.sun.star.text.ReferenceFieldSource import BOOKMARK +from org.libreoffice.unotest import UnoInProcess + + +class CheckCrossReferences(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls.document = cls._uno.openDocFromTDOC("CheckCrossReferences.odt") + cls.xParaEnum = None + cls.xPortionEnum = None + cls.xFieldsRefresh = None + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + # HACK in case cls.document holds a UNO proxy to an SwXTextDocument (whose dtor calls + # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be + # garbage-collected after VCL has already been deinitialized: + cls.document = None + + def getNextField(self): + while True: + while self.xPortionEnum is None: + if (not(self.xParaEnum.hasMoreElements())): + self.fail("Cannot retrieve next field.") + + aPara = self.xParaEnum.nextElement() + self.xPortionEnum = aPara.createEnumeration() + + if (self.xPortionEnum is None): + break + + for xPortionProps in self.xPortionEnum: + sPortionType = str(xPortionProps.getPropertyValue("TextPortionType")) + if (sPortionType == "TextField"): + xField = xPortionProps.getPropertyValue("TextField") + self.assertTrue(xField, "Cannot retrieve next field") + return xField + + self.xPortionEnum = None + return None # unreachable + + def getFieldProps(self, xField): + xProps = xField + self.assertTrue(xProps, "Cannot retrieve field properties.") + return xProps + + def checkField(self, xField, xProps, nFormat, aExpectedFieldResult): + # set requested format + xProps.setPropertyValue("ReferenceFieldPart", int(nFormat)) + + # refresh fields in order to get new format applied + self.xFieldsRefresh.refresh() + aFieldResult = xField.getPresentation(False) + self.assertEqual(aExpectedFieldResult, aFieldResult, "set reference field format doesn't result in correct field result") + + def test_checkCrossReferences(self): + xParaEnumAccess = self.document.getText() + self.xParaEnum = xParaEnumAccess.createEnumeration() + + # get field refresher + xFieldSupp = self.__class__.document + self.xFieldsRefresh = xFieldSupp.getTextFields() + + # check first reference field + # strings for checking + FieldResult1 = "*i*" + FieldResult2 = "+b+*i*" + FieldResult3 = "-1-+b+*i*" + FieldResult4 = "1" + FieldResult5 = "1" + FieldResult6 = "A.1" + FieldResult7 = " 2.(a)" + FieldResult8 = " 2.(b)" + FieldResult9 = " 2" + FieldResult10 = " 1.(a)" + FieldResult11 = "(b)" + FieldResult12 = "(a)" + FieldResult13 = " 1" + + # variables for current field + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult2) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult1) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult3) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult1) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult1) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult3) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult3) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult1) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult3) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult5) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult4) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult6) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult4) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult4) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult6) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult6) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult4) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult6) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult7) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult12) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult7) + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult8) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult11) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult8) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult9) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult9) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult9) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult13) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult13) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult13) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult10) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult12) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult10) + + xField = self.getNextField() + xProps = self.getFieldProps(xField) + self.checkField(xField, xProps, NUMBER, FieldResult12) + self.checkField(xField, xProps, NUMBER_NO_CONTEXT, FieldResult12) + self.checkField(xField, xProps, NUMBER_FULL_CONTEXT, FieldResult7) + + # insert a certain cross-reference bookmark and a reference field to this bookmark + # restart paragraph enumeration + xParaEnumAccess = self.__class__.document.getText() + self.xParaEnum = xParaEnumAccess.createEnumeration() + + # iterate on the paragraphs to find certain paragraph to insert the bookmark + for xParaTextRange in self.xParaEnum: + + if xParaTextRange.getString() == "J": + break + else: + xParaTextRange = None + + self.assertTrue(xParaTextRange, "Cannot find paragraph to insert cross-reference bookmark") + + # insert bookmark + xFac = self.__class__.document + cBookmarkName = "__RefNumPara__47114711" + xBookmark = xFac.createInstance("com.sun.star.text.Bookmark") + + if xBookmark is not None: + xName = xBookmark + xName.setName(cBookmarkName) + xBookmark.attach(xParaTextRange.getStart()) + + # insert reference field, which references the inserted bookmark + xNewField = xFac.createInstance("com.sun.star.text.TextField.GetReference") + + if xNewField is not None: + xFieldProps = xNewField + xFieldProps.setPropertyValue("ReferenceFieldPart", int(TEXT)) + xFieldProps.setPropertyValue("ReferenceFieldSource", int(BOOKMARK)) + xFieldProps.setPropertyValue("SourceName", cBookmarkName) + xFieldTextRange = self.xParaEnum.nextElement() + xNewField.attach(xFieldTextRange.getEnd()) + self.xFieldsRefresh.refresh() + + # check inserted reference field + xField = xNewField + self.assertEqual("J", xField.getPresentation(False), "inserted reference field doesn't has correct field result") + + xParaTextRange.getStart().setString("Hallo new bookmark: ") + self.xFieldsRefresh.refresh() + self.assertEqual("Hallo new bookmark: J", xField.getPresentation(False), "inserted reference field doesn't has correct field result") + + +if __name__ == "__main__": + unittest.main() diff --git a/sw/qa/python/check_drawpage.py b/sw/qa/python/check_drawpage.py new file mode 100644 index 000000000..ef28490ce --- /dev/null +++ b/sw/qa/python/check_drawpage.py @@ -0,0 +1,44 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + +from com.sun.star.text.TextContentAnchorType import AT_PARAGRAPH + +class CheckDrawPage(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + """ + Test that grouping shapes also works with a single shape. + """ + def test_group_single_shape(self): + xDoc = self.__class__._uno.openEmptyWriterDoc() + page = xDoc.DrawPage + collection = self.__class__._uno.xContext.ServiceManager.createInstance( 'com.sun.star.drawing.ShapeCollection' ) + shape = xDoc.createInstance('com.sun.star.drawing.TextShape') + shape.AnchorType = AT_PARAGRAPH + page.add(shape) + collection.add(shape) + shapegroup = page.group(collection) + + self.assertEqual(shapegroup.Count, 1) + + xDoc.close(True) + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/check_fields.py b/sw/qa/python/check_fields.py new file mode 100644 index 000000000..eb6dd2dc1 --- /dev/null +++ b/sw/qa/python/check_fields.py @@ -0,0 +1,53 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class CheckFields(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_fdo39694_load(self): + placeholders = ["<Kadr1>", "<Kadr2>", "<Kadr3>", "<Kadr4>", "<Pnname>", "<Pvname>", "<Pgeboren>"] + xDoc = self.__class__._uno.openTemplateFromTDOC("fdo39694.ott") + xEnumerationAccess = xDoc.getTextFields() + xFieldEnum = xEnumerationAccess.createEnumeration() + for xField in xFieldEnum: + if xField.supportsService("com.sun.star.text.TextField.JumpEdit"): + xAnchor = xField.getAnchor() + read_content = xAnchor.getString() + self.assertTrue(read_content in placeholders, + "field %s is not contained: " % read_content) + xDoc.close(True) + + def test_fdo42073(self): + xDoc = self.__class__._uno.openEmptyWriterDoc() + xBodyText = xDoc.getText() + xCursor = xBodyText.createTextCursor() + xTextField = xDoc.createInstance("com.sun.star.text.TextField.Input") + xBodyText.insertTextContent(xCursor, xTextField, True) + read_content = xTextField.getPropertyValue("Content") + self.assertEqual("", read_content) + content = "this is not surprising" + xTextField.setPropertyValue("Content", content) + read_content = xTextField.getPropertyValue("Content") + self.assertEqual(content, read_content) + xDoc.close(True) + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/check_flies.py b/sw/qa/python/check_flies.py new file mode 100644 index 000000000..0e60b2195 --- /dev/null +++ b/sw/qa/python/check_flies.py @@ -0,0 +1,119 @@ +''' + This is file is part of the LibreOffice project. + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + This file incorporates work covered by the following license notice: + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 . +''' + +from org.libreoffice.unotest import UnoInProcess +import unittest + + +class CheckFlies(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_checkFlies(self): + document = self.__class__._uno.openDocFromTDOC("CheckFlies.odt") + xTFS = document + self.checkTextFrames(xTFS) + xTGOS = document + self.checkGraphicFrames(xTGOS) + xTEOS = document + self.checkEmbeddedFrames(xTEOS) + + def checkEmbeddedFrames(self, xTGOS): + vExpectedEmbeddedFrames = ["Object1"] + nEmbeddedFrames = len(vExpectedEmbeddedFrames) + xEmbeddedFrames = xTGOS.getEmbeddedObjects() + nCurrentFrameIdx = 0 + + print (xEmbeddedFrames) + for sFrameName in xEmbeddedFrames.getElementNames(): + vExpectedEmbeddedFrames.remove(sFrameName) + # raises ValueError if not found + print (sFrameName) + xEmbeddedFrames[sFrameName] + self.assertTrue(xEmbeddedFrames.hasByName(sFrameName), + "Could not find embedded frame by name.") + + self.assertTrue(not(vExpectedEmbeddedFrames), + "Missing expected embedded frames.") + + xEmbeddedFramesIdx = xEmbeddedFrames + + self.assertEqual(nEmbeddedFrames, len(xEmbeddedFramesIdx), + "Unexpected number of embedded frames reported") + + for nCurrentFrameIdx in range(len(xEmbeddedFramesIdx)): + xEmbeddedFramesIdx[nCurrentFrameIdx] + + def checkGraphicFrames(self, xTGOS): + vExpectedGraphicFrames = ["graphics1"] + nGraphicFrames = len(vExpectedGraphicFrames) + xGraphicFrames = xTGOS.getGraphicObjects() + nCurrentFrameIdx = 0 + for sFrameName in xGraphicFrames.getElementNames(): + vExpectedGraphicFrames.remove(sFrameName) + # raises ValueError if not found + xGraphicFrames[sFrameName] + self.assertTrue( + sFrameName in xGraphicFrames, + "Could not find graphics frame by name.") + self.assertTrue( + not(vExpectedGraphicFrames), + "Missing expected graphics frames.") + + xGraphicFramesIdx = xGraphicFrames + self.assertEqual(nGraphicFrames, len(xGraphicFramesIdx), + "Unexpected number of graphics frames reported") + + for nCurrentFrameIdx in range(len(xGraphicFramesIdx)): + xGraphicFramesIdx[nCurrentFrameIdx] + + def checkTextFrames(self, xTFS): + vExpectedTextFrames = ["Frame1", "Frame2"] + nTextFrames = len(vExpectedTextFrames) + xTextFrames = xTFS.getTextFrames() + nCurrentFrameIdx = 0 + + for sFrameName in xTextFrames.getElementNames(): + vExpectedTextFrames.remove(sFrameName) + # raises ValueError if not found + xTextFrames[sFrameName] + self.assertTrue( + sFrameName in xTextFrames, + "Could not find text frame by name.") + + self.assertTrue( + not(vExpectedTextFrames), "Missing expected text frames.") + + xTextFramesIdx = xTextFrames + + self.assertEqual(nTextFrames, len(xTextFrames), + "Unexpected number of text frames reported") + + for nCurrentFrameIdx in range(len(xTextFramesIdx)): + xTextFramesIdx[nCurrentFrameIdx] + + +if __name__ == "__main__": + unittest.main() diff --git a/sw/qa/python/check_index.py b/sw/qa/python/check_index.py new file mode 100644 index 000000000..807f3e319 --- /dev/null +++ b/sw/qa/python/check_index.py @@ -0,0 +1,122 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +import unohelper +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK +from com.sun.star.util import XRefreshListener + + +class RefreshListener(XRefreshListener, unohelper.Base): + def __init__(self): + self.m_refreshed = False + self.m_disposed = False + + # Gets called when index is disposed + def disposing(self, event): + self.m_disposed = True + + # Gets called when index is refreshed + def refreshed(self, event): + self.m_refreshed = True + + +class CheckIndex(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + # Gets called every time a new test is run + def setUp(self): + """ + Every test should run with a new Instance of an Index and + Refresh Listener + So we need to reload the text of the document and initialize + the corresponding Cursor + """ + self.xDoc = self.__class__._uno.openEmptyWriterDoc() + self.xIndex = self.xDoc.createInstance( + "com.sun.star.text.ContentIndex") + self.xBodyText = self.xDoc.getText() + self.xCursor = self.xBodyText.createTextCursor() + self.xIndex.setPropertyValue("CreateFromOutline", True) + self.xBodyText.insertTextContent(self.xCursor, self.xIndex, True) + # Create Refresh Listener and bind it to the Index + self.listener = RefreshListener() + self.xIndex.addRefreshListener(self.listener) + + def tearDown(self): + """ + Dispose Index and Document and check if the index was + deleted while the tests + """ + self.assertFalse(self.listener.m_disposed, + "Unexpected disparue of the Refresh Listener!") + self.xIndex.dispose() + self.assertTrue(self.listener.m_disposed, + "Could not dispose Refresh Listener") + self.xDoc.dispose() + + def insert_heading(self, text): + """ + Insert a Heading at the end of the document + """ + self.xCursor.gotoEnd(False) + self.xBodyText.insertControlCharacter(self.xCursor, + PARAGRAPH_BREAK, False) + self.xCursor.gotoEnd(False) + self.xCursor.setString(text) + self.xCursor.gotoStartOfParagraph(True) + self.xCursor.setPropertyValue("ParaStyleName", "Heading 1") + + def test_index_refresh(self): + """ + Try to insert a heading at the index, refresh the index and + retrieve the heading again + """ + heading = "The best test heading you have seen in your entire life" + self.insert_heading(heading) + self.xIndex.refresh() + self.assertTrue(self.listener.m_refreshed, "Failed to refresh index!") + self.listener.m_refreshed = False + self.xCursor.gotoRange(self.xIndex.getAnchor().getEnd(), False) + self.xCursor.gotoStartOfParagraph(True) + # Get String at current position and search for the heading + text = self.xCursor.getString() + self.assertGreaterEqual(text.find(heading), 0, + "Failed to insert heading at index " + "and retrieve it again!") + + def test_index_update(self): + """ + Try to insert a heading at the index, update the index + and retrieve the heading again + """ + heading = "Heading to test the index update" + self.insert_heading(heading) + self.xIndex.update() + self.assertTrue(self.listener.m_refreshed, "Failed to update index!") + self.listener.m_refreshed = False + self.xCursor.gotoRange(self.xIndex.getAnchor().getEnd(), False) + self.xCursor.gotoStartOfParagraph(True) + # Get String at current position and search for the heading + text = self.xCursor.getString() + self.assertGreaterEqual(text.find(heading), 0, + "Failed to insert a heading at Index and " + "retrieve it again!") + + +if __name__ == "__main__": + unittest.main() diff --git a/sw/qa/python/check_indexed_property_values.py b/sw/qa/python/check_indexed_property_values.py new file mode 100644 index 000000000..4179872ce --- /dev/null +++ b/sw/qa/python/check_indexed_property_values.py @@ -0,0 +1,85 @@ +''' + This file is part of the LibreOffice project. + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + This file incorporates work covered by the following license notice: + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 . +''' + +import unittest +import uno +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.beans import PropertyValue +from com.sun.star.lang import IllegalArgumentException +from com.sun.star.lang import IndexOutOfBoundsException + + +class CheckIndexedPropertyValues(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls.xContext = cls._uno.getContext() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_checkIndexedPropertyValues(self): + xServiceManager = self.xContext.ServiceManager + xCont = xServiceManager.createInstanceWithContext('com.sun.star.document.IndexedPropertyValues', + self.xContext) + + p1 = PropertyValue(Name="Jupp", Value="GoodGuy") + prop1 = uno.Any("[]com.sun.star.beans.PropertyValue", (p1,)) + + p2 = PropertyValue(Name="Horst", Value="BadGuy") + prop2 = uno.Any("[]com.sun.star.beans.PropertyValue", (p2,)) + + p3 = PropertyValue(Name="Peter", Value="FamilyGuy") + prop3 = uno.Any("[]com.sun.star.beans.PropertyValue", (p3,)) + + t = xCont.getElementType() + self.assertEqual(0, len(xCont), "Initial container is not empty") + uno.invoke(xCont, "insertByIndex", (0, prop1)) + + ret = xCont[0] + self.assertEqual(p1.Name, ret[0].Name) + self.assertEqual(p1.Value, ret[0].Value) + + uno.invoke(xCont, "replaceByIndex", (0, prop2)) + ret = xCont[0] + self.assertEqual(p2.Name, ret[0].Name) + self.assertEqual(p2.Value, ret[0].Value) + + xCont.removeByIndex(0) + self.assertTrue(not(xCont.hasElements()) and len(xCont) == 0, "Could not remove PropertyValue") + uno.invoke(xCont, "insertByIndex", (0, prop1)) + uno.invoke(xCont, "insertByIndex", (1, prop2)) + self.assertTrue(xCont.hasElements() and len(xCont) == 2, "Did not insert PropertyValue") + + uno.invoke(xCont, "insertByIndex", (1, prop2)) + uno.invoke(xCont, "insertByIndex", (1, prop3)) + ret = xCont[1] + self.assertEqual(p3.Name, ret[0].Name) + self.assertEqual(p3.Value, ret[0].Value) + + with self.assertRaises(IndexOutOfBoundsException): + uno.invoke(xCont, "insertByIndex", (25, prop2)) + + with self.assertRaises(IndexOutOfBoundsException): + xCont.removeByIndex(25) + + with self.assertRaises(IllegalArgumentException): + uno.invoke(xCont, "insertByIndex", (3, "Example String")) diff --git a/sw/qa/python/check_named_property_values.py b/sw/qa/python/check_named_property_values.py new file mode 100644 index 000000000..d11ee2e46 --- /dev/null +++ b/sw/qa/python/check_named_property_values.py @@ -0,0 +1,83 @@ +''' + This file is part of the LibreOffice project. + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + This file incorporates work covered by the following license notice: + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 . +''' + +import unittest +import uno +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.beans import PropertyValue +from com.sun.star.container import ElementExistException +from com.sun.star.lang import IllegalArgumentException +from com.sun.star.container import NoSuchElementException + + +class CheckNamedPropertyValues(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls.xContext = cls._uno.getContext() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_checkNamedPropertyValues(self): + + xServiceManager = self.xContext.ServiceManager + xCont = xServiceManager.createInstanceWithContext('com.sun.star.document.NamedPropertyValues', + self.xContext) + + p1 = PropertyValue(Name="Jupp", Value="GoodGuy") + prop1 = uno.Any("[]com.sun.star.beans.PropertyValue", (p1,)) + + p2 = PropertyValue(Name="Horst", Value="BadGuy") + prop2 = uno.Any("[]com.sun.star.beans.PropertyValue", (p2,)) + + t = xCont.getElementType() + self.assertFalse(xCont.hasElements(), "Initial container is not empty") + + uno.invoke(xCont, "insertByName", ("prop1", prop1)) + ret = xCont["prop1"] + self.assertEqual(p1.Name, ret[0].Name) + self.assertEqual(p1.Value, ret[0].Value) + + uno.invoke(xCont, "replaceByName", ("prop1", prop2)) + ret = xCont["prop1"] + self.assertEqual(p2.Name, ret[0].Name) + self.assertEqual(p2.Value, ret[0].Value) + + xCont.removeByName("prop1") + self.assertFalse(xCont.hasElements(), "Could not remove PropertyValue.") + uno.invoke(xCont, "insertByName", ("prop1", prop1)) + uno.invoke(xCont, "insertByName", ("prop2", prop2)) + self.assertTrue(xCont.hasElements(), "Did not insert PropertyValue") + names = xCont.getElementNames() + self.assertEqual(2, len(names), "Not all element names were returned") + + for i in range(len(names)): + self.assertIn(names[i], ["prop1", "prop2"], "Got a wrong element name") + + with self.assertRaises(ElementExistException): + uno.invoke(xCont, "insertByName", ("prop2", prop1)) + + with self.assertRaises(IllegalArgumentException): + uno.invoke(xCont, "insertByName", ("prop3", "Example String")) + + with self.assertRaises(NoSuchElementException): + xCont.removeByName("prop3") diff --git a/sw/qa/python/check_range_properties.py b/sw/qa/python/check_range_properties.py new file mode 100644 index 000000000..26babf522 --- /dev/null +++ b/sw/qa/python/check_range_properties.py @@ -0,0 +1,39 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class CheckRangeProperties(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + # see tdf#127534 + def test_TextRangeProperties(self): + xDoc = CheckRangeProperties._uno.openEmptyWriterDoc() + xBodyText = xDoc.getText() + xCursor = xBodyText.createTextCursor() + xBodyText.insertString(xCursor, "Hello world", 0) + xTextRange = list(xBodyText)[0] + pnames = [p.Name for p in xTextRange.PropertySetInfo.Properties] + xTextRange.getPropertyValues(pnames) + xDoc.dispose() + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_styles.py b/sw/qa/python/check_styles.py new file mode 100644 index 000000000..d19f9850b --- /dev/null +++ b/sw/qa/python/check_styles.py @@ -0,0 +1,245 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.container import NoSuchElementException +from com.sun.star.beans import UnknownPropertyException +from com.sun.star.lang import IndexOutOfBoundsException +from com.sun.star.lang import IllegalArgumentException + + +class CheckStyle(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_StyleFamilies(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xStyleFamilies = xDoc.StyleFamilies + self.assertEqual(xStyleFamilies.ImplementationName, "SwXStyleFamilies") + self.assertEqual(len(xStyleFamilies.SupportedServiceNames), 1) + + for servicename in xStyleFamilies.SupportedServiceNames: + self.assertIn(servicename, ["com.sun.star.style.StyleFamilies"]) + self.assertTrue(xStyleFamilies.supportsService(servicename)) + self.assertFalse(xStyleFamilies.supportsService("foobarbaz")) + self.assertTrue(xStyleFamilies.hasElements()) + self.assertRegex(str(xStyleFamilies.ElementType), r"com\.sun\.star\.container\.XNameContainer") + self.assertEqual(len(xStyleFamilies.ElementNames), 7) + + for sFamilyname in xStyleFamilies.ElementNames: + self.assertIn(sFamilyname, + ["CharacterStyles", "ParagraphStyles", "PageStyles", "FrameStyles", "NumberingStyles", "TableStyles", "CellStyles"]) + + with self.assertRaises(NoSuchElementException): + xStyleFamilies.getByName("foobarbaz") + xDoc.dispose() + + def __test_StyleFamily(self, xFamily, vExpectedNames, sElementImplName): + self.assertEqual(xFamily.ImplementationName, "XStyleFamily") + self.assertEqual(len(xFamily.SupportedServiceNames), 1) + + for sServicename in xFamily.SupportedServiceNames: + self.assertIn(sServicename, ["com.sun.star.style.StyleFamily"]) + self.assertTrue(xFamily.supportsService(sServicename)) + self.assertFalse(xFamily.supportsService("foobarbaz")) + self.assertTrue(xFamily.hasElements()) + self.assertRegex(str(xFamily.ElementType), r"com\.sun\.star\.style\.XStyle") + + with self.assertRaises(NoSuchElementException): + xFamily.getByName("foobarbaz") + + with self.assertRaises(IndexOutOfBoundsException): + xFamily.getByIndex(-1) + + for sStylename in xFamily.ElementNames: + self.assertTrue(xFamily.hasByName(sStylename)) + self.assertEqual(xFamily[sStylename].ImplementationName, sElementImplName) + self.assertFalse(xFamily[sStylename].isUserDefined()) + + vExpectedNames.sort() + vNames = list(xFamily.ElementNames) + vNames.sort() + self.assertListEqual(vNames, vExpectedNames) + + def __test_StyleFamilyIndex(self, xFamily, vExpectedNames, sElementImplName): + self.assertEqual(len(xFamily), len(vExpectedNames)) + + for nIndex in range(len(xFamily)): + xStyle = xFamily[nIndex] + self.assertEqual(xStyle.ImplementationName, sElementImplName) + self.assertIn(xStyle.Name, vExpectedNames) + self.assertFalse(xStyle.isUserDefined()) + + def __test_StyleFamilyInsert(self, xDoc, xFamily, vExpectedNames, sRightStyle, sWrongStyle): + xRightStyle = xDoc.createInstance(sRightStyle) + xRightStyle.Name = "RightStyleOld" + xWrongStyle = xDoc.createInstance(sWrongStyle) + xWrongStyle.Name = "WrongtStyleOld" + xFamily.insertByName("RightStyle", xRightStyle) + self.assertEqual(xRightStyle.Name, "RightStyle") + self.assertTrue(xRightStyle.isUserDefined()) + self.assertEqual(xFamily[xRightStyle.Name], xRightStyle) + self.assertNotEqual(xFamily[xRightStyle.Name], xWrongStyle) + self.assertTrue(xFamily[xRightStyle.Name].isUserDefined()) + xRightStyle2 = xDoc.createInstance(sRightStyle) + xRightStyle2.Name = "RightStyle2Old" + xFamily.replaceByName("RightStyle", xRightStyle2) + self.assertEqual(xRightStyle2.Name, "RightStyle") + self.assertEqual(xFamily[xRightStyle2.Name], xRightStyle2) + xFamily.removeByName(xRightStyle2.Name) + + with self.assertRaises(NoSuchElementException): + nope = xFamily.getByName("RightStyleOld") + with self.assertRaises(NoSuchElementException): + nope = xFamily.getByName("RightStyle") + + with self.assertRaises(NoSuchElementException): + nope = xFamily.getByName("RightStyle2Old") + + with self.assertRaises(NoSuchElementException): + nope = xFamily.getByName("RightStyle2") + + with self.assertRaises(IllegalArgumentException): + xFamily.insertByName("WrongStyle", xWrongStyle) + + with self.assertRaises(NoSuchElementException): + nope = xFamily.getByName("WrongStyle") + + def test_CharacterFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xCharStyles = xDoc.StyleFamilies["CharacterStyles"] + vEmptyDocStyles = ['Standard', 'Footnote Symbol', 'Page Number', 'Caption characters', 'Drop Caps', 'Numbering Symbols', 'Bullet Symbols', 'Internet link', 'Visited Internet Link', 'Placeholder', 'Index Link', 'Endnote Symbol', 'Line numbering', 'Main index entry', 'Footnote anchor', 'Endnote anchor', 'Rubies', 'Vertical Numbering Symbols', 'Emphasis', 'Citation', 'Strong Emphasis', 'Source Text', 'Example', 'User Entry', 'Variable', 'Definition', 'Teletype'] + self.__test_StyleFamily(xCharStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyIndex(xCharStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyInsert(xDoc, xCharStyles, vEmptyDocStyles, "com.sun.star.style.CharacterStyle", "com.sun.star.style.ParagraphStyle") + xDoc.dispose() + + def test_ParagraphFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xParaStyles = xDoc.StyleFamilies["ParagraphStyles"] + vEmptyDocStyles = ['Standard', 'Heading', 'Text body', 'List', 'Caption', 'Index', 'First line indent', 'Hanging indent', 'Text body indent', 'Salutation', 'Signature', 'List Indent', 'Marginalia', 'Heading 1', 'Heading 2', 'Heading 3', 'Heading 4', 'Heading 5', 'Heading 6', 'Heading 7', 'Heading 8', 'Heading 9', 'Heading 10', 'Title', 'Subtitle', 'Appendix', 'Numbering 1 Start', 'Numbering 1', 'Numbering 1 End', 'Numbering 1 Cont.', 'Numbering 2 Start', 'Numbering 2', 'Numbering 2 End', 'Numbering 2 Cont.', 'Numbering 3 Start', 'Numbering 3', 'Numbering 3 End', 'Numbering 3 Cont.', 'Numbering 4 Start', 'Numbering 4', 'Numbering 4 End', 'Numbering 4 Cont.', 'Numbering 5 Start', 'Numbering 5', 'Numbering 5 End', 'Numbering 5 Cont.', 'List 1 Start', 'List 1', 'List 1 End', 'List 1 Cont.', 'List 2 Start', 'List 2', 'List 2 End', 'List 2 Cont.', 'List 3 Start', 'List 3', 'List 3 End', 'List 3 Cont.', 'List 4 Start', 'List 4', 'List 4 End', 'List 4 Cont.', 'List 5 Start', 'List 5', 'List 5 End', 'List 5 Cont.', 'Index Heading', 'Index 1', 'Index 2', 'Index 3', 'Index Separator', 'Contents Heading', 'Contents 1', 'Contents 2', 'Contents 3', 'Contents 4', 'Contents 5', 'User Index Heading', 'User Index 1', 'User Index 2', 'User Index 3', 'User Index 4', 'User Index 5', 'Contents 6', 'Contents 7', 'Contents 8', 'Contents 9', 'Contents 10', 'Figure Index Heading', 'Figure Index 1', 'Object index heading', 'Object index 1', 'Table index heading', 'Table index 1', 'Bibliography Heading', 'Bibliography 1', 'User Index 6', 'User Index 7', 'User Index 8', 'User Index 9', 'User Index 10', 'Header and Footer','Header', 'Header left', 'Header right', 'Footer', 'Footer left', 'Footer right', 'Table Contents', 'Table Heading', 'Illustration', 'Table', 'Text','Figure', 'Frame contents', 'Footnote', 'Addressee', 'Sender', 'Endnote', 'Drawing', 'Quotations', 'Preformatted Text', 'Horizontal Line', 'List Contents', 'List Heading'] + self.__test_StyleFamily(xParaStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyIndex(xParaStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyInsert(xDoc, xParaStyles, vEmptyDocStyles, "com.sun.star.style.ParagraphStyle", "com.sun.star.style.CharacterStyle") + xDoc.dispose() + + def test_PageFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xPageStyles = xDoc.StyleFamilies["PageStyles"] + vEmptyDocStyles = ['Standard', 'First Page', 'Left Page', 'Right Page', 'Envelope', 'Index', 'HTML', 'Footnote', 'Endnote', 'Landscape'] + self.__test_StyleFamily(xPageStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyIndex(xPageStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyInsert(xDoc, xPageStyles, vEmptyDocStyles, "com.sun.star.style.PageStyle", "com.sun.star.style.CharacterStyle") + xDoc.dispose() + + def test_FrameFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xFrameStyles = xDoc.StyleFamilies["FrameStyles"] + vEmptyDocStyles = ['Formula', 'Frame', 'Graphics', 'Labels', 'Marginalia', 'OLE', 'Watermark'] + self.__test_StyleFamily(xFrameStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyIndex(xFrameStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyInsert(xDoc, xFrameStyles, vEmptyDocStyles, "com.sun.star.style.FrameStyle", "com.sun.star.style.CharacterStyle") + xDoc.dispose() + + def test_NumberingFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xNumberingStyles = xDoc.StyleFamilies["NumberingStyles"] + vEmptyDocStyles = ['No List','List 1', 'List 2', 'List 3', 'List 4', 'List 5', 'Numbering 123', 'Numbering ABC', 'Numbering abc', 'Numbering IVX', 'Numbering ivx'] + self.__test_StyleFamily(xNumberingStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyIndex(xNumberingStyles, vEmptyDocStyles, "SwXStyle") + self.__test_StyleFamilyInsert(xDoc, xNumberingStyles, vEmptyDocStyles, "com.sun.star.style.NumberingStyle", "com.sun.star.style.CharacterStyle") + xDoc.dispose() + + def test_TableFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xTableStyles = xDoc.StyleFamilies["TableStyles"] + vEmptyDocStyles = ['Default Style'] + self.__test_StyleFamily(xTableStyles, vEmptyDocStyles, "SwXTextTableStyle") + self.__test_StyleFamilyIndex(xTableStyles, vEmptyDocStyles, "SwXTextTableStyle") + self.__test_StyleFamilyInsert(xDoc, xTableStyles, vEmptyDocStyles, "com.sun.star.style.TableStyle", "com.sun.star.style.CharacterStyle") + for sStyleName in vEmptyDocStyles: + self.assertIsNotNone(xTableStyles.getByName(sStyleName)) + #check SwXTextCellStyles + vCellStyles = ["first-row", "last-row", "first-column", "last-column", "body", "even-rows", "odd-rows", "even-columns", "odd-columns", "background"] + xDefaultTableStyle = xTableStyles[0] + for sCellStyle in vCellStyles: + xCellStyle = xDefaultTableStyle.getByName(sCellStyle) + self.assertIsNotNone(xCellStyle.getPropertyValue("BackColor")) + with self.assertRaises(UnknownPropertyException): + xCellStyle.getPropertyValue("foobarbaz") + xDoc.dispose() + + def test_tableStyleIsInUse(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xBodyText = xDoc.getText() + xCursor = xBodyText.createTextCursor() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(1, 1) + xBodyText.insertTextContent(xCursor, xTable, True) + xDefaultTableStyle = xDoc.StyleFamilies.getByName("TableStyles").getByName("Default Style") + xDefaultCellStyle = xDoc.StyleFamilies.getByName("CellStyles").getByName("Default Style.1") + self.assertFalse(xDefaultTableStyle.isInUse()) + self.assertFalse(xDefaultCellStyle.isInUse()) + xTable.setPropertyValue("TableTemplateName", "Default Style") + self.assertTrue(xDefaultTableStyle.isInUse()) + self.assertTrue(xDefaultCellStyle.isInUse()) + xTable.setPropertyValue("TableTemplateName", "") + self.assertFalse(xDefaultTableStyle.isInUse()) + self.assertFalse(xDefaultCellStyle.isInUse()) + xTableStyle = xDoc.createInstance("com.sun.star.style.TableStyle") + self.assertFalse(xTableStyle.isInUse()) + xDoc.StyleFamilies.getByName("TableStyles").insertByName("Test Table Style", xTableStyle) + self.assertFalse(xTableStyle.isInUse()) + xTable.setPropertyValue("TableTemplateName", "Test Table Style") + self.assertTrue(xTableStyle.isInUse()) + xTable.setPropertyValue("TableTemplateName", "") + self.assertFalse(xTableStyle.isInUse()) + xDoc.dispose() + + def test_CellFamily(self): + xDoc = CheckStyle._uno.openEmptyWriterDoc() + xCellStyles = xDoc.StyleFamilies["CellStyles"] + vEmptyDocStyles = ['Default Style.1', 'Default Style.2', 'Default Style.3', 'Default Style.4', 'Default Style.5', 'Default Style.6', 'Default Style.7', 'Default Style.8', 'Default Style.9', 'Default Style.10', 'Default Style.11', 'Default Style.12', 'Default Style.13', 'Default Style.14', 'Default Style.15', 'Default Style.16'] + self.__test_StyleFamily(xCellStyles, vEmptyDocStyles, "SwXTextCellStyle") + self.__test_StyleFamilyIndex(xCellStyles, vEmptyDocStyles, "SwXTextCellStyle") + self.__test_StyleFamilyInsert(xDoc, xCellStyles, vEmptyDocStyles, "com.sun.star.style.CellStyle", "com.sun.star.style.CharacterStyle") + xTableStyle = xDoc.createInstance("com.sun.star.style.TableStyle") + xCellStyle = xTableStyle.getByName("first-row") + xDoc.StyleFamilies.getByName("TableStyles").insertByName("Test Table Style", xTableStyle) + self.assertEqual(xTableStyle.getByName("first-row"), xCellStyle) + self.assertEqual(xTableStyle.getByName("first-row"), xDoc.StyleFamilies.getByName("TableStyles").getByName("Test Table Style").getByName("first-row")) + #replaceByName + with self.assertRaises(NoSuchElementException): + xTableStyle.replaceByName("foobarbaz", xCellStyle) + xCellStyle = xDoc.createInstance("com.sun.star.style.CellStyle") + with self.assertRaises(IllegalArgumentException): #replace with not inserted cell style + xTableStyle.replaceByName("first-row", xCellStyle) + xTableStyle2 = xDoc.createInstance("com.sun.star.style.TableStyle") + with self.assertRaises(IllegalArgumentException): #replace with other family style + xTableStyle.replaceByName("first-row", xTableStyle2) + #replace with already assigned cell style + xTableStyle.replaceByName("first-row", xTableStyle2.getByName("first-row")) + self.assertEqual(xTableStyle.getByName("first-row"), xTableStyle2.getByName("first-row")) + xDoc.StyleFamilies.getByName("CellStyles").insertByName("Test Cell Style", xCellStyle) + xTableStyle.replaceByName("first-row", xCellStyle) + self.assertEqual(xTableStyle.getByName("first-row"), xCellStyle) + xDoc.dispose() + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_table.py b/sw/qa/python/check_table.py new file mode 100644 index 000000000..f845be564 --- /dev/null +++ b/sw/qa/python/check_table.py @@ -0,0 +1,661 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import math +import unittest + +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.beans import PropertyValue +from com.sun.star.uno import RuntimeException +from com.sun.star.table import BorderLine +from com.sun.star.table import BorderLine2 +from com.sun.star.table.BorderLineStyle import (DOUBLE, SOLID, EMBOSSED, + THICKTHIN_LARGEGAP, DASHED, DOTTED) +from com.sun.star.lang import Locale + +class CheckTable(unittest.TestCase): + def _fill_table(self, xTable): + for x in range(3): + for y in range(3): + xTable[y, x].String = 'Cell %d %d' % (x, y) + + def _check_table(self, xTable): + for x in range(3): + for y in range(3): + self.assertEqual('Cell %d %d' % (x, y), xTable[y, x].String) + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls.OOLineVeryThin = 18 + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def __test_borderAsserts(self, xBorderLine, line_valid): + self.assertTrue(line_valid) + self.assertEqual(0, xBorderLine.InnerLineWidth) + self.assertEqual(self.OOLineVeryThin, xBorderLine.OuterLineWidth) + self.assertEqual(0, xBorderLine.LineDistance) + self.assertEqual(0, xBorderLine.Color) + + def __test_borderAssertsWithLineStyle(self, xBorderLine, line_valid): + self.__test_borderAsserts(xBorderLine, line_valid) + self.assertEqual(self.OOLineVeryThin, xBorderLine.LineWidth) + self.assertEqual(SOLID, xBorderLine.LineStyle) + + def __test_borderDistance(self, border): + self.assertTrue(border.IsDistanceValid) + self.assertEqual(97, border.Distance) + + def test_tableborder(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + # insert table + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(3, 3) + xText = xDoc.getText() + xCursor = xText.createTextCursor() + xText.insertTextContent(xCursor, xTable, False) + + border_distances = xTable.TableBorderDistances + + self.assertEqual(97, border_distances.TopDistance) + self.assertEqual(97, border_distances.BottomDistance) + self.assertEqual(97, border_distances.LeftDistance) + self.assertEqual(97, border_distances.RightDistance) + + self.assertEqual(True, border_distances.IsTopDistanceValid) + self.assertEqual(True, border_distances.IsBottomDistanceValid) + self.assertEqual(True, border_distances.IsLeftDistanceValid) + self.assertEqual(True, border_distances.IsRightDistanceValid) + + border = xTable.getPropertyValue("TableBorder") + + self.__test_borderAsserts(border.TopLine, border.IsTopLineValid) + self.__test_borderAsserts(border.BottomLine, border.IsBottomLineValid) + self.__test_borderAsserts(border.LeftLine, border.IsLeftLineValid) + self.__test_borderAsserts(border.RightLine, border.IsRightLineValid) + self.__test_borderAsserts(border.HorizontalLine, border.IsHorizontalLineValid) + self.__test_borderAsserts(border.VerticalLine, border.IsVerticalLineValid) + + self.__test_borderDistance(border) + + # set border + border.TopLine = BorderLine(0, 11, 19, 19) + border.BottomLine = BorderLine(0xFF, 00, 11, 00) + border.HorizontalLine = BorderLine(0xFF00, 00, 90, 00) + xTable.setPropertyValue("TableBorder", border) + + # read set border + border = xTable.getPropertyValue("TableBorder") + + self.assertTrue(border.IsTopLineValid) + self.assertEqual(11, border.TopLine.InnerLineWidth) + self.assertEqual(19, border.TopLine.OuterLineWidth) + self.assertEqual(19, border.TopLine.LineDistance) + self.assertEqual(0, border.TopLine.Color) + + self.assertTrue(border.IsBottomLineValid) + self.assertEqual(0, border.BottomLine.InnerLineWidth) + self.assertEqual(11, border.BottomLine.OuterLineWidth) + self.assertEqual(0, border.BottomLine.LineDistance) + self.assertEqual(0xFF, border.BottomLine.Color) + + self.__test_borderAsserts(border.LeftLine, border.IsLeftLineValid) + + self.__test_borderAsserts(border.RightLine, border.IsRightLineValid) + + self.assertTrue(border.IsHorizontalLineValid) + self.assertEqual(0, border.HorizontalLine.InnerLineWidth) + self.assertEqual(90, border.HorizontalLine.OuterLineWidth) + self.assertEqual(0, border.HorizontalLine.LineDistance) + self.assertEqual(0xFF00, border.HorizontalLine.Color) + + self.__test_borderAsserts(border.VerticalLine, border.IsVerticalLineValid) + + self.__test_borderDistance(border) + + border2 = xTable.getPropertyValue("TableBorder2") + + self.assertTrue(border2.IsTopLineValid) + self.assertEqual(11, border2.TopLine.InnerLineWidth) + self.assertEqual(19, border2.TopLine.OuterLineWidth) + self.assertEqual(19, border2.TopLine.LineDistance) + self.assertEqual(0, border2.TopLine.Color) + self.assertEqual(DOUBLE, border2.TopLine.LineStyle) + self.assertEqual(49, border2.TopLine.LineWidth) + + self.assertTrue(border2.IsBottomLineValid) + self.assertEqual(0, border2.BottomLine.InnerLineWidth) + self.assertEqual(11, border2.BottomLine.OuterLineWidth) + self.assertEqual(0, border2.BottomLine.LineDistance) + self.assertEqual(0xFF, border2.BottomLine.Color) + self.assertEqual(SOLID, border2.BottomLine.LineStyle) + self.assertEqual(11, border2.BottomLine.LineWidth) + + self.__test_borderAssertsWithLineStyle(border2.LeftLine, border2.IsLeftLineValid) + + self.__test_borderAssertsWithLineStyle(border2.RightLine, border2.IsRightLineValid) + + self.assertTrue(border2.IsHorizontalLineValid) + self.assertEqual(0, border2.HorizontalLine.InnerLineWidth) + self.assertEqual(90, border2.HorizontalLine.OuterLineWidth) + self.assertEqual(0, border2.HorizontalLine.LineDistance) + self.assertEqual(0xFF00, border2.HorizontalLine.Color) + self.assertEqual(SOLID, border2.HorizontalLine.LineStyle) + self.assertEqual(90, border2.HorizontalLine.LineWidth) + + self.__test_borderAssertsWithLineStyle(border2.VerticalLine, border2.IsVerticalLineValid) + + self.__test_borderDistance(border2) + + # set border2 + border2.RightLine = BorderLine2(0, 0, 0, 0, THICKTHIN_LARGEGAP, 120) + border2.LeftLine = BorderLine2(0, 0, 0, 0, EMBOSSED, 90) + border2.VerticalLine = BorderLine2(0xFF, 0, 90, 0, DOTTED, 0) + border2.HorizontalLine = BorderLine2(0xFF00, 0, 0, 0, DASHED, 11) + xTable.setPropertyValue("TableBorder2", border2) + + # read set border2 + border2 = xTable.getPropertyValue("TableBorder2") + + self.assertTrue(border2.IsTopLineValid) + self.assertEqual(11, border2.TopLine.InnerLineWidth) + self.assertEqual(19, border2.TopLine.OuterLineWidth) + self.assertEqual(19, border2.TopLine.LineDistance) + self.assertEqual(0, border2.TopLine.Color) + self.assertEqual(DOUBLE, border2.TopLine.LineStyle) + self.assertEqual(49, border2.TopLine.LineWidth) + + self.assertTrue(border2.IsBottomLineValid) + self.assertEqual(0, border2.BottomLine.InnerLineWidth) + self.assertEqual(11, border2.BottomLine.OuterLineWidth) + self.assertEqual(0, border2.BottomLine.LineDistance) + self.assertEqual(0xFF, border2.BottomLine.Color) + self.assertEqual(SOLID, border2.BottomLine.LineStyle) + self.assertEqual(11, border2.BottomLine.LineWidth) + + self.assertTrue(border2.IsLeftLineValid) + self.assertEqual(23, border2.LeftLine.InnerLineWidth) + self.assertEqual(23, border2.LeftLine.OuterLineWidth) + self.assertEqual(46, border2.LeftLine.LineDistance) + self.assertEqual(0, border2.LeftLine.Color) + self.assertEqual(EMBOSSED, border2.LeftLine.LineStyle) + self.assertEqual(90, border2.LeftLine.LineWidth) + + self.assertTrue(border2.IsRightLineValid) + self.assertEqual(53, border2.RightLine.InnerLineWidth) + self.assertEqual(26, border2.RightLine.OuterLineWidth) + self.assertEqual(41, border2.RightLine.LineDistance) + self.assertEqual(0, border2.RightLine.Color) + self.assertEqual(THICKTHIN_LARGEGAP, border2.RightLine.LineStyle) + self.assertEqual(120, border2.RightLine.LineWidth) + + self.assertTrue(border2.IsHorizontalLineValid) + self.assertEqual(0, border2.HorizontalLine.InnerLineWidth) + self.assertEqual(11, border2.HorizontalLine.OuterLineWidth) + self.assertEqual(0, border2.HorizontalLine.LineDistance) + self.assertEqual(0xFF00, border2.HorizontalLine.Color) + self.assertEqual(DASHED, border2.HorizontalLine.LineStyle) + self.assertEqual(11, border2.HorizontalLine.LineWidth) + + self.assertTrue(border2.IsVerticalLineValid) + self.assertEqual(0, border2.VerticalLine.InnerLineWidth) + self.assertEqual(90, border2.VerticalLine.OuterLineWidth) + self.assertEqual(0, border2.VerticalLine.LineDistance) + self.assertEqual(0xFF, border2.VerticalLine.Color) + self.assertEqual(DOTTED, border2.VerticalLine.LineStyle) + self.assertEqual(90, border2.VerticalLine.LineWidth) + + self.__test_borderDistance(border2) + + # close document + xDoc.dispose() + + def test_fdo58242(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + # insert table + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(3, 3) + xText = xDoc.getText() + xCursor = xText.createTextCursor() + xText.insertTextContent(xCursor, xTable, False) + # get anchor + xAnchor = xTable.getAnchor() + + # check all properties on the anchor - shouldn't crash despite + # pointing to a non-SwTextNode + xPropsInfo = xAnchor.getPropertySetInfo() + for i in xPropsInfo.getProperties(): + try: + xAnchor.getPropertyValue(i.Name) + except RuntimeException: + pass + + # close document + xDoc.dispose() + + def test_descriptions(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + # insert table + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(3, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + self.assertEqual(3, xTable.Rows.Count) + self.assertEqual(3, xTable.Columns.Count) + xTable.TableName = "foo" + self.assertEqual("foo", xTable.TableName) + xTable.TableTemplateName = "bar" + self.assertEqual("bar", xTable.TableTemplateName) + + # fill table + self._fill_table(xTable) + self._check_table(xTable) + + # check without labels first + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + self.assertEqual(0, len(xTable.RowDescriptions)) + self.assertEqual(0, len(xTable.ColumnDescriptions)) + self.RowDescriptions = ('foo', 'bar', 'baz') # no labels, thus noop + self.ColumnDescriptions = ('foo', 'bar', 'baz') # no labels, thus noop + self._check_table(xTable) + + # now check with labels + xTable.ChartColumnAsLabel = True + xTable.ChartRowAsLabel = True + self.assertEqual(2, len(xTable.RowDescriptions)) + self.assertEqual('Cell 0 1', xTable.RowDescriptions[0]) + self.assertEqual('Cell 0 2', xTable.RowDescriptions[1]) + self.assertEqual(2, len(xTable.ColumnDescriptions)) + self.assertEqual('Cell 1 0', xTable.ColumnDescriptions[0]) + self.assertEqual('Cell 2 0', xTable.ColumnDescriptions[1]) + + with self.assertRaises(Exception): + xTable.RowDescriptions = ('foo',) # too short + + with self.assertRaises(Exception): + xTable.ColumnDescriptions = ('foo',) # too short + + self._check_table(xTable) + xTable.RowDescriptions = ('fooRow', 'bazRow') + xTable.ColumnDescriptions = ('fooColumn', 'bazColumn') + self.assertEqual('fooRow', xTable[1, 0].String) + self.assertEqual('bazRow', xTable[2, 0].String) + self.assertEqual('fooColumn', xTable[0, 1].String) + self.assertEqual('bazColumn', xTable[0, 2].String) + xTable[1, 0].String = 'Cell 0 1' # reset changes values ... + xTable[2, 0].String = 'Cell 0 2' + xTable[0, 1].String = 'Cell 1 0' + xTable[0, 2].String = 'Cell 2 0' + self._check_table(xTable) # ... to ensure the rest was untouched + + # check disconnected table excepts, but doesn't crash + xTable2 = xDoc.createInstance("com.sun.star.text.TextTable") + xTable2.initialize(3, 3) + + with self.assertRaises(Exception): + xTable2.RowDescriptions + + with self.assertRaises(Exception): + xTable2.ColumnDescriptions + + xDoc.dispose() + + def test_getset_data(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + + # insert table + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(4, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + + # roundtrip + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + self.assertEqual(xTable.Data, ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12))) + + # missing row + with self.assertRaises(Exception): + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9)) + + # missing column + with self.assertRaises(Exception): + xTable.Data = ((1, 2), (4, 5), (7, 8), (10, 11)) + + # with labels + xTable.ChartColumnAsLabel = True + xTable.ChartRowAsLabel = True + self.assertEqual(xTable.Data, ((5, 6), (8, 9), (11, 12))) + xTable.Data = ((55, 66), (88, 99), (1111, 1212)) + xTable.ChartColumnAsLabel = True + xTable.ChartRowAsLabel = False + self.assertEqual(xTable.Data, ((2, 3), (55, 66), (88, 99), (1111, 1212))) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = True + self.assertEqual(xTable.Data, ((4, 55, 66), (7, 88, 99), (10, 1111, 1212))) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + self.assertEqual(xTable.Data, ((1, 2, 3), (4, 55, 66), (7, 88, 99), (10, 1111, 1212))) + xDoc.dispose() + + def test_remove_colrow(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(4, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + xRows = xTable.Rows + self.assertEqual(xRows.ImplementationName, 'SwXTableRows') + self.assertTrue(xRows.supportsService('com.sun.star.text.TableRows')) + self.assertFalse(xRows.supportsService('foo')) + self.assertIn('com.sun.star.text.TableRows', xRows.SupportedServiceNames) + self.assertNotIn('foo', xRows.SupportedServiceNames) + xRows.removeByIndex(1, 2) + self.assertEqual(xTable.Data, ((1, 2, 3), (10, 11, 12))) + xCols = xTable.Columns + self.assertEqual(xCols.ImplementationName, 'SwXTableColumns') + self.assertTrue(xCols.supportsService('com.sun.star.text.TableColumns')) + self.assertFalse(xCols.supportsService('foo')) + self.assertIn('com.sun.star.text.TableColumns', xCols.SupportedServiceNames) + self.assertNotIn('foo', xCols.SupportedServiceNames) + xCols.removeByIndex(1, 1) + self.assertEqual(xTable.Data, ((1, 3), (10, 12))) + xDoc.dispose() + + def test_insert_colrow(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(4, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + xRows = xTable.Rows + xRows.insertByIndex(1, 2) + self.assertEqual(xTable.Data[0], (1, 2, 3)) + self.assertEqual(xTable.Data[3], (4, 5, 6)) + self.assertEqual(xTable.Data[4], (7, 8, 9)) + self.assertEqual(xTable.Data[5], (10, 11, 12)) + + for x in range(3): + self.assertTrue(math.isnan(xTable.Data[1][x])) + self.assertTrue(math.isnan(xTable.Data[2][x])) + + xCols = xTable.Columns + xCols.insertByIndex(1, 1) + self.assertEqual(xTable.Data[0][0], 1) + self.assertTrue(math.isnan(xTable.Data[0][1])) + self.assertEqual(xTable.Data[0][2], 2) + self.assertEqual(xTable.Data[0][3], 3) + self.assertEqual(xTable.Data[3][0], 4) + self.assertTrue(math.isnan(xTable.Data[3][1])) + self.assertEqual(xTable.Data[3][2], 5) + self.assertEqual(xTable.Data[3][3], 6) + self.assertEqual(xTable.Data[4][0], 7) + self.assertTrue(math.isnan(xTable.Data[4][1])) + self.assertEqual(xTable.Data[4][2], 8) + self.assertEqual(xTable.Data[4][3], 9) + self.assertEqual(xTable.Data[5][0], 10) + self.assertTrue(math.isnan(xTable.Data[5][1])) + self.assertEqual(xTable.Data[5][2], 11) + self.assertEqual(xTable.Data[5][3], 12) + + for x in range(4): + self.assertTrue(math.isnan(xTable.Data[1][x])) + self.assertTrue(math.isnan(xTable.Data[2][x])) + xDoc.dispose() + + def test_chartdataprovider(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(4, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + self.assertTrue(xTable.Name == 'Table1') + self.assertIn('com.sun.star.text.GenericTextDocument', xDoc.SupportedServiceNames) + xChartdataprovider = xDoc.createInstance('com.sun.star.chart2.data.DataProvider') + self.assertEqual(xChartdataprovider.ImplementationName, 'SwChartDataProvider') + self.assertTrue(xChartdataprovider.supportsService('com.sun.star.chart2.data.DataProvider')) + self.assertFalse(xChartdataprovider.supportsService('foo')) + self.assertIn('com.sun.star.chart2.data.DataProvider', xChartdataprovider.SupportedServiceNames) + pv = PropertyValue() + pv.Name = 'CellRangeRepresentation' + pv.Value = 'Table1.A1:C2' + xDataSource = xChartdataprovider.createDataSource((pv,)) + self.assertEqual(len(xDataSource.DataSequences), 3) + expected_values = ((1, 4), (2, 5), (3, 6)) + expected_cellrange = ('A1:A2', 'B1:B2', 'C1:C2') + + for col in range(3): + xSeq = xDataSource.DataSequences[col].Values + self.assertEqual(xSeq.ImplementationName, 'SwChartDataSequence') + self.assertTrue(xSeq.supportsService('com.sun.star.chart2.data.DataSequence')) + self.assertFalse(xSeq.supportsService('foo')) + self.assertIn('com.sun.star.chart2.data.DataSequence', xSeq.SupportedServiceNames) + self.assertEqual(xSeq.SourceRangeRepresentation, 'Table1.%s' % expected_cellrange[col]) + self.assertEqual(xSeq.Data, expected_values[col]) + self.assertEqual(xSeq.NumericalData, expected_values[col]) + self.assertEqual([int(txtval) for txtval in xSeq.TextualData], + [val for val in expected_values[col]]) + + xSeq.Role = "One xSeq to rule them all" + self.assertEqual("One xSeq to rule them all", xSeq.Role) + + xSeqClone = xSeq.createClone() + self.assertEqual(xSeq.Role, xSeqClone.Role) + + xDoc.dispose() + + def test_tdf32082(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xDocFrame = xDoc.CurrentController.Frame + xContext = CheckTable._uno.getContext() + xServiceManager = xContext.ServiceManager + xDispatcher = xServiceManager.createInstanceWithContext('com.sun.star.frame.DispatchHelper', + xContext) + + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(1, 1) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + + # Setup numberformat for the cell + xNumberFormats = xDoc.NumberFormats + xLocale = Locale('en', 'US', '') + format_string = '#,##0.00 [$€-407];[RED]-#,##0.00 [$€-407]' + key = xNumberFormats.queryKey(format_string, xLocale, True) + + if key == -1: + key = xNumberFormats.addNew(format_string, xLocale) + + # Apply the format on the first cell + xTable[0, 0].NumberFormat = key + xDispatcher.executeDispatch(xDocFrame, '.uno:GoToStartOfDoc', '', 0, ()) + xDispatcher.executeDispatch(xDocFrame, '.uno:InsertText', '', 0, + (PropertyValue('Text', 0, '3', 0),)) + xDispatcher.executeDispatch(xDocFrame, '.uno:JumpToNextCell', '', 0, ()) + + # Check that the formatting we set up is not destroyed + self.assertEqual(xTable[0, 0].getString(), '3.00 €') + self.assertEqual(xTable[0, 0].getValue(), 3) + + # Verify that it works with number recognition turned on as well + xDispatcher.executeDispatch(xDocFrame, '.uno:TableNumberRecognition', '', 0, + (PropertyValue('TableNumberRecognition', 0, True, 0),)) + xDispatcher.executeDispatch(xDocFrame, '.uno:InsertText', '', 0, + (PropertyValue('Text', 0, '4', 0),)) + xDispatcher.executeDispatch(xDocFrame, '.uno:JumpToNextCell', '', 0, ()) + self.assertEqual(xTable[1, 0].getString(), '4.00 €') + self.assertEqual(xTable[1, 0].getValue(), 4) + xDoc.dispose() + + def test_numberRecognition(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xDocFrame = xDoc.CurrentController.Frame + xContext = CheckTable._uno.getContext() + xServiceManager = xContext.ServiceManager + xDispatcher = xServiceManager.createInstanceWithContext('com.sun.star.frame.DispatchHelper', + xContext) + + xTable = xDoc.createInstance('com.sun.star.text.TextTable') + xTable.initialize(2, 1) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xDispatcher.executeDispatch(xDocFrame, '.uno:GoToStartOfDoc', '', 0, ()) + xDispatcher.executeDispatch(xDocFrame, '.uno:InsertText', '', 0, + (PropertyValue('Text', 0, '2015-10-30', 0),)) + xDispatcher.executeDispatch(xDocFrame, '.uno:JumpToNextCell', '', 0, ()) + + # Without number recognition 2015-10-30 should not be interpreted as a date + self.assertEqual(xTable[0, 0].getString(), '2015-10-30') + self.assertEqual(xTable[0, 0].getValue(), 0) + + # Activate number recognition + xDispatcher.executeDispatch(xDocFrame, '.uno:TableNumberRecognition', '', 0, + (PropertyValue('TableNumberRecognition', 0, True, 0),)) + xDispatcher.executeDispatch(xDocFrame, '.uno:InsertText', '', 0, + (PropertyValue('Text', 0, '2015-10-30', 0),)) + xDispatcher.executeDispatch(xDocFrame, '.uno:JumpToNextCell', '', 0, ()) + + # With number recognition it should now be a date, confirm by checking + # the string and value of the cell. + self.assertEqual(xTable[1, 0].getString(), '2015-10-30') + self.assertEqual(xTable[1, 0].getValue(), 42307.0) + xDoc.dispose() + + def test_tableTemplate(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(1, 1) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, True) + # should return programmatic name, even though UI name was set. + xTable.setPropertyValue("TableTemplateName", "Default Table Style") + self.assertEqual(xTable.getPropertyValue("TableTemplateName"), "Default Style") + xTable.setPropertyValue("TableTemplateName", "Default") + self.assertEqual(xTable.getPropertyValue("TableTemplateName"), "Default") + xTable.setPropertyValue("TableTemplateName", "other_style") + self.assertEqual(xTable.getPropertyValue("TableTemplateName"), "other_style") + xTable.setPropertyValue("TableTemplateName", "") + self.assertEqual(xTable.getPropertyValue("TableTemplateName"), "") + xDoc.dispose() + + def test_unoNames(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(3, 3) + xText = xDoc.getText() + xCursor = xText.createTextCursor() + xText.insertTextContent(xCursor, xTable, False) + + self.assertEqual("SwXTextTable", xTable.ImplementationName) + self.assertEqual(("com.sun.star.document.LinkTarget", + "com.sun.star.text.TextTable", + "com.sun.star.text.TextContent", + "com.sun.star.text.TextSortable"), xTable.SupportedServiceNames) + self.assertEqual(b'', xTable.ImplementationId.value) + + xCell = xTable[1, 1] + self.assertEqual("SwXCell", xCell.ImplementationName) + self.assertEqual(("com.sun.star.text.CellProperties",), xCell.SupportedServiceNames) + self.assertEqual(b'', xCell.ImplementationId.value) + + xRow = xTable.Rows[0] + self.assertEqual("SwXTextTableRow", xRow.ImplementationName) + self.assertEqual(("com.sun.star.text.TextTableRow",), xRow.SupportedServiceNames) + self.assertEqual(b'', xRow.ImplementationId.value) + + xTableCursor = xTable.createCursorByCellName("A1") + self.assertEqual("SwXTextTableCursor", xTableCursor.ImplementationName) + self.assertEqual(("com.sun.star.text.TextTableCursor",), xTableCursor.SupportedServiceNames) + self.assertEqual(b'', xTableCursor.ImplementationId.value) + + xDoc.dispose() + + def test_xmlRangeConversions(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(4, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xTable.ChartColumnAsLabel = False + xTable.ChartRowAsLabel = False + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + + xChartDataProvider = xDoc.createInstance('com.sun.star.chart2.data.DataProvider') + + self.assertEqual('', xChartDataProvider.convertRangeToXML('')) + self.assertEqual('', xChartDataProvider.convertRangeFromXML('')) + self.assertEqual('.A1;.A1', xChartDataProvider.convertRangeFromXML('<some xml>')) + + xml = xChartDataProvider.convertRangeToXML('Table1.A1:C3') + + self.assertEqual("Table1.$A$1:.$C$3", xml) + + xCellRangeString = xChartDataProvider.convertRangeFromXML("Table1.$A$1:.$C$3") + self.assertEqual("Table1.A1:C3", xCellRangeString) + + xDoc.dispose() + + def test_splitRangeHorizontal(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(2, 2) + xText = xDoc.getText() + xCursor = xText.createTextCursor() + xText.insertTextContent(xCursor, xTable, False) + xTable.Data = ((1, 2), (3, 4)) + xCursor = xTable.createCursorByCellName("A1") + xCursor.splitRange(2, True) + self.assertEqual(len(xTable.Data), 4) + self.assertEqual(xTable.Data[0], (float(1), float(2))) + self.assertEqual(xTable.Data[3], (float(3), float(4))) + self.assertTrue(math.isnan(xTable.Data[1][0])) + self.assertTrue(math.isnan(xTable.Data[1][1])) + self.assertTrue(math.isnan(xTable.Data[2][0])) + self.assertTrue(math.isnan(xTable.Data[2][1])) + xDoc.dispose() + + def test_mergeRangeHorizontal(self): + xDoc = CheckTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(3, 3) + xText = xDoc.getText() + xCursor = xText.createTextCursor() + xText.insertTextContent(xCursor, xTable, False) + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9)) + xCursor = xTable.createCursorByCellName("A1") + xCursor.goDown(1, True) + xCursor.mergeRange() + self.assertEqual(len(xTable.Data), 3) + self.assertEqual(xTable.Data[0], (float(1), float(2), float(3))) + self.assertTrue(math.isnan(xTable.Data[1][0])) + self.assertEqual(xTable.Data[1][1], float(5)) + self.assertEqual(xTable.Data[1][2], float(6)) + self.assertEqual(xTable.Data[2], (float(7), float(8), float(9))) + xDoc.dispose() + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xautotextcontainer.py b/sw/qa/python/check_xautotextcontainer.py new file mode 100644 index 000000000..b01125dca --- /dev/null +++ b/sw/qa/python/check_xautotextcontainer.py @@ -0,0 +1,126 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.container import NoSuchElementException +from com.sun.star.lang import IllegalArgumentException + +class XAutoTextContainer(unittest.TestCase): + # 0 indicates the path of the Office Basis layer + # 1 indicates the path of the user directory + GROUP_POSTFIX = '*1' + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._uno.openEmptyWriterDoc() + + def setUp(self): + xServiceManager = self._uno.xContext.ServiceManager + self.xAutoTextContainer = xServiceManager.createInstance( + "com.sun.star.text.AutoTextContainer") + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_insertNewByName(self): + # group name must contain a-z, A-z, 0-9, '_', ' ' only + xNames = ['Name', 'TEST', 'Name2', '_With_underscore_', 'with space', '123456'] + for xName in xNames: + self.xAutoTextContainer.insertNewByName(xName+self.GROUP_POSTFIX) + self.xAutoTextContainer.removeByName(xName+self.GROUP_POSTFIX) + + def test_insertNewByName_Spaces(self): + # add + xName = ' spaces ' + self.xAutoTextContainer.insertNewByName(xName+self.GROUP_POSTFIX) + + # try to remove + with self.assertRaises(NoSuchElementException): + self.xAutoTextContainer.removeByName(xName+self.GROUP_POSTFIX) + + # remove trimmed + self.xAutoTextContainer.removeByName('spaces'+self.GROUP_POSTFIX) + + def test_insertNewByName_Several(self): + xAutoTextGroup1 = self.xAutoTextContainer.insertNewByName( + "atc_name1"+self.GROUP_POSTFIX) + xAutoTextGroup2 = self.xAutoTextContainer.insertNewByName( + "atc_name2"+self.GROUP_POSTFIX) + xAutoTextGroup3 = self.xAutoTextContainer.insertNewByName( + "atc_name3"+self.GROUP_POSTFIX) + + self.assertEqual("atc_name1"+self.GROUP_POSTFIX, xAutoTextGroup1.getName()) + self.assertEqual("atc_name2"+self.GROUP_POSTFIX, xAutoTextGroup2.getName()) + self.assertEqual("atc_name3"+self.GROUP_POSTFIX, xAutoTextGroup3.getName()) + + self.xAutoTextContainer.removeByName("atc_name1"+self.GROUP_POSTFIX) + self.xAutoTextContainer.removeByName("atc_name2"+self.GROUP_POSTFIX) + self.xAutoTextContainer.removeByName("atc_name3"+self.GROUP_POSTFIX) + + def test_insertNewByName_DifferentCase(self): + xAutoTextGroup1 = self.xAutoTextContainer.insertNewByName("myname"+self.GROUP_POSTFIX) + xAutoTextGroup2 = self.xAutoTextContainer.insertNewByName("MYNAME"+self.GROUP_POSTFIX) + xAutoTextGroup3 = self.xAutoTextContainer.insertNewByName("MyName"+self.GROUP_POSTFIX) + + self.assertEqual("myname"+self.GROUP_POSTFIX, xAutoTextGroup1.getName()) + + # Note: different platforms could support different cases + # in container names + validName2 = False + validName2 |= (xAutoTextGroup2.getName() == "MYNAME"+self.GROUP_POSTFIX) + validName2 |= (xAutoTextGroup2.getName()[:5] == "group") + + validName3 = False + validName3 |= (xAutoTextGroup3.getName() == "MyName"+self.GROUP_POSTFIX) + validName3 |= (xAutoTextGroup3.getName()[:5] == "group") + + self.assertTrue(validName2) + self.assertTrue(validName3) + + self.xAutoTextContainer.removeByName("myname"+self.GROUP_POSTFIX) + + xName = xAutoTextGroup2.getName() + xName = xName[:xName.find('*')] + self.xAutoTextContainer.removeByName(xName) + + xName = xAutoTextGroup3.getName() + xName = xName[:xName.find('*')] + self.xAutoTextContainer.removeByName(xName) + + def test_insertNewByName_Failed(self): + # group name must contain a-z, A-z, 0-9, '_', ' ' only + xNames = ['', 'Name!!!', 'Red & White', 'Name.With.Dot', 'Name-2', 'A1:B1'] + for xName in xNames: + with self.assertRaises(IllegalArgumentException): + self.xAutoTextContainer.insertNewByName(xName) + + def test_removeByName_Unknown(self): + with self.assertRaises(NoSuchElementException): + self.xAutoTextContainer.removeByName("Some Unknown Name") + + def test_removeByName_DifferentCases(self): + self.xAutoTextContainer.insertNewByName('GroupName'+self.GROUP_POSTFIX) + + with self.assertRaises(NoSuchElementException): + self.xAutoTextContainer.removeByName('groupname'+self.GROUP_POSTFIX) + + with self.assertRaises(NoSuchElementException): + self.xAutoTextContainer.removeByName('GROUPNAME'+self.GROUP_POSTFIX) + + self.xAutoTextContainer.removeByName('GroupName'+self.GROUP_POSTFIX) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xautotextgroup.py b/sw/qa/python/check_xautotextgroup.py new file mode 100644 index 000000000..504f12dcd --- /dev/null +++ b/sw/qa/python/check_xautotextgroup.py @@ -0,0 +1,149 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.container import ElementExistException +from com.sun.star.container import NoSuchElementException + + +class XAutoTextGroup(unittest.TestCase): + # 0 indicates the path of the Office Basis layer + # 1 indicates the path of the user directory + GROUP_NAME = 'atg_name1*1' + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._uno.openEmptyWriterDoc() + + def setUp(self): + self.xAutoTextContainer = self.createAutoTextContainer() + self.xAutoTextGroup = self.insertNewGroup(self.xAutoTextContainer) + + self.xText = self._uno.getDoc().getText() + self.xCursor = self.xText.createTextCursor() + self.xRange = self.xCursor.getStart() + + def tearDown(self): + self.xAutoTextContainer.removeByName(self.GROUP_NAME) + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_XAutoTextGroup(self): + xName = 'Name' + xTitle = 'Title' + + titlesBefore = self.xAutoTextGroup.getTitles() + + self.xAutoTextGroup.insertNewByName(xName, xTitle, self.xRange) + self.assertNotEqual(titlesBefore, self.xAutoTextGroup.getTitles()) + + self.xAutoTextGroup.removeByName(xName) + self.assertEqual(titlesBefore, self.xAutoTextGroup.getTitles()) + + def test_XAutoTextGroup_NoTitle(self): + xName = 'Name' + + titlesBefore = self.xAutoTextGroup.getTitles() + + self.xAutoTextGroup.insertNewByName(xName, xName, self.xRange) + self.assertNotEqual(titlesBefore, self.xAutoTextGroup.getTitles()) + + self.xAutoTextGroup.removeByName(xName) + self.assertEqual(titlesBefore, self.xAutoTextGroup.getTitles()) + + def test_insertNewByName_Twice(self): + xName = 'Name' + xTitle = 'Title' + + self.xAutoTextGroup.insertNewByName(xName, xTitle, self.xRange) + + with self.assertRaises(ElementExistException): + self.xAutoTextGroup.insertNewByName(xName, xTitle, self.xRange) + + self.xAutoTextGroup.removeByName(xName) + + def test_renameByName(self): + xName = 'Name' + xTitle = 'Title' + + xNewName = 'New Name' + xNewTitle = 'New Title' + + self.xAutoTextGroup.insertNewByName(xName, xTitle, self.xRange) + self.xAutoTextGroup.renameByName(xName, xNewName, xNewTitle) + + titlesBefore = self.xAutoTextGroup.getTitles() + with self.assertRaises(NoSuchElementException): + self.xAutoTextGroup.removeByName(xName) + titlesAfter = self.xAutoTextGroup.getTitles() + self.assertEqual(titlesBefore, titlesAfter) + + self.xAutoTextGroup.removeByName(xNewName) + titlesAfter2 = self.xAutoTextGroup.getTitles() + self.assertNotEqual(titlesBefore, titlesAfter2) + + def test_renameByName_Failed(self): + xName = 'Name' + xTitle = 'Title' + + xNewName = 'New Name' + xNewTitle = 'New Title' + + self.xAutoTextGroup.insertNewByName(xName, xTitle, self.xRange) + self.xAutoTextGroup.insertNewByName(xNewName, xNewTitle, self.xRange) + + with self.assertRaises(ElementExistException): + self.xAutoTextGroup.renameByName(xName, xNewName, xNewTitle) + + self.xAutoTextGroup.removeByName(xName) + self.xAutoTextGroup.removeByName(xNewName) + + def test_removeByName_Twice(self): + xName = 'Name' + xTitle = 'Title' + + self.xAutoTextGroup.insertNewByName(xName, xTitle, self.xRange) + self.xAutoTextGroup.removeByName(xName) + + # 2nd attempt should fail + with self.assertRaises(NoSuchElementException): + self.xAutoTextGroup.removeByName(xName) + + def test_removeByName_Empty(self): + with self.assertRaises(NoSuchElementException): + self.xAutoTextGroup.removeByName('') + + def test_removeByName_NoSuchElement(self): + with self.assertRaises(NoSuchElementException): + self.xAutoTextGroup.removeByName('ForSureNotExistName1') + + def createAutoTextContainer(self): + xServiceManager = self._uno.xContext.ServiceManager + self.assertIsNotNone(xServiceManager) + xAutoTextContainer = xServiceManager.createInstance("com.sun.star.text.AutoTextContainer") + self.assertIsNotNone(xAutoTextContainer) + + return xAutoTextContainer + + def insertNewGroup(self, xAutoTextContainer): + self.assertIsNotNone(xAutoTextContainer) + xAutoTextGroup = xAutoTextContainer.insertNewByName(self.GROUP_NAME) + self.assertIsNotNone(xAutoTextGroup) + return xAutoTextGroup + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xcloseable.py b/sw/qa/python/check_xcloseable.py new file mode 100644 index 000000000..172b852dc --- /dev/null +++ b/sw/qa/python/check_xcloseable.py @@ -0,0 +1,88 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +import unohelper +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.util import XCloseListener + + +listenerCallsOwner = 0 +listenerCallsNoOwner = 0 + + +class XCloseListenerExtended(unohelper.Base, XCloseListener): + @classmethod + def queryClosing(self, Source, GetsOwnership): + if GetsOwnership is True: + global listenerCallsOwner + listenerCallsOwner += 1 + else: + global listenerCallsNoOwner + listenerCallsNoOwner += 1 + + @classmethod + def notifyClosing(self, Source): + pass + + @classmethod + def disposing(self, Event): + pass + + +class XCloseable(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_closeTrue(self): + # prepare + global listenerCallsOwner + global listenerCallsNoOwner + listenerCallsOwner = 0 + listenerCallsNoOwner = 0 + + # run + xDoc = self._uno.openEmptyWriterDoc() + xListener = XCloseListenerExtended() + xDoc.addCloseListener(xListener) + xDoc.close(True) + + # verify results + self.assertEqual(1, listenerCallsOwner) + self.assertEqual(0, listenerCallsNoOwner) + + def test_closeFalse(self): + # prepare + global listenerCallsOwner + global listenerCallsNoOwner + listenerCallsOwner = 0 + listenerCallsNoOwner = 0 + + # run + xDoc = self._uno.openEmptyWriterDoc() + xListener = XCloseListenerExtended() + xDoc.addCloseListener(xListener) + xDoc.close(False) + + # verify results + self.assertEqual(0, listenerCallsOwner) + self.assertEqual(1, listenerCallsNoOwner) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xmodel.py b/sw/qa/python/check_xmodel.py new file mode 100644 index 000000000..6487b4781 --- /dev/null +++ b/sw/qa/python/check_xmodel.py @@ -0,0 +1,69 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.lang import IllegalArgumentException +from com.sun.star.beans import PropertyValue + + +class TestXModel(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_setArgs_valid(self): + xDoc = self._uno.openEmptyWriterDoc() + self.assertIsNotNone(xDoc) + + p1 = PropertyValue(Name="SuggestedSaveAsName", Value="prettyFileName") + p2 = PropertyValue(Name="SuggestedSaveAsDir", Value="/my/dir") + p3 = PropertyValue(Name="LockContentExtraction", Value=True) + p4 = PropertyValue(Name="LockExport", Value=True) + p5 = PropertyValue(Name="LockPrint", Value=True) + p6 = PropertyValue(Name="LockSave", Value=True) + p7 = PropertyValue(Name="LockEditDoc", Value=True) + p8 = PropertyValue(Name="Replaceable", Value=True) + xDoc.setArgs([p1, p2, p3, p4, p5, p6, p7, p8]) + + # Make sure that all properties are returned with getArgs() + args = xDoc.getArgs() + self.assertTrue(p1 in args) + self.assertTrue(p2 in args) + self.assertTrue(p3 in args) + self.assertTrue(p4 in args) + self.assertTrue(p5 in args) + self.assertTrue(p6 in args) + self.assertTrue(p7 in args) + self.assertTrue(p8 in args) + + xDoc.close(True) + + def test_setArgs_invalid(self): + xDoc = self._uno.openEmptyWriterDoc() + self.assertIsNotNone(xDoc) + + # IllegalArgumentException should be thrown when setting a non-existing property + p1 = PropertyValue(Name="PropertyNotExists", Value="doesntmatter") + with self.assertRaises(IllegalArgumentException): + xDoc.setArgs([p1]) + + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xmodifiable2.py b/sw/qa/python/check_xmodifiable2.py new file mode 100644 index 000000000..53082fc28 --- /dev/null +++ b/sw/qa/python/check_xmodifiable2.py @@ -0,0 +1,195 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class XModifiable2(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_XModifiable2(self): + # initialization + xDoc = self._uno.openEmptyWriterDoc() + + # perform unit test + self.assertTrue(xDoc.isSetModifiedEnabled()) + + self.assertTrue(xDoc.disableSetModified()) + self.assertFalse(xDoc.isSetModifiedEnabled()) + + self.assertFalse(xDoc.enableSetModified()) + self.assertTrue(xDoc.isSetModifiedEnabled()) + + # clean up + xDoc.close(True) + + def test_XModifiable2_Double(self): + # initialization + xDoc = self._uno.openEmptyWriterDoc() + + # perform unit test + self.assertTrue(xDoc.isSetModifiedEnabled()) + + # try to disable + # Expected return value: + # `TRUE` the changing of the modified state was disabled + self.assertTrue(xDoc.disableSetModified()) + self.assertFalse(xDoc.isSetModifiedEnabled()) + + # try to disable twice + # Expected return value: + # `FALSE` the changing of the modified state was already disabled + self.assertFalse(xDoc.disableSetModified()) + self.assertFalse(xDoc.isSetModifiedEnabled()) + + # try to disable third time + # Expected return value: + # `FALSE` the changing of the modified state was already disabled + # i.e. the same as in previous call + self.assertFalse(xDoc.disableSetModified()) + self.assertFalse(xDoc.isSetModifiedEnabled()) + + # try to enable + # Expected return value: + # `FALSE` the changing of the modified state was enabled + self.assertFalse(xDoc.enableSetModified()) + self.assertTrue(xDoc.isSetModifiedEnabled()) + + # try to enable twice + # Expected return value: + # `TRUE` the changing of the modified state was already enabled + self.assertTrue(xDoc.enableSetModified()) + self.assertTrue(xDoc.isSetModifiedEnabled()) + + # try to enable third time + # Expected return value: + # `TRUE` the changing of the modified state was already enabled + # i.e. the same as in previous call + self.assertTrue(xDoc.enableSetModified()) + self.assertTrue(xDoc.isSetModifiedEnabled()) + + # clean up + xDoc.close(True) + + def test_XModifiable2_setModified(self): + # initialization + xDoc = self._uno.openEmptyWriterDoc() + + # perform unit test + # try to set modified flag when modification enabled + self.assertTrue(xDoc.isSetModifiedEnabled()) + + self.assertFalse(xDoc.isModified()) + xDoc.setModified(True) + self.assertTrue(xDoc.isModified()) + + xDoc.setModified(False) + self.assertFalse(xDoc.isModified()) + + # try to set modified flag when modification disabled + self.assertTrue(xDoc.disableSetModified()) + self.assertFalse(xDoc.isSetModifiedEnabled()) + + self.assertFalse(xDoc.isModified()) + xDoc.setModified(True) + self.assertFalse(xDoc.isModified()) + + self.assertFalse(xDoc.enableSetModified()) + self.assertTrue(xDoc.isSetModifiedEnabled()) + + # document is still not modified + self.assertFalse(xDoc.isModified()) + + # try to set modified flag when modification enabled + # and when we have changed the modification possibility + self.assertTrue(xDoc.isSetModifiedEnabled()) + + self.assertFalse(xDoc.isModified()) + xDoc.setModified(True) + self.assertTrue(xDoc.isModified()) + + xDoc.setModified(False) + self.assertFalse(xDoc.isModified()) + + # clean up + xDoc.close(True) + + def test_setModified_ByContent(self): + # initialization + xDoc = self._uno.openEmptyWriterDoc() + + # perform unit test: + # set modified flag using text editing + # when modification of the flag is enabled + self.assertTrue(xDoc.isSetModifiedEnabled()) + self.assertFalse(xDoc.isModified()) + + cursor = xDoc.Text.createTextCursor() + xDoc.Text.insertString(cursor, "The first paragraph", 0) + + self.assertTrue(xDoc.isSetModifiedEnabled()) + self.assertTrue(xDoc.isModified()) + + # clean up + xDoc.close(True) + + def test_setModified_ByContent_Blocked(self): + # initialization + xDoc = self._uno.openEmptyWriterDoc() + + # perform unit test: + # it is unable to set modified flag using text editing + # when modification of the flag was disabled + self.assertTrue(xDoc.disableSetModified()) + self.assertFalse(xDoc.isSetModifiedEnabled()) + self.assertFalse(xDoc.isModified()) + + cursor = xDoc.Text.createTextCursor() + xDoc.Text.insertString(cursor, "The first paragraph", 0) + + self.assertFalse(xDoc.isSetModifiedEnabled()) + self.assertFalse(xDoc.isModified()) + + # clean up + xDoc.close(True) + + def test_WriteProtectedDocument(self): + # initialization + xDoc = self._uno.openTemplateFromTDOC('WriteProtected.odt') + + # perform unit test: + # it is unable to set modified flag using text editing + # when modification of the flag was disabled as + # ODT file was marked to be opened as read-only + self.assertFalse(xDoc.isSetModifiedEnabled()) + self.assertFalse(xDoc.isModified()) + + cursor = xDoc.Text.createTextCursor() + xDoc.Text.insertString(cursor, "The first paragraph", 0) + + self.assertFalse(xDoc.isSetModifiedEnabled()) + self.assertFalse(xDoc.isModified()) + + # clean up + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xnamedgraph.py b/sw/qa/python/check_xnamedgraph.py new file mode 100644 index 000000000..84c774bdd --- /dev/null +++ b/sw/qa/python/check_xnamedgraph.py @@ -0,0 +1,170 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class XNamedGraph(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_getStatements(self): + xDMA = self._uno.openTemplateFromTDOC("XNamedGraph.ott") + xRepo = xDMA.getRDFRepository() + xGraphs = xRepo.getGraphNames() + + all = self.getStatementsCount(xGraphs, xDMA, None, None) + self.assertTrue(all > 0) + + # check that we have some node from the second RDF graph + # (but not any from the list of LO standard nodes) + DATE_URI_STR = "http://www.wollmux.org/WollMuxMetadata#WollMuxVersion" + xUri = self.nameToUri(DATE_URI_STR) + + onlyWollMux = self.getStatementsCount(xGraphs, xDMA, xUri, None) + self.assertTrue(onlyWollMux > 0) + self.assertTrue(onlyWollMux < all) + + xDMA.close(True) + + def test_Statements_AddRemove(self): + # take any first graph + xDMA = self._uno.openTemplateFromTDOC("XNamedGraph.ott") + xGraph = self.getAnyGraph(xDMA) + + DATE_URI_STR = "http://www.example.com/Metadata#Version" + OBJECT_STR = "foo" + + # not exist => add => remove + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR)) + self.addStatement(xDMA, xGraph, DATE_URI_STR, OBJECT_STR) + self.assertTrue(self.hasStatement(xDMA, xGraph, DATE_URI_STR)) + self.removeStatement(xDMA, xGraph, DATE_URI_STR, None) + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR)) + + xDMA.close(True) + + def test_Statements_RemoveByObject(self): + # take any first graph + xDMA = self._uno.openTemplateFromTDOC("XNamedGraph.ott") + xGraph = self.getAnyGraph(xDMA) + + DATE_URI_STR = "http://www.example.com/Metadata#Version" + OBJECT_STR = "foo" + + # remove by object + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR)) + self.addStatement(xDMA, xGraph, DATE_URI_STR, OBJECT_STR) + self.assertTrue(self.hasStatement(xDMA, xGraph, DATE_URI_STR)) + self.removeStatement(xDMA, xGraph, None, OBJECT_STR) + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR)) + + xDMA.close(True) + + def test_Statements_RemoveByObject(self): + # take any first graph + xDMA = self._uno.openTemplateFromTDOC("XNamedGraph.ott") + xGraph = self.getAnyGraph(xDMA) + + DATE_URI_STR_1 = "http://www.example.com/Metadata#Version" + DATE_URI_STR_2 = "http://www.example.com/Metadata#Second" + OBJECT_STR = "foo" + + # remove several with one call + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR_1)) + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR_2)) + self.addStatement(xDMA, xGraph, DATE_URI_STR_1, OBJECT_STR) + self.addStatement(xDMA, xGraph, DATE_URI_STR_2, OBJECT_STR) + self.assertTrue(self.hasStatement(xDMA, xGraph, DATE_URI_STR_1)) + self.assertTrue(self.hasStatement(xDMA, xGraph, DATE_URI_STR_2)) + self.removeStatement(xDMA, xGraph, None, OBJECT_STR) + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR_1)) + self.assertFalse(self.hasStatement(xDMA, xGraph, DATE_URI_STR_2)) + + xDMA.close(True) + + def getAnyGraph(self, xDMA): + xRepo = xDMA.getRDFRepository() + xGraphs = xRepo.getGraphNames() + self.assertTrue(len(xGraphs) > 0) + xGraph = xRepo.getGraph(xGraphs[0]) + self.assertIsNotNone(xGraph) + return xGraph + + def getStatementsCount(self, xGraphs, xDMA, xPredicate, xObject): + count = 0 + xRepo = xDMA.getRDFRepository() + + for xGraphUri in xGraphs: + xGraph = xRepo.getGraph(xGraphUri) + + xStatements = xGraph.getStatements(xDMA, xPredicate, xObject) + self.assertIsNotNone(xStatements) + + if xPredicate is None: + self.assertTrue(xStatements.hasMoreElements()) + + for xStatement in xStatements: + count += 1 + + return count + + def hasStatement(self, xDMA, xGraph, xPredicateName): + xPredicate = self.nameToUri(xPredicateName) + self.assertIsNotNone(xPredicate) + + xStatements = xGraph.getStatements(xDMA, xPredicate, None) + self.assertIsNotNone(xStatements) + return xStatements.hasMoreElements() + + def nameToUri(self, xPredicateName): + xServiceManager = self.__class__._uno.xContext.ServiceManager + xUri = xServiceManager.createInstance("com.sun.star.rdf.URI") + xUri.initialize((xPredicateName,)) + return xUri + + def stringToLiteral(self, xString): + xServiceManager = self.__class__._uno.xContext.ServiceManager + xLiteral = xServiceManager.createInstance("com.sun.star.rdf.Literal") + xLiteral.initialize((xString,)) + return xLiteral + + def addStatement(self, xDMA, xGraph, xPredicateName, xObjectStr): + xPredicate = self.nameToUri(xPredicateName) + self.assertIsNotNone(xPredicate) + + xObject = self.stringToLiteral(xObjectStr) + self.assertIsNotNone(xObject) + + xGraph.addStatement(xDMA, xPredicate, xObject) + + def removeStatement(self, xDMA, xGraph, xPredicateName, xObjectStr): + xPredicate = None + if xPredicateName is not None: + xPredicate = self.nameToUri(xPredicateName) + + xObject = None + if xObjectStr is not None: + xObject = self.stringToLiteral(xObjectStr) + + xGraph.removeStatements(xDMA, xPredicate, xObject) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xrefreshable.py b/sw/qa/python/check_xrefreshable.py new file mode 100644 index 000000000..0b7b89652 --- /dev/null +++ b/sw/qa/python/check_xrefreshable.py @@ -0,0 +1,110 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +import unohelper +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.util import XRefreshListener + + +refreshed = 0 + + +class XRefreshListenerExtended(unohelper.Base, XRefreshListener): + @classmethod + def refreshed(self, Event): + global refreshed + refreshed += 1 + + @classmethod + def disposing(self, event): + pass + + +class XRefreshable(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_None(self): + global refreshed + refreshed = 0 + xDoc = self._uno.openEmptyWriterDoc() + + # attempt to add/remove none + xDoc.addRefreshListener(None) + xDoc.removeRefreshListener(None) + + # refresh without listeners + xDoc.refresh() + xDoc.close(True) + + def test_refresh(self): + global refreshed + refreshed = 0 + + xDoc = self._uno.openEmptyWriterDoc() + xListener = XRefreshListenerExtended() + xDoc.addRefreshListener(xListener) + xDoc.refresh() + xDoc.removeRefreshListener(xListener) + self.assertEqual(1, refreshed) + xDoc.refresh() + self.assertEqual(1, refreshed) + xDoc.close(True) + + def test_refreshTwice(self): + global refreshed + refreshed = 0 + + xDoc = self._uno.openEmptyWriterDoc() + xListener = XRefreshListenerExtended() + xDoc.addRefreshListener(xListener) + xDoc.refresh() + xDoc.refresh() + xDoc.removeRefreshListener(xListener) + self.assertEqual(2, refreshed) + xDoc.refresh() + self.assertEqual(2, refreshed) + xDoc.close(True) + + def test_DoubleInstances(self): + global refreshed + refreshed = 0 + + xDoc = self._uno.openEmptyWriterDoc() + xListener = XRefreshListenerExtended() + + xDoc.addRefreshListener(xListener) + xDoc.addRefreshListener(xListener) + xDoc.addRefreshListener(xListener) + + xDoc.refresh() + xDoc.refresh() + self.assertEqual(3*2, refreshed) + + refreshed = 0 + xDoc.removeRefreshListener(xListener) + xDoc.refresh() + # two instances should remain in the list + self.assertEqual(2, refreshed) + + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xtextrangecompare.py b/sw/qa/python/check_xtextrangecompare.py new file mode 100644 index 000000000..181e97ff6 --- /dev/null +++ b/sw/qa/python/check_xtextrangecompare.py @@ -0,0 +1,169 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.lang import IllegalArgumentException + + +class XTextRangeCompare(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_compareRegionStarts(self): + xDoc = self._uno.openEmptyWriterDoc() + + cursor = xDoc.Text.createTextCursor() + xDoc.Text.insertString(cursor, "The first paragraph", 0) + + self.assertEqual(0, xDoc.Text.compareRegionStarts(cursor, cursor)) + + with self.assertRaises(IllegalArgumentException): + xDoc.Text.compareRegionStarts(cursor, None) + + with self.assertRaises(IllegalArgumentException): + xDoc.Text.compareRegionStarts(None, cursor) + + with self.assertRaises(IllegalArgumentException): + xDoc.Text.compareRegionStarts(None, None) + + xDoc.close(True) + + def test_compareRegionEnds(self): + xDoc = self._uno.openEmptyWriterDoc() + + cursor = xDoc.Text.createTextCursor() + xDoc.Text.insertString(cursor, "The first paragraph", 0) + + self.assertEqual(0, xDoc.Text.compareRegionEnds(cursor, cursor)) + + with self.assertRaises(IllegalArgumentException): + xDoc.Text.compareRegionEnds(cursor, None) + + with self.assertRaises(IllegalArgumentException): + xDoc.Text.compareRegionEnds(None, cursor) + + with self.assertRaises(IllegalArgumentException): + xDoc.Text.compareRegionEnds(None, None) + + xDoc.close(True) + + def test_compareRegionStarts_Different(self): + xDoc = self._uno.openEmptyWriterDoc() + + cursor1 = xDoc.Text.createTextCursor() + cursor2 = xDoc.Text.createTextCursor() + + xDoc.Text.insertString(cursor1, "The first paragraph", 0) + + cursor1.gotoStart(False) + cursor2.gotoEnd(False) + + self.assertTrue(cursor1.isCollapsed()) + self.assertTrue(cursor1.isCollapsed()) + + self.assertEqual(1, xDoc.Text.compareRegionStarts(cursor1, cursor2)) + self.assertEqual(-1, xDoc.Text.compareRegionStarts(cursor2, cursor1)) + + xDoc.close(True) + + def test_compareRegionStarts_DiffSelection(self): + xDoc = self._uno.openEmptyWriterDoc() + + cursor1 = xDoc.Text.createTextCursor() + cursor2 = xDoc.Text.createTextCursor() + + xDoc.Text.insertString(cursor1, "The first paragraph", 0) + + cursor1.gotoStart(False) + cursor1.gotoEnd(True) + cursor2.gotoEnd(False) + cursor2.gotoStart(True) + + self.assertFalse(cursor1.isCollapsed()) + self.assertFalse(cursor1.isCollapsed()) + + self.assertEqual(0, xDoc.Text.compareRegionStarts(cursor1, cursor2)) + self.assertEqual(0, xDoc.Text.compareRegionEnds(cursor1, cursor2)) + + xDoc.close(True) + + def test_compareRegionStarts_DiffSelection(self): + xDoc = self._uno.openEmptyWriterDoc() + + cursor1 = xDoc.Text.createTextCursor() + cursor2 = xDoc.Text.createTextCursor() + + xDoc.Text.insertString(cursor1, "The first paragraph", 0) + + cursor1.gotoStart(False) + cursor1.gotoEnd(True) + cursor1.goLeft(2, True) + cursor2.gotoEnd(False) + cursor2.gotoStart(True) + cursor2.goRight(2, True) + + self.assertFalse(cursor1.isCollapsed()) + self.assertFalse(cursor1.isCollapsed()) + + # whole text: 123456789 + # cursor1: 1234567 + # cursor2: 3456789 + + self.assertEqual(1, xDoc.Text.compareRegionStarts(cursor1, cursor2)) + self.assertEqual(1, xDoc.Text.compareRegionEnds(cursor1, cursor2)) + + self.assertEqual(-1, xDoc.Text.compareRegionStarts(cursor2, cursor1)) + self.assertEqual(-1, xDoc.Text.compareRegionEnds(cursor2, cursor1)) + + xDoc.close(True) + + def test_compareRegionStarts_SameStart(self): + xDoc = self._uno.openEmptyWriterDoc() + + cursor1 = xDoc.Text.createTextCursor() + cursor2 = xDoc.Text.createTextCursor() + + xDoc.Text.insertString(cursor1, "The first paragraph", 0) + + cursor1.gotoStart(False) + cursor1.goRight(2, False) + cursor1.goRight(5, True) + + cursor2.gotoStart(False) + cursor2.goRight(2, False) + cursor2.goRight(7, True) + + self.assertFalse(cursor1.isCollapsed()) + self.assertFalse(cursor1.isCollapsed()) + + # whole text: 123456789 + # cursor1: 34567 + # cursor2: 3456789 + + self.assertEqual(0, xDoc.Text.compareRegionStarts(cursor1, cursor2)) + self.assertEqual(0, xDoc.Text.compareRegionStarts(cursor2, cursor1)) + + self.assertEqual(1, xDoc.Text.compareRegionEnds(cursor1, cursor2)) + self.assertEqual(-1, xDoc.Text.compareRegionEnds(cursor2, cursor1)) + + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/check_xtexttable.py b/sw/qa/python/check_xtexttable.py new file mode 100644 index 000000000..3c1f4c228 --- /dev/null +++ b/sw/qa/python/check_xtexttable.py @@ -0,0 +1,80 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +import random + + +class XTextTable(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_newTable(self): + xDoc = XTextTable._uno.openEmptyWriterDoc() + xTable = xDoc.createInstance("com.sun.star.text.TextTable") + xTable.initialize(4, 3) + xCursor = xDoc.Text.createTextCursor() + xDoc.Text.insertTextContent(xCursor, xTable, False) + xTable.Data = ((1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)) + + self.checkTable(xTable) + xDoc.close(True) + + def test_tableFromOdt(self): + hasNestedTable = False + + xDoc = self._uno.openTemplateFromTDOC("XTextTable.odt") + itr = iter(xDoc.Text.createEnumeration()) + for element in itr: + if element.supportsService("com.sun.star.text.TextTable"): + self.checkTable(element) + + # in second table, inside B1 cell we have nested table + xCellB1 = element.getCellByName("B1") + self.assertIsNotNone(xCellB1) + cellContent = iter(xCellB1.Text.createEnumeration()) + for cellElement in cellContent: + if cellElement.supportsService("com.sun.star.text.TextTable"): + self.checkTable(cellElement) + hasNestedTable = True + + self.assertTrue(hasNestedTable) + xDoc.close(True) + + def checkTable(self, xTable): + # in order + xNames = xTable.getCellNames() + for xName in xNames: + xCell = xTable.getCellByName(xName) + self.assertIsNotNone(xCell) + + # random access + xNames = xTable.getCellNames() + for i in random.sample(range(0, len(xNames)), len(xNames)): + xName = xNames[i] + xCell = xTable.getCellByName(xName) + self.assertIsNotNone(xCell) + + # wrong name + xCell = xTable.getCellByName('WRONG CELL NAME') + self.assertIsNone(xCell) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/get_expression.py b/sw/qa/python/get_expression.py new file mode 100644 index 000000000..98e9402bb --- /dev/null +++ b/sw/qa/python/get_expression.py @@ -0,0 +1,54 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class TestGetExpression(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._xDoc = cls._uno.openEmptyWriterDoc() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + # HACK in case cls._xDoc holds a UNO proxy to an SwXTextDocument (whose dtor calls + # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be + # garbage-collected after VCL has already been deinitialized: + cls._xDoc = None + + def test_get_expression(self): + self.__class__._uno.checkProperties( + self.__class__._xDoc.createInstance("com.sun.star.text.textfield.GetExpression"), + {"Content": "foo", + "CurrentPresentation": "bar", + "NumberFormat": 0, + "IsShowFormula": False, + "SubType": 0, + "VariableSubtype": 1, + "IsFixedLanguage": False, + }, + self + ) + + # property 'Value' is read only? + @unittest.expectedFailure + def test_get_expression_veto_read_only(self): + self.__class__._uno.checkProperties( + self.__class__._xDoc.createInstance("com.sun.star.text.textfield.GetExpression"), + {"Value": 0.0}, + self + ) + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/set_expression.py b/sw/qa/python/set_expression.py new file mode 100644 index 000000000..c5dc5e6ae --- /dev/null +++ b/sw/qa/python/set_expression.py @@ -0,0 +1,47 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +# @unittest.skip("that seems to work") +class TestSetExpression(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_set_expression(self): + xDoc = self.__class__._uno.openEmptyWriterDoc() + self.__class__._uno.checkProperties( + xDoc.createInstance("com.sun.star.text.textfield.SetExpression"), + {"NumberingType": 0, + "Content": "foo", + "CurrentPresentation": "bar", + "NumberFormat": 0, + "NumberingType": 0, + "IsShowFormula": False, + "IsInput": False, + "IsVisible": True, + "SequenceValue": 0, + "SubType": 0, + "Value": 1.0, + "IsFixedLanguage": False + }, + self + ) + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/testdocuments/CheckCrossReferences.odt b/sw/qa/python/testdocuments/CheckCrossReferences.odt Binary files differnew file mode 100644 index 000000000..d9484421d --- /dev/null +++ b/sw/qa/python/testdocuments/CheckCrossReferences.odt diff --git a/sw/qa/python/testdocuments/CheckFlies.odt b/sw/qa/python/testdocuments/CheckFlies.odt Binary files differnew file mode 100644 index 000000000..8f42989b3 --- /dev/null +++ b/sw/qa/python/testdocuments/CheckFlies.odt diff --git a/sw/qa/python/testdocuments/TESTMETA.odt b/sw/qa/python/testdocuments/TESTMETA.odt Binary files differnew file mode 100644 index 000000000..004af82e5 --- /dev/null +++ b/sw/qa/python/testdocuments/TESTMETA.odt diff --git a/sw/qa/python/testdocuments/TESTXMLID.odt b/sw/qa/python/testdocuments/TESTXMLID.odt Binary files differnew file mode 100644 index 000000000..063d392a4 --- /dev/null +++ b/sw/qa/python/testdocuments/TESTXMLID.odt diff --git a/sw/qa/python/testdocuments/WriteProtected.odt b/sw/qa/python/testdocuments/WriteProtected.odt Binary files differnew file mode 100644 index 000000000..5e75e0e7f --- /dev/null +++ b/sw/qa/python/testdocuments/WriteProtected.odt diff --git a/sw/qa/python/testdocuments/XNamedGraph.ott b/sw/qa/python/testdocuments/XNamedGraph.ott Binary files differnew file mode 100644 index 000000000..9c097024b --- /dev/null +++ b/sw/qa/python/testdocuments/XNamedGraph.ott diff --git a/sw/qa/python/testdocuments/XTextTable.odt b/sw/qa/python/testdocuments/XTextTable.odt Binary files differnew file mode 100644 index 000000000..2c15fc781 --- /dev/null +++ b/sw/qa/python/testdocuments/XTextTable.odt diff --git a/sw/qa/python/testdocuments/fdo39694.ott b/sw/qa/python/testdocuments/fdo39694.ott Binary files differnew file mode 100644 index 000000000..959d2a656 --- /dev/null +++ b/sw/qa/python/testdocuments/fdo39694.ott diff --git a/sw/qa/python/testdocuments/xcontrolshape.odt b/sw/qa/python/testdocuments/xcontrolshape.odt Binary files differnew file mode 100644 index 000000000..d8a003b13 --- /dev/null +++ b/sw/qa/python/testdocuments/xcontrolshape.odt diff --git a/sw/qa/python/testdocuments/xscriptprovider.odt b/sw/qa/python/testdocuments/xscriptprovider.odt Binary files differnew file mode 100644 index 000000000..fa3b8ec75 --- /dev/null +++ b/sw/qa/python/testdocuments/xscriptprovider.odt diff --git a/sw/qa/python/testdocuments/xstyleloader.odt b/sw/qa/python/testdocuments/xstyleloader.odt Binary files differnew file mode 100644 index 000000000..921623f16 --- /dev/null +++ b/sw/qa/python/testdocuments/xstyleloader.odt diff --git a/sw/qa/python/testdocuments/xtextcontent.odt b/sw/qa/python/testdocuments/xtextcontent.odt Binary files differnew file mode 100644 index 000000000..a1eb82741 --- /dev/null +++ b/sw/qa/python/testdocuments/xtextcontent.odt diff --git a/sw/qa/python/testdocuments/xtextcursor.odt b/sw/qa/python/testdocuments/xtextcursor.odt Binary files differnew file mode 100644 index 000000000..1fdcfb9b7 --- /dev/null +++ b/sw/qa/python/testdocuments/xtextcursor.odt diff --git a/sw/qa/python/testdocuments/xtextfieldssupplier.odt b/sw/qa/python/testdocuments/xtextfieldssupplier.odt Binary files differnew file mode 100644 index 000000000..0b7764a2b --- /dev/null +++ b/sw/qa/python/testdocuments/xtextfieldssupplier.odt diff --git a/sw/qa/python/testdocuments/xtextrange.odt b/sw/qa/python/testdocuments/xtextrange.odt Binary files differnew file mode 100644 index 000000000..70c978349 --- /dev/null +++ b/sw/qa/python/testdocuments/xtextrange.odt diff --git a/sw/qa/python/text_portion_enumeration_test.py b/sw/qa/python/text_portion_enumeration_test.py new file mode 100644 index 000000000..47f7c21e5 --- /dev/null +++ b/sw/qa/python/text_portion_enumeration_test.py @@ -0,0 +1,3506 @@ +# -*- coding: utf-8 -*- + +''' +This file is part of the LibreOffice project. + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +This file incorporates work covered by the following license notice: + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.apache.org/licenses/LICENSE-2.0 . +''' +import uno +import unittest +import os.path +from org.libreoffice.unotest import UnoInProcess +from tempfile import TemporaryDirectory +from com.sun.star.uno import RuntimeException +from com.sun.star.lang import IllegalArgumentException, NoSupportException +from com.sun.star.beans import StringPair +from com.sun.star.rdf.URIs import ODF_PREFIX, ODF_SUFFIX +from com.sun.star.i18n.NumberFormatIndex import NUMBER_INT +from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK, HARD_HYPHEN +from com.sun.star.text.TextContentAnchorType import ( + AT_CHARACTER, AS_CHARACTER, AT_PARAGRAPH, AT_PAGE, AT_FRAME) + + +class TreeNode(): + '''base class for tree nodes. only instance: root of tree.''' + + def __init__(self, content=None): + self.content = content + self._children = [] + self.nodetype = "__ROOT__" + self.isnesting = False + + def __str__(self): + return "<{}>".format(self.nodetype) + + def __eq__(self, other): + return type(self) == type(other) + + def __ne__(self, other): + return not self == other + + def _dup(self, nodetype, *args): + try: + return nodetype(*args) + except Exception as e: + raise RuntimeError("TreeNode.dup") from e + + def createenumeration(self): + return iter(self._children) + + def appendchild(self, child): + self._children.append(child) + return self + + +class ContentNode(TreeNode): + + def __init__(self, content): + super().__init__(content) + + def __str__(self): + return "{}\tcontent: {}".format(super().__str__(), self.content) + + def __eq__(self, other): + try: + return other.content == self.content and super().__eq__(other) + except AttributeError: + return False + + def appendchild(self, child): + try: + self._children.append(child) + return self + except Exception as e: + raise RuntimeError("ContentNode.appendchild") from e + + +class TextNode(ContentNode): + + def __init__(self, content): + super().__init__(content) + self.nodetype = "Text" + + def dup(self): + return self._dup(TextNode, self.content) + + +class TextFieldNode(ContentNode): + + def __init__(self, content): + super().__init__(content) + self.nodetype = "TextField" + + def dup(self): + return self._dup(TextFieldNode, self.content) + + +class ControlCharacterNode(TreeNode): + def __init__(self, char): + super().__init__() + self.char = char + self.nodetype = "ControlCharacter" + + def __str__(self): + return "{}\tcontent: {}".format(super().__str__(), self.char) + + def __eq__(self, other): + try: + return other.char == self.char and super().__eq__(other) + except AttributeError: + return False + + def dup(self): + return self._dup(ControlCharacterNode, self.char) + + +class FootnoteNode(TreeNode): + + def __init__(self, label): + super().__init__() + self.label = label + self.nodetype = "Footnote" + + def __str__(self): + return "{}\tlabel: {}".format(super().__str__(), self.label) + + def __eq__(self, other): + try: + return other.label == self.label and super().__eq__(other) + except AttributeError: + return False + + def dup(self): + return self._dup(FootnoteNode, self.label) + + +class FrameNode(TreeNode): + def __init__(self, name, anchor): + super().__init__() + self.name = name + self.anchor = anchor + self.nodetype = "Frame" + + def __str__(self): + return "{}\tname: {}\tanchor: {}".format( + super().__str__(),self.name, self.str_anchor(self.anchor)) + + def __eq__(self, other): + try: + return (other.name == self.name and + other.anchor == self.anchor and + super().__eq__(other)) + except AttributeError: + return False + + def dup(self): + return self._dup(FrameNode, self.name, self.anchor) + + def str_anchor(self, anchor): + anchors = {str(AS_CHARACTER): "AS_CHARACTER", + str(AT_CHARACTER): "AT_CHARACTER", + str(AT_PARAGRAPH): "AT_PARAGRAPH", + str(AT_PAGE): "AT_PAGE", + str(AT_FRAME): "AT_FRAME"} + try: + return anchors[str(anchor)] + except KeyError: + raise RuntimeError("unknown anchor") + + +class MetaNode(TreeNode): + def __init__(self, xmlid): + super().__init__() + self.xmlid = xmlid + self.nodetype = "InContentMetadata" + self.isnesting = True + + def __str__(self): + return "{}\txmlid: {}#{}".format( + super().__str__(), self.xmlid.First, self.xmlid.Second) + + def __eq__(self, other): + try: + return (type(other) == type(self) and + MetaNode.eq(other.xmlid, self.xmlid)) + except AttributeError: + return False + + @classmethod + def eq(cls, left, right): + return left.First == right.First and left.Second == right.Second + + def dup(self): + return self._dup(MetaNode, self.xmlid) + + +class MarkNode(TreeNode): + def __init__(self, name, ispoint=True): + super().__init__() + self.name = name + self.ispoint = ispoint + self.isstart = False + + def __str__(self): + return "{}\tisPoint: {}\tisStart: {}".format( + super().__str__(), self.ispoint, self.isstart) + + def __eq__(self, other): + try: + return (other.name == self.name and + other.ispoint == self.ispoint and + other.isstart == self.isstart) + except AttributeError: + return False + + +class BookmarkNode(MarkNode): + def __init__(self, name, xmlid=StringPair()): + super().__init__(name) + self.xmlid = xmlid + self.nodetype = "Bookmark" + + def __str__(self): + return "{}\txmlid: {}#{}".format( + super().__str__(), self.xmlid.First, self.xmlid.Second) + + def __eq__(self, other): + try: + return (type(other) == type(self) and + super().__eq__(other) and + MetaNode.eq(other.xmlid, self.xmlid)) + except AttributeError: + return False + + def dup(self): + return self._dup(BookmarkNode, self.name, self.xmlid) + + +class BookmarkStartNode(BookmarkNode): + + def __init__(self, name, xmlid=StringPair()): + super().__init__(name, xmlid) + self.ispoint = False + self.isstart = True + + def dup(self): + return self._dup(BookmarkStartNode, self.name) + + +class BookmarkEndNode(BookmarkNode): + + def __init__(self, name, xmlid=StringPair()): + super().__init__(name, xmlid) + self.ispoint = False + self.isstart = False + + def dup(self): + return self._dup(BookmarkEndNode, self.name) + + +class ReferenceMarkNode(MarkNode): + def __init__(self, name): + super().__init__(name) + self.nodetype = "ReferenceMark" + + def __eq__(self, other): + return (type(other) == type(self) and super().__eq__(other)) + + def dup(self): + return self._dup(ReferenceMarkNode, self.name) + + +class ReferenceMarkStartNode(ReferenceMarkNode): + def __init__(self, name): + super().__init__(name) + self.ispoint = False + self.isstart = True + + def dup(self): + return self._dup(ReferenceMarkStartNode, self.name) + + +class ReferenceMarkEndNode(ReferenceMarkNode): + def __init__(self, name): + super().__init__(name) + self.ispoint = False + self.isstart = False + + def dup(self): + return self._dup(ReferenceMarkEndNode, self.name) + + +class DocumentIndexMarkNode(MarkNode): + def __init__(self, name): + super().__init__(name) + self.nodetype = "DocumentIndexMark" + + def __eq__(self, other): + return (type(other) == type(self) and super().__eq__(other)) + + def dup(self): + return self._dup(DocumentIndexMarkNode, self.name) + + +class DocumentIndexMarkStartNode(DocumentIndexMarkNode): + def __init__(self, name): + super().__init__(name) + self.ispoint = False + self.isstart = True + + def dup(self): + return self._dup(DocumentIndexMarkStartNode, self.name) + + +class DocumentIndexMarkEndNode(DocumentIndexMarkNode): + def __init__(self, name): + super().__init__(name) + self.ispoint = False + self.isstart = False + + def dup(self): + return self._dup(DocumentIndexMarkEndNode, self.name) + + +class HyperlinkNode(TreeNode): + def __init__(self, url): + super().__init__() + self.nodetype = "Hyperlink" + self.isnesting = True + if url: + self.url = url + else: + raise RuntimeError("HyperlinkNode") + + def __str__(self): + return "{}\turl: {}".format(super().__str__(), self.url) + + def __eq__(self, other): + try: + return other.url == self.url and super().__eq__(other) + except AttributeError: + return False + + def dup(self): + return self._dup(HyperlinkNode, self.url) + + +class RubyNode(TreeNode): + def __init__(self, ruby): + super().__init__() + self.nodetype = "Ruby" + self.isnesting = True + if ruby: + self.ruby = ruby + else: + raise RuntimeError("RubyNode") + + def __str__(self): + return "{}\trubytext: {}".format(super().__str__(), self.ruby) + + def __eq__(self, other): + try: + return other.ruby == self.ruby and super().__eq__(other) + except AttributeError: + return False + + def dup(self): + return self._dup(RubyNode, self.ruby) + + +class MetaFieldNode(MetaNode): + def __init__(self, xmlid): + super().__init__(xmlid) + self.nodetype = "MetadataField" + + def dup(self): + return self._dup(MetaFieldNode, self.xmlid) + + +class Range(): + def __init__(self, start, end, node): + self.start = start + self.end = end + self.node = node + self.extent = end - start + + +class Inserter(): + + def __init__(self, xDoc): + self.xDoc = xDoc + self.xText = xDoc.getText() + self.xCursor = self.xText.createTextCursor() + + def initparagraph(self): + ## we split the first (empty) paragraph, and then insert into the + ## second (empty) paragraph; this ensures first is always empty! + self.xCursor.gotoStartOfParagraph(False) + self.xText.insertControlCharacter(self.xCursor, PARAGRAPH_BREAK, False) + + def inserttext(self, xCursor, text): + xCursor.setString(text) + + def inserttextfield(self, xCursor, content): + xContent = self.maketextfield(content) + xContent.attach(xCursor) + + def maketextfield(self, content): + xField = self.xDoc.createInstance("com.sun.star.text.textfield.Author") + xField.IsFixed = True + xField.FullName = False + xField.Content = content + return xField + + def insertcontrolcharacter(self, xCursor, cchar): + self.xText.insertControlCharacter(xCursor, cchar, False) + + def insertframe(self, xCursor, name, anchor): + xContent = self.makeframe(name, anchor) + xContent.attach(xCursor) + + def makeframe(self, name, anchor): + xFrame = self.xDoc.createInstance("com.sun.star.text.TextFrame") + xFrame.AnchorType = anchor + xFrame.setName(name) + return xFrame + + def insertfootnote(self, xCursor, label): + xContent = self.makefootnote(label) + xContent.attach(xCursor) + + def makefootnote(self, label): + xFootNote = self.xDoc.createInstance("com.sun.star.text.Footnote") + xFootNote.setLabel(label) + return xFootNote + + def insertbookmark(self, xCursor, name, xmlid): + xContent = self.makebookmark(name) + xContent.attach(xCursor) + if xmlid.First != "": + xContent.MetadataReference = xmlid + + def makebookmark(self, name): + xBookmark = self.xDoc.createInstance("com.sun.star.text.Bookmark") + xBookmark.setName(name) + return xBookmark + + def insertreferencemark(self, xCursor, name): + xContent = self.makereferencemark(name) + xContent.attach(xCursor) + + def makereferencemark(self, name): + xMark = self.xDoc.createInstance("com.sun.star.text.ReferenceMark") + xMark.setName(name) + return xMark + + def insertdocumentindexmark(self, xCursor, key): + xContent = self.makedocumentindexmark(key) + xContent.attach(xCursor) + + def makedocumentindexmark(self, key): + xMark = self.xDoc.createInstance("com.sun.star.text.DocumentIndexMark") + xMark.PrimaryKey = key + return xMark + + def inserthyperlink(self, xCursor, url): + xCursor.HyperLinkURL = url + + def insertruby(self, xCursor, rubytext): + xCursor.RubyText = rubytext + + def insertmeta(self, xCursor, xmlid): + xContent = self.makemeta() + xContent.attach(xCursor) + xContent.MetadataReference = xmlid + return xContent + + def makemeta(self): + xMeta = self.xDoc.createInstance("com.sun.star.text.InContentMetadata") + return xMeta + + def insertmetafield(self, xCursor, xmlid): + xContent = self.makemetafield() + xContent.attach(xCursor) + xContent.MetadataReference = xmlid + return xContent + + def makemetafield(self): + xMeta = self.xDoc.createInstance( + "com.sun.star.text.textfield.MetadataField") + return xMeta + + +class TreeInserter(Inserter): + + def __init__(self, xDoc): + super().__init__(xDoc) + self._bookmarkstarts = {} + self._referencemarkstarts = {} + self._documentindexmarkstarts = {} + self._framehints = [] + + def inserttree(self, tree): + if tree.nodetype != "__ROOT__": + raise RuntimeError("insertTree: test error: no root") + self.initparagraph() + self.insertchildren(tree.createenumeration()) + for p in self._framehints: + self.insertframe(p[0], p[1].name, p[1].anchor) + + def insertchildren(self, children): + xCursor = self.xCursor + for node in children: + xCursor.gotoEndOfParagraph(False) + type_ = node.nodetype + if type_ == "Text": + self.inserttext(xCursor, node.content) + elif type_ == "TextField": + self.inserttextfield(xCursor, node.content) + elif type_ == "ControlCharacter": + self.insertcontrolcharacter(xCursor, node.char) + elif type_ == "Footnote": + self.insertfootnote(xCursor, node.label) + elif type_ == "Frame": + if node.anchor == AT_CHARACTER: + self._framehints.append((xCursor.getStart(), node)) + else: + self.insertframe(xCursor, node.name, node.anchor) + elif type_ == "Bookmark": + name = node.name + id_ = node.xmlid + if node.ispoint: + self.insertbookmark(xCursor, name, id_) + elif node.isstart: + self._bookmarkstarts[name] = xCursor.getStart() + else: + xRange = self._bookmarkstarts[name] + xParaCursor = self.mkcursor(xRange) + self.insertbookmark(xParaCursor, name, id_) + elif type_ == "ReferenceMark": + name = node.name + if node.ispoint: + self.insertreferencemark(xCursor, name) + elif node.isstart: + self._referencemarkstarts[name] = xCursor.getStart() + else: + xRange = self._referencemarkstarts[name] + xParaCursor = self.mkcursor(xRange) + self.insertreferencemark(xParaCursor, name) + elif type_ == "DocumentIndexMark": + name = node.name + if node.ispoint: + self.insertdocumentindexmark(xCursor, name) + elif node.isstart: + self._documentindexmarkstarts[name] = xCursor.getStart() + else: + xRange = self._documentindexmarkstarts[name] + xParaCursor = self.mkcursor(xRange) + self.insertdocumentindexmark(xParaCursor, name) + elif type_ == "Hyperlink": + xRange = xCursor.getStart() + self.insertchildren(node.createenumeration()) + xParaCursor = self.mkcursor(xRange) + self.inserthyperlink(xParaCursor, node.url) + elif type_ == "Ruby": + xRange = xCursor.getStart() + self.insertchildren(node.createenumeration()) + xParaCursor = self.mkcursor(xRange) + self.insertruby(xParaCursor, node.ruby) + elif type_ == "InContentMetadata": + xRange = xCursor.getStart() + self.insertchildren(node.createenumeration()) + xParaCursor = self.mkcursor(xRange) + self.insertmeta(xParaCursor, node.xmlid) + elif type_ == "MetadataField": + xRange = xCursor.getStart() + self.insertchildren(node.createenumeration()) + xParaCursor = self.mkcursor(xRange) + self.insertmetafield(xParaCursor, node.xmlid) + elif type_ == "SoftPageBreak": + raise RuntimeError("sorry, cannot test SoftPageBreak") + else: + raise RuntimeError("unexpected type: {}".format(type_)) + + def mkcursor(self, xRange): + xCursor = self.xText.createTextCursorByRange(xRange) + xCursor.gotoEndOfParagraph(True) + return xCursor + + +# FIXME: this does not account for inserted dummy characters! +class RangeInserter(Inserter): + def __init__(self, xDoc): + super().__init__(xDoc) + self.initparagraph() + + # def inserttext(self, pos, text): + # self.xCursor.gotoStartOfParagraph(False) + # self.xCursor.goRight(pos, False) + # self.inserttext(self.xCursor, text) + + def insertrange(self, range): + self.xCursor.gotoStartOfParagraph(False) + self.xCursor.goRight(range.start, False) + self.xCursor.goRight(range.extent, True) + return self.insertnode(self.xCursor, range.node) + + def insertnode(self, xParaCursor, node): + nodetype = node.nodetype + if nodetype == "Text": + text = node + self.inserttext(xParaCursor, text.content) + elif nodetype == "Hyperlink": + href = node + self.inserthyperlink(xParaCursor, href.url) + elif nodetype == "Ruby": + ruby = node + self.insertruby(xParaCursor, ruby.ruby) + elif nodetype == "InContentMetadata": + meta = node + return self.insertmeta(xParaCursor, meta.xmlid) + elif nodetype == "MetadataField": + meta = node + return self.insertmetafield(xParaCursor, meta.xmlid) + elif nodetype == "Bookmark": + bkmk = node + if bkmk.ispoint: + raise RuntimeError("range only") + self.insertbookmark(xParaCursor, bkmk.name, bkmk.xmlid) + elif nodetype == "ReferenceMark": + mark = node + if mark.ispoint: + raise RuntimeError("range only") + self.insertreferencemark(xParaCursor, mark.name) + elif nodetype == "DocumentIndexMark": + mark = node + if mark.ispoint: + raise RuntimeError("range only") + self.insertdocumentindexmark(xParaCursor, mark.name) + elif nodetype == "TextField": + field = node + self.inserttextfield(self.xCursor, field.content) + elif nodetype == "Footnote": + note = node + self.insertfootnote(self.xCursor, note.label) + elif nodetype == "Frame": + frame = node + self.insertframe(xParaCursor, frame.name, frame.anchor) + elif nodetype == "ControlCharacter": + cchar = node + self.insertcontrolcharacter(self.xCursor, cchar.char) + elif nodetype == "SoftPageBreak": + raise RuntimeError("sorry, cannot test SoftPageBreak") + else: + raise RuntimeError("unexpected nodetype: {}".format(nodetype)) + return None + + +class EnumConverter(): + + def __init__(self): + self._stack = [] + + def convert(self, xEnum): + root = TreeNode() + self._stack.append(root) + ret = self.convertchildren(xEnum) + assert (len(self._stack)==0), "EnumConverter.convert: stack is not empty" + return ret + + def convertchildren(self, xEnum): + for xPortion in xEnum: + type_ = xPortion.TextPortionType + if type_ == "Text": + text = xPortion.getString() + node = TextNode(text) + url = xPortion.HyperLinkURL + if len(url) > 0: + temp = node + node = HyperlinkNode(url) + node.appendchild(temp) + elif type_ == "TextField": + xField = xPortion.TextField + if xField.supportsService("com.sun.star.text.textfield.MetadataField"): + xmlid = xField.MetadataReference + node = MetaFieldNode(xmlid) + self._stack.append(node) + xEnumChildren = xField.createEnumeration() + node2 = self.convertchildren(xEnumChildren) + print(node) + print(node2) + assert (node2 is node), "stack error: meta-field" + else: + content = xField.Content + isFixed = xField.IsFixed + assert isFixed, "field not fixed?" + node = TextFieldNode(content) + elif type_ == "ControlCharacter": + c = xPortion.ControlCharacter + node = ControlCharacterNode(c) + elif type_ == "Footnote": + xFootnote = xPortion.Footnote + label = xFootnote.getLabel() + node = FootnoteNode(label) + elif type_ == "Frame": + xCEA = xPortion.createContentEnumeration('') + while xCEA.hasMoreElements(): + xFrame = xCEA.nextElement() + anchor = xFrame.AnchorType + name = xFrame.getName() + node = FrameNode(name, anchor) + self._stack[-1].appendchild(node) + continue + elif type_ == "Bookmark": + xMark = xPortion.Bookmark + name = xMark.getName() + xmlid = xMark.MetadataReference + isCollapsed = xPortion.IsCollapsed + if isCollapsed: + node = BookmarkNode(name, xmlid) + else: + isStart = xPortion.IsStart + if isStart: + node = BookmarkStartNode(name, xmlid) + else: + node = BookmarkEndNode(name, xmlid) + elif type_ == "ReferenceMark": + xMark = xPortion.ReferenceMark + name = xMark.getName() + isCollapsed = xPortion.IsCollapsed + if isCollapsed: + node = ReferenceMarkNode(name) + else: + isStart = xPortion.IsStart + if isStart: + node = ReferenceMarkStartNode(name) + else: + node = ReferenceMarkEndNode(name) + elif type_ == "DocumentIndexMark": + xMark = xPortion.DocumentIndexMark + name = xMark.PrimaryKey + isCollapsed = xPortion.IsCollapsed + if isCollapsed: + node = DocumentIndexMarkNode(name) + else: + isStart = xPortion.IsStart + if isStart: + node = DocumentIndexMarkStartNode(name) + else: + node = DocumentIndexMarkEndNode(name) + elif type_ == "Ruby": + isStart = xPortion.IsStart + if isStart: + # ARRGH!!! stupid api... + # the text is ONLY at the start! + ruby = xPortion.RubyText + node = RubyNode(ruby) + self._stack.append(node) + continue + else: + node = self._stack.pop() + assert (isinstance(node, RubyNode)), "stack error: Ruby expected; is: {}".format(str(node)) + elif type_ == "InContentMetadata": + xMeta = xPortion.InContentMetadata + xmlid = xMeta.MetadataReference + node = MetaNode(xmlid) + self._stack.append(node) + xEnumChildren = xMeta.createEnumeration() + node2 = self.convertchildren(xEnumChildren) + assert (node2 is node), "stack error: meta" + elif type_ == "SoftPageBreak": + node = SoftPageBreakNode() + else: + raise RuntimeError("unexpected type: {}".format(type_)) + self._stack[-1].appendchild(node) + ret = self._stack.pop() + return ret + + +class FuzzyTester(): + '''this is where we nail the pudding to the wall''' + def __init__(self): + self.diffcontent = 0 + self.diffmissing = 0 + self.diffnesting = 0 + self.diffspuriousemptytext = 0 + self.diffsequence = 0 # ignored? + self.stackexpected = [] + self.stackactual = [] + self.bufferexpected = [] + self.bufferactual = [] + + def dotest(self, expected, actual): + '''idea: traverse both trees, enumerate nodes, stopping at content nodes. + then compare buffers.''' + assert "__ROOT__" == expected.nodetype + assert "__ROOT__" == actual.nodetype + self.stackexpected.append((expected, expected.createenumeration())) + self.stackactual.append((actual, actual.createenumeration())) + while self.stackexpected or self.stackactual: + self.traverse(self.stackexpected, self.bufferexpected) + self.traverse(self.stackactual, self.bufferactual) + self.testbuffer() + if self.diffsequence: + print("warning: {} differences in sequence".format( + self.diffsequence)) + if self.diffspuriousemptytext: + print("warning: {} spurious empty text nodes".format( + self.diffspuriousemptytext)) + if self.diffnesting: + print("WARNING: {} differences in nesting".format( + self.diffnesting)) + assert self.diffcontent == 0 + assert self.diffmissing == 0 + + def traverse(self, stack, buffer): + while stack: + topenum = stack[-1][1] + try: + node = next(topenum) + buffer.append(node) + if node._children: + node_enum = node.createenumeration() + stack.append((node, node_enum)) + if node.content: + if not (isinstance(node, TextNode) and # spurious empty text? + len(node.content) == 0): + return # break here + except StopIteration: + buffer.append(stack[-1][0]) + stack.pop() + + def testterminatingnode(self): + lenexpected = len(self.bufferexpected) + lenactual = len(self.bufferactual) + if lenexpected == 0 or lenactual == 0: + return + expected = self.bufferexpected[-1] + actual = self.bufferactual[-1] + eroot = expected.nodetype == "__ROOT__" + aroot = actual.nodetype == "__ROOT__" + if eroot or aroot: + if not (eroot and aroot): + if aroot: + self.printmissing(expected) + else: + self.printunexpected(actual) + self.diffmissing += 1 + return + self.testcontentnode(expected, actual) + self.bufferexpected[-1] = None + self.bufferactual[-1] = None + + def testcontentnode(self, expected, actual): + contentexpected = expected.content + contentactual = actual.content + if expected != actual: + self.printdiff("text content differs", contentexpected, contentactual) + self.diffcontent += 1 + + def testbuffer(self): + lenactual = len(self.bufferactual) + tmp_bufferactual = self.bufferactual[:] + for i, node in enumerate(self.bufferexpected): + try: + j = tmp_bufferactual.index(node) + if j != i: + # FIXME how bad is this? + self.printdiff("position differs", i, j) + # a hacky hack + min_ = min(i,j) + max_ = max(min(lenactual-1, i),j) + for k in range(min_, max_): + tmp = tmp_bufferactual[k] + if tmp and tmp.isnesting: + self.printnesting(node, tmp) + self.diffnesting += 1 + self.diffsequence += 1 + tmp_bufferactual[j] = None + except ValueError: + print('perdrix') + self.printmissing(node) + self.diffmissing += 1 + for j, node in enumerate(tmp_bufferactual): + if node: + self.printunexpected(node) + if isinstance(node, TextNode) and len(node.content) == 0: + self.diffspuriousemptytext += 1 + else: + print('renard') + self.diffmissing += 1 + self.testterminatingnode() + self.bufferexpected[:] = [] + self.bufferactual[:] = [] + + def printdiff(self, prefix, expected, actual): + print("{}:\texpected: {}\tactual: {}".format(prefix, expected, actual)) + + def printnesting(self, node, nesting): + print("node: {} possibly moved across nesting {}".format( + str(node), str(nesting))) + + def printmissing(self, node): + print(" missing node: {}".format(str(node))) + + def printunexpected(self, node): + print("unexpected node: {}".format(str(node))) + + +class TextPortionEnumerationTest(unittest.TestCase): + + xMSF = None + xContext = None + tempdir = None + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls.xDoc = cls._uno.openEmptyWriterDoc() + cls.count = 0 + + @classmethod + def tearDownClass(cls): + cls.xDoc.close(True) + cls._uno.tearDown() + # HACK in case cls.xDoc holds a UNO proxy to an SwXTextDocument (whose dtor calls + # Application::GetSolarMutex via sw::UnoImplPtrDeleter), which would potentially only be + # garbage-collected after VCL has already been deinitialized: + cls.xDoc = None + + def test_text(self): + root = TreeNode() + text = TextNode("abc") + root.appendchild(text) + self.dotest(root) + + def test_text_field(self): + self.mkname("ruby") + root = TreeNode() + txtf = TextFieldNode("abc") + root.appendchild(txtf) + self.dotest(root) + + @unittest.skip("FIXME this is converted to a text portion: ControlCharacter is obsolete") + def test_control_char(self): + root = TreeNode() + cchr = ControlCharacterNode(HARD_HYPHEN) + root.appendchild(cchr) + self.dotest(root) + + @unittest.skip("FIXME: insert a soft page break: not done") + def test_soft_page_break(self): + root = TreeNode() + spbk =SoftPageBreakNode() + text = TextNode("abc") + root.appendchild(spbk) + root.appendchild(text) + self.dotest(root) + + def test_footnote(self): + name = self.mkname("ftn") + root = TreeNode() + ftnd = FootnoteNode(name) + root.appendchild(ftnd) + self.dotest(root) + + def test_frame_as(self): + name = self.mkname("frame") + root = TreeNode() + fram = FrameNode(name, AS_CHARACTER) + root.appendchild(fram) + self.dotest(root) + + def test_frame_at(self): + name = self.mkname("frame") + root = TreeNode() + fram = FrameNode(name, AT_CHARACTER) + root.appendchild(fram) + self.dotest(root) + + def test_bookmark_point(self): + name = self.mkname("mark") + root = TreeNode() + bkmk = BookmarkNode(name) + text = TextNode("abc") + root.appendchild(bkmk) + root.appendchild(text) + self.dotest(root) + + def test_bookmark(self): + name = self.mkname("mark") + root = TreeNode() + bkm1 = BookmarkStartNode(name) + text = TextNode("abc") + bkm2 = BookmarkEndNode(name) + root.appendchild(bkm1) + root.appendchild(text) + root.appendchild(bkm2) + self.dotest(root) + + def test_bookmark_point_xmlid(self): + name = self.mkname("mark") + id = self.mkid("id") + root = TreeNode() + bkmk = BookmarkNode(name, id) + text = TextNode("abc") + root.appendchild(bkmk) + root.appendchild(text) + self.dotest(root) + + def test_bookmark_xmlid(self): + name = self.mkname("mark") + id = self.mkid("id") + root = TreeNode() + bkm1 = BookmarkStartNode(name, id) + text = TextNode("abc") + bkm2 = BookmarkEndNode(name, id) + root.appendchild(bkm1) + root.appendchild(text) + root.appendchild(bkm2) + self.dotest(root) + + def test_refmark_point(self): + name = self.mkname("refmark") + root = TreeNode() + rfmk = ReferenceMarkNode(name) + text = TextNode("abc") + root.appendchild(rfmk) + root.appendchild(text) + self.dotest(root) + + def test_refmark(self): + name = self.mkname("refmark") + root = TreeNode() + rfm1 = ReferenceMarkStartNode(name) + text = TextNode("abc") + rfm2 = ReferenceMarkEndNode(name) + root.appendchild(rfm1) + root.appendchild(text) + root.appendchild(rfm2) + self.dotest(root) + + def test_toxmark_point(self): + name = self.mkname("toxmark") + root = TreeNode() + txmk = DocumentIndexMarkNode(name) + text = TextNode("abc") + root.appendchild(txmk) + root.appendchild(text) + self.dotest(root) + + def test_toxmark(self): + name = self.mkname("toxmark") + root = TreeNode() + txm1 = DocumentIndexMarkStartNode(name) + text = TextNode("abc") + txm2 = DocumentIndexMarkEndNode(name) + root.appendchild(txm1) + root.appendchild(text) + root.appendchild(txm2) + self.dotest(root) + + def test_hyperlink(self): + name = self.mkname("url") + root = TreeNode() + href = HyperlinkNode(name) + text = TextNode("abc") + href.appendchild(text) + root.appendchild(href) + self.dotest(root) + + def test_hyperlink_empty(self): + name = self.mkname("url") + root = TreeNode() + href = HyperlinkNode(name) + text = TextNode("") + href.appendchild(text) + root.appendchild(href) + self.dotest(root) + + def test_ruby(self): + name = self.mkname("ruby") + root = TreeNode() + ruby = RubyNode(name) + text = TextNode("abc") + ruby.appendchild(text) + root.appendchild(ruby) + self.dotest(root) + + def test_ruby_empty(self): + # BUG: #i91534# + name = self.mkname("ruby") + root = TreeNode() + ruby = RubyNode(name) + root.appendchild(ruby) + self.dotest(root) + + def test_meta(self): + id = StringPair("content.xml", self.mkname("id")) + root = TreeNode() + meta = MetaNode(id) + text = TextNode("abc") + root.appendchild(TextNode("123")) + meta.appendchild(text) + root.appendchild(meta) + self.dotest(root) + + def test_meta_empty(self): + id = StringPair("content.xml", self.mkname("id")) + root = TreeNode() + meta = MetaNode(id) + root.appendchild(meta) + self.dotest(root) + + def test_meta_field(self): + id = StringPair("content.xml", self.mkname("id")) + root = TreeNode() + meta = MetaFieldNode(id) + text = TextNode("abc") + root.appendchild(TextNode("123")) + meta.appendchild(text) + root.appendchild(meta) + self.dotest(root) + + def test_meta_field_empty(self): + id = StringPair("content.xml", self.mkname("id")) + root = TreeNode() + meta = MetaFieldNode(id) + root.appendchild(meta) + self.dotest(root) + + def test_bookmark1(self): + name1 = self.mkname("mark") + name2 = self.mkname("mark") + name3 = self.mkname("mark") + root = TreeNode() + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(BookmarkNode(name2)) + root.appendchild(BookmarkStartNode(name3)) + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkEndNode(name1)) + root.appendchild(TextNode("de")) + root.appendchild(BookmarkEndNode(name3)) + self.dotest(root) + + def test_bookmark2(self): + name1 = self.mkname("mark") + name2 = self.mkname("mark") + name3 = self.mkname("mark") + root = TreeNode() + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkNode(name2)) + root.appendchild(BookmarkStartNode(name3)) + root.appendchild(BookmarkEndNode(name1)) + root.appendchild(TextNode("de")) + root.appendchild(BookmarkEndNode(name3)) + self.dotest(root) + + def test_refmark2(self): + name1 = self.mkname("refmark") + root = TreeNode() + root.appendchild(ReferenceMarkStartNode(name1)) + root.appendchild(TextNode("abc")) + # BUG: #i102541# (this is actually not unoportenum's fault) + root.appendchild(ReferenceMarkEndNode(name1)) + root.appendchild(TextNode("de")) + self.dotest(root) + + def test_refmark3(self): + # BUG: #i107672# (non-deterministic; depends on pointer ordering) + name1 = self.mkname("refmark") + name2 = self.mkname("refmark") + name3 = self.mkname("refmark") + name4 = self.mkname("refmark") + name5 = self.mkname("refmark") + name6 = self.mkname("refmark") + name7 = self.mkname("refmark") + root = TreeNode() + root.appendchild(ReferenceMarkStartNode(name1)) + root.appendchild(ReferenceMarkStartNode(name2)) + root.appendchild(ReferenceMarkStartNode(name3)) + root.appendchild(ReferenceMarkStartNode(name4)) + root.appendchild(ReferenceMarkStartNode(name5)) + root.appendchild(ReferenceMarkStartNode(name6)) + root.appendchild(ReferenceMarkStartNode(name7)) + root.appendchild(TextNode("abc")) + root.appendchild(ReferenceMarkEndNode(name7)) + root.appendchild(ReferenceMarkEndNode(name6)) + root.appendchild(ReferenceMarkEndNode(name5)) + root.appendchild(ReferenceMarkEndNode(name4)) + root.appendchild(ReferenceMarkEndNode(name3)) + root.appendchild(ReferenceMarkEndNode(name2)) + root.appendchild(ReferenceMarkEndNode(name1)) + root.appendchild(TextNode("de")) + self.dotest(root) + + def test_toxmark2(self): + name1 = self.mkname("toxmark") + root = TreeNode() + root.appendchild(DocumentIndexMarkStartNode(name1)) + root.appendchild(TextNode("abc")) + root.appendchild(DocumentIndexMarkEndNode(name1)) + root.appendchild(TextNode("de")) + self.dotest(root) + + def test_toxmark3(self): + # BUG: #i107672# (non-deterministic; depends on pointer ordering) + name1 = self.mkname("toxmark") + name2 = self.mkname("toxmark") + name3 = self.mkname("toxmark") + name4 = self.mkname("toxmark") + name5 = self.mkname("toxmark") + name6 = self.mkname("toxmark") + name7 = self.mkname("toxmark") + root = TreeNode() + root.appendchild(DocumentIndexMarkStartNode(name1)) + root.appendchild(DocumentIndexMarkStartNode(name2)) + root.appendchild(DocumentIndexMarkStartNode(name3)) + root.appendchild(DocumentIndexMarkStartNode(name4)) + root.appendchild(DocumentIndexMarkStartNode(name5)) + root.appendchild(DocumentIndexMarkStartNode(name6)) + root.appendchild(DocumentIndexMarkStartNode(name7)) + root.appendchild(TextNode("abc")) + root.appendchild(DocumentIndexMarkEndNode(name7)) + root.appendchild(DocumentIndexMarkEndNode(name6)) + root.appendchild(DocumentIndexMarkEndNode(name5)) + root.appendchild(DocumentIndexMarkEndNode(name4)) + root.appendchild(DocumentIndexMarkEndNode(name3)) + root.appendchild(DocumentIndexMarkEndNode(name2)) + root.appendchild(DocumentIndexMarkEndNode(name1)) + root.appendchild(TextNode("de")) + self.dotest(root) + + def test_marks1(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("toxmark") + name3 = self.mkname("refmark") + name4 = self.mkname("toxmark") + root = TreeNode() + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(DocumentIndexMarkNode(name2)) + root.appendchild(ReferenceMarkStartNode(name3)) + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkEndNode(name1)) + root.appendchild(DocumentIndexMarkStartNode(name4)) + root.appendchild(TextNode("de")) + root.appendchild(DocumentIndexMarkEndNode(name4)) + root.appendchild(ReferenceMarkEndNode(name3)) + self.dotest(root) + + def test_marks2(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("refmark") + name3 = self.mkname("refmark") + name4 = self.mkname("toxmark") + name5 = self.mkname("refmark") + root = TreeNode() + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(ReferenceMarkNode(name2)) + root.appendchild(ReferenceMarkStartNode(name3)) + root.appendchild(TextNode("abc")) + root.appendchild(DocumentIndexMarkStartNode(name4)) + root.appendchild(ReferenceMarkStartNode(name5)) + # BUG: #i102541# (this is actually not unoportenum's fault) + root.appendchild(ReferenceMarkEndNode(name3)) + root.appendchild(TextNode("de")) + root.appendchild(DocumentIndexMarkEndNode(name4)) + root.appendchild(BookmarkEndNode(name1)) + root.appendchild(ReferenceMarkEndNode(name5)) + self.dotest(root) + + def test_marks3(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("refmark") + name3 = self.mkname("refmark") + name4 = self.mkname("toxmark") + name5 = self.mkname("refmark") + root = TreeNode() + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(DocumentIndexMarkNode(name2)) + root.appendchild(DocumentIndexMarkStartNode(name3)) + root.appendchild(TextNode("abc")) + root.appendchild(ReferenceMarkStartNode(name4)) + root.appendchild(DocumentIndexMarkStartNode(name5)) + root.appendchild(DocumentIndexMarkEndNode(name3)) + root.appendchild(TextNode("de")) + root.appendchild(ReferenceMarkEndNode(name4)) + root.appendchild(BookmarkEndNode(name1)) + root.appendchild(DocumentIndexMarkEndNode(name5)) + self.dotest(root) + + def test_frame_mark1(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("frame") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkNode(name1)) + root.appendchild(TextNode("de")) + root.appendchild(FrameNode(name2, AS_CHARACTER)) + self.dotest(root) + + def test_frame_mark2(self): + # BUG: #i98530# + name1 = self.mkname("bookmark") + name2 = self.mkname("frame") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkNode(name1)) + root.appendchild(TextNode("de")) + root.appendchild(FrameNode(name2, AT_CHARACTER)) + self.dotest(root) + + def test_frame_mark3(self): + name1 = self.mkname("frame") + name2 = self.mkname("bookmark") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(FrameNode(name1, AS_CHARACTER)) + root.appendchild(TextNode("de")) + root.appendchild(BookmarkNode(name2)) + self.dotest(root) + + def test_frame_mark4(self): + name1 = self.mkname("frame") + name2 = self.mkname("bookmark") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(FrameNode(name1, AT_CHARACTER)) + root.appendchild(TextNode("de")) + root.appendchild(BookmarkNode(name2)) + self.dotest(root) + + def test_frames1(self): + name1 = self.mkname("frame") + name2 = self.mkname("frame") + name3 = self.mkname("frame") + root = TreeNode() + root.appendchild(FrameNode(name1, AT_CHARACTER)) + root.appendchild(FrameNode(name2, AT_CHARACTER)) + root.appendchild(FrameNode(name3, AT_CHARACTER)) + self.dotest(root) + + def test_frames2(self): + name1 = self.mkname("frame") + name2 = self.mkname("frame") + name3 = self.mkname("frame") + root = TreeNode() + root.appendchild(FrameNode(name1, AS_CHARACTER)) + root.appendchild(FrameNode(name2, AS_CHARACTER)) + root.appendchild(FrameNode(name3, AS_CHARACTER)) + self.dotest(root) + + def test_frames3(self): + name1 = self.mkname("frame") + name2 = self.mkname("frame") + name3 = self.mkname("frame") + root = TreeNode() + root.appendchild(FrameNode(name1, AT_CHARACTER)) + root.appendchild(FrameNode(name2, AS_CHARACTER)) + root.appendchild(FrameNode(name3, AT_CHARACTER)) + self.dotest(root) + + def test_frames4(self): + name1 = self.mkname("frame") + name2 = self.mkname("frame") + name3 = self.mkname("frame") + root = TreeNode() + root.appendchild(FrameNode(name1, AT_CHARACTER)) + root.appendchild(FrameNode(name2, AT_CHARACTER)) + root.appendchild(FrameNode(name3, AS_CHARACTER)) + self.dotest(root) + + def test_frames5(self): + name1 = self.mkname("frame") + name2 = self.mkname("frame") + name3 = self.mkname("frame") + root = TreeNode() + root.appendchild(FrameNode(name1, AS_CHARACTER)) + root.appendchild(FrameNode(name2, AT_CHARACTER)) + root.appendchild(FrameNode(name3, AT_CHARACTER)) + self.dotest(root) + + def test_ruby_hyperlink1(self): + name1 = self.mkname("ruby") + name2 = self.mkname("url") + root = TreeNode() + ruby = RubyNode(name1) + href = HyperlinkNode(name2) + href.appendchild(TextNode("abc")) + ruby.appendchild(href) + root.appendchild(ruby) + self.dotest(root) + + def test_ruby_hyperlink2(self): + name1 = self.mkname("url") + name2 = self.mkname("ruby") + root = TreeNode() + href = HyperlinkNode(name1) + ruby = RubyNode(name2) + ruby.appendchild(TextNode("abc")) + href.appendchild(ruby) + root.appendchild(href) + self.dotest(root) + + def test_end1(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("toxmark") + name3 = self.mkname("refmark") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkNode(name1)) + root.appendchild(DocumentIndexMarkNode(name2)) + root.appendchild(ReferenceMarkNode(name3)) + self.dotest(root) + + def test_end2(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("frame") + name3 = self.mkname("refmark") + name4 = self.mkname("frame") + name5 = self.mkname("frame") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(FrameNode(name2, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name1)) + root.appendchild(ReferenceMarkNode(name3)) + root.appendchild(FrameNode(name4, AT_CHARACTER)) + root.appendchild(FrameNode(name5, AT_CHARACTER)) + self.dotest(root) + + def test_end3(self): + name1 = self.mkname("ftn") + name2 = self.mkname("toxmark") + root = TreeNode() + root.appendchild(TextNode("abc")) + root.appendchild(FootnoteNode(name1)) + root.appendchild(DocumentIndexMarkNode(name2)) + self.dotest(root) + + def test_end4(self): + name1 = self.mkname("bookmark") + name2 = self.mkname("frame") + root = TreeNode() + root.appendchild(BookmarkStartNode(name1)) + root.appendchild(TextNode("abc")) + root.appendchild(FrameNode(name2, AS_CHARACTER)) + root.appendchild(BookmarkEndNode(name1)) + self.dotest(root) + + def test_end5(self): + name1 = self.mkname("refmark") + name2 = self.mkname("ruby") + root = TreeNode() + root.appendchild(ReferenceMarkStartNode(name1)) + root.appendchild(TextNode("abc")) + ruby = RubyNode(name2) + ruby.appendchild(TextFieldNode("de")) + root.appendchild(ruby) + root.appendchild(ReferenceMarkEndNode(name1)) + self.dotest(root) + + def test_empty1(self): + name1 = self.mkname("refmark") + name2 = self.mkname("toxmark") + name3 = self.mkname("bookmark") + name4 = self.mkname("frame") + name7 = self.mkname("refmark") + name8 = self.mkname("toxmark") + name9 = self.mkname("bookmark") + nameA = self.mkname("frame") + root = TreeNode() + root.appendchild(ReferenceMarkNode(name1)) + root.appendchild(DocumentIndexMarkNode(name2)) + root.appendchild(BookmarkStartNode(name3)) + root.appendchild(FrameNode(name4, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name3)) + root.appendchild(ReferenceMarkNode(name7)) + root.appendchild(DocumentIndexMarkNode(name8)) + root.appendchild(BookmarkStartNode(name9)) + root.appendchild(FrameNode(nameA, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name9)) + self.dotest(root) + + def test_empty2(self): + name3 = self.mkname("bookmark") + name4 = self.mkname("frame") + name9 = self.mkname("bookmark") + nameA = self.mkname("frame") + root = TreeNode() + root.appendchild(BookmarkStartNode(name3)) + root.appendchild(FrameNode(name4, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name3)) + root.appendchild(BookmarkStartNode(name9)) + root.appendchild(FrameNode(nameA, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name9)) + self.dotest(root) + + def test_empty3(self): + name1 = self.mkname("refmark") + name2 = self.mkname("toxmark") + name3 = self.mkname("bookmark") + name4 = self.mkname("frame") + name5 = self.mkname("url") + name6 = self.mkname("ruby") + name7 = self.mkname("refmark") + name8 = self.mkname("toxmark") + name9 = self.mkname("bookmark") + nameA = self.mkname("frame") + root = TreeNode() + root.appendchild(ReferenceMarkNode(name1)) + root.appendchild(DocumentIndexMarkNode(name2)) + root.appendchild(BookmarkStartNode(name3)) + root.appendchild(FrameNode(name4, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name3)) + ## currently empty hyperlinks may get eaten... + # href = HyperlinkNode(name5) + # href.appendchild(TextNode("")) + # root.appendchild(href) + ruby = RubyNode(name6) + root.appendchild(ruby) + root.appendchild(ReferenceMarkNode(name7)) + root.appendchild(DocumentIndexMarkNode(name8)) + root.appendchild(BookmarkStartNode(name9)) + root.appendchild(FrameNode(nameA, AT_CHARACTER)) + root.appendchild(BookmarkEndNode(name9)) + self.dotest(root) + + def test1(self): + name1 = self.mkname("frame") + name2 = self.mkname("bookmark") + name3 = self.mkname("ruby") + name4 = self.mkname("ftn") + name5 = self.mkname("frame") + root = TreeNode() + root.appendchild(FrameNode(name1, AT_CHARACTER)) + root.appendchild(BookmarkStartNode(name2)) + root.appendchild(TextNode("abc")) + ruby = RubyNode(name3) + ruby.appendchild(TextNode("de")) + ruby.appendchild(FootnoteNode(name4)) + ruby.appendchild(BookmarkEndNode(name2)) + root.appendchild(ruby) + root.appendchild(TextNode("fg")) + root.appendchild(FrameNode(name5, AT_CHARACTER)) + root.appendchild(TextFieldNode("h")) + self.dotest(root) + + # some range tests for the insertion: these are for the current + # API which treats hyperlinks and rubys not as entities, but as formatting + # attributes; if these ever become entities, they should not be split!''' + + def test_range1(self): + name1 = self.mkname("url") + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("12345") + inserter.insertrange(Range(0, 0, text)) + url1 = HyperlinkNode(name1) + range1 = Range(0, 5, url1) + inserter.insertrange(range1) + root = TreeNode() + root.appendchild(url1) + url1.appendchild(text) + self.dotest(root, False) + + def test_range_hyperlink_hyperlink(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + url1 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(1, 4, url1)) + ## overlap left + url2 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(0, 2, url2)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("12"))) + root.appendchild(url1.dup().appendchild(TextNode("34"))) + root.appendchild(TextNode("56789")) + self.dotest(root, False) + ## overlap right + url3 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(3, 7, url3)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("12"))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(url3.dup().appendchild(TextNode("4567"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## around + url4 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(3, 7, url4)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("12"))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(url4.dup().appendchild(TextNode("4567"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## inside + url5 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(4, 6, url5)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("12"))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(url4.dup().appendchild(TextNode("4"))) + root.appendchild(url5.dup().appendchild(TextNode("56"))) + root.appendchild(url4.dup().appendchild(TextNode("7"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## empty + url6 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(7, 7, url6)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("12"))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(url4.dup().appendchild(TextNode("4"))) + root.appendchild(url5.dup().appendchild(TextNode("56"))) + root.appendchild(url4.dup().appendchild(TextNode("7"))) + ## this one gets eaten, but we still need to test inserting it (#i106930#) + # root.appendchild(url6.dup().appendchild(TextNode(""))) + root.appendchild(TextNode("89")) + ## inside (left-edge) + url7 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(0, 1, url7)) + root = TreeNode() + root.appendchild(url7.dup().appendchild(TextNode("1"))) + root.appendchild(url2.dup().appendchild(TextNode("2"))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(url4.dup().appendchild(TextNode("4"))) + root.appendchild(url5.dup().appendchild(TextNode("56"))) + root.appendchild(url4.dup().appendchild(TextNode("7"))) + root.appendchild(TextNode("89")) + ## inside (right-edge) + url8 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(5, 6, url8)) + root = TreeNode() + root.appendchild(url7.dup().appendchild(TextNode("1"))) + root.appendchild(url2.dup().appendchild(TextNode("2"))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(url4.dup().appendchild(TextNode("4"))) + root.appendchild(url5.dup().appendchild(TextNode("5"))) + root.appendchild(url8.dup().appendchild(TextNode("6"))) + root.appendchild(url4.dup().appendchild(TextNode("7"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + + def test_range_hyperlink_ruby(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + url1 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(1, 4, url1)) + # overlap left + rby2 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(0, 2, rby2)) + root = TreeNode() + root.appendchild(rby2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(url1.dup().appendchild(TextNode("34"))) + root.appendchild(TextNode("56789")) + self.dotest(root, False) + # overlap right + rby3 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(3, 5, rby3)) + root = TreeNode() + root.appendchild(rby2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(rby3.dup() + .appendchild(url1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + # around + rby4 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(2, 3, rby4)) + root = TreeNode() + root.appendchild(rby2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(rby4.dup() + .appendchild(url1.dup().appendchild(TextNode("3")))) + root.appendchild(rby3.dup() + .appendchild(url1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + # inside + url5 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(6, 9, url5)) + rby6 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(7, 8, rby6)) + root = TreeNode() + root.appendchild(rby2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(rby4.dup() + .appendchild(url1.dup().appendchild(TextNode("3")))) + root.appendchild(rby3.dup() + .appendchild(url1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6")) + root.appendchild(url5.dup().appendchild(TextNode("7"))) + root.appendchild(rby6.dup() + .appendchild(url5.dup().appendchild(TextNode("8")))) + root.appendchild(url5.dup().appendchild(TextNode("9"))) + self.dotest(root, False) + + def test_range_ruby_hyperlink(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + rby1 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(1, 6, rby1)) + ## overlap left + url2 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(0, 3, url2)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(rby1.dup() + .appendchild(url2.dup().appendchild(TextNode("23"))) + .appendchild(TextNode("456"))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## overlap right + url3 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(5, 7, url3)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(rby1.dup() + .appendchild(url2.dup().appendchild(TextNode("23"))) + .appendchild(TextNode("45")) + .appendchild(url3.dup().appendchild(TextNode("6")))) + root.appendchild(url3.dup().appendchild(TextNode("7"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## around (not quite, due to API) + url4 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(1, 8, url4)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(rby1.dup() + .appendchild(url4.dup() + .appendchild(TextNode("23456")))) + root.appendchild(url4.dup().appendchild(TextNode("78"))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + ## inside + url5 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(3, 5, url5)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(rby1.dup() + .appendchild(url4.dup() + .appendchild(TextNode("23"))) + .appendchild(url5.dup() + .appendchild(TextNode("45"))) + .appendchild(url4.dup() + .appendchild(TextNode("6")))) + root.appendchild(url4.dup().appendchild(TextNode("78"))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + + def test_range_ruby_ruby(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + rby1 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(1, 4, rby1)) + ## overlap left + rby2 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(0, 2, rby2)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("12"))) + root.appendchild(rby1.dup().appendchild(TextNode("34"))) + root.appendchild(TextNode("56789")) + self.dotest(root, False) + ## overlap right + rby3 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(3, 7, rby3)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("12"))) + root.appendchild(rby1.dup().appendchild(TextNode("3"))) + root.appendchild(rby3.dup().appendchild(TextNode("4567"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## around + rby4 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(3, 7, rby4)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("12"))) + root.appendchild(rby1.dup().appendchild(TextNode("3"))) + root.appendchild(rby4.dup().appendchild(TextNode("4567"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## inside + rby5 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(4, 6, rby5)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("12"))) + root.appendchild(rby1.dup().appendchild(TextNode("3"))) + root.appendchild(rby4.dup().appendchild(TextNode("4"))) + root.appendchild(rby5.dup().appendchild(TextNode("56"))) + root.appendchild(rby4.dup().appendchild(TextNode("7"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + + def test_range_hyperlink_meta(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + url1 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(1, 4, url1)) + ## overlap left + met2 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(0, 2, met2)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(url1.dup().appendchild(TextNode("34"))) + root.appendchild(TextNode("56789")) + self.dotest(root, False) + ## overlap right + met3 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(4-1, 6-1, met3)) + inserter.insertrange(Range(4, 6, met3)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(url1.dup().appendchild(TextNode("3"))) + root.appendchild(met3.dup() + .appendchild(url1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## around + met4 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(3-1, 4-1, met4)) + inserter.insertrange(Range(3, 4, met4)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(met4.dup() + .appendchild(url1.dup().appendchild(TextNode("3")))) + root.appendchild(met3.dup() + .appendchild(url1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## inside + url5 = HyperlinkNode(self.mkname("url")) + # inserter.insertrange(Range(9-3, 12-3, url5)) + inserter.insertrange(Range(9, 12, url5)) + met6 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(10-3, 11-3, met6)) + inserter.insertrange(Range(10, 11, met6)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(url1.dup().appendchild(TextNode("2")))) + root.appendchild(met4.dup() + .appendchild(url1.dup().appendchild(TextNode("3")))) + root.appendchild(met3.dup() + .appendchild(url1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6")) + root.appendchild(url5.dup().appendchild(TextNode("7"))) + root.appendchild(met6.dup() + .appendchild(url5.dup().appendchild(TextNode("8")))) + root.appendchild(url5.dup().appendchild(TextNode("9"))) + self.dotest(root, False) + + def test_range_ruby_meta(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + rby1 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(1, 4, rby1)) + ## overlap left + met2 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(0, 2, met2)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(rby1.dup().appendchild(TextNode("2")))) + root.appendchild(rby1.dup().appendchild(TextNode("34"))) + root.appendchild(TextNode("56789")) + self.dotest(root, False) + ## overlap right + met3 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(4-1, 6-1, met3)) + inserter.insertrange(Range(4, 6, met3)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(rby1.dup().appendchild(TextNode("2")))) + root.appendchild(rby1.dup().appendchild(TextNode("3"))) + root.appendchild(met3.dup() + .appendchild(rby1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## around + met4 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(3-1, 4-1, met4)) + inserter.insertrange(Range(3, 4, met4)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(rby1.dup().appendchild(TextNode("2")))) + root.appendchild(met4.dup() + .appendchild(rby1.dup().appendchild(TextNode("3")))) + root.appendchild(met3.dup() + .appendchild(rby1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## inside + rby5 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(9-3, 12-3, rby5)) + inserter.insertrange(Range(9, 12, rby5)) + met6 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(10-3, 11-3, met6)) + inserter.insertrange(Range(10, 11, met6)) + root = TreeNode() + root.appendchild(met2.dup() + .appendchild(TextNode("1")) + .appendchild(rby1.dup().appendchild(TextNode("2")))) + root.appendchild(met4.dup() + .appendchild(rby1.dup().appendchild(TextNode("3")))) + root.appendchild(met3.dup() + .appendchild(rby1.dup().appendchild(TextNode("4"))) + .appendchild(TextNode("5"))) + root.appendchild(TextNode("6")) + root.appendchild(rby5.dup() + .appendchild(TextNode("7")) + .appendchild(met6.dup() + .appendchild(TextNode("8"))) + .appendchild(TextNode("9"))) + self.dotest(root, False) + + def test_range_meta_hyperlink(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(1, 6, met1)) + ## overlap left + url2 = HyperlinkNode(self.mkname("url")) + # inserter.insertrange(Range(0, 4-1, url2)) + inserter.insertrange(Range(0, 4, url2)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(url2.dup().appendchild(TextNode("23"))) + .appendchild(TextNode("456"))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## overlap right + url3 = HyperlinkNode(self.mkname("url")) + # inserter.insertrange(Range(6-1, 8-1, url3)) + inserter.insertrange(Range(6, 8, url3)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(url2.dup().appendchild(TextNode("23"))) + .appendchild(TextNode("45")) + .appendchild(url3.dup().appendchild(TextNode("6")))) + root.appendchild(url3.dup().appendchild(TextNode("7"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + ## around (not quite, due to API) + url4 = HyperlinkNode(self.mkname("url")) + # inserter.insertrange(Range(1, 9-1, url4)) + inserter.insertrange(Range(1, 9, url4)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(url4.dup() + .appendchild(TextNode("23456")))) + root.appendchild(url4.dup().appendchild(TextNode("78"))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + ## inside + url5 = HyperlinkNode(self.mkname("url")) + # inserter.insertrange(Range(4-1, 6-1, url5)) + inserter.insertrange(Range(4, 6, url5)) + root = TreeNode() + root.appendchild(url2.dup().appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(url4.dup() + .appendchild(TextNode("23"))) + .appendchild(url5.dup() + .appendchild(TextNode("45"))) + .appendchild(url4.dup() + .appendchild(TextNode("6")))) + root.appendchild(url4.dup().appendchild(TextNode("78"))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + + def test_range_meta_ruby(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(1, 5, met1)) + ## overlap left + rby2 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(0, 3-1, rby2)) + inserter.insertrange(Range(0, 3, rby2)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(rby2.dup().appendchild(TextNode("2"))) + .appendchild(TextNode("345"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## overlap right + rby3 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(5-1, 7-1, rby3)) + inserter.insertrange(Range(5, 7, rby3)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(rby2.dup().appendchild(TextNode("2"))) + .appendchild(TextNode("34")) + .appendchild(rby3.dup().appendchild(TextNode("5")))) + root.appendchild(rby3.dup().appendchild(TextNode("6"))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## // around + rby4 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(1, 7-1, rby4)) + inserter.insertrange(Range(1, 7, rby4)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("1"))) + root.appendchild(rby4.dup() + .appendchild(met1.dup() + .appendchild(TextNode("2345"))) + .appendchild(TextNode("6"))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## inside + met5 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(7-1, 9-1, met5)) + inserter.insertrange(Range(7, 9, met5)) + rby6 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(9-2, 10/-2, rby6)) + inserter.insertrange(Range(9, 10, rby6)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("1"))) + root.appendchild(rby4.dup() + .appendchild(met1.dup() + .appendchild(TextNode("2345"))) + .appendchild(TextNode("6"))) + root.appendchild(met5.dup() + .appendchild(TextNode("7")) + .appendchild(rby6.dup() + .appendchild(TextNode("8")))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + ## inside, with invalid range that includes the dummy char + rby7 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(7-1, 9-2, rby7)) + inserter.insertrange(Range(7, 9, rby7)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("1"))) + root.appendchild(rby4.dup() + .appendchild(met1.dup() + .appendchild(TextNode("2345"))) + .appendchild(TextNode("6"))) + root.appendchild(met5.dup() + .appendchild(rby7.dup() + .appendchild(TextNode("7"))) + .appendchild(rby6.dup() + .appendchild(TextNode("8")))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + ## around, at same position as meta + rby8 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(7-1, 10-2, rby8)) + inserter.insertrange(Range(7, 10, rby8)) + root = TreeNode() + root.appendchild(rby2.dup().appendchild(TextNode("1"))) + root.appendchild(rby4.dup() + .appendchild(met1.dup() + .appendchild(TextNode("2345"))) + .appendchild(TextNode("6"))) + root.appendchild(rby8.dup() + .appendchild(met5.dup() + .appendchild(TextNode("78")))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + + def test_range_meta_meta(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(3, 6, met1)) + ## overlap left + met2 = MetaNode(self.mkid("id")) + try: + inserter.insertrange(Range(0, 4, met2)) + fail("testRangeMetaMeta: overlap left allowed") + except IllegalArgumentException: + pass + root = TreeNode() + root.appendchild(TextNode("123")) + root.appendchild(met1.dup().appendchild(TextNode("456"))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## overlap right + met3 = MetaNode(self.mkid("id")) + + try: + # inserter.insertrange(Range(5-1, 8-1, met3)) + inserter.insertrange(Range(5, 8, met3)) + self.fail("testRangeMetaMeta: overlap right allowed") + except IllegalArgumentException: + pass + + root = TreeNode() + root.appendchild(TextNode("123")) + root.appendchild(met1.dup().appendchild(TextNode("456"))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## around + met4 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(3, 7-1, met4)) + inserter.insertrange(Range(3, 7, met4)) + root = TreeNode() + root.appendchild(TextNode("123")) + root.appendchild(met4.dup() + .appendchild(met1.dup().appendchild(TextNode("456")))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + ## inside + met5 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(6-2, 8-2, met5)) + inserter.insertrange(Range(6, 8, met5)) + root = TreeNode() + root.appendchild(TextNode("123")) + root.appendchild(met4.dup() + .appendchild(met1.dup() + .appendchild(TextNode("4")) + .appendchild(met5.dup() + .appendchild(TextNode("56"))))) + root.appendchild(TextNode("789")) + self.dotest(root, False) + + def test_range2(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(1, 8, met1)) + met2 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(3-1, 8-1, met2)) + inserter.insertrange(Range(3, 8, met2)) + met3 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(5-2, 8-2, met3)) + inserter.insertrange(Range(5, 8, met3)) + root = TreeNode() + root.appendchild(TextNode("1")) + root.appendchild(met1.dup() + .appendchild(TextNode("2")) + .appendchild(met2.dup() + .appendchild(TextNode("3")) + .appendchild(met3.dup() + .appendchild(TextNode("456"))) + .appendchild(TextNode("7"))) + .appendchild(TextNode("8"))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + ## split ruby at every meta start! + rby4 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(0, 7-3, rby4)) + inserter.insertrange(Range(0, 7, rby4)) + root = TreeNode() + root.appendchild(rby4.dup() + .appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(rby4.dup() + .appendchild(TextNode("2"))) + .appendchild(met2.dup() + .appendchild(rby4.dup() + .appendchild(TextNode("3"))) + .appendchild(met3.dup() + .appendchild(rby4.dup() + .appendchild(TextNode("4"))) + .appendchild(TextNode("56"))) + .appendchild(TextNode("7"))) + .appendchild(TextNode("8"))) + root.appendchild(TextNode("9")) + self.dotest(root, False) + ## split ruby at every meta end! + rby5 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(8-3, 12-3, rby5)) + inserter.insertrange(Range(8, 12, rby5)) + root = TreeNode() + root.appendchild(rby4.dup() + .appendchild(TextNode("1"))) + root.appendchild(met1.dup() + .appendchild(rby4.dup() + .appendchild(TextNode("2"))) + .appendchild(met2.dup() + .appendchild(rby4.dup() + .appendchild(TextNode("3"))) + .appendchild(met3.dup() + .appendchild(rby4.dup() + .appendchild(TextNode("4"))) + .appendchild(TextNode("5")) + .appendchild(rby5.dup() + .appendchild(TextNode("6")))) + .appendchild(rby5.dup() + .appendchild(TextNode("7")))) + .appendchild(rby5.dup() + .appendchild(TextNode("8")))) + root.appendchild(rby5.dup() + .appendchild(TextNode("9"))) + self.dotest(root, False) + + def test_range3(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + rby1 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(0, 9, rby1)) + met2 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(2, 7, met2)) + root = TreeNode() + root.appendchild(rby1.dup() + .appendchild(TextNode("12")) + .appendchild(met2.dup() + .appendchild(TextNode("34567"))) + .appendchild(TextNode("89"))) + self.dotest(root, False) + ## overwrite outer ruby, split remains at inner meta! + rby3 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(5-1, 6-1, rby3)) + inserter.insertrange(Range(5, 6, rby3)) + root = TreeNode() + root.appendchild(rby1.dup() + .appendchild(TextNode("12"))) + root.appendchild(met2.dup() + .appendchild(rby1.dup() + .appendchild(TextNode("34"))) + .appendchild(rby3.dup() + .appendchild(TextNode("5"))) + .appendchild(rby1.dup() + .appendchild(TextNode("67")))) + root.appendchild(rby1.dup() + .appendchild(TextNode("89"))) + self.dotest(root, False) + + def test_range4(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + rby1 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(0, 9, rby1)) + met2 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(1, 8, met2)) + met3 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(3-1, 8-1, met3)) + inserter.insertrange(Range(3, 8, met3)) + met4 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(5-2, 8-2, met4)) + inserter.insertrange(Range(5, 8, met4)) + root = TreeNode() + root.appendchild(rby1.dup() + .appendchild(TextNode("1")) + .appendchild(met2.dup() + .appendchild(TextNode("2")) + .appendchild(met3.dup() + .appendchild(TextNode("3")) + .appendchild(met4.dup() + .appendchild(TextNode("456"))) + .appendchild(TextNode("7"))) + .appendchild(TextNode("8"))) + .appendchild(TextNode("9"))) + self.dotest(root, False) + ## overwrite outer ruby, split remains at every inner meta! + rby5 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(7-3, 8-3, rby5)) + inserter.insertrange(Range(7, 8, rby5)) + root = TreeNode() + root.appendchild(rby1.dup() + .appendchild(TextNode("1"))) + root.appendchild(met2.dup() + .appendchild(rby1.dup() + .appendchild(TextNode("2"))) + .appendchild(met3.dup() + .appendchild(rby1.dup() + .appendchild(TextNode("3"))) + .appendchild(met4.dup() + .appendchild(rby1.dup() + .appendchild(TextNode("4"))) + .appendchild(rby5.dup() + .appendchild(TextNode("5"))) + .appendchild(rby1.dup() + .appendchild(TextNode("6")))) + .appendchild(rby1.dup() + .appendchild(TextNode("7")))) + .appendchild(rby1.dup() + .appendchild(TextNode("8")))) + root.appendchild(rby1.dup() + .appendchild(TextNode("9"))) + self.dotest(root, False) + + def test_range5(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + rby1 = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(0, 9, rby1)) + met2 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(1, 3, met2)) + met3 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(5-1, 6-1, met3)) + inserter.insertrange(Range(5, 6, met3)) + met4 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(8-2, 10-2, met4)) + inserter.insertrange(Range(8, 10, met4)) + root = TreeNode() + root.appendchild(rby1.dup() + .appendchild(TextNode("1")) + .appendchild(met2.dup().appendchild(TextNode("23"))) + .appendchild(TextNode("4")) + .appendchild(met3.dup().appendchild(TextNode("5"))) + .appendchild(TextNode("6")) + .appendchild(met4.dup().appendchild(TextNode("78"))) + .appendchild(TextNode("9"))) + self.dotest(root, False) + ## overwrite outer ruby, but split at inner metas! + rby5 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(3-1, 10-3, rby5)) + inserter.insertrange(Range(3, 10, rby5)) + root = TreeNode() + root.appendchild(rby1.dup() + .appendchild(TextNode("1"))) + root.appendchild(met2.dup() + .appendchild(rby1.dup() + .appendchild(TextNode("2"))) + .appendchild(rby5.dup() + .appendchild(TextNode("3")))) + root.appendchild(rby5.dup() + .appendchild(TextNode("4")) + .appendchild(met3.dup() + .appendchild(TextNode("5"))) + .appendchild(TextNode("6"))) + root.appendchild(met4.dup() + .appendchild(rby5.dup() + .appendchild(TextNode("7"))) + .appendchild(rby1.dup() + .appendchild(TextNode("8")))) + root.appendchild(rby1.dup() + .appendchild(TextNode("9"))) + self.dotest(root, False) + + def test_range6(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(1, 5, met1)) + met2 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(3-1, 6-1, met2)) + inserter.insertrange(Range(3, 6, met2)) + met3 = MetaNode(self.mkid("id")) + # inserter.insertrange(Range(5-2, 7-2, met3)) + inserter.insertrange(Range(5, 7, met3)) + root = TreeNode() + root.appendchild(TextNode("1")) + root.appendchild(met1.dup() + .appendchild(TextNode("2")) + .appendchild(met2.dup() + .appendchild(TextNode("3")) + .appendchild(met3.dup() + .appendchild(TextNode("45"))))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## split at 3 metas, all at same position + rby4 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(7-3, 10-3, rby4)) + inserter.insertrange(Range(7, 10, rby4)) + root = TreeNode() + root.appendchild(TextNode("1")) + root.appendchild(met1.dup() + .appendchild(TextNode("2")) + .appendchild(met2.dup() + .appendchild(TextNode("3")) + .appendchild(met3.dup() + .appendchild(TextNode("4")) + .appendchild(rby4.dup() + .appendchild(TextNode("5")))))) + root.appendchild(rby4.dup() + .appendchild(TextNode("67"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + + def test_range7(self): + inserter = RangeInserter(self.__class__.xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + url1 = HyperlinkNode(self.mkname("url")) + inserter.insertrange(Range(1, 5, url1)) + met2 = MetaNode(self.mkid("id")) + inserter.insertrange(Range(3, 5, met2)) + root = TreeNode() + root.appendchild(TextNode("1")) + root.appendchild(url1.dup() + .appendchild(TextNode("23"))) + root.appendchild(met2.dup() + .appendchild(url1.dup() + .appendchild(TextNode("45")))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + ## this should result in not splitting the hyperlink, but due to API + ## we can't tell :( + rby3 = RubyNode(self.mkname("ruby")) + # inserter.insertrange(Range(5-1, 8-1, rby3)) + inserter.insertrange(Range(5, 8, rby3)) + root = TreeNode() + root.appendchild(TextNode("1")) + root.appendchild(url1.dup() + .appendchild(TextNode("23"))) + root.appendchild(met2.dup() + .appendchild(url1.dup() + .appendchild(TextNode("4"))) + .appendchild(rby3.dup() + .appendchild(url1.dup() + .appendchild(TextNode("5"))))) + root.appendchild(rby3.dup() + .appendchild(TextNode("67"))) + root.appendchild(TextNode("89")) + self.dotest(root, False) + + # TODO: test partial selection, test UNDO/REDO + + ##i109601# NestedTextContent and XChild + def test_meta_xchild(self): + xDoc = self.__class__.xDoc + id1 = StringPair("content.xml", self.mkname("id")) + id2 = StringPair("content.xml", self.mkname("id")) + id3 = StringPair("content.xml", self.mkname("id")) + id4 = StringPair("content.xml", self.mkname("id")) + id5 = StringPair("content.xml", self.mkname("id")) + id6 = StringPair("content.xml", self.mkname("id")) + meta1 = MetaNode(id1) + meta2 = MetaNode(id2) + meta3 = MetaFieldNode(id3) + meta4 = MetaNode(id4) + meta5 = MetaNode(id5) + meta6 = MetaFieldNode(id6) + root = TreeNode() + root.appendchild(meta1.dup() + .appendchild(TextNode("1"))) + root.appendchild(TextNode("2")) + root.appendchild(meta2.dup() + .appendchild(meta3.dup() + .appendchild(TextNode("34")) + .appendchild(meta4.dup() + .appendchild(TextNode("56"))) + .appendchild(meta5.dup()) + .appendchild(TextNode("7")))) + root.appendchild(TextNode("8")) + root.appendchild(meta6.dup() + .appendchild(TextNode("9"))) + + inserter = RangeInserter(xDoc) + text = TextNode("123456789") + inserter.insertrange(Range(0, 0, text)) + xMeta1 = inserter.insertrange(Range(0, 1, meta1)) + xMeta2 = inserter.insertrange(Range(3, 8, meta2)) + xMeta3 = inserter.insertrange(Range(4, 9, meta3)) + xMeta4 = inserter.insertrange(Range(7, 9, meta4)) + xMeta5 = inserter.insertrange(Range(10, 10, meta5)) + xMeta6 = inserter.insertrange(Range(13, 14, meta6)) + + self.dotest(root, False) + + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.gotoNextParagraph(False) # second paragraph + # X12XX34X56X78X9 + # 1 23 4 5 6 + # 1 452 6 + # 3 + nestedTextContent = ( + None, + id1, + id1, + None, + id2, + id3, + id3, + id3, + id4, + id4, + id4, + id5, + id3, + None, + id6, + id6) + for i, ntc in enumerate(nestedTextContent): + oNTC = xDocTextCursor.NestedTextContent + if ntc is None: + self.assertIsNone(oNTC, + "unexpected NestedTextContent at: {}".format(i)) + else: + xmlid = oNTC.MetadataReference + self.assertTrue(MetaNode.eq(ntc, xmlid), + "wrong NestedTextContent at: {}".format(i)) + xDocTextCursor.goRight(1, False) + + try: + xMeta1.setParent(xMeta4) + fail("setParent(): allowed?") + except NoSupportException: + pass + self.assertIsNone(xMeta1.getParent(), "getParent(): not None") + self.assertIsNone(xMeta2.getParent(), "getParent(): not None") + self.assertIsNone(xMeta6.getParent(), "getParent(): not None") + + xParent3 = xMeta3.getParent() + self.assertIsNotNone(xParent3, "getParent(): None") + xmlid = xParent3.MetadataReference + self.assertTrue(MetaNode.eq(xmlid, id2), "getParent(): wrong") + + xParent4 = xMeta4.getParent() + self.assertIsNotNone(xParent4, "getParent(): None") + xmlid = xParent4.MetadataReference + self. assertTrue(MetaNode.eq(xmlid, id3), "getParent(): wrong") + + xParent5 = xMeta5.getParent() + self.assertIsNotNone(xParent5, "getParent(): None") + xmlid = xParent5.MetadataReference + self.assertTrue(MetaNode.eq(xmlid, id3), "getParent(): wrong") + + # test SwXMeta XText interface + def test_meta_xtext(self): + xDoc = self.__class__.xDoc + inserter = RangeInserter(xDoc) + text = TextNode("12AB6789") + inserter.insertrange(Range(0, 0, text)) + meta = MetaNode(self.mkid("id")) + xMeta = inserter.makemeta() + + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.goRight(3, False) + xDocTextCursor.goRight(2, True) + xDocText.insertTextContent(xDocTextCursor, xMeta, True) + + xMeta.MetadataReference = meta.xmlid + xParentText = xMeta.getText() + self.assertIsNotNone(xParentText, "getText(): no parent") + + xStart = xMeta.getStart() + self.assertIsNotNone(xStart, "getStart(): no start") + + xEnd = xMeta.getEnd() + self.assertIsNotNone(xEnd, "getEnd(): no end") + + xMeta.setString("45") + + string = xMeta.getString() + self.assertEqual("45", string, "getString(): invalid string returned") + + xTextCursor = xMeta.createTextCursor() + self.assertIsNotNone(xTextCursor, "createTextCursor(): failed") + + try: + xMeta.createTextCursorByRange(None) + fail("createTextCursorByRange(): None allowed?") + except RuntimeException: + pass + + xTextCursorStart = xMeta.createTextCursorByRange(xStart) + self.assertIsNotNone(xTextCursorStart, + "createTextCursorByRange(): failed for start") + + xTextCursorEnd = xMeta.createTextCursorByRange(xEnd) + self.assertIsNotNone(xTextCursorEnd, + "createTextCursorByRange(): failed for end") + + ## move outside meta + xDocTextCursor.gotoStart(False) + + try: + xMeta.insertString(None, "foo", False) + fail("insertString(): None allowed?") + except RuntimeException: + pass + + try: + xMeta.insertString(xDocTextCursor, "foo", False) + fail("insertString(): cursor outside allowed?") + except RuntimeException: + pass + + xStart = xMeta.getStart() + xMeta.insertString(xStart, "A", False) + string = xMeta.getString() + self.assertEqual("A45", string, "getString(): invalid string returned") + + xMeta.insertString(xEnd, "B", False) + string = xMeta.getString() + self.assertEqual("A45B", string, "getString(): invalid string returned") + + try: + xMeta.insertControlCharacter(None, HARD_HYPHEN, False) + fail("insertControlCharacter(): None allowed?") + except IllegalArgumentException: + pass + + xStart = xMeta.getStart() + try: + xMeta.insertControlCharacter(xDocTextCursor, HARD_HYPHEN, False) + fail("insertControlCharacter(): cursor outside allowed?") + except IllegalArgumentException: + pass + + xMeta.insertControlCharacter(xStart, HARD_HYPHEN, False) + string = xMeta.getString() + self.assertEqual('\u2011' + 'A45B', string, + "getString(): invalid string returned") + + xMeta.insertControlCharacter(xEnd, HARD_HYPHEN, False) + string = xMeta.getString() + self.assertEqual('\u2011' + 'A45B' + '\u2011', string, + "getString(): invalid string returned") + + xMeta.setString("45") + try: + xMeta.insertTextContent(None, xMeta, False) + fail("insertTextContent(): None range allowed?") + except IllegalArgumentException: + pass + + try: + xMeta.insertTextContent(xStart, None, False) + fail("insertTextContent(): None content allowed?") + except IllegalArgumentException: + pass + + try: + xMeta.insertTextContent(xDocTextCursor, xMeta, False) + fail("insertTextContent(): cursor outside allowed?") + except IllegalArgumentException: + pass + + field1 = TextFieldNode("f1") + field2 = TextFieldNode("f2") + xField1 = inserter.maketextfield(field1.content) + xField2 = inserter.maketextfield(field2.content) + + xStart = xMeta.getStart() + xMeta.insertTextContent(xStart, xField1, False) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(meta.dup() + .appendchild(field1.dup()) + .appendchild(TextNode("45"))) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + xMeta.insertTextContent(xEnd, xField2, False) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(meta.dup() + .appendchild(field1.dup()) + .appendchild(TextNode("45")) + .appendchild(field2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + try: + xMeta.removeTextContent(None) + fail("removeTextContent(): None content allowed?") + except RuntimeException: + pass + + xMeta.removeTextContent(xField1) + + xAnchor = xMeta.getAnchor() + self.assertIsNotNone(xAnchor, "getAnchor(): None") + + ## evil test case: insert ruby around meta + ruby = RubyNode(self.mkname("ruby")) + inserter.insertrange(Range(2, 6, ruby)) + + ## prevent caching... + # root = TreeNode() + # root.appendchild(TextNode("12")) + # root.appendchild(ruby.dup() + # .appendchild(meta.dup() + # .appendchild(TextNode("45")) + # .appendchild(field2.dup()))) + # root.appendchild(TextNode("6789")) + # self.dotest(root, False) + + xEnum = xMeta.createEnumeration() + self.assertIsNotNone("createEnumeration(): returns None", xEnum) + + self.assertTrue(xEnum.hasMoreElements(),"hasNext(): first missing") + xPortion = xEnum.nextElement() + type_ = xPortion.TextPortionType + self.assertEqual("Text", type_, "first: not text") + txt = xPortion.getString() + self.assertEqual("45", txt, "first: text differs") + + self.assertTrue(xEnum.hasMoreElements(),"hasNext(): second missing") + xPortion = xEnum.nextElement() + type_ = xPortion.TextPortionType + self.assertEqual("TextField", type_, "second: not text") + + ## no ruby end here!!! + self.assertFalse(xEnum.hasMoreElements(), "hasNext(): more elements?") + + xMeta.dispose() + + try: + xCursor = xMeta.createTextCursor() + self.assertIsNone(xCursor, + "createTextCursor(): succeeds on disposed object?") + except RuntimeException: + pass + + # check that cursor move methods move to positions in the meta, + # but do not move to positions outside the meta. + def test_meta_xtextcursor(self): + xDoc = self.__class__.xDoc + inserter = RangeInserter(xDoc) + text = TextNode("Text. 12 More text here.") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + xMeta = inserter.makemeta() + + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.goRight(7, False) + xDocTextCursor.goRight(2, True) + xDocText.insertTextContent(xDocTextCursor, xMeta, True) + xDocTextCursor.gotoStart(True) + + xMeta.MetadataReference = met1.xmlid + xStart = xMeta.getStart() + self.assertIsNotNone(xStart, "getStart(): no start") + xEnd = xMeta.getEnd() + self.assertIsNotNone(xEnd, "getEnd(): no end") + + ## XTextCursor + xMetaCursor = xMeta.createTextCursor() + self.assertIsNotNone(xMetaCursor, "createTextCursor(): no cursor") + bSuccess = False + xMetaCursor.gotoStart(False) + xMetaCursor.gotoEnd(False) + bSuccess = xMetaCursor.goLeft(1, False) + self.assertTrue(bSuccess, "goLeft(): failed") + bSuccess = xMetaCursor.goLeft(1000, False) + self.assertFalse(bSuccess, "goLeft(): succeeded") + bSuccess = xMetaCursor.goRight(1, False) + self.assertTrue(bSuccess, "goRight(): failed") + bSuccess = xMetaCursor.goRight(1000, False) + self.assertFalse(bSuccess, "goRight(): succeeded") + xMetaCursor.gotoRange(xStart, False) + xMetaCursor.gotoRange(xEnd, False) + try: + xMetaCursor.gotoRange(xDocTextCursor, False) + fail("gotoRange(): succeeded") + except RuntimeException: + pass + + ## XWordCursor + xMeta.setString("Two words") + xMetaCursor.gotoStart(False) + + bSuccess = xMetaCursor.gotoNextWord(True) # at start of "words" + self.assertTrue(bSuccess, "gotoNextWord(): failed") + + string = xMetaCursor.getString() + self.assertEqual("Two ", string, "gotoNextWord(): wrong string") + + bSuccess = xMetaCursor.gotoNextWord(False) # at end of "words", cannot leave metafield + self.assertFalse(bSuccess,"gotoNextWord(): succeeded") + xMetaCursor.collapseToEnd() + bSuccess = xMetaCursor.gotoPreviousWord(True) # at start of "words" + self.assertTrue(bSuccess, "gotoPreviousWord(): failed") + + string = xMetaCursor.getString() + self.assertEqual("words", string, "gotoPreviousWord(): wrong string") + + bSuccess = xMetaCursor.gotoPreviousWord(False) # at start of "Two" + self.assertTrue(bSuccess, "gotoPreviousWord(): failed") + + bSuccess = xMetaCursor.gotoPreviousWord(False) # cannot leave metafield + self.assertFalse(bSuccess, "gotoPreviousWord(): succeeded") + + bSuccess = xMetaCursor.gotoEndOfWord(True) # at end of "Two" + self.assertTrue(bSuccess, "gotoEndOfWord(): failed") + + string = xMetaCursor.getString() + self.assertEqual("Two", string, "gotoEndOfWord(): wrong string") + + xMetaCursor.gotoEnd(False) + bSuccess = xMetaCursor.gotoStartOfWord(True) + self.assertTrue(bSuccess, "gotoStartOfWord(): failed") + + string = xMetaCursor.getString() + self.assertEqual("words", string, "gotoStartOfWord(): wrong string") + + xMeta.setString("") + bSuccess = xMetaCursor.gotoEndOfWord(False) + self.assertFalse(bSuccess, "gotoEndOfWord(): succeeded") + bSuccess = xMetaCursor.gotoStartOfWord(False) + self.assertFalse(bSuccess, "gotoStartOfWord(): succeeded") + + ## XSentenceCursor + xMeta.setString("This is a sentence. Another sentence.") + xMetaCursor.gotoStart(False) + + bSuccess = xMetaCursor.gotoNextSentence(True) + self.assertTrue(bSuccess,"gotoNextSentence(): failed") + + string = xMetaCursor.getString() + self.assertEqual("This is a sentence. ", string, + "gotoNextSentence(): wrong string") + + bSuccess = xMetaCursor.gotoNextSentence(False) + self.assertFalse(bSuccess, "gotoNextSentence(): succeeded") + ## FIXME: + ## the sentence cursor seems to work differently than the word cursor + xMeta.setString("This is a sentence. Another sentence. Sentence 3.") + xMetaCursor.gotoEnd(False) + bSuccess = xMetaCursor.gotoPreviousSentence(True) + self.assertTrue(bSuccess, "gotoPreviousSentence(): failed") + + string = xMetaCursor.getString() + self.assertEqual("Another sentence. Sentence 3.", string, + "gotoPreviousSentence(): wrong string") + + bSuccess = xMetaCursor.gotoPreviousSentence(False) + self.assertFalse(bSuccess, "gotoPreviousSentence(): succeeded") + bSuccess = xMetaCursor.gotoEndOfSentence(True) + self.assertTrue(bSuccess, "gotoEndOfSentence(): failed") + + string = xMetaCursor.getString() + self.assertEqual("This is a sentence.", string, + "gotoEndOfSentence(): wrong string") + + xMetaCursor.gotoEnd(False) + bSuccess = xMetaCursor.gotoStartOfSentence(True) + self.assertTrue(bSuccess,"gotoStartOfSentence(): failed") + + string = xMetaCursor.getString() + self.assertEqual("Sentence 3.", string, + "gotoStartOfSentence(): wrong string") + + xMeta.setString("") + bSuccess = xMetaCursor.gotoEndOfSentence(False) + self.assertFalse(bSuccess, "gotoEndOfSentence(): succeeded") + bSuccess = xMetaCursor.gotoStartOfSentence(False) + self.assertFalse(bSuccess, "gotoStartOfSentence(): succeeded") + + ## XParagraphCursor (does not make sense) + bSuccess = xMetaCursor.gotoNextParagraph(False) + self.assertFalse(bSuccess, "gotoNextParagraph(): succeeded") + bSuccess = xMetaCursor.gotoPreviousParagraph(False) + self.assertFalse(bSuccess, "gotoPreviousParagraph(): succeeded") + bSuccess = xMetaCursor.gotoStartOfParagraph(False) + self.assertFalse(bSuccess, "gotoStartOfParagraph(): succeeded") + bSuccess = xMetaCursor.gotoEndOfParagraph(False) + self.assertFalse(bSuccess, "gotoEndOfParagraph(): succeeded") + + # See https://bugs.libreoffice.org/show_bug.cgi?id=49629 + # ensure that gotoEndOfWord does not fail when footnote is at word end + def test_xtextcursor(self): + xDoc = self.__class__.xDoc + inserter = RangeInserter(xDoc) + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.gotoNextParagraph(False) + inserter.inserttext(xDocTextCursor, "Text") + xDocTextCursor.gotoEndOfWord(False) + inserter.insertfootnote(xDocTextCursor, "footnote") + xDocTextCursor.gotoStartOfParagraph(False) + bSuccess = xDocTextCursor.gotoEndOfWord(True) + self.assertTrue(bSuccess, "gotoEndOfWord(): failed") + string = xDocTextCursor.getString() + self.assertEqual("Text", string, "gotoEndOfWord(): wrong string") + self.assertNotEqual("a","b") + + class AttachHelper(): + def isattribute(self): pass + def mktreenode(self): pass + def mktextcontent(self, inserter, node): pass + def postinserted(self, node, xContent): pass + + def test_meta_xtextattach_toxmark(self): + class Helper(self.AttachHelper): + def isattribute(_): + return True + def mktreenode(_): + return DocumentIndexMarkNode(self.mkname("toxmark")) + def mktextcontent(_, inserter, node): + return inserter.makedocumentindexmark(node.name) + self.do_meta_xtextattach(Helper()) + + def test_meta_xtextattach_refmark(self): + class Helper(self.AttachHelper): + def isattribute(_): + return True + def mktreenode(_): + return ReferenceMarkNode(self.mkname("refmark")) + def mktextcontent(_, inserter, node): + return inserter.makereferencemark(node.name) + self.do_meta_xtextattach(Helper()) + + def test_meta_xtextattach_textfield(self): + class Helper(self.AttachHelper): + def isattribute(_): + return False + def mktreenode(_): + return TextFieldNode(self.mkname("field")) + def mktextcontent(_, inserter, node): + return inserter.maketextfield(node.content) + self.do_meta_xtextattach(Helper()) + + def test_meta_xtextattach_footnote(self): + class Helper(self.AttachHelper): + def isattribute(_): + return False + def mktreenode(_): + return FootnoteNode(self.mkname("ftn")) + def mktextcontent(_, inserter, node): + return inserter.makefootnote(node.label) + self.do_meta_xtextattach(Helper()) + + def test_meta_xtextattach_meta(self): + class Helper(self.AttachHelper): + def isattribute(_): + return True + def mktreenode(_): + return MetaNode(self.mkid("id")) + def mktextcontent(_, inserter, node): + return inserter.makemeta() + def postinserted(_, node, xContent): + xContent.MetadataReference = node.xmlid + self.do_meta_xtextattach(Helper()) + + def do_meta_xtextattach(self, helper): + xDoc = self.__class__.xDoc + inserter = RangeInserter(xDoc) + text = TextNode("12AB6789") + inserter.insertrange(Range(0, 0, text)) + met1 = MetaNode(self.mkid("id")) + xMeta = inserter.makemeta() + + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.goRight(3, False) + xDocTextCursor.goRight(2, True) + xDocText.insertTextContent(xDocTextCursor, xMeta, True) + + xMeta.MetadataReference = met1.xmlid + xStart = None + xEnd = None + + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + ## insertTextContent with meta getStart()/getEnd() + xMeta.insertTextContent(xStart, xContent1, False) + xMeta.insertTextContent(xEnd, xContent2, False) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + xMeta.setString("AB") + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + xTextCursor = xMeta.createTextCursor() + xTextCursor.gotoStart(False) + + ## insertTextContent with meta cursor + xMeta.insertTextContent(xTextCursor, xContent1, False) + xTextCursor.gotoEnd(False) + xMeta.insertTextContent(xTextCursor, xContent2, False) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + if not helper.isattribute(): + # xMeta.setString("AB") + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + xTextCursor = xMeta.createTextCursor() + xTextCursor.gotoStart(False) + xTextCursor.goRight(1, True) + + ## insertTextContent with meta cursor and absorb + xMeta.insertTextContent(xTextCursor, xContent1, True) + xTextCursor.gotoEnd(False) + xTextCursor.goLeft(1, True) + xMeta.insertTextContent(xTextCursor, xContent2, True) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + xMeta.setString("AB") + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + xDocTextCursor.gotoRange(xStart, False) + + ## insertTextContent with document cursor + xMeta.insertTextContent(xDocTextCursor, xContent1, False) + xDocTextCursor.gotoRange(xEnd, False) + xMeta.insertTextContent(xDocTextCursor, xContent2, False) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + if not helper.isattribute(): + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + xDocTextCursor.gotoRange(xStart, False) + xDocTextCursor.goRight(1, True) + + ## insertTextContent with document cursor and absorb + xMeta.insertTextContent(xDocTextCursor, xContent1, True) + xDocTextCursor.gotoRange(xEnd, False) + xDocTextCursor.goLeft(1, True) + xMeta.insertTextContent(xDocTextCursor, xContent2, True) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + xMeta.setString("AB") + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + ## attach to range from meta getStart()/getEnd() + xContent1.attach(xStart) + xContent2.attach(xEnd) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + xMeta.setString("AB") + xStart = xMeta.getStart() + xEnd = xMeta.getEnd() + + nod1 = helper.mktreenode() + nod2 = helper.mktreenode() + xContent1 = helper.mktextcontent(inserter, nod1) + xContent2 = helper.mktextcontent(inserter, nod2) + + xTextCursor = xMeta.createTextCursor() + xTextCursor.gotoStart(False) + + ## attach to cursor from meta XText + xContent1.attach(xTextCursor) + xTextCursor.gotoEnd(False) + xContent2.attach(xTextCursor) + + helper.postinserted(nod1, xContent1) + helper.postinserted(nod2, xContent2) + + root = TreeNode() + root.appendchild(TextNode("12")) + root.appendchild(met1.dup() + .appendchild(nod1.dup()) + .appendchild(TextNode("AB")) + .appendchild(nod2.dup())) + root.appendchild(TextNode("6789")) + self.dotest(root, False) + + def test_metafield_xtextfield(self): + xDoc = self.__class__.xDoc + smgr = self.__class__._uno.xContext.ServiceManager + xRepo = xDoc.getRDFRepository() + ## for testing just add it to the first graph + Graphs = xRepo.getGraphNames() + xGraph = xRepo.getGraph(Graphs[0]) + xOdfPrefix = smgr.createInstance("com.sun.star.rdf.URI") + xOdfPrefix.initialize((ODF_PREFIX,)) + xOdfSuffix = smgr.createInstance("com.sun.star.rdf.URI") + xOdfSuffix.initialize((ODF_SUFFIX,)) + + xPrefix = smgr.createInstance("com.sun.star.rdf.Literal") + xPrefix.initialize(("foo",)) + xSuffix = smgr.createInstance("com.sun.star.rdf.Literal") + xSuffix.initialize(("bar",)) + + inserter = RangeInserter(xDoc) + text = TextNode("abc") + inserter.insertrange(Range(0, 0, text)) + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.goRight(1, False) + xDocTextCursor.goRight(3, True) + + xMetaField = inserter.makemetafield() + + xDocText.insertTextContent(xDocTextCursor, xMetaField, True) + + xMetaField.ensureMetadataReference + + xGraph.addStatement(xMetaField, xOdfPrefix, xPrefix) + xGraph.addStatement(xMetaField, xOdfSuffix, xSuffix) + self.assertEqual("fooabcbar", xMetaField.getPresentation(False), + "getPresentation(): wrong") + inserter.insertrange(Range(0, 0, text)) + + def test_metafield_xpropertyset(self): + xDoc = self.__class__.xDoc + inserter = RangeInserter(xDoc) + text = TextNode("123") + inserter.insertrange(Range(0, 0, text)) + xDocText = xDoc.getText() + xDocTextCursor = xDocText.createTextCursor() + xDocTextCursor.goRight(1, False) + xDocTextCursor.goRight(3, True) + + xMetaField = inserter.makemetafield() + + xDocText.insertTextContent(xDocTextCursor, xMetaField, True) + + self.assertIsNotNone(xMetaField, "PropertySet: not supported?") + xPropertySetInfo = xMetaField.getPropertySetInfo() + self.assertTrue(xPropertySetInfo.hasPropertyByName("NumberFormat"), + 'hasPropertyByName("NumberFormat"):') + self.assertTrue(xPropertySetInfo.hasPropertyByName("IsFixedLanguage"), + 'hasPropertyByName("IsFixedLanguage"):') + + def_ = xMetaField.NumberFormat + print("NumberFormat: default is {}".format(def_)) + xMetaField.NumberFormat = NUMBER_INT + xMetaField.IsFixedLanguage = True + format = xMetaField.NumberFormat + self.assertEqual(NUMBER_INT, format, "NumberFormat: failed") + isfixed = xMetaField.IsFixedLanguage + self.assertTrue(isfixed, "IsFixedLanguage: failed") + + def dostore(self, xComp, file): + print("Storing test document...") + file = uno.systemPathToFileUrl(file) + xComp.storeToURL(file, ()) + print("...done") + + def doload(self, file): + xComp = None + print("Loading test document...") + xComp = self.__class__._uno.openDocFromAbsolutePath(file) + self.assertIsNotNone(xComp, "cannot load: {}".format(file)) + print("...done") + return xComp + + def close(self, i_comp): + try: + if i_comp: + i_comp.close(True) + except Exception as e: + pass + + def test_load_store(self): + xComp = None + filename = "TESTMETA.odt" + try: + xComp = self.__class__._uno.openDocFromTDOC(filename) + if xComp: + self.checkloadmeta(xComp) + with TemporaryDirectory() as tempdir: + file = os.path.join(tempdir, filename) + self.dostore(xComp, file) + self.close(xComp) + xComp2 = None + try: + xComp2 = self.doload(file) + self.checkloadmeta(xComp2) + finally: + self.close(xComp2) + finally: + self.close(xComp) + + def checkloadmeta(self, xTextDoc): + xText = xTextDoc.getText() + print("Checking meta(-field)s in loaded test document...") + root = TreeNode() + root.appendchild(RubyNode("ruby1") + .appendchild(TextNode("1"))) + root.appendchild(MetaNode(self.mkid_("id1")) + .appendchild(TextNode("2"))) + root.appendchild(MetaFieldNode(self.mkid_("id2")) + .appendchild(TextNode("3"))) + root.appendchild(RubyNode("ruby2") + .appendchild(MetaNode(self.mkid_("id3")) + .appendchild(TextNode("4")))) + root.appendchild(RubyNode("ruby3") + .appendchild(MetaFieldNode(self.mkid_("id4")) + .appendchild(TextNode("5")))) + root.appendchild(MetaNode(self.mkid_("id5")) + .appendchild(RubyNode("ruby4") + .appendchild(TextNode("6")))) + root.appendchild(MetaFieldNode(self.mkid_("id6")) + .appendchild(RubyNode("ruby5") + .appendchild(TextNode("7")))) + root.appendchild(MetaNode(self.mkid_("id7")) + .appendchild(MetaNode(self.mkid_("id8")) + .appendchild(TextNode("8")))) + root.appendchild(MetaNode(self.mkid_("id9")) + .appendchild(MetaFieldNode(self.mkid_("id10")) + .appendchild(TextNode("9")))) + root.appendchild(MetaFieldNode(self.mkid_("id11")) + .appendchild(MetaNode(self.mkid_("id12")) + .appendchild(TextNode("10")))) + root.appendchild(MetaFieldNode(self.mkid_("id13")) + .appendchild(MetaFieldNode(self.mkid_("id14")) + .appendchild(TextNode("11")))) + root.appendchild(MetaNode(self.mkid_("id15")) + .appendchild(RubyNode("ruby6") + .appendchild(MetaFieldNode(self.mkid_("id16")) + .appendchild(TextNode("12"))))) + + class MetaNode_(MetaNode): + def __init__(self, id): + super().__init__(id) + def __eq__(self, other): + return isinstance(other, MetaNode) + root.appendchild(MetaNode_(self.mkid_("")) + .appendchild(TextNode("13"))) + root.appendchild(TextNode(" X X ")) + self._dotest(xTextDoc, root, False) + print("...done") + + def test_load_store_xmlid(self): + xComp = None + filename = "TESTXMLID.odt" + try: + xComp = self.__class__._uno.openDocFromTDOC(filename) + if xComp: + self.checkloadxmlid(xComp) + with TemporaryDirectory() as tempdir: + file = os.path.join(tempdir, filename) + self.dostore(xComp, file) + self.close(xComp) + xComp2 = None + try: + xComp2 = self.doload(file) + self.checkloadxmlid(xComp2) + finally: + self.close(xComp2) + finally: + self.close(xComp) + + def checkloadxmlid(self, xTextDoc): + xText = xTextDoc.getText() + xRepo = xTextDoc.getRDFRepository() + + print("Checking bookmarks in loaded test document...") + xBookmarks = xTextDoc.getBookmarks() + xMark1 = xBookmarks["mk1"] + self.assertTrue(self.eq(xMark1.MetadataReference, + StringPair("content.xml", "id90")), "mark1") + xMark2 = xBookmarks["mk2"] + result = xRepo.getStatementRDFa(xMark2) + self.assertTrue(len(result.First) == 1 and + result.First[0].Subject.StringValue == "uri:foo" and + result.First[0].Predicate.StringValue == "uri:bar" and + result.First[0].Object.Value == "a fooish bar", + "mark2") + xMark3 = xBookmarks["mk3"] + self.assertTrue(self.eq(xMark3.MetadataReference, + StringPair("content.xml", "id91")), "mark3") + print("...done") + + print("Checking sections in loaded test document...") + xSections = xTextDoc.getTextSections() + xSection1 = xSections["Section 1"] + self.assertTrue(self.eq(xSection1.MetadataReference, + StringPair("content.xml", "idSection1")), "idsection1") + xSection2 = xSections["Section 2"] + self.assertTrue(self.eq(xSection2.MetadataReference, + StringPair("content.xml", "idSection2")),"idSection2") + xSection3 = xSections["Table of Contents1_Head"] + self.assertTrue(self.eq(xSection3.MetadataReference, + StringPair("content.xml", "idTOCTitle")), "idTOCTitle") + xSection4 = xSections["Alphabetical Index1_Head"] + self.assertTrue(self.eq(xSection4.MetadataReference, + StringPair("content.xml", "idAITitle")), "idAITitle") + xSection5 = xSections["Illustration Index1_Head"] + self.assertTrue(self.eq(xSection5.MetadataReference, + StringPair("content.xml", "idIITitle")), "idIITitle") + xSection6 = xSections["Index of Tables1_Head"] + self.assertTrue(self.eq(xSection6.MetadataReference, + StringPair("content.xml", "idIOTTitle")), "idIOTTitle") + xSection7 = xSections["User-Defined1_Head"] + self.assertTrue(self.eq(xSection7.MetadataReference, + StringPair("content.xml", "idUDTitle")), "idUDTitle") + xSection8 = xSections["Table of Objects1_Head"] + self.assertTrue(self.eq(xSection8.MetadataReference, + StringPair("content.xml", "idTOOTitle")), "idTOOTitle") + xSection9 = xSections["Bibliography1_Head"] + self.assertTrue(self.eq(xSection9.MetadataReference, + StringPair("content.xml", "idBibTitle")), "idBibTitle") + print("...done") + + print("Checking indexes in loaded test document...") + xIndexes = xTextDoc.getDocumentIndexes() + xIndex1 = xIndexes["Table of Contents1"] + self.assertTrue(self.eq(xIndex1.MetadataReference, + StringPair("content.xml", "idTOC")), "idTOC") + xIndex1s = xSections["Table of Contents1"] + self.assertTrue(self.eq(xIndex1s.MetadataReference, + StringPair("content.xml", "idTOC")), "idTOC") + xIndex2 = xIndexes["Alphabetical Index1"] + self.assertTrue(self.eq(xIndex2.MetadataReference, + StringPair("content.xml", "idAI")), "idAI") + xIndex2s = xSections["Alphabetical Index1"] + self.assertTrue(self.eq(xIndex2s.MetadataReference, + StringPair("content.xml", "idAI")), "idAI") + xIndex3 = xIndexes["Illustration Index1"] + self.assertTrue(self.eq(xIndex3.MetadataReference, + StringPair("content.xml", "idII")), "idII") + xIndex3s = xSections["Table of Figures1"] + self.assertTrue(self.eq(xIndex3s.MetadataReference, + StringPair("content.xml", "idII")), "idII") + xIndex4 = xIndexes["Index of Tables1"] + self.assertTrue(self.eq(xIndex4.MetadataReference, + StringPair("content.xml", "idIOT")), "idIOT") + xIndex4s = xSections["Index of Tables1"] + self.assertTrue(self.eq(xIndex4s.MetadataReference, + StringPair("content.xml", "idIOT")), "idIOT") + xIndex5 = xIndexes["User-Defined1"] + self.assertTrue(self.eq(xIndex5.MetadataReference, + StringPair("content.xml", "idUD")), "idUD") + xIndex5s = xSections["User-Defined1"] + self.assertTrue(self.eq(xIndex5s.MetadataReference, + StringPair("content.xml", "idUD")), "idUD") + xIndex6 = xIndexes["Table of Objects1"] + self.assertTrue(self.eq(xIndex6.MetadataReference, + StringPair("content.xml", "idTOO")), "idTOO") + xIndex6s = xSections["Table of Objects1"] + self.assertTrue(self.eq(xIndex6s.MetadataReference, + StringPair("content.xml", "idTOO")), "idTOO") + xIndex7 = xIndexes["Bibliography1"] + self.assertTrue(self.eq(xIndex7.MetadataReference, + StringPair("content.xml", "idBib")), "idBib") + xIndex7s = xSections["Bibliography1"] + self.assertTrue(self.eq(xIndex7s.MetadataReference, + StringPair("content.xml", "idBib")), "idBib") + print("...done") + + def dotest(self, intree, insert=True): + xDoc = self.__class__.xDoc + self._dotest(xDoc, intree, insert) + + def _dotest(self, xDoc, intree, insert): + self._dumptree(intree, "I: ") + if insert: + TreeInserter(xDoc).inserttree(intree) + xText = xDoc.getText() + xTextEnum = xText.createEnumeration() + ## skip to right paragraph + xTextEnum.nextElement(); # skip first -- always empty! + xElement = xTextEnum.nextElement() # second contains test case + xEnum = xElement.createEnumeration() + outtree = EnumConverter().convert(xEnum) + self._dumptree(outtree, "O: ") + FuzzyTester().dotest(intree, outtree) + + def _dumptree(self, tree, prefix): + print('{}{}'.format(prefix, str(tree))) + children = tree.createenumeration() + for node in children: + self._dumptree(node, "{} ".format(prefix)) + + def mkname(self, prefix): + self.__class__.count += 1 + return "{}{}".format(prefix, self.__class__.count) + + def mkid(self, prefix): + id = self.mkname(prefix) + return StringPair("content.xml", self.mkname(prefix)) + + def mkid_(self, id): + return StringPair("content.xml", id) + + def eq(self, left, right): + return (left.First == right.First and + left.Second == right.Second) + + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/var_fields.py b/sw/qa/python/var_fields.py new file mode 100644 index 000000000..0c88b5357 --- /dev/null +++ b/sw/qa/python/var_fields.py @@ -0,0 +1,138 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import unittest +import os + +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK + +class TestVarFields(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_var_fields(self): + """ + Reproduce fdo#55814. + + Note: this test was migrated from java (the steps numbering too) + sw/qa/complex/writer/VarFields.java + + """ + + xDoc = self.__class__._uno.openEmptyWriterDoc() + xBodyText = xDoc.getText() + xCursor = xBodyText.createTextCursor() + + # 0. create text field + xField = xDoc.createInstance("com.sun.star.text.textfield.SetExpression") + + # 1. fill it with properties + self.__class__._uno.setProperties(xField, + {"Content": "0", + "IsVisible": True, + "Hint": "trying to reproduce fdo#55814", + "SubType": 0, # VAR + "Value": 0.0}) + + # 2. create master field + xMaster = xDoc.createInstance("com.sun.star.text.fieldmaster.SetExpression") + + # 3. set name of the master field to "foo" + xMaster.setPropertyValue("Name", "foo") + + # 4. get Dependent Field + # no op in python ;-) + + # 5. connect real field to the master + xField.attachTextFieldMaster(xMaster) + + # 6. insert text field into the document + xBodyText.insertTextContent(xCursor, xField, False) + + # 7. retrieve paragraph cursor + xParagraphCursor = xCursor + xParagraphCursor.gotoEndOfParagraph(False) # not selected + + # 8. enter new line + xBodyText.insertControlCharacter(xCursor, PARAGRAPH_BREAK, False) + + # 9. create new text section + xTextSection = xDoc.createInstance("com.sun.star.text.TextSection") + + # 10. fill the properties of section + self.__class__._uno.checkProperties(xTextSection, + {"Condition": "foo EQ 1", + "IsVisible": False}, + self) + + # 11. Insert some text to be content on the section + xBodyText.insertString(xCursor, + "The quick brown fox jumps over the lazy dog", + True) + + # 12. insert section + xBodyText.insertTextContent(xCursor, xTextSection, True) + + # 12.1 insert new paragraph. Note: that's here the difference + xParagraphCursor.gotoEndOfParagraph(False) # not select + + # TODO: how to leave the section now? + xBodyText.insertControlCharacter(xCursor, PARAGRAPH_BREAK, False) + xBodyText.insertString(xCursor, "new paragraph", False) + + # 13. Access fields to refresh the document + xTextFields = xDoc.getTextFields() + + # 14. refresh document to update the fields + xTextFields.refresh() + + # 15. retrieve the field + xFieldEnum = xTextFields.createEnumeration() + + # Note: we have only one field here, that why nextElement() is just fine here + xField = xFieldEnum.nextElement() + + # check + read_content = xField.getPropertyValue("Content") + self.assertEqual("0", read_content) + read_content = xField.getPropertyValue("Value") + self.assertEqual(0.0, read_content) + + # 16. change the value of the field from 0 to 1 and check + self.__class__._uno.checkProperties(xField, {"Value": 1.0, + "Content": "1"}, + self) + + # 17. refresh document to update the fields again + xTextFields.refresh() + + # 18. store document + url = os.path.join(os.environ["TestUserDir"], "VarFields.odt") + xDoc.storeToURL(url, tuple(list(range(0)))) + + # 19. retrieve the section + xSection = xDoc.getTextSections()[0] + + # 20. retrieve the condition property of that section + read_content = xSection.getPropertyValue("Condition") + + # 21. check + self.assertEqual("foo EQ 1", read_content) + +if __name__ == '__main__': + unittest.main() diff --git a/sw/qa/python/xcontrolshape.py b/sw/qa/python/xcontrolshape.py new file mode 100644 index 000000000..899c0c20b --- /dev/null +++ b/sw/qa/python/xcontrolshape.py @@ -0,0 +1,78 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.beans import UnknownPropertyException + + +class TestXControlShape(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + if cls._uno: + cls._uno.tearDown() + + def test_getAndSetControlShape(self): + xDoc = self.__class__._uno.openDocFromTDOC("xcontrolshape.odt") + self.assertIsNotNone(xDoc) + + xDrawPage = xDoc.getDrawPage() + self.assertIsNotNone(xDrawPage) + + # Date picker has control + xShapeDatePicker = xDrawPage[0] + self.assertIsNotNone(xShapeDatePicker) + self.assertIsNotNone(xShapeDatePicker.getControl()) + + # Combobox also has control + xShapeCombo = xDrawPage[1] + self.assertIsNotNone(xShapeCombo) + self.assertIsNotNone(xShapeCombo.getControl()) + + # Simple draw shape has no ControlShape + xShapeSimple = xDrawPage[2] + self.assertIsNotNone(xShapeSimple) + # Shape has no XControlShape interface and we get AttributeError exception + # during getControl() call + with self.assertRaises(AttributeError): + self.assertIsNone(xShapeSimple.getControl()) + + xOldControlShape = xShapeCombo.getControl() + + # Combo box was a combo box and had no "Date" attribute" + with self.assertRaises(UnknownPropertyException): + xShapeCombo.getControl().getPropertyValue("Date") + + # We are setting new Control Shape + xShapeCombo.setControl(xShapeDatePicker.getControl()) + + # And we can get date with some value + xDate = xShapeCombo.getControl().getPropertyValue("Date") + self.assertIsNotNone(xDate) + self.assertTrue(xDate.Day > 0 and xDate.Month > 0 and xDate.Year > 0) + + # Return back original controlshape + xShapeCombo.setControl(xOldControlShape) + # ...and ensure that date no longer available + with self.assertRaises(UnknownPropertyException): + xShapeCombo.getControl().getPropertyValue("Date") + + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xscriptprovider.py b/sw/qa/python/xscriptprovider.py new file mode 100644 index 000000000..b63812a59 --- /dev/null +++ b/sw/qa/python/xscriptprovider.py @@ -0,0 +1,80 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import unittest +import uno + +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.script.provider import ScriptFrameworkErrorException + +class TestXScriptProvider(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def setUp(self): + xMasterScriptProviderFactory = create_master_script_provider_factory() + self.xScriptProvider = xMasterScriptProviderFactory.createScriptProvider("") + + def tearDown(self): + del self.xScriptProvider + + def test_get_script_application(self): + #getScript for built-in StarBasic function + xScript = self.xScriptProvider.getScript( + "vnd.sun.star.script:Tools.Misc.CreateNewDocument?language=Basic&" + "location=application") + + self.assertIsNotNone(xScript, "xScript was not loaded") + + def test_get_script_document(self): + #getScript for StarBasic function in loaded document + x_doc = self.__class__._uno.openTemplateFromTDOC("xscriptprovider.odt") + + xMasterScriptProviderFactory = create_master_script_provider_factory() + xScriptProvider = xMasterScriptProviderFactory.createScriptProvider(x_doc) + + xScript = xScriptProvider.getScript( + "vnd.sun.star.script:Standard.Module1.Main?language=Basic&" + "location=document") + + self.assertIsNotNone(xScript, "xScript was not loaded") + + x_doc.close(True) + + def test_get_script_invalid_uri(self): + # getScript fails with invalid URI + with self.assertRaises(ScriptFrameworkErrorException): + self.xScriptProvider.getScript("invalid URI, isn't it?") + + def test_get_script_not_found(self): + # getScript fails when script not found + with self.assertRaises(ScriptFrameworkErrorException): + self.xScriptProvider.getScript( + "vnd.sun.star.script:NotExisting.NotExisting.NotExisting?" + "language=Basic&location=document") + +def create_master_script_provider_factory(): + xServiceManager = uno.getComponentContext().ServiceManager + + return xServiceManager.createInstanceWithContext( + "com.sun.star.script.provider.MasterScriptProviderFactory", + uno.getComponentContext()) + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xstyleloader.py b/sw/qa/python/xstyleloader.py new file mode 100644 index 000000000..92b901b9d --- /dev/null +++ b/sw/qa/python/xstyleloader.py @@ -0,0 +1,53 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import pathlib +import unittest + +from org.libreoffice.unotest import UnoInProcess, makeCopyFromTDOC +from com.sun.star.beans import PropertyValue + + +class TestXStyleLoader(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_loadStyleFromStream(self): + xDoc = self.__class__._uno.openEmptyWriterDoc() + self.assertIsNotNone(xDoc) + + xServiceManager = self.__class__._uno.xContext.ServiceManager + simpleFileAccess = xServiceManager.createInstance( + "com.sun.star.ucb.SimpleFileAccess") + xInputStream = simpleFileAccess.openFileRead( + pathlib.Path(makeCopyFromTDOC("xstyleloader.odt")).as_uri()) + + p1 = PropertyValue(Name="InputStream", Value=xInputStream) + p2 = PropertyValue(Name="LoadTextStyles", Value=True) + + styles = xDoc.getStyleFamilies() + styles.loadStylesFromURL("private:stream", [p1, p2]) + textStyles = styles.getByName("ParagraphStyles") + self.assertTrue(textStyles.hasByName("Test_Template")) + + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xtext.py b/sw/qa/python/xtext.py new file mode 100644 index 000000000..db1f83803 --- /dev/null +++ b/sw/qa/python/xtext.py @@ -0,0 +1,92 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import unittest + +from org.libreoffice.unotest import UnoInProcess + +class TestXText(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._uno.openEmptyWriterDoc() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_insert_and_remove_annotations(self): + x_text = self._uno.getDoc().getText() + self.assertIsNotNone(x_text) + + # Insert annotation field + x_annotation = self.create_annotation("John Doe") + x_cursor = x_text.createTextCursor() + x_text.insertTextContent(x_cursor, x_annotation, False) + + # And the same once again, actually not inserted + x_text.insertTextContent(x_cursor, x_annotation, False) + + # no exception if we try to replace object by itself: + # this did throw in the past, but only because the inserted + # UNO annotation had a core object assigned, but no document + # which insertTextContent then didn't like on another call. + x_text.insertTextContent(x_cursor, x_annotation, True) + + # We expect just one annotation actually + self.check_annotations(["John Doe"]) + + x_annotation_2 = self.create_annotation("Richard Roe") + x_text.insertTextContent(x_cursor, x_annotation_2, True) + self.check_annotations(["Richard Roe"]) + + x_annotation_3 = self.create_annotation("Jane Roe") + x_text.insertTextContent(x_cursor, x_annotation_3, True) + self.check_annotations(["Jane Roe", "Richard Roe"]) + + # Remove annotations + x_text.removeTextContent(x_annotation_3) + self.check_annotations(["Richard Roe"]) + x_text.removeTextContent(x_annotation_2) + self.check_annotations([]) + + # Remove _already removed_ ones again + # TODO: unexpected behaviour, it should throw an exception, + # but let's nail down current behaviour + # NOTE: reported as tdf#123404 + x_text.removeTextContent(x_annotation_2) + x_text.removeTextContent(x_annotation) + + self.check_annotations([]) + + def create_annotation(self, author): + x_annotation = self._uno.getDoc().createInstance("com.sun.star.text.TextField.Annotation") + self.assertIsNotNone(x_annotation) + x_annotation.setPropertyValue("Author", author) + return x_annotation + + def check_annotations(self, authors): + x_fields_enum = self._uno.getDoc().getTextFields().createEnumeration() + + annotations_found = 0 + + for x_field, author in zip(x_fields_enum, authors): + self.assertTrue(x_field.supportsService("com.sun.star.text.TextField.Annotation")) + self.assertEqual(x_field.getPropertyValue("Author"), author) + annotations_found += 1 + + self.assertEqual(annotations_found, len(authors)) + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xtextcontent.py b/sw/qa/python/xtextcontent.py new file mode 100644 index 000000000..a8193c87b --- /dev/null +++ b/sw/qa/python/xtextcontent.py @@ -0,0 +1,81 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +import unittest + +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.lang import IllegalArgumentException + +class TestXTextContent(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_anchor_operations(self): + x_doc = self._uno.openDocFromTDOC("xtextcontent.odt") + self.assertIsNotNone(x_doc) + + # getAnchor for both text frames and ensure we receive ranges we expect + x_frame_1 = self.get_text_frame("Frame1") + x_range_1 = x_frame_1.getAnchor() + self.assertIsNotNone(x_range_1) + self.compare_range(x_range_1, "String1") + + x_frame_2 = self.get_text_frame("Frame2") + x_range_2 = x_frame_2.getAnchor() + self.assertIsNotNone(x_range_2) + self.compare_range(x_range_2, "String2") + + # Check how XTextContent::attach works. Try to exchange anchors + x_frame_1.attach(x_range_2) + x_frame_2.attach(x_range_1) + self.compare_range(x_frame_1.getAnchor(), "String2") + self.compare_range(x_frame_2.getAnchor(), "String1") + + # Try to attach to None + with self.assertRaises(IllegalArgumentException): + x_frame_1.attach(None) + + # Trying to attach frame to range from other document + x_doc_2 = self._uno.openDocFromTDOC("xcontrolshape.odt") + with self.assertRaises(IllegalArgumentException): + x_frame_1.attach(x_doc_2.getText()) + + x_doc_2.close(True) + x_doc.close(True) + + def get_text_frame(self, frame_name): + x_test_frames = self._uno.getDoc().getTextFrames() + self.assertIsNotNone(x_test_frames) + + x_test_frame = x_test_frames[frame_name] + self.assertIsNotNone(x_test_frame) + + return x_test_frame + + # Helper to extract text content from range and compare to expected string + def compare_range(self, x_range, expected_content): + x_cursor = x_range.getText().createTextCursor() + self.assertIsNotNone(x_cursor) + + x_cursor.collapseToStart() + x_cursor.goRight(len(expected_content), True) + self.assertEqual(x_cursor.getString(), expected_content) + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xtextcursor.py b/sw/qa/python/xtextcursor.py new file mode 100644 index 000000000..2c374bee5 --- /dev/null +++ b/sw/qa/python/xtextcursor.py @@ -0,0 +1,104 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class TestXTextCursor(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._uno.openDocFromTDOC("xtextcursor.odt") + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def createTextCursorInFrame(self, frameName): + xTextFrames = self._uno.getDoc().getTextFrames() + self.assertIsNotNone(xTextFrames) + xTextFrame = xTextFrames[frameName] + self.assertIsNotNone(xTextFrame) + xCursor = xTextFrame.getText().createTextCursor() + self.assertIsNotNone(xCursor) + return xCursor + + def test_cursorMoveInText(self): + # Create cursor in frame with simple text (to avoid moving beyond) + xCursor = self.createTextCursorInFrame("FrameSimple") + + xCursor.collapseToStart() + self.assertTrue(xCursor.isCollapsed()) + self.assertTrue(xCursor.goRight(1, True)) + self.assertFalse(xCursor.isCollapsed()) + # Try to move right 10 characters, but we really can just 3, so partial move + self.assertFalse(xCursor.goRight(10, True)) + self.assertFalse(xCursor.isCollapsed()) + # Ensure that all line text is selected + self.assertEqual(xCursor.getString(), "1234") + + self.assertFalse(xCursor.goRight(-10, True)) + self.assertEqual(xCursor.getString(), "1234") + + xCursor.collapseToEnd() + self.assertTrue(xCursor.isCollapsed()) + self.assertTrue(xCursor.goLeft(2, True)) + self.assertFalse(xCursor.isCollapsed()) + self.assertEqual(xCursor.getString(), "34") + + # Move to start without selection + self.assertTrue(xCursor.goLeft(2, False)) + self.assertEqual(xCursor.getString(), "") + + self.assertTrue(xCursor.isCollapsed()) + + # Select all text + xCursor.gotoStart(False) + self.assertTrue(xCursor.isCollapsed()) + xCursor.gotoEnd(True) + self.assertFalse(xCursor.isCollapsed()) + self.assertEqual(xCursor.getString(), "1234") + + # Select all text from behind + xCursor.gotoEnd(False) + self.assertTrue(xCursor.isCollapsed()) + xCursor.gotoStart(True) + self.assertFalse(xCursor.isCollapsed()) + self.assertEqual(xCursor.getString(), "1234") + + # Select all text, alternative way via gotoRange + xCursor2 = self.createTextCursorInFrame("FrameSimple") + xCursor2.gotoEnd(False) + xCursor2.gotoStart(True) + xCursor.gotoEnd(False) + self.assertTrue(xCursor.isCollapsed()) + xCursor.gotoRange(xCursor2, True) + self.assertFalse(xCursor.isCollapsed()) + self.assertEqual(xCursor.getString(), "1234") + + def test_cursorMoveInTable(self): + # Create cursor in frame with table + xCursor = self.createTextCursorInFrame("FrameTable") + + # Nothing is selected + xCursor.collapseToEnd() + self.assertTrue(xCursor.isCollapsed()) + self.assertEqual(xCursor.getString(), "") + self.assertFalse(xCursor.goLeft(1, False)) + self.assertFalse(xCursor.goLeft(1, True)) + self.assertEqual(xCursor.getString(), "") + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xtextfieldssupplier.py b/sw/qa/python/xtextfieldssupplier.py new file mode 100755 index 000000000..76004a095 --- /dev/null +++ b/sw/qa/python/xtextfieldssupplier.py @@ -0,0 +1,90 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess +from com.sun.star.container import NoSuchElementException + + +class TestXTextFieldsSupplier(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_getTextFields(self): + xDoc = self.__class__._uno.openDocFromTDOC("xtextfieldssupplier.odt") + self.assertIsNotNone(xDoc, "document was not loaded") + xTextFieldsSupplier = xDoc + + # Get text fields collection + xTextFields = xTextFieldsSupplier.getTextFields() + self.assertIsNotNone(xTextFields, "getTextFields fails") + + # Iterate through collection and ensure that we receive expected fields + fieldTypesList = [ + "com.sun.star.text.textfield.PageNumber", + "com.sun.star.text.textfield.Annotation", + "com.sun.star.text.textfield.docinfo.CreateDateTime" + ] + xFieldEnum = xTextFields.createEnumeration() + for fieldType, xField in zip(fieldTypesList, xFieldEnum): + self.assertTrue(xField.supportsService(fieldType), + "field " + xField.getPresentation(True) + + " does not support " + fieldType + " service!") + + xDoc.close(True) + + def test_getTextFieldMasters(self): + xDoc = self.__class__._uno.openDocFromTDOC("xtextfieldssupplier.odt") + self.assertIsNotNone(xDoc, "document was not loaded") + xTextFieldsSupplier = xDoc + + # Get text fields master + xFieldMasters = xTextFieldsSupplier.getTextFieldMasters() + self.assertIsNotNone(xFieldMasters, "getTextFieldMasters fails") + self.assertTrue(xFieldMasters.hasElements(), "TextFieldMaster has no elements") + + # Check elements in TextFieldsMaster collection + masterNames = [ + "com.sun.star.text.fieldmaster.SetExpression.Illustration", + "com.sun.star.text.fieldmaster.SetExpression.Table", + "com.sun.star.text.fieldmaster.SetExpression.Text", + "com.sun.star.text.fieldmaster.SetExpression.Drawing", + "com.sun.star.text.fieldmaster.SetExpression.Figure", + ] + for masterName in masterNames: + self.assertTrue(xFieldMasters.hasByName(masterName), + "TextFieldMaster has no element " + masterName) + master = xFieldMasters.getByName(masterName) + self.assertIsNotNone(master, + "can't get " + masterName + " from TextFieldMaster") + self.assertIsNotNone(master.getPropertyValue("Name"), + "can't get Name property from TextFieldMaster " + masterName) + + # Ensure that invalid elements are not accessible + invalidMasterName = "com.sun.star.text.fieldmaster.SetExpression.NoSuchMaster" + self.assertFalse(xFieldMasters.hasByName(invalidMasterName), + "TextFieldMaster has element " + invalidMasterName) + + with self.assertRaises(NoSuchElementException): + xFieldMasters.getByName(invalidMasterName) + + xDoc.close(True) + + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/qa/python/xtextrange.py b/sw/qa/python/xtextrange.py new file mode 100644 index 000000000..583cabc30 --- /dev/null +++ b/sw/qa/python/xtextrange.py @@ -0,0 +1,115 @@ +#! /usr/bin/env python +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +import unittest +from org.libreoffice.unotest import UnoInProcess + + +class TestXTextRange(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._uno = UnoInProcess() + cls._uno.setUp() + cls._uno.openDocFromTDOC("xtextrange.odt") + + @classmethod + def tearDownClass(cls): + cls._uno.tearDown() + + def test_textRangesInPlainText(self): + xCursor = self._uno.getDoc().getText().createTextCursor() + xCursor.goRight(10, True) + xTextRange = xCursor + self.assertEqual(xTextRange.getString(), "0123456789") + + # getStart + xTextRangeStart = xTextRange.getStart() + self.assertIsNotNone(xTextRangeStart) + self.assertEqual(xTextRangeStart.getString(), "") + xTextRangeStart.setString("before") + self.assertEqual(xTextRangeStart.getString(), "before") + + # Overwrite strings + xTextRangeStart.setString("beforebeforebeforebefore") + xTextRangeStart.setString("before2") + xTextRangeStart.setString("before3") + xTextRangeStart.setString("before") + self.assertEqual(xTextRangeStart.getString(), "before") + xCursor = self._uno.getDoc().getText().createTextCursor() + xCursor.goRight(16, True) + self.assertEqual(xCursor.getString(), "before0123456789") + + # getEnd + xTextRangeEnd = xTextRange.getEnd() + self.assertIsNotNone(xTextRangeEnd) + self.assertEqual(xTextRangeEnd.getString(), "") + xTextRangeEnd.setString("after") + self.assertEqual(xTextRangeEnd.getString(), "after") + + # getText + xText = xTextRange.getText() + self.assertIsNotNone(xText) + + # Final check of what we have inserted + xCursor = self._uno.getDoc().getText().createTextCursor() + xCursor.goRight(21, True) + self.assertEqual(xCursor.getString(), "before0123456789after") + + def test_textRangesInTable(self): + xTextTables = self._uno.getDoc().getTextTables() + self.assertIsNotNone(xTextTables) + xTextTable = xTextTables[0] + self.assertIsNotNone(xTextTable) + + xTextRange = xTextTable.getCellByName("A1") + self.assertIsNotNone(xTextRange) + self.assertEqual(xTextRange.getString(), "C1") + + xTextRangeStart = xTextRange.getStart() + self.assertIsNotNone(xTextRangeStart) + self.assertEqual(xTextRangeStart.getString(), "") + xTextRangeStart.setString("before") + self.assertEqual(xTextRangeStart.getString(), "before") + + xTextRangeEnd = xTextRange.getEnd() + self.assertIsNotNone(xTextRangeEnd) + self.assertEqual(xTextRangeEnd.getString(), "") + xTextRangeEnd.setString("after") + self.assertEqual(xTextRangeEnd.getString(), "after") + + # Ensure that what we inserted is in cell + xTextRange2 = xTextTable.getCellByName("A1") + self.assertEqual(xTextRange2.getString(), "beforeC1after") + + def test_textRangesCompare(self): + doc = self._uno.getDoc() + # Bookmark in body text + bookmark1 = doc.getBookmarks().getByIndex(0).getAnchor() + + # Bookmarks in table + bookmark2 = doc.getBookmarks().getByIndex(1).getAnchor() + bookmark3 = doc.getBookmarks().getByIndex(2).getAnchor() + + res = doc.Text.compareRegionStarts(bookmark1, bookmark2) + self.assertEqual(res, 1) + + res = doc.Text.compareRegionStarts(bookmark2, bookmark1) + self.assertEqual(res, -1) + + res = doc.Text.compareRegionStarts(bookmark2, bookmark3) + self.assertEqual(res, 1) + + res = doc.Text.compareRegionStarts(bookmark1, bookmark3) + self.assertEqual(res, 1) + +if __name__ == '__main__': + unittest.main() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: |