Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include <com/sun/star/container/XContentEnumerationAccess.hpp>
11 : #include <com/sun/star/frame/Desktop.hpp>
12 : #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
13 : #include <com/sun/star/text/XPageCursor.hpp>
14 : #include <com/sun/star/text/XTextDocument.hpp>
15 : #include <com/sun/star/text/XTextRange.hpp>
16 : #include <com/sun/star/text/XTextTable.hpp>
17 : #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
18 : #include <com/sun/star/table/XCell.hpp>
19 :
20 : #include <test/bootstrapfixture.hxx>
21 : #include <unotest/macros_test.hxx>
22 : #include <rtl/ustrbuf.hxx>
23 : #include <comphelper/processfactory.hxx>
24 : #include <unotools/tempfile.hxx>
25 :
26 : #include <unotxdoc.hxx>
27 : #include <docsh.hxx>
28 : #include <doc.hxx>
29 : #include <rootfrm.hxx>
30 :
31 : #include <libxml/xmlwriter.h>
32 : #include <libxml/xpath.h>
33 :
34 : using namespace com::sun::star;
35 :
36 : #define DEFAULT_STYLE "Default Style"
37 :
38 : /// Base class for filter tests loading or roundtriping a document, then asserting the document model.
39 : class SwModelTestBase : public test::BootstrapFixture, public unotest::MacrosTest
40 : {
41 : public:
42 9 : SwModelTestBase()
43 9 : : mpXmlBuffer(0)
44 : {
45 9 : }
46 :
47 9 : ~SwModelTestBase()
48 9 : {
49 9 : }
50 :
51 9 : virtual void setUp()
52 : {
53 9 : test::BootstrapFixture::setUp();
54 :
55 9 : mxDesktop.set( com::sun::star::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())) );
56 9 : }
57 :
58 9 : virtual void tearDown()
59 : {
60 9 : if (mxComponent.is())
61 9 : mxComponent->dispose();
62 :
63 9 : test::BootstrapFixture::tearDown();
64 9 : }
65 :
66 : private:
67 12 : void dumpLayout()
68 : {
69 : // create the xml writer
70 12 : mpXmlBuffer = xmlBufferCreate();
71 12 : xmlTextWriterPtr pXmlWriter = xmlNewTextWriterMemory(mpXmlBuffer, 0);
72 12 : xmlTextWriterStartDocument(pXmlWriter, NULL, NULL, NULL);
73 :
74 : // create the dump
75 12 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
76 12 : SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
77 12 : SwRootFrm* pLayout = pDoc->GetCurrentLayout();
78 12 : pLayout->dumpAsXml(pXmlWriter);
79 :
80 : // delete xml writer
81 12 : xmlTextWriterEndDocument(pXmlWriter);
82 12 : xmlFreeTextWriter(pXmlWriter);
83 12 : }
84 :
85 397 : void calcLayout()
86 : {
87 397 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
88 397 : SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
89 397 : pDoc->GetCurrentViewShell()->CalcLayout();
90 397 : }
91 :
92 :
93 : protected:
94 : /// Get the length of the whole document.
95 11 : int getLength()
96 : {
97 11 : uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
98 22 : uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
99 22 : uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
100 22 : OUStringBuffer aBuf;
101 34 : while (xParaEnum->hasMoreElements())
102 : {
103 12 : uno::Reference<container::XEnumerationAccess> xRangeEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
104 24 : uno::Reference<container::XEnumeration> xRangeEnum = xRangeEnumAccess->createEnumeration();
105 40 : while (xRangeEnum->hasMoreElements())
106 : {
107 16 : uno::Reference<text::XTextRange> xRange(xRangeEnum->nextElement(), uno::UNO_QUERY);
108 16 : aBuf.append(xRange->getString());
109 16 : }
110 12 : }
111 22 : return aBuf.getLength();
112 : }
113 :
114 : /// Get a family of styles, see com.sun.star.style.StyleFamilies for possible values.
115 42 : uno::Reference<container::XNameAccess> getStyles(OUString aFamily)
116 : {
117 42 : uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
118 84 : uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
119 42 : uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(aFamily), uno::UNO_QUERY);
120 84 : return xStyleFamily;
121 : }
122 :
123 : /**
124 : * Extract a value from the layout dump using an XPath expression and an attribute name.
125 : *
126 : * If the attribute is omitted, the text of the node is returned.
127 : */
128 66 : OUString parseDump(OString aXPath, OString aAttribute = OString())
129 : {
130 66 : if (!mpXmlBuffer)
131 12 : dumpLayout();
132 :
133 66 : xmlDocPtr pXmlDoc = xmlParseMemory((const char*)xmlBufferContent(mpXmlBuffer), xmlBufferLength(mpXmlBuffer));;
134 :
135 66 : xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
136 66 : xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
137 66 : xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
138 66 : CPPUNIT_ASSERT_EQUAL(1, xmlXPathNodeSetGetLength(pXmlNodes));
139 66 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
140 66 : OUString aRet;
141 66 : if (aAttribute.getLength())
142 5 : aRet = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
143 : else
144 61 : aRet = OUString::createFromAscii((const char*)XML_GET_CONTENT(pXmlNode));
145 :
146 66 : xmlFreeDoc(pXmlDoc);
147 :
148 66 : return aRet;
149 : }
150 :
151 : template< typename T >
152 78 : T getProperty( uno::Any obj, const OUString& name ) const
153 : {
154 78 : uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY );
155 78 : T data = T();
156 78 : properties->getPropertyValue( name ) >>= data;
157 78 : return data;
158 : }
159 :
160 : template< typename T >
161 483 : T getProperty( uno::Reference< uno::XInterface > obj, const OUString& name ) const
162 : {
163 483 : uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY );
164 483 : T data = T();
165 483 : properties->getPropertyValue( name ) >>= data;
166 483 : return data;
167 : }
168 :
169 : /// Get number of paragraphs of the document.
170 1 : int getParagraphs()
171 : {
172 1 : uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
173 2 : uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
174 2 : uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
175 1 : int nRet = 0;
176 4 : while (xParaEnum->hasMoreElements())
177 : {
178 2 : xParaEnum->nextElement();
179 2 : nRet++;
180 : }
181 2 : return nRet;
182 : }
183 :
184 203 : uno::Reference<text::XTextContent> getParagraphOrTable(int number, uno::Reference<text::XText> xText = uno::Reference<text::XText>()) const
185 : {
186 203 : uno::Reference<container::XEnumerationAccess> paraEnumAccess;
187 203 : if (xText.is())
188 14 : paraEnumAccess.set(xText, uno::UNO_QUERY);
189 : else
190 : {
191 189 : uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
192 189 : paraEnumAccess.set(textDocument->getText(), uno::UNO_QUERY);
193 : }
194 406 : uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
195 333 : for( int i = 1;
196 : i < number;
197 : ++i )
198 130 : paraEnum->nextElement();
199 203 : uno::Reference< text::XTextContent> const xElem(paraEnum->nextElement(),
200 203 : uno::UNO_QUERY_THROW);
201 406 : return xElem;
202 : }
203 :
204 : // Get paragraph (counted from 1), optionally check it contains the given text.
205 188 : uno::Reference< text::XTextRange > getParagraph( int number, OUString content = OUString() ) const
206 : {
207 : uno::Reference<text::XTextRange> const xParagraph(
208 188 : getParagraphOrTable(number), uno::UNO_QUERY_THROW);
209 188 : if( !content.isEmpty())
210 18 : CPPUNIT_ASSERT_EQUAL( content, xParagraph->getString());
211 188 : return xParagraph;
212 : }
213 :
214 11 : uno::Reference<text::XTextRange> getParagraphOfText(int number, uno::Reference<text::XText> xText) const
215 : {
216 11 : uno::Reference<text::XTextRange> const xParagraph(getParagraphOrTable(number, xText), uno::UNO_QUERY_THROW);
217 11 : return xParagraph;
218 : }
219 :
220 : /// Get run (counted from 1) of a paragraph, optionally check it contains the given text.
221 178 : uno::Reference<text::XTextRange> getRun(uno::Reference<text::XTextRange> xParagraph, int number, OUString content = OUString()) const
222 : {
223 178 : uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
224 356 : uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
225 357 : for (int i = 1; i < number; ++i)
226 179 : xRunEnum->nextElement();
227 178 : uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
228 178 : if( !content.isEmpty())
229 27 : CPPUNIT_ASSERT_EQUAL( content, xRun->getString());
230 356 : return xRun;
231 : }
232 :
233 : /// Get math formula string of a run.
234 114 : OUString getFormula(uno::Reference<text::XTextRange> xRun) const
235 : {
236 114 : uno::Reference<container::XContentEnumerationAccess> xContentEnumAccess(xRun, uno::UNO_QUERY);
237 228 : uno::Reference<container::XEnumeration> xContentEnum(xContentEnumAccess->createContentEnumeration(""), uno::UNO_QUERY);
238 228 : uno::Reference<beans::XPropertySet> xFormula(xContentEnum->nextElement(), uno::UNO_QUERY);
239 228 : return getProperty<OUString>(getProperty< uno::Reference<beans::XPropertySet> >(xFormula, "Model"), "Formula");
240 : }
241 :
242 : /// get cell of a table; table can be retrieved with getParagraphOrTable
243 5 : uno::Reference<table::XCell> getCell(
244 : uno::Reference<uno::XInterface> const& xTableIfc,
245 : OUString const& rCell, OUString const& rContent = OUString())
246 : {
247 : uno::Reference<text::XTextTable> const xTable(xTableIfc,
248 5 : uno::UNO_QUERY_THROW);
249 : uno::Reference<table::XCell> const xCell(
250 5 : xTable->getCellByName(rCell), uno::UNO_SET_THROW);
251 5 : if (!rContent.isEmpty())
252 : {
253 : uno::Reference<text::XText> const xCellText(xCell,
254 3 : uno::UNO_QUERY_THROW);
255 3 : CPPUNIT_ASSERT_EQUAL(rContent, xCellText->getString());
256 : }
257 5 : return xCell;
258 : }
259 :
260 9 : void header()
261 : {
262 9 : std::cerr << "File tested,Execution Time (ms)" << std::endl;
263 9 : }
264 :
265 298 : void load(const char* pDir, const char* pName, bool bCalcLayout = true)
266 : {
267 298 : if (mxComponent.is())
268 289 : mxComponent->dispose();
269 : // Output name early, so in the case of a hang, the name of the hanging input file is visible.
270 298 : std::cerr << pName << ",";
271 298 : m_nStartTime = osl_getGlobalTimer();
272 298 : mxComponent = loadFromDesktop(getURLFromSrc(pDir) + OUString::createFromAscii(pName), "com.sun.star.text.TextDocument");
273 298 : if (bCalcLayout)
274 297 : calcLayout();
275 298 : }
276 :
277 100 : void reload(OUString aFilter)
278 : {
279 100 : uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
280 200 : uno::Sequence<beans::PropertyValue> aArgs(1);
281 100 : aArgs[0].Name = "FilterName";
282 100 : aArgs[0].Value <<= aFilter;
283 200 : utl::TempFile aTempFile;
284 100 : aTempFile.EnableKillingFile();
285 100 : xStorable->storeToURL(aTempFile.GetURL(), aArgs);
286 200 : uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY);
287 100 : xComponent->dispose();
288 100 : mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
289 100 : if (mpXmlBuffer)
290 : {
291 4 : xmlBufferFree(mpXmlBuffer);
292 4 : mpXmlBuffer = 0;
293 : }
294 200 : calcLayout();
295 100 : }
296 :
297 : /// Save the loaded document to a tempfile. Can be used to check the resulting docx/odt directly as a ZIP file.
298 4 : void save(OUString aFilter, utl::TempFile& rTempFile)
299 : {
300 4 : rTempFile.EnableKillingFile();
301 4 : uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
302 8 : uno::Sequence<beans::PropertyValue> aFilterArgs(1);
303 4 : aFilterArgs[0].Name = "FilterName";
304 4 : aFilterArgs[0].Value <<= aFilter;
305 8 : xStorable->storeToURL(rTempFile.GetURL(), aFilterArgs);
306 4 : }
307 :
308 298 : void finish()
309 : {
310 298 : sal_uInt32 nEndTime = osl_getGlobalTimer();
311 298 : std::cerr << (nEndTime - m_nStartTime) << std::endl;
312 298 : if (mpXmlBuffer)
313 : {
314 8 : xmlBufferFree(mpXmlBuffer);
315 8 : mpXmlBuffer = 0;
316 : }
317 298 : }
318 :
319 : /// Get page count.
320 9 : int getPages()
321 : {
322 9 : uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
323 18 : uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
324 18 : uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
325 9 : xCursor->jumpToLastPage();
326 18 : return xCursor->getPage();
327 : }
328 :
329 : uno::Reference<lang::XComponent> mxComponent;
330 : xmlBufferPtr mpXmlBuffer;
331 :
332 : template< typename T >
333 : struct MethodEntry
334 : {
335 : const char* pName;
336 : void (T::*pMethod)();
337 : };
338 : sal_uInt32 m_nStartTime;
339 : };
340 :
341 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|