Bump for 3.6-28
[LibreOffice.git] / sw / qa / extras / README
blobae174a55337260bec23e28ce41c6e0a6186c157b
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. All start with a `load()` method that loads a
9 file to `mxComponent`, which represents the UNO model of the document.
11 The rest of the testcase is about asserting this document model: use the UNO
12 API to retrieve properties, then use `CPPUNIT_ASSERT_EQUAL()` to test against
13 an expected value.
15 See below for more details on writing the UNO code see below.
17 == Export tests
19 Export tests are similar. Given that test documents are easier to provide in
20 some format (instead of writing code to build the documents from scratch) in
21 most cases, we will do an import, then do an export (to invoke the code we want
22 to test) and then do an import again, so we can do the testing by asserting the
23 document model, just like we did for import tests.
25 Yes, this means that you can test the export code (using this framework) if the
26 importer is working correctly. (But that's not so bad, users usually expect a
27 feature to work in both the importer and the exporter.)
29 The only difference is that instead of `load()`, you call `roundtrip()` to load
30 the test document, then you can assert it as discussed above.
32 == Helper methods
34 When two or more tests do the same (for example determine the number of
35 characters in the document), helper methods are introduced to avoid code
36 duplication. When you need something more complex, check if there is already a
37 helper method, they are also good examples.
39 Helper methods which are used by more than one testsuite are in the
40 `SwModelTestBase` class. For example the `getLength()` method uses the trick
41 that you can simply enumerate over the document model, getting the paragraphs
42 of it; and inside those, you can enumerate over their runs. That alone is
43 enough if you want to test a paragraph or character property.
45 == Using UNO for tests
47 Figuring out the UNO API just by reading the idl files under `offapi/` is not
48 that productive. Xray can help in this case. Download it from:
50 http://bernard.marcelly.perso.sfr.fr/index2.html
52 It's an SXW file, start Writer, Tools -> Options -> LibreOffice -> Security,
53 Macro Security, and there choose Low. Then open the SXW, and click `Install
54 Xray`. Now you can close the SXW. Open your testcase, which is imported
55 correctly (from a fixed bugs's point of view). Then open the basic editor
56 (Tools -> Macros -> LibreOffice Basic -> Organize Macros, Edit), and start to
57 write your testcase as `Sub Main`. You don't have to know much about basic, for
58 a typical testcase you need no `if`, `for`, or anything like that.
60 NOTE: Once you restart Writer, xray will no longer be loaded automatically. For
61 subsequent starts, place the following line in `Main` before you do anything
62 else:
64 ----
65 GlobalScope.BasicLibraries.LoadLibrary("XrayTool")
66 ----
68 The above `mxComponent` is available as `ThisComponent` in basic, and if you
69 want to inspect a variable here, you can use the `xray` command to inspect
70 properties, methods, interfaces, etc.
72 Let's take for example fdo#49501. The problem there was the page was not
73 landscape (and a few more, let's ignore that).
75 You can start with:
77 ----
78 xray ThisComponent
79 ----
81 and navigate around (it is a good idea to click Configuration and enable
82 alphabetical sorting). The good thing is that once you write the code, you can
83 just start F5 without restarting LibreOffice to see the result, so you can
84 develop quickly.
86 With some experimenting, you'll end up with something like this:
88 ----
89 oStyle = ThisComponent.StyleFamilies.PageStyles.Default
90 xray oStyle.IsLandscape
91 ----
93 Now all left is to rewrite that in cpp, where it'll be much easier to debug
94 when later this test fails for some reason. In cpp, you typically need to be
95 more verbose, so the code will look like:
97 ----
98 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
99 uno::Reference<container::XNameAccess> xStyles(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
100 uno::Reference<container::XNameAccess> xPageStyles(xStyles->getByName("PageStyles"), uno::UNO_QUERY);
101 uno::Reference<beans::XPropertySet> xStyle(xPageStyles->getByName("Default"), uno::UNO_QUERY);
103 sal_Bool bIsLandscape = sal_False;
104 xStyle->getPropertyValue("IsLandscape") >>= bIsLandscape;
105 CPPUNIT_ASSERT_EQUAL(sal_True, bIsLandscape);
106 ----
108 == UNO, in more details, various tips:
110 === writing code based xray inspection:
112 In general, if you want to access a property, in Basic it's enough to write 'object.property',
113 such as printing character count that 'xray ThisComponent' prints as 'CharacterCount':
115 count = ThisComponent.CharacterCount
116 text = paragraph.String
118 In C++, this can get more complicated, as you need to use the right interface for access. Xray
119 prints the internal name of the object (e.g. 'SwXTextDocument' for 'xray ThisComponent')
120 above the list of its properties. Inspect this class/interface in the code (that is,
121 under offapi/, udkapi/, or wherever it is implemented) and search for a function named
122 similarly to the property you want (getXYZ()). If there is none, it is most
123 probably a property that can be read using XPropertySet:
125 uno::Reference<beans::XPropertySet> properties(textDocument, uno::UNO_QUERY);
126 sal_Int32 val; // the right type for the property
127 properties->getPropertyValue("CharacterCount") >>= val;
129 If there is a function to obtain the property, you need access it using the right interface.
130 If the class itself is not the right interface, then it is one of the classes it inherits
131 from, usually the block of functions that are implemented for this interface starts with
132 stating the name. For example see sw/inc/unoparagraph.hxx for class SwXParagraph, it has
133 function getString() in a block introduced with 'XTextRange', so XTextRange is the interface
134 it inherits from:
136 // text of the paragraph
137 uno::Reference<text::XTextRange> text(paragraph, uno::UNO_QUERY);
138 OUString value = text->getString();
140 Some properties may be more complicated to access, such as using XEnumerationAccess, XIndexAccess
141 or XNamedAccess to enumerate items, index them by number of name (clicking 'Dbg_SupportedInterfaces'
142 in xray gives a list of interfaces the object implements, and 'Count' shows the number of items).
144 === XEnumerationAccess (e.g. get the 2nd paragraph of the document):
146 Basic:
148 enum = ThisComponent.Text.createEnumeration
149 para = enum.NextElement
150 para = enum.NextElement
151 xray para
153 C++:
155 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
156 uno::Reference<container::XEnumerationAccess> paraEnumAccess(textDocument->getText(), uno::UNO_QUERY);
157 // list of paragraphs
158 uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
159 // go to 1st paragraph
160 (void) paraEnum->nextElement();
161 // get the 2nd paragraph
162 uno::Reference<uno::XInterface> paragraph(paraEnum->nextElement(), uno::UNO_QUERY);
164 === XNamedAccess (e.g. get a bookmark named 'position1'):
166 Basic:
168 bookmark = ThisComponent.Bookmarks.getByName("position1")
170 or even simpler
172 bookmark = ThisComponent.Bookmarks.position1
174 C++:
176 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
177 // XBookmarksSupplier interface will be needed to access the bookmarks
178 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(textDocument, uno::UNO_QUERY);
179 // get the bookmarks
180 uno::Reference<container::XNameAccess> bookmarks(bookmarksSupplier->getBookmarks(), uno::UNO_QUERY);
181 uno::Reference<uno::XInterface> bookmark;
182 // get the bookmark by name
183 bookmarks->getByName("position1") >>= bookmark;
185 === XIndexAccess (e.g. get the first bookmark):
187 Basic:
189 bookmark = ThisComponent.Bookmarks.getByIndex(0)
191 C++:
193 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
194 // XBookmarksSupplier interface will be needed to access the bookmarks
195 uno::Reference<text::XBookmarksSupplier> bookmarksSupplier(textDocument, uno::UNO_QUERY);
196 // get the bookmarks
197 uno::Reference<container::XIndexAccess> bookmarks(bookmarksSupplier->getBookmarks(), uno::UNO_QUERY);
198 uno::Reference<uno::XInterface> bookmark;
199 // get the bookmark by index
200 bookmarks->getByIndex(0) >>= bookmark;
202 === Images
204 Embedded images seem to be accessed like this:
206 Basic:
208 image = ThisComponent.DrawPage.getByIndex(0)
209 graphic = image.Graphic
211 C++:
213 uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
214 uno::Reference<drawing::XDrawPageSupplier> drawPageSupplier(textDocument, uno::UNO_QUERY);
215 uno::Reference<drawing::XDrawPage> drawPage = drawPageSupplier->getDrawPage();
216 uno::Reference<drawing::XShape> image;
217 drawPage->getByIndex(0) >>= image;
218 uno::Reference<beans::XPropertySet> imageProperties(image, uno::UNO_QUERY);
219 uno::Reference<graphic::XGraphic> graphic;
220 imageProperties->getPropertyValue( "Graphic" ) >>= graphic;