sw/qa/extras/mailmerge/mailmerge.cxx         |  350 ------------------------
 sw/qa/extras/mailmerge/mailmerge2.cxx        |  306 ---------------------
 sw/qa/extras/mailmerge/mailmergetestbase.cxx |  381 +++++++++++++++++++++++++++
 3 files changed, 387 insertions(+), 650 deletions(-)

New commits:
commit 212ffc410aeac602efb2c2bfca3eb2d14a31f490
Author:     Xisco Fauli <[email protected]>
AuthorDate: Mon Jul 29 11:26:56 2024 +0200
Commit:     Xisco Fauli <[email protected]>
CommitDate: Mon Jul 29 15:32:01 2024 +0200

    CppunitTest_sw_mailmerge: factour out common code
    
    Change-Id: I82de04eaabc7efd2440c263064f0c074b30473ab
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171181
    Tested-by: Jenkins
    Reviewed-by: Xisco Fauli <[email protected]>

diff --git a/sw/qa/extras/mailmerge/mailmerge.cxx 
b/sw/qa/extras/mailmerge/mailmerge.cxx
index 57ebd8fee23f..ce4a890ec97f 100644
--- a/sw/qa/extras/mailmerge/mailmerge.cxx
+++ b/sw/qa/extras/mailmerge/mailmerge.cxx
@@ -7,360 +7,14 @@
  * 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>
+#include "mailmergetestbase.cxx"
 
 namespace
 {
-/**
- * Maps database URIs to the registered database names for quick lookups
- */
-typedef std::map<OUString, OUString> DBuriMap;
-DBuriMap aDBuriMap;
-
-class MMTest : public SwModelTestBase
+class MMTest : public MailMergeTestBase
 {
-public:
-    MMTest();
-
-    virtual void tearDown() override
-    {
-        if (mxSwTextDocument.is())
-        {
-            if (mnCurOutputType == text::MailMergeType::SHELL)
-                mxSwTextDocument->GetDocShell()->DoClose();
-            else
-                mxSwTextDocument->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 )
-    {
-        maMMtestFilename = filename;
-        header();
-
-        utl::TempFileNamed aTempDir(nullptr, true);
-        aTempDir.EnableKillingFile();
-        const OUString aWorkDir = aTempDir.GetURL();
-        const OUString aURI( 
createFileURL(OUString::createFromAscii(datasource) ) );
-        const OUString aPrefix = column ? OUString::createFromAscii( column ) 
: u"LOMM_"_ustr;
-        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( u"com.sun.star.sdb.RowSet"_ustr );
-        uno::Reference< beans::XPropertySet > xRowSetPropSet( xInstance, 
uno::UNO_QUERY );
-        assert( xRowSetPropSet.is() && "failed to get XPropertySet interface 
from RowSet" );
-        if (xRowSetPropSet.is())
-        {
-            xRowSetPropSet->setPropertyValue( u"DataSourceName"_ustr,    
uno::Any( aDBName ) );
-            xRowSetPropSet->setPropertyValue( u"Command"_ustr,           
uno::Any( OUString::createFromAscii(tablename) ) );
-            xRowSetPropSet->setPropertyValue( u"CommandType"_ustr,       
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( u"com.sun.star.text.MailMerge"_ustr 
), 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(
-                                         ( 
createFileURL(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)
-        createSwDoc(maMMtestFilename);
-
-        if (mnCurOutputType == text::MailMergeType::SHELL)
-        {
-            uno::Reference< lang::XComponent > xTmp;
-            CPPUNIT_ASSERT(res >>= xTmp);
-            mxSwTextDocument = dynamic_cast<SwXTextDocument*>(xTmp.get());
-            CPPUNIT_ASSERT(mxSwTextDocument.is());
-        }
-        else
-        {
-            CPPUNIT_ASSERT_EQUAL(uno::Any(true), res);
-            if( !bMMFilenameFromColumn && !bDontLoadResult )
-                loadMailMergeDocument( 0 );
-        }
-    }
-
-    /**
-     * Like parseExport(), but for given mail merge document.
-     */
-    xmlDocUniquePtr parseMailMergeExport(const OUString& rStreamName)
-    {
-        if (mnCurOutputType != text::MailMergeType::FILE)
-            return nullptr;
-
-        OUString name = msMailMergeOutputPrefix + OUString::number( 0 ) + 
".odt";
-        std::unique_ptr<SvStream> 
pStream(parseExportStream(msMailMergeOutputURL + "/" + name, rStreamName));
-
-        return parseXmlStream(pStream.get());
-    }
-
-    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, 
u"com.sun.star.text.TextDocument"_ustr);
-        discardDumpedLayout();
-        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(static_cast<SfxBaseModel*>(mxSwTextDocument.get()));
-    }
-
-protected:
-    // Returns page number of the first page of a MM document inside the large 
MM document (used in the SHELL case).
-    int documentStartPageNumber( int document ) const;
-
-    uno::Reference< css::task::XJob > mxJob;
-    std::vector< beans::NamedValue > mMMargs;
-    OUString msMailMergeDocumentURL;
-    OUString msMailMergeOutputURL;
-    OUString msMailMergeOutputPrefix;
-    sal_Int16 mnCurOutputType;
-    rtl::Reference< SwXTextDocument > mxSwTextDocument;
-    uno::Reference< sdbc::XRowSet > mxCurResultSet;
-    const char* maMMtestFilename;
 };
 
-#define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
filter, BaseClass, selection, column) \
-    class TestName : public BaseClass { \
-    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, 
MMTest, 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", MMTest, 0, nullptr)
-
-#define DECLARE_SHELL_MAILMERGE_TEST_SELECTION(TestName, filename, datasource, 
tablename, selection) \
-    DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, nullptr, 
MMTest, selection, nullptr)
-
-#define DECLARE_FILE_MAILMERGE_TEST_COLUMN(TestName, filename, datasource, 
tablename, column) \
-    DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
"writer8", MMTest, 0, column)
-
-int MMTest::documentStartPageNumber( int document ) const
-{   // See documentStartPageNumber() .
-    CPPUNIT_ASSERT(mxSwTextDocument);
-    SwWrtShell* shell = mxSwTextDocument->GetDocShell()->GetWrtShell();
-    IDocumentMarkAccess* marks = shell->GetDoc()->getIDocumentMarkAccess();
-    // Unfortunately, the pages are marked using UNO bookmarks, which have 
internals names, so they cannot be referred to by their names.
-    // Assume that there are no other UNO bookmarks than the ones used by mail 
merge, and that they are in the sorted order.
-    IDocumentMarkAccess::const_iterator_t mark;
-    int pos = 0;
-    for( mark = marks->getAllMarksBegin(); mark != marks->getAllMarksEnd() && 
pos < document; ++mark )
-    {
-        if( IDocumentMarkAccess::GetType( **mark ) == 
IDocumentMarkAccess::MarkType::UNO_BOOKMARK )
-            ++pos;
-    }
-    CPPUNIT_ASSERT_EQUAL(document, pos);
-    sal_uInt16 page, dummy;
-    shell->Push();
-    shell->GotoMark( *mark );
-    shell->GetPageNum( page, dummy );
-    shell->Pop(SwCursorShell::PopMode::DeleteCurrent);
-    return page;
-}
-
-MMTest::MMTest()
-    : SwModelTestBase(u"/sw/qa/extras/mailmerge/data/"_ustr, u"writer8"_ustr)
-    , mnCurOutputType(0)
-    , maMMtestFilename(nullptr)
-{
-}
-
 DECLARE_SHELL_MAILMERGE_TEST(testMultiPageAnchoredDraws, 
"multiple-page-anchored-draws.odt", "4_v01.ods", "Tabelle1")
 {
     executeMailMerge();
diff --git a/sw/qa/extras/mailmerge/mailmerge2.cxx 
b/sw/qa/extras/mailmerge/mailmerge2.cxx
index 8bbc4cf8b2f8..6eb3a35822d7 100644
--- a/sw/qa/extras/mailmerge/mailmerge2.cxx
+++ b/sw/qa/extras/mailmerge/mailmerge2.cxx
@@ -7,321 +7,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include <sal/config.h>
+#include "mailmergetestbase.cxx"
 
-#include <set>
-#include <vector>
-
-#include <swmodeltestbase.hxx>
-
-#include <com/sun/star/util/URLTransformer.hpp>
-#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
-#include <com/sun/star/text/MailMergeType.hpp>
 #include <com/sun/star/text/XPageCursor.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 <vcl/filter/PDFiumLibrary.hxx>
-#include <tools/urlobj.hxx>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
 #include <comphelper/sequence.hxx>
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertyvalue.hxx>
 #include <comphelper/DirectoryHelper.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>
-
 namespace
 {
-/**
- * Maps database URIs to the registered database names for quick lookups
- */
-typedef std::map<OUString, OUString> DBuriMap;
-DBuriMap aDBuriMap;
 
-class MMTest2 : public SwModelTestBase
+class MMTest2 : public MailMergeTestBase
 {
-public:
-    MMTest2();
-
-    virtual void tearDown() override
-    {
-        if (mxSwTextDocument.is())
-        {
-            if (mnCurOutputType == text::MailMergeType::SHELL)
-                mxSwTextDocument->GetDocShell()->DoClose();
-            else
-                mxSwTextDocument->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();
-
-        utl::TempFileNamed aTempDir(nullptr, true);
-        aTempDir.EnableKillingFile();
-        const OUString aWorkDir = aTempDir.GetURL();
-        const OUString aURI( 
createFileURL(OUString::createFromAscii(datasource)) );
-        const OUString aPrefix = column ? OUString::createFromAscii( column ) 
: u"LOMM_"_ustr;
-        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( u"com.sun.star.sdb.RowSet"_ustr );
-        uno::Reference< beans::XPropertySet > xRowSetPropSet( xInstance, 
uno::UNO_QUERY );
-        assert( xRowSetPropSet.is() && "failed to get XPropertySet interface 
from RowSet" );
-        if (xRowSetPropSet.is())
-        {
-            xRowSetPropSet->setPropertyValue( u"DataSourceName"_ustr,    
uno::Any( aDBName ) );
-            xRowSetPropSet->setPropertyValue( u"Command"_ustr,           
uno::Any( OUString::createFromAscii(tablename) ) );
-            xRowSetPropSet->setPropertyValue( u"CommandType"_ustr,       
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( u"com.sun.star.text.MailMerge"_ustr 
), 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(
-                                         ( 
createFileURL(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)
-        createSwDoc(maMMTest2Filename);
-
-        if (mnCurOutputType == text::MailMergeType::SHELL)
-        {
-            uno::Reference< lang::XComponent > xTmp;
-            CPPUNIT_ASSERT(res >>= xTmp);
-            mxSwTextDocument = dynamic_cast<SwXTextDocument*>(xTmp.get());
-            CPPUNIT_ASSERT(mxSwTextDocument.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, 
u"com.sun.star.text.TextDocument"_ustr);
-        discardDumpedLayout();
-        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(static_cast<SfxBaseModel*>(mxSwTextDocument.get()));
-    }
-
-protected:
-    uno::Reference< css::task::XJob > mxJob;
-    std::vector< beans::NamedValue > mMMargs;
-    OUString msMailMergeDocumentURL;
-    OUString msMailMergeOutputURL;
-    OUString msMailMergeOutputPrefix;
-    sal_Int16 mnCurOutputType;
-    rtl::Reference< SwXTextDocument > mxSwTextDocument;
-    uno::Reference< sdbc::XRowSet > mxCurResultSet;
-    const char* maMMTest2Filename;
 };
 
-#define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
filter, BaseClass, selection, column) \
-    class TestName : public BaseClass { \
-    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(u"/sw/qa/extras/mailmerge/data/"_ustr, u"writer8"_ustr)
-    , mnCurOutputType(0)
-    , maMMTest2Filename(nullptr)
-{
-}
-
 DECLARE_SHELL_MAILMERGE_TEST(tdf125522_shell, "tdf125522.odt", 
"10-testing-addresses.ods", "testing-addresses")
 {
     // prepare unit test and run
diff --git a/sw/qa/extras/mailmerge/mailmergetestbase.cxx 
b/sw/qa/extras/mailmerge/mailmergetestbase.cxx
new file mode 100644
index 000000000000..1cc2bc35c3a2
--- /dev/null
+++ b/sw/qa/extras/mailmerge/mailmergetestbase.cxx
@@ -0,0 +1,381 @@
+/* -*- 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;
+DBuriMap aDBuriMap;
+
+class MailMergeTestBase : public SwModelTestBase
+{
+public:
+    MailMergeTestBase()
+        : SwModelTestBase(u"/sw/qa/extras/mailmerge/data/"_ustr, 
u"writer8"_ustr)
+        , mnCurOutputType(0)
+        , maMMtestFilename(nullptr)
+    {
+    }
+
+    virtual void tearDown() override
+    {
+        if (mxSwTextDocument.is())
+        {
+            if (mnCurOutputType == text::MailMergeType::SHELL)
+                mxSwTextDocument->GetDocShell()->DoClose();
+            else
+                mxSwTextDocument->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)
+    {
+        maMMtestFilename = filename;
+        header();
+
+        utl::TempFileNamed aTempDir(nullptr, true);
+        aTempDir.EnableKillingFile();
+        const OUString aWorkDir = aTempDir.GetURL();
+        const OUString 
aURI(createFileURL(OUString::createFromAscii(datasource)));
+        const OUString aPrefix = column ? OUString::createFromAscii(column) : 
u"LOMM_"_ustr;
+        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(u"com.sun.star.sdb.RowSet"_ustr);
+        uno::Reference<beans::XPropertySet> xRowSetPropSet(xInstance, 
uno::UNO_QUERY);
+        assert(xRowSetPropSet.is() && "failed to get XPropertySet interface 
from RowSet");
+        if (xRowSetPropSet.is())
+        {
+            xRowSetPropSet->setPropertyValue(u"DataSourceName"_ustr, 
uno::Any(aDBName));
+            xRowSetPropSet->setPropertyValue(u"Command"_ustr,
+                                             
uno::Any(OUString::createFromAscii(tablename)));
+            xRowSetPropSet->setPropertyValue(u"CommandType"_ustr,
+                                             
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(u"com.sun.star.text.MailMerge"_ustr),
+            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((createFileURL(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)
+        createSwDoc(maMMtestFilename);
+
+        if (mnCurOutputType == text::MailMergeType::SHELL)
+        {
+            uno::Reference<lang::XComponent> xTmp;
+            CPPUNIT_ASSERT(res >>= xTmp);
+            mxSwTextDocument = dynamic_cast<SwXTextDocument*>(xTmp.get());
+            CPPUNIT_ASSERT(mxSwTextDocument.is());
+        }
+        else
+        {
+            CPPUNIT_ASSERT_EQUAL(uno::Any(true), res);
+            if (!bMMFilenameFromColumn && !bDontLoadResult)
+                loadMailMergeDocument(0);
+        }
+    }
+
+    /**
+     * Like parseExport(), but for given mail merge document.
+     */
+    xmlDocUniquePtr parseMailMergeExport(const OUString& rStreamName)
+    {
+        if (mnCurOutputType != text::MailMergeType::FILE)
+            return nullptr;
+
+        OUString name = msMailMergeOutputPrefix + OUString::number(0) + ".odt";
+        std::unique_ptr<SvStream> pStream(
+            parseExportStream(msMailMergeOutputURL + "/" + name, rStreamName));
+
+        return parseXmlStream(pStream.get());
+    }
+
+    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,
+                                      u"com.sun.star.text.TextDocument"_ustr);
+        discardDumpedLayout();
+        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(static_cast<SfxBaseModel*>(mxSwTextDocument.get()));
+    }
+
+    // Returns page number of the first page of a MM document inside the large 
MM document (used in the SHELL case).
+    int documentStartPageNumber(int document) const
+    { // See documentStartPageNumber() .
+        CPPUNIT_ASSERT(mxSwTextDocument);
+        SwWrtShell* shell = mxSwTextDocument->GetDocShell()->GetWrtShell();
+        IDocumentMarkAccess* marks = shell->GetDoc()->getIDocumentMarkAccess();
+        // Unfortunately, the pages are marked using UNO bookmarks, which have 
internals names, so they cannot be referred to by their names.
+        // Assume that there are no other UNO bookmarks than the ones used by 
mail merge, and that they are in the sorted order.
+        IDocumentMarkAccess::const_iterator_t mark;
+        int pos = 0;
+        for (mark = marks->getAllMarksBegin(); mark != marks->getAllMarksEnd() 
&& pos < document;
+             ++mark)
+        {
+            if (IDocumentMarkAccess::GetType(**mark) == 
IDocumentMarkAccess::MarkType::UNO_BOOKMARK)
+                ++pos;
+        }
+        CPPUNIT_ASSERT_EQUAL(document, pos);
+        sal_uInt16 page, dummy;
+        shell->Push();
+        shell->GotoMark(*mark);
+        shell->GetPageNum(page, dummy);
+        shell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+        return page;
+    }
+
+protected:
+    uno::Reference<css::task::XJob> mxJob;
+    std::vector<beans::NamedValue> mMMargs;
+    OUString msMailMergeDocumentURL;
+    OUString msMailMergeOutputURL;
+    OUString msMailMergeOutputPrefix;
+    sal_Int16 mnCurOutputType;
+    rtl::Reference<SwXTextDocument> mxSwTextDocument;
+    uno::Reference<sdbc::XRowSet> mxCurResultSet;
+    const char* maMMtestFilename;
+};
+
+#define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
filter, BaseClass,       \
+                               selection, column)                              
                    \
+    class TestName : public BaseClass                                          
                    \
+    {                                                                          
                    \
+    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, 
MailMergeTestBase,  \
+                           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",                   \
+                           MailMergeTestBase, 0, nullptr)
+
+#define DECLARE_SHELL_MAILMERGE_TEST_SELECTION(TestName, filename, datasource, 
tablename,          \
+                                               selection)                      
                    \
+    DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, nullptr, 
MailMergeTestBase,  \
+                           selection, nullptr)
+
+#define DECLARE_FILE_MAILMERGE_TEST_COLUMN(TestName, filename, datasource, 
tablename, column)      \
+    DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, 
"writer8",                   \
+                           MailMergeTestBase, 0, column)
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to