testing: enhanced DAV source testing + infrastructure

The main goal is to test CalDAV/CardDAV sources as part
of a SyncML client and/or server. A test involving syncevo-http-server
is now named "<client storage><server storage>":
- edsfile = EDS in client, file in server (used to be syncevohttp)
- davfile = CalDAV/CardDAV in client, file in server (new)
- edsdav = EDS in client, CalDAV/CardDAV in server (new)

For this, WebDAVSourceRegister.cpp must be able to create test sources
which match the client 1/2 sync configs. The client "1" or "2" strings
are passed through the abstract ClientTest into the source A/B create
callbacks.  WebDAVSourceRegister.cpp cannot get this directly from
ClientTest because it lives in a plugin which is not necessarily
linked against ClientTest.

A conceptual change is that CLIENT_TEST_EVOLUTION_PREFIX/USER/PASSWORD
no longer override existing config properties. That is necessary
because the shared prefix is too simplistic for WebDAV (needs full URL
in "database"); also helps KDE (needs resource URI). The env variables
and the default "SyncEvolution_Test_" value for the database prefix are
still used if the config does not exist. That is useful to prevent
accidentally running client-test against the default databases.

The nightly setup script might (should!?) be made public to simplify
configuring a server.

Another change is the user-configurable part of client-test now lives
entirely in the _1/_2 client sync configs and contexts. From there the
source properties are copied into the Client::Source context each time
client-test runs.
This commit is contained in:
Patrick Ohly 2012-04-23 11:03:32 +00:00
parent a27f2af580
commit 5eed5ea60b
6 changed files with 219 additions and 52 deletions

View File

@ -171,9 +171,11 @@ static int DumpItems(ClientTest &client, TestingSyncSource &source, const std::s
}
static TestingSyncSource *createEASSource(const ClientTestConfig::createsource_t &create,
ClientTest &client, int source, bool isSourceA)
ClientTest &client,
const std::string &clientID,
int source, bool isSourceA)
{
TestingSyncSource *res = create(client, source, isSourceA);
TestingSyncSource *res = create(client, clientID, source, isSourceA);
// Mangle username: if the base username in the config is account
// "foo", then source B uses "foo_B", because otherwise it'll end
@ -205,9 +207,9 @@ static void updateConfigEAS(const RegisterSyncSourceTest */* me */,
// wrap orginal source creation, set default database for
// database #0 and refuse to return a source for database #1
config.m_createSourceA = boost::bind(createEASSource, config.m_createSourceA,
_1, _2, _3);
_1, _2, _3, _4);
config.m_createSourceB = boost::bind(createEASSource, config.m_createSourceB,
_1, _2, _3);
_1, _2, _3, _4);
config.m_dump = DumpItems;
config.m_sourceLUIDsAreVolatile = true;

View File

@ -233,8 +233,8 @@ public:
if (m_type == "caldav") {
config.m_supportsReccurenceEXDates = true;
}
config.m_createSourceA = boost::bind(&WebDAVTest::createSource, this, _3);
config.m_createSourceB = boost::bind(&WebDAVTest::createSource, this, _3);
config.m_createSourceA = boost::bind(&WebDAVTest::createSource, this, _2, _4);
config.m_createSourceB = boost::bind(&WebDAVTest::createSource, this, _2, _4);
ConfigProps::const_iterator it = m_props.find(m_type + "/testcases");
if (it != m_props.end() ||
(it = m_props.find("testcases")) != m_props.end()) {
@ -242,19 +242,65 @@ public:
}
}
TestingSyncSource *createSource(bool isSourceA) const
// This is very similar to client-test-app.cpp. TODO: refactor?!
TestingSyncSource *createSource(const std::string &clientID, bool isSourceA) const
{
boost::shared_ptr<SyncConfig> context(new SyncConfig(string("target-config@client-test-") + m_server));
SyncSourceNodes nodes = context->getSyncSourceNodes(m_type,
/* string("_") m_clientID + */
string("_") + (isSourceA ? "A" : "B"));
std::string name = m_server + "_" + m_type;
const char *server = getenv("CLIENT_TEST_SERVER");
std::string config = "target-config@client-test";
if (server) {
config += "-";
config += server;
}
std::string tracking =
string("_") + clientID +
string("_") + (isSourceA ? "A" : "B");
// always set properties taken from the environment;
// TODO: "database" property (currently always uses the default)
SE_LOG_DEBUG(NULL, NULL, "instantiating testing source %s in config %s, with tracking name %s",
name.c_str(),
config.c_str(),
tracking.c_str());
boost::shared_ptr<SyncConfig> context(new SyncConfig(config));
SyncSourceNodes nodes = context->getSyncSourceNodes(name, tracking);
// Copy properties from the Client::Sync
// @<CLIENT_TEST_SERVER>_<clientID>/<name> config, to ensure
// that a testing source used as part of Client::Sync uses the
// same settings.
std::string peerName = std::string(server ? server : "no-such-server") + "_" + clientID;
boost::shared_ptr<SyncConfig> peer(new SyncConfig(peerName));
SyncSourceNodes peerNodes = peer->getSyncSourceNodes(name);
SE_LOG_DEBUG(NULL, NULL, "overriding testing source %s properties with the ones from config %s = %s",
name.c_str(),
peerName.c_str(),
peer->getRootPath().c_str());
BOOST_FOREACH(const ConfigProperty *prop, SyncSourceConfig::getRegistry()) {
if (prop->isHidden()) {
continue;
}
boost::shared_ptr<FilterConfigNode> node = peerNodes.getNode(*prop);
InitStateString value = prop->getProperty(*node);
SE_LOG_DEBUG(NULL, NULL, " %s = %s (%s)",
prop->getMainName().c_str(),
value.c_str(),
value.wasSet() ? "set" : "default");
node = nodes.getNode(*prop);
node->setProperty(prop->getMainName(), value);
}
// Also copy loglevel.
context->setLogLevel(peer->getLogLevel());
context->flush();
// Always set properties taken from the environment.
nodes.getProperties()->setProperty("backend", m_type);
SE_LOG_DEBUG(NULL, NULL, " additional property backend = %s (from CLIENT_TEST_WEBDAV)",
m_type.c_str());
BOOST_FOREACH(const StringPair &propval, m_props) {
boost::shared_ptr<FilterConfigNode> node = context->getNode(propval.first);
if (node) {
SE_LOG_DEBUG(NULL, NULL, " additional property %s = %s (from CLIENT_TEST_WEBDAV)",
propval.first.c_str(), propval.second.c_str());
node->setProperty(propval.first, propval.second);
} else if (!boost::ends_with(propval.first, "testconfig") &&
!boost::ends_with(propval.first, "testcases")) {

View File

@ -135,6 +135,8 @@ public:
{
}
virtual std::string getClientID() const { return m_clientID; }
/**
* code depends on other global constructors to run first, execute it after constructor but before
* any other methods
@ -239,11 +241,22 @@ public:
}
}
// always set these properties: they might have changed since the last run
string database = getDatabaseName(test->m_configName);
sc->setDatabaseID(database);
sc->setUser(m_evoUser);
sc->setPassword(m_evoPassword);
// Set these properties if not set yet: that means the env
// variables are used when creating the config initially,
// but then no longer can be used to change the config.
// This prevents accidentally running a test with default
// values, for example for the database.
if (sc->getDatabaseID().empty()) {
string database = getDatabaseName(test->m_configName);
sc->setDatabaseID(database);
}
if (sc->getUser().empty() && !m_evoUser.empty()) {
sc->setUser(m_evoUser);
}
if (sc->getPassword().empty() && !m_evoPassword.empty()) {
sc->setPassword(m_evoPassword);
}
// Always set this one, to ensure the config matches the test.
sc->setBackend(SourceType(testconfig.m_type).m_backend);
}
config->flush();
@ -461,7 +474,7 @@ private:
}
/** called by test frame work */
static TestingSyncSource *createSource(ClientTest &client, int source, bool isSourceA) {
static TestingSyncSource *createSource(ClientTest &client, const std::string &clientID, int source, bool isSourceA) {
TestEvolution &evClient((TestEvolution &)client);
string name = evClient.m_localSource2Config[source];
@ -480,15 +493,57 @@ private:
config += "-";
config += server;
}
std::string tracking =
string("_") + m_clientID +
"_" + (isSourceA ? "A" : "B");
SE_LOG_DEBUG(NULL, NULL, "instantiating testing source %s in config %s, with tracking name %s",
name.c_str(),
config.c_str(),
tracking.c_str());
boost::shared_ptr<SyncConfig> context(new SyncConfig(config));
SyncSourceNodes nodes = context->getSyncSourceNodes(name,
string("_") + m_clientID +
"_" + (isSourceA ? "A" : "B"));
SyncSourceNodes nodes = context->getSyncSourceNodes(name, tracking);
// always set this property: the name might have changes since last test run
nodes.getProperties()->setProperty("evolutionsource", database.c_str());
nodes.getProperties()->setProperty("evolutionuser", m_evoUser.c_str());
nodes.getProperties()->setProperty("evolutionpassword", m_evoPassword.c_str());
// The user of client-test must have configured the source
// @<CLIENT_TEST_SERVER>_<m_clientID>/<name> when doing
// Client::Sync testing. Our testing source must use the same
// properties, but different change tracking.
std::string peerName = server ? (std::string(server) + "_" + m_clientID) : "@default";
boost::shared_ptr<SyncConfig> peer(new SyncConfig(peerName));
SyncSourceNodes peerNodes = peer->getSyncSourceNodes(name);
SE_LOG_DEBUG(NULL, NULL, "overriding testing source %s properties with the ones from config %s = %s",
name.c_str(),
peerName.c_str(),
peer->getRootPath().c_str());
BOOST_FOREACH(const ConfigProperty *prop, SyncSourceConfig::getRegistry()) {
if (prop->isHidden()) {
continue;
}
boost::shared_ptr<FilterConfigNode> node = peerNodes.getNode(*prop);
InitStateString value = prop->getProperty(*node);
SE_LOG_DEBUG(NULL, NULL, " %s = %s (%s)",
prop->getMainName().c_str(),
value.c_str(),
value.wasSet() ? "set" : "default");
node = nodes.getNode(*prop);
node->setProperty(prop->getMainName(), value);
}
context->flush();
// Same as in init() above: set values if still empty, but don't
// overwrite anything.
boost::shared_ptr<FilterConfigNode> props = nodes.getProperties();
std::string value;
if (!props->getProperty("database", value)) {
props->setProperty("database", database);
}
if (!props->getProperty("databaseUser", value) &&
!m_evoUser.empty()) {
props->setProperty("databaseUser", m_evoUser);
}
if (!props->getProperty("databasePassword", value) &&
!m_evoPassword.empty()) {
props->setProperty("databasePassword", m_evoPassword);
}
SyncSourceParams params(name,
nodes,

View File

@ -201,11 +201,14 @@ struct ClientTestConfig {
* the sync source's desctructor should not thow exceptions.
*
* @param client the same instance to which this config belongs
* @param clientID the unique ID of the client, "1" resp. "2" in practice (can also be obtained as
* client->getClientID(), but not all implementers have (or want) access to the
* class definition)
* @param source index of the data source (from 0 to ClientTest::getNumSources() - 1)
* @param isSourceA true if the requested SyncSource is the first one accessing that
* data, otherwise the second
*/
typedef boost::function<TestingSyncSource *(ClientTest &, int, bool)> createsource_t;
typedef boost::function<TestingSyncSource *(ClientTest &, const std::string &, int, bool)> createsource_t;
/**
* Creates a sync source which references the primary database;

View File

@ -241,6 +241,9 @@ class ClientTest {
ClientTest(int serverSleepSec = 0, const std::string &serverLog= "");
virtual ~ClientTest();
/** a unique string - "1" or "2" in practice */
virtual std::string getClientID() const = 0;
/** set up before running a test */
virtual void setup() { }
@ -448,7 +451,7 @@ public:
TestingSyncSource *operator() () {
CPPUNIT_ASSERT(createSource);
return createSource(client, source, isSourceA);
return createSource(client, client.getClientID(), source, isSourceA);
}
const ClientTest::Config::createsource_t createSource;

View File

@ -1164,7 +1164,29 @@ class ActiveSyncTest(SyncEvolutionTest):
test = ActiveSyncTest("exchange")
context.add(test)
test = SyncEvolutionTest("syncevohttp",
syncevoPrefix=" ".join([os.path.join(sync.basedir, "test", "wrappercheck.sh")] +
# redirect output of command run under valgrind (when
# using valgrind) or of the whole command (otherwise)
# to syncevohttp.log
( 'valgrindcheck' in options.testprefix and \
[ "VALGRIND_CMD_LOG=syncevohttp.log" ] or \
[ "--daemon-log", "syncevohttp.log" ] ) +
[ options.testprefix,
os.path.join(compile.installdir, "usr", "libexec", "syncevo-dbus-server"),
"--",
os.path.join(sync.basedir, "test", "wrappercheck.sh"),
# also redirect additional syncevo-http-server
# output into the same file
"--daemon-log", "syncevohttp.log",
os.path.join(compile.installdir, "usr", "bin", "syncevo-http-server"),
"--quiet",
"http://127.0.0.1:9999/syncevolution",
"--",
options.testprefix])
# The test uses EDS on the clients and a server config with file
# backends.
test = SyncEvolutionTest("edsfile",
compile,
"", options.shell,
"Client::Sync::eds_event Client::Sync::eds_contact Client::Sync::eds_event_eds_contact",
@ -1172,9 +1194,9 @@ test = SyncEvolutionTest("syncevohttp",
"CLIENT_TEST_NUM_ITEMS=10 "
"CLIENT_TEST_LOG=syncevohttp.log "
# could be enabled, but reporting result is currently missing (BMC #1009)
#"CLIENT_TEST_RETRY=t "
#"CLIENT_TEST_RESEND=t "
#"CLIENT_TEST_SUSPEND=t "
"CLIENT_TEST_RETRY=t "
"CLIENT_TEST_RESEND=t "
"CLIENT_TEST_SUSPEND=t "
# server supports refresh-from-client, use it for
# more efficient test setup
"CLIENT_TEST_DELETE_REFRESH=1 "
@ -1183,27 +1205,63 @@ test = SyncEvolutionTest("syncevohttp",
"CLIENT_TEST_SKIP="
# server does not detect duplicates (uses file backend), detecting on the
# client breaks syncing (see '[SyncEvolution] 409 "item merged" in client')
"Client::Sync::.*::testAddBothSides.*"
# "Client::Sync::.*::testAddBothSides.*"
,
testPrefix=" ".join([os.path.join(sync.basedir, "test", "wrappercheck.sh")] +
# redirect output of command run under valgrind (when
# using valgrind) or of the whole command (otherwise)
# to syncevohttp.log
( 'valgrindcheck' in options.testprefix and \
[ "VALGRIND_CMD_LOG=syncevohttp.log" ] or \
[ "--daemon-log", "syncevohttp.log" ] ) +
[ options.testprefix,
os.path.join(compile.installdir, "usr", "libexec", "syncevo-dbus-server"),
"--",
os.path.join(sync.basedir, "test", "wrappercheck.sh"),
# also redirect additional syncevo-http-server
# output into the same file
"--daemon-log", "syncevohttp.log",
os.path.join(compile.installdir, "usr", "bin", "syncevo-http-server"),
"--quiet",
"http://127.0.0.1:9999/syncevolution",
"--",
options.testprefix]))
testPrefix=syncevoPrefix)
context.add(test)
# This one uses CalDAV/CardDAV in DAViCal and the same server config
# with file backends as edsfile.
test = SyncEvolutionTest("davfile",
compile,
"", options.shell,
"Client::Sync::davical_caldav Client::Sync::davical_carddav Client::Sync::davical_caldav_davical_carddav",
[ "davical_caldav", "davical_carddav" ],
"CLIENT_TEST_SIMPLE_UID=1 " # DAViCal server gets confused by UID with special characters
"CLIENT_TEST_WEBDAV='davical caldav carddav' "
"CLIENT_TEST_NUM_ITEMS=10 "
"CLIENT_TEST_LOG=syncevohttp.log "
# could be enabled, but reporting result is currently missing (BMC #1009)
# "CLIENT_TEST_RETRY=t "
# "CLIENT_TEST_RESEND=t "
# "CLIENT_TEST_SUSPEND=t "
# server supports refresh-from-client, use it for
# more efficient test setup
"CLIENT_TEST_DELETE_REFRESH=1 "
# server supports multiple cycles inside the same session
"CLIENT_TEST_PEER_CAN_RESTART=1 "
"CLIENT_TEST_SKIP="
# server does not detect duplicates (uses file backend), detecting on the
# client breaks syncing (see '[SyncEvolution] 409 "item merged" in client')
# "Client::Sync::.*::testAddBothSides.*"
,
testPrefix=syncevoPrefix)
context.add(test)
# EDS on client side, DAV on server.
test = SyncEvolutionTest("edsdav",
compile,
"", options.shell,
"Client::Sync::eds_event Client::Sync::eds_contact Client::Sync::eds_event_eds_contact",
[ "eds_event", "eds_contact" ],
"CLIENT_TEST_SIMPLE_UID=1 " # DAViCal server gets confused by UID with special characters
"CLIENT_TEST_NUM_ITEMS=10 "
"CLIENT_TEST_LOG=syncevohttp.log "
# could be enabled, but reporting result is currently missing (BMC #1009)
# "CLIENT_TEST_RETRY=t "
# "CLIENT_TEST_RESEND=t "
# "CLIENT_TEST_SUSPEND=t "
# server supports refresh-from-client, use it for
# more efficient test setup
"CLIENT_TEST_DELETE_REFRESH=1 "
# server supports multiple cycles inside the same session
"CLIENT_TEST_PEER_CAN_RESTART=1 "
"CLIENT_TEST_SKIP="
# server does not detect duplicates (uses file backend), detecting on the
# client breaks syncing (see '[SyncEvolution] 409 "item merged" in client')
# "Client::Sync::.*::testAddBothSides.*"
,
testPrefix=syncevoPrefix)
context.add(test)
scheduleworldtest = SyncEvolutionTest("scheduleworld", compile,