finished porting all tests from SyncEvolution test suite, improved log file handling

git-svn-id: https://core.forge.funambol.org/svn/core/top-level/trunk/3x/client-api/native@11864 e8e8ed6c-164c-0410-afcf-9e9a7c7d8c10
This commit is contained in:
Patrick Ohly 2006-12-09 17:50:06 +00:00
parent 9432e753c2
commit ccedc74a64
2 changed files with 629 additions and 61 deletions

View File

@ -31,6 +31,7 @@
#include <sstream>
#include <iomanip>
#include <fstream>
#include <iostream>
/** execute _x and then check the status of the _source pointer */
#define SOURCE_ASSERT_NO_FAILURE(_source, _x) \
@ -530,6 +531,7 @@ public:
// delete item again via sync source A
deleteAll(config.createSourceA);
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
@ -717,12 +719,37 @@ public:
ADD_TEST(SyncTests, testRefreshSemantic);
ADD_TEST(SyncTests, testRefreshStatus);
if (accessClientB) {
if (accessClientB &&
config.dump &&
config.compare) {
ADD_TEST(SyncTests, testCopy);
ADD_TEST(SyncTests, testDelete);
ADD_TEST(SyncTests, testAddUpdate);
ADD_TEST(SyncTests, testManyItems);
if (config.updateItem) {
ADD_TEST(SyncTests, testUpdate);
}
if (config.complexUpdateItem) {
ADD_TEST(SyncTests, testComplexUpdate);
}
if (config.mergeItem1 && config.mergeItem2) {
ADD_TEST(SyncTests, testMerge);
}
if (config.import) {
ADD_TEST(SyncTests, testTwinning);
ADD_TEST(SyncTests, testItems);
}
if (config.templateItem) {
ADD_TEST(SyncTests, testMaxMsg);
ADD_TEST(SyncTests, testLargeObject);
ADD_TEST(SyncTests, testLargeObjectBin);
if (client.isB64Enabled()) {
ADD_TEST(SyncTests, testLargeObjectEncoded);
}
ADD_TEST(SyncTests, testOneWayFromServer);
ADD_TEST(SyncTests, testOneWayFromClient);
}
}
}
}
@ -813,6 +840,19 @@ private:
compareDatabases();
}
/**
* 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
*/
void refreshClient() {
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->deleteAll(it->second->config.createSourceA);
}
sync(SYNC_SLOW, ".refresh");
}
// do a two-way sync without additional checks
void testTwoWaySync() {
sync(SYNC_TWO_WAY);
@ -927,6 +967,10 @@ private:
// test that a two-way sync copies updates from database to the other client,
// using simple data commonly supported by servers
void testUpdate() {
CPPUNIT_ASSERT(sources.begin() != sources.end());
CPPUNIT_ASSERT(sources.begin()->second->config.updateItem);
// setup client A, B and server so that they all contain the same item
doCopy();
source_it it;
@ -934,8 +978,8 @@ private:
it->second->update(it->second->config.createSourceA, it->second->config.updateItem);
}
sync(SYNC_TWO_WAY, "update");
accessClientB->sync(SYNC_TWO_WAY, "update");
sync(SYNC_TWO_WAY, ".update");
accessClientB->sync(SYNC_TWO_WAY, ".update");
compareDatabases();
}
@ -943,16 +987,115 @@ 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 testComplexUpdate() {
// setup client A, B and server so that they all contain the same item
doCopy();
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->update(it->second->config.createSourceA, it->second->config.complexUpdateItem);
}
sync(SYNC_TWO_WAY, ".update");
accessClientB->sync(SYNC_TWO_WAY, ".update");
compareDatabases();
}
// test that a two-way sync deletes the copy of an item in the other database
void testDelete();
void testDelete() {
// setup client A, B and server so that they all contain the same item
doCopy();
// delete it on A
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->deleteAll(it->second->config.createSourceA);
}
// transfer change from A to server to B
sync(SYNC_TWO_WAY, ".delete");
accessClientB->sync(SYNC_TWO_WAY, ".delete");
// check client B: shouldn't have any items now
for (it = sources.begin(); it != sources.end(); ++it) {
std::auto_ptr<SyncSource> copy;
SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(it->second->config.createSourceA(client)));
SOURCE_ASSERT_EQUAL(copy.get(), 0, copy->beginSync());
SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
SOURCE_ASSERT_EQUAL(copy.get(), 0, copy->endSync());
CPPUNIT_ASSERT_NO_THROW(copy.reset());
}
}
// test what the server does when it finds that different
// fields of the same item have been modified
void testMerge();
void testMerge() {
// setup client A, B and server so that they all contain the same item
doCopy();
// update in client A
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->update(it->second->config.createSourceA, it->second->config.mergeItem1);
}
// update in client B
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
it->second->update(it->second->config.createSourceA, it->second->config.mergeItem2);
}
// send change to server from client A (no conflict), then from B (conflict)
sync(SYNC_TWO_WAY, ".send");
sync(SYNC_TWO_WAY, ".recv");
// figure out how the conflict during ".recv" was handled
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
std::auto_ptr<SyncSource> copy;
SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(it->second->config.createSourceA(client)));
SOURCE_ASSERT_EQUAL(copy.get(), 0, copy->beginSync());
int numItems;
SOURCE_ASSERT_NO_FAILURE(copy.get(), numItems = countItems(copy.get()));
SOURCE_ASSERT_EQUAL(copy.get(), 0, copy->endSync());
CPPUNIT_ASSERT(numItems >= 1);
CPPUNIT_ASSERT(numItems <= 2);
std::cout << " " << it->second->config.sourceName << ": " << (numItems == 1 ? "conflicting items were merged" : "both of the conflicting items were preserved") << " ";
std::cout.flush();
CPPUNIT_ASSERT_NO_THROW(copy.reset());
}
}
// 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 testTwinning() {
// clean server and client A
deleteAll();
// import test data
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->testImport();
}
// send to server
sync(SYNC_TWO_WAY, ".send");
// ensure that client has the same data, thus ignoring data conversion
// issues (those are covered by testItems())
refreshClient();
// copy to client B to have another copy
accessClientB->refreshClient();
// slow sync should not change anything
sync(SYNC_TWO_WAY, ".twinning");
// check
compareDatabases();
}
// tests one-way sync from server:
// - get both clients and server in sync with no items anywhere
// - add one item on first client, copy to server
@ -962,7 +1105,152 @@ 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 testOneWayFromServer() {
// no items anywhere
deleteAll();
accessClientB->refreshClient();
// check that everything is empty, also resets change tracking
// in second sources of each client
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// add one item on first client, copy to server, and check change tracking via second source
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->insertManyItems(it->second->config.createSourceA, 1, 1);
}
sync(SYNC_TWO_WAY, ".send");
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// add a different item on second client, one-way-from-server
// => one item added locally, none sent to server
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
it->second->insertManyItems(it->second->config.createSourceA, 2, 1);
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
accessClientB->sync(SYNC_ONE_WAY_FROM_SERVER, ".recv");
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 2, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// two-way sync with first client for verification
// => no changes
sync(SYNC_TWO_WAY, ".check");
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// delete items on clientA, sync to server
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->deleteAll(it->second->config.createSourceA);
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
sync(SYNC_TWO_WAY, ".delete");
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// sync the same change to second client
// => one item left (the one inserted locally)
accessClientB->sync(SYNC_ONE_WAY_FROM_SERVER, ".delete");
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
}
// tests one-way sync from client:
// - get both clients and server in sync with no items anywhere
// - add one item on first client, copy to server
@ -972,17 +1260,206 @@ 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 testOneWayFromClient() {
// no items anywhere
deleteAll();
accessClientB->deleteAll();
// check that everything is empty, also resets change tracking
// in second sources of each client
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// add one item on first client, copy to server, and check change tracking via second source
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->insertManyItems(it->second->config.createSourceA, 1, 1);
}
sync(SYNC_TWO_WAY, ".send");
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// add a different item on second client, one-way-from-client
// => no item added locally, one sent to server
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
it->second->insertManyItems(it->second->config.createSourceA, 2, 1);
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
accessClientB->sync(SYNC_ONE_WAY_FROM_CLIENT, ".send");
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// two-way sync with client A for verification
// => receive one item
sync(SYNC_TWO_WAY, ".check");
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 2, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// delete items on client B, sync to server
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
it->second->deleteAll(it->second->config.createSourceA);
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
accessClientB->sync(SYNC_ONE_WAY_FROM_CLIENT, ".delete");
for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(accessClientB->client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
// sync the same change to client A
// => one item left (the one inserted locally)
sync(SYNC_TWO_WAY, ".delete");
for (it = sources.begin(); it != sources.end(); ++it) {
if (it->second->config.createSourceB) {
std::auto_ptr<SyncSource> source;
SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->config.createSourceB(client)));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->beginSync());
SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
SOURCE_ASSERT_EQUAL(source.get(), 0, source->endSync());
CPPUNIT_ASSERT_NO_THROW(source.reset());
}
}
}
// creates several items, transmits them back and forth and
// then compares which of them have been preserved
void testItems();
void testItems() {
// clean server and first test database
deleteAll();
// import data
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->testImport();
}
// transfer from client A to server to client B
sync(SYNC_TWO_WAY, ".send");
accessClientB->refreshClient();
compareDatabases();
}
// tests the following sequence of events:
// - both clients in sync with server
// - client 1 adds item
// - client 1 updates the same item
// - client 2 gets item (depending on server, might be flagged as update)
// See http://forge.objectweb.org/tracker/?func=detail&atid=100096&aid=305018&group_id=96
void testAddUpdate();
// - client 2 gets item: the client should be asked to add the item
//
// However it has been observed that sometimes the item was sent as "update"
// 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() {
// clean server and both test databases
deleteAll();
accessClientB->refreshClient();
// add item
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->insert(it->second->config.createSourceA, it->second->config.insertItem);
}
sync(SYNC_TWO_WAY, ".add");
// update it
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->insert(it->second->config.createSourceB, it->second->config.updateItem);
}
sync(SYNC_TWO_WAY, ".update");
// now download the updated item into the second client
accessClientB->sync(SYNC_TWO_WAY, ".recv");
// compare the two databases
compareDatabases();
}
// test copying with maxMsg and no large object support
void testMaxMsg() {
doVarSizes(true, false, NULL);
@ -1008,7 +1485,32 @@ private:
// two-way sync with clean client/server,
// followed by slow sync and comparison
// via second client
void testManyItems();
void testManyItems() {
// clean server and client A
deleteAll();
// import artificial data
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
it->second->insertManyItems(it->second->config.createSourceA);
}
// send data to server
sync(SYNC_TWO_WAY, ".send");
// ensure that client has the same data, ignoring data conversion
// issues (those are covered by testItems())
refreshClient();
// also copy to second client
accessClientB->refreshClient();
// slow sync now should not change anything
sync(SYNC_SLOW, ".twinning");
// compare
compareDatabases();
}
/**
@ -1017,8 +1519,43 @@ private:
*/
void doVarSizes(bool withMaxMsgSize,
bool withLargeObject,
const char *encoding);
const char *encoding) {
static const int maxMsgSize = 8 * 1024;
// clean server and client A
deleteAll();
// insert items, doubling their size, then restart with small size
source_it it;
for (it = sources.begin(); it != sources.end(); ++it) {
int item = 1;
for (int i = 0; i < 2; i++ ) {
int size = 1;
while (size < 2 * maxMsgSize) {
it->second->insertManyItems(it->second->config.createSourceA, item, 1, strlen(it->second->config.templateItem) + 10 + size);
size *= 2;
item++;
}
}
}
// transfer to server
sync(SYNC_TWO_WAY, ".send",
withMaxMsgSize ? maxMsgSize : 0,
withMaxMsgSize ? maxMsgSize * 100 : 0,
withLargeObject,
encoding);
// copy to second client
accessClientB->sync(SYNC_REFRESH_FROM_SERVER, ".recv",
withLargeObject ? maxMsgSize : withMaxMsgSize ? maxMsgSize * 100 /* large enough so that server can sent the largest item */ : 0,
withMaxMsgSize ? maxMsgSize * 100 : 0,
withLargeObject,
encoding);
// compare
compareDatabases();
}
void sync(SyncMode syncMode,
const std::string &logprefix = "",
@ -1027,9 +1564,15 @@ private:
bool loSupport = false,
const char *encoding = 0) {
int res = 0;
std::string logfile = getCurrentTest() + logprefix + ".client." + (accessClientB ? "a" : "b") + ".log";
simplifyFilename(logfile);
static int syncCounter = 0;
std::stringstream logstream;
logstream << std::setw(4) << std::setfill('0') << syncCounter << "_" << getCurrentTest() << logprefix;
std::string logname = logstream.str();
simplifyFilename(logname);
syncCounter++;
std::string logfile = logname + ".client." + (accessClientB ? "A" : "B") + ".log";
remove(logfile.c_str());
LOG.setLogName(logfile.c_str());
LOG.reset();
@ -1038,43 +1581,18 @@ private:
res = client.sync(sourceArray,
syncMode,
maxMsgSize,
maxObjSize,
loSupport,
encoding);
} catch(...) {
/* TODO: EvolutionSyncSource::handleException(); */
client.postSync(res, logname);
} catch (...) {
res = 1;
client.postSync(res, logname);
// this logs the original exception using CPPUnit mechanisms
CPPUNIT_ASSERT_NO_THROW( throw );
}
sleep(sources.begin()->second->config.serverDelaySeconds);
#if 0
// make a copy of the server's log (if found), then truncate it
if (m_serverLog.size()) {
int fd = open( m_serverLog.c_str(), O_RDWR );
if (fd >= 0) {
// let the server finish
sleep(m_syncDelay);
string serverLog = logfile;
size_t pos = serverLog.find( "client" );
if (pos != serverLog.npos ) {
serverLog.replace( pos, 6, "server" );
} else {
serverLog += ".server.log";
}
string cmd = string("cp ") + m_serverLog + " " + serverLog;
system( cmd.c_str() );
ftruncate( fd, 0 );
} else {
perror( m_serverLog.c_str() );
}
} else {
// let the server finish
sleep(m_syncDelay);
}
#endif
CPPUNIT_ASSERT( !res );
}
};
@ -1140,7 +1658,9 @@ void ClientTest::registerTests()
#endif
}
ClientTest::ClientTest() :
ClientTest::ClientTest(int serverSleepSec, const std::string &serverLog) :
serverSleepSeconds(serverSleepSec),
serverLogFileName(serverLog),
factory(NULL)
{
}
@ -1196,3 +1716,27 @@ int ClientTest::import(ClientTest &client, SyncSource &source, const char *file)
return 0;
}
#ifndef WIN32
#include <fcntl.h>
#endif
void ClientTest::postSync(int res, const std::string &logname)
{
sleep(serverSleepSeconds);
#ifndef WIN32
// make a copy of the server's log (if found), then truncate it
if (serverLogFileName.size()) {
int fd = open(serverLogFileName.c_str(), O_RDWR);
if (fd >= 0) {
std::string cmd = std::string("cp ") + serverLogFileName + " " + logname + ".server.log";
system(cmd.c_str());
ftruncate(fd, 0);
} else {
perror(serverLogFileName.c_str());
}
}
#endif
}

View File

@ -19,6 +19,7 @@
#ifndef INCL_TESTSYNCCLIENT
#define INCL_TESTSYNCCLIENT
#include <string>
#include "spds/SyncSource.h"
/**
@ -64,7 +65,7 @@
*/
class ClientTest {
public:
ClientTest();
ClientTest(int serverSleepSec = 0, const std::string &serverLog= "");
virtual ~ClientTest();
/**
@ -148,14 +149,6 @@ class ClientTest {
*/
createsource_t createSourceB;
/**
* Time the server needs before it correctly handles another
* connection. Some servers use time stamps to track modifications
* and get confused when connections are made to quickly after
* another.
*/
int serverDelaySeconds;
/**
* The framework can generate vCard and vCalendar/iCalendar items
* automatically by copying a template item and modifying certain
@ -283,6 +276,13 @@ class ClientTest {
*/
virtual ClientTest *getClientB() = 0;
/**
* Returning true enables tests which only work if the server is
* a Funambol server which supports the "b64" encoding of items
* on the transport level.
*/
virtual bool isB64Enabled() = 0;
/**
* Execute a synchronization with the selected sync sources
* and the selected synchronization options. The log file
@ -296,7 +296,7 @@ class ClientTest {
* @param encoding if non-NULL, then let client library transform all items
* into this format
*
* @return - 0 on success, an error otherwise
* @return - 0 on success, an error otherwise (may also throw an exception)
*/
virtual int sync(
const int *sources,
@ -306,6 +306,30 @@ class ClientTest {
bool loSupport = false,
const char *encoding = 0) = 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