tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / sw / qa / extras / README
blob2e5df81d4751e0fc7f8ef0d24d0460e755f9a839
1 = How to add a new Writer filter test
3 The `sw/qa/extras/` subdirectory has multiple import and export filter unit
4 tests. This file documents how to add new testcases to this framework.
6 == Import tests
8 Import tests are the easier ones. First you need to use
9 `CPPUNIT_TEST_FIXTURE()` and `load()`, so the framework will load the
10 specified file to `mxComponent`, which represents the UNO model of the
11 document.
13 The rest of the testcase is about implementing the test method asserting this
14 document model: use the UNO API to retrieve properties, then use
15 `CPPUNIT_ASSERT_EQUAL()` to test against an expected value.
17 See below for more details on writing the UNO code see below.
19 === Direct XPath assertions on the layout dump
21 In most cases you want to assert the document model, but sometimes asserting
22 the layout is easier. If you want to do so, check the methods in
23 test/source/xmltesttools.cxx. If you want
24 to have a look at the XML document that can be asserted, start soffice with the
25 `SW_DEBUG=1` environment variable, load a document, press F12, and have a look
26 at the `layout.xml` file in the current directory. Once you find the needed
27 information in that file, you can write your XPath expression to turn that into
28 a testcase.
30 (Similarly, Shift-F12 produces a `nodes.xml` for the document model dump, but
31 it's unlikely that you'll need that in a unit test.)
33 == Export tests
35 Export tests are similar. Given that test documents are easier to provide in
36 some format (instead of writing code to build the documents from scratch) in
37 most cases, we will do an import, then do an export (to invoke the code we want
38 to test) and then do an import again, so we can do the testing by asserting the
39 document model, just like we did for import tests.
41 Yes, this means that you can only test the export code (using this framework)
42 if the importer is working correctly. (But that's not so bad, users usually
43 expect a feature to work in both the importer and the exporter.)
45 The only difference is that in these tests the test method is called twice:
46 once after the initial import -- so you can see if the export fails due to an
47 import problem in fact -- and once after the export and import.
49 === Direct XPath assertions
51 Another alternative is to assert the resulted export document directly.
52 Currently this is only implemented for DOCX, which is a zipped XML, so it's
53 possible to evaluate XPath checks. A check looks like this:
55 xmlDocPtr pXmlDoc = parseExport("word/document.xml");
56 assertXPath(pXmlDoc, <xpath selecting the node>, <attribute>, <value>);
58 It's important to check for the NULL pointer here, it's expected that it'll be
59 NULL when the test runs first (after the first import), as there is nothing
60 exported yet. For other XPath assert variants, see the `XmlTestTools` class.
62 == Helper methods
64 When two or more tests do the same (for example determine the number of
65 characters in the document), helper methods are introduced to avoid code
66 duplication. When you need something more complex, check if there is already a
67 helper method, they are also good examples.
69 Helper methods which are used by more than one testsuite are in the
70 `SwModelTestBase` class. For example the `getLength()` method uses the trick
71 that you can simply enumerate over the document model, getting the paragraphs
72 of it; and inside those, you can enumerate over their runs. That alone is
73 enough if you want to test a paragraph or character property.
75 == Using UNO for tests
77 Figuring out the UNO API just by reading the idl files under `offapi/` is not
78 that productive. Xray can help in this case. Download it from:
80 https://dev-www.libreoffice.org/extern/XrayTool52_en.sxw
82 It's a document file, start Writer, Tools -> Options -> LibreOffice -> Security,
83 Macro Security, and there choose Low. Then open the document, and click `Install
84 Xray`. Now you can close the file. Open your testcase, which is imported
85 correctly (from a fixed bugs's point of view). Then open the basic editor
86 (Tools -> Macros -> LibreOffice Basic -> Organize Macros, Edit), and start to
87 write your testcase as `Sub Main`. You don't have to know much about basic, for
88 a typical testcase you need no `if`, `for`, or anything like that.
90 NOTE: Once you restart Writer, xray will no longer be loaded automatically. For
91 subsequent starts, place the following line in `Main` before you do anything
92 else:
94 ----
95 GlobalScope.BasicLibraries.LoadLibrary("XrayTool")
96 ----
98 The above `mxComponent` is available as `ThisComponent` in basic, and if you
99 want to inspect a variable here, you can use the `xray` command to inspect
100 properties, methods, interfaces, etc.
102 Let's take for example fdo#49501. The problem there was the page was not
103 landscape (and a few more, let's ignore that).
105 You can start with:
107 ----
108 xray ThisComponent
109 ----
111 and navigate around (it is a good idea to click Configuration and enable
112 alphabetical sorting). The good thing is that once you write the code, you can
113 just start F5 without restarting LibreOffice to see the result, so you can
114 develop quickly.
116 With some experimenting, you'll end up with something like this:
118 ----
119 oStyle = ThisComponent.StyleFamilies.PageStyles.getByName("Default Style")
120 xray oStyle.IsLandscape
121 ----
123 Now all left is to rewrite that in cpp, where it'll be much easier to debug
124 when later this test fails for some reason. In cpp, you typically need to be
125 more verbose, so the code will look like:
127 ----
128 uno::Reference<beans::XPropertySet> xStyle(getStyles("PageStyles")->getByName("Standard"), uno::UNO_QUERY);
129 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xStyle, "IsLandscape"));
130 ----
132 == CppUnit tips
134 === sal_Bool
136 In case a UNO method returns sal_Bool, and the assert fails, CppUnit won't be
137 able to print a usable error message, as it will think that the value is a
138 printable character. Best to use `bool` for the expected value and cast the
139 actual value to `bool` as well before comparing.
141 === Running only a single test
143 If you want to run only a single test to allow quick development iteration,
144 then use `CPPUNIT_TEST_NAME` to specify the name of the single test:
146 ----
147 CPPUNIT_TEST_NAME="testTdf91074" make -sr CppunitTest_sw_rtfexport6
148 ----
150 == UNO, in more details, various tips:
152 === writing code based xray inspection:
154 In general, if you want to access a property, in Basic it's enough to write 'object.property',
155 such as printing character count that 'xray ThisComponent' prints as 'CharacterCount':
157 count = ThisComponent.CharacterCount
158 text = paragraph.String
160 In C++, this can get more complicated, as you need to use the right interface for access. Xray
161 prints the internal name of the object (e.g. 'SwXTextDocument' for 'xray ThisComponent')
162 above the list of its properties. Inspect this class/interface in the code (that is,
163 under offapi/, udkapi/, or wherever it is implemented) and search for a function named
164 similarly to the property you want (getXYZ()). If there is none, it is most
165 probably a property that can be read using XPropertySet or using the getProperty helper:
167 sal_Int32 val = getProperty< sal_Int32 >( textDocument, "CharacterCount" );
169 If there is a function to obtain the property, you need access it using the right interface.
170 If the class itself is not the right interface, then it is one of the classes it inherits
171 from, usually the block of functions that are implemented for this interface starts with
172 stating the name. For example see sw/inc/unoparagraph.hxx for class SwXParagraph, it has
173 function getString() in a block introduced with 'XTextRange', so XTextRange is the interface
174 it inherits from:
176 // text of the paragraph
177 uno::Reference<text::XTextRange> text(paragraph, uno::UNO_QUERY);
178 OUString value = text->getString();
180 Some properties may be more complicated to access, such as using XEnumerationAccess, XIndexAccess
181 or XNamedAccess to enumerate items, index them by number of name (clicking 'Dbg_SupportedInterfaces'
182 in xray gives a list of interfaces the object implements, and 'Count' shows the number of items).
184 === XEnumerationAccess (e.g. get the 2nd paragraph of the document):
186 Basic:
188 enum = ThisComponent.Text.createEnumeration
189 para = enum.NextElement
190 para = enum.NextElement
191 xray para
193 C++:
195 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
196 uno::Reference<container::XEnumerationAccess> paraEnumAccess(textDocument->getText(), uno::UNO_QUERY);
197 // list of paragraphs
198 uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
199 // go to 1st paragraph
200 (void) paraEnum->nextElement();
201 // get the 2nd paragraph
202 uno::Reference<uno::XInterface> paragraph(paraEnum->nextElement(), uno::UNO_QUERY);
204 Note that for paragraphs it's easier to use getParagraph(), which gets the given
205 paragraph (counted from 1) and optionally checks the paragraph text.
207 uno::Reference< text::XTextRange > paragraph = getParagraph( 2, "TEXT" )
209 === XNamedAccess (e.g. get a bookmark named 'position1'):
211 Basic:
213 bookmark = ThisComponent.Bookmarks.getByName("position1")
215 or even simpler
217 bookmark = ThisComponent.Bookmarks.position1
219 C++:
221 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
222 // XBookmarksSupplier interface will be needed to access the bookmarks
223 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(textDocument, uno::UNO_QUERY);
224 // get the bookmarks
225 uno::Reference<container::XNameAccess> bookmarks(bookmarksSupplier->getBookmarks(), uno::UNO_QUERY);
226 uno::Reference<uno::XInterface> bookmark;
227 // get the bookmark by name
228 bookmarks->getByName("position1") >>= bookmark;
230 === XIndexAccess (e.g. get the first bookmark):
232 Basic:
234 bookmark = ThisComponent.Bookmarks.getByIndex(0)
236 C++:
238 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
239 // XBookmarksSupplier interface will be needed to access the bookmarks
240 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(textDocument, uno::UNO_QUERY);
241 // get the bookmarks
242 uno::Reference<container::XIndexAccess> bookmarks(bookmarksSupplier->getBookmarks(), uno::UNO_QUERY);
243 uno::Reference<uno::XInterface> bookmark;
244 // get the bookmark by index
245 bookmarks->getByIndex(0) >>= bookmark;
247 === Images
249 Embedded images seem to be accessed like this:
251 Basic:
253 image = ThisComponent.DrawPage.getByIndex(0)
254 graphic = image.Graphic
256 C++:
258 uno::Reference<drawing::XShape> image = getShape(1);
259 uno::Reference<graphic::XGraphic> graphic = getProperty< uno::Reference< graphic::XGraphic > >( image, "Graphic" );
262 === Styles
264 Styles provide information about many properties of (parts of) the document, for example
265 page width:
267 Basic:
269 ThisComponent.StyleFamilies.PageStyles.getByName("Default Style").Width
271 C++:
273 getStyles("PageStyles")->getByName("Standard") >>= defaultStyle;
274 sal_Int32 width = getProperty< sal_Int32 >( defaultStyle, "Width" );