2 * Copyright (C) 2008 Funambol, Inc.
3 * Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
4 * Copyright (C) 2009 Intel Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) version 3.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 /** @addtogroup ClientTest */
30 #ifdef ENABLE_INTEGRATION_TESTS
32 #include "ClientTest.h"
34 #include <SyncSource.h>
35 #include <TransportAgent.h>
37 #include <syncevo/util.h>
38 #include <syncevo/SyncContext.h>
39 #include <VolatileConfigNode.h>
41 #include <synthesis/dataconversion.h>
54 #include <boost/bind.hpp>
56 #include <syncevo/declarations.h>
60 * Using this pointer automates the open()/beginSync()/endSync()/close()
61 * life cycle: it automatically calls these functions when a new
62 * pointer is assigned or deleted.
64 class TestingSyncSourcePtr : public std::auto_ptr<TestingSyncSource>
66 typedef std::auto_ptr<TestingSyncSource> base_t;
68 TestingSyncSourcePtr() {}
69 TestingSyncSourcePtr(TestingSyncSource *source) :
72 CPPUNIT_ASSERT(source);
73 SOURCE_ASSERT_NO_FAILURE(source, source->open());
74 SOURCE_ASSERT_NO_FAILURE(source, source->beginSync("", ""));
75 const char * serverMode = getenv ("CLIENT_TEST_MODE");
76 if (serverMode && !strcmp (serverMode, "server")) {
77 SOURCE_ASSERT_NO_FAILURE(source, source->enableServerMode());
80 ~TestingSyncSourcePtr()
85 void reset(TestingSyncSource *source = NULL)
88 BOOST_FOREACH(const SyncSource::Operations::CallbackFunctor_t &callback,
89 get()->getOperations().m_endSession) {
92 SOURCE_ASSERT_NO_FAILURE(get(), get()->endSync(true));
93 SOURCE_ASSERT_NO_FAILURE(get(), get()->close());
95 CPPUNIT_ASSERT_NO_THROW(base_t::reset(source));
97 SOURCE_ASSERT_NO_FAILURE(source, source->open());
98 SOURCE_ASSERT_NO_FAILURE(source, source->beginSync("", ""));
99 const char * serverMode = getenv ("CLIENT_TEST_MODE");
100 if (serverMode && !strcmp (serverMode, "server")) {
101 SOURCE_ASSERT_NO_FAILURE(source, source->enableServerMode());
103 BOOST_FOREACH(const SyncSource::Operations::CallbackFunctor_t &callback,
104 source->getOperations().m_endSession) {
111 bool SyncOptions::defaultWBXML()
113 const char *t = getenv("CLIENT_TEST_XML");
114 if (t && (!strcmp(t, "1") || !strcasecmp(t, "t"))) {
122 std::list<std::string> listItemsOfType(TestingSyncSource *source, int state)
124 std::list<std::string> res;
126 BOOST_FOREACH(const string &luid, source->getItems(SyncSourceChanges::State(state))) {
131 static std::list<std::string> listNewItems(TestingSyncSource *source) { return listItemsOfType(source, SyncSourceChanges::NEW); }
132 static std::list<std::string> listUpdatedItems(TestingSyncSource *source) { return listItemsOfType(source, SyncSourceChanges::UPDATED); }
133 static std::list<std::string> listDeletedItems(TestingSyncSource *source) { return listItemsOfType(source, SyncSourceChanges::DELETED); }
134 static std::list<std::string> listItems(TestingSyncSource *source) { return listItemsOfType(source, SyncSourceChanges::ANY); }
136 int countItemsOfType(TestingSyncSource *source, int type) { return source->getItems(SyncSourceChanges::State(type)).size(); }
137 static int countNewItems(TestingSyncSource *source) { return countItemsOfType(source, SyncSourceChanges::NEW); }
138 static int countUpdatedItems(TestingSyncSource *source) { return countItemsOfType(source, SyncSourceChanges::UPDATED); }
139 static int countDeletedItems(TestingSyncSource *source) { return countItemsOfType(source, SyncSourceChanges::DELETED); }
140 static int countItems(TestingSyncSource *source) { return countItemsOfType(source, SyncSourceChanges::ANY); }
143 /** insert new item, return LUID */
144 static std::string importItem(TestingSyncSource *source, std::string &data)
146 CPPUNIT_ASSERT(source);
148 SyncSourceRaw::InsertItemResult res;
149 SOURCE_ASSERT_NO_FAILURE(source, res = source->insertItemRaw("", data));
150 CPPUNIT_ASSERT(!res.m_luid.empty());
157 /** adds the supported tests to the instance itself */
158 void LocalTests::addTests() {
159 if (config.createSourceA) {
160 ADD_TEST(LocalTests, testOpen);
161 ADD_TEST(LocalTests, testIterateTwice);
162 if (config.insertItem) {
163 ADD_TEST(LocalTests, testSimpleInsert);
164 ADD_TEST(LocalTests, testLocalDeleteAll);
165 ADD_TEST(LocalTests, testComplexInsert);
167 if (config.updateItem) {
168 ADD_TEST(LocalTests, testLocalUpdate);
170 if (config.createSourceB) {
171 ADD_TEST(LocalTests, testChanges);
179 ADD_TEST(LocalTests, testImport);
180 ADD_TEST(LocalTests, testImportDelete);
183 if (config.templateItem &&
184 config.uniqueProperties) {
185 ADD_TEST(LocalTests, testManyChanges);
188 if (config.parentItem &&
190 ADD_TEST(LocalTests, testLinkedItemsParent);
191 ADD_TEST(LocalTests, testLinkedItemsChild);
192 ADD_TEST(LocalTests, testLinkedItemsParentChild);
193 ADD_TEST(LocalTests, testLinkedItemsChildParent);
194 ADD_TEST(LocalTests, testLinkedItemsChildChangesParent);
195 ADD_TEST(LocalTests, testLinkedItemsRemoveParentFirst);
196 ADD_TEST(LocalTests, testLinkedItemsRemoveNormal);
197 if (config.sourceKnowsItemSemantic) {
198 ADD_TEST(LocalTests, testLinkedItemsInsertParentTwice);
199 ADD_TEST(LocalTests, testLinkedItemsInsertChildTwice);
201 ADD_TEST(LocalTests, testLinkedItemsParentUpdate);
202 ADD_TEST(LocalTests, testLinkedItemsUpdateChild);
203 ADD_TEST(LocalTests, testLinkedItemsInsertBothUpdateChild);
204 ADD_TEST(LocalTests, testLinkedItemsInsertBothUpdateParent);
210 std::string LocalTests::insert(CreateSource createSource, const char *data, bool relaxed) {
212 TestingSyncSourcePtr source(createSource());
214 // count number of already existing items
216 CPPUNIT_ASSERT_NO_THROW(numItems = countItems(source.get()));
217 SyncSourceRaw::InsertItemResult res;
218 SOURCE_ASSERT_NO_FAILURE(source.get(), res = source->insertItemRaw("", data));
219 CPPUNIT_ASSERT(!res.m_luid.empty());
221 // delete source again
225 // two possible results:
226 // - a new item was added
227 // - the item was matched against an existing one
228 CPPUNIT_ASSERT_NO_THROW(source.reset(createSource()));
229 CPPUNIT_ASSERT_EQUAL(numItems + (res.m_merged ? 0 : 1),
230 countItems(source.get()));
231 CPPUNIT_ASSERT(countNewItems(source.get()) == 0);
232 CPPUNIT_ASSERT(countUpdatedItems(source.get()) == 0);
233 CPPUNIT_ASSERT(countDeletedItems(source.get()) == 0);
239 /** deletes specific item locally via sync source */
240 static std::string updateItem(CreateSource createSource, const std::string &uid, const char *data) {
243 CPPUNIT_ASSERT(createSource.createSource);
246 TestingSyncSourcePtr source(createSource());
249 SyncSourceRaw::InsertItemResult res;
250 SOURCE_ASSERT_NO_FAILURE(source.get(), res = source->insertItemRaw(uid, data));
251 SOURCE_ASSERT(source.get(), !res.m_luid.empty());
256 /** updates specific item locally via sync source */
257 static void removeItem(CreateSource createSource, const std::string &luid)
259 CPPUNIT_ASSERT(createSource.createSource);
262 TestingSyncSourcePtr source(createSource());
265 SOURCE_ASSERT_NO_FAILURE(source.get(), source->deleteItem(luid));
268 void LocalTests::update(CreateSource createSource, const char *data, bool check) {
269 CPPUNIT_ASSERT(createSource.createSource);
270 CPPUNIT_ASSERT(data);
273 TestingSyncSourcePtr source(createSource());
275 // get existing item, then update it
276 SyncSourceChanges::Items_t::const_iterator it;
277 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getAllItems().begin());
278 CPPUNIT_ASSERT(it != source->getAllItems().end());
280 SOURCE_ASSERT_NO_FAILURE(source.get(), source->insertItemRaw(luid, data));
281 CPPUNIT_ASSERT_NO_THROW(source.reset());
287 // check that the right changes are reported when reopening the source
288 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSource()));
289 CPPUNIT_ASSERT_EQUAL(1, countItems(source.get()));
290 CPPUNIT_ASSERT_EQUAL(0, countNewItems(source.get()));
291 CPPUNIT_ASSERT_EQUAL(0, countUpdatedItems(source.get()));
292 CPPUNIT_ASSERT_EQUAL(0, countDeletedItems(source.get()));
294 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getAllItems().begin());
295 CPPUNIT_ASSERT(it != source->getAllItems().end());
296 CPPUNIT_ASSERT_EQUAL(luid, *it);
299 void LocalTests::update(CreateSource createSource, const char *data, const std::string &luid) {
300 CPPUNIT_ASSERT(createSource.createSource);
301 CPPUNIT_ASSERT(data);
304 TestingSyncSourcePtr source(createSource());
307 SOURCE_ASSERT_NO_FAILURE(source.get(), source->insertItemRaw(luid, data));
310 /** deletes all items locally via sync source */
311 void LocalTests::deleteAll(CreateSource createSource) {
312 CPPUNIT_ASSERT(createSource.createSource);
315 TestingSyncSourcePtr source(createSource());
318 SOURCE_ASSERT_NO_FAILURE(source.get(), source->removeAllItems());
319 CPPUNIT_ASSERT_NO_THROW(source.reset());
321 // check that all items are gone
322 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSource()));
323 SOURCE_ASSERT_MESSAGE(
324 "should be empty now",
326 countItems(source.get()) == 0);
327 CPPUNIT_ASSERT_EQUAL( 0, countNewItems(source.get()) );
328 CPPUNIT_ASSERT_EQUAL( 0, countUpdatedItems(source.get()) );
329 CPPUNIT_ASSERT_EQUAL( 0, countDeletedItems(source.get()) );
332 /** deletes specific item locally via sync source */
333 static void deleteItem(CreateSource createSource, const std::string &uid) {
334 CPPUNIT_ASSERT(createSource.createSource);
337 TestingSyncSourcePtr source(createSource());
340 SOURCE_ASSERT_NO_FAILURE(source.get(), source->deleteItem(uid));
344 * takes two databases, exports them,
345 * then compares them using synccompare
347 * @param refFile existing file with source reference items, NULL uses a dump of sync source A instead
348 * @param copy a sync source which contains the copied items, begin/endSync will be called
349 * @param raiseAssert raise assertion if comparison yields differences (defaults to true)
351 bool LocalTests::compareDatabases(const char *refFile, TestingSyncSource ©, bool raiseAssert) {
352 CPPUNIT_ASSERT(config.dump);
354 std::string sourceFile, copyFile;
357 sourceFile = refFile;
359 sourceFile = getCurrentTest() + ".A.test.dat";
360 simplifyFilename(sourceFile);
361 TestingSyncSourcePtr source;
362 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceA()));
363 SOURCE_ASSERT_EQUAL(source.get(), 0, config.dump(client, *source.get(), sourceFile.c_str()));
364 CPPUNIT_ASSERT_NO_THROW(source.reset());
367 copyFile = getCurrentTest() + ".B.test.dat";
368 simplifyFilename(copyFile);
369 SOURCE_ASSERT_EQUAL(©, 0, config.dump(client, copy, copyFile.c_str()));
371 bool equal = config.compare(client, sourceFile.c_str(), copyFile.c_str());
372 CPPUNIT_ASSERT(!raiseAssert || equal);
377 std::string LocalTests::createItem(int item, const std::string &revision, int size)
379 std::string data = config.templateItem;
380 std::stringstream prefix;
382 prefix << std::setfill('0') << std::setw(3) << item << " ";
384 const char *prop = config.uniqueProperties;
385 const char *nextProp;
388 nextProp = strchr(prop, ':');
392 curProp = std::string(prop, 0, nextProp - prop);
395 std::string property;
396 // property is expected to not start directly at the
401 size_t off = data.find(property);
402 if (off != data.npos) {
403 data.insert(off + property.size(), prefix.str());
411 /** add check for if not found, STL will crash */
412 if(data.find("<<REVISION>>") != std::string::npos) {
413 data.replace(data.find("<<REVISION>>"), strlen("<<REVISION>>"), revision);
414 } else if (data.find("REVISION") != std::string::npos) {
415 /* change "<<REVISION>>" to "REVISION" for memo */
416 data.replace(data.find("REVISION"), strlen("REVISION"), revision);
418 if (size > 0 && (int)data.size() < size) {
419 int additionalBytes = size - (int)data.size();
421 /* vCard 2.1 and vCal 1.0 need quoted-printable line breaks */
422 bool quoted = data.find("VERSION:1.0") != data.npos ||
423 data.find("VERSION:2.1") != data.npos;
424 size_t toreplace = 1;
426 CPPUNIT_ASSERT(config.sizeProperty);
428 /* stuff the item so that it reaches at least that size */
429 size_t off = data.find(config.sizeProperty);
430 CPPUNIT_ASSERT(off != data.npos);
431 std::stringstream stuffing;
433 stuffing << ";ENCODING=QUOTED-PRINTABLE:";
438 // insert after the first line, it often acts as the summary
439 if (data.find("BEGIN:VJOURNAL") != data.npos) {
440 size_t start = data.find(":", off);
441 CPPUNIT_ASSERT( start != data.npos );
442 size_t eol = data.find("\\n", off);
443 CPPUNIT_ASSERT( eol != data.npos );
444 stuffing << data.substr(start + 1, eol - start + 1);
445 toreplace += eol - start + 1;
448 while(added < additionalBytes) {
451 while(added + 4 < additionalBytes &&
457 // insert line breaks to allow folding
459 stuffing << "x=0D=0Ax";
466 off = data.find(":", off);
467 data.replace(off, toreplace, stuffing.str());
475 * insert artificial items, number of them determined by config.numItems
476 * unless passed explicitly
478 * @param createSource a factory for the sync source that is to be used
479 * @param startIndex IDs are generated starting with this value
480 * @param numItems number of items to be inserted if non-null, otherwise config.numItems is used
481 * @param size minimum size for new items
482 * @return LUIDs of all inserted items
484 std::list<std::string> LocalTests::insertManyItems(CreateSource createSource, int startIndex, int numItems, int size) {
485 std::list<std::string> luids;
487 CPPUNIT_ASSERT(config.templateItem);
488 CPPUNIT_ASSERT(config.uniqueProperties);
490 TestingSyncSourcePtr source;
491 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceA()));
492 CPPUNIT_ASSERT(startIndex > 1 || !countItems(source.get()));
494 int firstIndex = startIndex;
495 if (firstIndex < 0) {
498 int lastIndex = firstIndex + (numItems >= 1 ? numItems : config.numItems) - 1;
499 for (int item = firstIndex; item <= lastIndex; item++) {
500 std::string data = createItem(item, "", size);
501 luids.push_back(importItem(source.get(), data));
507 // creating sync source
508 void LocalTests::testOpen() {
509 // check requirements
510 CPPUNIT_ASSERT(config.createSourceA);
512 // Intentionally use the plain auto_ptr here and
513 // call open directly. That way it is a bit more clear
514 // what happens and where it fails, if it fails.
515 std::auto_ptr<TestingSyncSource> source(createSourceA());
516 // got a sync source?
517 CPPUNIT_ASSERT(source.get() != 0);
519 SOURCE_ASSERT_NO_FAILURE(source.get(), source->open());
521 CPPUNIT_ASSERT_NO_THROW(source.reset());
524 // restart scanning of items
525 void LocalTests::testIterateTwice() {
526 // check requirements
527 CPPUNIT_ASSERT(config.createSourceA);
530 TestingSyncSourcePtr source(createSourceA());
531 SOURCE_ASSERT_MESSAGE(
532 "iterating twice should produce identical results",
534 countItems(source.get()) == countItems(source.get()));
537 // insert one contact without clearing the source first
538 void LocalTests::testSimpleInsert() {
539 // check requirements
540 CPPUNIT_ASSERT(config.insertItem);
541 CPPUNIT_ASSERT(config.createSourceA);
543 insert(createSourceA, config.insertItem);
547 void LocalTests::testLocalDeleteAll() {
548 // check requirements
549 CPPUNIT_ASSERT(config.insertItem);
550 CPPUNIT_ASSERT(config.createSourceA);
552 // make sure there is something to delete, then delete again
553 insert(createSourceA, config.insertItem);
554 deleteAll(createSourceA);
557 // clean database, then insert
558 void LocalTests::testComplexInsert() {
559 testLocalDeleteAll();
564 // clean database, insert item, update it
565 void LocalTests::testLocalUpdate() {
566 // check additional requirements
567 CPPUNIT_ASSERT(config.updateItem);
569 testLocalDeleteAll();
571 update(createSourceA, config.updateItem);
574 // complex sequence of changes
575 void LocalTests::testChanges() {
576 SyncSourceChanges::Items_t::const_iterator it, it2;
578 // check additional requirements
579 CPPUNIT_ASSERT(config.createSourceB);
581 testLocalDeleteAll();
584 // clean changes in sync source B by creating and closing it
585 TestingSyncSourcePtr source(createSourceB());
586 CPPUNIT_ASSERT_NO_THROW(source.reset());
588 // no new changes now
589 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
590 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
591 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
592 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
593 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
596 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getAllItems().begin());
597 CPPUNIT_ASSERT(it != source->getAllItems().end());
599 SOURCE_ASSERT_NO_FAILURE(source.get(), source->readItem(*it, item));
600 CPPUNIT_ASSERT_NO_THROW(source.reset());
602 // delete item again via sync source A
603 deleteAll(createSourceA);
604 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
605 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
606 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
607 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
608 SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
609 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getDeletedItems().begin());
610 CPPUNIT_ASSERT(it != source->getDeletedItems().end());
611 CPPUNIT_ASSERT(!it->empty());
612 CPPUNIT_ASSERT_EQUAL(luid, *it);
613 CPPUNIT_ASSERT_NO_THROW(source.reset());
615 // insert another item via sync source A
617 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
618 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
619 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
620 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
621 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
622 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getAllItems().begin());
623 CPPUNIT_ASSERT(it != source->getAllItems().end());
625 SOURCE_ASSERT_NO_FAILURE(source.get(), source->readItem(*it, item));
627 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getNewItems().begin());
628 CPPUNIT_ASSERT(it != source->getNewItems().end());
629 SOURCE_ASSERT_NO_FAILURE(source.get(), source->readItem(*it, item));
630 CPPUNIT_ASSERT_EQUAL(luid, *it);
631 CPPUNIT_ASSERT_NO_THROW(source.reset());
633 // update item via sync source A
634 update(createSourceA, config.updateItem);
635 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
636 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
637 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
638 SOURCE_ASSERT_EQUAL(source.get(), 1, countUpdatedItems(source.get()));
639 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
641 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getUpdatedItems().begin());
642 CPPUNIT_ASSERT(it != source->getUpdatedItems().end());
643 SOURCE_ASSERT_NO_FAILURE(source.get(), source->readItem(*it, updatedItem));
644 CPPUNIT_ASSERT_EQUAL(luid, *it);
645 CPPUNIT_ASSERT_NO_THROW(source.reset());
647 // start anew, then create and update an item -> should only be listed as new
648 // or updated, but not both
649 deleteAll(createSourceA);
650 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
653 update(createSourceA, config.updateItem);
654 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
655 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
656 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()) + countUpdatedItems(source.get()));
657 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
659 // start anew, then create, delete and recreate an item -> should only be listed as new or updated,
660 // even if (as for calendar with UID) the same LUID gets reused
661 deleteAll(createSourceA);
662 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
665 deleteAll(createSourceA);
667 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceB()));
668 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
669 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()) + countUpdatedItems(source.get()));
670 if (countDeletedItems(source.get()) == 1) {
671 // It's not nice, but acceptable to send the LUID of a deleted item to a
672 // server which has never seen that LUID. The LUID must not be the same as
673 // the one we list as new or updated, though.
674 SOURCE_ASSERT_NO_FAILURE(source.get(), it = source->getDeletedItems().begin());
675 CPPUNIT_ASSERT(it != source->getDeletedItems().end());
676 SOURCE_ASSERT_NO_FAILURE(source.get(), it2 = source->getNewItems().begin());
677 if (it2 == source->getNewItems().end()) {
678 SOURCE_ASSERT_NO_FAILURE(source.get(), it2 = source->getUpdatedItems().begin());
679 CPPUNIT_ASSERT(it2 != source->getUpdatedItems().end());
681 CPPUNIT_ASSERT(*it != *it2);
683 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
687 // clean database, import file, then export again and compare
688 void LocalTests::testImport() {
689 // check additional requirements
690 CPPUNIT_ASSERT(config.import);
691 CPPUNIT_ASSERT(config.dump);
692 CPPUNIT_ASSERT(config.compare);
693 CPPUNIT_ASSERT(config.testcases);
695 testLocalDeleteAll();
697 // import via sync source A
698 TestingSyncSourcePtr source;
699 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceA()));
700 std::string testcases;
701 SOURCE_ASSERT_EQUAL(source.get(), 0, config.import(client, *source.get(), config.testcases, testcases));
702 CPPUNIT_ASSERT_NO_THROW(source.reset());
704 // export again and compare against original file
705 TestingSyncSourcePtr copy;
706 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceA()));
707 compareDatabases(testcases.c_str(), *copy.get());
708 CPPUNIT_ASSERT_NO_THROW(source.reset());
711 // same as testImport() with immediate delete
712 void LocalTests::testImportDelete() {
715 // delete again, because it was observed that this did not
716 // work right with calendars in SyncEvolution
717 testLocalDeleteAll();
720 // test change tracking with large number of items
721 void LocalTests::testManyChanges() {
722 // check additional requirements
723 CPPUNIT_ASSERT(config.templateItem);
724 CPPUNIT_ASSERT(config.uniqueProperties);
726 deleteAll(createSourceA);
728 // check that everything is empty, also resets change counter of sync source B
729 TestingSyncSourcePtr copy;
730 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
731 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
732 CPPUNIT_ASSERT_NO_THROW(copy.reset());
734 // now insert plenty of items
735 int numItems = insertManyItems(createSourceA).size();
737 // check that exactly this number of items is listed as new
738 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
739 SOURCE_ASSERT_EQUAL(copy.get(), numItems, countItems(copy.get()));
740 SOURCE_ASSERT_EQUAL(copy.get(), numItems, countNewItems(copy.get()));
741 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
742 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
743 CPPUNIT_ASSERT_NO_THROW(copy.reset());
746 deleteAll(createSourceA);
749 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
750 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
751 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
752 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
753 SOURCE_ASSERT_EQUAL(copy.get(), numItems, countDeletedItems(copy.get()));
754 CPPUNIT_ASSERT_NO_THROW(copy.reset());
757 template<class T, class V> int countEqual(const T &container,
759 return count(container.begin(),
764 // test inserting, removing and updating of parent + child item in
765 // various order plus change tracking
766 void LocalTests::testLinkedItemsParent() {
767 // check additional requirements
768 CPPUNIT_ASSERT(config.parentItem);
769 CPPUNIT_ASSERT(config.childItem);
771 deleteAll(createSourceA);
772 std::string parent, child;
773 TestingSyncSourcePtr copy;
775 // check that everything is empty, also resets change counter of sync source B
776 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
777 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
778 CPPUNIT_ASSERT_NO_THROW(copy.reset());
780 // now insert main item
781 parent = insert(createSourceA, config.parentItem, config.itemType);
783 // check that exactly the parent is listed as new
784 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
785 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
786 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
787 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
788 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
789 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
790 CPPUNIT_ASSERT_NO_THROW(copy.reset());
793 deleteAll(createSourceA);
796 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
797 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
798 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
799 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
800 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
801 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
804 // test inserting, removing and updating of parent + child item in
805 // various order plus change tracking
806 void LocalTests::testLinkedItemsChild() {
807 #if LINKED_ITEMS_RELAXED_SEMANTIC
808 // check additional requirements
809 CPPUNIT_ASSERT(config.parentItem);
810 CPPUNIT_ASSERT(config.childItem);
812 deleteAll(createSourceA);
813 std::string parent, child;
814 TestingSyncSourcePtr copy;
816 // check that everything is empty, also resets change counter of sync source B
817 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
818 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
819 CPPUNIT_ASSERT_NO_THROW(copy.reset());
821 // same as above for child item
822 child = insert(createSourceA, config.childItem, config.itemType);
824 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
825 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
826 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
827 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
828 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
829 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
830 CPPUNIT_ASSERT_NO_THROW(copy.reset());
832 deleteAll(createSourceA);
834 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
835 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
836 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
837 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
838 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
839 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
843 // test inserting, removing and updating of parent + child item in
844 // various order plus change tracking
845 void LocalTests::testLinkedItemsParentChild() {
846 // check additional requirements
847 CPPUNIT_ASSERT(config.parentItem);
848 CPPUNIT_ASSERT(config.childItem);
850 deleteAll(createSourceA);
851 std::string parent, child;
852 TestingSyncSourcePtr copy;
854 // check that everything is empty, also resets change counter of sync source B
855 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
856 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
857 CPPUNIT_ASSERT_NO_THROW(copy.reset());
859 // insert parent first, then child
860 parent = insert(createSourceA, config.parentItem, config.itemType);
861 child = insert(createSourceA, config.childItem, config.itemType);
863 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
864 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
865 SOURCE_ASSERT_EQUAL(copy.get(), 2, countNewItems(copy.get()));
866 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
867 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
868 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
869 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
870 CPPUNIT_ASSERT_NO_THROW(copy.reset());
872 deleteAll(createSourceA);
874 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
875 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
876 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
877 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
878 SOURCE_ASSERT_EQUAL(copy.get(), 2, countDeletedItems(copy.get()));
879 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
880 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
883 // test inserting, removing and updating of parent + child item in
884 // various order plus change tracking
885 void LocalTests::testLinkedItemsChildParent() {
886 #if LINKED_ITEMS_RELAXED_SEMANTIC
887 // check additional requirements
888 CPPUNIT_ASSERT(config.parentItem);
889 CPPUNIT_ASSERT(config.childItem);
891 deleteAll(createSourceA);
892 std::string parent, child;
893 TestingSyncSourcePtr copy;
895 // check that everything is empty, also resets change counter of sync source B
896 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
897 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
898 CPPUNIT_ASSERT_NO_THROW(copy.reset());
900 // insert child first, then parent
901 child = insert(createSourceA, config.childItem);
902 parent = insert(createSourceA, config.parentItem, true);
904 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
905 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
906 SOURCE_ASSERT_EQUAL(copy.get(), 2, countNewItems(copy.get()));
907 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
908 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
909 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
910 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
911 CPPUNIT_ASSERT_NO_THROW(copy.reset());
913 deleteAll(createSourceA);
915 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
916 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
917 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
918 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
919 SOURCE_ASSERT_EQUAL(copy.get(), 2, countDeletedItems(copy.get()));
920 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
921 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
925 // test inserting, removing and updating of parent + child item in
926 // various order plus change tracking
927 void LocalTests::testLinkedItemsChildChangesParent() {
928 #if LINKED_ITEMS_RELAXED_SEMANTIC
929 // check additional requirements
930 CPPUNIT_ASSERT(config.parentItem);
931 CPPUNIT_ASSERT(config.childItem);
933 deleteAll(createSourceA);
934 std::string parent, child;
935 TestingSyncSourcePtr copy;
937 // check that everything is empty, also resets change counter of sync source B
938 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
939 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
940 CPPUNIT_ASSERT_NO_THROW(copy.reset());
942 // insert child first, check changes, then insert the parent
943 child = insert(createSourceA, config.childItem, config.itemType);
945 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
946 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
947 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
948 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
949 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
950 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
951 CPPUNIT_ASSERT_NO_THROW(copy.reset());
953 parent = insert(createSourceA, config.parentItem, true);
955 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
956 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
957 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
958 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listNewItems(copy.get()), parent));
959 // relaxed semantic: the child item might be considered updated now if
960 // it had to be modified when inserting the parent
961 SOURCE_ASSERT(copy.get(), 1 >= countUpdatedItems(copy.get()));
962 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
963 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
964 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
965 CPPUNIT_ASSERT_NO_THROW(copy.reset());
967 deleteAll(createSourceA);
969 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
970 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
971 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
972 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
973 SOURCE_ASSERT_EQUAL(copy.get(), 2, countDeletedItems(copy.get()));
974 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
975 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
979 // test inserting, removing and updating of parent + child item in
980 // various order plus change tracking
981 void LocalTests::testLinkedItemsRemoveParentFirst() {
982 #if LINKED_ITEMS_RELAXED_SEMANTIC
983 // check additional requirements
984 CPPUNIT_ASSERT(config.parentItem);
985 CPPUNIT_ASSERT(config.childItem);
987 deleteAll(createSourceA);
988 std::string parent, child;
989 TestingSyncSourcePtr copy;
991 // check that everything is empty, also resets change counter of sync source B
992 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
993 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
994 CPPUNIT_ASSERT_NO_THROW(copy.reset());
996 // insert both items, remove parent, then child
997 parent = insert(createSourceA, config.parentItem);
998 child = insert(createSourceA, config.childItem);
1000 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1001 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
1002 SOURCE_ASSERT_EQUAL(copy.get(), 2, countNewItems(copy.get()));
1003 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1004 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1005 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
1006 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
1007 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1009 deleteItem(createSourceA, parent);
1011 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1012 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1013 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1014 // deleting the parent may or may not modify the child
1015 SOURCE_ASSERT(copy.get(), 1 >= countUpdatedItems(copy.get()));
1016 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1017 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
1018 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1020 deleteItem(createSourceA, child);
1022 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1023 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1024 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1025 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1026 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1027 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
1028 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1032 // test inserting, removing and updating of parent + child item in
1033 // various order plus change tracking
1034 void LocalTests::testLinkedItemsRemoveNormal() {
1035 // check additional requirements
1036 CPPUNIT_ASSERT(config.parentItem);
1037 CPPUNIT_ASSERT(config.childItem);
1039 deleteAll(createSourceA);
1040 std::string parent, child;
1041 TestingSyncSourcePtr source, copy;
1043 // check that everything is empty, also resets change counter of sync source B
1044 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1045 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1046 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1048 // insert both items, remove child, then parent
1049 parent = insert(createSourceA, config.parentItem);
1050 child = insert(createSourceA, config.childItem);
1052 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1053 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
1054 SOURCE_ASSERT_EQUAL(copy.get(), 2, countNewItems(copy.get()));
1055 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1056 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1057 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
1058 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
1059 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1061 deleteItem(createSourceA, child);
1063 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceA()));
1064 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
1065 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
1066 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
1067 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
1068 CPPUNIT_ASSERT_NO_THROW(source.reset());
1070 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1071 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1072 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1073 // parent might have been updated
1074 int updated = countUpdatedItems(copy.get());
1075 SOURCE_ASSERT(copy.get(), 0 <= updated && updated <= 1);
1076 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1077 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
1078 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1080 deleteItem(createSourceA, parent);
1082 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1083 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1084 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1085 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1086 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1087 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
1088 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1091 // test inserting, removing and updating of parent + child item in
1092 // various order plus change tracking
1093 void LocalTests::testLinkedItemsInsertParentTwice() {
1094 // check additional requirements
1095 CPPUNIT_ASSERT(config.parentItem);
1096 CPPUNIT_ASSERT(config.childItem);
1098 deleteAll(createSourceA);
1099 std::string parent, child;
1100 TestingSyncSourcePtr copy;
1102 // check that everything is empty, also resets change counter of sync source B
1103 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1104 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1105 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1107 // add parent twice (should be turned into update)
1108 parent = insert(createSourceA, config.parentItem);
1110 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1111 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1112 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
1113 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1114 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1115 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
1116 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1118 parent = insert(createSourceA, config.parentItem);
1120 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1121 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1122 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1123 SOURCE_ASSERT_EQUAL(copy.get(), 1, countUpdatedItems(copy.get()));
1124 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1125 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listUpdatedItems(copy.get()), parent));
1126 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1128 deleteItem(createSourceA, parent);
1130 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1131 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1132 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1133 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1134 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1135 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
1138 // test inserting, removing and updating of parent + child item in
1139 // various order plus change tracking
1140 void LocalTests::testLinkedItemsInsertChildTwice() {
1141 // check additional requirements
1142 CPPUNIT_ASSERT(config.parentItem);
1143 CPPUNIT_ASSERT(config.childItem);
1145 deleteAll(createSourceA);
1146 std::string parent, child;
1147 TestingSyncSourcePtr copy;
1149 // check that everything is empty, also resets change counter of sync source B
1150 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1151 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1152 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1154 #if LINKED_ITEMS_RELAXED_SEMANTIC
1155 // add child twice (should be turned into update)
1156 child = insert(createSourceA, config.childItem);
1158 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1159 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1160 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
1161 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1162 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1163 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
1164 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1166 child = insert(createSourceA, config.childItem);
1168 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1169 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1170 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1171 SOURCE_ASSERT_EQUAL(copy.get(), 1, countUpdatedItems(copy.get()));
1172 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1173 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listUpdatedItems(copy.get()), child));
1174 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1176 deleteItem(createSourceA, child);
1178 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1179 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1180 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1181 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1182 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1183 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
1187 // test inserting, removing and updating of parent + child item in
1188 // various order plus change tracking
1189 void LocalTests::testLinkedItemsParentUpdate() {
1190 // check additional requirements
1191 CPPUNIT_ASSERT(config.parentItem);
1192 CPPUNIT_ASSERT(config.childItem);
1194 deleteAll(createSourceA);
1195 std::string parent, child;
1196 TestingSyncSourcePtr copy;
1198 // check that everything is empty, also resets change counter of sync source B
1199 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1200 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1201 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1203 // add parent, then update it
1204 parent = insert(createSourceA, config.parentItem);
1206 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1207 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1208 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
1209 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1210 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1211 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
1212 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1214 parent = updateItem(createSourceA, parent, config.parentItem);
1216 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1217 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1218 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1219 SOURCE_ASSERT_EQUAL(copy.get(), 1, countUpdatedItems(copy.get()));
1220 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1221 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listUpdatedItems(copy.get()), parent));
1222 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1224 deleteItem(createSourceA, parent);
1226 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1227 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1228 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1229 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1230 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1231 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
1232 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1235 // test inserting, removing and updating of parent + child item in
1236 // various order plus change tracking
1237 void LocalTests::testLinkedItemsUpdateChild() {
1238 #if LINKED_ITEMS_RELAXED_SEMANTIC
1239 // check additional requirements
1240 CPPUNIT_ASSERT(config.parentItem);
1241 CPPUNIT_ASSERT(config.childItem);
1243 deleteAll(createSourceA);
1244 std::string parent, child;
1245 TestingSyncSourcePtr copy;
1247 // check that everything is empty, also resets change counter of sync source B
1248 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1249 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1250 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1252 // add child, then update it
1253 child = insert(createSourceA, config.childItem);
1255 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1256 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1257 SOURCE_ASSERT_EQUAL(copy.get(), 1, countNewItems(copy.get()));
1258 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1259 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1260 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
1261 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1263 child = updateItem(createSourceA, child, config.childItem);
1265 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1266 SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
1267 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1268 SOURCE_ASSERT_EQUAL(copy.get(), 1, countUpdatedItems(copy.get()));
1269 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1270 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listUpdatedItems(copy.get()), child));
1271 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1273 deleteItem(createSourceA, child);
1275 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1276 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1277 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1278 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1279 SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
1280 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
1281 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1285 // test inserting, removing and updating of parent + child item in
1286 // various order plus change tracking
1287 void LocalTests::testLinkedItemsInsertBothUpdateChild() {
1288 // check additional requirements
1289 CPPUNIT_ASSERT(config.parentItem);
1290 CPPUNIT_ASSERT(config.childItem);
1292 deleteAll(createSourceA);
1293 std::string parent, child;
1294 TestingSyncSourcePtr copy;
1296 // check that everything is empty, also resets change counter of sync source B
1297 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1298 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1299 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1301 // add parent and child, then update child
1302 parent = insert(createSourceA, config.parentItem);
1303 child = insert(createSourceA, config.childItem);
1305 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1306 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
1307 SOURCE_ASSERT_EQUAL(copy.get(), 2, countNewItems(copy.get()));
1308 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1309 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1310 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
1311 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
1312 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1314 child = updateItem(createSourceA, child, config.childItem);
1316 // child has to be listed as modified, parent may be
1317 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1318 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
1319 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1320 SOURCE_ASSERT(copy.get(), 1 <= countUpdatedItems(copy.get()));
1321 SOURCE_ASSERT(copy.get(), 2 >= countUpdatedItems(copy.get()));
1322 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1323 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listUpdatedItems(copy.get()), child));
1324 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1326 deleteItem(createSourceA, parent);
1327 deleteItem(createSourceA, child);
1329 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1330 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1331 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1332 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1333 SOURCE_ASSERT_EQUAL(copy.get(), 2, countDeletedItems(copy.get()));
1334 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
1335 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
1336 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1339 // test inserting, removing and updating of parent + child item in
1340 // various order plus change tracking
1341 void LocalTests::testLinkedItemsInsertBothUpdateParent() {
1342 // check additional requirements
1343 CPPUNIT_ASSERT(config.parentItem);
1344 CPPUNIT_ASSERT(config.childItem);
1346 deleteAll(createSourceA);
1347 std::string parent, child;
1348 TestingSyncSourcePtr copy;
1350 // check that everything is empty, also resets change counter of sync source B
1351 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1352 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1353 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1355 // add parent and child, then update parent
1356 parent = insert(createSourceA, config.parentItem);
1357 child = insert(createSourceA, config.childItem);
1359 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1360 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
1361 SOURCE_ASSERT_EQUAL(copy.get(), 2, countNewItems(copy.get()));
1362 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1363 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1364 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), child));
1365 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listItems(copy.get()), parent));
1366 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1368 parent = updateItem(createSourceA, parent, config.parentItem);
1370 // parent has to be listed as modified, child may be
1371 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1372 SOURCE_ASSERT_EQUAL(copy.get(), 2, countItems(copy.get()));
1373 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1374 SOURCE_ASSERT(copy.get(), 1 <= countUpdatedItems(copy.get()));
1375 SOURCE_ASSERT(copy.get(), 2 >= countUpdatedItems(copy.get()));
1376 SOURCE_ASSERT_EQUAL(copy.get(), 0, countDeletedItems(copy.get()));
1377 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listUpdatedItems(copy.get()), parent));
1378 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1380 deleteItem(createSourceA, parent);
1381 deleteItem(createSourceA, child);
1383 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
1384 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1385 SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
1386 SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
1387 SOURCE_ASSERT_EQUAL(copy.get(), 2, countDeletedItems(copy.get()));
1388 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), parent));
1389 SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
1393 SyncTests::SyncTests(const std::string &name, ClientTest &cl, std::vector<int> sourceIndices, bool isClientA) :
1394 CppUnit::TestSuite(name),
1396 sourceArray = new int[sourceIndices.size() + 1];
1398 for (std::vector<int>::iterator it = sourceIndices.begin();
1399 it != sourceIndices.end();
1401 ClientTest::Config config;
1402 client.getSyncSourceConfig(*it, config);
1404 if (config.sourceName) {
1405 sourceArray[sources.size()+offset] = *it;
1406 if (config.subConfigs) {
1407 vector<string> subs;
1408 boost::split (subs, config.subConfigs, boost::is_any_of(","));
1410 ClientTest::Config subConfig;
1411 BOOST_FOREACH (string sub, subs) {
1412 client.getSourceConfig (sub, subConfig);
1413 sources.push_back(std::pair<int,LocalTests *>(*it, cl.createLocalTests(sub, client.getLocalSourcePosition(sub), subConfig)));
1417 sources.push_back(std::pair<int,LocalTests *>(*it, cl.createLocalTests(config.sourceName, client.getLocalSourcePosition(config.sourceName), config)));
1421 sourceArray[sources.size()+ offset] = -1;
1423 // check whether we have a second client
1424 ClientTest *clientB = cl.getClientB();
1426 accessClientB = clientB->createSyncTests(name, sourceIndices, false);
1432 SyncTests::~SyncTests() {
1433 for (source_it it = sources.begin();
1434 it != sources.end();
1438 delete [] sourceArray;
1439 if (accessClientB) {
1440 delete accessClientB;
1444 /** adds the supported tests to the instance itself */
1445 void SyncTests::addTests() {
1446 if (sources.size()) {
1447 const ClientTest::Config &config(sources[0].second->config);
1449 // run this test first, even if it is more complex:
1450 // if it works, all the following tests will run with
1451 // the server in a deterministic state
1452 if (config.createSourceA) {
1453 if (config.insertItem) {
1454 ADD_TEST(SyncTests, testDeleteAllRefresh);
1458 ADD_TEST(SyncTests, testTwoWaySync);
1459 ADD_TEST(SyncTests, testSlowSync);
1460 ADD_TEST(SyncTests, testRefreshFromServerSync);
1461 ADD_TEST(SyncTests, testRefreshFromClientSync);
1463 if (config.compare &&
1465 ADD_TEST(SyncTests, testConversion);
1468 if (config.createSourceA) {
1469 if (config.insertItem) {
1470 ADD_TEST(SyncTests, testRefreshFromServerSemantic);
1471 ADD_TEST(SyncTests, testRefreshFromClientSemantic);
1472 ADD_TEST(SyncTests, testRefreshStatus);
1474 if (accessClientB &&
1477 ADD_TEST(SyncTests, testCopy);
1478 ADD_TEST(SyncTests, testDelete);
1479 ADD_TEST(SyncTests, testAddUpdate);
1480 ADD_TEST(SyncTests, testManyItems);
1481 ADD_TEST(SyncTests, testManyDeletes);
1482 ADD_TEST(SyncTests, testSlowSyncSemantic);
1483 ADD_TEST(SyncTests, testComplexRefreshFromServerSemantic);
1485 if (config.updateItem) {
1486 ADD_TEST(SyncTests, testUpdate);
1488 if (config.complexUpdateItem) {
1489 ADD_TEST(SyncTests, testComplexUpdate);
1491 if (config.mergeItem1 && config.mergeItem2) {
1492 ADD_TEST(SyncTests, testMerge);
1494 if (config.import) {
1495 ADD_TEST(SyncTests, testTwinning);
1496 ADD_TEST(SyncTests, testItems);
1497 ADD_TEST(SyncTests, testItemsXML);
1499 if (config.templateItem) {
1500 ADD_TEST(SyncTests, testMaxMsg);
1501 ADD_TEST(SyncTests, testLargeObject);
1502 ADD_TEST(SyncTests, testOneWayFromServer);
1503 ADD_TEST(SyncTests, testOneWayFromClient);
1509 if (config.retrySync &&
1510 config.insertItem &&
1511 config.updateItem &&
1515 CppUnit::TestSuite *retryTests = new CppUnit::TestSuite(getName() + "::Retry");
1516 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeClientAdd);
1517 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeClientRemove);
1518 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeClientUpdate);
1519 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeServerAdd);
1520 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeServerRemove);
1521 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeServerUpdate);
1522 ADD_TEST_TO_SUITE(retryTests, SyncTests, testInterruptResumeFull);
1523 addTest(FilterTest(retryTests));
1526 if (config.suspendSync &&
1527 config.insertItem &&
1528 config.updateItem &&
1532 CppUnit::TestSuite *suspendTests = new CppUnit::TestSuite(getName() + "::Suspend");
1533 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendClientAdd);
1534 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendClientRemove);
1535 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendClientUpdate);
1536 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendServerAdd);
1537 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendServerRemove);
1538 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendServerUpdate);
1539 ADD_TEST_TO_SUITE(suspendTests, SyncTests, testUserSuspendFull);
1540 addTest(FilterTest(suspendTests));
1543 if (config.resendSync &&
1544 config.insertItem &&
1545 config.updateItem &&
1549 CppUnit::TestSuite *resendTests = new CppUnit::TestSuite(getName() + "::Resend");
1550 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendClientAdd);
1551 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendClientRemove);
1552 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendClientUpdate);
1553 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendServerAdd);
1554 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendServerRemove);
1555 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendServerUpdate);
1556 ADD_TEST_TO_SUITE(resendTests, SyncTests, testResendFull);
1557 addTest(FilterTest(resendTests));
1563 bool SyncTests::compareDatabases(const char *refFileBase, bool raiseAssert) {
1568 CPPUNIT_ASSERT(accessClientB);
1569 for (it1 = sources.begin(), it2 = accessClientB->sources.begin();
1570 it1 != sources.end() && it2 != accessClientB->sources.end();
1572 TestingSyncSourcePtr copy;
1573 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(it2->second->createSourceB()));
1575 std::string refFile = refFileBase;
1576 refFile += it1->second->config.sourceName;
1578 simplifyFilename(refFile);
1579 if (!it1->second->compareDatabases(refFile.c_str(), *copy.get(), raiseAssert)) {
1583 if (!it1->second->compareDatabases(NULL, *copy.get(), raiseAssert)) {
1587 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1589 CPPUNIT_ASSERT(it1 == sources.end());
1590 CPPUNIT_ASSERT(it2 == accessClientB->sources.end());
1592 CPPUNIT_ASSERT(!raiseAssert || equal);
1596 /** deletes all items locally and on server */
1597 void SyncTests::deleteAll(DeleteAllMode mode) {
1599 SyncPrefix prefix("deleteall", *this);
1602 case DELETE_ALL_SYNC:
1603 // a refresh from server would slightly reduce the amount of data exchanged, but not all servers support it
1604 for (it = sources.begin(); it != sources.end(); ++it) {
1605 it->second->deleteAll(it->second->createSourceA);
1607 doSync("init", SyncOptions(SYNC_SLOW));
1608 // now that client and server are in sync, delete locally and sync again
1609 for (it = sources.begin(); it != sources.end(); ++it) {
1610 it->second->deleteAll(it->second->createSourceA);
1613 SyncOptions(SYNC_TWO_WAY,
1614 CheckSyncReport(0,0,0, 0,0,-1, true, SYNC_TWO_WAY)));
1616 case DELETE_ALL_REFRESH:
1617 // delete locally and then tell the server to "copy" the empty databases
1618 for (it = sources.begin(); it != sources.end(); ++it) {
1619 it->second->deleteAll(it->second->createSourceA);
1621 doSync("refreshserver",
1622 SyncOptions(SYNC_REFRESH_FROM_CLIENT,
1623 CheckSyncReport(0,0,0, 0,0,-1, true, SYNC_REFRESH_FROM_CLIENT)));
1628 /** get both clients in sync with empty server, then copy one item from client A to B */
1629 void SyncTests::doCopy() {
1630 SyncPrefix("copy", *this);
1632 // check requirements
1633 CPPUNIT_ASSERT(accessClientB);
1636 accessClientB->deleteAll();
1638 // insert into first database, copy to server
1640 for (it = sources.begin(); it != sources.end(); ++it) {
1641 it->second->testSimpleInsert();
1644 SyncOptions(SYNC_TWO_WAY,
1645 CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
1647 // copy into second database
1648 accessClientB->doSync("recv",
1649 SyncOptions(SYNC_TWO_WAY,
1650 CheckSyncReport(1,0,0, 0,0,0, true, SYNC_TWO_WAY)));
1656 * replicate server database locally: same as SYNC_REFRESH_FROM_SERVER,
1657 * but done with explicit local delete and then a SYNC_SLOW because some
1658 * servers do no support SYNC_REFRESH_FROM_SERVER
1660 void SyncTests::refreshClient(SyncOptions options) {
1662 for (it = sources.begin(); it != sources.end(); ++it) {
1663 it->second->deleteAll(it->second->createSourceA);
1668 .setSyncMode(SYNC_SLOW)
1669 .setCheckReport(CheckSyncReport(-1,0,0, 0,0,0, true, SYNC_SLOW)));
1673 // delete all items, locally and on server using refresh-from-client sync
1674 void SyncTests::testDeleteAllRefresh() {
1677 // copy something to server first; doesn't matter whether it has the
1678 // item already or not, as long as it exists there afterwards
1679 for (it = sources.begin(); it != sources.end(); ++it) {
1680 it->second->testSimpleInsert();
1682 doSync("insert", SyncOptions(SYNC_SLOW));
1684 // now ensure we can delete it
1685 deleteAll(DELETE_ALL_SYNC);
1687 // nothing stored locally?
1688 for (it = sources.begin(); it != sources.end(); ++it) {
1689 TestingSyncSourcePtr source;
1690 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceA()));
1691 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
1692 CPPUNIT_ASSERT_NO_THROW(source.reset());
1695 // make sure server really deleted everything
1697 SyncOptions(SYNC_SLOW,
1698 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_SLOW)));
1699 for (it = sources.begin(); it != sources.end(); ++it) {
1700 TestingSyncSourcePtr source;
1701 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceA()));
1702 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
1703 CPPUNIT_ASSERT_NO_THROW(source.reset());
1707 // test that a refresh sync from an empty server leads to an empty datatbase
1708 // and no changes are sent to server during next two-way sync
1709 void SyncTests::testRefreshFromServerSemantic() {
1712 // clean client and server
1715 // insert item, then refresh from empty server
1716 for (it = sources.begin(); it != sources.end(); ++it) {
1717 it->second->testSimpleInsert();
1720 SyncOptions(SYNC_REFRESH_FROM_SERVER,
1721 CheckSyncReport(0,0,-1, 0,0,0, true, SYNC_REFRESH_FROM_SERVER)));
1724 for (it = sources.begin(); it != sources.end(); ++it) {
1725 TestingSyncSourcePtr source;
1726 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceA()));
1727 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
1728 CPPUNIT_ASSERT_NO_THROW(source.reset());
1731 SyncOptions(SYNC_TWO_WAY,
1732 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_TWO_WAY)));
1735 // test that a refresh sync from an empty client leads to an empty datatbase
1736 // and no changes are sent to server during next two-way sync
1737 void SyncTests::testRefreshFromClientSemantic() {
1740 // clean client and server
1743 // insert item, send to server
1744 for (it = sources.begin(); it != sources.end(); ++it) {
1745 it->second->testSimpleInsert();
1748 SyncOptions(SYNC_TWO_WAY,
1749 CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
1752 for (it = sources.begin(); it != sources.end(); ++it) {
1753 it->second->deleteAll(it->second->createSourceA);
1756 // refresh from client
1758 SyncOptions(SYNC_REFRESH_FROM_CLIENT,
1759 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_REFRESH_FROM_CLIENT)));
1763 SyncOptions(SYNC_REFRESH_FROM_SERVER,
1764 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_REFRESH_FROM_SERVER)));
1767 // tests the following sequence of events:
1769 // - delete all items
1770 // - insert one other item
1771 // - refresh from client
1772 // => no items should now be listed as new, updated or deleted for this client during another sync
1773 void SyncTests::testRefreshStatus() {
1776 for (it = sources.begin(); it != sources.end(); ++it) {
1777 it->second->testSimpleInsert();
1779 for (it = sources.begin(); it != sources.end(); ++it) {
1780 it->second->deleteAll(it->second->createSourceA);
1782 for (it = sources.begin(); it != sources.end(); ++it) {
1783 it->second->testSimpleInsert();
1785 doSync("refresh-from-client",
1786 SyncOptions(SYNC_REFRESH_FROM_CLIENT,
1787 CheckSyncReport(0,0,0, -1,-1,-1, /* strictly speaking 1,0,0, but not sure exactly what the server will be told */
1788 true, SYNC_REFRESH_FROM_CLIENT)));
1790 SyncOptions(SYNC_TWO_WAY,
1791 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_TWO_WAY)));
1794 // test that a two-way sync copies updates from database to the other client,
1795 // using simple data commonly supported by servers
1796 void SyncTests::testUpdate() {
1797 CPPUNIT_ASSERT(sources.begin() != sources.end());
1798 CPPUNIT_ASSERT(sources.begin()->second->config.updateItem);
1800 // setup client A, B and server so that they all contain the same item
1804 for (it = sources.begin(); it != sources.end(); ++it) {
1805 it->second->update(it->second->createSourceA, it->second->config.updateItem);
1809 SyncOptions(SYNC_TWO_WAY,
1810 CheckSyncReport(0,0,0, 0,1,0, true, SYNC_TWO_WAY)));
1811 accessClientB->doSync("update",
1812 SyncOptions(SYNC_TWO_WAY,
1813 CheckSyncReport(0,1,0, 0,0,0, true, SYNC_TWO_WAY)));
1818 // test that a two-way sync copies updates from database to the other client,
1819 // using data that some, but not all servers support, like adding a second
1820 // phone number to a contact
1821 void SyncTests::testComplexUpdate() {
1822 // setup client A, B and server so that they all contain the same item
1826 for (it = sources.begin(); it != sources.end(); ++it) {
1827 it->second->update(it->second->createSourceA,
1828 /* this test might get executed with some sources which have
1829 a complex update item while others don't: use the normal update item
1830 for them or even just the same item */
1831 it->second->config.complexUpdateItem ? it->second->config.complexUpdateItem :
1832 it->second->config.updateItem ? it->second->config.updateItem :
1833 it->second->config.insertItem
1838 SyncOptions(SYNC_TWO_WAY,
1839 CheckSyncReport(0,0,0, 0,1,0, true, SYNC_TWO_WAY)));
1840 accessClientB->doSync("update",
1841 SyncOptions(SYNC_TWO_WAY,
1842 CheckSyncReport(0,1,0, 0,0,0, true, SYNC_TWO_WAY)));
1848 // test that a two-way sync deletes the copy of an item in the other database
1849 void SyncTests::testDelete() {
1850 // setup client A, B and server so that they all contain the same item
1855 for (it = sources.begin(); it != sources.end(); ++it) {
1856 it->second->deleteAll(it->second->createSourceA);
1859 // transfer change from A to server to B
1861 SyncOptions(SYNC_TWO_WAY,
1862 CheckSyncReport(0,0,0, 0,0,1, true, SYNC_TWO_WAY)));
1863 accessClientB->doSync("delete",
1864 SyncOptions(SYNC_TWO_WAY,
1865 CheckSyncReport(0,0,1, 0,0,0, true, SYNC_TWO_WAY)));
1867 // check client B: shouldn't have any items now
1868 for (it = sources.begin(); it != sources.end(); ++it) {
1869 TestingSyncSourcePtr copy;
1870 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(it->second->createSourceA()));
1871 SOURCE_ASSERT_EQUAL(copy.get(), 0, countItems(copy.get()));
1872 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1876 // test what the server does when it finds that different
1877 // fields of the same item have been modified
1878 void SyncTests::testMerge() {
1879 // setup client A, B and server so that they all contain the same item
1882 // update in client A
1884 for (it = sources.begin(); it != sources.end(); ++it) {
1885 it->second->update(it->second->createSourceA, it->second->config.mergeItem1);
1888 // update in client B
1889 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
1890 it->second->update(it->second->createSourceA, it->second->config.mergeItem2);
1893 // send change to server from client A (no conflict)
1895 SyncOptions(SYNC_TWO_WAY,
1896 CheckSyncReport(0,0,0, 0,1,0, true, SYNC_TWO_WAY)));
1897 // Now the changes from client B (conflict!).
1898 // There are several possible outcomes:
1899 // - client item completely replaces server item
1900 // - server item completely replaces client item (update on client)
1901 // - server merges and updates client
1902 accessClientB->doSync("conflict",
1903 SyncOptions(SYNC_TWO_WAY,
1904 CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_TWO_WAY)));
1906 // figure out how the conflict during ".conflict" was handled
1907 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
1908 TestingSyncSourcePtr copy;
1909 SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(it->second->createSourceA()));
1911 SOURCE_ASSERT_NO_FAILURE(copy.get(), numItems = countItems(copy.get()));
1912 CPPUNIT_ASSERT(numItems >= 1);
1913 CPPUNIT_ASSERT(numItems <= 2);
1914 std::cout << " \"" << it->second->config.sourceName << ": " << (numItems == 1 ? "conflicting items were merged" : "both of the conflicting items were preserved") << "\" ";
1916 CPPUNIT_ASSERT_NO_THROW(copy.reset());
1919 // now pull the same changes into client A
1921 SyncOptions(SYNC_TWO_WAY,
1922 CheckSyncReport(-1,-1,-1, 0,0,0, true, SYNC_TWO_WAY)));
1924 // client A and B should have identical data now
1927 // Furthermore, it should be identical with the server.
1928 // Be extra careful and pull that data anew and compare once more.
1930 SyncOptions(SYNC_REFRESH_FROM_SERVER,
1931 CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_SERVER)));
1935 // test what the server does when it has to execute a slow sync
1936 // with identical data on client and server:
1937 // expected behaviour is that nothing changes
1938 void SyncTests::testTwinning() {
1939 // clean server and client A
1944 for (it = sources.begin(); it != sources.end(); ++it) {
1945 it->second->testImport();
1949 doSync("send", SyncOptions(SYNC_TWO_WAY));
1951 // ensure that client has the same data, thus ignoring data conversion
1952 // issues (those are covered by testItems())
1955 // copy to client B to have another copy
1956 accessClientB->refreshClient();
1958 // slow sync should not change anything
1959 doSync("twinning", SyncOptions(SYNC_SLOW));
1965 // tests one-way sync from server:
1966 // - get both clients and server in sync with no items anywhere
1967 // - add one item on first client, copy to server
1968 // - add a different item on second client, one-way-from-server
1969 // - two-way sync with first client
1970 // => one item on first client, two on second
1971 // - delete on first client, sync that to second client
1972 // via two-way sync + one-way-from-server
1973 // => one item left on second client (the one inserted locally)
1974 void SyncTests::testOneWayFromServer() {
1975 // no items anywhere
1977 accessClientB->refreshClient();
1979 // check that everything is empty, also resets change tracking
1980 // in second sources of each client
1982 for (it = sources.begin(); it != sources.end(); ++it) {
1983 if (it->second->config.createSourceB) {
1984 TestingSyncSourcePtr source;
1985 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
1986 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
1987 CPPUNIT_ASSERT_NO_THROW(source.reset());
1990 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
1991 if (it->second->config.createSourceB) {
1992 TestingSyncSourcePtr source;
1993 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
1994 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
1995 CPPUNIT_ASSERT_NO_THROW(source.reset());
1999 // add one item on first client, copy to server, and check change tracking via second source
2000 for (it = sources.begin(); it != sources.end(); ++it) {
2001 it->second->insertManyItems(it->second->createSourceA, 200, 1);
2004 SyncOptions(SYNC_TWO_WAY,
2005 CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
2006 for (it = sources.begin(); it != sources.end(); ++it) {
2007 if (it->second->config.createSourceB) {
2008 TestingSyncSourcePtr source;
2009 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2010 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2011 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
2012 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2013 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2014 CPPUNIT_ASSERT_NO_THROW(source.reset());
2018 // add a different item on second client, one-way-from-server
2019 // => one item added locally, none sent to server
2020 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2021 it->second->insertManyItems(it->second->createSourceA, 2, 1);
2023 if (it->second->config.createSourceB) {
2024 TestingSyncSourcePtr source;
2025 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2026 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2027 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
2028 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2029 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2030 CPPUNIT_ASSERT_NO_THROW(source.reset());
2033 accessClientB->doSync("recv",
2034 SyncOptions(SYNC_ONE_WAY_FROM_SERVER,
2035 CheckSyncReport(1,0,0, 0,0,0, true, SYNC_ONE_WAY_FROM_SERVER)));
2036 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2037 if (it->second->config.createSourceB) {
2038 TestingSyncSourcePtr source;
2039 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2040 SOURCE_ASSERT_EQUAL(source.get(), 2, countItems(source.get()));
2041 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
2042 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2043 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2044 CPPUNIT_ASSERT_NO_THROW(source.reset());
2048 // two-way sync with first client for verification
2051 SyncOptions(SYNC_TWO_WAY,
2052 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_TWO_WAY)));
2053 for (it = sources.begin(); it != sources.end(); ++it) {
2054 if (it->second->config.createSourceB) {
2055 TestingSyncSourcePtr source;
2056 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2057 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2058 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2059 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2060 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2061 CPPUNIT_ASSERT_NO_THROW(source.reset());
2065 // delete items on clientA, sync to server
2066 for (it = sources.begin(); it != sources.end(); ++it) {
2067 it->second->deleteAll(it->second->createSourceA);
2069 if (it->second->config.createSourceB) {
2070 TestingSyncSourcePtr source;
2071 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2072 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
2073 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2074 SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
2075 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2076 CPPUNIT_ASSERT_NO_THROW(source.reset());
2080 SyncOptions(SYNC_TWO_WAY,
2081 CheckSyncReport(0,0,0, 0,0,1, true, SYNC_TWO_WAY)));
2082 for (it = sources.begin(); it != sources.end(); ++it) {
2083 if (it->second->config.createSourceB) {
2084 TestingSyncSourcePtr source;
2085 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2086 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
2087 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2088 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2089 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2090 CPPUNIT_ASSERT_NO_THROW(source.reset());
2094 // sync the same change to second client
2095 // => one item left (the one inserted locally)
2096 accessClientB->doSync("delete",
2097 SyncOptions(SYNC_ONE_WAY_FROM_SERVER,
2098 CheckSyncReport(0,0,1, 0,0,0, true, SYNC_ONE_WAY_FROM_SERVER)));
2099 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2100 if (it->second->config.createSourceB) {
2101 TestingSyncSourcePtr source;
2102 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2103 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2104 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2105 SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
2106 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2107 CPPUNIT_ASSERT_NO_THROW(source.reset());
2112 // tests one-way sync from client:
2113 // - get both clients and server in sync with no items anywhere
2114 // - add one item on first client, copy to server
2115 // - add a different item on second client, one-way-from-client
2116 // - two-way sync with first client
2117 // => two items on first client, one on second
2118 // - delete on second client, sync that to first client
2119 // via one-way-from-client, two-way
2120 // => one item left on first client (the one inserted locally)
2121 void SyncTests::testOneWayFromClient() {
2122 // no items anywhere
2124 accessClientB->deleteAll();
2126 // check that everything is empty, also resets change tracking
2127 // in second sources of each client
2129 for (it = sources.begin(); it != sources.end(); ++it) {
2130 if (it->second->config.createSourceB) {
2131 TestingSyncSourcePtr source;
2132 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2133 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
2134 CPPUNIT_ASSERT_NO_THROW(source.reset());
2137 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2138 if (it->second->config.createSourceB) {
2139 TestingSyncSourcePtr source;
2140 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2141 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
2142 CPPUNIT_ASSERT_NO_THROW(source.reset());
2146 // add one item on first client, copy to server, and check change tracking via second source
2147 for (it = sources.begin(); it != sources.end(); ++it) {
2148 it->second->insertManyItems(it->second->createSourceA, 1, 1);
2151 SyncOptions(SYNC_TWO_WAY,
2152 CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
2153 for (it = sources.begin(); it != sources.end(); ++it) {
2154 if (it->second->config.createSourceB) {
2155 TestingSyncSourcePtr source;
2156 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2157 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2158 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
2159 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2160 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2161 CPPUNIT_ASSERT_NO_THROW(source.reset());
2165 // add a different item on second client, one-way-from-client
2166 // => no item added locally, one sent to server
2167 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2168 it->second->insertManyItems(it->second->createSourceA, 2, 1);
2170 if (it->second->config.createSourceB) {
2171 TestingSyncSourcePtr source;
2172 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2173 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2174 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
2175 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2176 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2177 CPPUNIT_ASSERT_NO_THROW(source.reset());
2180 accessClientB->doSync("send",
2181 SyncOptions(SYNC_ONE_WAY_FROM_CLIENT,
2182 CheckSyncReport(0,0,0, 1,0,0, true, SYNC_ONE_WAY_FROM_CLIENT)));
2183 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2184 if (it->second->config.createSourceB) {
2185 TestingSyncSourcePtr source;
2186 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2187 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2188 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2189 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2190 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2191 CPPUNIT_ASSERT_NO_THROW(source.reset());
2195 // two-way sync with client A for verification
2196 // => receive one item
2198 SyncOptions(SYNC_TWO_WAY,
2199 CheckSyncReport(1,0,0, 0,0,0, true, SYNC_TWO_WAY)));
2200 for (it = sources.begin(); it != sources.end(); ++it) {
2201 if (it->second->config.createSourceB) {
2202 TestingSyncSourcePtr source;
2203 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2204 SOURCE_ASSERT_EQUAL(source.get(), 2, countItems(source.get()));
2205 SOURCE_ASSERT_EQUAL(source.get(), 1, countNewItems(source.get()));
2206 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2207 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2208 CPPUNIT_ASSERT_NO_THROW(source.reset());
2212 // delete items on client B, sync to server
2213 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2214 it->second->deleteAll(it->second->createSourceA);
2216 if (it->second->config.createSourceB) {
2217 TestingSyncSourcePtr source;
2218 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2219 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
2220 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2221 SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
2222 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2223 CPPUNIT_ASSERT_NO_THROW(source.reset());
2226 accessClientB->doSync("delete",
2227 SyncOptions(SYNC_ONE_WAY_FROM_CLIENT,
2228 CheckSyncReport(0,0,0, 0,0,1, true, SYNC_ONE_WAY_FROM_CLIENT)));
2229 for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) {
2230 if (it->second->config.createSourceB) {
2231 TestingSyncSourcePtr source;
2232 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2233 SOURCE_ASSERT_EQUAL(source.get(), 0, countItems(source.get()));
2234 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2235 SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
2236 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2237 CPPUNIT_ASSERT_NO_THROW(source.reset());
2241 // sync the same change to client A
2242 // => one item left (the one inserted locally)
2244 SyncOptions(SYNC_TWO_WAY,
2245 CheckSyncReport(0,0,1, 0,0,0, true, SYNC_TWO_WAY)));
2246 for (it = sources.begin(); it != sources.end(); ++it) {
2247 if (it->second->config.createSourceB) {
2248 TestingSyncSourcePtr source;
2249 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceB()));
2250 SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
2251 SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
2252 SOURCE_ASSERT_EQUAL(source.get(), 1, countDeletedItems(source.get()));
2253 SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
2254 CPPUNIT_ASSERT_NO_THROW(source.reset());
2259 // get engine ready, then use it to convert our test items
2260 // to and from the internal field list
2261 void SyncTests::testConversion() {
2262 bool success = false;
2263 SyncOptions::Callback_t callback = boost::bind(&SyncTests::doConversionCallback, this, &success, _1, _2);
2265 doSync(SyncOptions(SYNC_TWO_WAY, CheckSyncReport(-1,-1,-1, -1,-1,-1, false))
2266 .setStartCallback(callback));
2267 CPPUNIT_ASSERT(success);
2270 bool SyncTests::doConversionCallback(bool *success,
2271 SyncContext &syncClient,
2272 SyncOptions &options) {
2275 for (source_it it = sources.begin(); it != sources.end(); ++it) {
2276 const ClientTest::Config *config = &it->second->config;
2277 TestingSyncSource *source = static_cast<TestingSyncSource *>(syncClient.findSource(config->sourceName));
2278 CPPUNIT_ASSERT(source);
2280 std::string type = source->getNativeDatatypeName();
2285 std::list<std::string> items;
2286 std::string testcases;
2287 ClientTest::getItems(config->testcases, items, testcases);
2288 std::string converted = getCurrentTest();
2289 converted += ".converted.";
2290 converted += config->sourceName;
2291 converted += ".dat";
2292 simplifyFilename(converted);
2293 std::ofstream out(converted.c_str());
2294 BOOST_FOREACH(const string &item, items) {
2295 string convertedItem = item;
2296 if(!sysync::DataConversion(syncClient.getSession().get(),
2300 SE_LOG_ERROR(NULL, NULL, "failed parsing as %s:\n%s",
2304 out << convertedItem << "\n";
2308 CPPUNIT_ASSERT(config->compare(client, testcases.c_str(), converted.c_str()));
2311 // abort sync after completing the test successfully (no exception so far!)
2316 // creates several items, transmits them back and forth and
2317 // then compares which of them have been preserved
2318 void SyncTests::testItems() {
2319 // clean server and first test database
2324 for (it = sources.begin(); it != sources.end(); ++it) {
2325 it->second->testImport();
2328 // transfer from client A to server to client B
2329 doSync("send", SyncOptions(SYNC_TWO_WAY).setWBXML(true));
2330 accessClientB->refreshClient(SyncOptions().setWBXML(true));
2335 // creates several items, transmits them back and forth and
2336 // then compares which of them have been preserved
2337 void SyncTests::testItemsXML() {
2338 // clean server and first test database
2343 for (it = sources.begin(); it != sources.end(); ++it) {
2344 it->second->testImport();
2347 // transfer from client A to server to client B using the non-default XML format
2348 doSync("send", SyncOptions(SYNC_TWO_WAY).setWBXML(false));
2349 accessClientB->refreshClient(SyncOptions().setWBXML(false));
2354 // tests the following sequence of events:
2355 // - both clients in sync with server
2356 // - client 1 adds item
2357 // - client 1 updates the same item
2358 // - client 2 gets item: the client should be asked to add the item
2360 // However it has been observed that sometimes the item was sent as "update"
2361 // for a non-existant local item. This is a server bug, the client does not
2362 // have to handle that. See
2363 // http://forge.objectweb.org/tracker/?func=detail&atid=100096&aid=305018&group_id=96
2364 void SyncTests::testAddUpdate() {
2365 // clean server and both test databases
2367 accessClientB->refreshClient();
2371 for (it = sources.begin(); it != sources.end(); ++it) {
2372 it->second->insert(it->second->createSourceA, it->second->config.insertItem, it->second->config.itemType);
2375 SyncOptions(SYNC_TWO_WAY,
2376 CheckSyncReport(0,0,0, 1,0,0, true, SYNC_TWO_WAY)));
2379 for (it = sources.begin(); it != sources.end(); ++it) {
2380 it->second->update(it->second->createSourceB, it->second->config.updateItem);
2383 SyncOptions(SYNC_TWO_WAY,
2384 CheckSyncReport(0,0,0, 0,1,0, true, SYNC_TWO_WAY)));
2386 // now download the updated item into the second client
2387 accessClientB->doSync("recv",
2388 SyncOptions(SYNC_TWO_WAY,
2389 CheckSyncReport(1,0,0, 0,0,0, true, SYNC_TWO_WAY)));
2391 // compare the two databases
2396 // stress tests: execute some of the normal operations,
2397 // but with large number of artificially generated items
2400 // two-way sync with clean client/server,
2401 // followed by slow sync and comparison
2402 // via second client
2403 void SyncTests::testManyItems() {
2404 // clean server and client A
2407 // import artificial data: make them large to generate some
2408 // real traffic and test buffer handling
2411 for (it = sources.begin(); it != sources.end(); ++it) {
2412 if (num_items == -1) {
2413 num_items = it->second->config.numItems;
2415 CPPUNIT_ASSERT_EQUAL(num_items, it->second->config.numItems);
2417 it->second->insertManyItems(it->second->createSourceA, 0, num_items, 2000);
2420 // send data to server
2422 SyncOptions(SYNC_TWO_WAY,
2423 CheckSyncReport(0,0,0, num_items,0,0, true, SYNC_TWO_WAY),
2424 SyncOptions::DEFAULT_MAX_MSG_SIZE,
2425 SyncOptions::DEFAULT_MAX_OBJ_SIZE,
2428 // ensure that client has the same data, ignoring data conversion
2429 // issues (those are covered by testItems())
2432 // also copy to second client
2433 accessClientB->refreshClient();
2435 // slow sync now should not change anything
2437 SyncOptions(SYNC_SLOW,
2438 CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_SLOW),
2439 SyncOptions::DEFAULT_MAX_MSG_SIZE,
2440 SyncOptions::DEFAULT_MAX_OBJ_SIZE,
2448 * Tell server to delete plenty of items.
2450 void SyncTests::testManyDeletes() {
2451 // clean server and client A
2454 // import artificial data: make them small, we just want
2458 for (it = sources.begin(); it != sources.end(); ++it) {
2459 if (num_items == -1) {
2460 num_items = it->second->config.numItems;
2462 CPPUNIT_ASSERT_EQUAL(num_items, it->second->config.numItems);
2464 it->second->insertManyItems(it->second->createSourceA, 0, num_items, 100);
2467 // send data to server
2469 SyncOptions(SYNC_TWO_WAY,
2470 CheckSyncReport(0,0,0, num_items,0,0, true, SYNC_TWO_WAY),
2471 64 * 1024, 64 * 1024, true));
2473 // ensure that client has the same data, ignoring data conversion
2474 // issues (those are covered by testItems())
2477 // also copy to second client
2478 accessClientB->refreshClient();
2480 // slow sync now should not change anything
2482 SyncOptions(SYNC_SLOW,
2483 CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_SLOW),
2484 64 * 1024, 64 * 1024, true));
2489 // delete everything locally
2490 BOOST_FOREACH(source_array_t::value_type &source_pair, sources) {
2491 source_pair.second->deleteAll(source_pair.second->createSourceA);
2493 doSync("delete-server",
2494 SyncOptions(SYNC_TWO_WAY,
2495 CheckSyncReport(0,0,0, 0,0,num_items, true, SYNC_TWO_WAY),
2498 // update second client
2499 accessClientB->doSync("delete-client",
2500 SyncOptions(SYNC_REFRESH_FROM_SERVER,
2501 CheckSyncReport(0,0,num_items, 0,0,0, true, SYNC_REFRESH_FROM_SERVER),
2506 * - get client A, server, client B in sync with one item
2507 * - force slow sync in A: must not duplicate items, but may update it locally
2508 * - refresh client B (in case that the item was updated)
2509 * - delete item in B and server via two-way sync
2510 * - refresh-from-server in B to check that item is gone
2511 * - two-way in A: must delete the item
2513 void SyncTests::testSlowSyncSemantic()
2515 // set up one item everywhere
2520 SyncOptions(SYNC_SLOW,
2521 CheckSyncReport(0,-1,0, -1,-1,0, true, SYNC_SLOW)));
2523 // refresh B, delete item
2524 accessClientB->doSync("refresh",
2525 SyncOptions(SYNC_TWO_WAY,
2526 CheckSyncReport(0,-1,0, 0,0,0, true, SYNC_TWO_WAY)));
2527 BOOST_FOREACH(source_array_t::value_type &source_pair, accessClientB->sources) {
2528 source_pair.second->deleteAll(source_pair.second->createSourceA);
2530 accessClientB->doSync("delete",
2531 SyncOptions(SYNC_TWO_WAY,
2532 CheckSyncReport(0,0,0, 0,0,1, true, SYNC_TWO_WAY)));
2533 accessClientB->doSync("check",
2534 SyncOptions(SYNC_REFRESH_FROM_SERVER,
2535 CheckSyncReport(0,0,0, 0,0,0, true, SYNC_REFRESH_FROM_SERVER)));
2537 // now the item should also be deleted on A
2539 SyncOptions(SYNC_TWO_WAY,
2540 CheckSyncReport(0,0,1, 0,0,0, true, SYNC_TWO_WAY)));
2544 * check that refresh-from-server works correctly:
2545 * - create the same item on A, server, B via testCopy()
2546 * - refresh B (one item deleted, one created)
2547 * - delete item on A and server
2548 * - refresh B (one item deleted)
2550 void SyncTests::testComplexRefreshFromServerSemantic()
2554 // check refresh with one item on server
2555 accessClientB->doSync("refresh-one",
2556 SyncOptions(SYNC_REFRESH_FROM_SERVER,
2557 CheckSyncReport(1,0,1, 0,0,0, true, SYNC_REFRESH_FROM_SERVER)));
2559 // delete that item via A, check again
2560 BOOST_FOREACH(source_array_t::value_type &source_pair, sources) {
2561 source_pair.second->deleteAll(source_pair.second->createSourceA);
2563 doSync("delete-item",
2564 SyncOptions(SYNC_TWO_WAY,
2565 CheckSyncReport(0,0,0, 0,0,1, true, SYNC_TWO_WAY)));
2566 accessClientB->doSync("refresh-none",
2567 SyncOptions(SYNC_REFRESH_FROM_SERVER,
2568 CheckSyncReport(0,0,1, 0,0,0, true, SYNC_REFRESH_FROM_SERVER)));
2572 * implements testMaxMsg(), testLargeObject(), testLargeObjectEncoded()
2573 * using a sequence of items with varying sizes
2575 void SyncTests::doVarSizes(bool withMaxMsgSize,
2576 bool withLargeObject) {
2577 int maxMsgSize = 8 * 1024;
2578 const char* maxItemSize = getenv("CLIENT_TEST_MAX_ITEMSIZE");
2579 int tmpSize = maxItemSize ? atoi(maxItemSize) : 0;
2581 maxMsgSize = tmpSize;
2583 // clean server and client A
2586 // insert items, doubling their size, then restart with small size
2588 for (it = sources.begin(); it != sources.end(); ++it) {
2590 for (int i = 0; i < 2; i++ ) {
2592 while (size < 2 * maxMsgSize) {
2593 it->second->insertManyItems(it->second->createSourceA, item, 1, (int)strlen(it->second->config.templateItem) + 10 + size);
2600 // transfer to server
2602 SyncOptions(SYNC_TWO_WAY,
2603 CheckSyncReport(0,0,0, -1,0,0, true, SYNC_TWO_WAY), // number of items sent to server depends on source
2604 withMaxMsgSize ? SyncOptions::DEFAULT_MAX_MSG_SIZE: 0,
2605 withMaxMsgSize ? SyncOptions::DEFAULT_MAX_OBJ_SIZE : 0,
2608 // copy to second client
2609 const char *value = getenv ("CLIENT_TEST_NOREFRESH");
2610 // If refresh_from_server or refresh_from_client (depending on this is a
2611 // server or client) is not supported, we can still test via slow sync.
2613 accessClientB->refreshClient();
2615 accessClientB->doSync("recv",
2616 SyncOptions(SYNC_REFRESH_FROM_SERVER,
2617 CheckSyncReport(-1,0,-1, 0,0,0, true, SYNC_REFRESH_FROM_SERVER), // number of items received from server depends on source
2618 withLargeObject ? maxMsgSize : withMaxMsgSize ? maxMsgSize * 100 /* large enough so that server can sent the largest item */ : 0,
2619 withMaxMsgSize ? maxMsgSize * 100 : 0,
2626 class TransportResendInjector : public TransportWrapper{
2630 TransportResendInjector()
2631 :TransportWrapper() {
2632 const char *s = getenv("CLIENT_TEST_RESEND_TIMEOUT");
2633 timeout = s ? atoi(s) : 0;
2636 ~TransportResendInjector() {
2639 virtual void send(const char *data, size_t len)
2642 if (m_interruptAtMessage >= 0 &&
2643 m_messageCount == m_interruptAtMessage+1) {
2644 m_wrappedAgent->send(data, len);
2645 m_status = m_wrappedAgent->wait();
2646 //trigger client side resend
2648 m_status = TIME_OUT;
2652 m_wrappedAgent->send(data, len);
2653 m_status = m_wrappedAgent->wait();
2657 virtual void getReply(const char *&data, size_t &len, std::string &contentType) {
2658 if (m_status == FAILED) {
2662 m_wrappedAgent->getReply(data, len, contentType);
2667 class TransportFaultInjector : public TransportWrapper{
2669 TransportFaultInjector()
2670 :TransportWrapper() {
2673 ~TransportFaultInjector() {
2676 virtual void send(const char *data, size_t len)
2678 if (m_interruptAtMessage == m_messageCount) {
2679 SE_LOG_DEBUG(NULL, NULL, "TransportFaultInjector: interrupt before sending message #%d", m_messageCount);
2682 if (m_interruptAtMessage >= 0 &&
2683 m_messageCount > m_interruptAtMessage) {
2684 throw string("TransportFaultInjector: interrupt before send");
2687 m_wrappedAgent->send(data, len);
2689 m_status = m_wrappedAgent->wait();
2691 if (m_interruptAtMessage == m_messageCount) {
2692 SE_LOG_DEBUG(NULL, NULL, "TransportFaultInjector: interrupt after receiving reply #%d", m_messageCount);
2695 if (m_interruptAtMessage >= 0 &&
2696 m_messageCount > m_interruptAtMessage) {
2701 virtual void getReply(const char *&data, size_t &len, std::string &contentType) {
2702 if (m_status == FAILED) {
2706 m_wrappedAgent->getReply(data, len, contentType);
2712 * Emulates a user suspend just after receving response
2715 class UserSuspendInjector : public TransportWrapper{
2717 UserSuspendInjector()
2718 :TransportWrapper() {
2721 ~UserSuspendInjector() {
2724 virtual void send(const char *data, size_t len)
2726 m_wrappedAgent->send(data, len);
2727 m_status = m_wrappedAgent->wait();
2730 virtual void getReply(const char *&data, size_t &len, std::string &contentType) {
2731 if (m_status == FAILED) {
2735 if (m_interruptAtMessage == m_messageCount) {
2736 SE_LOG_DEBUG(NULL, NULL, "UserSuspendInjector: user suspend after getting reply #%d", m_messageCount);
2739 if (m_interruptAtMessage >= 0 &&
2740 m_messageCount > m_interruptAtMessage) {
2741 m_options->m_isSuspended = true;
2743 m_wrappedAgent->getReply(data, len, contentType);
2749 * This function covers different error scenarios that can occur
2750 * during real synchronization. To pass, clients must either force a
2751 * slow synchronization after a failed synchronization or implement
2752 * the error handling described in the design guide (track server's
2753 * status for added/updated/deleted items and resend unacknowledged
2756 * The items used during these tests are synthetic. They are
2757 * constructed so that normally a server should be able to handle
2758 * twinning during a slow sync correctly.
2760 * Errors are injected into a synchronization by wrapping the normal
2761 * HTTP transport agent. The wrapper enumerates messages sent between
2762 * client and server (i.e., one message exchange increments the
2763 * counter by two), starting from zero. It "cuts" the connection before
2764 * sending out the next message to the server respectively after the
2765 * server has replied, but before returning the reply to the client.
2766 * The first case simulates a lost message from the client to the server
2767 * and the second case a lost message from the server to the client.
2769 * The expected result is the same as in an uninterrupted sync, which
2770 * is done once at the beginning.
2772 * Each test goes through the following steps:
2773 * - client A and B reset local data store
2774 * - client A creates 3 new items, remembers LUIDs
2775 * - refresh-from-client A sync
2776 * - refresh-from-client B sync
2777 * - client B creates 3 different items, remembers LUIDs
2779 * - client A syncs => A, B, server are in sync
2780 * - client A modifies his items (depends on test) and
2781 * sends changes to server => server has changes for B
2782 * - client B modifies his items (depends on test)
2783 * - client B syncs, transport wrapper simulates lost message n
2784 * - client B syncs again, resuming synchronization if possible or
2785 * slow sync otherwise (responsibility of the client!)
2786 * - client A syncs (not tested yet: A should be sent exactly the changes made by B)
2787 * - test that A and B contain same items
2788 * - test that A contains the same items as the uninterrupted reference run
2789 * - repeat the steps above ranging starting with lost message 0 until no
2792 * Set the CLIENT_TEST_INTERRUPT_AT env variable to a message number
2793 * >= 0 to execute one uninterrupted run and then interrupt at that
2796 void SyncTests::doInterruptResume(int changes,
2797 boost::shared_ptr<TransportWrapper> wrapper)
2799 int interruptAtMessage = -1;
2800 const char *t = getenv("CLIENT_TEST_INTERRUPT_AT");
2801 int requestedInterruptAt = t ? atoi(t) : -1;
2802 const char *s = getenv("CLIENT_TEST_INTERRUPT_SLEEP");
2803 int sleep_t = s ? atoi(s) : 0;
2805 std::string refFileBase = getCurrentTest() + ".ref.";
2810 sprintf(buffer, "%d", interruptAtMessage);
2811 const char *prefix = interruptAtMessage == -1 ? "complete" : buffer;
2812 SyncPrefix prefixA(prefix, *this);
2813 SyncPrefix prefixB(prefix, *accessClientB);
2815 std::vector< std::list<std::string> > clientAluids;
2816 std::vector< std::list<std::string> > clientBluids;
2818 // create new items in client A and sync to server
2819 clientAluids.resize(sources.size());
2820 for (i = 0; i < sources.size(); i++) {
2821 sources[i].second->deleteAll(sources[i].second->createSourceA);
2823 sources[i].second->insertManyItems(sources[i].second->createSourceA,
2826 doSync("fromA", SyncOptions(SYNC_REFRESH_FROM_CLIENT));
2828 // init client B and add its items to server and client A
2829 accessClientB->doSync("initB", SyncOptions(SYNC_REFRESH_FROM_SERVER));
2830 clientBluids.resize(sources.size());
2831 for (i = 0; i < sources.size(); i++) {
2833 accessClientB->sources[i].second->insertManyItems(accessClientB->sources[i].second->createSourceA,
2836 accessClientB->doSync("fromB", SyncOptions(SYNC_TWO_WAY));
2837 doSync("updateA", SyncOptions(SYNC_TWO_WAY));
2839 // => client A, B and server in sync with a total of six items
2841 // make changes as requested on client A and sync to server
2842 for (i = 0; i < sources.size(); i++) {
2843 if (changes & SERVER_ADD) {
2844 sources[i].second->insertManyItems(sources[i].second->createSourceA,
2847 if (changes & SERVER_REMOVE) {
2848 // remove second item
2849 removeItem(sources[i].second->createSourceA,
2850 *(++clientAluids[i].begin()));
2852 if (changes & SERVER_UPDATE) {
2853 // update third item
2854 updateItem(sources[i].second->createSourceA,
2855 *(++ ++clientAluids[i].begin()),
2856 sources[i].second->createItem(3, "updated", 0).c_str());
2860 if (changes & (SERVER_ADD|SERVER_REMOVE|SERVER_UPDATE)) {
2861 doSync("changesFromA", SyncOptions(SYNC_TWO_WAY));
2864 // make changes as requested on client B
2865 for (i = 0; i < sources.size(); i++) {
2866 if (changes & CLIENT_ADD) {
2867 accessClientB->sources[i].second->insertManyItems(accessClientB->sources[i].second->createSourceA,
2870 if (changes & CLIENT_REMOVE) {
2871 // remove second item
2872 removeItem(accessClientB->sources[i].second->createSourceA,
2873 *(++clientBluids[i].begin()));
2875 if (changes & CLIENT_UPDATE) {
2876 // update third item
2877 updateItem(accessClientB->sources[i].second->createSourceA,
2878 *(++ ++clientBluids[i].begin()),
2879 accessClientB->sources[i].second->createItem(13, "updated", 0).c_str());
2883 // Now do an interrupted sync between B and server.
2884 // The explicit delete of the TransportAgent is suppressed
2885 // by overloading the delete operator.
2888 wrapper->setInterruptAtMessage(interruptAtMessage);
2889 accessClientB->doSync("changesFromB",
2890 SyncOptions(SYNC_TWO_WAY,
2891 CheckSyncReport(-1, -1, -1, -1,
2892 -1, -1, false)).setTransportAgent(wrapper));
2893 wasInterrupted = interruptAtMessage != -1 &&
2894 wrapper->getMessageCount() <= interruptAtMessage;
2898 if (interruptAtMessage != -1) {
2899 if (wasInterrupted) {
2900 // uninterrupted sync, done
2904 // continue, wait until server timeout
2908 // no need for resend tests
2909 if (!dynamic_cast <TransportResendInjector *> (wrapper.get())) {
2910 accessClientB->doSync("retryB", SyncOptions(SYNC_TWO_WAY));
2914 // copy changes to client A
2915 doSync("toA", SyncOptions(SYNC_TWO_WAY));
2917 // compare client A and B
2918 if (interruptAtMessage != -1 &&
2919 !compareDatabases(refFileBase.c_str(), false)) {
2921 std::cout << "====> comparison of client B against reference file(s) failed after interrupting at message #" <<
2922 interruptAtMessage << std::endl;
2925 if (!compareDatabases(NULL, false)) {
2927 std::cout << "====> comparison of client A and B failed after interrupting at message #" <<
2928 interruptAtMessage << std::endl;
2932 // save reference files from uninterrupted run?
2933 if (interruptAtMessage == -1) {
2934 for (source_it it = sources.begin();
2935 it != sources.end();
2937 std::string refFile = refFileBase;
2938 refFile += it->second->config.sourceName;
2940 simplifyFilename(refFile);
2941 TestingSyncSourcePtr source;
2942 SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(it->second->createSourceA()));
2943 SOURCE_ASSERT_EQUAL(source.get(), 0, it->second->config.dump(client, *source.get(), refFile.c_str()));
2944 CPPUNIT_ASSERT_NO_THROW(source.reset());
2948 // pick next iterration
2949 if (requestedInterruptAt != -1) {
2950 // only do one interrupted run of the test
2951 if (requestedInterruptAt == interruptAtMessage) {
2954 interruptAtMessage = requestedInterruptAt;
2957 // interrupt one message later than before
2958 interruptAtMessage++;
2962 CPPUNIT_ASSERT(equal);
2965 void SyncTests::testInterruptResumeClientAdd()
2967 doInterruptResume(CLIENT_ADD, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
2970 void SyncTests::testInterruptResumeClientRemove()
2972 doInterruptResume(CLIENT_REMOVE, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
2975 void SyncTests::testInterruptResumeClientUpdate()
2977 doInterruptResume(CLIENT_UPDATE, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
2980 void SyncTests::testInterruptResumeServerAdd()
2982 doInterruptResume(SERVER_ADD, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
2985 void SyncTests::testInterruptResumeServerRemove()
2987 doInterruptResume(SERVER_REMOVE, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
2990 void SyncTests::testInterruptResumeServerUpdate()
2992 doInterruptResume(SERVER_UPDATE, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
2995 void SyncTests::testInterruptResumeFull()
2997 doInterruptResume(CLIENT_ADD|CLIENT_REMOVE|CLIENT_UPDATE|
2998 SERVER_ADD|SERVER_REMOVE|SERVER_UPDATE, boost::shared_ptr<TransportWrapper> (new TransportFaultInjector()));
3001 void SyncTests::testUserSuspendClientAdd()
3003 doInterruptResume(CLIENT_ADD, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3006 void SyncTests::testUserSuspendClientRemove()
3008 doInterruptResume(CLIENT_REMOVE, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3011 void SyncTests::testUserSuspendClientUpdate()
3013 doInterruptResume(CLIENT_UPDATE, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3016 void SyncTests::testUserSuspendServerAdd()
3018 doInterruptResume(SERVER_ADD, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3021 void SyncTests::testUserSuspendServerRemove()
3023 doInterruptResume(SERVER_REMOVE, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3026 void SyncTests::testUserSuspendServerUpdate()
3028 doInterruptResume(SERVER_UPDATE, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3031 void SyncTests::testUserSuspendFull()
3033 doInterruptResume(CLIENT_ADD|CLIENT_REMOVE|CLIENT_UPDATE|
3034 SERVER_ADD|SERVER_REMOVE|SERVER_UPDATE, boost::shared_ptr<TransportWrapper> (new UserSuspendInjector()));
3037 void SyncTests::testResendClientAdd()
3039 doInterruptResume(CLIENT_ADD, boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3042 void SyncTests::testResendClientRemove()
3044 doInterruptResume(CLIENT_REMOVE, boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3047 void SyncTests::testResendClientUpdate()
3049 doInterruptResume(CLIENT_UPDATE, boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3052 void SyncTests::testResendServerAdd()
3054 doInterruptResume(SERVER_ADD, boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3057 void SyncTests::testResendServerRemove()
3059 doInterruptResume(SERVER_REMOVE, boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3062 void SyncTests::testResendServerUpdate()
3064 doInterruptResume(SERVER_UPDATE, boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3067 void SyncTests::testResendFull()
3069 doInterruptResume(CLIENT_ADD|CLIENT_REMOVE|CLIENT_UPDATE|
3070 SERVER_ADD|SERVER_REMOVE|SERVER_UPDATE,
3071 boost::shared_ptr<TransportWrapper> (new TransportResendInjector()));
3074 void SyncTests::doSync(const SyncOptions &options)
3077 static int syncCounter = 0;
3078 static std::string lastTest;
3079 std::stringstream logstream;
3081 // reset counter when switching tests
3082 if (lastTest != getCurrentTest()) {
3084 lastTest = getCurrentTest();
3089 for (std::list<std::string>::iterator it = logPrefixes.begin();
3090 it != logPrefixes.end();
3095 if (!prefix.empty()) {
3096 printf(" %s", prefix.c_str() + 1);
3100 logstream /* << std::setw(4) << std::setfill('0') << syncCounter << "_" */ << getCurrentTest()
3102 << ".client." << (accessClientB ? "A" : "B");
3103 std::string logname = logstream.str();
3104 simplifyFilename(logname);
3107 SE_LOG_DEBUG(NULL, NULL, "%d. starting %s with sync mode %s",
3108 syncCounter, logname.c_str(), PrettyPrintSyncMode(options.m_syncMode).c_str());
3111 res = client.doSync(sourceArray,
3115 client.postSync(res, logname);
3116 } catch (CppUnit::Exception &ex) {
3118 client.postSync(res, logname);
3120 // report the original exception without altering the source line
3124 client.postSync(res, logname);
3126 // this logs the original exception using CPPUnit mechanisms,
3127 // with current line as source
3128 CPPUNIT_ASSERT_NO_THROW(throw);
3133 /** generates tests on demand based on what the client supports */
3134 class ClientTestFactory : public CppUnit::TestFactory {
3136 ClientTestFactory(ClientTest &c) :
3139 virtual CppUnit::Test *makeTest() {
3141 CppUnit::TestSuite *alltests = new CppUnit::TestSuite("Client");
3142 CppUnit::TestSuite *tests;
3144 // create local source tests
3145 tests = new CppUnit::TestSuite(alltests->getName() + "::Source");
3146 for (source=0; source < client.getNumLocalSources(); source++) {
3147 ClientTest::Config config;
3148 client.getLocalSourceConfig(source, config);
3149 if (config.sourceName) {
3150 LocalTests *sourcetests =
3151 client.createLocalTests(tests->getName() + "::" + config.sourceName, source, config);
3152 sourcetests->addTests();
3153 tests->addTest(FilterTest(sourcetests));
3156 alltests->addTest(FilterTest(tests));
3159 // create sync tests with just one source
3160 tests = new CppUnit::TestSuite(alltests->getName() + "::Sync");
3161 for (source=0; source < client.getNumSyncSources(); source++) {
3162 ClientTest::Config config;
3163 client.getSyncSourceConfig(source, config);
3164 if (config.sourceName) {
3165 std::vector<int> sources;
3166 sources.push_back(source);
3167 SyncTests *synctests =
3168 client.createSyncTests(tests->getName() + "::" + config.sourceName, sources);
3169 synctests->addTests();
3170 tests->addTest(FilterTest(synctests));
3174 // create sync tests with all sources enabled, unless we only have one:
3175 // that would be identical to the test above
3176 std::vector<int> sources;
3177 std::string name, name_reversed;
3178 for (source=0; source < client.getNumSyncSources(); source++) {
3179 ClientTest::Config config;
3180 client.getSyncSourceConfig(source, config);
3181 if (config.sourceName) {
3182 sources.push_back(source);
3183 if (name.size() > 0) {
3185 name_reversed = std::string("_") + name_reversed;
3187 name += config.sourceName;
3188 name_reversed = config.sourceName + name_reversed;
3191 if (sources.size() > 1) {
3192 SyncTests *synctests =
3193 client.createSyncTests(tests->getName() + "::" + name, sources);
3194 synctests->addTests();
3195 tests->addTest(FilterTest(synctests));
3198 // now also in reversed order - who knows, it might make a difference
3199 std::reverse(sources.begin(), sources.end());
3201 client.createSyncTests(tests->getName() + "::" + name_reversed, sources);
3202 synctests->addTests();
3203 tests->addTest(FilterTest(synctests));
3207 alltests->addTest(FilterTest(tests));
3217 void ClientTest::registerTests()
3219 factory = (void *)new ClientTestFactory(*this);
3220 CppUnit::TestFactoryRegistry::getRegistry().registerFactory((CppUnit::TestFactory *)factory);
3223 ClientTest::ClientTest(int serverSleepSec, const std::string &serverLog) :
3224 serverSleepSeconds(serverSleepSec),
3225 serverLogFileName(serverLog),
3230 ClientTest::~ClientTest()
3233 CppUnit::TestFactoryRegistry::getRegistry().unregisterFactory((CppUnit::TestFactory *)factory);
3234 delete (CppUnit::TestFactory *)factory;
3239 LocalTests *ClientTest::createLocalTests(const std::string &name, int sourceParam, ClientTest::Config &co)
3241 return new LocalTests(name, *this, sourceParam, co);
3244 SyncTests *ClientTest::createSyncTests(const std::string &name, std::vector<int> sourceIndices, bool isClientA)
3246 return new SyncTests(name, *this, sourceIndices, isClientA);
3249 int ClientTest::dump(ClientTest &client, TestingSyncSource &source, const char *file)
3251 BackupReport report;
3252 boost::shared_ptr<ConfigNode> node(new VolatileConfigNode);
3256 CPPUNIT_ASSERT(source.getOperations().m_backupData);
3257 source.getOperations().m_backupData(SyncSource::Operations::ConstBackupInfo(),
3258 SyncSource::Operations::BackupInfo(SyncSource::Operations::BackupInfo::BACKUP_OTHER, file, node),
3263 void ClientTest::getItems(const char *file, list<string> &items, std::string &testcases)
3268 std::ifstream input;
3269 string server = getenv("CLIENT_TEST_SERVER");
3270 testcases = string(file) + '.' + server +".tem";
3271 input.open(testcases.c_str());
3275 input.open(testcases.c_str());
3277 CPPUNIT_ASSERT(!input.bad());
3278 CPPUNIT_ASSERT(input.is_open());
3279 std::string data, line;
3281 bool wasend = false;
3283 getline(input, line);
3284 CPPUNIT_ASSERT(!input.bad());
3285 // empty lines directly after line which starts with END mark end of record;
3286 // check for END necessary becayse vCard 2.1 ENCODING=BASE64 may have empty lines in body of VCARD!
3287 if ((line != "\r" && line.size() > 0) || !wasend) {
3291 if (!data.empty()) {
3292 items.push_back(data);
3296 wasend = !line.compare(0, 4, "END:");
3297 } while(!input.eof());
3299 if (!data.empty()) {
3300 items.push_back(data);
3304 int ClientTest::import(ClientTest &client, TestingSyncSource &source, const char *file, std::string &realfile)
3307 getItems(file, items, realfile);
3308 BOOST_FOREACH(string &data, items) {
3309 importItem(&source, data);
3314 bool ClientTest::compare(ClientTest &client, const char *fileA, const char *fileB)
3316 std::string cmdstr = std::string("env PATH=.:$PATH synccompare ") + fileA + " " + fileB;
3317 setenv("CLIENT_TEST_HEADER", "\n\n", 1);
3318 setenv("CLIENT_TEST_LEFT_NAME", fileA, 1);
3319 setenv("CLIENT_TEST_RIGHT_NAME", fileB, 1);
3320 setenv("CLIENT_TEST_REMOVED", "only in left file", 1);
3321 setenv("CLIENT_TEST_ADDED", "only in right file", 1);
3322 const char* compareLog = getenv("CLIENT_TEST_COMPARE_LOG");
3323 if(compareLog && strlen(compareLog))
3325 string tmpfile = "____compare.log";
3326 cmdstr =string("bash -c 'set -o pipefail;") + cmdstr;
3327 cmdstr += " 2>&1|tee " +tmpfile+"'";
3329 bool success = system(cmdstr.c_str()) == 0;
3331 printf("failed: env CLIENT_TEST_SERVER=%s PATH=.:$PATH synccompare %s %s\n",
3332 getenv("CLIENT_TEST_SERVER") ? getenv("CLIENT_TEST_SERVER") : "",
3338 void ClientTest::postSync(int res, const std::string &logname)
3341 Sleep(serverSleepSeconds * 1000);
3343 sleep(serverSleepSeconds);
3345 // make a copy of the server's log (if found), then truncate it
3346 if (serverLogFileName.size()) {
3347 int fd = open(serverLogFileName.c_str(), O_RDWR);
3350 std::string cmd = std::string("cp ") + serverLogFileName + " " + logname + ".server.log";
3351 if (system(cmd.c_str())) {
3352 fprintf(stderr, "copying log file failed: %s\n", cmd.c_str());
3354 if (ftruncate(fd, 0)) {
3355 perror("truncating log file");
3358 perror(serverLogFileName.c_str());
3364 void ClientTest::getTestData(const char *type, Config &config)
3366 memset(&config, 0, sizeof(config));
3367 char *numitems = getenv("CLIENT_TEST_NUM_ITEMS");
3368 config.numItems = numitems ? atoi(numitems) : 100;
3369 char *env = getenv("CLIENT_TEST_RETRY");
3370 config.retrySync = (env && !strcmp (env, "t")) ?true :false;
3371 env = getenv("CLIENT_TEST_RESEND");
3372 config.resendSync = (env && !strcmp (env, "t")) ?true :false;
3373 env = getenv("CLIENT_TEST_SUSPEND");
3374 config.suspendSync = (env && !strcmp (env, "t")) ?true :false;
3375 config.sourceKnowsItemSemantic = true;
3376 config.itemType = "";
3377 config.import = import;
3379 config.compare = compare;
3381 // redirect requests for "ical20" towards "ical20_noutc"?
3383 env = getenv ("CLIENT_TEST_NOUTC");
3384 if (env && !strcmp (env, "t")) {
3388 if (!strcmp(type, "vcard30")) {
3389 config.sourceName = "vcard30";
3390 config.sourceNameServerTemplate = "addressbook";
3391 config.uri = "card3"; // ScheduleWorld
3392 config.type = "text/vcard";
3399 "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
3400 "X-EVOLUTION-FILE-AS:Doe\\, John\n"
3401 "X-MOZILLA-HTML:FALSE\n"
3402 "NOTE:<<REVISION>>\n"
3410 "X-EVOLUTION-FILE-AS:Doe\\, Joan\n"
3411 "TEL;TYPE=WORK;TYPE=VOICE:business 2\n"
3413 "X-MOZILLA-HTML:TRUE\n"
3415 /* adds a second phone number: */
3416 config.complexUpdateItem =
3422 "X-EVOLUTION-FILE-AS:Doe\\, Joan\n"
3423 "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
3424 "TEL;TYPE=HOME;TYPE=VOICE:home 2\n"
3426 "X-MOZILLA-HTML:TRUE\n"
3428 /* add a telephone number, email and X-AIM to initial item */
3435 "X-EVOLUTION-FILE-AS:Doe\\, John\n"
3436 "X-MOZILLA-HTML:FALSE\n"
3437 "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
3438 "EMAIL:john.doe@work.com\n"
3447 "TEL;TYPE=WORK;TYPE=VOICE:123456\n"
3448 "X-EVOLUTION-FILE-AS:Doe\\, John\n"
3449 "X-MOZILLA-HTML:TRUE\n"
3452 config.templateItem = config.insertItem;
3453 config.uniqueProperties = "FN:N:X-EVOLUTION-FILE-AS";
3454 config.sizeProperty = "NOTE";
3455 config.testcases = "testcases/vcard30.vcf";
3456 } else if (!strcmp(type, "vcard21")) {
3457 config.sourceName = "vcard21";
3458 config.sourceNameServerTemplate = "addressbook";
3459 config.uri = "card"; // Funambol
3460 config.type = "text/x-vcard";
3467 "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
3468 "X-MOZILLA-HTML:FALSE\n"
3469 "NOTE:<<REVISION>>\n"
3477 "TEL;TYPE=WORK;TYPE=VOICE:business 2\n"
3479 "X-MOZILLA-HTML:TRUE\n"
3481 /* adds a second phone number: */
3482 config.complexUpdateItem =
3488 "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
3489 "TEL;TYPE=HOME;TYPE=VOICE:home 2\n"
3491 "X-MOZILLA-HTML:TRUE\n"
3493 /* add email and X-AIM to initial item */
3500 "X-MOZILLA-HTML:FALSE\n"
3501 "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
3502 "EMAIL:john.doe@work.com\n"
3505 /* change X-MOZILLA-HTML */
3512 "X-MOZILLA-HTML:TRUE\n"
3515 config.templateItem = config.insertItem;
3516 config.uniqueProperties = "FN:N";
3517 config.sizeProperty = "NOTE";
3518 config.testcases = "testcases/vcard21.vcf";
3519 } else if (!strcmp(type, "ical20") && !noutc) {
3520 config.sourceName = "ical20";
3521 config.sourceNameServerTemplate = "calendar";
3522 config.uri = "cal2"; // ScheduleWorld
3523 config.type = "text/x-vcalendar";
3526 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3530 "SUMMARY:phone meeting\n"
3531 "DTEND:20060406T163000Z\n"
3532 "DTSTART:20060406T160000Z\n"
3533 "UID:1234567890!@#$%^&*()<>@dummy\n"
3534 "DTSTAMP:20060406T211449Z\n"
3535 "LAST-MODIFIED:20060409T213201\n"
3536 "CREATED:20060409T213201\n"
3537 "LOCATION:my office\n"
3538 "DESCRIPTION:let's talk<<REVISION>>\n"
3546 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3550 "SUMMARY:meeting on site\n"
3551 "DTEND:20060406T163000Z\n"
3552 "DTSTART:20060406T160000Z\n"
3553 "UID:1234567890!@#$%^&*()<>@dummy\n"
3554 "DTSTAMP:20060406T211449Z\n"
3555 "LAST-MODIFIED:20060409T213201\n"
3556 "CREATED:20060409T213201\n"
3557 "LOCATION:big meeting room\n"
3558 "DESCRIPTION:nice to see you\n"
3564 /* change location and description of insertItem in testMerge(), add alarm */
3567 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3571 "SUMMARY:phone meeting\n"
3572 "DTEND:20060406T163000Z\n"
3573 "DTSTART:20060406T160000Z\n"
3574 "UID:1234567890!@#$%^&*()<>@dummy\n"
3575 "DTSTAMP:20060406T211449Z\n"
3576 "LAST-MODIFIED:20060409T213201\n"
3577 "CREATED:20060409T213201\n"
3578 "LOCATION:calling from home\n"
3579 "DESCRIPTION:let's talk\n"
3584 "DESCRIPTION:alarm\n"
3586 "TRIGGER;VALUE=DURATION;RELATED=START:-PT15M\n"
3590 /* change location to something else, add category */
3593 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3597 "SUMMARY:phone meeting\n"
3598 "DTEND:20060406T163000Z\n"
3599 "DTSTART:20060406T160000Z\n"
3600 "UID:1234567890!@#$%^&*()<>@dummy\n"
3601 "DTSTAMP:20060406T211449Z\n"
3602 "LAST-MODIFIED:20060409T213201\n"
3603 "CREATED:20060409T213201\n"
3604 "LOCATION:my office\n"
3606 "DESCRIPTION:what the heck\\, let's even shout a bit\n"
3614 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3618 "UID:20080407T193125Z-19554-727-1-50@gollum\n"
3619 "DTSTAMP:20080407T193125Z\n"
3620 "DTSTART:20080406T090000Z\n"
3621 "DTEND:20080406T093000Z\n"
3624 "SUMMARY:Recurring\n"
3625 "DESCRIPTION:recurs each Monday\\, 10 times\n"
3627 "RRULE:FREQ=WEEKLY;COUNT=10;INTERVAL=1;BYDAY=SU\n"
3628 "CREATED:20080407T193241\n"
3629 "LAST-MODIFIED:20080407T193241\n"
3634 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3638 "UID:20080407T193125Z-19554-727-1-50@gollum\n"
3639 "DTSTAMP:20080407T193125Z\n"
3640 "DTSTART:20080413T090000Z\n"
3641 "DTEND:20080413T093000Z\n"
3644 "SUMMARY:Recurring: Modified\n"
3646 "CREATED:20080407T193241\n"
3647 "LAST-MODIFIED:20080407T193647\n"
3648 "RECURRENCE-ID:20080413T090000Z\n"
3649 "DESCRIPTION:second instance modified\n"
3652 config.templateItem = config.insertItem;
3653 config.uniqueProperties = "SUMMARY:UID:LOCATION";
3654 config.sizeProperty = "DESCRIPTION";
3655 config.testcases = "testcases/ical20.ics";
3656 } if(!strcmp(type, "vcal10")) {
3657 config.sourceName = "vcal10";
3658 config.sourceNameServerTemplate = "calendar";
3659 config.uri = "cal"; // Funambol 3.0
3660 config.type = "text/x-vcalendar";
3665 "SUMMARY:phone meeting\n"
3666 "DTEND:20060406T163000Z\n"
3667 "DTSTART:20060406T160000Z\n"
3668 "DTSTAMP:20060406T211449Z\n"
3669 "LOCATION:my office\n"
3670 "DESCRIPTION:let's talk<<REVISION>>\n"
3677 "SUMMARY:meeting on site\n"
3678 "DTEND:20060406T163000Z\n"
3679 "DTSTART:20060406T160000Z\n"
3680 "DTSTAMP:20060406T211449Z\n"
3681 "LOCATION:big meeting room\n"
3682 "DESCRIPTION:nice to see you\n"
3685 /* change location in insertItem in testMerge() */
3690 "SUMMARY:phone meeting\n"
3691 "DTEND:20060406T163000Z\n"
3692 "DTSTART:20060406T160000Z\n"
3693 "DTSTAMP:20060406T211449Z\n"
3694 "LOCATION:calling from home\n"
3695 "DESCRIPTION:let's talk\n"
3702 "SUMMARY:phone meeting\n"
3703 "DTEND:20060406T163000Z\n"
3704 "DTSTART:20060406T160000Z\n"
3705 "DTSTAMP:20060406T211449Z\n"
3706 "LOCATION:my office\n"
3707 "DESCRIPTION:what the heck, let's even shout a bit\n"
3710 config.templateItem = config.insertItem;
3711 config.uniqueProperties = "SUMMARY:UID:LOCATION";
3712 config.sizeProperty = "DESCRIPTION";
3713 config.testcases = "testcases/vcal10.ics";
3714 } else if (!strcmp(type, "ical20_noutc") ||
3715 (!strcmp(type, "ical20") && noutc)) {
3716 config.sourceName = "ical20";
3717 config.sourceNameServerTemplate = "calendar";
3718 config.uri = "cal2"; // ScheduleWorld
3719 config.type = "text/x-vcalendar";
3722 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3726 "TZID:Asia/Shanghai\n"
3728 "DTSTART:19670101T000000\n"
3729 "TZOFFSETFROM:+0800\n"
3730 "TZOFFSETTO:+0800\n"
3734 "TZID:/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai\n"
3735 "X-LIC-LOCATION:Asia/Shanghai\n"
3738 "DTSTART:19700914T230000\n"
3739 "TZOFFSETFROM:+0800\n"
3740 "TZOFFSETTO:+0800\n"
3744 "SUMMARY:phone meeting\n"
3745 "DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai:20060406T163000\n"
3746 "DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai:20060406T160000\n"
3747 "UID:1234567890!@#$%^&*()<>@dummy\n"
3748 "DTSTAMP:20060406T211449Z\n"
3749 "LAST-MODIFIED:20060409T213201\n"
3750 "CREATED:20060409T213201\n"
3751 "LOCATION:my office\n"
3752 "DESCRIPTION:let's talk<<REVISION>>\n"
3760 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3764 "TZID:Asia/Shanghai\n"
3766 "DTSTART:19670101T000000\n"
3767 "TZOFFSETFROM:+0800\n"
3768 "TZOFFSETTO:+0800\n"
3772 "TZID:/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai\n"
3773 "X-LIC-LOCATION:Asia/Shanghai\n"
3776 "DTSTART:19700914T230000\n"
3777 "TZOFFSETFROM:+0800\n"
3778 "TZOFFSETTO:+0800\n"
3782 "SUMMARY:meeting on site\n"
3783 "DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai:20060406T163000\n"
3784 "DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Asia/Shanghai:20060406T160000\n"
3785 "UID:1234567890!@#$%^&*()<>@dummy\n"
3786 "DTSTAMP:20060406T211449Z\n"
3787 "LAST-MODIFIED:20060409T213201\n"
3788 "CREATED:20060409T213201\n"
3789 "LOCATION:big meeting room\n"
3790 "DESCRIPTION:nice to see you\n"
3796 /* change location and description of insertItem in testMerge(), add alarm */
3797 config.mergeItem1 = "";
3798 config.mergeItem2 = "";
3799 config.parentItem = "";
3800 config.childItem = "";
3801 config.templateItem = config.insertItem;
3802 config.uniqueProperties = "SUMMARY:UID:LOCATION";
3803 config.sizeProperty = "DESCRIPTION";
3804 config.testcases = "testcases/ical20.ics";
3805 } else if(!strcmp(type, "itodo20")) {
3806 config.sourceName = "itodo20";
3807 config.sourceNameServerTemplate = "todo";
3808 config.uri = "task2"; // ScheduleWorld
3809 config.type = "text/x-vcalendar";
3812 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3816 "UID:20060417T173712Z-4360-727-1-2730@gollum\n"
3817 "DTSTAMP:20060417T173712Z\n"
3819 "DESCRIPTION:to be done<<REVISION>>\n"
3821 "STATUS:IN-PROCESS\n"
3822 "CREATED:20060417T173712\n"
3823 "LAST-MODIFIED:20060417T173712\n"
3828 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3832 "UID:20060417T173712Z-4360-727-1-2730@gollum\n"
3833 "DTSTAMP:20060417T173712Z\n"
3834 "SUMMARY:do me ASAP\n"
3835 "DESCRIPTION:to be done\n"
3837 "STATUS:IN-PROCESS\n"
3838 "CREATED:20060417T173712\n"
3839 "LAST-MODIFIED:20060417T173712\n"
3842 /* change summary in insertItem in testMerge() */
3845 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3849 "UID:20060417T173712Z-4360-727-1-2730@gollum\n"
3850 "DTSTAMP:20060417T173712Z\n"
3851 "SUMMARY:do me please\\, please\n"
3852 "DESCRIPTION:to be done\n"
3854 "STATUS:IN-PROCESS\n"
3855 "CREATED:20060417T173712\n"
3856 "LAST-MODIFIED:20060417T173712\n"
3861 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3865 "UID:20060417T173712Z-4360-727-1-2730@gollum\n"
3866 "DTSTAMP:20060417T173712Z\n"
3868 "DESCRIPTION:to be done\n"
3870 "STATUS:IN-PROCESS\n"
3871 "CREATED:20060417T173712\n"
3872 "LAST-MODIFIED:20060417T173712\n"
3875 config.templateItem = config.insertItem;
3876 config.uniqueProperties = "SUMMARY:UID";
3877 config.sizeProperty = "DESCRIPTION";
3878 config.testcases = "testcases/itodo20.ics";
3879 } else if(!strcmp(type, "text")) {
3880 // The "text" test uses iCalendar 2.0 VJOURNAL
3881 // as format because synccompare doesn't handle
3882 // plain text. A backend which wants to use this
3883 // test data must support importing/exporting
3884 // the test data in that format, see EvolutionMemoSource
3886 config.uri = "note"; // ScheduleWorld
3887 config.sourceNameServerTemplate = "memo";
3888 config.type = "memo";
3889 config.itemType = "text/calendar";
3892 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3897 "DESCRIPTION:Summary\\nBody text REVISION\n"
3902 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3906 "SUMMARY:Summary Modified\n"
3907 "DESCRIPTION:Summary Modified\\nBody text\n"
3910 /* change summary, as in updateItem, and the body in the other merge item */
3911 config.mergeItem1 = config.updateItem;
3914 "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
3919 "DESCRIPTION:Summary\\nBody modified\n"
3922 config.templateItem = config.insertItem;
3923 config.uniqueProperties = "SUMMARY:DESCRIPTION";
3924 config.sizeProperty = "DESCRIPTION";
3925 config.testcases = "testcases/imemo20.ics";
3926 }else if (!strcmp (type, "super")) {
3927 config.subConfigs = "ical20,itodo20";
3932 void CheckSyncReport::check(SyncMLStatus status, SyncReport &report) const
3937 str << "----------|--------CLIENT---------|--------SERVER---------|\n";
3938 str << " | NEW | MOD | DEL | NEW | MOD | DEL |\n";
3939 str << "----------|-----------------------------------------------|\n";
3940 str << StringPrintf("Expected | %3d | %3d | %3d | %3d | %3d | %3d |\n",
3941 clientAdded, clientUpdated, clientDeleted,
3942 serverAdded, serverUpdated, serverDeleted);
3943 str << "Expected sync mode: " << PrettyPrintSyncMode(syncMode) << "\n";
3944 SE_LOG_INFO(NULL, NULL, "sync report:\n%s\n", str.str().c_str());
3947 CPPUNIT_ASSERT_EQUAL(STATUS_OK, status);
3950 // this code is intentionally duplicated to produce nicer CPPUNIT asserts
3951 BOOST_FOREACH(SyncReport::value_type &entry, report) {
3952 const std::string &name = entry.first;
3953 const SyncSourceReport &source = entry.second;
3955 SE_LOG_DEBUG(NULL, NULL, "Checking sync source %s...", name.c_str());
3957 CLIENT_TEST_EQUAL(name, STATUS_OK, source.getStatus());
3959 CLIENT_TEST_EQUAL(name, 0, source.getItemStat(SyncSourceReport::ITEM_LOCAL,
3960 SyncSourceReport::ITEM_ANY,
3961 SyncSourceReport::ITEM_REJECT));
3962 CLIENT_TEST_EQUAL(name, 0, source.getItemStat(SyncSourceReport::ITEM_REMOTE,
3963 SyncSourceReport::ITEM_ANY,
3964 SyncSourceReport::ITEM_REJECT));
3966 const char* checkSyncModeStr = getenv("CLIENT_TEST_NOCHECK_SYNCMODE");
3967 bool checkSyncMode = true;
3968 bool checkSyncStats = getenv ("CLIENT_TEST_NOCHECK_SYNCSTATS") ? false : true;
3969 if (checkSyncModeStr &&
3970 (!strcmp(checkSyncModeStr, "1") || !strcasecmp(checkSyncModeStr, "t"))) {
3971 checkSyncMode = false;
3974 if (syncMode != SYNC_NONE && checkSyncMode) {
3975 CLIENT_TEST_EQUAL(name, syncMode, source.getFinalSyncMode());
3978 if (clientAdded != -1 && checkSyncStats) {
3979 CLIENT_TEST_EQUAL(name, clientAdded,
3980 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
3981 SyncSourceReport::ITEM_ADDED,
3982 SyncSourceReport::ITEM_TOTAL));
3984 if (clientUpdated != -1 && checkSyncStats) {
3985 CLIENT_TEST_EQUAL(name, clientUpdated,
3986 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
3987 SyncSourceReport::ITEM_UPDATED,
3988 SyncSourceReport::ITEM_TOTAL));
3990 if (clientDeleted != -1 && checkSyncStats) {
3991 CLIENT_TEST_EQUAL(name, clientDeleted,
3992 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
3993 SyncSourceReport::ITEM_REMOVED,
3994 SyncSourceReport::ITEM_TOTAL));
3997 if (serverAdded != -1 && checkSyncStats) {
3998 CLIENT_TEST_EQUAL(name, serverAdded,
3999 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
4000 SyncSourceReport::ITEM_ADDED,
4001 SyncSourceReport::ITEM_TOTAL));
4003 if (serverUpdated != -1 && checkSyncStats) {
4004 CLIENT_TEST_EQUAL(name, serverUpdated,
4005 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
4006 SyncSourceReport::ITEM_UPDATED,
4007 SyncSourceReport::ITEM_TOTAL));
4009 if (serverDeleted != -1 && checkSyncStats) {
4010 CLIENT_TEST_EQUAL(name, serverDeleted,
4011 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
4012 SyncSourceReport::ITEM_REMOVED,
4013 SyncSourceReport::ITEM_TOTAL));
4016 SE_LOG_DEBUG(NULL, NULL, "Done with checking sync report.");
4021 #endif // ENABLE_INTEGRATION_TESTS