testing: added Client::Sync::*::testConversion

The conversion test checks whether information is lost and/or modified
when converting the test items into the field list defined in the Synthesis
XML config. For that it converts into the field list and back, then compares
against the original test item file with synccompare.

Because a valid Synthesis session is necessary, this test hooks into
running a sync session via a callback that is triggered by the first
Synthesis progress event. It then aborts the session without contacting
the server.

boost::bind() with a reference should have worked (?), but didn't:
the bound function did not update the variable it was bound to.
Using a pointer worked.
This commit is contained in:
Patrick Ohly 2009-03-11 15:01:07 +01:00 committed by Patrick Ohly
parent 0464497692
commit 11e10f2556
7 changed files with 188 additions and 15 deletions

View File

@ -322,7 +322,9 @@ public:
const SyncOptions &options) :
EvolutionSyncClient(server, false, activeSources),
m_logbase(logbase),
m_options(options)
m_options(options),
m_started(false),
m_aborted(false)
{}
protected:
@ -342,9 +344,24 @@ public:
EvolutionSyncClient::prepare(sources);
}
virtual void displaySyncProgress(sysync::TProgressEventEnum type,
int32_t extra1, int32_t extra2, int32_t extra3)
{
if (!m_started) {
m_started = true;
if (m_options.m_startCallback(*this, m_options)) {
m_aborted = true;
}
}
}
virtual bool checkForAbort() { return m_aborted; }
private:
const string m_logbase;
SyncOptions m_options;
bool m_started;
bool m_aborted;
} client(server, activeSources, logbase, options);
SyncReport report;

View File

@ -1348,6 +1348,7 @@ SyncMLStatus EvolutionSyncClient::doSync()
sysync::uInt16 stepCmd = sysync::STEPCMD_CLIENTSTART; // first step
SharedSession session = m_engine.OpenSession();
SharedBuffer sendBuffer;
SessionSentinel sessionSentinel(*this, session);
// Sync main loop: runs until SessionStep() signals end or error.
// Exceptions are caught and lead to a call of SessionStep() with

View File

@ -55,6 +55,27 @@ class EvolutionSyncClient : public EvolutionSyncConfig, public ConfigUserInterfa
*/
SharedEngine m_engine;
/**
* Synthesis session handle. Only valid while sync is running.
*/
SharedSession m_session;
/**
* installs session in EvolutionSyncClient and removes it again
* when going out of scope
*/
class SessionSentinel {
EvolutionSyncClient &m_client;
public:
SessionSentinel(EvolutionSyncClient &client, SharedSession &session) :
m_client(client) {
m_client.m_session = session;
}
~SessionSentinel() {
m_client.m_session.reset();
}
};
public:
/**
* @param server identifies the server config to be used
@ -150,9 +171,15 @@ class EvolutionSyncClient : public EvolutionSyncConfig, public ConfigUserInterfa
*/
virtual void setConfigFilter(bool sync, const FilterConfigNode::ConfigFilter &filter);
protected:
SharedEngine getEngine() { return m_engine; }
const SharedEngine getEngine() const { return m_engine; }
/**
* Handle for active session, may be NULL.
*/
SharedSession getSession() { return m_session; }
protected:
SharedEngine swapEngine(SharedEngine newengine) {
SharedEngine oldengine = m_engine;
m_engine = newengine;

View File

@ -213,29 +213,32 @@ EvolutionSyncSource *EvolutionSyncSource::createTestingSource(const string &name
return createSource(params, error);
}
void EvolutionSyncSource::getDatastoreXML(string &xml)
void EvolutionSyncSource::getSynthesisInfo(string &profile,
string &datatypes,
string &native)
{
stringstream xmlstream;
string type = getMimeType();
string profile;
string datatypes;
if (type == "text/x-vcard") {
native = "vCard21";
profile = "\"vCard\", 1";
datatypes =
" <use datatype='vCard21' mode='rw' preferred='yes'/>\n"
" <use datatype='vCard30' mode='rw'/>\n";
} else if (type == "text/vcard") {
native = "vCard30";
profile = "\"vCard\", 2";
datatypes =
" <use datatype='vCard21' mode='rw'/>\n"
" <use datatype='vCard30' mode='rw' preferred='yes'/>\n";
} else if (type == "text/x-calendar") {
native = "vCalendar10";
profile = "\"vCalendar\", 1";
datatypes =
" <use datatype='vCalendar10' mode='rw' preferred='yes'/>\n"
" <use datatype='iCalendar20' mode='rw'/>\n";
} else if (type == "text/calendar") {
native = "iCalendar20";
profile = "\"vCalendar\", 2";
datatypes =
" <use datatype='vCalendar10' mode='rw'/>\n"
@ -274,6 +277,16 @@ void EvolutionSyncSource::getDatastoreXML(string &xml)
} else {
throwError(string("configured MIME type not supported: ") + type);
}
}
void EvolutionSyncSource::getDatastoreXML(string &xml)
{
stringstream xmlstream;
string profile;
string datatypes;
string native;
getSynthesisInfo(profile, datatypes, native);
xmlstream <<
" <plugin_module>SyncEvolution</plugin_module>\n"

View File

@ -508,6 +508,20 @@ class EvolutionSyncSource : public EvolutionSyncSourceConfig, public LoggerBase,
*/
virtual void getDatastoreXML(string &xml);
/**
* Synthesis <datatype> name which matches the format used
* for importing and exporting items (exportData()).
* This is not necessarily the same format that is given
* to the Synthesis engine. If this internal format doesn't
* have a <datatype> in the engine, then an empty string is
* returned.
*/
virtual string getNativeDatatypeName() {
string profile, datatypes, native;
getSynthesisInfo(profile, datatypes, native);
return native;
}
/**
* @name default implementation of SyncSource iterators
*
@ -642,6 +656,17 @@ class EvolutionSyncSource : public EvolutionSyncSourceConfig, public LoggerBase,
ESource *findSource( ESourceList *list, const string &id );
#endif
/**
* helper function: checks getMimeType() and getSourceType() to determine XML config elements
*
* @retval profile profile name to use for MAKE/PARSETEXTWITHPROFILE
* @retval datatypes list of supported datatypes in "<use .../>" format
* @retval native native datatype (see getNativeDatatypeName())
*/
virtual void getSynthesisInfo(string &profile,
string &datatypes,
string &native);
public:
#ifdef HAVE_EDS
/**

View File

@ -49,6 +49,9 @@
#include <TransportAgent.h>
#include <Logging.h>
#include <SyncEvolutionUtil.h>
#include <EvolutionSyncClient.h>
#include <synthesis/dataconversion.h>
#include <memory>
#include <vector>
@ -59,6 +62,8 @@
#include <iostream>
#include <algorithm>
#include <boost/bind.hpp>
/** utility function to iterate over different kinds of items in a sync source */
static std::list<std::string> listAnyItems(
SyncSource *source,
@ -1629,6 +1634,11 @@ void SyncTests::addTests() {
ADD_TEST(SyncTests, testRefreshFromServerSync);
ADD_TEST(SyncTests, testRefreshFromClientSync);
if (config.compare &&
config.testcases) {
ADD_TEST(SyncTests, testConversion);
}
if (config.createSourceA) {
if (config.insertItem) {
ADD_TEST(SyncTests, testRefreshSemantic);
@ -2375,6 +2385,62 @@ void SyncTests::testOneWayFromClient() {
}
}
// get engine ready, then use it to convert our test items
// to and from the internal field list
void SyncTests::testConversion() {
bool success = false;
SyncOptions::Callback_t callback = boost::bind(&SyncTests::doConversionCallback, this, &success, _1, _2);
doSync(SyncOptions(SYNC_TWO_WAY, CheckSyncReport(-1,-1,-1, -1,-1,-1, false))
.setStartCallback(callback));
CPPUNIT_ASSERT(success);
}
bool SyncTests::doConversionCallback(bool *success,
EvolutionSyncClient &syncClient,
SyncOptions &options) {
*success = false;
for (source_it it = sources.begin(); it != sources.end(); ++it) {
const ClientTest::Config *config = &it->second->config;
EvolutionSyncSource *source = syncClient.findSource(config->sourceName);
CPPUNIT_ASSERT(source);
std::string type = source->getNativeDatatypeName();
if (type.empty()) {
continue;
}
std::list<std::string> items;
ClientTest::getItems(config->testcases, items);
std::string converted = getCurrentTest();
converted += ".converted";
converted += config->sourceName;
converted += ".dat";
simplifyFilename(converted);
std::ofstream out(converted.c_str());
BOOST_FOREACH(const string &item, items) {
string convertedItem = item;
if(!sysync::DataConversion(syncClient.getSession().get(),
type.c_str(),
type.c_str(),
convertedItem)) {
SE_LOG_ERROR(NULL, NULL, "failed parsing as %s:\n%s",
type.c_str(),
item.c_str());
} else {
out << convertedItem << "\n";
}
}
out.close();
CPPUNIT_ASSERT(config->compare(client, config->testcases, converted.c_str()));
}
// abort sync after completing the test successfully (no exception so far!)
*success = true;
return true;
}
// creates several items, transmits them back and forth and
// then compares which of them have been preserved
void SyncTests::testItems() {
@ -3036,13 +3102,17 @@ void ClientTest::getItems(const char *file, list<string> &items)
data += line;
data += "\n";
} else {
items.push_back(data);
if (!data.empty()) {
items.push_back(data);
}
data = "";
}
wasend = !line.compare(0, 4, "END:");
} while(!input.eof());
}
items.push_back(data);
if (!data.empty()) {
items.push_back(data);
}
}
int ClientTest::import(ClientTest &client, SyncSource &source, const char *file)
@ -3522,7 +3592,9 @@ void CheckSyncReport::check(SyncMLStatus status, SyncReport &report) const
str << "Expected sync mode: " << PrettyPrintSyncMode(syncMode) << "\n";
SE_LOG_INFO(NULL, NULL, "sync report:\n%s\n", str.str().c_str());
CPPUNIT_ASSERT_EQUAL(STATUS_OK, status);
if (mustSucceed) {
CPPUNIT_ASSERT_EQUAL(STATUS_OK, status);
}
// this code is intentionally duplicated to produce nicer CPPUNIT asserts
BOOST_FOREACH(SyncReport::value_type &entry, report) {
@ -3530,7 +3602,9 @@ void CheckSyncReport::check(SyncMLStatus status, SyncReport &report) const
const SyncSourceReport &source = entry.second;
SE_LOG_DEBUG(NULL, NULL, "Checking sync source %s...", name.c_str());
CLIENT_TEST_EQUAL(name, STATUS_OK, source.getStatus());
if (mustSucceed) {
CLIENT_TEST_EQUAL(name, STATUS_OK, source.getStatus());
}
CLIENT_TEST_EQUAL(name, 0, source.getItemStat(SyncSourceReport::ITEM_LOCAL,
SyncSourceReport::ITEM_ANY,
SyncSourceReport::ITEM_REJECT));

View File

@ -42,9 +42,6 @@
#include <boost/function.hpp>
namespace sysync {
class TEngineModuleBase;
}
class EvolutionSyncClient;
class EvolutionSyncSource;
typedef EvolutionSyncSource SyncSource;
@ -118,19 +115,30 @@ struct SyncOptions {
bool m_loSupport;
/** preferred item encoding */
std::string m_encoding;
typedef boost::function<bool (EvolutionSyncClient &,
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;
SyncOptions(SyncMode syncMode = SYNC_NONE,
const CheckSyncReport &checkReport = CheckSyncReport(),
long maxMsgSize = 0,
long maxObjSize = 0,
bool loSupport = false,
const std::string &encoding = std::string()) :
const std::string &encoding = std::string(),
Callback_t startCallback = EmptyCallback) :
m_syncMode(syncMode),
m_checkReport(checkReport),
m_maxMsgSize(maxMsgSize),
m_maxObjSize(maxObjSize),
m_loSupport(loSupport),
m_encoding(encoding)
m_encoding(encoding),
m_startCallback(startCallback)
{}
SyncOptions &setSyncMode(SyncMode syncMode) { m_syncMode = syncMode; return *this; }
@ -139,6 +147,10 @@ struct SyncOptions {
SyncOptions &setMaxObjSize(long maxObjSize) { m_maxObjSize = maxObjSize; return *this; }
SyncOptions &setLOSupport(bool loSupport) { m_loSupport = loSupport; return *this; }
SyncOptions &setEncoding(const std::string &encoding) { m_encoding = encoding; return *this; }
SyncOptions &setStartCallback(const Callback_t &callback) { m_startCallback = callback; return *this; }
static bool EmptyCallback(EvolutionSyncClient &,
SyncOptions &) { return false; }
};
class LocalTests;
@ -866,6 +878,10 @@ protected:
virtual void testTwinning();
virtual void testOneWayFromServer();
virtual void testOneWayFromClient();
bool doConversionCallback(bool *success,
EvolutionSyncClient &client,
SyncOptions &options);
virtual void testConversion();
virtual void testItems();
virtual void testAddUpdate();