made it possible for clients to not just configure existing tests, but also add their own ones

git-svn-id: https://core.forge.funambol.org/svn/core/top-level/trunk/3x/client-api/native@17602 e8e8ed6c-164c-0410-afcf-9e9a7c7d8c10
This commit is contained in:
Patrick Ohly 2007-06-27 20:08:46 +00:00
parent 8de15bab34
commit 3f95bf1734
2 changed files with 425 additions and 216 deletions

View File

@ -39,34 +39,6 @@
#include <iostream>
#include <algorithm>
/** execute _x and then check the status of the _source pointer */
#define SOURCE_ASSERT_NO_FAILURE(_source, _x) \
{ \
CPPUNIT_ASSERT_NO_THROW(_x); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** check _x for true and then the status of the _source pointer */
#define SOURCE_ASSERT(_source, _x) \
{ \
CPPUNIT_ASSERT(_x); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** 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); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** same as SOURCE_ASSERT() with a specific failure message */
#define SOURCE_ASSERT_MESSAGE(_message, _source, _x) \
{ \
CPPUNIT_ASSERT_MESSAGE((_message), (_x)); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** utility function to iterate over different kinds of items in a sync source */
static int countAnyItems(
SyncSource *source,
@ -124,6 +96,30 @@ static int countItems( SyncSource *source )
return res;
}
int countItemsOfType(SyncSource *source, itemType type)
{
int res = 0;
switch(type) {
case NEW_ITEMS:
res = countNewItems(source);
break;
case UPDATED_ITEMS:
res = countUpdatedItems(source);
break;
case DELETED_ITEMS:
res = countDeletedItems(source);
break;
case TOTAL_ITEMS:
res = countItems(source);
break;
default:
CPPUNIT_ASSERT(false);
break;
}
return res;
}
static void importItem(SyncSource *source, std::string &data)
{
CPPUNIT_ASSERT(source);
@ -139,69 +135,8 @@ static void importItem(SyncSource *source, std::string &data)
}
}
/**
* 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) {}
SyncSource *operator() () {
CPPUNIT_ASSERT(createSource);
return createSource(client, source, isSourceA);
}
const ClientTest::Config::createsource_t createSource;
ClientTest &client;
const int source;
const bool isSourceA;
};
/**
* convenience macro for adding a test name like a function,
* to be used inside 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) \
addTest(new CppUnit::TestCaller<_class>(getName() + "::" #_function, &_class::_function, *this))
/**
* 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;
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)
{}
/** adds the supported tests to the instance itself */
void addTests() {
void LocalTests::addTests() {
if (config.createSourceA) {
ADD_TEST(LocalTests, testOpen);
ADD_TEST(LocalTests, testIterateTwice);
@ -241,7 +176,7 @@ public:
* The type of the item is unset; it is assumed that the source
* can handle that.
*/
void insert(CreateSource createSource, const char *data) {
void LocalTests::insert(CreateSource createSource, const char *data) {
// create source
std::auto_ptr<SyncSource> source(createSource());
CPPUNIT_ASSERT(source.get() != 0);
@ -293,7 +228,7 @@ public:
*
* The type of the item is cleared, as in insert() above.
*/
void update(CreateSource createSource, const char *data) {
void LocalTests::update(CreateSource createSource, const char *data) {
CPPUNIT_ASSERT(createSource.createSource);
CPPUNIT_ASSERT(data);
@ -328,7 +263,7 @@ public:
}
/** deletes all items locally via sync source */
void deleteAll(CreateSource createSource) {
void LocalTests::deleteAll(CreateSource createSource) {
CPPUNIT_ASSERT(createSource.createSource);
// create source
@ -368,7 +303,7 @@ public:
* @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)
*/
void compareDatabases(const char *refFile, SyncSource &copy, bool raiseAssert = true) {
void LocalTests::compareDatabases(const char *refFile, SyncSource &copy, bool raiseAssert) {
CPPUNIT_ASSERT(config.dump);
std::string sourceFile, copyFile;
@ -405,7 +340,7 @@ public:
* @param size minimum size for new items
* @return number of items inserted
*/
int insertManyItems(CreateSource createSource, int startIndex = 1, int numItems = 0, int size = -1) {
int LocalTests::insertManyItems(CreateSource createSource, int startIndex, int numItems, int size) {
CPPUNIT_ASSERT(config.templateItem);
CPPUNIT_ASSERT(config.uniqueProperties);
@ -515,7 +450,7 @@ public:
}
// creating sync source
void testOpen() {
void LocalTests::testOpen() {
// check requirements
CPPUNIT_ASSERT(config.createSourceA);
@ -525,7 +460,7 @@ public:
}
// restart scanning of items
void testIterateTwice() {
void LocalTests::testIterateTwice() {
// check requirements
CPPUNIT_ASSERT(config.createSourceA);
@ -539,7 +474,7 @@ public:
}
// insert one contact without clearing the source first
void testSimpleInsert() {
void LocalTests::testSimpleInsert() {
// check requirements
CPPUNIT_ASSERT(config.insertItem);
CPPUNIT_ASSERT(config.createSourceA);
@ -548,7 +483,7 @@ public:
}
// delete all items
void testLocalDeleteAll() {
void LocalTests::testLocalDeleteAll() {
// check requirements
CPPUNIT_ASSERT(config.insertItem);
CPPUNIT_ASSERT(config.createSourceA);
@ -559,14 +494,14 @@ public:
}
// clean database, then insert
void testComplexInsert() {
void LocalTests::testComplexInsert() {
testLocalDeleteAll();
testSimpleInsert();
testIterateTwice();
}
// clean database, insert item, update it
void testLocalUpdate() {
void LocalTests::testLocalUpdate() {
// check additional requirements
CPPUNIT_ASSERT(config.updateItem);
@ -576,7 +511,7 @@ public:
}
// complex sequence of changes
void testChanges() {
void LocalTests::testChanges() {
// check additional requirements
CPPUNIT_ASSERT(config.createSourceB);
@ -653,7 +588,7 @@ public:
}
// clean database, import file, then export again and compare
void testImport() {
void LocalTests::testImport() {
// check additional requirements
CPPUNIT_ASSERT(config.import);
CPPUNIT_ASSERT(config.dump);
@ -678,7 +613,7 @@ public:
}
// same as testImport() with immediate delete
void testImportDelete() {
void LocalTests::testImportDelete() {
testImport();
// delete again, because it was observed that this did not
@ -687,7 +622,7 @@ public:
}
// test change tracking with large number of items
void testManyChanges() {
void LocalTests::testManyChanges() {
// check additional requirements
CPPUNIT_ASSERT(config.templateItem);
CPPUNIT_ASSERT(config.uniqueProperties);
@ -727,19 +662,9 @@ public:
SOURCE_ASSERT_EQUAL(copy.get(), 0, copy->endSync());
CPPUNIT_ASSERT_NO_THROW(copy.reset());
}
};
/**
* 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::SyncTests(const std::string &name, ClientTest &cl, std::vector<int> sourceIndices, bool isClientA) :
CppUnit::TestSuite(name),
client(cl) {
sourceArray = new int[sourceIndices.size() + 1];
@ -751,7 +676,7 @@ public:
if (config.sourceName) {
sourceArray[sources.size()] = *it;
sources.push_back(std::pair<int,LocalTests *>(*it, new LocalTests(config.sourceName, cl, *it, config)));
sources.push_back(std::pair<int,LocalTests *>(*it, cl.createLocalTests(config.sourceName, *it, config)));
}
}
sourceArray[sources.size()] = -1;
@ -759,13 +684,13 @@ public:
// check whether we have a second client
ClientTest *clientB = cl.getClientB();
if (clientB) {
accessClientB = new SyncTests(name, *clientB, sourceIndices, false);
accessClientB = clientB->createSyncTests(name, sourceIndices, false);
} else {
accessClientB = 0;
}
}
~SyncTests() {
SyncTests::~SyncTests() {
for (source_it it = sources.begin();
it != sources.end();
++it) {
@ -778,7 +703,7 @@ public:
}
/** adds the supported tests to the instance itself */
void addTests() {
void SyncTests::addTests() {
if (sources.size()) {
const ClientTest::Config &config(sources[0].second->config);
@ -838,26 +763,8 @@ public:
}
}
private:
/** list with all local test classes for manipulating the sources and their index in the client */
std::vector< std::pair<int, LocalTests *> > sources;
typedef std::vector< std::pair<int, LocalTests *> >::iterator source_it;
/** 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 of first and second client */
void compareDatabases() {
void SyncTests::compareDatabases() {
source_it it1;
source_it it2;
@ -875,7 +782,7 @@ private:
}
/** deletes all items locally and on server */
void deleteAll(DeleteAllMode mode = DELETE_ALL_SYNC) {
void SyncTests::deleteAll(DeleteAllMode mode) {
source_it it;
switch(mode) {
@ -902,7 +809,7 @@ private:
}
/** get both clients in sync with empty server, then copy one item from client A to B */
void doCopy() {
void SyncTests::doCopy() {
// check requirements
CPPUNIT_ASSERT(accessClientB);
@ -927,7 +834,7 @@ private:
* but done with explicit local delete and then a SYNC_SLOW because some
* servers do no support SYNC_REFRESH_FROM_SERVER
*/
void refreshClient() {
void SyncTests::refreshClient() {
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->deleteAll(it->second->createSourceA);
@ -935,31 +842,9 @@ private:
sync(SYNC_SLOW, ".refresh", CheckSyncReport(-1,0,0, 0,0,0));
}
// do a two-way sync without additional checks
void testTwoWaySync() {
sync(SYNC_TWO_WAY);
}
// do a slow sync without additional checks
void testSlowSync() {
sync(SYNC_SLOW);
}
// do a refresh from server sync without additional checks
void testRefreshFromServerSync() {
sync(SYNC_REFRESH_FROM_SERVER);
}
// do a refresh from client sync without additional checks
void testRefreshFromClientSync() {
sync(SYNC_REFRESH_FROM_CLIENT);
}
// delete all items, locally and on server using two-way sync
void testDeleteAllSync() {
deleteAll(DELETE_ALL_SYNC);
}
// delete all items, locally and on server using refresh-from-client sync
void testDeleteAllRefresh() {
void SyncTests::testDeleteAllRefresh() {
source_it it;
// copy something to server first; doesn't matter whether it has the
@ -995,7 +880,7 @@ private:
}
// test that a refresh sync of an empty server leads to an empty datatbase
void testRefreshSemantic() {
void SyncTests::testRefreshSemantic() {
source_it it;
// clean client and server
@ -1024,7 +909,7 @@ private:
// - insert one other item
// - refresh from client
// => no items should now be listed as new, updated or deleted for this client during another sync
void testRefreshStatus() {
void SyncTests::testRefreshStatus() {
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
@ -1040,15 +925,9 @@ private:
sync(SYNC_TWO_WAY, ".two-way", CheckSyncReport(0,0,0, 0,0,0));
}
// test that a two-way sync copies an item from one address book into the other
void testCopy() {
doCopy();
compareDatabases();
}
// test that a two-way sync copies updates from database to the other client,
// using simple data commonly supported by servers
void testUpdate() {
void SyncTests::testUpdate() {
CPPUNIT_ASSERT(sources.begin() != sources.end());
CPPUNIT_ASSERT(sources.begin()->second->config.updateItem);
@ -1069,7 +948,7 @@ private:
// test that a two-way sync copies updates from database to the other client,
// using data that some, but not all servers support, like adding a second
// phone number to a contact
void testComplexUpdate() {
void SyncTests::testComplexUpdate() {
// setup client A, B and server so that they all contain the same item
doCopy();
@ -1086,7 +965,7 @@ private:
// test that a two-way sync deletes the copy of an item in the other database
void testDelete() {
void SyncTests::testDelete() {
// setup client A, B and server so that they all contain the same item
doCopy();
@ -1113,7 +992,7 @@ private:
// test what the server does when it finds that different
// fields of the same item have been modified
void testMerge() {
void SyncTests::testMerge() {
// setup client A, B and server so that they all contain the same item
doCopy();
@ -1151,7 +1030,7 @@ private:
// test what the server does when it has to execute a slow sync
// with identical data on client and server:
// expected behaviour is that nothing changes
void testTwinning() {
void SyncTests::testTwinning() {
// clean server and client A
deleteAll();
@ -1187,7 +1066,7 @@ private:
// - delete on first client, sync that to second client
// via two-way sync + one-way-from-server
// => one item left on second client (the one inserted locally)
void testOneWayFromServer() {
void SyncTests::testOneWayFromServer() {
// no items anywhere
deleteAll();
accessClientB->refreshClient();
@ -1342,7 +1221,7 @@ private:
// - delete on second client, sync that to first client
// via one-way-from-client, two-way
// => one item left on first client (the one inserted locally)
void testOneWayFromClient() {
void SyncTests::testOneWayFromClient() {
// no items anywhere
deleteAll();
accessClientB->deleteAll();
@ -1490,7 +1369,7 @@ private:
// creates several items, transmits them back and forth and
// then compares which of them have been preserved
void testItems() {
void SyncTests::testItems() {
// clean server and first test database
deleteAll();
@ -1517,7 +1396,7 @@ private:
// for a non-existant local item. This is a server bug, the client does not
// have to handle that. See
// http://forge.objectweb.org/tracker/?func=detail&atid=100096&aid=305018&group_id=96
void testAddUpdate() {
void SyncTests::testAddUpdate() {
// clean server and both test databases
deleteAll();
accessClientB->refreshClient();
@ -1542,23 +1421,6 @@ private:
compareDatabases();
}
// test copying with maxMsg and no large object support
void testMaxMsg() {
doVarSizes(true, false, NULL);
}
// test copying with maxMsg and large object support
void testLargeObject() {
doVarSizes(true, true, NULL);
}
// test copying with maxMsg and large object support using explicit "bin" encoding
void testLargeObjectBin() {
doVarSizes(true, true, "bin");
}
// test copying with maxMsg and large object support using B64 encoding
void testLargeObjectEncoded() {
doVarSizes(true, true, "b64");
}
//
// stress tests: execute some of the normal operations,
// but with large number of artificially generated items
@ -1567,7 +1429,7 @@ private:
// two-way sync with clean client/server,
// followed by slow sync and comparison
// via second client
void testManyItems() {
void SyncTests::testManyItems() {
// clean server and client A
deleteAll();
@ -1599,7 +1461,7 @@ private:
* implements testMaxMsg(), testLargeObject(), testLargeObjectEncoded()
* using a sequence of items with varying sizes
*/
void doVarSizes(bool withMaxMsgSize,
void SyncTests::doVarSizes(bool withMaxMsgSize,
bool withLargeObject,
const char *encoding) {
static const int maxMsgSize = 8 * 1024;
@ -1641,13 +1503,13 @@ private:
compareDatabases();
}
void sync(SyncMode syncMode,
const std::string &logprefix = "",
CheckSyncReport checkReport = CheckSyncReport(),
long maxMsgSize = 0,
long maxObjSize = 0,
bool loSupport = false,
const char *encoding = "") {
void SyncTests::sync(SyncMode syncMode,
const std::string &logprefix,
CheckSyncReport checkReport,
long maxMsgSize,
long maxObjSize,
bool loSupport,
const char *encoding) {
int res = 0;
static int syncCounter = 0;
static std::string lastTest;
@ -1688,7 +1550,6 @@ private:
}
CPPUNIT_ASSERT( !res );
}
};
/** generates tests on demand based on what the client supports */
@ -1709,7 +1570,7 @@ public:
client.getSourceConfig(source, config);
if (config.sourceName) {
LocalTests *sourcetests =
new LocalTests(tests->getName() + "::" + config.sourceName, client, source, config);
client.createLocalTests(tests->getName() + "::" + config.sourceName, source, config);
sourcetests->addTests();
tests->addTest(sourcetests);
}
@ -1726,7 +1587,7 @@ public:
std::vector<int> sources;
sources.push_back(source);
SyncTests *synctests =
new SyncTests(tests->getName() + "::" + config.sourceName, client, sources);
client.createSyncTests(tests->getName() + "::" + config.sourceName, sources);
synctests->addTests();
tests->addTest(synctests);
}
@ -1751,7 +1612,7 @@ public:
}
if (sources.size() > 1) {
SyncTests *synctests =
new SyncTests(tests->getName() + "::" + name, client, sources);
client.createSyncTests(tests->getName() + "::" + name, sources);
synctests->addTests();
tests->addTest(synctests);
synctests = 0;
@ -1759,7 +1620,7 @@ public:
// now also in reversed order - who knows, it might make a difference
std::reverse(sources.begin(), sources.end());
synctests =
new SyncTests(tests->getName() + "::" + name_reversed, client, sources);
client.createSyncTests(tests->getName() + "::" + name_reversed, sources);
synctests->addTests();
tests->addTest(synctests);
synctests = 0;
@ -1797,6 +1658,16 @@ ClientTest::~ClientTest()
}
}
LocalTests *ClientTest::createLocalTests(const std::string &name, int sourceParam, ClientTest::Config &co)
{
return new LocalTests(name, *this, sourceParam, co);
}
SyncTests *ClientTest::createSyncTests(const std::string &name, std::vector<int> sourceIndices, bool isClientA)
{
return new SyncTests(name, *this, sourceIndices, isClientA);
}
int ClientTest::dump(ClientTest &client, SyncSource &source, const char *file)
{
std::ofstream out(file);

View File

@ -20,11 +20,16 @@
#define INCL_TESTSYNCCLIENT
#include <string>
#include <vector>
#include "spds/SyncSource.h"
#include "spds/SyncReport.h"
#ifdef ENABLE_INTEGRATION_TESTS
#include <cppunit/TestSuite.h>
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
/**
* This class encapsulates logging and checking of a SyncReport.
* When constructed with default parameters, no checking will be done.
@ -59,6 +64,9 @@ class CheckSyncReport {
virtual void check(int res, SyncReport &report) const;
};
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
@ -108,16 +116,39 @@ class ClientTest {
virtual ~ClientTest();
/**
* This is the only function provided by ClientTest itself:
* it registers tests using this instance of ClientTest for
* This function registers tests using this instance of ClientTest for
* later use during a test run.
*
* Theinstance must remain valid until after the tests were
* 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();
class 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
*/
@ -421,6 +452,273 @@ class ClientTest {
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) {}
SyncSource *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;
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)
{}
/**
* 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
*
* The type of the item is unset; it is assumed that the source
* can handle that.
*/
virtual void insert(CreateSource createSource, const char *data);
/**
* assumes that exactly one element is currently inserted and updates it with the given item
*
* The type of the item is cleared, as in insert() above.
*/
virtual void update(CreateSource createSource, const char *data);
/** 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)
*/
virtual void compareDatabases(const char *refFile, SyncSource &copy, bool raiseAssert = true);
/**
* 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 int insertManyItems(CreateSource createSource, int startIndex = 1, int numItems = 0, int size = -1);
/* 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();
};
enum itemType {
NEW_ITEMS,
UPDATED_ITEMS,
DELETED_ITEMS,
TOTAL_ITEMS
};
/**
* utility function which counts items of a certain kind known to the sync source
* @param source valid source ready to iterate; NULL triggers an assert
* @param itemType determines which iterator functions are used
* @return number of valid items iterated over
*/
int countItemsOfType(SyncSource *source, itemType type);
/**
* 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 */
virtual void addTests();
protected:
/** list with all local test classes for manipulating the sources and their index in the client */
std::vector< std::pair<int, LocalTests *> > sources;
typedef std::vector< std::pair<int, LocalTests *> >::iterator source_it;
/** 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 of first and second client */
virtual void compareDatabases();
/** 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();
/* for more information on the different tests see their implementation */
// do a two-way sync without additional checks
virtual void testTwoWaySync() {
sync(SYNC_TWO_WAY);
}
// do a slow sync without additional checks
virtual void testSlowSync() {
sync(SYNC_SLOW);
}
// do a refresh from server sync without additional checks
virtual void testRefreshFromServerSync() {
sync(SYNC_REFRESH_FROM_SERVER);
}
// do a refresh from client sync without additional checks
virtual void testRefreshFromClientSync() {
sync(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 testRefreshSemantic();
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();
virtual void testItems();
virtual void testAddUpdate();
// test copying with maxMsg and no large object support
void testMaxMsg() {
doVarSizes(true, false, NULL);
}
// test copying with maxMsg and large object support
void testLargeObject() {
doVarSizes(true, true, NULL);
}
// test copying with maxMsg and large object support using explicit "bin" encoding
void testLargeObjectBin() {
doVarSizes(true, true, "bin");
}
// test copying with maxMsg and large object support using B64 encoding
void testLargeObjectEncoded() {
doVarSizes(true, true, "b64");
}
virtual void testManyItems();
/**
* implements testMaxMsg(), testLargeObject(), testLargeObjectEncoded()
* using a sequence of items with varying sizes
*/
virtual void doVarSizes(bool withMaxMsgSize,
bool withLargeObject,
const char *encoding);
/**
* executes a sync with the given options,
* checks the result and (optionally) the sync report
*/
virtual void sync(SyncMode syncMode,
const std::string &logprefix = "",
CheckSyncReport checkReport = CheckSyncReport(),
long maxMsgSize = 0,
long maxObjSize = 0,
bool loSupport = false,
const char *encoding = "");
};
/** assert equality, include string in message if unequal */
#define CLIENT_TEST_EQUAL( _prefix, \
_expected, \
@ -429,5 +727,45 @@ class ClientTest {
_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); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** check _x for true and then the status of the _source pointer */
#define SOURCE_ASSERT(_source, _x) \
{ \
CPPUNIT_ASSERT(_x); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** 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); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/** same as SOURCE_ASSERT() with a specific failure message */
#define SOURCE_ASSERT_MESSAGE(_message, _source, _x) \
{ \
CPPUNIT_ASSERT_MESSAGE((_message), (_x)); \
CPPUNIT_ASSERT((_source) && (!(_source)->getReport() || (_source)->getReport()->getState() != SOURCE_ERROR)); \
}
/**
* 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) \
addTest(new CppUnit::TestCaller<_class>(getName() + "::" #_function, &_class::_function, *this))
#endif // ENABLE_INTEGRATION_TESTS
#endif // INCL_TESTSYNCCLIENT