syncevolution/test/ClientTest.h

931 lines
34 KiB
C
Raw Normal View History

/*
* Copyright (C) 2008 Funambol, Inc.
* Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
* Copyright (C) 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef INCL_TESTSYNCCLIENT
#define INCL_TESTSYNCCLIENT
#include <string>
#include <vector>
#include <list>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <SyncML.h>
#include <TransportAgent.h>
#include <SyncSource.h>
#include "test.h"
#ifdef ENABLE_INTEGRATION_TESTS
#include <cppunit/TestSuite.h>
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
class SyncContext;
class EvolutionSyncSource;
class TransportWrapper;
class TestingSyncSource;
/**
* This class encapsulates logging and checking of a SyncReport.
* When constructed with default parameters, no checking will be done.
* Otherwise the sync report has to contain exactly the expected result.
* When multiple sync sources are active, @b all of them have to behave
* alike (which is how the tests are constructed).
*
* No item is ever supposed to fail.
*/
class CheckSyncReport {
public:
CheckSyncReport(int clAdded = -1, int clUpdated = -1, int clDeleted = -1,
int srAdded = -1, int srUpdated = -1, int srDeleted = -1,
bool mstSucceed = true, SyncMode mode = SYNC_NONE) :
clientAdded(clAdded),
clientUpdated(clUpdated),
clientDeleted(clDeleted),
serverAdded(srAdded),
serverUpdated(srUpdated),
serverDeleted(srDeleted),
mustSucceed(mstSucceed),
syncMode(mode),
m_report(NULL)
{}
virtual ~CheckSyncReport() {}
int clientAdded, clientUpdated, clientDeleted,
serverAdded, serverUpdated, serverDeleted;
bool mustSucceed;
SyncMode syncMode;
// if set, then the report is copied here
SyncReport *m_report;
CheckSyncReport &setMode(SyncMode mode) { syncMode = mode; return *this; }
CheckSyncReport &setReport(SyncReport *report) { m_report = report; return *this; }
/**
* checks that the sync completed as expected and throws
* CPPUnit exceptions if something is wrong
*
* @param res return code from SyncClient::sync()
* @param report the sync report stored in the SyncClient
*/
virtual void check(SyncMLStatus status, SyncReport &report) const;
};
/**
* parameters for running a sync
*/
struct SyncOptions {
/** default maximum message size */
static const long DEFAULT_MAX_MSG_SIZE = 128 * 1024;
/** default maximum object size */
static const long DEFAULT_MAX_OBJ_SIZE = 1024 * 1024 * 1024;
/** sync mode chosen by client */
SyncMode m_syncMode;
/**
* has to be called after a successful or unsuccessful sync,
* will dump the report and (optionally) check the result;
* beware, the later may throw exceptions inside CPPUNIT macros
*/
CheckSyncReport m_checkReport;
/** maximum message size supported by client */
long m_maxMsgSize;
/** maximum object size supported by client */
long m_maxObjSize;
/** enabled large object support */
bool m_loSupport;
/** enabled WBXML (default) */
bool m_isWBXML;
/** overrides resend properties */
int m_retryDuration;
int m_retryInterval;
bool m_isSuspended;
bool m_isAborted;
typedef boost::function<bool (SyncContext &,
SyncOptions &)> Callback_t;
/**
* Callback to be invoked after setting up local sources, but
* before running the engine. May throw exception to indicate
* error and return true to stop sync without error.
*/
Callback_t m_startCallback;
/**
* called while configuration is prepared for sync, see
* SyncContext::prepare()
*/
Callback_t m_prepareCallback;
boost::shared_ptr<TransportAgent> m_transport;
SyncOptions(SyncMode syncMode = SYNC_NONE,
const CheckSyncReport &checkReport = CheckSyncReport(),
long maxMsgSize = DEFAULT_MAX_MSG_SIZE, // 128KB = large enough that normal tests should run with a minimal number of messages
long maxObjSize = DEFAULT_MAX_OBJ_SIZE, // 1GB = basically unlimited...
bool loSupport = false,
bool isWBXML = defaultWBXML(),
Callback_t startCallback = EmptyCallback,
boost::shared_ptr<TransportAgent> transport =
boost::shared_ptr<TransportAgent>()) :
m_syncMode(syncMode),
m_checkReport(checkReport),
m_maxMsgSize(maxMsgSize),
m_maxObjSize(maxObjSize),
m_loSupport(loSupport),
m_isWBXML(isWBXML),
m_retryDuration(300),
m_retryInterval(60),
m_isSuspended(false),
m_isAborted(false),
m_startCallback(startCallback),
m_transport (transport)
{}
SyncOptions &setSyncMode(SyncMode syncMode) { m_syncMode = syncMode; return *this; }
SyncOptions &setCheckReport(const CheckSyncReport &checkReport) { m_checkReport = checkReport; return *this; }
SyncOptions &setMaxMsgSize(long maxMsgSize) { m_maxMsgSize = maxMsgSize; return *this; }
SyncOptions &setMaxObjSize(long maxObjSize) { m_maxObjSize = maxObjSize; return *this; }
SyncOptions &setLOSupport(bool loSupport) { m_loSupport = loSupport; return *this; }
SyncOptions &setWBXML(bool isWBXML) { m_isWBXML = isWBXML; return *this; }
SyncOptions &setRetryDuration(int retryDuration) { m_retryDuration = retryDuration; return *this; }
SyncOptions &setRetryInterval(int retryInterval) { m_retryInterval = retryInterval; return *this; }
SyncOptions &setStartCallback(const Callback_t &callback) { m_startCallback = callback; return *this; }
SyncOptions &setPrepareCallback(const Callback_t &callback) { m_prepareCallback = callback; return *this; }
SyncOptions &setTransportAgent(const boost::shared_ptr<TransportAgent> transport)
{m_transport = transport; return *this;}
static bool EmptyCallback(SyncContext &,
SyncOptions &) { return false; }
/** if CLIENT_TEST_XML=1, then XML, otherwise WBXML */
static bool defaultWBXML();
};
class LocalTests;
class SyncTests;
/**
* This is the interface expected by the testing framework for sync
* clients. It defines several methods that a derived class must
* implement if it wants to use that framework. Note that this class
* itself is not derived from SyncClient. This gives a user of this
* framework the freedom to implement it in two different ways:
* - implement a class derived from both SyncClient and ClientTest
* - add testing of an existing subclass of SyncClient by implementing
* a ClientTest which uses that subclass
*
* The client is expected to support change tracking for multiple
* servers. Although the framework always always tests against the
* same server, for most tests it is necessary to access the database
* without affecting the next synchronization with the server. This is
* done by asking the client for two different sync sources via
* Config::createSourceA and Config::createSourceB which have to
* create them in a suitable way - pretty much as if the client was
* synchronized against different server. A third, different change
* tracking is needed for real synchronizations of the data.
*
* Furthermore the client is expected to support multiple data sources
* of the same kind, f.i. two different address books. This is used to
* test full client A <-> server <-> client B synchronizations in some
* tests or to check server modifications done by client A with a
* synchronization against client B. In those tests client A is mapped
* to the first data source and client B to the second one.
*
* Handling configuration and creating classes is entirely done by the
* subclass of ClientTest, the frameworks makes no assumptions
* about how this is done. Instead it queries the ClientTest for
* properties (like available sync sources) and then creates several
* tests.
*/
class ClientTest {
public:
ClientTest(int serverSleepSec = 0, const std::string &serverLog= "");
virtual ~ClientTest();
/** set up before running a test */
virtual void setup() { }
/** cleanup function to be called when shutting down testing */
typedef void (*Cleanup_t)(void);
/**
* Call this to register another shutdown cleanup functions.
* Every unique function will be called exactly once.
*/
static void registerCleanup(Cleanup_t cleanup);
/**
* Call cleanup functions.
*/
static void shutdown();
/**
* This function registers tests using this instance of ClientTest for
* later use during a test run.
*
* The instance must remain valid until after the tests were
* run. To run them use a separate test runner, like the one from
* client-test-main.cpp.
*/
virtual void registerTests();
typedef ClientTestConfig Config;
/**
* Creates an instance of LocalTests (default implementation) or a
* class derived from it. LocalTests provides tests which cover
* the SyncSource interface and can be executed without a SyncML
* server. It also contains utility functions for working with
* SyncSources.
*
* A ClientTest implementation can, but doesn't have to extend
* these tests by instantiating a derived class here.
*/
virtual LocalTests *createLocalTests(const std::string &name, int sourceParam, ClientTest::Config &co);
/**
* Creates an instance of SyncTests (default) or a class derived
* from it. SyncTests provides tests which cover the actual
* interaction with a SyncML server.
*
* A ClientTest implementation can, but doesn't have to extend
* these tests by instantiating a derived class here.
*/
virtual SyncTests *createSyncTests(const std::string &name, std::vector<int> sourceIndices, bool isClientA = true);
/**
* utility function for dumping items which are C strings with blank lines as separator
*/
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
static int dump(ClientTest &client, TestingSyncSource &source, const char *file);
/**
* utility function for splitting file into items with blank lines as separator
*
* @retval realfile If <file>.<server>.tem exists, then it is used instead
* of the generic version. The caller gets the name of the
* file that was opened here.
*/
static void getItems(const char *file, std::list<std::string> &items, std::string &realfile);
/**
* utility function for importing items with blank lines as separator
*/
static int import(ClientTest &client, TestingSyncSource &source,
const ClientTestConfig &config,
const char *file, std::string &realfile);
/**
* utility function for comparing vCard and iCal files with the external
* synccompare.pl Perl script
*/
static bool compare(ClientTest &client, const char *fileA, const char *fileB);
struct ClientTestConfig config;
/**
* A derived class can use this call to get default test
* cases, but still has to add callbacks which create sources
* and execute a sync session.
*
* Some of the test cases are compiled into the library, other
* depend on the auxiliary files from the "test" directory.
* Currently supported types:
* - vcard30 = vCard 3.0 contacts
* - vcard21 = vCard 2.1 contacts
* - ical20 = iCal 2.0 events
* - vcal10 = vCal 1.0 events
* - itodo20 = iCal 2.0 tasks
*/
static void getTestData(const char *type, Config &config);
/**
* Data sources are enumbered from 0 to n-1 for the purpose of
* testing. This call returns n.
*/
virtual int getNumLocalSources() = 0;
virtual int getNumSyncSources() = 0;
/**
* Called to fill the given test source config with information
* about a sync source identified by its index. It's okay to only
* fill in the available pieces of information and set everything
* else to zero.
* Two kinds of source config indexs are maintained, used for localSources
* and SyncSources, this is because virtual datasoures should be visible as
* a whole to the synccontext while should be viewed as a list of sub
* datasoures for Localtests.
*/
virtual void getLocalSourceConfig(int source, Config &config) = 0;
virtual void getSyncSourceConfig(int source, Config &config) = 0;
/**
* Find the correspoding test source config via config name.
*/
virtual void getSourceConfig(const string &configName, Config &config) =0;
/*
* Give me a test source config name, return the index in localSyncSources.
* */
virtual int getLocalSourcePosition (const string &configName) =0;
/**
* The instance to use as second client. Returning NULL disables
* all checks which require a second client. The returned pointer
* must remain valid throughout the life time of the tests.
*
* The second client must be configured to access the same server
* and have data sources which match the ones from the primary
* client.
*/
virtual ClientTest *getClientB() = 0;
/**
* Execute a synchronization with the selected sync sources
* and the selected synchronization options. The log file
* in LOG has been set up already for the synchronization run
* and should not be changed by the client.
*
* @param activeSources a -1 terminated array of sync source indices
* @param logbase basename for logging: can be used for directory or as file (by adding .log suffix)
* @param options sync options to be used
* @return return code of SyncClient::sync()
*/
virtual SyncMLStatus doSync(
const int *activeSources,
const std::string &logbase,
const SyncOptions &options) = 0;
/**
* This is called after successful sync() calls (res == 0) as well
* as after unsuccessful ones (res != 1). The default implementation
* sleeps for the number of seconds specified when constructing this
* instance and copies the server log if one was named.
*
* @param res result of sync()
* @param logname base name of the current sync log (without ".client.[AB].log" suffix)
*/
virtual void postSync(int res, const std::string &logname);
protected:
/**
* time to sleep in postSync()
*/
int serverSleepSeconds;
/**
* server log file which is copied by postSync() and then
* truncated (Unix only, Windows does not allow such access
* to an open file)
*/
std::string serverLogFileName;
private:
/**
* really a CppUnit::TestFactory, but declared as void * to avoid
* dependencies on the CPPUnit header files: created by
* registerTests() and remains valid until the client is deleted
*/
void *factory;
};
/**
* helper class to encapsulate ClientTest::Config::createsource_t
* pointer and the corresponding parameters
*/
class CreateSource {
public:
CreateSource(ClientTest::Config::createsource_t createSourceParam, ClientTest &clientParam, int sourceParam, bool isSourceAParam) :
createSource(createSourceParam),
client(clientParam),
source(sourceParam),
isSourceA(isSourceAParam) {}
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
TestingSyncSource *operator() () {
CPPUNIT_ASSERT(createSource);
return createSource(client, source, isSourceA);
}
const ClientTest::Config::createsource_t createSource;
ClientTest &client;
const int source;
const bool isSourceA;
};
/**
* local test of one sync source and utility functions also used by
* sync tests
*/
class LocalTests : public CppUnit::TestSuite, public CppUnit::TestFixture {
public:
/** the client we are testing */
ClientTest &client;
/** number of the source we are testing in that client */
const int source;
/** configuration that corresponds to source */
const ClientTest::Config config;
/** helper funclets to create sources */
CreateSource createSourceA, createSourceB;
/** if set, then this will be called at the end of testing */
void (*cleanupSources)();
LocalTests(const std::string &name, ClientTest &cl, int sourceParam, ClientTest::Config &co) :
CppUnit::TestSuite(name),
client(cl),
source(sourceParam),
config(co),
createSourceA(co.createSourceA, cl, sourceParam, true),
createSourceB(co.createSourceB, cl, sourceParam, false)
{}
/** set up before running a test */
virtual void setUp() { client.setup(); }
/**
* adds the supported tests to the instance itself;
* this is the function that a derived class can override
* to add additional tests
*/
virtual void addTests();
/**
* opens source and inserts the given item; can be called
* regardless whether the data source already contains items or not
*
* @param relaxed if true, then disable some of the additional checks after adding the item
* @retval inserted actual data that was inserted, optional
* @return the LUID of the inserted item
*/
virtual std::string insert(CreateSource createSource, const char *data, bool relaxed = false, std::string *inserted = NULL);
/**
* assumes that exactly one element is currently inserted and updates it with the given item
*
* @param check if true, then reopen the source and verify that the reported items are as expected
*/
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
virtual void update(CreateSource createSource, const char *data, bool check = true);
/**
* updates one item identified by its LUID with the given item
*
* The type of the item is cleared, as in insert() above.
*/
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
virtual void update(CreateSource createSource, const char *data, const std::string &luid);
/** deletes all items locally via sync source */
virtual void deleteAll(CreateSource createSource);
/**
* takes two databases, exports them,
* then compares them using synccompare
*
* @param refFile existing file with source reference items, NULL uses a dump of sync source A instead
* @param copy a sync source which contains the copied items, begin/endSync will be called
* @param raiseAssert raise assertion if comparison yields differences (defaults to true)
* @return true if the two databases are equal
*/
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
virtual bool compareDatabases(const char *refFile, TestingSyncSource &copy, bool raiseAssert = true);
/**
* compare data in source with vararg list of std::string pointers, NULL terminated
*/
void compareDatabases(TestingSyncSource &copy, ...);
/**
* insert artificial items, number of them determined by TEST_EVOLUTION_NUM_ITEMS
* unless passed explicitly
*
* @param createSource a factory for the sync source that is to be used
* @param startIndex IDs are generated starting with this value
* @param numItems number of items to be inserted if non-null, otherwise TEST_EVOLUTION_NUM_ITEMS is used
* @param size minimum size for new items
* @return number of items inserted
*/
virtual std::list<std::string> insertManyItems(CreateSource createSource, int startIndex = 1, int numItems = 0, int size = -1);
/**
* create an artificial item for the current database
*
* @param item item number: items with different number should be
* recognized as different by SyncML servers
* @param revision differentiates items with the same item number (= updates of an older item)
* @param size if > 0, then create items at least that large (in bytes)
* @return created item
*/
std::string createItem(int item, const std::string &revision, int size);
std::string createItem(int item, int revision, int size) {
char buffer[32];
sprintf(buffer, "%d", revision);
return createItem(item, std::string(buffer), size);
}
/* for more information on the different tests see their implementation */
virtual void testOpen();
virtual void testIterateTwice();
virtual void testSimpleInsert();
virtual void testLocalDeleteAll();
virtual void testComplexInsert();
virtual void testLocalUpdate();
virtual void testChanges();
virtual void testImport();
virtual void testImportDelete();
virtual void testManyChanges();
virtual void testLinkedItemsParent();
virtual void testLinkedItemsChild();
virtual void testLinkedItemsParentChild();
virtual void testLinkedItemsChildParent();
virtual void testLinkedItemsChildChangesParent();
virtual void testLinkedItemsRemoveParentFirst();
virtual void testLinkedItemsRemoveNormal();
virtual void testLinkedItemsInsertParentTwice();
virtual void testLinkedItemsInsertChildTwice();
virtual void testLinkedItemsParentUpdate();
virtual void testLinkedItemsUpdateChild();
virtual void testLinkedItemsInsertBothUpdateChild();
virtual void testLinkedItemsInsertBothUpdateParent();
};
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
int countItemsOfType(TestingSyncSource *source, int state);
std::list<std::string> listItemsOfType(TestingSyncSource *source, int state);
/**
* Tests synchronization with one or more sync sources enabled.
* When testing multiple sources at once only the first config
* is checked to see which tests can be executed.
*/
class SyncTests : public CppUnit::TestSuite, public CppUnit::TestFixture {
public:
/** the client we are testing */
ClientTest &client;
SyncTests(const std::string &name, ClientTest &cl, std::vector<int> sourceIndices, bool isClientA = true);
~SyncTests();
/**
* adds the supported tests to the instance itself
* @param isFirstSource the tests are getting generated for a single source,
* the one which was listed first; some tests are the
* same for all sources and should only be run once
*/
virtual void addTests(bool isFirstSource = false);
/** set up before running a test */
virtual void setUp() { client.setup(); }
protected:
/** list with all local test classes for manipulating the sources and their index in the client */
typedef std::vector< std::pair<int, LocalTests *> > source_array_t;
source_array_t sources;
typedef source_array_t::iterator source_it;
/**
* Stack of log file prefixes which are to be appended to the base name,
* which already contains the current test name. Add a new prefix by
* instantiating SyncPrefix. Its destructor takes care of popping
* the prefix.
*/
std::list<std::string> logPrefixes;
class SyncPrefix {
SyncTests &m_tests;
public:
SyncPrefix(const std::string &prefix, SyncTests &tests) :
m_tests(tests) {
tests.logPrefixes.push_back(prefix);
}
~SyncPrefix() {
m_tests.logPrefixes.pop_back();
}
};
friend class SyncPrefix;
/** the indices from sources, terminated by -1 (for sync()) */
int *sourceArray;
/** utility functions for second client */
SyncTests *accessClientB;
enum DeleteAllMode {
DELETE_ALL_SYNC, /**< make sure client and server are in sync,
delete locally,
sync again */
DELETE_ALL_REFRESH /**< delete locally, refresh server */
};
/**
* Compare databases second client with either reference file(s)
* or first client. The reference file(s) must follow the naming
* scheme <reFileBase><source name>.dat
*/
virtual bool compareDatabases(const char *refFileBase = NULL,
bool raiseAssert = true);
/** deletes all items locally and on server */
virtual void deleteAll(DeleteAllMode mode = DELETE_ALL_SYNC);
/** get both clients in sync with empty server, then copy one item from client A to B */
virtual void doCopy();
/**
* replicate server database locally: same as SYNC_REFRESH_FROM_SERVER,
* but done with explicit local delete and then a SYNC_SLOW because some
* servers do no support SYNC_REFRESH_FROM_SERVER
*/
virtual void refreshClient(SyncOptions options = SyncOptions());
/* for more information on the different tests see their implementation */
// do a two-way sync without additional checks,
// may or may not actually be done in two-way mode
virtual void testTwoWaySync() {
doSync(SyncOptions(SYNC_TWO_WAY));
}
// do a slow sync without additional checks
virtual void testSlowSync() {
doSync(SyncOptions(SYNC_SLOW,
CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_SLOW)));
}
// do a refresh from server sync without additional checks
virtual void testRefreshFromServerSync() {
doSync(SyncOptions(SYNC_REFRESH_FROM_SERVER,
CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_SERVER)));
}
// do a refresh from client sync without additional checks
virtual void testRefreshFromClientSync() {
doSync(SyncOptions(SYNC_REFRESH_FROM_CLIENT,
CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_CLIENT)));
}
// delete all items, locally and on server using two-way sync
virtual void testDeleteAllSync() {
deleteAll(DELETE_ALL_SYNC);
}
virtual void testDeleteAllRefresh();
virtual void testRefreshFromClientSemantic();
virtual void testRefreshFromServerSemantic();
virtual void testRefreshStatus();
// test that a two-way sync copies an item from one address book into the other
void testCopy() {
doCopy();
compareDatabases();
}
virtual void testUpdate();
virtual void testComplexUpdate();
virtual void testDelete();
virtual void testMerge();
virtual void testTwinning();
virtual void testOneWayFromServer();
virtual void testOneWayFromClient();
bool doConversionCallback(bool *success,
SyncContext &client,
SyncOptions &options);
virtual void testConversion();
virtual void testItems();
virtual void testItemsXML();
virtual void testAddUpdate();
// test copying with maxMsg and no large object support
void testMaxMsg() {
doVarSizes(true, false);
}
// test copying with maxMsg and large object support
void testLargeObject() {
doVarSizes(true, true);
}
virtual void testManyItems();
virtual void testManyDeletes();
virtual void testSlowSyncSemantic();
virtual void testComplexRefreshFromServerSemantic();
virtual void doInterruptResume(int changes,
boost::shared_ptr<TransportWrapper> wrapper);
/**
* CLIENT_ = change made on client B before interrupting
* SERVER_ = change made on client A and applied to server before interrupting
* while sending to B
* _ADD = new item added
* _REMOVE = existing item deleted
* _UPDATE = existing item replaced
* BIG = when adding or updating, make the new item so large that it does
* not fit into a single message
*/
enum {
CLIENT_ADD = (1<<0),
CLIENT_REMOVE = (1<<1),
CLIENT_UPDATE = (1<<2),
SERVER_ADD = (1<<3),
SERVER_REMOVE = (1<<4),
SERVER_UPDATE = (1<<5),
BIG = (1<<6)
};
virtual void testInterruptResumeClientAdd();
virtual void testInterruptResumeClientRemove();
virtual void testInterruptResumeClientUpdate();
virtual void testInterruptResumeServerAdd();
virtual void testInterruptResumeServerRemove();
virtual void testInterruptResumeServerUpdate();
virtual void testInterruptResumeClientAddBig();
virtual void testInterruptResumeClientUpdateBig();
virtual void testInterruptResumeServerAddBig();
virtual void testInterruptResumeServerUpdateBig();
virtual void testInterruptResumeFull();
virtual void testUserSuspendClientAdd();
virtual void testUserSuspendClientRemove();
virtual void testUserSuspendClientUpdate();
virtual void testUserSuspendServerAdd();
virtual void testUserSuspendServerRemove();
virtual void testUserSuspendServerUpdate();
virtual void testUserSuspendClientAddBig();
virtual void testUserSuspendClientUpdateBig();
virtual void testUserSuspendServerAddBig();
virtual void testUserSuspendServerUpdateBig();
virtual void testUserSuspendFull();
virtual void testResendClientAdd();
virtual void testResendClientRemove();
virtual void testResendClientUpdate();
virtual void testResendServerAdd();
virtual void testResendServerRemove();
virtual void testResendServerUpdate();
virtual void testResendFull();
virtual void testResendProxyClientAdd();
virtual void testResendProxyClientRemove();
virtual void testResendProxyClientUpdate();
virtual void testResendProxyServerAdd();
virtual void testResendProxyServerRemove();
virtual void testResendProxyServerUpdate();
virtual void testResendProxyFull();
virtual void testTimeout();
/**
* implements testMaxMsg(), testLargeObject(), testLargeObjectEncoded()
* using a sequence of items with varying sizes
*/
virtual void doVarSizes(bool withMaxMsgSize,
bool withLargeObject);
/**
* executes a sync with the given options,
* checks the result and (optionally) the sync report
*/
virtual void doSync(const SyncOptions &options);
virtual void doSync(const char *logPrefix,
const SyncOptions &options) {
SyncPrefix prefix(logPrefix, *this);
doSync(options);
}
virtual void postSync(int res, const std::string &logname);
};
/*
* A transport wraper wraps a real transport impl and gives user
* possibility to do additional work before/after transport operation.
* We use TransportFaultInjector to emulate a network failure;
* We use UserSuspendInjector to emulate a user suspend after receving
* a response.
*/
class TransportWrapper : public TransportAgent {
protected:
int m_interruptAtMessage, m_messageCount;
boost::shared_ptr<TransportAgent> m_wrappedAgent;
Status m_status;
SyncOptions *m_options;
public:
TransportWrapper() {
m_messageCount = 0;
m_interruptAtMessage = -1;
m_wrappedAgent = boost::shared_ptr<TransportAgent>();
m_status = INACTIVE;
m_options = NULL;
}
~TransportWrapper() {
}
/**
* -1 for wrappers which are meant to be used without message resending,
* otherwise the number x for which "interrupt" <= x will lead to
* an aborted sync (0 for TransportResendInjector, 2 for TransportResendProxy)
*/
virtual int getResendFailureThreshold() { return -1; }
virtual int getMessageCount() { return m_messageCount; }
virtual void setURL(const std::string &url) { m_wrappedAgent->setURL(url); }
virtual void setContentType(const std::string &type) { m_wrappedAgent->setContentType(type); }
virtual void setAgent(boost::shared_ptr<TransportAgent> agent) {m_wrappedAgent = agent;}
virtual void setSyncOptions(SyncOptions *options) {m_options = options;}
virtual void setInterruptAtMessage (int interrupt) {m_interruptAtMessage = interrupt;}
virtual void cancel() { m_wrappedAgent->cancel(); }
virtual void shutdown() { m_wrappedAgent->shutdown(); }
virtual void rewind() {
m_messageCount = 0;
m_interruptAtMessage = -1;
m_status = INACTIVE;
m_options = NULL;
m_wrappedAgent.reset();
}
virtual Status wait(bool noReply = false) { return m_status; }
virtual void setTimeout(int seconds) { m_wrappedAgent->setTimeout(seconds); }
};
/** assert equality, include string in message if unequal */
#define CLIENT_TEST_EQUAL( _prefix, \
_expected, \
_actual ) \
CPPUNIT_ASSERT_EQUAL_MESSAGE( std::string(_prefix) + ": " + #_expected + " == " + #_actual, \
_expected, \
_actual )
/** execute _x and then check the status of the _source pointer */
#define SOURCE_ASSERT_NO_FAILURE(_source, _x) \
{ \
CPPUNIT_ASSERT_NO_THROW(_x); \
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
CPPUNIT_ASSERT((_source)); \
}
/** check _x for true and then the status of the _source pointer */
#define SOURCE_ASSERT(_source, _x) \
{ \
CPPUNIT_ASSERT(_x); \
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
CPPUNIT_ASSERT((_source)); \
}
/** check that _x evaluates to a specific value and then the status of the _source pointer */
#define SOURCE_ASSERT_EQUAL(_source, _value, _x) \
{ \
CPPUNIT_ASSERT_EQUAL(_value, _x); \
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
CPPUNIT_ASSERT((_source)); \
}
/** same as SOURCE_ASSERT() with a specific failure message */
#define SOURCE_ASSERT_MESSAGE(_message, _source, _x) \
{ \
CPPUNIT_ASSERT_MESSAGE((_message), (_x)); \
redesigned SyncSource base class + API The main motivation for this change is that it allows the implementor of a backend to choose the implementations for the different aspects of a datasource (change tracking, item import/export, logging, ...) independently of each other. For example, change tracking via revision strings can now be combined with exchanging data with the Synthesis engine via a single string (the traditional method in SyncEvolution) and with direct access to the Synthesis field list (now possible for the first time). The new backend API is based on the concept of providing implementations for certain functionality via function objects instead of implementing certain virtual methods. The advantage is that implementors can define their own, custom interfaces and mix and match implementations of the different groups of functionality. Logging (see SyncSourceLogging in a later commit) can be done by wrapping some arbitrary other item import/export function objects (decorator design pattern). The class hierarchy is now this: - SyncSourceBase: interface for common utility code, all other classes are derived from it and thus can use that code - SyncSource: base class which implements SyncSourceBase and hooks a datasource into the SyncEvolution core; its "struct Operations" holds the function objects which can be implemented in different ways - TestingSyncSource: combines some of the following classes into an interface that is expected by the client-test program; backends only have to derive from (and implement this) if they want to use the automated testing - TrackingSyncSource: provides the same functionality as before (change tracking via revision strings, item import/export as string) in a single interface; the description of the pure virtual methods are duplicated so that developers can go through this class and find everything they need to know to implement it The following classes contain the code that was previously found in the EvolutionSyncSource base class. Implementors can derive from them and call the init() methods to inherit and activate the functionality: - SyncSourceSession: binds Synthesis session callbacks to virtual methods beginSync(), endSync() - SyncSourceChanges: implements Synthesis item tracking callbacks with set of LUIDs that the user of the class has to fill - SyncSourceDelete: binds Synthesis delete callback to virtual method - SyncSourceRaw: read and write items in the backends format, used for testing and backup/restore - SyncSourceSerialize: exchanges items with Synthesis engine using a string representation of the data; this is how EvolutionSyncSource has traditionally worked, so much of the same virtual methods are now in this class - SyncSourceRevisions: utility class which does change tracking via some kind of "revision" string which changes each time an item is modified; this code was previously in the TrackingSyncSource
2009-08-25 09:27:46 +02:00
CPPUNIT_ASSERT((_source)); \
}
/**
* convenience macro for adding a test name like a function,
* to be used inside addTests() of an instance of that class
*
* @param _class class which contains the function
* @param _function a function without parameters in that class
*/
#define ADD_TEST(_class, _function) \
ADD_TEST_TO_SUITE(this, _class, _function)
#define ADD_TEST_TO_SUITE(_suite, _class, _function) \
_suite->addTest(FilterTest(new CppUnit::TestCaller<_class>(_suite->getName() + "::" #_function, &_class::_function, *this)))
SE_END_CXX
#endif // ENABLE_INTEGRATION_TESTS
#endif // INCL_TESTSYNCCLIENT