bump product version to 4.1.6.2
[LibreOffice.git] / sw / qa / extras / README
blob09a9b6dfad3fd5097fc981d2476f965b6ff777fe
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 add a new entry to the
9 table inside the `run()` method, so the framework will load the specified file
10 to `mxComponent`, which represents the UNO model of the document.
12 The rest of the testcase is about implementing the test method asserting this
13 document model: use the UNO API to retrieve properties, then use
14 `CPPUNIT_ASSERT_EQUAL()` to test against an expected value.
16 See below for more details on writing the UNO code see below.
18 == Export tests
20 Export tests are similar. Given that test documents are easier to provide in
21 some format (instead of writing code to build the documents from scratch) in
22 most cases, we will do an import, then do an export (to invoke the code we want
23 to test) and then do an import again, so we can do the testing by asserting the
24 document model, just like we did for import tests.
26 Yes, this means that you can test the export code (using this framework) if the
27 importer is working correctly. (But that's not so bad, users usually expect a
28 feature to work in both the importer and the exporter.)
30 The only difference is that in these tests the test method is called twice:
31 once after the initial import -- so you can see if the export fails due to an
32 import problem in fact -- and once after the export and import.  The test
33 method should still assert the document model only, as discussed above.
35 == Helper methods
37 When two or more tests do the same (for example determine the number of
38 characters in the document), helper methods are introduced to avoid code
39 duplication. When you need something more complex, check if there is already a
40 helper method, they are also good examples.
42 Helper methods which are used by more than one testsuite are in the
43 `SwModelTestBase` class. For example the `getLength()` method uses the trick
44 that you can simply enumerate over the document model, getting the paragraphs
45 of it; and inside those, you can enumerate over their runs. That alone is
46 enough if you want to test a paragraph or character property.
48 == Using UNO for tests
50 Figuring out the UNO API just by reading the idl files under `offapi/` is not
51 that productive. Xray can help in this case. Download it from:
53 http://bernard.marcelly.perso.sfr.fr/index2.html
55 It's an SXW file, start Writer, Tools -> Options -> LibreOffice -> Security,
56 Macro Security, and there choose Low. Then open the SXW, and click `Install
57 Xray`. Now you can close the SXW. Open your testcase, which is imported
58 correctly (from a fixed bugs's point of view). Then open the basic editor
59 (Tools -> Macros -> LibreOffice Basic -> Organize Macros, Edit), and start to
60 write your testcase as `Sub Main`. You don't have to know much about basic, for
61 a typical testcase you need no `if`, `for`, or anything like that.
63 NOTE: Once you restart Writer, xray will no longer be loaded automatically. For
64 subsequent starts, place the following line in `Main` before you do anything
65 else:
67 ----
68 GlobalScope.BasicLibraries.LoadLibrary("XrayTool")
69 ----
71 The above `mxComponent` is available as `ThisComponent` in basic, and if you
72 want to inspect a variable here, you can use the `xray` command to inspect
73 properties, methods, interfaces, etc.
75 Let's take for example fdo#49501. The problem there was the page was not
76 landscape (and a few more, let's ignore that).
78 You can start with:
80 ----
81 xray ThisComponent
82 ----
84 and navigate around (it is a good idea to click Configuration and enable
85 alphabetical sorting). The good thing is that once you write the code, you can
86 just start F5 without restarting LibreOffice to see the result, so you can
87 develop quickly.
89 With some experimenting, you'll end up with something like this:
91 ----
92 oStyle = ThisComponent.StyleFamilies.PageStyles.Default
93 xray oStyle.IsLandscape
94 ----
96 Now all left is to rewrite that in cpp, where it'll be much easier to debug
97 when later this test fails for some reason. In cpp, you typically need to be
98 more verbose, so the code will look like:
100 ----
101 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
102 uno::Reference<container::XNameAccess> xStyles(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
103 uno::Reference<container::XNameAccess> xPageStyles(xStyles->getByName("PageStyles"), uno::UNO_QUERY);
104 uno::Reference<beans::XPropertySet> xStyle(xPageStyles->getByName(DEFAULT_STYLE), uno::UNO_QUERY);
106 sal_Bool bIsLandscape = sal_False;
107 xStyle->getPropertyValue("IsLandscape") >>= bIsLandscape;
108 CPPUNIT_ASSERT_EQUAL(sal_True, bIsLandscape);
109 ----
111 == UNO, in more details, various tips:
113 === writing code based xray inspection:
115 In general, if you want to access a property, in Basic it's enough to write 'object.property',
116 such as printing character count that 'xray ThisComponent' prints as 'CharacterCount':
118 count = ThisComponent.CharacterCount
119 text = paragraph.String
121 In C++, this can get more complicated, as you need to use the right interface for access. Xray
122 prints the internal name of the object (e.g. 'SwXTextDocument' for 'xray ThisComponent')
123 above the list of its properties. Inspect this class/interface in the code (that is,
124 under offapi/, udkapi/, or wherever it is implemented) and search for a function named
125 similarly to the property you want (getXYZ()). If there is none, it is most
126 probably a property that can be read using XPropertySet or using the getProperty helper:
128 sal_Int32 val = getProperty< sal_Int32 >( textDocument, "CharacterCount" );
130 If there is a function to obtain the property, you need access it using the right interface.
131 If the class itself is not the right interface, then it is one of the classes it inherits
132 from, usually the block of functions that are implemented for this interface starts with
133 stating the name. For example see sw/inc/unoparagraph.hxx for class SwXParagraph, it has
134 function getString() in a block introduced with 'XTextRange', so XTextRange is the interface
135 it inherits from:
137 // text of the paragraph
138 uno::Reference<text::XTextRange> text(paragraph, uno::UNO_QUERY);
139 OUString value = text->getString();
141 Some properties may be more complicated to access, such as using XEnumerationAccess, XIndexAccess
142 or XNamedAccess to enumerate items, index them by number of name (clicking 'Dbg_SupportedInterfaces'
143 in xray gives a list of interfaces the object implements, and 'Count' shows the number of items).
145 === XEnumerationAccess (e.g. get the 2nd paragraph of the document):
147 Basic:
149 enum = ThisComponent.Text.createEnumeration
150 para = enum.NextElement
151 para = enum.NextElement
152 xray para
154 C++:
156 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
157 uno::Reference<container::XEnumerationAccess> paraEnumAccess(textDocument->getText(), uno::UNO_QUERY);
158 // list of paragraphs
159 uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
160 // go to 1st paragraph
161 (void) paraEnum->nextElement();
162 // get the 2nd paragraph
163 uno::Reference<uno::XInterface> paragraph(paraEnum->nextElement(), uno::UNO_QUERY);
165 Note that for paragraphs it's easier to use getParagraph(), which gets the given
166 paragraph (counted from 1) and optionally checks the paragraph text.
168 uno::Reference< text::XTextRange > paragraph = getParagraph( 2, "TEXT" )
170 === XNamedAccess (e.g. get a bookmark named 'position1'):
172 Basic:
174 bookmark = ThisComponent.Bookmarks.getByName("position1")
176 or even simpler
178 bookmark = ThisComponent.Bookmarks.position1
180 C++:
182 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
183 // XBookmarksSupplier interface will be needed to access the bookmarks
184 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(textDocument, uno::UNO_QUERY);
185 // get the bookmarks
186 uno::Reference<container::XNameAccess> bookmarks(bookmarksSupplier->getBookmarks(), uno::UNO_QUERY);
187 uno::Reference<uno::XInterface> bookmark;
188 // get the bookmark by name
189 bookmarks->getByName("position1") >>= bookmark;
191 === XIndexAccess (e.g. get the first bookmark):
193 Basic:
195 bookmark = ThisComponent.Bookmarks.getByIndex(0)
197 C++:
199 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
200 // XBookmarksSupplier interface will be needed to access the bookmarks
201 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(textDocument, uno::UNO_QUERY);
202 // get the bookmarks
203 uno::Reference<container::XIndexAccess> bookmarks(bookmarksSupplier->getBookmarks(), uno::UNO_QUERY);
204 uno::Reference<uno::XInterface> bookmark;
205 // get the bookmark by index
206 bookmarks->getByIndex(0) >>= bookmark;
208 === Images
210 Embedded images seem to be accessed like this:
212 Basic:
214 image = ThisComponent.DrawPage.getByIndex(0)
215 graphic = image.Graphic
217 C++:
219 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
220 uno::Reference<drawing::XDrawPageSupplier> drawPageSupplier(textDocument, uno::UNO_QUERY);
221 uno::Reference<drawing::XDrawPage> drawPage = drawPageSupplier->getDrawPage();
222 uno::Reference<drawing::XShape> image;
223 drawPage->getByIndex(0) >>= image;
224 uno::Reference<graphic::XGraphic> graphic = getProperty< uno::Reference< graphic::XGraphic > >( image, "Graphic" );
227 === Styles
229 Styles provide information about many properties of (parts of) the document, for example
230 page width:
232 Basic:
234 ThisComponent.StyleFamilies.PageStyles.Default.Width
236 C++:
238 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
239 uno::Reference<style::XStyleFamiliesSupplier> styleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
240 uno::Reference<container::XNameAccess> styleFamilies = styleFamiliesSupplier->getStyleFamilies();
241 uno::Reference<container::XNameAccess> pageStyles;
242 styleFamilies->getByName("PageStyles") >>= pageStyles;
243 uno::Reference<uno::XInterface> defaultStyle;
244 pageStyles->getByName(DEFAULT_STYLE) >>= defaultStyle;
245 sal_Int32 width = getProperty< sal_Int32 >( defaultStyle, "Width" );