2 * Copyright (C) 2005-2009 Patrick Ohly <patrick.ohly@gmx.de>
3 * Copyright (C) 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) version 3.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 #include <ClientTest.h>
31 #ifdef HAVE_VALGRIND_VALGRIND_H
32 # include <valgrind/valgrind.h>
34 #ifdef HAVE_EXECINFO_H
35 # include <execinfo.h>
38 #include <syncevo/SyncContext.h>
39 #include "EvolutionSyncSource.h"
40 #include <syncevo/util.h>
41 #include <syncevo/VolatileConfigNode.h>
43 #include <syncevo/declarations.h>
47 * always provide this test class, even if not used:
48 * that way the test scripts can unconditionally
49 * invoke "client-test SyncEvolution"
51 CPPUNIT_REGISTRY_ADD_TO_DEFAULT("SyncEvolution");
53 class EvolutionLocalTests : public LocalTests {
55 EvolutionLocalTests(const std::string &name, ClientTest &cl, int sourceParam, ClientTest::Config &co) :
56 LocalTests(name, cl, sourceParam, co)
59 virtual void addTests() {
60 LocalTests::addTests();
63 if (config.createSourceA &&
64 config.createSourceB &&
65 config.templateItem &&
66 strstr(config.templateItem, "BEGIN:VCARD") &&
67 config.uniqueProperties) {
68 ADD_TEST(EvolutionLocalTests, testOssoDelete);
76 // overwrite it with an additional X-OSSO-CONTACT-STATE:DELETED as Maemoe address book does,
77 // iterate again and check that our own code deleted the item
78 void testOssoDelete() {
79 // get into clean state with one template item added
80 deleteAll(createSourceA);
81 insert(createSourceA, config.templateItem, config.itemType);
83 // add X-OSSO-CONTACT-STATE:DELETED
84 string item = config.templateItem;
85 const char *comma = strchr(config.uniqueProperties, ':');
86 size_t offset = item.find(config.uniqueProperties, 0,
87 comma ? comma - config.uniqueProperties : strlen(config.uniqueProperties));
88 CPPUNIT_ASSERT(offset != item.npos);
89 item.insert(offset, "X-OSSO-CONTACT-STATE:DELETED\n");
90 update(createSourceA, item.c_str(), false);
92 // opening and preparing the source should delete the item
93 std::auto_ptr<TestingSyncSource> source;
94 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceA()));
95 SOURCE_ASSERT_NO_FAILURE(source.get(), source->open());
96 SOURCE_ASSERT_NO_FAILURE(source.get(), source->beginSync("", "") );
97 CPPUNIT_ASSERT_EQUAL(0, countItemsOfType(source.get(), SyncSourceChanges::ANY));
98 CPPUNIT_ASSERT_EQUAL(0, countItemsOfType(source.get(), SyncSourceChanges::NEW));
99 CPPUNIT_ASSERT_EQUAL(0, countItemsOfType(source.get(), SyncSourceChanges::UPDATED));
100 CPPUNIT_ASSERT_EQUAL(1, countItemsOfType(source.get(), SyncSourceChanges::DELETED));
105 * This code uses the ClientTest and and information provided by
106 * the backends in their RegisterSyncSourceTest instances to test
107 * real synchronization with a server.
109 * Configuration is done by environment variables which indicate which
110 * part below the root node "client-test" of the the configuration tree to use;
111 * beyond that everything needed for synchronization is read from the
112 * configuration tree.
114 * - CLIENT_TEST_SERVER = maps to name of root node in configuration tree
115 * - CLIENT_TEST_EVOLUTION_PREFIX = a common "evolutionsource" prefix for *all*
116 * sources; the source name followed by "_[12]"
117 * is appended to get unique names
118 * - CLIENT_TEST_EVOLUTION_USER = sets the "evolutionuser" property of all sources
119 * - CLIENT_TEST_EVOLUTION_PASSWORD = sets the "evolutionpassword" property of all sources
120 * - CLIENT_TEST_SOURCES = comma separated list of active sources,
121 * names as selected in their RegisterSyncSourceTest
123 * - CLIENT_TEST_DELAY = number of seconds to sleep between syncs, required
125 * - CLIENT_TEST_LOG = logfile name of a server, can be empty:
126 * if given, then the content of that file will be
127 * copied and stored together with the client log
128 * (only works on Unix)
129 * - CLIENT_TEST_NUM_ITEMS = numbers of contacts/events/... to use during
130 * local and sync tests which create artificial
133 * The CLIENT_TEST_SERVER also has another meaning: it is used as hint
134 * by the synccompare.pl script and causes it to automatically ignore
135 * known, acceptable data modifications caused by sending an item to
136 * a server and back again. Currently the script recognizes "funambol",
137 * "scheduleworld", "synthesis" and "egroupware" as special server
140 class TestEvolution : public ClientTest {
143 * can be instantiated as client A with id == "1" and client B with id == "2"
145 TestEvolution(const string &id) :
146 ClientTest(getenv("CLIENT_TEST_DELAY") ? atoi(getenv("CLIENT_TEST_DELAY")) : 0,
147 getenv("CLIENT_TEST_LOG") ? getenv("CLIENT_TEST_LOG") : ""),
149 m_configs(EvolutionSyncSource::getTestRegistry())
151 const char *server = getenv("CLIENT_TEST_SERVER");
154 m_clientB.reset(new TestEvolution("2"));
160 setenv("CLIENT_TEST_SERVER", "funambol", 1);
163 /* override Evolution database names? */
164 const char *evoprefix = getenv("CLIENT_TEST_EVOLUTION_PREFIX");
165 m_evoPrefix = evoprefix ? evoprefix : "SyncEvolution_Test_";
166 const char *evouser = getenv("CLIENT_TEST_EVOLUTION_USER");
170 const char *evopasswd = getenv("CLIENT_TEST_EVOLUTION_PASSWORD");
172 m_evoPassword = evopasswd;
176 const char *sourcelist = getenv("CLIENT_TEST_SOURCES");
179 boost::split(sources, sourcelist, boost::is_any_of(","));
181 BOOST_FOREACH(const RegisterSyncSourceTest *test, m_configs) {
182 sources.insert(test->m_configName);
186 BOOST_FOREACH(const RegisterSyncSourceTest *test, m_configs) {
187 if (sources.find(test->m_configName) != sources.end()) {
188 m_source2Config.push_back(test->m_configName);
192 // get configuration and set obligatory fields
193 LoggerBase::instance().setLevel(Logger::DEBUG);
194 std::string root = std::string("evolution/") + server + "_" + id;
195 boost::shared_ptr<SyncConfig> config(new SyncConfig(string(server) + "_" + id));
196 boost::shared_ptr<SyncConfig> from = boost::shared_ptr<SyncConfig> ();
198 if (!config->exists()) {
199 // no configuration yet, create in different contexts because
200 // device ID is different
201 config.reset(new SyncConfig(string(server) + "_" + id + "@client-test-" + id));
202 config->setDefaults();
203 from = SyncConfig::createServerTemplate(server);
206 config->copy(*from, &filter);
208 config->setDevID(id == "1" ? "sc-api-nat" : "sc-pim-ppc");
210 BOOST_FOREACH(const RegisterSyncSourceTest *test, m_configs) {
211 ClientTest::Config testconfig;
212 getSourceConfig(test, testconfig);
213 CPPUNIT_ASSERT(testconfig.type);
215 boost::shared_ptr<SyncSourceConfig> sc = config->getSyncSourceConfig(testconfig.sourceName);
216 if (!sc || !sc->exists()) {
217 // no configuration yet
218 config->setSourceDefaults(testconfig.sourceName);
219 sc = config->getSyncSourceConfig(testconfig.sourceName);
221 sc->setURI(testconfig.uri);
222 if(from && testconfig.sourceNameServerTemplate){
223 boost::shared_ptr<SyncSourceConfig> scServerTemplate = from->getSyncSourceConfig(testconfig.sourceNameServerTemplate);
224 sc->setURI(scServerTemplate->getURI());
226 sc->setSourceType(testconfig.type);
229 // always set these properties: they might have changed since the last run
230 string database = getDatabaseName(test->m_configName);
231 sc->setDatabaseID(database);
232 sc->setUser(m_evoUser);
233 sc->setPassword(m_evoPassword);
238 virtual LocalTests *createLocalTests(const std::string &name, int sourceParam, ClientTest::Config &co) {
239 return new EvolutionLocalTests(name, *this, sourceParam, co);
242 virtual int getNumSources() {
243 return m_source2Config.size();
246 virtual void getSourceConfig(int source, Config &config) {
247 getSourceConfig(m_configs[m_source2Config[source]], config);
250 static void getSourceConfig(const RegisterSyncSourceTest *test, Config &config) {
251 memset(&config, 0, sizeof(config));
252 ClientTest::getTestData(test->m_testCaseName.c_str(), config);
253 config.createSourceA = createSource;
254 config.createSourceB = createSource;
255 config.sourceName = test->m_configName.c_str();
257 test->updateConfig(config);
260 virtual ClientTest *getClientB() {
261 return m_clientB.get();
264 virtual bool isB64Enabled() {
268 virtual SyncMLStatus doSync(const int *sources,
269 const std::string &logbase,
270 const SyncOptions &options)
272 string server = getenv("CLIENT_TEST_SERVER") ? getenv("CLIENT_TEST_SERVER") : "funambol";
274 server += m_clientID;
276 class ClientTest : public SyncContext {
278 ClientTest(const string &server,
279 const string &logbase,
280 const SyncOptions &options) :
281 SyncContext(server, false),
288 virtual void prepare() {
289 setLogDir(m_logbase, true);
290 setMaxLogDirs(0, true);
291 setMaxObjSize(m_options.m_maxObjSize, true);
292 setMaxMsgSize(m_options.m_maxMsgSize, true);
293 setWBXML(m_options.m_isWBXML, true);
294 SyncContext::prepare();
297 virtual void displaySyncProgress(sysync::TProgressEventEnum type,
298 int32_t extra1, int32_t extra2, int32_t extra3)
302 if (m_options.m_startCallback(*this, m_options)) {
303 m_options.m_isAborted = true;
308 virtual bool checkForAbort() { return m_options.m_isAborted; }
309 virtual bool checkForSuspend() {return m_options.m_isSuspended;}
311 virtual boost::shared_ptr<TransportAgent> createTransportAgent()
313 boost::shared_ptr<TransportAgent>wrapper = m_options.m_transport;
314 boost::shared_ptr<TransportAgent>agent =SyncContext::createTransportAgent();
317 dynamic_cast<TransportWrapper*>(wrapper.get())->setAgent(agent);
318 dynamic_cast<TransportWrapper*>(wrapper.get())->setSyncOptions(&m_options);
323 const string m_logbase;
324 SyncOptions m_options;
326 } client(server, logbase, options);
328 // configure active sources with the desired sync mode,
330 FilterConfigNode::ConfigFilter filter;
331 filter[SyncSourceConfig::m_sourcePropSync.getName()] = "none";
332 client.setConfigFilter(false, "", filter);
333 filter[SyncSourceConfig::m_sourcePropSync.getName()] =
334 PrettyPrintSyncMode(options.m_syncMode);
335 for(int i = 0; sources[i] >= 0; i++) {
336 client.setConfigFilter(false, m_source2Config[sources[i]], filter);
340 SyncMLStatus status = client.sync(&report);
341 options.m_checkReport.check(status, report);
347 std::auto_ptr<TestEvolution> m_clientB;
348 const TestRegistry &m_configs;
350 /** prefix, username, password to be used for local databases */
351 string m_evoPrefix, m_evoUser, m_evoPassword;
354 * The ClientTest framework identifies active configs with an integer.
355 * This is the mapping to the corresponding config name, created when
356 * constructing this instance.
358 vector<string> m_source2Config;
360 /** returns the name of the Evolution database */
361 string getDatabaseName(const string &configName) {
362 return m_evoPrefix + configName + "_" + m_clientID;
365 static TestingSyncSource *createSource(ClientTest &client, int source, bool isSourceA) {
366 TestEvolution &evClient((TestEvolution &)client);
367 string name = evClient.m_source2Config[source];
368 string database = evClient.getDatabaseName(name);
370 SyncConfig config("client-test-changes");
371 SyncSourceNodes nodes = config.getSyncSourceNodes(name,
372 string("_") + ((TestEvolution &)client).m_clientID +
373 "_" + (isSourceA ? "A" : "B"));
375 // always set this property: the name might have changes since last test run
376 nodes.getProperties()->setProperty("evolutionsource", database.c_str());
377 nodes.getProperties()->setProperty("evolutionuser", evClient.m_evoUser.c_str());
378 nodes.getProperties()->setProperty("evolutionpassword", evClient.m_evoPassword.c_str());
380 SyncSourceParams params(name,
383 const RegisterSyncSourceTest *test = evClient.m_configs[name];
384 ClientTestConfig testConfig;
385 getSourceConfig(test, testConfig);
387 PersistentSyncSourceConfig sourceConfig(params.m_name, params.m_nodes);
388 sourceConfig.setSourceType(testConfig.type);
390 // downcasting here: anyone who registers his sources for testing
391 // must ensure that they are indeed TestingSyncSource instances
392 SyncSource *ss = SyncSource::createSource(params);
393 return static_cast<TestingSyncSource *>(ss);
397 static void handler(int sig)
402 fprintf(stderr, "\ncaught signal %d\n", sig);
404 #ifdef HAVE_EXECINFO_H
405 size = backtrace(buffer, sizeof(buffer)/sizeof(buffer[0]));
406 backtrace_symbols_fd(buffer, size, 2);
408 #ifdef HAVE_VALGRIND_VALGRIND_H
409 VALGRIND_PRINTF_BACKTRACE("\ncaught signal %d\n", sig);
411 /* system("objdump -l -C -d client-test >&2"); */
412 struct sigaction act;
413 memset(&act, 0, sizeof(act));
414 act.sa_handler = SIG_DFL;
415 sigaction(SIGABRT, &act, NULL);
419 static class RegisterTestEvolution {
421 RegisterTestEvolution() :
423 struct sigaction act;
425 memset(&act, 0, sizeof(act));
426 act.sa_handler = handler;
427 sigaction(SIGABRT, &act, NULL);
428 sigaction(SIGSEGV, &act, NULL);
429 sigaction(SIGILL, &act, NULL);
431 #if defined(HAVE_GLIB)
432 // this is required when using glib directly or indirectly
437 testClient.registerTests();
441 TestEvolution testClient;