solenv/clang-format/excludelist       |    1 
 sw/CppunitTest_sw_mailmerge2.mk       |   62 ++
 sw/Module_sw.mk                       |    1 
 sw/qa/extras/mailmerge/mailmerge.cxx  |  391 ------------------
 sw/qa/extras/mailmerge/mailmerge2.cxx |  718 ++++++++++++++++++++++++++++++++++
 5 files changed, 782 insertions(+), 391 deletions(-)

New commits:
commit e4fe4a2918fbca0dc38441261a0f890720538639
Author:     Noel Grandin <[email protected]>
AuthorDate: Tue Sep 20 13:59:15 2022 +0200
Commit:     Noel Grandin <[email protected]>
CommitDate: Tue Sep 20 15:49:13 2022 +0200

    split the mailmerge unit test
    
    since it is substantially longer running than the next longest test
    
    Change-Id: Iec8f1e2f3d98a1c290c1a66620443c79dda9c237
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140239
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <[email protected]>

diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist
index 9f9138d1bdd8..3ad66572897a 100644
--- a/solenv/clang-format/excludelist
+++ b/solenv/clang-format/excludelist
@@ -12230,6 +12230,7 @@ sw/qa/extras/docbookexport/docbookexport.cxx
 sw/qa/extras/globalfilter/globalfilter.cxx
 sw/qa/extras/htmlimport/htmlimport.cxx
 sw/qa/extras/mailmerge/mailmerge.cxx
+sw/qa/extras/mailmerge/mailmerge2.cxx
 sw/qa/extras/odfexport/odfexport.cxx
 sw/qa/extras/odfexport/odfexport2.cxx
 sw/qa/extras/odfimport/odfimport.cxx
diff --git a/sw/CppunitTest_sw_mailmerge2.mk b/sw/CppunitTest_sw_mailmerge2.mk
new file mode 100644
index 000000000000..2fd6e107258a
--- /dev/null
+++ b/sw/CppunitTest_sw_mailmerge2.mk
@@ -0,0 +1,62 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sw_mailmerge2))
+
+$(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_mailmerge2))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sw_mailmerge2, \
+    sw/qa/extras/mailmerge/mailmerge2 \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sw_mailmerge2, \
+    comphelper \
+    cppu \
+    cppuhelper \
+    sal \
+    sfx \
+    svl \
+    sw \
+       swqahelper \
+    test \
+    tl \
+    unotest \
+    utl \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sw_mailmerge2, \
+    boost_headers \
+    libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_use_api,sw_mailmerge2,\
+       udkapi \
+       offapi \
+       oovbaapi \
+))
+
+$(eval $(call gb_CppunitTest_use_rdb,sw_mailmerge2,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,sw_mailmerge2))
+$(eval $(call gb_CppunitTest_use_ure,sw_mailmerge2))
+$(eval $(call gb_CppunitTest_use_vcl,sw_mailmerge2))
+
+$(eval $(call gb_CppunitTest_set_include,sw_mailmerge2,\
+    -I$(SRCDIR)/sw/inc \
+    -I$(SRCDIR)/sw/source/core/inc \
+    -I$(SRCDIR)/sw/qa/inc \
+    -I$(SRCDIR)/sw/source/uibase/inc \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_uiconfigs,sw_mailmerge2,\
+       modules/swriter \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sw/Module_sw.mk b/sw/Module_sw.mk
index 8b5a42e0ff14..754411a7dc42 100644
--- a/sw/Module_sw.mk
+++ b/sw/Module_sw.mk
@@ -120,6 +120,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sw,\
     CppunitTest_sw_layoutwriter \
     CppunitTest_sw_layoutwriter2 \
     CppunitTest_sw_mailmerge \
+    CppunitTest_sw_mailmerge2 \
     CppunitTest_sw_globalfilter \
     CppunitTest_sw_accessible_relation_set \
     CppunitTest_sw_apiterminate \
diff --git a/sw/qa/extras/mailmerge/mailmerge.cxx 
b/sw/qa/extras/mailmerge/mailmerge.cxx
index fbeded40b94b..8d024194c985 100644
--- a/sw/qa/extras/mailmerge/mailmerge.cxx
+++ b/sw/qa/extras/mailmerge/mailmerge.cxx
@@ -974,397 +974,6 @@ DECLARE_SHELL_MAILMERGE_TEST(testTdf62364, 
"tdf62364.odt", "10-testing-addresses
     }
 }
 
-DECLARE_SHELL_MAILMERGE_TEST(tdf125522_shell, "tdf125522.odt", 
"10-testing-addresses.ods", "testing-addresses")
-{
-    // prepare unit test and run
-    executeMailMerge();
-
-    // reset currently opened layout of the original template,
-    // and create the layout of the document with 10 mails inside
-    dumpMMLayout();
-
-    // there should be no any text frame in output
-    SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument 
*>(mxMMComponent.get());
-    CPPUNIT_ASSERT(pTextDoc);
-
-    const auto & rNodes = pTextDoc->GetDocShell()->GetDoc()->GetNodes();
-    for (SwNodeOffset nodeIndex(0); nodeIndex<rNodes.Count(); nodeIndex++)
-    {
-        SwNode* aNode = rNodes[nodeIndex];
-        if (aNode->StartOfSectionNode())
-        {
-            CPPUNIT_ASSERT(!aNode->StartOfSectionNode()->GetFlyFormat());
-        }
-    }
-}
-
-DECLARE_SHELL_MAILMERGE_TEST(testTd78611_shell, "tdf78611.odt", 
"10-testing-addresses.ods", "testing-addresses")
-{
-    // prepare unit test and run
-    executeMailMerge();
-
-    // reset currently opened layout of the original template,
-    // and create the layout of the document with 10 mails inside
-    dumpMMLayout();
-
-    // check: each page (one page is one sub doc) has different paragraphs and 
header paragraphs.
-    // All header paragraphs should have numbering.
-
-    // check first page
-    CPPUNIT_ASSERT_EQUAL( OUString("1"), 
parseDump("/root/page[1]/body/txt[6]/Special", "rText"));
-    CPPUNIT_ASSERT_EQUAL( OUString("1.1"), 
parseDump("/root/page[1]/body/txt[8]/Special", "rText"));
-    CPPUNIT_ASSERT_EQUAL( OUString("1.2"), 
parseDump("/root/page[1]/body/txt[10]/Special", "rText"));
-
-    // check some other pages
-    CPPUNIT_ASSERT_EQUAL( OUString("1"), 
parseDump("/root/page[3]/body/txt[6]/Special", "rText"));
-    CPPUNIT_ASSERT_EQUAL( OUString("1.1"), 
parseDump("/root/page[5]/body/txt[8]/Special", "rText"));
-    CPPUNIT_ASSERT_EQUAL( OUString("1.2"), 
parseDump("/root/page[7]/body/txt[10]/Special", "rText"));
-}
-
-DECLARE_FILE_MAILMERGE_TEST(testTd78611_file, "tdf78611.odt", 
"10-testing-addresses.ods", "testing-addresses")
-{
-    executeMailMerge(true);
-    for (int doc = 0; doc < 10; ++doc)
-    {
-        loadMailMergeDocument( doc );
-        CPPUNIT_ASSERT_EQUAL( OUString("1"), 
parseDump("/root/page[1]/body/txt[6]/Special", "rText"));
-        CPPUNIT_ASSERT_EQUAL( OUString("1.1"), 
parseDump("/root/page[1]/body/txt[8]/Special", "rText"));
-        CPPUNIT_ASSERT_EQUAL( OUString("1.2"), 
parseDump("/root/page[1]/body/txt[10]/Special", "rText"));
-    }
-}
-
-DECLARE_SHELL_MAILMERGE_TEST(testTdf122156_shell, "linked-with-condition.odt", 
"5-with-blanks.ods",
-                             "names")
-{
-    // A document with a linked section hidden on an "empty field" condition
-    // For combined documents, hidden sections are removed completely
-    executeMailMerge();
-    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
-    CPPUNIT_ASSERT(pTextDoc);
-    // 5 documents 1 page each, starting at odd page numbers => 9
-    CPPUNIT_ASSERT_EQUAL(sal_uInt16(9), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
-    uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxMMComponent,
-                                                                  
uno::UNO_QUERY_THROW);
-    uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
-                                                      uno::UNO_QUERY_THROW);
-    // 2 out of 5 dataset records have empty "Title" field => no sections in 
respective documents
-    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xSections->getCount());
-}
-
-DECLARE_FILE_MAILMERGE_TEST(testTdf122156_file, "linked-with-condition.odt", 
"5-with-blanks.ods",
-                            "names")
-{
-    // A document with a linked section hidden on an "empty field" condition
-    // For separate documents, the sections are hidden, but not removed
-    executeMailMerge();
-    {
-        loadMailMergeDocument(0);
-        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
-                                                                      
uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
-                                                          
uno::UNO_QUERY_THROW);
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
-        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
-        // Record 1 has empty "Title" field => section is not shown
-        CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
-    }
-    {
-        loadMailMergeDocument(1);
-        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
-                                                                      
uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
-                                                          
uno::UNO_QUERY_THROW);
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
-        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
-        // Record 2 has non-empty "Title" field => section is shown
-        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
-    }
-    {
-        loadMailMergeDocument(2);
-        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
-                                                                      
uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
-                                                          
uno::UNO_QUERY_THROW);
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
-        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
-        // Record 3 has non-empty "Title" field => section is shown
-        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
-    }
-    {
-        loadMailMergeDocument(3);
-        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
-                                                                      
uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
-                                                          
uno::UNO_QUERY_THROW);
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
-        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
-        // Record 4 has empty "Title" field => section is not shown
-        CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
-    }
-    {
-        loadMailMergeDocument(4);
-        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
-                                                                      
uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
-                                                          
uno::UNO_QUERY_THROW);
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
-        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
-        // Record 5 has non-empty "Title" field => section is shown
-        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
-    }
-}
-
-DECLARE_SHELL_MAILMERGE_TEST(testTdf121168, "section_ps.odt", "4_v01.ods", 
"Tabelle1")
-{
-    // A document starting with a section on a page with non-default page 
style with header
-    executeMailMerge();
-    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
-    CPPUNIT_ASSERT(pTextDoc);
-    // 4 documents 1 page each, starting at odd page numbers => 7
-    CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
-
-    SwDoc* pDocMM = pTextDoc->GetDocShell()->GetDoc();
-    SwNodeOffset nSizeMM = pDocMM->GetNodes().GetEndOfContent().GetIndex()
-                        - pDocMM->GetNodes().GetEndOfExtras().GetIndex() - 2;
-    CPPUNIT_ASSERT_EQUAL(SwNodeOffset(16), nSizeMM);
-
-    // All even pages should be empty, all sub-documents have one page
-    const SwRootFrame* pLayout = 
pDocMM->getIDocumentLayoutAccess().GetCurrentLayout();
-    const SwPageFrame* pPageFrm = static_cast<const 
SwPageFrame*>(pLayout->Lower());
-    while (pPageFrm)
-    {
-        sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum();
-        bool bOdd = (1 == (nPageNum % 2));
-        CPPUNIT_ASSERT_EQUAL(!bOdd, pPageFrm->IsEmptyPage());
-        CPPUNIT_ASSERT_EQUAL(sal_uInt16(bOdd ? 1 : 2), 
pPageFrm->GetVirtPageNum());
-        if (bOdd)
-        {
-            const SwPageDesc* pDesc = pPageFrm->GetPageDesc();
-            CPPUNIT_ASSERT_EQUAL(OUString("Teststyle" + 
OUString::number(nPageNum / 2 + 1)),
-                                 pDesc->GetName());
-        }
-        pPageFrm = static_cast<const SwPageFrame*>(pPageFrm->GetNext());
-    }
-}
-
-DECLARE_FILE_MAILMERGE_TEST(testTdf81782_file, "tdf78611.odt", 
"10-testing-addresses.ods", "testing-addresses")
-{
-    executeMailMerge(true);
-    for (int doc = 0; doc < 10; ++doc)
-    {
-        loadMailMergeDocument( doc );
-
-        // get document properties
-        uno::Reference<document::XDocumentPropertiesSupplier> 
xDocumentPropertiesSupplier(mxComponent, uno::UNO_QUERY);
-        uno::Reference<document::XDocumentProperties> 
xDocumentProperties(xDocumentPropertiesSupplier->getDocumentProperties());
-
-        // check if properties were set
-        uno::Sequence<OUString> aKeywords(xDocumentProperties->getKeywords());
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aKeywords.getLength());
-        CPPUNIT_ASSERT_EQUAL(OUString("one two"), aKeywords[0]);
-
-        // check title and subject
-        CPPUNIT_ASSERT_EQUAL(OUString("my title"), 
xDocumentProperties->getTitle());
-        CPPUNIT_ASSERT_EQUAL(OUString("my subject"), 
xDocumentProperties->getSubject());
-    }
-}
-
-// problem was: field content was duplicated & truncated
-DECLARE_SHELL_MAILMERGE_TEST(testTdf81750_shell, "tdf81750.odt", 
"10-testing-addresses.ods", "testing-addresses")
-{
-    // prepare unit test and run
-    executeMailMerge();
-
-    // reset currently opened layout of the original template,
-    // and create the layout of the document with 10 mails inside
-    dumpMMLayout();
-
-    // check several pages page
-    OUString aExpected("Text: Foo ");
-    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[1]/body/txt[2]", 
""));
-    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[3]/body/txt[2]", 
""));
-    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[5]/body/txt[2]", 
""));
-    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[7]/body/txt[2]", 
""));
-    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[9]/body/txt[2]", 
""));
-}
-
-
-DECLARE_FILE_MAILMERGE_TEST(testTdf123057_file, "pagecounttest.ott", 
"db_pagecounttest.ods", "Sheet1")
-{
-    executeMailMerge(true);
-
-    for (int doc = 0; doc < 4; ++doc)
-    {
-        loadMailMergeDocument(doc);
-
-        // get document properties
-        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW);
-        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW);
-
-        CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
-        uno::Reference<beans::XPropertySet> xSect0(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
-        uno::Reference<beans::XPropertySet> xSect1(xSections->getByIndex(1), 
uno::UNO_QUERY_THROW);
-
-        OUString sFieldPageCount;
-        uno::Reference<text::XTextFieldsSupplier> 
xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
-        uno::Reference<container::XEnumerationAccess> 
xFieldsAccess(xTextFieldsSupplier->getTextFields());
-        uno::Reference<container::XEnumeration> 
xFields(xFieldsAccess->createEnumeration());
-
-        if (xFields.is())
-        {
-            while (xFields->hasMoreElements())
-            {
-                uno::Any aField = xFields->nextElement();
-                uno::Reference<lang::XServiceInfo> xServiceInfo(aField, 
uno::UNO_QUERY);
-                if 
(xServiceInfo->supportsService("com.sun.star.text.textfield.PageCount"))
-                {
-                    uno::Reference<text::XTextContent> xField(aField, 
uno::UNO_QUERY);
-                    sFieldPageCount = xField->getAnchor()->getString();
-                }
-            }
-        }
-
-        switch (doc)
-        {
-        case 0:
-            // both sections visible, page num is 2
-            CPPUNIT_ASSERT_EQUAL(2, getPages());
-            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(OUString("2"), sFieldPageCount);
-            break;
-        case 1:
-            // second section hidden, page num is 1
-            CPPUNIT_ASSERT_EQUAL(1, getPages());
-            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(OUString("1"), sFieldPageCount);
-            break;
-        case 2:
-            // first section hidden, page num is 1
-            CPPUNIT_ASSERT_EQUAL(1, getPages());
-            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(OUString("1"), sFieldPageCount);
-            break;
-        case 3:
-            // both sections hidden, page num is 1
-            CPPUNIT_ASSERT_EQUAL(1, getPages());
-            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
-            CPPUNIT_ASSERT_EQUAL(OUString("1"), sFieldPageCount);
-            break;
-        }
-    }
-}
-
-// The document has a header with page number and total page count on page 2
-// (which uses page style "Default Style") but doesn't have a header set
-// for the first page (which uses page style "First Page").
-// Fields in the header hadn't been replaced properly.
-DECLARE_SHELL_MAILMERGE_TEST(testTdf128148, "tdf128148.odt", "4_v01.ods", 
"Tabelle1")
-{
-    executeMailMerge();
-    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
-    CPPUNIT_ASSERT(pTextDoc);
-
-    // 4 documents with 2 pages each => 8 pages in total
-    CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
-
-    SwDoc* pDocMM = pTextDoc->GetDocShell()->GetDoc();
-    uno::Reference<frame::XModel> xModel = 
pTextDoc->GetDocShell()->GetBaseModel();
-    uno::Reference<style::XStyleFamiliesSupplier> 
xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
-    uno::Reference<container::XNameAccess> xStyleFamilies = 
xStyleFamiliesSupplier->getStyleFamilies();
-    uno::Reference<container::XNameAccess> 
xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
-
-    // All odd pages have no header, all even pages should have header with 
text "Page 2 of 2"
-    const SwRootFrame* pLayout = 
pDocMM->getIDocumentLayoutAccess().GetCurrentLayout();
-    const SwPageFrame* pPageFrm = static_cast<const 
SwPageFrame*>(pLayout->Lower());
-    while (pPageFrm)
-    {
-        const sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum();
-        const bool bIsEvenPage = ((nPageNum % 2) == 0);
-
-        const OUString& sPageStyle = pPageFrm->GetPageDesc()->GetName();
-        uno::Reference<beans::XPropertySet> 
xPageStyle(xStyleFamily->getByName(sPageStyle), uno::UNO_QUERY);
-
-        bool bHeaderIsOn = false;
-        xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
-
-        // first page for every data record shouldn't have header, second 
should
-        CPPUNIT_ASSERT_EQUAL(bIsEvenPage, bHeaderIsOn);
-        if (bIsEvenPage)
-        {
-            // text in header on even pages with correctly replaced fields is 
"Page 2 of 2"
-            uno::Reference<text::XText> xHeaderText;
-            xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
-            const OUString sHeaderText = xHeaderText->getString();
-            CPPUNIT_ASSERT_EQUAL(OUString("Page 2 of 2"), sHeaderText);
-        }
-
-        pPageFrm = static_cast<const SwPageFrame*>(pPageFrm->GetNext());
-    }
-}
-
-namespace com::sun::star::table {
-
-static std::ostream& operator<<(std::ostream& rStream, table::BorderLine 
const& rLine)
-{
-    rStream << "BorderLine(" << rLine.Color << "," << rLine.InnerLineWidth << 
"," << rLine.OuterLineWidth << "," << rLine.LineDistance << ")";
-    return rStream;
-}
-
-static std::ostream& operator<<(std::ostream& rStream, table::TableBorder 
const& rBorder)
-{
-    rStream << "TableBorder(\n  "
-        << rBorder.TopLine << "," << static_cast<bool>(rBorder.IsTopLineValid) 
<< ",\n  "
-        << rBorder.BottomLine << "," << 
static_cast<bool>(rBorder.IsBottomLineValid) << ",\n  "
-        << rBorder.LeftLine << "," << 
static_cast<bool>(rBorder.IsLeftLineValid) << ",\n  "
-        << rBorder.RightLine << "," << 
static_cast<bool>(rBorder.IsRightLineValid) << ",\n  "
-        << rBorder.HorizontalLine << "," << 
static_cast<bool>(rBorder.IsHorizontalLineValid) << ",\n  "
-        << rBorder.VerticalLine << "," << 
static_cast<bool>(rBorder.IsVerticalLineValid) << ",\n  "
-        << rBorder.Distance << "," << 
static_cast<bool>(rBorder.IsDistanceValid) << ")";
-    return rStream;
-}
-
-}
-
-DECLARE_MAILMERGE_TEST(testGrabBag, "grabbagtest.docx", "onecell.xlsx", 
"Sheet1", "MS Word 2007 XML", MMTest, 0, nullptr)
-{
-    executeMailMerge(true);
-
-    loadMailMergeDocument(0, ".docx");
-
-    SwXTextDocument *const pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
-    CPPUNIT_ASSERT(pTextDoc);
-
-    CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
-
-    // check grabbag
-    uno::Reference<beans::XPropertySet> const xModel(
-        mxComponent, uno::UNO_QUERY_THROW);
-    uno::Sequence<beans::PropertyValue> aInteropGrabBag;
-    pTextDoc->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
-    CPPUNIT_ASSERT_EQUAL(sal_Int32(12), aInteropGrabBag.getLength());
-
-    // check table border - comes from table style "Tabellenraster"
-    uno::Reference<text::XTextTable> const xTable(getParagraphOrTable(1, 
pTextDoc->getText()), uno::UNO_QUERY_THROW);
-    uno::Reference<beans::XPropertySet> const xTableProps(xTable, 
uno::UNO_QUERY_THROW);
-    CPPUNIT_ASSERT_EQUAL(table::TableBorder(
-                table::BorderLine(util::Color(0), 0, 18, 0), true,
-                table::BorderLine(util::Color(0), 0, 18, 0), true,
-                table::BorderLine(util::Color(0), 0, 18, 0), true,
-                table::BorderLine(util::Color(0), 0, 18, 0), true,
-                table::BorderLine(util::Color(0), 0, 18, 0), true,
-                table::BorderLine(util::Color(0), 0, 0, 0), true,
-                sal_Int16(191), true),
-            getProperty<table::TableBorder>(xTableProps, "TableBorder"));
-
-    // check font is Arial - comes from theme (wrong result was "" - nothing)
-    uno::Reference<text::XText> const xCell(xTable->getCellByName("A1"), 
uno::UNO_QUERY_THROW);
-    uno::Reference<beans::XPropertySet> const xParaA1(getParagraphOrTable(1, 
xCell->getText()), uno::UNO_QUERY_THROW);
-    CPPUNIT_ASSERT_EQUAL(OUString("Arial"), getProperty<OUString>(xParaA1, 
"CharFontName"));
-}
 
 CPPUNIT_PLUGIN_IMPLEMENT();
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/mailmerge/mailmerge2.cxx 
b/sw/qa/extras/mailmerge/mailmerge2.cxx
new file mode 100644
index 000000000000..af49344e52b9
--- /dev/null
+++ b/sw/qa/extras/mailmerge/mailmerge2.cxx
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <set>
+#include <vector>
+
+#include <swmodeltestbase.hxx>
+
+#include <com/sun/star/text/MailMergeType.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/XTextTable.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.hpp>
+#include <com/sun/star/task/XJob.hpp>
+
+#include <tools/urlobj.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <wrtsh.hxx>
+#include <ndtxt.hxx>
+#include <pagefrm.hxx>
+#include <unoprnms.hxx>
+#include <dbmgr.hxx>
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+
+/**
+ * Maps database URIs to the registered database names for quick lookups
+ */
+typedef std::map<OUString, OUString> DBuriMap;
+static DBuriMap aDBuriMap;
+
+class MMTest2 : public SwModelTestBase
+{
+public:
+    MMTest2();
+
+    virtual void tearDown() override
+    {
+        if (mxMMComponent.is())
+        {
+            if (mnCurOutputType == text::MailMergeType::SHELL)
+            {
+                SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
+                CPPUNIT_ASSERT(pTextDoc);
+                pTextDoc->GetDocShell()->DoClose();
+            }
+            else
+                mxMMComponent->dispose();
+        }
+        if (mxCurResultSet.is())
+        {
+            css::uno::Reference<css::lang::XComponent>(
+                mxCurResultSet, css::uno::UNO_QUERY_THROW)->dispose();
+        }
+        SwModelTestBase::tearDown();
+    }
+
+    /**
+     * Helper func used by each unit test to test the 'mail merge' code.
+     *
+     * Registers the data source, loads the original file as reference,
+     * initializes the mail merge job and its default argument sequence.
+     *
+     * The 'verify' method actually has to execute the mail merge by
+     * calling executeMailMerge() after modifying the job arguments.
+     */
+    void executeMailMergeTest( const char* filename, const char* datasource, 
const char* tablename,
+                               char const*const filter, int selection, const 
char* column )
+    {
+        maMMTest2Filename = filename;
+        header();
+        preTest(filename);
+
+        utl::TempFile aTempDir(nullptr, true);
+        aTempDir.EnableKillingFile();
+        const OUString aWorkDir = aTempDir.GetURL();
+        const OUString aURI( m_directories.getURLFromSrc(mpTestDocumentPath) + 
OUString::createFromAscii(datasource) );
+        const OUString aPrefix = column ? OUString::createFromAscii( column ) 
: "LOMM_";
+        const OUString aDBName = registerDBsource( aURI, aWorkDir );
+        initMailMergeJobAndArgs( filename, tablename, aDBName, aPrefix, 
aWorkDir, filter, selection, column != nullptr );
+
+        verify();
+        finish();
+
+        mnCurOutputType = 0;
+    }
+
+    OUString registerDBsource( const OUString &aURI, const OUString &aWorkDir )
+    {
+        OUString aDBName;
+        DBuriMap::const_iterator pos = aDBuriMap.find( aURI );
+        if (pos == aDBuriMap.end())
+        {
+            aDBName = SwDBManager::LoadAndRegisterDataSource( aURI, &aWorkDir 
);
+            aDBuriMap.insert( std::pair< OUString, OUString >( aURI, aDBName ) 
);
+            std::cout << "New datasource name: '" << aDBName << "'" << 
std::endl;
+        }
+        else
+        {
+            aDBName = pos->second;
+            std::cout << "Old datasource name: '" << aDBName << "'" << 
std::endl;
+        }
+        CPPUNIT_ASSERT(!aDBName.isEmpty());
+        return aDBName;
+    }
+
+    uno::Reference< sdbc::XRowSet > getXResultFromDataset( const char* 
tablename, const OUString &aDBName )
+    {
+        uno::Reference< sdbc::XRowSet > xCurResultSet;
+        uno::Reference< uno::XInterface > xInstance = 
getMultiServiceFactory()->createInstance( "com.sun.star.sdb.RowSet" );
+        uno::Reference< beans::XPropertySet > xRowSetPropSet( xInstance, 
uno::UNO_QUERY );
+        assert( xRowSetPropSet.is() && "failed to get XPropertySet interface 
from RowSet" );
+        if (xRowSetPropSet.is())
+        {
+            xRowSetPropSet->setPropertyValue( "DataSourceName",    uno::Any( 
aDBName ) );
+            xRowSetPropSet->setPropertyValue( "Command",           uno::Any( 
OUString::createFromAscii(tablename) ) );
+            xRowSetPropSet->setPropertyValue( "CommandType",       uno::Any( 
sdb::CommandType::TABLE ) );
+
+            uno::Reference< sdbc::XRowSet > xRowSet( xInstance, uno::UNO_QUERY 
);
+            if (xRowSet.is())
+                xRowSet->execute(); // build ResultSet from properties
+            xCurResultSet = xRowSet;
+            assert( xCurResultSet.is() && "failed to build ResultSet" );
+        }
+        return xCurResultSet;
+    }
+
+    void initMailMergeJobAndArgs( const char* filename, const char* tablename, 
const OUString &aDBName,
+                                  const OUString &aPrefix, const OUString 
&aWorkDir,
+                                  char const*const filter, int nDataSets,
+                                  const bool bPrefixIsColumn )
+    {
+        uno::Reference< task::XJob > xJob( 
getMultiServiceFactory()->createInstance( "com.sun.star.text.MailMerge" ), 
uno::UNO_QUERY_THROW );
+        mxJob.set( xJob );
+
+        mMMargs.reserve( 15 );
+
+        mMMargs.emplace_back( UNO_NAME_OUTPUT_TYPE, uno::Any( filter ? 
text::MailMergeType::FILE : text::MailMergeType::SHELL ) );
+        mMMargs.emplace_back( UNO_NAME_DOCUMENT_URL, uno::Any(
+                                         ( OUString( 
m_directories.getURLFromSrc(mpTestDocumentPath) + 
OUString::createFromAscii(filename)) ) ) );
+        mMMargs.emplace_back( UNO_NAME_DATA_SOURCE_NAME, uno::Any( aDBName ) );
+        mMMargs.emplace_back( UNO_NAME_OUTPUT_URL, uno::Any( aWorkDir ) );
+        if (filter)
+        {
+            mMMargs.emplace_back( UNO_NAME_FILE_NAME_PREFIX, uno::Any( aPrefix 
) );
+            mMMargs.emplace_back(UNO_NAME_SAVE_FILTER, 
uno::Any(OUString::createFromAscii(filter)));
+        }
+
+        if (bPrefixIsColumn)
+            mMMargs.emplace_back( UNO_NAME_FILE_NAME_FROM_COLUMN, uno::Any( 
true ) );
+
+        if (tablename)
+        {
+            mMMargs.emplace_back( UNO_NAME_DAD_COMMAND_TYPE, uno::Any( 
sdb::CommandType::TABLE ) );
+            mMMargs.emplace_back( UNO_NAME_DAD_COMMAND, uno::Any( 
OUString::createFromAscii(tablename) ) );
+        }
+
+        if (nDataSets > 0)
+        {
+            mxCurResultSet = getXResultFromDataset( tablename, aDBName );
+            uno::Reference< sdbcx::XRowLocate > xCurRowLocate( mxCurResultSet, 
uno::UNO_QUERY );
+            mMMargs.emplace_back( UNO_NAME_RESULT_SET, uno::Any( 
mxCurResultSet ) );
+            std::vector< uno::Any > vResult;
+            vResult.reserve( nDataSets );
+            sal_Int32 i;
+            for (i = 0, mxCurResultSet->first(); i < nDataSets; i++, 
mxCurResultSet->next())
+            {
+                vResult.emplace_back( xCurRowLocate->getBookmark() );
+            }
+            mMMargs.emplace_back( UNO_NAME_SELECTION, uno::Any( 
comphelper::containerToSequence(vResult) ) );
+        }
+
+    }
+
+    void executeMailMerge( bool bDontLoadResult = false )
+    {
+        const uno::Sequence< beans::NamedValue > aSeqMailMergeArgs = 
comphelper::containerToSequence( mMMargs );
+        uno::Any res = mxJob->execute( aSeqMailMergeArgs );
+
+        bool bOk = true;
+        bool bMMFilenameFromColumn = false;
+
+        for (const beans::NamedValue& rArgument : aSeqMailMergeArgs) {
+            const OUString &rName  = rArgument.Name;
+            const uno::Any &rValue = rArgument.Value;
+
+            // all error checking was already done by the MM job execution
+            if (rName == UNO_NAME_OUTPUT_URL)
+                bOk &= rValue >>= msMailMergeOutputURL;
+            else if (rName == UNO_NAME_FILE_NAME_PREFIX)
+                bOk &= rValue >>= msMailMergeOutputPrefix;
+            else if (rName == UNO_NAME_OUTPUT_TYPE)
+                bOk &= rValue >>= mnCurOutputType;
+            else if (rName == UNO_NAME_FILE_NAME_FROM_COLUMN)
+                bOk &= rValue >>= bMMFilenameFromColumn;
+            else if (rName == UNO_NAME_DOCUMENT_URL)
+                bOk &= rValue >>= msMailMergeDocumentURL;
+        }
+
+        CPPUNIT_ASSERT(bOk);
+
+        // MM via UNO just works with file names. If we load the file on
+        // Windows before MM uses it, MM won't work, as it's already open.
+        // Don't move the load before the mail merge execution!
+        // (see gb_CppunitTest_use_instdir_configuration)
+        load(mpTestDocumentPath, maMMTest2Filename);
+
+        if (mnCurOutputType == text::MailMergeType::SHELL)
+        {
+            CPPUNIT_ASSERT(res >>= mxMMComponent);
+            CPPUNIT_ASSERT(mxMMComponent.is());
+        }
+        else
+        {
+            CPPUNIT_ASSERT_EQUAL(uno::Any(true), res);
+            if( !bMMFilenameFromColumn && !bDontLoadResult )
+                loadMailMergeDocument( 0 );
+        }
+    }
+
+    void loadMailMergeDocument( const OUString &filename )
+    {
+        assert( mnCurOutputType == text::MailMergeType::FILE );
+        if (mxComponent.is())
+            mxComponent->dispose();
+        // Output name early, so in the case of a hang, the name of the 
hanging input file is visible.
+        std::cout << filename << ",";
+        mnStartTime = osl_getGlobalTimer();
+        mxComponent = loadFromDesktop(msMailMergeOutputURL + "/" + filename, 
"com.sun.star.text.TextDocument");
+        OString name2 = OUStringToOString( filename, RTL_TEXTENCODING_UTF8 );
+        discardDumpedLayout();
+        if (mustCalcLayoutOf(name2.getStr()))
+            calcLayout();
+    }
+
+    /**
+     Loads number-th document from mail merge. Requires file output from mail 
merge.
+    */
+    void loadMailMergeDocument(int number, char const*const ext = ".odt")
+    {
+        OUString name;
+        if (!msMailMergeOutputPrefix.isEmpty())
+            name = msMailMergeOutputPrefix;
+        else
+        {
+            INetURLObject aURLObj;
+            aURLObj.SetSmartProtocol( INetProtocol::File );
+            aURLObj.SetSmartURL( msMailMergeDocumentURL );
+            name = aURLObj.GetBase();
+        }
+        name += OUString::number(number) + 
OStringToOUString(std::string_view(ext, strlen(ext)), 
RTL_TEXTENCODING_ASCII_US);
+        loadMailMergeDocument( name );
+    }
+
+    /**
+     Resets currently opened layout of the original template,
+     and creates the layout of the document with N mails inside
+     (result run with text::MailMergeType::SHELL)
+    */
+    void dumpMMLayout()
+    {
+        mpXmlBuffer = xmlBufferPtr();
+        dumpLayout(mxMMComponent);
+    }
+
+protected:
+    uno::Reference< css::task::XJob > mxJob;
+    std::vector< beans::NamedValue > mMMargs;
+    OUString msMailMergeDocumentURL;
+    OUString msMailMergeOutputURL;
+    OUString msMailMergeOutputPrefix;
+    sal_Int16 mnCurOutputType;
+    uno::Reference< lang::XComponent > mxMMComponent;
+    uno::Reference< sdbc::XRowSet > mxCurResultSet;
+    const char* maMMTest2Filename;
+};
+
+#define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
filter, BaseClass, selection, column) \
+    class TestName : public BaseClass { \
+    protected: \
+        virtual OUString getTestName() override { return #TestName; } \
+    public: \
+        CPPUNIT_TEST_SUITE(TestName); \
+        CPPUNIT_TEST(MailMerge); \
+        CPPUNIT_TEST_SUITE_END(); \
+    \
+        void MailMerge() { \
+            executeMailMergeTest(filename, datasource, tablename, filter, 
selection, column); \
+        } \
+        void verify() override; \
+    }; \
+    CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
+    void TestName::verify()
+
+// Will generate the resulting document in mxMMDocument.
+#define DECLARE_SHELL_MAILMERGE_TEST(TestName, filename, datasource, 
tablename) \
+    DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, nullptr, 
MMTest2, 0, nullptr)
+
+// Will generate documents as files, use loadMailMergeDocument().
+#define DECLARE_FILE_MAILMERGE_TEST(TestName, filename, datasource, tablename) 
\
+    DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
"writer8", MMTest2, 0, nullptr)
+
+MMTest2::MMTest2()
+    : SwModelTestBase("/sw/qa/extras/mailmerge/data/", "writer8")
+    , mnCurOutputType(0)
+    , maMMTest2Filename(nullptr)
+{
+}
+
+DECLARE_SHELL_MAILMERGE_TEST(tdf125522_shell, "tdf125522.odt", 
"10-testing-addresses.ods", "testing-addresses")
+{
+    // prepare unit test and run
+    executeMailMerge();
+
+    // reset currently opened layout of the original template,
+    // and create the layout of the document with 10 mails inside
+    dumpMMLayout();
+
+    // there should be no any text frame in output
+    SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument 
*>(mxMMComponent.get());
+    CPPUNIT_ASSERT(pTextDoc);
+
+    const auto & rNodes = pTextDoc->GetDocShell()->GetDoc()->GetNodes();
+    for (SwNodeOffset nodeIndex(0); nodeIndex<rNodes.Count(); nodeIndex++)
+    {
+        SwNode* aNode = rNodes[nodeIndex];
+        if (aNode->StartOfSectionNode())
+        {
+            CPPUNIT_ASSERT(!aNode->StartOfSectionNode()->GetFlyFormat());
+        }
+    }
+}
+
+DECLARE_SHELL_MAILMERGE_TEST(testTd78611_shell, "tdf78611.odt", 
"10-testing-addresses.ods", "testing-addresses")
+{
+    // prepare unit test and run
+    executeMailMerge();
+
+    // reset currently opened layout of the original template,
+    // and create the layout of the document with 10 mails inside
+    dumpMMLayout();
+
+    // check: each page (one page is one sub doc) has different paragraphs and 
header paragraphs.
+    // All header paragraphs should have numbering.
+
+    // check first page
+    CPPUNIT_ASSERT_EQUAL( OUString("1"), 
parseDump("/root/page[1]/body/txt[6]/Special", "rText"));
+    CPPUNIT_ASSERT_EQUAL( OUString("1.1"), 
parseDump("/root/page[1]/body/txt[8]/Special", "rText"));
+    CPPUNIT_ASSERT_EQUAL( OUString("1.2"), 
parseDump("/root/page[1]/body/txt[10]/Special", "rText"));
+
+    // check some other pages
+    CPPUNIT_ASSERT_EQUAL( OUString("1"), 
parseDump("/root/page[3]/body/txt[6]/Special", "rText"));
+    CPPUNIT_ASSERT_EQUAL( OUString("1.1"), 
parseDump("/root/page[5]/body/txt[8]/Special", "rText"));
+    CPPUNIT_ASSERT_EQUAL( OUString("1.2"), 
parseDump("/root/page[7]/body/txt[10]/Special", "rText"));
+}
+
+
+DECLARE_FILE_MAILMERGE_TEST(testTd78611_file, "tdf78611.odt", 
"10-testing-addresses.ods", "testing-addresses")
+{
+    executeMailMerge(true);
+    for (int doc = 0; doc < 10; ++doc)
+    {
+        loadMailMergeDocument( doc );
+        CPPUNIT_ASSERT_EQUAL( OUString("1"), 
parseDump("/root/page[1]/body/txt[6]/Special", "rText"));
+        CPPUNIT_ASSERT_EQUAL( OUString("1.1"), 
parseDump("/root/page[1]/body/txt[8]/Special", "rText"));
+        CPPUNIT_ASSERT_EQUAL( OUString("1.2"), 
parseDump("/root/page[1]/body/txt[10]/Special", "rText"));
+    }
+}
+
+DECLARE_SHELL_MAILMERGE_TEST(testTdf122156_shell, "linked-with-condition.odt", 
"5-with-blanks.ods",
+                             "names")
+{
+    // A document with a linked section hidden on an "empty field" condition
+    // For combined documents, hidden sections are removed completely
+    executeMailMerge();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
+    CPPUNIT_ASSERT(pTextDoc);
+    // 5 documents 1 page each, starting at odd page numbers => 9
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(9), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
+    uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxMMComponent,
+                                                                  
uno::UNO_QUERY_THROW);
+    uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
+                                                      uno::UNO_QUERY_THROW);
+    // 2 out of 5 dataset records have empty "Title" field => no sections in 
respective documents
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xSections->getCount());
+}
+
+DECLARE_FILE_MAILMERGE_TEST(testTdf122156_file, "linked-with-condition.odt", 
"5-with-blanks.ods",
+                            "names")
+{
+    // A document with a linked section hidden on an "empty field" condition
+    // For separate documents, the sections are hidden, but not removed
+    executeMailMerge();
+    {
+        loadMailMergeDocument(0);
+        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
+                                                                      
uno::UNO_QUERY_THROW);
+        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
+                                                          
uno::UNO_QUERY_THROW);
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
+        // Record 1 has empty "Title" field => section is not shown
+        CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
+    }
+    {
+        loadMailMergeDocument(1);
+        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
+                                                                      
uno::UNO_QUERY_THROW);
+        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
+                                                          
uno::UNO_QUERY_THROW);
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
+        // Record 2 has non-empty "Title" field => section is shown
+        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
+    }
+    {
+        loadMailMergeDocument(2);
+        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
+                                                                      
uno::UNO_QUERY_THROW);
+        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
+                                                          
uno::UNO_QUERY_THROW);
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
+        // Record 3 has non-empty "Title" field => section is shown
+        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
+    }
+    {
+        loadMailMergeDocument(3);
+        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
+                                                                      
uno::UNO_QUERY_THROW);
+        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
+                                                          
uno::UNO_QUERY_THROW);
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
+        // Record 4 has empty "Title" field => section is not shown
+        CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
+    }
+    {
+        loadMailMergeDocument(4);
+        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent,
+                                                                      
uno::UNO_QUERY_THROW);
+        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(),
+                                                          
uno::UNO_QUERY_THROW);
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
+        uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
+        // Record 5 has non-empty "Title" field => section is shown
+        CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, 
"IsCurrentlyVisible"));
+    }
+}
+
+DECLARE_SHELL_MAILMERGE_TEST(testTdf121168, "section_ps.odt", "4_v01.ods", 
"Tabelle1")
+{
+    // A document starting with a section on a page with non-default page 
style with header
+    executeMailMerge();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
+    CPPUNIT_ASSERT(pTextDoc);
+    // 4 documents 1 page each, starting at odd page numbers => 7
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
+
+    SwDoc* pDocMM = pTextDoc->GetDocShell()->GetDoc();
+    SwNodeOffset nSizeMM = pDocMM->GetNodes().GetEndOfContent().GetIndex()
+                        - pDocMM->GetNodes().GetEndOfExtras().GetIndex() - 2;
+    CPPUNIT_ASSERT_EQUAL(SwNodeOffset(16), nSizeMM);
+
+    // All even pages should be empty, all sub-documents have one page
+    const SwRootFrame* pLayout = 
pDocMM->getIDocumentLayoutAccess().GetCurrentLayout();
+    const SwPageFrame* pPageFrm = static_cast<const 
SwPageFrame*>(pLayout->Lower());
+    while (pPageFrm)
+    {
+        sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum();
+        bool bOdd = (1 == (nPageNum % 2));
+        CPPUNIT_ASSERT_EQUAL(!bOdd, pPageFrm->IsEmptyPage());
+        CPPUNIT_ASSERT_EQUAL(sal_uInt16(bOdd ? 1 : 2), 
pPageFrm->GetVirtPageNum());
+        if (bOdd)
+        {
+            const SwPageDesc* pDesc = pPageFrm->GetPageDesc();
+            CPPUNIT_ASSERT_EQUAL(OUString("Teststyle" + 
OUString::number(nPageNum / 2 + 1)),
+                                 pDesc->GetName());
+        }
+        pPageFrm = static_cast<const SwPageFrame*>(pPageFrm->GetNext());
+    }
+}
+
+
+DECLARE_FILE_MAILMERGE_TEST(testTdf81782_file, "tdf78611.odt", 
"10-testing-addresses.ods", "testing-addresses")
+{
+    executeMailMerge(true);
+    for (int doc = 0; doc < 10; ++doc)
+    {
+        loadMailMergeDocument( doc );
+
+        // get document properties
+        uno::Reference<document::XDocumentPropertiesSupplier> 
xDocumentPropertiesSupplier(mxComponent, uno::UNO_QUERY);
+        uno::Reference<document::XDocumentProperties> 
xDocumentProperties(xDocumentPropertiesSupplier->getDocumentProperties());
+
+        // check if properties were set
+        uno::Sequence<OUString> aKeywords(xDocumentProperties->getKeywords());
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aKeywords.getLength());
+        CPPUNIT_ASSERT_EQUAL(OUString("one two"), aKeywords[0]);
+
+        // check title and subject
+        CPPUNIT_ASSERT_EQUAL(OUString("my title"), 
xDocumentProperties->getTitle());
+        CPPUNIT_ASSERT_EQUAL(OUString("my subject"), 
xDocumentProperties->getSubject());
+    }
+}
+
+// problem was: field content was duplicated & truncated
+DECLARE_SHELL_MAILMERGE_TEST(testTdf81750_shell, "tdf81750.odt", 
"10-testing-addresses.ods", "testing-addresses")
+{
+    // prepare unit test and run
+    executeMailMerge();
+
+    // reset currently opened layout of the original template,
+    // and create the layout of the document with 10 mails inside
+    dumpMMLayout();
+
+    // check several pages page
+    OUString aExpected("Text: Foo ");
+    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[1]/body/txt[2]", 
""));
+    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[3]/body/txt[2]", 
""));
+    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[5]/body/txt[2]", 
""));
+    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[7]/body/txt[2]", 
""));
+    CPPUNIT_ASSERT_EQUAL( aExpected, parseDump("/root/page[9]/body/txt[2]", 
""));
+}
+
+
+DECLARE_FILE_MAILMERGE_TEST(testTdf123057_file, "pagecounttest.ott", 
"db_pagecounttest.ods", "Sheet1")
+{
+    executeMailMerge(true);
+
+    for (int doc = 0; doc < 4; ++doc)
+    {
+        loadMailMergeDocument(doc);
+
+        // get document properties
+        uno::Reference<text::XTextSectionsSupplier> 
xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW);
+        uno::Reference<container::XIndexAccess> 
xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW);
+
+        CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
+        uno::Reference<beans::XPropertySet> xSect0(xSections->getByIndex(0), 
uno::UNO_QUERY_THROW);
+        uno::Reference<beans::XPropertySet> xSect1(xSections->getByIndex(1), 
uno::UNO_QUERY_THROW);
+
+        OUString sFieldPageCount;
+        uno::Reference<text::XTextFieldsSupplier> 
xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
+        uno::Reference<container::XEnumerationAccess> 
xFieldsAccess(xTextFieldsSupplier->getTextFields());
+        uno::Reference<container::XEnumeration> 
xFields(xFieldsAccess->createEnumeration());
+
+        if (xFields.is())
+        {
+            while (xFields->hasMoreElements())
+            {
+                uno::Any aField = xFields->nextElement();
+                uno::Reference<lang::XServiceInfo> xServiceInfo(aField, 
uno::UNO_QUERY);
+                if 
(xServiceInfo->supportsService("com.sun.star.text.textfield.PageCount"))
+                {
+                    uno::Reference<text::XTextContent> xField(aField, 
uno::UNO_QUERY);
+                    sFieldPageCount = xField->getAnchor()->getString();
+                }
+            }
+        }
+
+        switch (doc)
+        {
+        case 0:
+            // both sections visible, page num is 2
+            CPPUNIT_ASSERT_EQUAL(2, getPages());
+            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(OUString("2"), sFieldPageCount);
+            break;
+        case 1:
+            // second section hidden, page num is 1
+            CPPUNIT_ASSERT_EQUAL(1, getPages());
+            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(OUString("1"), sFieldPageCount);
+            break;
+        case 2:
+            // first section hidden, page num is 1
+            CPPUNIT_ASSERT_EQUAL(1, getPages());
+            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(OUString("1"), sFieldPageCount);
+            break;
+        case 3:
+            // both sections hidden, page num is 1
+            CPPUNIT_ASSERT_EQUAL(1, getPages());
+            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect0, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xSect1, 
"IsCurrentlyVisible"));
+            CPPUNIT_ASSERT_EQUAL(OUString("1"), sFieldPageCount);
+            break;
+        }
+    }
+}
+
+// The document has a header with page number and total page count on page 2
+// (which uses page style "Default Style") but doesn't have a header set
+// for the first page (which uses page style "First Page").
+// Fields in the header hadn't been replaced properly.
+DECLARE_SHELL_MAILMERGE_TEST(testTdf128148, "tdf128148.odt", "4_v01.ods", 
"Tabelle1")
+{
+    executeMailMerge();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxMMComponent.get());
+    CPPUNIT_ASSERT(pTextDoc);
+
+    // 4 documents with 2 pages each => 8 pages in total
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
+
+    SwDoc* pDocMM = pTextDoc->GetDocShell()->GetDoc();
+    uno::Reference<frame::XModel> xModel = 
pTextDoc->GetDocShell()->GetBaseModel();
+    uno::Reference<style::XStyleFamiliesSupplier> 
xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
+    uno::Reference<container::XNameAccess> xStyleFamilies = 
xStyleFamiliesSupplier->getStyleFamilies();
+    uno::Reference<container::XNameAccess> 
xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
+
+    // All odd pages have no header, all even pages should have header with 
text "Page 2 of 2"
+    const SwRootFrame* pLayout = 
pDocMM->getIDocumentLayoutAccess().GetCurrentLayout();
+    const SwPageFrame* pPageFrm = static_cast<const 
SwPageFrame*>(pLayout->Lower());
+    while (pPageFrm)
+    {
+        const sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum();
+        const bool bIsEvenPage = ((nPageNum % 2) == 0);
+
+        const OUString& sPageStyle = pPageFrm->GetPageDesc()->GetName();
+        uno::Reference<beans::XPropertySet> 
xPageStyle(xStyleFamily->getByName(sPageStyle), uno::UNO_QUERY);
+
+        bool bHeaderIsOn = false;
+        xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+
+        // first page for every data record shouldn't have header, second 
should
+        CPPUNIT_ASSERT_EQUAL(bIsEvenPage, bHeaderIsOn);
+        if (bIsEvenPage)
+        {
+            // text in header on even pages with correctly replaced fields is 
"Page 2 of 2"
+            uno::Reference<text::XText> xHeaderText;
+            xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+            const OUString sHeaderText = xHeaderText->getString();
+            CPPUNIT_ASSERT_EQUAL(OUString("Page 2 of 2"), sHeaderText);
+        }
+
+        pPageFrm = static_cast<const SwPageFrame*>(pPageFrm->GetNext());
+    }
+}
+
+DECLARE_MAILMERGE_TEST(testGrabBag, "grabbagtest.docx", "onecell.xlsx", 
"Sheet1", "MS Word 2007 XML", MMTest2, 0, nullptr)
+{
+    executeMailMerge(true);
+
+    loadMailMergeDocument(0, ".docx");
+
+    SwXTextDocument *const pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    CPPUNIT_ASSERT(pTextDoc);
+
+    CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), 
pTextDoc->GetDocShell()->GetWrtShell()->GetPhyPageNum());
+
+    // check grabbag
+    uno::Reference<beans::XPropertySet> const xModel(
+        mxComponent, uno::UNO_QUERY_THROW);
+    uno::Sequence<beans::PropertyValue> aInteropGrabBag;
+    pTextDoc->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(12), aInteropGrabBag.getLength());
+
+    // check table border - comes from table style "Tabellenraster"
+    uno::Reference<text::XTextTable> const xTable(getParagraphOrTable(1, 
pTextDoc->getText()), uno::UNO_QUERY_THROW);
+    uno::Reference<beans::XPropertySet> const xTableProps(xTable, 
uno::UNO_QUERY_THROW);
+    CPPUNIT_ASSERT_EQUAL(table::TableBorder(
+                table::BorderLine(util::Color(0), 0, 18, 0), true,
+                table::BorderLine(util::Color(0), 0, 18, 0), true,
+                table::BorderLine(util::Color(0), 0, 18, 0), true,
+                table::BorderLine(util::Color(0), 0, 18, 0), true,
+                table::BorderLine(util::Color(0), 0, 18, 0), true,
+                table::BorderLine(util::Color(0), 0, 0, 0), true,
+                sal_Int16(191), true),
+            getProperty<table::TableBorder>(xTableProps, "TableBorder"));
+
+    // check font is Arial - comes from theme (wrong result was "" - nothing)
+    uno::Reference<text::XText> const xCell(xTable->getCellByName("A1"), 
uno::UNO_QUERY_THROW);
+    uno::Reference<beans::XPropertySet> const xParaA1(getParagraphOrTable(1, 
xCell->getText()), uno::UNO_QUERY_THROW);
+    CPPUNIT_ASSERT_EQUAL(OUString("Arial"), getProperty<OUString>(xParaA1, 
"CharFontName"));
+}
+
+namespace com::sun::star::table {
+
+static std::ostream& operator<<(std::ostream& rStream, table::BorderLine 
const& rLine)
+{
+    rStream << "BorderLine(" << rLine.Color << "," << rLine.InnerLineWidth << 
"," << rLine.OuterLineWidth << "," << rLine.LineDistance << ")";
+    return rStream;
+}
+
+static std::ostream& operator<<(std::ostream& rStream, table::TableBorder 
const& rBorder)
+{
+    rStream << "TableBorder(\n  "
+        << rBorder.TopLine << "," << static_cast<bool>(rBorder.IsTopLineValid) 
<< ",\n  "
+        << rBorder.BottomLine << "," << 
static_cast<bool>(rBorder.IsBottomLineValid) << ",\n  "
+        << rBorder.LeftLine << "," << 
static_cast<bool>(rBorder.IsLeftLineValid) << ",\n  "
+        << rBorder.RightLine << "," << 
static_cast<bool>(rBorder.IsRightLineValid) << ",\n  "
+        << rBorder.HorizontalLine << "," << 
static_cast<bool>(rBorder.IsHorizontalLineValid) << ",\n  "
+        << rBorder.VerticalLine << "," << 
static_cast<bool>(rBorder.IsVerticalLineValid) << ",\n  "
+        << rBorder.Distance << "," << 
static_cast<bool>(rBorder.IsDistanceValid) << ")";
+    return rStream;
+}
+
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to