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 : #ifndef INCLUDED_SW_QA_EXTRAS_INC_SWMODELTESTBASE_HXX
11 : #define INCLUDED_SW_QA_EXTRAS_INC_SWMODELTESTBASE_HXX
12 :
13 : #include <com/sun/star/container/XContentEnumerationAccess.hpp>
14 : #include <com/sun/star/frame/Desktop.hpp>
15 : #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
16 : #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
17 : #include <com/sun/star/style/XAutoStylesSupplier.hpp>
18 : #include <com/sun/star/style/XAutoStyleFamily.hpp>
19 : #include <com/sun/star/text/XPageCursor.hpp>
20 : #include <com/sun/star/text/XTextDocument.hpp>
21 : #include <com/sun/star/text/XTextRange.hpp>
22 : #include <com/sun/star/text/XTextTable.hpp>
23 : #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
24 : #include <com/sun/star/table/XCell.hpp>
25 : #include <com/sun/star/table/BorderLine2.hpp>
26 : #include <com/sun/star/task/XJob.hpp>
27 : #include <com/sun/star/sdb/CommandType.hpp>
28 : #include <com/sun/star/sdb/DatabaseContext.hpp>
29 : #include <com/sun/star/sdb/XDocumentDataSource.hpp>
30 : #include <com/sun/star/text/MailMergeType.hpp>
31 :
32 : #include <test/bootstrapfixture.hxx>
33 : #include <test/xmltesttools.hxx>
34 : #include <unotest/macros_test.hxx>
35 : #include <unotools/ucbstreamhelper.hxx>
36 : #include <rtl/strbuf.hxx>
37 : #include <rtl/ustrbuf.hxx>
38 : #include <rtl/byteseq.hxx>
39 : #include <comphelper/processfactory.hxx>
40 : #include <unotools/tempfile.hxx>
41 : #include <unotools/localfilehelper.hxx>
42 : #include <unotools/mediadescriptor.hxx>
43 : #include <dbmgr.hxx>
44 : #include <unoprnms.hxx>
45 :
46 : #include <unotxdoc.hxx>
47 : #include <docsh.hxx>
48 : #include <doc.hxx>
49 : #include <IDocumentLayoutAccess.hxx>
50 : #include <rootfrm.hxx>
51 :
52 : using namespace css;
53 :
54 : #define DEFAULT_STYLE "Default Style"
55 : #define EMU_TO_MM100(EMU) (EMU / 360)
56 :
57 : /**
58 : * Macro to declare a new test (with full round-trip. To test
59 : * import only use the DECLARE_SW_IMPORT_TEST macro instead).
60 : * In order to add a new test, one only needs to use this macro
61 : * and then specify the test content, like this:
62 : *
63 : * DECLARE_SW_ROUNDTRIP_TEST(MyTest, "myfilename.docx", Test)
64 : * {
65 : * CPPUNIT_ASSERT_EQUAL(blabla);
66 : * }
67 : *
68 : */
69 : #define DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, BaseClass) \
70 : class TestName : public BaseClass { \
71 : protected:\
72 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
73 : public:\
74 : CPPUNIT_TEST_SUITE(TestName); \
75 : CPPUNIT_TEST(Import); \
76 : CPPUNIT_TEST(Import_Export_Import); \
77 : CPPUNIT_TEST_SUITE_END(); \
78 : \
79 : void Import() { \
80 : executeImportTest(filename);\
81 : }\
82 : void Import_Export_Import() {\
83 : executeImportExportImportTest(filename);\
84 : }\
85 : void verify() SAL_OVERRIDE;\
86 : }; \
87 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
88 : void TestName::verify()
89 :
90 : #if 1
91 : #define DECLARE_OOXMLIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
92 : #define DECLARE_OOXMLEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
93 : #define DECLARE_RTFIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
94 : #define DECLARE_RTFEXPORT_TEST(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
95 : #define DECLARE_ODFIMPORT_TEST(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
96 :
97 : // For testing during development of a test, you want to use
98 : // DECLARE_OOXMLEXPORT_TEST_ONLY, and change the above to #if 0
99 : // Of course, don't forget to set back to #if 1 when you are done :-)
100 : #else
101 : #define DECLARE_OOXMLIMPORT_TEST_ONLY(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
102 : #define DECLARE_OOXMLEXPORT_TEST_ONLY(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
103 : #define DECLARE_RTFIMPORT_TEST_ONLY(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
104 : #define DECLARE_RTFEXPORT_TEST_ONLY(TestName, filename) DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, Test)
105 : #define DECLARE_ODFIMPORT_TEST_ONLY(TestName, filename) DECLARE_SW_IMPORT_TEST(TestName, filename, Test)
106 :
107 : #undef DECLARE_OOXMLEXPORT_TEST
108 : #define DECLARE_OOXMLIMPORT_TEST(TestName, filename) class disabled##TestName : public Test { void disabled(); }; void disabled##TestName::disabled()
109 : #define DECLARE_OOXMLEXPORT_TEST(TestName, filename) class disabled##TestName : public Test { void disabled(); }; void disabled##TestName::disabled()
110 : #define DECLARE_RTFIMPORT_TEST(TestName, filename) class disabled##TestName : public Test { void disabled(); }; void disabled##TestName::disabled()
111 : #define DECLARE_RTFEXPORT_TEST(TestName, filename) class disabled##TestName : public Test { void disabled(); }; void disabled##TestName::disabled()
112 : #define DECLARE_ODFIMPORT_TEST(TestName, filename) class disabled##TestName : public Test { void disabled(); }; void disabled##TestName::disabled()
113 : #endif
114 :
115 : #define DECLARE_SW_IMPORT_TEST(TestName, filename, BaseClass) \
116 : class TestName : public BaseClass { \
117 : protected:\
118 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
119 : public:\
120 : CPPUNIT_TEST_SUITE(TestName); \
121 : CPPUNIT_TEST(Import); \
122 : CPPUNIT_TEST_SUITE_END(); \
123 : \
124 : void Import() { \
125 : executeImportTest(filename);\
126 : }\
127 : void verify() SAL_OVERRIDE;\
128 : }; \
129 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
130 : void TestName::verify()
131 :
132 : #define DECLARE_SW_EXPORT_TEST(TestName, filename, BaseClass) \
133 : class TestName : public BaseClass { \
134 : protected:\
135 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
136 : public:\
137 : CPPUNIT_TEST_SUITE(TestName); \
138 : CPPUNIT_TEST(Import_Export); \
139 : CPPUNIT_TEST_SUITE_END(); \
140 : \
141 : void Import_Export() {\
142 : executeImportExport(filename);\
143 : }\
144 : void verify() SAL_OVERRIDE;\
145 : }; \
146 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
147 : void TestName::verify()
148 :
149 : #define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, BaseClass) \
150 : class TestName : public BaseClass { \
151 : protected: \
152 : virtual OUString getTestName() SAL_OVERRIDE { return OUString::createFromAscii(#TestName); } \
153 : public: \
154 : CPPUNIT_TEST_SUITE(TestName); \
155 : CPPUNIT_TEST(MailMerge); \
156 : CPPUNIT_TEST_SUITE_END(); \
157 : \
158 : void MailMerge() { \
159 : executeMailMergeTest(filename, datasource, tablename); \
160 : } \
161 : void verify() SAL_OVERRIDE; \
162 : }; \
163 : CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
164 : void TestName::verify()
165 :
166 : /**
167 : * Maps database URIs to the registered database names for quick lookups
168 : */
169 : typedef std::map<OUString, OUString> DBuriMap;
170 38 : DBuriMap aDBuriMap;
171 :
172 : /// Base class for filter tests loading or roundtriping a document, then asserting the document model.
173 : class SwModelTestBase : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
174 : {
175 : private:
176 : OUString maFilterOptions;
177 :
178 : protected:
179 : uno::Reference< lang::XComponent > mxComponent;
180 : uno::Reference< lang::XComponent > mxMMComponent;
181 : uno::Reference< com::sun::star::task::XJob > mxJob;
182 : uno::Sequence< beans::NamedValue > mSeqMailMergeArgs;
183 :
184 : xmlBufferPtr mpXmlBuffer;
185 : const char* mpTestDocumentPath;
186 : const char* mpFilter;
187 :
188 : template<typename T>
189 : struct MethodEntry
190 : {
191 : const char* pName;
192 : void (T::*pMethod)();
193 : };
194 :
195 : sal_uInt32 mnStartTime;
196 : utl::TempFile maTempFile;
197 : bool mbExported; ///< Does maTempFile already contain something useful?
198 : sal_Int16 nCurOutputType;
199 :
200 : protected:
201 0 : virtual OUString getTestName() { return OUString(); }
202 :
203 : public:
204 : OUString& getFilterOptions()
205 : {
206 : return maFilterOptions;
207 : }
208 22 : void setFilterOptions(const OUString &rFilterOptions)
209 : {
210 22 : maFilterOptions = rFilterOptions;
211 22 : }
212 :
213 2800 : SwModelTestBase(const char* pTestDocumentPath = "", const char* pFilter = "")
214 : : mpXmlBuffer(0)
215 : , mpTestDocumentPath(pTestDocumentPath)
216 : , mpFilter(pFilter)
217 : , mnStartTime(0)
218 : , mbExported(false)
219 2800 : , nCurOutputType(0)
220 : {
221 2800 : maTempFile.EnableKillingFile();
222 2800 : }
223 :
224 2800 : virtual ~SwModelTestBase()
225 2800 : {}
226 :
227 2800 : virtual void setUp() SAL_OVERRIDE
228 : {
229 2800 : test::BootstrapFixture::setUp();
230 :
231 2800 : mxDesktop.set(css::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
232 2800 : }
233 :
234 2800 : virtual void tearDown() SAL_OVERRIDE
235 : {
236 2800 : if (mxComponent.is())
237 2686 : mxComponent->dispose();
238 2800 : if (mxMMComponent.is())
239 : {
240 4 : if (nCurOutputType == text::MailMergeType::SHELL)
241 : {
242 4 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
243 4 : CPPUNIT_ASSERT(pTxtDoc);
244 4 : pTxtDoc->GetDocShell()->DoClose();
245 : }
246 : else
247 0 : mxMMComponent->dispose();
248 : }
249 :
250 2800 : test::BootstrapFixture::tearDown();
251 2800 : }
252 :
253 : protected:
254 : /**
255 : * Helper func used by each unit test to test the 'import' code.
256 : * (Loads the requested file and then calls 'verify' method)
257 : */
258 1760 : void executeImportTest(const char* filename)
259 : {
260 : // If the testcase is stored in some other format, it's pointless to test.
261 1760 : if (mustTestImportOf(filename))
262 : {
263 1646 : maTempFile.EnableKillingFile(false);
264 1646 : header();
265 1646 : preTest(filename);
266 1646 : load(mpTestDocumentPath, filename);
267 1646 : postTest(filename);
268 1646 : verify();
269 1646 : finish();
270 1646 : maTempFile.EnableKillingFile();
271 : }
272 1760 : }
273 :
274 : /**
275 : * Helper func used by each unit test to test the 'export' code.
276 : * (Loads the requested file, save it to temp file, load the
277 : * temp file and then calls 'verify' method)
278 : */
279 984 : void executeImportExportImportTest(const char* filename)
280 : {
281 984 : maTempFile.EnableKillingFile(false);
282 984 : header();
283 984 : preTest(filename);
284 984 : load(mpTestDocumentPath, filename);
285 984 : reload(mpFilter, filename);
286 984 : postTest(filename);
287 984 : verify();
288 984 : finish();
289 984 : maTempFile.EnableKillingFile();
290 984 : }
291 :
292 : /**
293 : * Helper func used by each unit test to test the 'export' code.
294 : * (Loads the requested file for document base (this represents
295 : * the initial document condition), exports with the desired
296 : * export filter and then calls 'verify' method)
297 : */
298 12 : void executeImportExport(const char* filename)
299 : {
300 12 : maTempFile.EnableKillingFile(false);
301 12 : header();
302 12 : preTest(filename);
303 12 : load(mpTestDocumentPath, filename);
304 12 : save(OUString::createFromAscii(mpFilter), maTempFile);
305 12 : maTempFile.EnableKillingFile(false);
306 12 : postTest(filename);
307 12 : verify();
308 12 : finish();
309 12 : maTempFile.EnableKillingFile();
310 12 : }
311 :
312 : /**
313 : * Helper func used by each unit test to test the 'mail merge' code.
314 : *
315 : * Registers the data source, loads the original file as reference,
316 : * initializes the mail merge job and its default argument sequence.
317 : *
318 : * The 'verify' method actually has to execute the mail merge by
319 : * calling executeMailMerge() after modifying the job arguments.
320 : */
321 4 : void executeMailMergeTest(const char* filename, const char* datasource, const char* tablename = 0)
322 : {
323 4 : header();
324 4 : preTest(filename);
325 4 : load(mpTestDocumentPath, filename);
326 :
327 4 : const OUString aPrefix( "LOMM_" );
328 8 : utl::TempFile aTempDir(nullptr, true);
329 8 : const OUString aWorkDir = aTempDir.GetURL();
330 8 : const OUString aURI( getURLFromSrc(mpTestDocumentPath) + OUString::createFromAscii(datasource) );
331 8 : OUString aDBName = registerDBsource( aURI, aPrefix, aWorkDir );
332 4 : initMailMergeJobAndArgs( filename, tablename, aDBName, aPrefix, aWorkDir );
333 :
334 4 : postTest(filename);
335 4 : verify();
336 4 : finish();
337 :
338 8 : ::utl::removeTree(aWorkDir);
339 4 : }
340 :
341 : /**
342 : * Function overloaded by unit test. See DECLARE_SW_*_TEST macros
343 : */
344 0 : virtual void verify()
345 : {
346 0 : CPPUNIT_FAIL( "verify method must be overridden" );
347 0 : }
348 :
349 : /**
350 : * Override this function if interested in skipping import test for this file
351 : */
352 776 : virtual bool mustTestImportOf(const char* /* filename */) const
353 : {
354 776 : return true;
355 : }
356 : /**
357 : * Override this function if some special filename-specific setup is needed
358 : */
359 1954 : virtual void preTest(const char* /*filename*/)
360 : {
361 1954 : }
362 :
363 : /**
364 : * Override this function if some special filename-specific teardown is needed
365 : */
366 1954 : virtual void postTest(const char* /*filename*/)
367 : {
368 1954 : }
369 :
370 : /**
371 : * Override this function if calcing layout is not needed
372 : */
373 3642 : virtual bool mustCalcLayoutOf(const char* /*filename*/)
374 : {
375 3642 : return true;
376 : }
377 :
378 : /**
379 : * Override this function if validation is wanted
380 : */
381 852 : virtual bool mustValidate(const char* /*filename*/) const
382 : {
383 852 : return false;
384 : }
385 :
386 : private:
387 66 : void dumpLayout()
388 : {
389 : // create the xml writer
390 66 : mpXmlBuffer = xmlBufferCreate();
391 66 : xmlTextWriterPtr pXmlWriter = xmlNewTextWriterMemory(mpXmlBuffer, 0);
392 66 : xmlTextWriterStartDocument(pXmlWriter, NULL, NULL, NULL);
393 :
394 : // create the dump
395 66 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
396 66 : CPPUNIT_ASSERT(pTxtDoc);
397 66 : SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
398 66 : SwRootFrm* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
399 66 : pLayout->dumpAsXml(pXmlWriter);
400 :
401 : // delete xml writer
402 66 : xmlTextWriterEndDocument(pXmlWriter);
403 66 : xmlFreeTextWriter(pXmlWriter);
404 66 : }
405 :
406 3666 : void calcLayout()
407 : {
408 3666 : SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
409 3666 : CPPUNIT_ASSERT(pTxtDoc);
410 3666 : SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
411 3666 : pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->CalcLayout();
412 3666 : }
413 :
414 : protected:
415 : /// Get the length of the whole document.
416 22 : int getLength()
417 : {
418 22 : uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
419 44 : uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
420 44 : uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
421 44 : OUStringBuffer aBuf;
422 68 : while (xParaEnum->hasMoreElements())
423 : {
424 24 : uno::Reference<container::XEnumerationAccess> xRangeEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
425 48 : uno::Reference<container::XEnumeration> xRangeEnum = xRangeEnumAccess->createEnumeration();
426 80 : while (xRangeEnum->hasMoreElements())
427 : {
428 32 : uno::Reference<text::XTextRange> xRange(xRangeEnum->nextElement(), uno::UNO_QUERY);
429 32 : aBuf.append(xRange->getString());
430 32 : }
431 24 : }
432 44 : return aBuf.getLength();
433 : }
434 :
435 : /// Get a family of styles, see com.sun.star.style.StyleFamilies for possible values.
436 214 : uno::Reference<container::XNameAccess> getStyles(const OUString& aFamily)
437 : {
438 214 : uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
439 428 : uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
440 214 : uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(aFamily), uno::UNO_QUERY);
441 428 : return xStyleFamily;
442 : }
443 :
444 : /// Get a family of auto styles, see com.sun.star.style.StyleFamilies for possible values.
445 4 : uno::Reference<style::XAutoStyleFamily> getAutoStyles(const OUString& aFamily)
446 : {
447 4 : uno::Reference< style::XAutoStylesSupplier > xAutoStylesSupplier(mxComponent, uno::UNO_QUERY);
448 8 : uno::Reference< style::XAutoStyles > xAutoStyles(xAutoStylesSupplier->getAutoStyles());
449 4 : uno::Reference< style::XAutoStyleFamily > xAutoStyleFamily(xAutoStyles->getByName(aFamily), uno::UNO_QUERY);
450 8 : return xAutoStyleFamily;
451 : }
452 :
453 : /**
454 : * Extract a value from the layout dump using an XPath expression and an attribute name.
455 : *
456 : * If the attribute is omitted, the text of the node is returned.
457 : */
458 242 : OUString parseDump(const OString& aXPath, const OString& aAttribute = OString())
459 : {
460 242 : if (!mpXmlBuffer)
461 66 : dumpLayout();
462 :
463 242 : xmlDocPtr pXmlDoc = xmlParseMemory((const char*)xmlBufferContent(mpXmlBuffer), xmlBufferLength(mpXmlBuffer));;
464 :
465 242 : xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
466 242 : xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
467 242 : xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
468 242 : CPPUNIT_ASSERT_EQUAL_MESSAGE("parsing dump failed", 1, xmlXPathNodeSetGetLength(pXmlNodes));
469 242 : xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
470 242 : OUString aRet;
471 242 : if (aAttribute.getLength())
472 52 : aRet = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
473 : else
474 190 : aRet = OUString::createFromAscii((const char*)XML_GET_CONTENT(pXmlNode));
475 :
476 242 : xmlFreeDoc(pXmlDoc);
477 :
478 242 : return aRet;
479 : }
480 :
481 : template< typename T >
482 186 : T getProperty( const uno::Any& obj, const OUString& name ) const
483 : {
484 186 : uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY_THROW );
485 186 : T data = T();
486 186 : if (!(properties->getPropertyValue(name) >>= data))
487 : {
488 0 : CPPUNIT_FAIL("the property is of unexpected type or void");
489 : }
490 186 : return data;
491 : }
492 :
493 : template< typename T >
494 2824 : T getProperty( const uno::Reference< uno::XInterface >& obj, const OUString& name ) const
495 : {
496 2824 : uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY_THROW );
497 2824 : T data = T();
498 2824 : if (!(properties->getPropertyValue(name) >>= data))
499 : {
500 0 : CPPUNIT_FAIL("the property is of unexpected type or void");
501 : }
502 2824 : return data;
503 : }
504 :
505 36 : bool hasProperty(const uno::Reference<uno::XInterface>& obj, const OUString& name) const
506 : {
507 36 : uno::Reference<beans::XPropertySet> properties(obj, uno::UNO_QUERY_THROW);
508 36 : return properties->getPropertySetInfo()->hasPropertyByName(name);
509 : }
510 :
511 : /// Get number of paragraphs of the document.
512 4 : int getParagraphs()
513 : {
514 4 : uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
515 8 : uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
516 8 : uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
517 4 : int nRet = 0;
518 14 : while (xParaEnum->hasMoreElements())
519 : {
520 6 : xParaEnum->nextElement();
521 6 : nRet++;
522 : }
523 8 : return nRet;
524 : }
525 :
526 1100 : uno::Reference<text::XTextContent> getParagraphOrTable(int number, uno::Reference<text::XText> xText = uno::Reference<text::XText>()) const
527 : {
528 1100 : uno::Reference<container::XEnumerationAccess> paraEnumAccess;
529 1100 : if (xText.is())
530 170 : paraEnumAccess.set(xText, uno::UNO_QUERY);
531 : else
532 : {
533 930 : uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
534 930 : paraEnumAccess.set(textDocument->getText(), uno::UNO_QUERY);
535 : }
536 2200 : uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
537 2718 : for( int i = 1;
538 : i < number;
539 : ++i )
540 1618 : paraEnum->nextElement();
541 1100 : uno::Reference< text::XTextContent> const xElem(paraEnum->nextElement(),
542 1100 : uno::UNO_QUERY_THROW);
543 2198 : return xElem;
544 : }
545 :
546 : // Get paragraph (counted from 1), optionally check it contains the given text.
547 910 : uno::Reference< text::XTextRange > getParagraph( int number, const OUString& content = OUString() ) const
548 : {
549 : uno::Reference<text::XTextRange> const xParagraph(
550 910 : getParagraphOrTable(number), uno::UNO_QUERY_THROW);
551 908 : if( !content.isEmpty())
552 100 : CPPUNIT_ASSERT_EQUAL_MESSAGE( "paragraph does not have expected content", content, xParagraph->getString());
553 908 : return xParagraph;
554 : }
555 :
556 154 : uno::Reference<text::XTextRange> getParagraphOfText(int number, uno::Reference<text::XText> xText, const OUString& content = OUString()) const
557 : {
558 154 : uno::Reference<text::XTextRange> const xParagraph(getParagraphOrTable(number, xText), uno::UNO_QUERY_THROW);
559 154 : if (!content.isEmpty())
560 20 : CPPUNIT_ASSERT_EQUAL_MESSAGE( "paragraph does not contain expected content", content, xParagraph->getString());
561 154 : return xParagraph;
562 : }
563 :
564 : /// Get run (counted from 1) of a paragraph, optionally check it contains the given text.
565 824 : uno::Reference<text::XTextRange> getRun(uno::Reference<text::XTextRange> xParagraph, int number, const OUString& content = OUString()) const
566 : {
567 824 : uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
568 1648 : uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
569 1820 : for (int i = 1; i < number; ++i)
570 996 : xRunEnum->nextElement();
571 824 : uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
572 824 : if( !content.isEmpty())
573 78 : CPPUNIT_ASSERT_EQUAL_MESSAGE( "run does not contain expected content", content, xRun->getString());
574 1648 : return xRun;
575 : }
576 :
577 : /// Get math formula string of a run.
578 228 : OUString getFormula(uno::Reference<text::XTextRange> xRun) const
579 : {
580 228 : uno::Reference<container::XContentEnumerationAccess> xContentEnumAccess(xRun, uno::UNO_QUERY);
581 456 : uno::Reference<container::XEnumeration> xContentEnum(xContentEnumAccess->createContentEnumeration(""), uno::UNO_QUERY);
582 456 : uno::Reference<beans::XPropertySet> xFormula(xContentEnum->nextElement(), uno::UNO_QUERY);
583 456 : return getProperty<OUString>(getProperty< uno::Reference<beans::XPropertySet> >(xFormula, "Model"), "Formula");
584 : }
585 :
586 : /// get cell of a table; table can be retrieved with getParagraphOrTable
587 14 : uno::Reference<table::XCell> getCell(
588 : uno::Reference<uno::XInterface> const& xTableIfc,
589 : OUString const& rCell, OUString const& rContent = OUString())
590 : {
591 : uno::Reference<text::XTextTable> const xTable(xTableIfc,
592 14 : uno::UNO_QUERY_THROW);
593 : uno::Reference<table::XCell> const xCell(
594 14 : xTable->getCellByName(rCell), uno::UNO_SET_THROW);
595 14 : if (!rContent.isEmpty())
596 : {
597 : uno::Reference<text::XText> const xCellText(xCell,
598 6 : uno::UNO_QUERY_THROW);
599 6 : CPPUNIT_ASSERT_EQUAL_MESSAGE("cell does not contain expected content", rContent, xCellText->getString());
600 : }
601 14 : return xCell;
602 : }
603 :
604 : /// Get shape (counted from 1)
605 632 : uno::Reference<drawing::XShape> getShape(int number)
606 : {
607 632 : uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
608 1264 : uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
609 632 : uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(number - 1), uno::UNO_QUERY);
610 1258 : return xShape;
611 : }
612 :
613 : /// Get shape by name
614 12 : uno::Reference<drawing::XShape> getShapeByName(const OUString& aName)
615 : {
616 12 : uno::Reference<drawing::XShape> xRet;
617 :
618 24 : uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
619 24 : uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
620 24 : for (sal_Int32 i = 0; i < xDrawPage->getCount(); ++i)
621 : {
622 24 : uno::Reference<container::XNamed> xShape(xDrawPage->getByIndex(i), uno::UNO_QUERY);
623 24 : if (xShape->getName() == aName)
624 : {
625 12 : xRet.set(xShape, uno::UNO_QUERY);
626 12 : break;
627 : }
628 12 : }
629 :
630 24 : return xRet;
631 : }
632 : /// Get TextFrame by name
633 12 : uno::Reference<drawing::XShape> getTextFrameByName(const OUString& aName)
634 : {
635 12 : uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
636 24 : uno::Reference<container::XNameAccess> xNameAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
637 12 : uno::Reference<drawing::XShape> xShape(xNameAccess->getByName(aName), uno::UNO_QUERY);
638 24 : return xShape;
639 : }
640 :
641 2646 : void header()
642 : {
643 2646 : std::cout << "File tested,Execution Time (ms)" << std::endl;
644 2646 : }
645 :
646 2686 : void load(const char* pDir, const char* pName)
647 : {
648 2686 : if (mxComponent.is())
649 0 : mxComponent->dispose();
650 : // Output name early, so in the case of a hang, the name of the hanging input file is visible.
651 2686 : std::cout << pName << ",";
652 2686 : mnStartTime = osl_getGlobalTimer();
653 2686 : mxComponent = loadFromDesktop(getURLFromSrc(pDir) + OUString::createFromAscii(pName), "com.sun.star.text.TextDocument");
654 2686 : if (mustCalcLayoutOf(pName))
655 2684 : calcLayout();
656 2686 : }
657 :
658 984 : void reload(const char* pFilter, const char* filename)
659 : {
660 984 : uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
661 1968 : OUString aFilterName = OUString::createFromAscii(pFilter);
662 1968 : utl::MediaDescriptor aMediaDescriptor;
663 984 : aMediaDescriptor["FilterName"] <<= aFilterName;
664 984 : if (!maFilterOptions.isEmpty())
665 0 : aMediaDescriptor["FilterOptions"] <<= maFilterOptions;
666 984 : xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
667 1968 : uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY);
668 984 : xComponent->dispose();
669 984 : mbExported = true;
670 984 : mxComponent = loadFromDesktop(maTempFile.GetURL(), "com.sun.star.text.TextDocument");
671 984 : if (mustValidate(filename))
672 : {
673 10 : if(aFilterName == "Office Open XML Text")
674 : {
675 : // too many validation errors right now
676 10 : validate(maTempFile.GetFileName(), test::OOXML);
677 : }
678 0 : else if(aFilterName == "writer8")
679 : {
680 : // still a few validation errors
681 0 : validate(maTempFile.GetFileName(), test::ODF);
682 : }
683 : }
684 :
685 984 : if (mpXmlBuffer)
686 : {
687 0 : xmlBufferFree(mpXmlBuffer);
688 0 : mpXmlBuffer = 0;
689 : }
690 984 : if (mustCalcLayoutOf(filename))
691 1966 : calcLayout();
692 984 : }
693 :
694 : /// Save the loaded document to a tempfile. Can be used to check the resulting docx/odt directly as a ZIP file.
695 14 : void save(const OUString& aFilterName, utl::TempFile& rTempFile)
696 : {
697 14 : rTempFile.EnableKillingFile();
698 14 : uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
699 28 : utl::MediaDescriptor aMediaDescriptor;
700 14 : aMediaDescriptor["FilterName"] <<= aFilterName;
701 14 : if (!maFilterOptions.isEmpty())
702 6 : aMediaDescriptor["FilterOptions"] <<= maFilterOptions;
703 28 : xStorable->storeToURL(rTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
704 14 : }
705 :
706 2646 : void finish()
707 : {
708 2646 : sal_uInt32 nEndTime = osl_getGlobalTimer();
709 2646 : std::cout << (nEndTime - mnStartTime) << std::endl;
710 2646 : if (mpXmlBuffer)
711 : {
712 66 : xmlBufferFree(mpXmlBuffer);
713 66 : mpXmlBuffer = 0;
714 : }
715 2646 : }
716 :
717 : /// Get page count.
718 32 : int getPages()
719 : {
720 32 : uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
721 64 : uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
722 64 : uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
723 32 : xCursor->jumpToLastPage();
724 64 : return xCursor->getPage();
725 : }
726 :
727 : /**
728 : * Given that some problem doesn't affect the result in the importer, we
729 : * test the resulting file directly, by opening the zip file, parsing an
730 : * xml stream, and asserting an XPath expression. This method returns the
731 : * xml stream, so that you can do the asserting.
732 : */
733 1068 : xmlDocPtr parseExport(const OUString& rStreamName = OUString("word/document.xml"))
734 : {
735 1068 : if (!mbExported)
736 518 : return 0;
737 :
738 : // Read the XML stream we're interested in.
739 550 : uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), maTempFile.GetURL());
740 1100 : uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(rStreamName), uno::UNO_QUERY);
741 1100 : boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
742 :
743 550 : xmlDocPtr pXmlDoc = parseXmlStream(pStream.get());
744 550 : pXmlDoc->name = reinterpret_cast<char *>(xmlStrdup(reinterpret_cast<xmlChar const *>(OUStringToOString(maTempFile.GetURL(), RTL_TEXTENCODING_UTF8).getStr())));
745 1100 : return pXmlDoc;
746 : }
747 :
748 : /**
749 : * Helper method to return nodes represented by rXPath.
750 : */
751 2230 : virtual void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) SAL_OVERRIDE
752 : {
753 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
754 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("v"), BAD_CAST("urn:schemas-microsoft-com:vml"));
755 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("mc"), BAD_CAST("http://schemas.openxmlformats.org/markup-compatibility/2006"));
756 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wps"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingShape"));
757 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wpg"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"));
758 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wp"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"));
759 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wp14"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"));
760 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("a"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/main"));
761 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("pic"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/picture"));
762 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("rels"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/relationships"));
763 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w14"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordml"));
764 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("m"), BAD_CAST("http://schemas.openxmlformats.org/officeDocument/2006/math"));
765 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("ContentType"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/content-types"));
766 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("lc"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas"));
767 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("extended-properties"), BAD_CAST("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"));
768 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("a14"), BAD_CAST("http://schemas.microsoft.com/office/drawing/2010/main"));
769 2230 : xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("o"), BAD_CAST("urn:schemas-microsoft-com:office:office"));
770 2230 : }
771 :
772 4 : virtual OUString registerDBsource( const OUString &aURI, const OUString &aPrefix, const OUString &aWorkDir )
773 : {
774 4 : OUString aDBName;
775 4 : DBuriMap::const_iterator pos = aDBuriMap.find( aURI );
776 4 : if (pos == aDBuriMap.end())
777 : {
778 4 : aDBName = SwDBManager::LoadAndRegisterDataSource( aURI, &aPrefix, &aWorkDir );
779 4 : aDBuriMap.insert( std::pair< OUString, OUString >( aURI, aDBName ) );
780 4 : std::cout << "New datasource name: '" << aDBName << "'" << std::endl;
781 : }
782 : else
783 : {
784 0 : aDBName = pos->second;
785 0 : std::cout << "Old datasource name: '" << aDBName << "'" << std::endl;
786 : }
787 4 : CPPUNIT_ASSERT(!aDBName.isEmpty());
788 4 : return aDBName;
789 : }
790 :
791 4 : virtual void initMailMergeJobAndArgs( const char* filename, const char* tablename, const OUString &aDBName,
792 : const OUString &aPrefix, const OUString &aWorkDir )
793 : {
794 4 : uno::Reference< task::XJob > xJob( getMultiServiceFactory()->createInstance( "com.sun.star.text.MailMerge" ), uno::UNO_QUERY_THROW );
795 4 : mxJob.set( xJob );
796 :
797 4 : int seq_id = 5;
798 4 : if (tablename) seq_id += 2;
799 4 : mSeqMailMergeArgs.realloc( seq_id );
800 :
801 4 : seq_id = 0;
802 4 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_OUTPUT_TYPE ), uno::Any( text::MailMergeType::SHELL ) );
803 8 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_DOCUMENT_URL ), uno::Any(
804 12 : ( OUString(getURLFromSrc(mpTestDocumentPath) + OUString::createFromAscii(filename)) ) ) );
805 4 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_DATA_SOURCE_NAME ), uno::Any( aDBName ) );
806 4 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_OUTPUT_URL ), uno::Any( aWorkDir ) );
807 4 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_FILE_NAME_PREFIX ), uno::Any( aPrefix ));
808 4 : if (tablename)
809 : {
810 4 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_DAD_COMMAND_TYPE ), uno::Any( sdb::CommandType::TABLE ) );
811 4 : mSeqMailMergeArgs[ seq_id++ ] = beans::NamedValue( OUString( UNO_NAME_DAD_COMMAND ), uno::Any( OUString::createFromAscii(tablename) ) );
812 4 : }
813 4 : }
814 :
815 4 : virtual void executeMailMerge()
816 : {
817 4 : uno::Any res = mxJob->execute( mSeqMailMergeArgs );
818 :
819 8 : OUString aCurOutputURL;
820 8 : OUString aCurFileNamePrefix;
821 4 : const beans::NamedValue *pArguments = mSeqMailMergeArgs.getConstArray();
822 4 : bool bOk = true;
823 4 : sal_Int32 nArgs = mSeqMailMergeArgs.getLength();
824 :
825 32 : for (sal_Int32 i = 0; i < nArgs; ++i) {
826 28 : const OUString &rName = pArguments[i].Name;
827 28 : const uno::Any &rValue = pArguments[i].Value;
828 :
829 : // all error checking was already done by the MM job execution
830 28 : if (rName == UNO_NAME_OUTPUT_URL)
831 4 : bOk &= rValue >>= aCurOutputURL;
832 24 : else if (rName == UNO_NAME_FILE_NAME_PREFIX)
833 4 : bOk &= rValue >>= aCurFileNamePrefix;
834 20 : else if (rName == UNO_NAME_OUTPUT_TYPE)
835 4 : bOk &= rValue >>= nCurOutputType;
836 : }
837 :
838 4 : CPPUNIT_ASSERT(bOk);
839 :
840 4 : if (nCurOutputType == text::MailMergeType::SHELL)
841 : {
842 4 : CPPUNIT_ASSERT(res >>= mxMMComponent);
843 4 : CPPUNIT_ASSERT(mxMMComponent.is());
844 : }
845 : else
846 : {
847 0 : CPPUNIT_ASSERT(res == true);
848 0 : mxMMComponent = loadFromDesktop( aCurOutputURL + "/" + aCurFileNamePrefix + "0.odt",
849 0 : "com.sun.star.text.TextDocument");
850 0 : CPPUNIT_ASSERT(mxMMComponent.is());
851 0 : calcLayout();
852 4 : }
853 4 : }
854 : };
855 :
856 : /**
857 : * Test whether the expected and actual borderline parameters are equal
858 : * and assert if not.
859 : *
860 : * @param[in] rExpected expected borderline object
861 : * @param[in] rActual actual borderline object
862 : * @param[in] rSourceLine line from where the assertion is called
863 : * Note: This method is the implementatition of CPPUNIT_ASSERT_BORDER_EQUAL, so
864 : * use that macro instead.
865 : **/
866 252 : inline void assertBorderEqual(
867 : const table::BorderLine2& rExpected, const table::BorderLine2& rActual,
868 : const CppUnit::SourceLine& rSourceLine )
869 : {
870 252 : CPPUNIT_NS::assertEquals<util::Color>( rExpected.Color, rActual.Color, rSourceLine, "different Color" );
871 252 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.InnerLineWidth, rActual.InnerLineWidth, rSourceLine, "different InnerLineWidth" );
872 252 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.OuterLineWidth, rActual.OuterLineWidth, rSourceLine, "different OuterLineWidth" );
873 252 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.LineDistance, rActual.LineDistance, rSourceLine, "different LineDistance" );
874 252 : CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.LineStyle, rActual.LineStyle, rSourceLine, "different LineStyle" );
875 252 : CPPUNIT_NS::assertEquals<sal_Int32>( rExpected.LineWidth, rActual.LineWidth, rSourceLine, "different LineWidth" );
876 252 : }
877 :
878 : #define CPPUNIT_ASSERT_BORDER_EQUAL(aExpected, aActual) \
879 : assertBorderEqual( aExpected, aActual, CPPUNIT_SOURCELINE() ) \
880 :
881 : #endif // INCLUDED_SW_QA_EXTRAS_INC_SWMODELTESTBASE_HXX
882 :
883 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|