1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <sal/config.h>
15 #include <swmodeltestbase.hxx>
17 #include <com/sun/star/text/MailMergeType.hpp>
18 #include <com/sun/star/sdb/CommandType.hpp>
19 #include <com/sun/star/table/TableBorder.hpp>
20 #include <com/sun/star/text/TextContentAnchorType.hpp>
21 #include <com/sun/star/text/XTextTable.hpp>
22 #include <com/sun/star/sdbc/XRowSet.hpp>
23 #include <com/sun/star/sdbcx/XRowLocate.hpp>
24 #include <com/sun/star/task/XJob.hpp>
26 #include <tools/urlobj.hxx>
27 #include <comphelper/sequence.hxx>
31 #include <pagefrm.hxx>
32 #include <unoprnms.hxx>
34 #include <unotxdoc.hxx>
36 #include <IDocumentLayoutAccess.hxx>
37 #include <rootfrm.hxx>
40 * Maps database URIs to the registered database names for quick lookups
42 typedef std::map
<OUString
, OUString
> DBuriMap
;
43 static DBuriMap aDBuriMap
;
45 class MMTest
: public SwModelTestBase
50 virtual void tearDown() override
52 if (mxMMComponent
.is())
54 if (mnCurOutputType
== text::MailMergeType::SHELL
)
56 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
57 CPPUNIT_ASSERT(pTextDoc
);
58 pTextDoc
->GetDocShell()->DoClose();
61 mxMMComponent
->dispose();
63 if (mxCurResultSet
.is())
65 css::uno::Reference
<css::lang::XComponent
>(
66 mxCurResultSet
, css::uno::UNO_QUERY_THROW
)->dispose();
68 SwModelTestBase::tearDown();
72 * Helper func used by each unit test to test the 'mail merge' code.
74 * Registers the data source, loads the original file as reference,
75 * initializes the mail merge job and its default argument sequence.
77 * The 'verify' method actually has to execute the mail merge by
78 * calling executeMailMerge() after modifying the job arguments.
80 void executeMailMergeTest( const char* filename
, const char* datasource
, const char* tablename
,
81 char const*const filter
, int selection
, const char* column
)
83 maMMtestFilename
= filename
;
87 utl::TempFileNamed
aTempDir(nullptr, true);
88 aTempDir
.EnableKillingFile();
89 const OUString aWorkDir
= aTempDir
.GetURL();
90 const OUString
aURI( createFileURL(OUString::createFromAscii(datasource
) ) );
91 const OUString aPrefix
= column
? OUString::createFromAscii( column
) : "LOMM_";
92 const OUString aDBName
= registerDBsource( aURI
, aWorkDir
);
93 initMailMergeJobAndArgs( filename
, tablename
, aDBName
, aPrefix
, aWorkDir
, filter
, selection
, column
!= nullptr );
101 OUString
registerDBsource( const OUString
&aURI
, const OUString
&aWorkDir
)
104 DBuriMap::const_iterator pos
= aDBuriMap
.find( aURI
);
105 if (pos
== aDBuriMap
.end())
107 aDBName
= SwDBManager::LoadAndRegisterDataSource( aURI
, &aWorkDir
);
108 aDBuriMap
.insert( std::pair
< OUString
, OUString
>( aURI
, aDBName
) );
109 std::cout
<< "New datasource name: '" << aDBName
<< "'" << std::endl
;
113 aDBName
= pos
->second
;
114 std::cout
<< "Old datasource name: '" << aDBName
<< "'" << std::endl
;
116 CPPUNIT_ASSERT(!aDBName
.isEmpty());
120 uno::Reference
< sdbc::XRowSet
> getXResultFromDataset( const char* tablename
, const OUString
&aDBName
)
122 uno::Reference
< sdbc::XRowSet
> xCurResultSet
;
123 uno::Reference
< uno::XInterface
> xInstance
= getMultiServiceFactory()->createInstance( "com.sun.star.sdb.RowSet" );
124 uno::Reference
< beans::XPropertySet
> xRowSetPropSet( xInstance
, uno::UNO_QUERY
);
125 assert( xRowSetPropSet
.is() && "failed to get XPropertySet interface from RowSet" );
126 if (xRowSetPropSet
.is())
128 xRowSetPropSet
->setPropertyValue( "DataSourceName", uno::Any( aDBName
) );
129 xRowSetPropSet
->setPropertyValue( "Command", uno::Any( OUString::createFromAscii(tablename
) ) );
130 xRowSetPropSet
->setPropertyValue( "CommandType", uno::Any( sdb::CommandType::TABLE
) );
132 uno::Reference
< sdbc::XRowSet
> xRowSet( xInstance
, uno::UNO_QUERY
);
134 xRowSet
->execute(); // build ResultSet from properties
135 xCurResultSet
= xRowSet
;
136 assert( xCurResultSet
.is() && "failed to build ResultSet" );
138 return xCurResultSet
;
141 void initMailMergeJobAndArgs( const char* filename
, const char* tablename
, const OUString
&aDBName
,
142 const OUString
&aPrefix
, const OUString
&aWorkDir
,
143 char const*const filter
, int nDataSets
,
144 const bool bPrefixIsColumn
)
146 uno::Reference
< task::XJob
> xJob( getMultiServiceFactory()->createInstance( "com.sun.star.text.MailMerge" ), uno::UNO_QUERY_THROW
);
149 mMMargs
.reserve( 15 );
151 mMMargs
.emplace_back( UNO_NAME_OUTPUT_TYPE
, uno::Any( filter
? text::MailMergeType::FILE : text::MailMergeType::SHELL
) );
152 mMMargs
.emplace_back( UNO_NAME_DOCUMENT_URL
, uno::Any(
153 ( createFileURL(OUString::createFromAscii(filename
)) ) ) );
154 mMMargs
.emplace_back( UNO_NAME_DATA_SOURCE_NAME
, uno::Any( aDBName
) );
155 mMMargs
.emplace_back( UNO_NAME_OUTPUT_URL
, uno::Any( aWorkDir
) );
158 mMMargs
.emplace_back( UNO_NAME_FILE_NAME_PREFIX
, uno::Any( aPrefix
) );
159 mMMargs
.emplace_back(UNO_NAME_SAVE_FILTER
, uno::Any(OUString::createFromAscii(filter
)));
163 mMMargs
.emplace_back( UNO_NAME_FILE_NAME_FROM_COLUMN
, uno::Any( true ) );
167 mMMargs
.emplace_back( UNO_NAME_DAD_COMMAND_TYPE
, uno::Any( sdb::CommandType::TABLE
) );
168 mMMargs
.emplace_back( UNO_NAME_DAD_COMMAND
, uno::Any( OUString::createFromAscii(tablename
) ) );
173 mxCurResultSet
= getXResultFromDataset( tablename
, aDBName
);
174 uno::Reference
< sdbcx::XRowLocate
> xCurRowLocate( mxCurResultSet
, uno::UNO_QUERY
);
175 mMMargs
.emplace_back( UNO_NAME_RESULT_SET
, uno::Any( mxCurResultSet
) );
176 std::vector
< uno::Any
> vResult
;
177 vResult
.reserve( nDataSets
);
179 for (i
= 0, mxCurResultSet
->first(); i
< nDataSets
; i
++, mxCurResultSet
->next())
181 vResult
.emplace_back( xCurRowLocate
->getBookmark() );
183 mMMargs
.emplace_back( UNO_NAME_SELECTION
, uno::Any( comphelper::containerToSequence(vResult
) ) );
188 void executeMailMerge( bool bDontLoadResult
= false )
190 const uno::Sequence
< beans::NamedValue
> aSeqMailMergeArgs
= comphelper::containerToSequence( mMMargs
);
191 uno::Any res
= mxJob
->execute( aSeqMailMergeArgs
);
194 bool bMMFilenameFromColumn
= false;
196 for (const beans::NamedValue
& rArgument
: aSeqMailMergeArgs
) {
197 const OUString
&rName
= rArgument
.Name
;
198 const uno::Any
&rValue
= rArgument
.Value
;
200 // all error checking was already done by the MM job execution
201 if (rName
== UNO_NAME_OUTPUT_URL
)
202 bOk
&= rValue
>>= msMailMergeOutputURL
;
203 else if (rName
== UNO_NAME_FILE_NAME_PREFIX
)
204 bOk
&= rValue
>>= msMailMergeOutputPrefix
;
205 else if (rName
== UNO_NAME_OUTPUT_TYPE
)
206 bOk
&= rValue
>>= mnCurOutputType
;
207 else if (rName
== UNO_NAME_FILE_NAME_FROM_COLUMN
)
208 bOk
&= rValue
>>= bMMFilenameFromColumn
;
209 else if (rName
== UNO_NAME_DOCUMENT_URL
)
210 bOk
&= rValue
>>= msMailMergeDocumentURL
;
215 // MM via UNO just works with file names. If we load the file on
216 // Windows before MM uses it, MM won't work, as it's already open.
217 // Don't move the load before the mail merge execution!
218 // (see gb_CppunitTest_use_instdir_configuration)
219 createSwDoc(maMMtestFilename
);
221 if (mnCurOutputType
== text::MailMergeType::SHELL
)
223 CPPUNIT_ASSERT(res
>>= mxMMComponent
);
224 CPPUNIT_ASSERT(mxMMComponent
.is());
228 CPPUNIT_ASSERT_EQUAL(uno::Any(true), res
);
229 if( !bMMFilenameFromColumn
&& !bDontLoadResult
)
230 loadMailMergeDocument( 0 );
235 * Like parseExport(), but for given mail merge document.
237 xmlDocUniquePtr
parseMailMergeExport(const OUString
& rStreamName
)
239 if (mnCurOutputType
!= text::MailMergeType::FILE)
242 OUString name
= msMailMergeOutputPrefix
+ OUString::number( 0 ) + ".odt";
243 std::unique_ptr
<SvStream
> pStream(parseExportStream(msMailMergeOutputURL
+ "/" + name
, rStreamName
));
245 return parseXmlStream(pStream
.get());
248 void loadMailMergeDocument( const OUString
&filename
)
250 assert( mnCurOutputType
== text::MailMergeType::FILE );
251 if (mxComponent
.is())
252 mxComponent
->dispose();
253 // Output name early, so in the case of a hang, the name of the hanging input file is visible.
254 std::cout
<< filename
<< ",";
255 mnStartTime
= osl_getGlobalTimer();
256 mxComponent
= loadFromDesktop(msMailMergeOutputURL
+ "/" + filename
, "com.sun.star.text.TextDocument");
257 discardDumpedLayout();
262 Loads number-th document from mail merge. Requires file output from mail merge.
264 void loadMailMergeDocument(int number
, char const*const ext
= ".odt")
267 if (!msMailMergeOutputPrefix
.isEmpty())
268 name
= msMailMergeOutputPrefix
;
271 INetURLObject aURLObj
;
272 aURLObj
.SetSmartProtocol( INetProtocol::File
);
273 aURLObj
.SetSmartURL( msMailMergeDocumentURL
);
274 name
= aURLObj
.GetBase();
276 name
+= OUString::number(number
) + OStringToOUString(std::string_view(ext
, strlen(ext
)), RTL_TEXTENCODING_ASCII_US
);
277 loadMailMergeDocument( name
);
281 Resets currently opened layout of the original template,
282 and creates the layout of the document with N mails inside
283 (result run with text::MailMergeType::SHELL)
287 mpXmlBuffer
= xmlBufferPtr();
288 dumpLayout(mxMMComponent
);
292 // Returns page number of the first page of a MM document inside the large MM document (used in the SHELL case).
293 int documentStartPageNumber( int document
) const;
295 uno::Reference
< css::task::XJob
> mxJob
;
296 std::vector
< beans::NamedValue
> mMMargs
;
297 OUString msMailMergeDocumentURL
;
298 OUString msMailMergeOutputURL
;
299 OUString msMailMergeOutputPrefix
;
300 sal_Int16 mnCurOutputType
;
301 uno::Reference
< lang::XComponent
> mxMMComponent
;
302 uno::Reference
< sdbc::XRowSet
> mxCurResultSet
;
303 const char* maMMtestFilename
;
306 #define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, filter, BaseClass, selection, column) \
307 class TestName : public BaseClass { \
309 virtual OUString getTestName() override { return #TestName; } \
311 CPPUNIT_TEST_SUITE(TestName); \
312 CPPUNIT_TEST(MailMerge); \
313 CPPUNIT_TEST_SUITE_END(); \
316 executeMailMergeTest(filename, datasource, tablename, filter, selection, column); \
318 void verify() override; \
320 CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
321 void TestName::verify()
323 // Will generate the resulting document in mxMMDocument.
324 #define DECLARE_SHELL_MAILMERGE_TEST(TestName, filename, datasource, tablename) \
325 DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, nullptr, MMTest, 0, nullptr)
327 // Will generate documents as files, use loadMailMergeDocument().
328 #define DECLARE_FILE_MAILMERGE_TEST(TestName, filename, datasource, tablename) \
329 DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, "writer8", MMTest, 0, nullptr)
331 #define DECLARE_SHELL_MAILMERGE_TEST_SELECTION(TestName, filename, datasource, tablename, selection) \
332 DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, nullptr, MMTest, selection, nullptr)
334 #define DECLARE_FILE_MAILMERGE_TEST_COLUMN(TestName, filename, datasource, tablename, column) \
335 DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, "writer8", MMTest, 0, column)
337 int MMTest::documentStartPageNumber( int document
) const
338 { // See documentStartPageNumber() .
339 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
340 CPPUNIT_ASSERT(pTextDoc
);
341 SwWrtShell
* shell
= pTextDoc
->GetDocShell()->GetWrtShell();
342 IDocumentMarkAccess
* marks
= shell
->GetDoc()->getIDocumentMarkAccess();
343 // Unfortunately, the pages are marked using UNO bookmarks, which have internals names, so they cannot be referred to by their names.
344 // Assume that there are no other UNO bookmarks than the ones used by mail merge, and that they are in the sorted order.
345 IDocumentMarkAccess::const_iterator_t mark
;
347 for( mark
= marks
->getAllMarksBegin(); mark
!= marks
->getAllMarksEnd() && pos
< document
; ++mark
)
349 if( IDocumentMarkAccess::GetType( **mark
) == IDocumentMarkAccess::MarkType::UNO_BOOKMARK
)
352 CPPUNIT_ASSERT_EQUAL(document
, pos
);
353 sal_uInt16 page
, dummy
;
355 shell
->GotoMark( *mark
);
356 shell
->GetPageNum( page
, dummy
);
357 shell
->Pop(SwCursorShell::PopMode::DeleteCurrent
);
362 : SwModelTestBase("/sw/qa/extras/mailmerge/data/", "writer8")
364 , maMMtestFilename(nullptr)
368 DECLARE_SHELL_MAILMERGE_TEST(testMultiPageAnchoredDraws
, "multiple-page-anchored-draws.odt", "4_v01.ods", "Tabelle1")
372 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
373 CPPUNIT_ASSERT(pTextDoc
);
374 sal_uInt16 nPhysPages
= pTextDoc
->GetDocShell()->GetWrtShell()->GetPhyPageNum();
375 CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), nPhysPages
);
377 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxMMComponent
, uno::UNO_QUERY
);
378 uno::Reference
<container::XIndexAccess
> xDraws
= xDrawPageSupplier
->getDrawPage();
379 CPPUNIT_ASSERT_EQUAL(sal_Int32(8), xDraws
->getCount());
381 std::set
<sal_uInt16
> pages
;
382 uno::Reference
<beans::XPropertySet
> xPropertySet
;
384 for (sal_Int32 i
= 0; i
< xDraws
->getCount(); i
++)
386 xPropertySet
.set(xDraws
->getByIndex(i
), uno::UNO_QUERY
);
388 text::TextContentAnchorType nAnchorType
;
389 CPPUNIT_ASSERT(xPropertySet
->getPropertyValue( UNO_NAME_ANCHOR_TYPE
) >>= nAnchorType
);
390 CPPUNIT_ASSERT_EQUAL( text::TextContentAnchorType_AT_PAGE
, nAnchorType
);
392 sal_uInt16 nAnchorPageNo
= {};
393 CPPUNIT_ASSERT(xPropertySet
->getPropertyValue( UNO_NAME_ANCHOR_PAGE_NO
) >>= nAnchorPageNo
);
394 // are all shapes are on different page numbers?
395 CPPUNIT_ASSERT(pages
.insert(nAnchorPageNo
).second
);
399 DECLARE_FILE_MAILMERGE_TEST(testMissingDefaultLineColor
, "missing-default-line-color.ott", "one-empty-address.ods", "one-empty-address")
402 // The document was created by LO version which didn't write out the default value for line color
403 // (see XMLGraphicsDefaultStyle::SetDefaults()).
404 uno::Reference
<beans::XPropertySet
> xPropertySet(getShape(5), uno::UNO_QUERY
);
405 // Lines do not have a line color.
406 CPPUNIT_ASSERT( !xPropertySet
->getPropertySetInfo()->hasPropertyByName( "LineColor" ));
407 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
408 CPPUNIT_ASSERT(pTextDoc
);
409 uno::Reference
< lang::XMultiServiceFactory
> xFact( mxComponent
, uno::UNO_QUERY
);
410 uno::Reference
< beans::XPropertySet
> xDefaults( xFact
->createInstance( "com.sun.star.drawing.Defaults" ), uno::UNO_QUERY
);
411 CPPUNIT_ASSERT( xDefaults
.is());
412 uno::Reference
< beans::XPropertySetInfo
> xInfo( xDefaults
->getPropertySetInfo());
413 CPPUNIT_ASSERT( xInfo
->hasPropertyByName( "LineColor" ));
415 xDefaults
->getPropertyValue( "LineColor" ) >>= lineColor
;
416 // And the default value is black (wasn't copied properly by mailmerge).
417 CPPUNIT_ASSERT_EQUAL( COL_BLACK
, lineColor
);
418 // And check that the resulting file has the proper default.
419 xmlDocUniquePtr pXmlDoc
= parseMailMergeExport( "styles.xml" );
420 CPPUNIT_ASSERT_EQUAL( OUString( "graphic" ), getXPath(pXmlDoc
, "/office:document-styles/office:styles/style:default-style[1]", "family"));
421 CPPUNIT_ASSERT_EQUAL( OUString( "#000000" ), getXPath(pXmlDoc
, "/office:document-styles/office:styles/style:default-style[1]/style:graphic-properties", "stroke-color"));
424 DECLARE_FILE_MAILMERGE_TEST(testSimpleMailMerge
, "simple-mail-merge.odt", "10-testing-addresses.ods", "testing-addresses")
431 loadMailMergeDocument( doc
);
432 CPPUNIT_ASSERT_EQUAL( 1, getPages());
433 CPPUNIT_ASSERT_EQUAL( OUString( "Fixed text." ), getRun( getParagraph( 1 ), 1 )->getString());
434 CPPUNIT_ASSERT_EQUAL( OUString( "lastname" + OUString::number( doc
+ 1 )), getRun( getParagraph( 2 ), 1 )->getString());
435 CPPUNIT_ASSERT_EQUAL( OUString( "Another fixed text." ), getRun( getParagraph( 3 ), 1 )->getString());
439 DECLARE_FILE_MAILMERGE_TEST(testWriterDataSource
, "writer-mail-merge.odt", "10-testing-addresses-writer.odt", "testing-addresses-writer")
441 // This failed as the .odt data source was mapped to the jdbc: protocol.
443 for (int doc
= 0; doc
< 10; ++doc
)
445 loadMailMergeDocument(doc
);
446 CPPUNIT_ASSERT_EQUAL(1, getPages());
447 CPPUNIT_ASSERT_EQUAL(OUString("Fixed text."), getRun(getParagraph(1), 1)->getString());
448 CPPUNIT_ASSERT_EQUAL(OUString("lastname" + OUString::number(doc
+ 1)), getRun(getParagraph(2), 1)->getString());
449 CPPUNIT_ASSERT_EQUAL(OUString("Another fixed text."), getRun(getParagraph(3), 1)->getString());
453 DECLARE_FILE_MAILMERGE_TEST(testWriterMergedDataSource
, "writer-merged-mail-merge.odt", "10-testing-addresses-writer-merged.odt", "testing-addresses-writer-merged")
455 // This failed with com.sun.star.lang.IndexOutOfBoundsException, leading to
456 // a crash, as the last row had merged cells in
457 // 10-testing-addresses-writer-merged.odt.
459 for (int doc
= 0; doc
< 10; ++doc
)
461 loadMailMergeDocument(doc
);
462 CPPUNIT_ASSERT_EQUAL(1, getPages());
463 CPPUNIT_ASSERT_EQUAL(OUString("Fixed text."), getRun(getParagraph(1), 1)->getString());
464 CPPUNIT_ASSERT_EQUAL(OUString("lastname" + OUString::number(doc
+ 1)), getRun(getParagraph(2), 1)->getString());
465 CPPUNIT_ASSERT_EQUAL(OUString("Another fixed text."), getRun(getParagraph(3), 1)->getString());
469 DECLARE_FILE_MAILMERGE_TEST(test2Pages
, "simple-mail-merge-2pages.odt", "10-testing-addresses.ods", "testing-addresses")
476 loadMailMergeDocument( doc
);
477 OUString lastname
= "lastname" + OUString::number( doc
+ 1 );
478 OUString firstname
= "firstname" + OUString::number( doc
+ 1 );
479 CPPUNIT_ASSERT_EQUAL( 2, getPages());
480 CPPUNIT_ASSERT_EQUAL( OUString( "Fixed text." ), getRun( getParagraph( 1 ), 1 )->getString());
481 CPPUNIT_ASSERT_EQUAL( lastname
, getRun( getParagraph( 2 ), 1 )->getString());
482 CPPUNIT_ASSERT_EQUAL( OUString( "Another fixed text." ), getRun( getParagraph( 3 ), 1 )->getString());
483 CPPUNIT_ASSERT_EQUAL( OUString(), getRun( getParagraph( 4 ), 1 )->getString()); // empty para at the end of page 1
484 CPPUNIT_ASSERT_EQUAL( OUString( "Second page." ), getRun( getParagraph( 5 ), 1 )->getString());
485 CPPUNIT_ASSERT_EQUAL( firstname
, getRun( getParagraph( 6 ), 1 )->getString());
486 // Also verify the layout.
487 CPPUNIT_ASSERT_EQUAL( lastname
, parseDump("/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand"));
488 CPPUNIT_ASSERT_EQUAL( OUString( "Fixed text." ), parseDump("/root/page[1]/body/txt[1]", ""));
489 CPPUNIT_ASSERT_EQUAL( OUString(), parseDump("/root/page[1]/body/txt[4]", ""));
490 CPPUNIT_ASSERT_EQUAL( OUString( "Second page." ), parseDump("/root/page[2]/body/txt[1]", ""));
491 CPPUNIT_ASSERT_EQUAL( firstname
, parseDump("/root/page[2]/body/txt[2]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand"));
495 DECLARE_SHELL_MAILMERGE_TEST(testPageBoundariesSimpleMailMerge
, "simple-mail-merge.odt", "10-testing-addresses.ods", "testing-addresses")
497 // This is like the test above, but this one uses the create-single-document-containing-everything-generated approach,
498 // and verifies that boundaries of the generated sub-documents are correct inside that document.
499 // These boundaries are done using "documentStartPageNumber<number>" UNO bookmarks (see also
500 // documentStartPageNumber() ).
502 // Here getPages() works on the source document, so get pages of the resulting one.
503 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
504 CPPUNIT_ASSERT(pTextDoc
);
505 CPPUNIT_ASSERT_EQUAL( sal_uInt16( 19 ), pTextDoc
->GetDocShell()->GetWrtShell()->GetPhyPageNum()); // 10 pages, but each sub-document starts on odd page number
510 CPPUNIT_ASSERT_EQUAL( doc
* 2 + 1, documentStartPageNumber( doc
));
514 DECLARE_SHELL_MAILMERGE_TEST(testPageBoundaries2Pages
, "simple-mail-merge-2pages.odt", "10-testing-addresses.ods", "testing-addresses")
517 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
518 CPPUNIT_ASSERT(pTextDoc
);
519 CPPUNIT_ASSERT_EQUAL( sal_uInt16( 20 ), pTextDoc
->GetDocShell()->GetWrtShell()->GetPhyPageNum()); // 20 pages, each sub-document starts on odd page number
524 CPPUNIT_ASSERT_EQUAL( doc
* 2 + 1, documentStartPageNumber( doc
));
528 DECLARE_SHELL_MAILMERGE_TEST(testTdf89214
, "tdf89214.odt", "10-testing-addresses.ods", "testing-addresses")
532 uno::Reference
<text::XTextDocument
> xTextDocument(mxMMComponent
, uno::UNO_QUERY
);
533 uno::Reference
<text::XTextRange
> xParagraph(getParagraphOrTable(3, xTextDocument
->getText()), uno::UNO_QUERY
);
534 // Make sure that we assert the right paragraph.
535 CPPUNIT_ASSERT_EQUAL(OUString("a"), xParagraph
->getString());
536 // This paragraph had a bullet numbering, make sure that the list id is not empty.
537 CPPUNIT_ASSERT(!getProperty
<OUString
>(xParagraph
, "ListId").isEmpty());
540 DECLARE_SHELL_MAILMERGE_TEST(testTdf90230
, "empty.odt", "10-testing-addresses.ods", "testing-addresses")
542 // MM of an empty document caused an assertion in the SwContentIndexReg dtor.
546 DECLARE_SHELL_MAILMERGE_TEST(testTdf92623
, "tdf92623.odt", "10-testing-addresses.ods", "testing-addresses")
548 // Copying bookmarks for MM was broken because of the StartOfContent node copy
549 // copied marks were off by one
552 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
553 CPPUNIT_ASSERT(pTextDoc
);
554 IDocumentMarkAccess
const *pIDMA
= pTextDoc
->GetDocShell()->GetDoc()->getIDocumentMarkAccess();
555 // There is just one mark...
556 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), pIDMA
->getAllMarksCount());
557 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), pIDMA
->getBookmarksCount());
558 IDocumentMarkAccess::const_iterator_t mark
= pIDMA
->getAllMarksBegin();
559 // and it's a TEXT_FIELDMARK
560 CPPUNIT_ASSERT_EQUAL( sal_Int32(IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
),
561 sal_Int32(IDocumentMarkAccess::GetType( **mark
)) );
562 SwNodeOffset src_pos
= (*mark
)->GetMarkPos().GetNodeIndex();
564 // Get the size of the document in nodes
565 SwDoc
*doc
= pTextDoc
->GetDocShell()->GetDoc();
566 SwNodeOffset size
= doc
->GetNodes().GetEndOfContent().GetIndex() - doc
->GetNodes().GetEndOfExtras().GetIndex();
567 CPPUNIT_ASSERT_EQUAL( SwNodeOffset(13), size
);
568 size
-= SwNodeOffset(2); // For common start and end nodes
570 // Iterate over all field marks in the target document and check that they
571 // are positioned at a multitude of the document size
572 SwXTextDocument
* pMMTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
573 CPPUNIT_ASSERT(pMMTextDoc
);
574 pIDMA
= pMMTextDoc
->GetDocShell()->GetDoc()->getIDocumentMarkAccess();
575 // The target document has the duplicated amount of bookmarks
576 // as the helping uno bookmark from the mail merge is left in the doc
577 // TODO should be fixed!
578 CPPUNIT_ASSERT_EQUAL(sal_Int32(20), pIDMA
->getAllMarksCount());
579 std::set
<SwNodeOffset
> pages
;
580 sal_Int32 countFieldMarks
= 0;
581 for( mark
= pIDMA
->getAllMarksBegin(); mark
!= pIDMA
->getAllMarksEnd(); ++mark
)
583 IDocumentMarkAccess::MarkType markType
= IDocumentMarkAccess::GetType( **mark
);
584 if( markType
== IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
)
586 SwNodeOffset pos
= (*mark
)->GetMarkPos().GetNodeIndex() - src_pos
;
587 CPPUNIT_ASSERT_EQUAL(SwNodeOffset(0), pos
% size
);
588 CPPUNIT_ASSERT(pages
.insert(pos
).second
);
591 else // see previous TODO
592 CPPUNIT_ASSERT_EQUAL( sal_Int32(IDocumentMarkAccess::MarkType::UNO_BOOKMARK
), sal_Int32(markType
) );
594 CPPUNIT_ASSERT_EQUAL(sal_Int32(10), countFieldMarks
);
597 DECLARE_SHELL_MAILMERGE_TEST(testBookmarkCondition
, "bookmarkcondition.fodt", "bookmarkcondition.ods", "data")
602 xmlDocUniquePtr
pLayout(
603 xmlParseMemory(reinterpret_cast<const char*>(xmlBufferContent(mpXmlBuffer
)),
604 xmlBufferLength(mpXmlBuffer
)));
606 // check that conditions on sections and bookmarks are evaluated the same
607 assertXPath(pLayout
, "/root/page", 7);
608 assertXPath(pLayout
, "/root/page[1]/body/section", 1);
609 assertXPath(pLayout
, "/root/page[1]/body/section[1]/txt[1]/SwParaPortion/SwLineLayout", "portion", u
"In den Bergen war es anstrengend.");
610 assertXPath(pLayout
, "/root/page[1]/body/txt[5]/SwParaPortion/SwLineLayout", "portion", u
"Mein Urlaub war anstrengend . ");
611 assertXPath(pLayout
, "/root/page[3]/body/section", 1);
612 assertXPath(pLayout
, "/root/page[3]/body/section[1]/txt[1]/SwParaPortion/SwLineLayout", "portion", u
"In Barcelona war es schön.");
613 assertXPath(pLayout
, "/root/page[3]/body/txt[5]/SwParaPortion/SwLineLayout", "portion", u
"Mein Urlaub war schön . ");
614 assertXPath(pLayout
, "/root/page[5]/body/section", 1);
615 assertXPath(pLayout
, "/root/page[5]/body/section[1]/txt[1]/SwParaPortion/SwLineLayout", "portion", "In Paris war es erlebnisreich.");
616 assertXPath(pLayout
, "/root/page[5]/body/txt[5]/SwParaPortion/SwLineLayout", "portion", u
"Mein Urlaub war erlebnisreich . ");
617 assertXPath(pLayout
, "/root/page[7]/body/section", 3);
618 assertXPath(pLayout
, "/root/page[7]/body/section[1]/txt[1]/SwParaPortion/SwLineLayout", "portion", u
"In den Bergen war es anstrengend.");
619 assertXPath(pLayout
, "/root/page[7]/body/section[2]/txt[1]/SwParaPortion/SwLineLayout", "portion", u
"In Barcelona war es schön.");
620 assertXPath(pLayout
, "/root/page[7]/body/section[3]/txt[1]/SwParaPortion/SwLineLayout", "portion", u
"In Paris war es erlebnisreich.");
621 assertXPath(pLayout
, "/root/page[7]/body/txt[5]/SwParaPortion/SwLineLayout", "portion", u
"Mein Urlaub war anstrengend schön erlebnisreich . ");
624 DECLARE_SHELL_MAILMERGE_TEST_SELECTION(testTdf95292
, "linked-labels.odt", "10-testing-addresses.ods", "testing-addresses", 5)
626 // A document with two labels merged with 5 datasets should result in three pages
629 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>( mxComponent
.get() );
630 CPPUNIT_ASSERT( pTextDoc
);
631 SwWrtShell
*pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
632 CPPUNIT_ASSERT( pWrtShell
->IsLabelDoc() );
634 pTextDoc
= dynamic_cast<SwXTextDocument
*>( mxMMComponent
.get() );
635 CPPUNIT_ASSERT( pTextDoc
);
636 pWrtShell
= pTextDoc
->GetDocShell()->GetWrtShell();
637 CPPUNIT_ASSERT( !pWrtShell
->IsLabelDoc() );
638 CPPUNIT_ASSERT_EQUAL( sal_uInt16( 5 ), pWrtShell
->GetPhyPageNum() );
641 DECLARE_SHELL_MAILMERGE_TEST(test_sections_first_last
, "sections_first_last.odt", "10-testing-addresses.ods", "testing-addresses")
643 // A document with a leading, middle and trailing section
644 // Originally we were losing the trailing section during merge
647 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
648 CPPUNIT_ASSERT(pTextDoc
);
650 // Get the size of the document in nodes
651 SwDoc
*pDoc
= pTextDoc
->GetDocShell()->GetDoc();
652 SwNodeOffset nSize
= pDoc
->GetNodes().GetEndOfContent().GetIndex() - pDoc
->GetNodes().GetEndOfExtras().GetIndex();
653 nSize
-= SwNodeOffset(2); // The common start and end node
654 CPPUNIT_ASSERT_EQUAL( SwNodeOffset(13), nSize
);
656 SwXTextDocument
* pTextDocMM
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
657 CPPUNIT_ASSERT(pTextDocMM
);
659 SwDoc
*pDocMM
= pTextDocMM
->GetDocShell()->GetDoc();
660 SwNodeOffset nSizeMM
= pDocMM
->GetNodes().GetEndOfContent().GetIndex() - pDocMM
->GetNodes().GetEndOfExtras().GetIndex();
661 nSizeMM
-= SwNodeOffset(2);
662 CPPUNIT_ASSERT_EQUAL( SwNodeOffset(10) * nSize
, nSizeMM
);
664 CPPUNIT_ASSERT_EQUAL( sal_uInt16(19), pDocMM
->GetDocShell()->GetWrtShell()->GetPhyPageNum() );
666 // All even pages should be empty, all sub-documents have two pages
667 const SwRootFrame
* pLayout
= pDocMM
->getIDocumentLayoutAccess().GetCurrentLayout();
668 const SwPageFrame
* pPageFrm
= static_cast<const SwPageFrame
*>( pLayout
->Lower() );
671 bool bOdd
= (1 == (pPageFrm
->GetPhyPageNum() % 2));
672 CPPUNIT_ASSERT_EQUAL( !bOdd
, pPageFrm
->IsEmptyPage() );
673 CPPUNIT_ASSERT_EQUAL( sal_uInt16( bOdd
? 1 : 2 ), pPageFrm
->GetVirtPageNum() );
674 pPageFrm
= static_cast<const SwPageFrame
*>( pPageFrm
->GetNext() );
678 DECLARE_FILE_MAILMERGE_TEST_COLUMN(testDirMailMerge
, "simple-mail-merge.odt", "10-testing-addresses.ods", "testing-addresses", "Filename")
685 OUString filename
= "sub/lastname" + OUString::number( doc
)
686 + " firstname" + OUString::number( doc
) + ".odt";
687 loadMailMergeDocument( filename
);
688 CPPUNIT_ASSERT_EQUAL( 1, getPages());
689 CPPUNIT_ASSERT_EQUAL( OUString( "Fixed text." ), getRun( getParagraph( 1 ), 1 )->getString());
690 CPPUNIT_ASSERT_EQUAL( OUString( "lastname" + OUString::number( doc
)), getRun( getParagraph( 2 ), 1 )->getString());
691 CPPUNIT_ASSERT_EQUAL( OUString( "Another fixed text." ), getRun( getParagraph( 3 ), 1 )->getString());
695 DECLARE_FILE_MAILMERGE_TEST(testTdf102010
, "empty.odt", "10-testing-addresses.ods", "testing-addresses")
697 // Create "correct" automatic filename for non-user-supplied-prefix
698 for (auto aNamedValueIter
= mMMargs
.begin(); aNamedValueIter
!= mMMargs
.end();)
700 if ( aNamedValueIter
->Name
== UNO_NAME_FILE_NAME_PREFIX
)
701 aNamedValueIter
= mMMargs
.erase( aNamedValueIter
);
704 std::cout
<< aNamedValueIter
->Name
<< ": " << aNamedValueIter
->Value
<< std::endl
;
708 mMMargs
.emplace_back( UNO_NAME_SAVE_AS_SINGLE_FILE
, uno::Any( true ) );
710 // Generate correct mail merge result filename
712 // Don't overwrite previous result
713 executeMailMerge( true );
714 loadMailMergeDocument( 1 );
717 DECLARE_SHELL_MAILMERGE_TEST(testTdf118113
, "tdf118113.odt", "tdf118113.ods", "testing-addresses")
721 // The document contains a text box anchored to the page and a conditionally hidden
722 // section that is only shown for one of the 4 recipients, namely the 3rd record.
723 // In case the hidden section is shown, the page count is 3 for a single data entry, otherwise 1.
724 // Previously, the page number was calculated incorrectly which led to the
725 // text box being anchored to the wrong page.
727 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
728 CPPUNIT_ASSERT(pTextDoc
);
729 // 3 documents with 1 page size each + 1 document with 3 pages
730 // + an additional page after each of the first 3 documents to make
731 // sure that each document starts on an odd page number
732 sal_uInt16 nPhysPages
= pTextDoc
->GetDocShell()->GetWrtShell()->GetPhyPageNum();
733 CPPUNIT_ASSERT_EQUAL(sal_uInt16(9), nPhysPages
);
735 // verify that there is a text box for each data record
736 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxMMComponent
, uno::UNO_QUERY
);
737 uno::Reference
<container::XIndexAccess
> xDraws
= xDrawPageSupplier
->getDrawPage();
738 CPPUNIT_ASSERT_EQUAL(sal_Int32(4), xDraws
->getCount());
740 // verify the text box for each data record is anchored to the first page of the given data record's pages
741 std::vector
<sal_uInt16
> expectedPageNumbers
{1, 3, 5, 9};
742 uno::Reference
<beans::XPropertySet
> xPropertySet
;
743 for (sal_Int32 i
= 0; i
< xDraws
->getCount(); i
++)
745 xPropertySet
.set(xDraws
->getByIndex(i
), uno::UNO_QUERY
);
747 text::TextContentAnchorType nAnchorType
;
748 CPPUNIT_ASSERT(xPropertySet
->getPropertyValue( UNO_NAME_ANCHOR_TYPE
) >>= nAnchorType
);
749 CPPUNIT_ASSERT_EQUAL( text::TextContentAnchorType_AT_PAGE
, nAnchorType
);
751 sal_uInt16 nAnchorPageNo
= {};
752 CPPUNIT_ASSERT(xPropertySet
->getPropertyValue( UNO_NAME_ANCHOR_PAGE_NO
) >>= nAnchorPageNo
);
754 CPPUNIT_ASSERT_EQUAL(expectedPageNumbers
.at(i
), nAnchorPageNo
);
761 constexpr char const* const EmptyValuesLegacyData
[][8]
762 = { { "Heading", "Title: ", "First Name: firstname1", "Last Name: lastname1",
763 "Title: First Name: firstname1", "First Name: firstname1 Last Name: lastname1",
764 "Title: First Name: firstname1 Last Name: lastname1", "Trailing text" },
765 { "Heading", "Title: title2", "First Name: ", "Last Name: lastname2",
766 "Title: title2 First Name: ", "First Name: Last Name: lastname2",
767 "Title: title2 First Name: Last Name: lastname2", "Trailing text" },
768 { "Heading", "Title: title3", "First Name: firstname3", "Last Name: ",
769 "Title: title3 First Name: firstname3", "First Name: firstname3 Last Name: ",
770 "Title: title3 First Name: firstname3 Last Name: ", "Trailing text" },
771 { "Heading", "Title: ", "First Name: ", "Last Name: lastname4",
772 "Title: First Name: ", "First Name: Last Name: lastname4",
773 "Title: First Name: Last Name: lastname4", "Trailing text" },
774 { "Heading", "Title: title5", "First Name: ", "Last Name: ", "Title: title5 First Name: ",
775 "First Name: Last Name: ", "Title: title5 First Name: Last Name: ", "Trailing text" } };
776 constexpr char const* const EmptyValuesNewData
[][8]
777 = { { "Heading", "First Name: firstname1", "Last Name: lastname1",
778 "Title: First Name: firstname1", "First Name: firstname1 Last Name: lastname1",
779 "Title: First Name: firstname1 Last Name: lastname1", "Trailing text" },
780 { "Heading", "Title: title2", "Last Name: lastname2",
781 "Title: title2 First Name: ", "First Name: Last Name: lastname2",
782 "Title: title2 First Name: Last Name: lastname2", "Trailing text" },
783 { "Heading", "Title: title3", "First Name: firstname3",
784 "Title: title3 First Name: firstname3", "First Name: firstname3 Last Name: ",
785 "Title: title3 First Name: firstname3 Last Name: ", "Trailing text" },
786 { "Heading", "Last Name: lastname4", "First Name: Last Name: lastname4",
787 "Title: First Name: Last Name: lastname4", "Trailing text" },
788 { "Heading", "Title: title5", "Title: title5 First Name: ",
789 "Title: title5 First Name: Last Name: ", "Trailing text" } };
792 // The following four tests (testEmptyValuesLegacyODT, testEmptyValuesNewODT, (testEmptyValuesLegacyFODT, testEmptyValuesNewFODT)
793 // check that for native documents without "EmptyDbFieldHidesPara" compatibility option, all paragraphs are exported visible,
794 // while for documents with the option enabled, the paragraphs with all Database fields having empty values are hidden.
796 DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesLegacyODT
, "tdf35798-legacy.odt", "5-with-blanks.ods",
800 for (int doc
= 0; doc
< 5; ++doc
)
802 loadMailMergeDocument(doc
);
803 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
804 CPPUNIT_ASSERT(pTextDoc
);
805 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
806 pDoc
->RemoveInvisibleContent();
807 CPPUNIT_ASSERT_EQUAL(1, getPages());
808 for (int i
= 0; i
< 8; ++i
)
810 auto xPara
= getParagraph(i
+1);
811 CPPUNIT_ASSERT_EQUAL_MESSAGE(
812 OString("in doc " + OString::number(doc
) + " paragraph " + OString::number(i
+ 1))
814 OUString::createFromAscii(EmptyValuesLegacyData
[doc
][i
]), xPara
->getString());
816 CPPUNIT_ASSERT_EQUAL(8, getParagraphs());
820 DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesNewODT
, "tdf35798-new.odt", "5-with-blanks.ods",
824 for (int doc
= 0; doc
< 5; ++doc
)
826 loadMailMergeDocument(doc
);
827 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
828 CPPUNIT_ASSERT(pTextDoc
);
829 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
830 pDoc
->RemoveInvisibleContent();
831 CPPUNIT_ASSERT_EQUAL(1, getPages());
833 for (i
= 0; i
< 8; ++i
)
835 const char* pExpected
= EmptyValuesNewData
[doc
][i
];
838 auto xPara
= getParagraph(i
+ 1);
839 CPPUNIT_ASSERT_EQUAL_MESSAGE(
840 OString("in doc " + OString::number(doc
) + " paragraph " + OString::number(i
+ 1))
842 OUString::createFromAscii(pExpected
), xPara
->getString());
844 CPPUNIT_ASSERT_EQUAL(i
, getParagraphs());
848 DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesLegacyFODT
, "tdf35798-legacy.fodt", "5-with-blanks.ods",
852 for (int doc
= 0; doc
< 5; ++doc
)
854 loadMailMergeDocument(doc
);
855 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
856 CPPUNIT_ASSERT(pTextDoc
);
857 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
858 pDoc
->RemoveInvisibleContent();
859 CPPUNIT_ASSERT_EQUAL(1, getPages());
860 for (int i
= 0; i
< 8; ++i
)
862 auto xPara
= getParagraph(i
+ 1);
863 CPPUNIT_ASSERT_EQUAL_MESSAGE(
864 OString("in doc " + OString::number(doc
) + " paragraph " + OString::number(i
+ 1))
866 OUString::createFromAscii(EmptyValuesLegacyData
[doc
][i
]), xPara
->getString());
868 CPPUNIT_ASSERT_EQUAL(8, getParagraphs());
872 DECLARE_FILE_MAILMERGE_TEST(testEmptyValuesNewFODT
, "tdf35798-new.fodt", "5-with-blanks.ods",
876 for (int doc
= 0; doc
< 5; ++doc
)
878 loadMailMergeDocument(doc
);
879 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
880 CPPUNIT_ASSERT(pTextDoc
);
881 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
882 pDoc
->RemoveInvisibleContent();
883 CPPUNIT_ASSERT_EQUAL(1, getPages());
885 for (i
= 0; i
< 8; ++i
)
887 const char* pExpected
= EmptyValuesNewData
[doc
][i
];
890 auto xPara
= getParagraph(i
+ 1);
891 CPPUNIT_ASSERT_EQUAL_MESSAGE(
892 OString("in doc " + OString::number(doc
) + " paragraph " + OString::number(i
+ 1))
894 OUString::createFromAscii(pExpected
), xPara
->getString());
896 CPPUNIT_ASSERT_EQUAL(i
, getParagraphs());
900 DECLARE_SHELL_MAILMERGE_TEST(testTdf118845
, "tdf118845.fodt", "4_v01.ods", "Tabelle1")
904 // Both male and female greetings were shown, thus each page had 3 paragraphs
906 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
907 CPPUNIT_ASSERT(pTextDoc
);
908 sal_uInt16 nPhysPages
= pTextDoc
->GetDocShell()->GetWrtShell()->GetPhyPageNum();
909 CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), nPhysPages
); // 4 pages, each odd, and 3 blanks
911 uno::Reference
<text::XTextDocument
> xTextDocument(mxMMComponent
, uno::UNO_QUERY
);
912 CPPUNIT_ASSERT_EQUAL(8, getParagraphs(xTextDocument
->getText()));
914 uno::Reference
<text::XTextRange
> xParagraph(getParagraphOrTable(1, xTextDocument
->getText()),
916 CPPUNIT_ASSERT_EQUAL(OUString("Dear Mrs. Mustermann1,"), xParagraph
->getString());
918 xParagraph
.set(getParagraphOrTable(2, xTextDocument
->getText()), uno::UNO_QUERY
);
919 CPPUNIT_ASSERT_EQUAL(OUString(""), xParagraph
->getString());
921 xParagraph
.set(getParagraphOrTable(3, xTextDocument
->getText()), uno::UNO_QUERY
);
922 CPPUNIT_ASSERT_EQUAL(OUString("Dear Mr. Mustermann2,"), xParagraph
->getString());
924 xParagraph
.set(getParagraphOrTable(4, xTextDocument
->getText()), uno::UNO_QUERY
);
925 CPPUNIT_ASSERT_EQUAL(OUString(""), xParagraph
->getString());
927 xParagraph
.set(getParagraphOrTable(5, xTextDocument
->getText()), uno::UNO_QUERY
);
928 CPPUNIT_ASSERT_EQUAL(OUString("Dear Mrs. Mustermann3,"), xParagraph
->getString());
930 xParagraph
.set(getParagraphOrTable(6, xTextDocument
->getText()), uno::UNO_QUERY
);
931 CPPUNIT_ASSERT_EQUAL(OUString(""), xParagraph
->getString());
933 xParagraph
.set(getParagraphOrTable(7, xTextDocument
->getText()), uno::UNO_QUERY
);
934 CPPUNIT_ASSERT_EQUAL(OUString("Dear Mr. Mustermann4,"), xParagraph
->getString());
936 xParagraph
.set(getParagraphOrTable(8, xTextDocument
->getText()), uno::UNO_QUERY
);
937 CPPUNIT_ASSERT_EQUAL(OUString(""), xParagraph
->getString());
940 DECLARE_SHELL_MAILMERGE_TEST(testTdf62364
, "tdf62364.odt", "10-testing-addresses.ods", "testing-addresses")
942 // prepare unit test and run
944 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxMMComponent
.get());
945 CPPUNIT_ASSERT(pTextDoc
);
946 CPPUNIT_ASSERT_EQUAL( sal_uInt16( 19 ), pTextDoc
->GetDocShell()->GetWrtShell()->GetPhyPageNum()); // 10 pages, but each sub-document starts on odd page number
948 // check: each page (one page is one sub doc) has 4 paragraphs:
949 // - 1st and 2nd are regular paragraphs
950 // - 3rd and 4th are inside list
951 const bool nodeInList
[4] = { false, false, true, true };
953 const auto & rNodes
= pTextDoc
->GetDocShell()->GetDoc()->GetNodes();
954 for (int pageIndex
=0; pageIndex
<10; pageIndex
++)
956 for (int nodeIndex
= 0; nodeIndex
<4; nodeIndex
++)
958 SwNode
* aNode
= rNodes
[SwNodeOffset(9 + pageIndex
* 4 + nodeIndex
)];
959 CPPUNIT_ASSERT_EQUAL(true, aNode
->IsTextNode());
961 const SwTextNode
* pTextNode
= aNode
->GetTextNode();
962 CPPUNIT_ASSERT(pTextNode
);
963 CPPUNIT_ASSERT_EQUAL(nodeInList
[nodeIndex
], pTextNode
->GetSwAttrSet().HasItem(RES_PARATR_LIST_ID
));
969 CPPUNIT_PLUGIN_IMPLEMENT();
970 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */