02070608a54ece3501801ad3c2f1b892d034af01
[platform/upstream/syncevolution.git] / test / ClientTest.h
1 /*
2  * Copyright (C) 2008 Funambol, Inc.
3  * Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
4  * Copyright (C) 2009 Intel Corporation
5  *
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.
10  *
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.
15  *
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
19  * 02110-1301  USA
20  */
21
22 #ifndef INCL_TESTSYNCCLIENT
23 #define INCL_TESTSYNCCLIENT
24
25 #include <string>
26 #include <vector>
27 #include <list>
28
29 #include <boost/function.hpp>
30 #include <boost/shared_ptr.hpp>
31
32 class EvolutionSyncClient;
33 class EvolutionSyncSource;
34 class TransportWrapper;
35 typedef EvolutionSyncSource SyncSource;
36
37 #include <SyncML.h>
38 #include <TransportAgent.h>
39
40 #include "test.h"
41
42 #ifdef ENABLE_INTEGRATION_TESTS
43
44 #include <cppunit/TestSuite.h>
45 #include <cppunit/TestAssert.h>
46 #include <cppunit/TestFixture.h>
47
48 /**
49  * This class encapsulates logging and checking of a SyncReport.
50  * When constructed with default parameters, no checking will be done.
51  * Otherwise the sync report has to contain exactly the expected result.
52  * When multiple sync sources are active, @b all of them have to behave
53  * alike (which is how the tests are constructed).
54  *
55  * No item is ever supposed to fail.
56  */
57 class CheckSyncReport {
58   public:
59     CheckSyncReport(int clAdded = -1, int clUpdated = -1, int clDeleted = -1,
60                     int srAdded = -1, int srUpdated = -1, int srDeleted = -1,
61                     bool mstSucceed = true, SyncMode mode = SYNC_NONE) :
62         clientAdded(clAdded),
63         clientUpdated(clUpdated),
64         clientDeleted(clDeleted),
65         serverAdded(srAdded),
66         serverUpdated(srUpdated),
67         serverDeleted(srDeleted),
68         mustSucceed(mstSucceed),
69         syncMode(mode)
70         {}
71
72     virtual ~CheckSyncReport() {}
73
74     int clientAdded, clientUpdated, clientDeleted,
75         serverAdded, serverUpdated, serverDeleted;
76     bool mustSucceed;
77     SyncMode syncMode;
78
79     /**
80      * checks that the sync completed as expected and throws
81      * CPPUnit exceptions if something is wrong
82      *
83      * @param res     return code from SyncClient::sync()
84      * @param report  the sync report stored in the SyncClient
85      */
86     virtual void check(SyncMLStatus status, SyncReport &report) const;
87 };
88
89 /**
90  * parameters for running a sync
91  */
92 struct SyncOptions {
93     /** sync mode chosen by client */
94     SyncMode m_syncMode;
95     /**
96      * has to be called after a successful or unsuccessful sync,
97      * will dump the report and (optionally) check the result;
98      * beware, the later may throw exceptions inside CPPUNIT macros
99      */
100     CheckSyncReport m_checkReport;
101
102     /** maximum message size supported by client */
103     long m_maxMsgSize;
104     /** maximum object size supported by client */
105     long m_maxObjSize;
106     /** enabled large object support */
107     bool m_loSupport;
108     /** enabled WBXML (default) */
109     bool m_isWBXML;
110
111     bool m_isSuspended; 
112     
113     bool m_isAborted;
114
115     typedef boost::function<bool (EvolutionSyncClient &,
116                                   SyncOptions &)> Callback_t;
117     /**
118      * Callback to be invoked after setting up local sources, but
119      * before running the engine. May throw exception to indicate
120      * error and return true to stop sync without error.
121      */
122     Callback_t m_startCallback;
123
124     boost::shared_ptr<SyncEvolution::TransportAgent> m_transport;
125
126     SyncOptions(SyncMode syncMode = SYNC_NONE,
127                 const CheckSyncReport &checkReport = CheckSyncReport(),
128                 long maxMsgSize = 128 * 1024, // 128KB = large enough that normal tests should run with a minimal number of messages
129                 long maxObjSize = 1 * 1024 * 1024 * 1024, // 1GB = basically unlimited...
130                 bool loSupport = false,
131                 bool isWBXML = defaultWBXML(),
132                 Callback_t startCallback = EmptyCallback,
133                 boost::shared_ptr<SyncEvolution::TransportAgent> transport =
134                 boost::shared_ptr<SyncEvolution::TransportAgent>()) :
135         m_syncMode(syncMode),
136         m_checkReport(checkReport),
137         m_maxMsgSize(maxMsgSize),
138         m_maxObjSize(maxObjSize),
139         m_loSupport(loSupport),
140         m_isWBXML(isWBXML),
141         m_isSuspended(false),
142         m_isAborted(false),
143         m_startCallback(startCallback),
144         m_transport (transport)
145     {}
146
147     SyncOptions &setSyncMode(SyncMode syncMode) { m_syncMode = syncMode; return *this; }
148     SyncOptions &setCheckReport(const CheckSyncReport &checkReport) { m_checkReport = checkReport; return *this; }
149     SyncOptions &setMaxMsgSize(long maxMsgSize) { m_maxMsgSize = maxMsgSize; return *this; }
150     SyncOptions &setMaxObjSize(long maxObjSize) { m_maxObjSize = maxObjSize; return *this; }
151     SyncOptions &setLOSupport(bool loSupport) { m_loSupport = loSupport; return *this; }
152     SyncOptions &setWBXML(bool isWBXML) { m_isWBXML = isWBXML; return *this; }
153     SyncOptions &setStartCallback(const Callback_t &callback) { m_startCallback = callback; return *this; }
154     SyncOptions &setTransportAgent(const boost::shared_ptr<SyncEvolution::TransportAgent> transport)
155                                   {m_transport = transport; return *this;}
156
157     static bool EmptyCallback(EvolutionSyncClient &,
158                               SyncOptions &) { return false; }
159
160     /** if CLIENT_TEST_XML=1, then XML, otherwise WBXML */
161     static bool defaultWBXML();
162 };
163
164 class LocalTests;
165 class SyncTests;
166
167 /**
168  * This is the interface expected by the testing framework for sync
169  * clients.  It defines several methods that a derived class must
170  * implement if it wants to use that framework. Note that this class
171  * itself is not derived from SyncClient. This gives a user of this
172  * framework the freedom to implement it in two different ways:
173  * - implement a class derived from both SyncClient and ClientTest
174  * - add testing of an existing subclass of SyncClient by implementing
175  *   a ClientTest which uses that subclass
176  *
177  * The client is expected to support change tracking for multiple
178  * servers. Although the framework always always tests against the
179  * same server, for most tests it is necessary to access the database
180  * without affecting the next synchronization with the server. This is
181  * done by asking the client for two different sync sources via
182  * Config::createSourceA and Config::createSourceB which have to
183  * create them in a suitable way - pretty much as if the client was
184  * synchronized against different server. A third, different change
185  * tracking is needed for real synchronizations of the data.
186  *
187  * Furthermore the client is expected to support multiple data sources
188  * of the same kind, f.i. two different address books. This is used to
189  * test full client A <-> server <-> client B synchronizations in some
190  * tests or to check server modifications done by client A with a
191  * synchronization against client B. In those tests client A is mapped
192  * to the first data source and client B to the second one.
193  *
194  * Finally the SyncSource API is used in slightly different ways which
195  * go beyond what is normally expected from a SyncSource implementation:
196  * - beginSync() may be called without setting a sync mode:
197  *   when SyncSource::getSyncMode() returns SYNC_NONE the source is
198  *   expected to make itself ready to iterate over all, new, updated and
199  *   deleted items
200  * - items may be added via SyncSource::addItem() with a type of "raw":
201  *   this implies that the type is the one used for items in the
202  *   ClientTest::Config below
203  *
204  * Handling configuration and creating classes is entirely done by the
205  * subclass of ClientTest, the frameworks makes no assumptions
206  * about how this is done. Instead it queries the ClientTest for
207  * properties (like available sync sources) and then creates several
208  * tests.
209  */
210 class ClientTest {
211   public:
212     ClientTest(int serverSleepSec = 0, const std::string &serverLog= "");
213     virtual ~ClientTest();
214
215     /**
216      * This function registers tests using this instance of ClientTest for
217      * later use during a test run.
218      *
219      * The instance must remain valid until after the tests were
220      * run. To run them use a separate test runner, like the one from
221      * client-test-main.cpp.
222      */
223     virtual void registerTests();
224
225     class Config;
226
227     /**
228      * Creates an instance of LocalTests (default implementation) or a
229      * class derived from it.  LocalTests provides tests which cover
230      * the SyncSource interface and can be executed without a SyncML
231      * server. It also contains utility functions for working with
232      * SyncSources.
233      *
234      * A ClientTest implementation can, but doesn't have to extend
235      * these tests by instantiating a derived class here.
236      */
237     virtual LocalTests *createLocalTests(const std::string &name, int sourceParam, ClientTest::Config &co);
238
239     /**
240      * Creates an instance of SyncTests (default) or a class derived
241      * from it.  SyncTests provides tests which cover the actual
242      * interaction with a SyncML server.
243      *
244      * A ClientTest implementation can, but doesn't have to extend
245      * these tests by instantiating a derived class here.
246      */
247     virtual SyncTests *createSyncTests(const std::string &name, std::vector<int> sourceIndices, bool isClientA = true);
248
249     /**
250      * utility function for dumping items which are C strings with blank lines as separator
251      */
252     static int dump(ClientTest &client, SyncSource &source, const char *file);
253
254     /**
255      * utility function for splitting file into items with blank lines as separator
256      *
257      * @retval realfile       If <file>.<server>.tem exists, then it is used instead
258      *                        of the generic version. The caller gets the name of the
259      *                        file that was opened here.
260      */
261     static void getItems(const char *file, std::list<std::string> &items, std::string &realfile);
262
263     /**
264      * utility function for importing items with blank lines as separator
265      */
266     static int import(ClientTest &client, SyncSource &source, const char *file, std::string &realfile);
267
268     /**
269      * utility function for comparing vCard and iCal files with the external
270      * synccompare.pl Perl script
271      */
272     static bool compare(ClientTest &client, const char *fileA, const char *fileB);
273
274     struct Config;
275
276     /**
277      * A derived class can use this call to get default test
278      * cases, but still has to add callbacks which create sources
279      * and execute a sync session.
280      *
281      * Some of the test cases are compiled into the library, other
282      * depend on the auxiliary files from the "test" directory.
283      * Currently supported types:
284      * - vcard30 = vCard 3.0 contacts
285      * - vcard21 = vCard 2.1 contacts
286      * - ical20 = iCal 2.0 events
287      * - vcal10 = vCal 1.0 events
288      * - itodo20 = iCal 2.0 tasks
289      */
290     static void getTestData(const char *type, Config &config);
291
292     /**
293      * Information about a data source. For the sake of simplicity all
294      * items pointed to are owned by the ClientTest and must
295      * remain valid throughout a test session. Not setting a pointer
296      * is okay, but it will disable all tests that need the
297      * information.
298      */
299     struct Config {
300         /**
301          * The name is used in test names and has to be set.
302          */
303         const char *sourceName;
304
305         /**
306          * A default URI to be used when creating a client config.
307          */
308         const char *uri;
309
310         /**
311          * A member function of a subclass which is called to create a
312          * sync source referencing the data. This is used in tests of
313          * the SyncSource API itself as well as in tests which need to
314          * modify or check the data sources used during synchronization.
315          *
316          * The test framework will call beginSync() and then some of
317          * the functions it wants to test. After a successful test it
318          * will call endSync() which is then expected to store all
319          * changes persistently. Creating a sync source again
320          * with the same call should not report any
321          * new/updated/deleted items until such changes are made via
322          * another sync source.
323          *
324          * The instance will be deleted by the caller. Because this
325          * may be in the error case or in an exception handler,
326          * the sync source's desctructor should not thow exceptions.
327          *
328          * @param client    the same instance to which this config belongs
329          * @param source    index of the data source (from 0 to ClientTest::getNumSources() - 1)
330          * @param isSourceA true if the requested SyncSource is the first one accessing that
331          *                  data, otherwise the second
332          */
333         typedef SyncSource *(*createsource_t)(ClientTest &client, int source, bool isSourceA);
334
335         /**
336          * Creates a sync source which references the primary database;
337          * it may report the same changes as the sync source used during
338          * sync tests.
339          */
340         createsource_t createSourceA;
341
342         /**
343          * A second sync source also referencing the primary data
344          * source, but configured so that it tracks changes
345          * independently from the the primary sync source.
346          *
347          * In local tests the usage is like this:
348          * - add item via first SyncSource
349          * - iterate over new items in second SyncSource
350          * - check that it lists the added item
351          *
352          * In tests with a server the usage is:
353          * - do a synchronization with the server
354          * - iterate over items in second SyncSource
355          * - check that the total number and number of
356          *   added/updated/deleted items is as expected
357          */
358         createsource_t createSourceB;
359
360         /**
361          * The framework can generate vCard and vCalendar/iCalendar items
362          * automatically by copying a template item and modifying certain
363          * properties.
364          *
365          * This is the template for these automatically generated items.
366          * It must contain the string <<REVISION>> which will be replaced
367          * with the revision parameter of the createItem() method.
368          */
369         const char *templateItem;
370
371          /**
372          * This is a colon (:) separated list of properties which need
373          * to be modified in templateItem.
374          */
375         const char *uniqueProperties;
376
377         /**
378          * the number of items to create during stress tests
379          */
380         int numItems;
381
382         /**
383          * This is a single property in templateItem which can be extended
384          * to increase the size of generated items.
385          */
386         const char *sizeProperty;
387
388         /**
389          * Type to be set when importing any of the items into the
390          * corresponding sync sources. Use "" if sync source doesn't
391          * need this information.
392          */
393         const char *itemType;
394
395         /**
396          * A very simple item that is inserted during basic tests. Ideally
397          * it only contains properties supported by all servers.
398          */
399         const char *insertItem;
400
401         /**
402          * A slightly modified version of insertItem. If the source has UIDs
403          * embedded into the item data, then both must have the same UID.
404          * Again all servers should better support these modified properties.
405          */
406         const char *updateItem;
407
408         /**
409          * A more heavily modified version of insertItem. Same UID if necessary,
410          * but can test changes to items only supported by more advanced
411          * servers.
412          */
413         const char *complexUpdateItem;
414
415         /**
416          * To test merge conflicts two different updates of insertItem are
417          * needed. This is the first such update.
418          */
419         const char *mergeItem1;
420
421         /**
422          * The second merge update item. To avoid true conflicts it should
423          * update different properties than mergeItem1, but even then servers
424          * usually have problems perfectly merging items. Therefore the
425          * test is run without expecting a certain merge result.
426          */
427         const char *mergeItem2;
428
429         /**
430          * These two items are related: one is main one, the other is
431          * a subordinate one. The semantic is that the main item is
432          * complete on it its own, while the other normally should only
433          * be used in combination with the main one.
434          *
435          * Because SyncML cannot express such dependencies between items,
436          * a SyncSource has to be able to insert, updated and remove
437          * both items independently. However, operations which violate
438          * the semantic of the related items (like deleting the parent, but
439          * not the child) may have unspecified results (like also deleting
440          * the child). See LINKED_ITEMS_RELAXED_SEMANTIC.
441          *
442          * One example for main and subordinate items are a recurring
443          * iCalendar 2.0 event and a detached recurrence.
444          */
445         const char *parentItem, *childItem;
446
447         /**
448          * define to 0 to disable tests which slightly violate the
449          * semantic of linked items by inserting children
450          * before/without their parent
451          */
452 #ifndef LINKED_ITEMS_RELAXED_SEMANTIC
453 # define LINKED_ITEMS_RELAXED_SEMANTIC 1
454 #endif
455
456         /**
457          * setting this to false disables tests which depend
458          * on the source's support for linked item semantic
459          * (testLinkedItemsInsertParentTwice, testLinkedItemsInsertChildTwice)
460          */
461         bool sourceKnowsItemSemantic;
462
463         /**
464          * called to dump all items into a file, required by tests which need
465          * to compare items
466          *
467          * ClientTest::dump can be used: it will simply dump all items of the source
468          * with a blank line as separator.
469          *
470          * @param source     sync source A already created and with beginSync() called
471          * @param file       a file name
472          * @return error code, 0 for success
473          */
474         int (*dump)(ClientTest &client, SyncSource &source, const char *file);
475
476         /**
477          * import test items: which these are is determined entirely by
478          * the implementor, but tests work best if several complex items are
479          * imported
480          *
481          * ClientTest::import can be used if the file contains items separated by
482          * empty lines.
483          *
484          * @param source     sync source A already created and with beginSync() called
485          * @param file       the name of the file to import
486          * @retval realfile  the name of the file that was really imported;
487          *                   this may depend on the current server that is being tested
488          * @return error code, 0 for success
489          */
490         int (*import)(ClientTest &client, SyncSource &source, const char *file, std::string &realfile);
491
492         /**
493          * a function which compares two files with items in the format used by "dump"
494          *
495          * @param fileA      first file name
496          * @param fileB      second file name
497          * @return true if the content of the files is considered equal
498          */
499         bool (*compare)(ClientTest &client, const char *fileA, const char *fileB);
500
501         /**
502          * a file with test cases in the format expected by import and compare
503          */
504         const char *testcases;
505
506         /**
507          * the item type normally used by the source (not used by the tests
508          * themselves; client-test.cpp uses it to initialize source configs)
509          */
510         const char *type;
511
512         /**
513          * TRUE if the source supports recovery from an interrupted
514          * synchronization. Enables the Client::Sync::*::Retry group
515          * of tests.
516          */
517         bool retrySync;
518     };
519
520     /**
521      * Data sources are enumbered from 0 to n-1 for the purpose of
522      * testing. This call returns n.
523      */
524     virtual int getNumSources() = 0;
525
526     /**
527      * Called to fill the given test source config with information
528      * about a sync source identified by its index. It's okay to only
529      * fill in the available pieces of information and set everything
530      * else to zero.
531      */
532     virtual void getSourceConfig(int source, Config &config) = 0;
533
534     /**
535      * The instance to use as second client. Returning NULL disables
536      * all checks which require a second client. The returned pointer
537      * must remain valid throughout the life time of the tests.
538      *
539      * The second client must be configured to access the same server
540      * and have data sources which match the ones from the primary
541      * client.
542      */
543     virtual ClientTest *getClientB() = 0;
544
545     /**
546      * Execute a synchronization with the selected sync sources
547      * and the selected synchronization options. The log file
548      * in LOG has been set up already for the synchronization run
549      * and should not be changed by the client.
550      *
551      * @param activeSources a -1 terminated array of sync source indices
552      * @param logbase      basename for logging: can be used for directory or as file (by adding .log suffix)
553      * @param options      sync options to be used
554      * @return return code of SyncClient::sync()
555      */
556     virtual SyncMLStatus doSync(
557         const int *activeSources,
558         const std::string &logbase,
559         const SyncOptions &options) = 0;
560
561
562     /**
563      * This is called after successful sync() calls (res == 0) as well
564      * as after unsuccessful ones (res != 1). The default implementation
565      * sleeps for the number of seconds specified when constructing this
566      * instance and copies the server log if one was named.
567      *
568      * @param res       result of sync()
569      * @param logname   base name of the current sync log (without ".client.[AB].log" suffix)
570      */
571     virtual void postSync(int res, const std::string &logname);
572
573   protected:
574     /**
575      * time to sleep in postSync()
576      */
577     int serverSleepSeconds;
578
579     /**
580      * server log file which is copied by postSync() and then
581      * truncated (Unix only, Windows does not allow such access
582      * to an open file)
583      */
584     std::string serverLogFileName;
585
586   private:
587     /**
588      * really a CppUnit::TestFactory, but declared as void * to avoid
589      * dependencies on the CPPUnit header files: created by
590      * registerTests() and remains valid until the client is deleted
591      */
592     void *factory;
593 };
594
595 /**
596  * helper class to encapsulate ClientTest::Config::createsource_t
597  * pointer and the corresponding parameters
598  */
599 class CreateSource {
600 public:
601     CreateSource(ClientTest::Config::createsource_t createSourceParam, ClientTest &clientParam, int sourceParam, bool isSourceAParam) :
602         createSource(createSourceParam),
603         client(clientParam),
604         source(sourceParam),
605         isSourceA(isSourceAParam) {}
606
607     SyncSource *operator() () {
608         CPPUNIT_ASSERT(createSource);
609         return createSource(client, source, isSourceA);
610     }
611
612     const ClientTest::Config::createsource_t createSource;
613     ClientTest &client;
614     const int source;
615     const bool isSourceA;
616 };
617
618
619 /**
620  * local test of one sync source and utility functions also used by
621  * sync tests
622  */
623 class LocalTests : public CppUnit::TestSuite, public CppUnit::TestFixture {
624 public:
625     /** the client we are testing */
626     ClientTest &client;
627
628     /** number of the source we are testing in that client */
629     const int source;
630
631     /** configuration that corresponds to source */
632     const ClientTest::Config config;
633
634     /** helper funclets to create sources */
635     CreateSource createSourceA, createSourceB;
636
637     LocalTests(const std::string &name, ClientTest &cl, int sourceParam, ClientTest::Config &co) :
638         CppUnit::TestSuite(name),
639         client(cl),
640         source(sourceParam),
641         config(co),
642         createSourceA(co.createSourceA, cl, sourceParam, true),
643         createSourceB(co.createSourceB, cl, sourceParam, false)
644         {}
645
646     /**
647      * adds the supported tests to the instance itself;
648      * this is the function that a derived class can override
649      * to add additional tests
650      */
651     virtual void addTests();
652
653     /**
654      * opens source and inserts the given item; can be called
655      * regardless whether the data source already contains items or not
656      *
657      * @param relaxed   if true, then disable some of the additional checks after adding the item
658      * @return the LUID of the inserted item
659      */
660     virtual std::string insert(CreateSource createSource, const char *data, const char *dataType, bool relaxed = false);
661
662     /**
663      * assumes that exactly one element is currently inserted and updates it with the given item
664      *
665      * @param check     if true, then reopen the source and verify that the reported items are as expected
666      */
667     virtual void update(CreateSource createSource, const char *data, const char *dataType, bool check = true);
668
669     /**
670      * updates one item identified by its LUID with the given item
671      *
672      * The type of the item is cleared, as in insert() above.
673      */
674     virtual void update(CreateSource createSource, const char *data, const char *dataType, const std::string &luid);
675
676     /** deletes all items locally via sync source */
677     virtual void deleteAll(CreateSource createSource);
678
679     /**
680      * takes two databases, exports them,
681      * then compares them using synccompare
682      *
683      * @param refFile      existing file with source reference items, NULL uses a dump of sync source A instead
684      * @param copy         a sync source which contains the copied items, begin/endSync will be called
685      * @param raiseAssert  raise assertion if comparison yields differences (defaults to true)
686      * @return true if the two databases are equal
687      */
688     virtual bool compareDatabases(const char *refFile, SyncSource &copy, bool raiseAssert = true);
689
690     /**
691      * insert artificial items, number of them determined by TEST_EVOLUTION_NUM_ITEMS
692      * unless passed explicitly
693      *
694      * @param createSource    a factory for the sync source that is to be used
695      * @param startIndex      IDs are generated starting with this value
696      * @param numItems        number of items to be inserted if non-null, otherwise TEST_EVOLUTION_NUM_ITEMS is used
697      * @param size            minimum size for new items
698      * @return number of items inserted
699      */
700     virtual std::list<std::string> insertManyItems(CreateSource createSource, int startIndex = 1, int numItems = 0, int size = -1);
701
702     /**
703      * create an artificial item for the current database
704      *
705      * @param item      item number: items with different number should be
706      *                  recognized as different by SyncML servers
707      * @param revision  differentiates items with the same item number (= updates of an older item)
708      * @param size      if > 0, then create items at least that large (in bytes)
709      * @return created item
710      */
711     std::string createItem(int item, const std::string &revision, int size);
712     std::string createItem(int item, int revision, int size) {
713         char buffer[32];
714         sprintf(buffer, "%d", revision);
715         return createItem(item, std::string(buffer), size);
716     }
717
718     /* for more information on the different tests see their implementation */
719
720     virtual void testOpen();
721     virtual void testIterateTwice();
722     virtual void testSimpleInsert();
723     virtual void testLocalDeleteAll();
724     virtual void testComplexInsert();
725     virtual void testLocalUpdate();
726     virtual void testChanges();
727     virtual void testImport();
728     virtual void testImportDelete();
729     virtual void testManyChanges();
730     virtual void testLinkedItemsParent();
731     virtual void testLinkedItemsChild();
732     virtual void testLinkedItemsParentChild();
733     virtual void testLinkedItemsChildParent();
734     virtual void testLinkedItemsChildChangesParent();
735     virtual void testLinkedItemsRemoveParentFirst();
736     virtual void testLinkedItemsRemoveNormal();
737     virtual void testLinkedItemsInsertParentTwice();
738     virtual void testLinkedItemsInsertChildTwice();
739     virtual void testLinkedItemsParentUpdate();
740     virtual void testLinkedItemsUpdateChild();
741     virtual void testLinkedItemsInsertBothUpdateChild();
742     virtual void testLinkedItemsInsertBothUpdateParent();
743
744 };
745
746 enum itemType {
747     NEW_ITEMS,
748     UPDATED_ITEMS,
749     DELETED_ITEMS,
750     TOTAL_ITEMS
751 };
752
753 /**
754  * utility function which counts items of a certain kind known to the sync source
755  * @param source      valid source ready to iterate; NULL triggers an assert
756  * @param itemType    determines which iterator functions are used
757  * @return number of valid items iterated over
758  */
759 int countItemsOfType(SyncSource *source, itemType type);
760
761 typedef std::list<std::string> UIDList;
762 /**
763  * generates list of UIDs in the specified kind of items
764  */
765 UIDList listItemsOfType(SyncSource *source, itemType type);
766
767 /**
768  * Tests synchronization with one or more sync sources enabled.
769  * When testing multiple sources at once only the first config
770  * is checked to see which tests can be executed.
771  */
772 class SyncTests : public CppUnit::TestSuite, public CppUnit::TestFixture {
773 public:
774     /** the client we are testing */
775     ClientTest &client;
776
777     SyncTests(const std::string &name, ClientTest &cl, std::vector<int> sourceIndices, bool isClientA = true);
778     ~SyncTests();
779
780     /** adds the supported tests to the instance itself */
781     virtual void addTests();
782
783 protected:
784     /** list with all local test classes for manipulating the sources and their index in the client */
785     std::vector< std::pair<int, LocalTests *> > sources;
786     typedef std::vector< std::pair<int, LocalTests *> >::iterator source_it;
787
788     /**
789      * Stack of log file prefixes which are to be appended to the base name,
790      * which already contains the current test name. Add a new prefix by
791      * instantiating SyncPrefix. Its destructor takes care of popping
792      * the prefix.
793      */
794     std::list<std::string> logPrefixes;
795
796     class SyncPrefix {
797         SyncTests &m_tests;
798     public:
799         SyncPrefix(const std::string &prefix, SyncTests &tests) :
800         m_tests(tests) {
801             tests.logPrefixes.push_back(prefix);
802         }
803         ~SyncPrefix() {
804             m_tests.logPrefixes.pop_back();
805         }
806     };
807     friend class SyncPrefix;
808
809     /** the indices from sources, terminated by -1 (for sync()) */
810     int *sourceArray;
811
812     /** utility functions for second client */
813     SyncTests *accessClientB;
814
815     enum DeleteAllMode {
816         DELETE_ALL_SYNC,   /**< make sure client and server are in sync,
817                               delete locally,
818                               sync again */
819         DELETE_ALL_REFRESH /**< delete locally, refresh server */
820     };
821
822     /**
823      * Compare databases second client with either reference file(s)
824      * or first client.  The reference file(s) must follow the naming
825      * scheme <reFileBase><source name>.dat
826      */
827     virtual bool compareDatabases(const char *refFileBase = NULL,
828                                   bool raiseAssert = true);
829
830     /** deletes all items locally and on server */
831     virtual void deleteAll(DeleteAllMode mode = DELETE_ALL_SYNC);
832
833     /** get both clients in sync with empty server, then copy one item from client A to B */
834     virtual void doCopy();
835
836     /**
837      * replicate server database locally: same as SYNC_REFRESH_FROM_SERVER,
838      * but done with explicit local delete and then a SYNC_SLOW because some
839      * servers do no support SYNC_REFRESH_FROM_SERVER
840      */
841     virtual void refreshClient(SyncOptions options = SyncOptions());
842
843     /* for more information on the different tests see their implementation */
844
845     // do a two-way sync without additional checks,
846     // may or may not actually be done in two-way mode
847     virtual void testTwoWaySync() {
848         doSync(SyncOptions(SYNC_TWO_WAY));
849     }
850
851     // do a slow sync without additional checks
852     virtual void testSlowSync() {
853         doSync(SyncOptions(SYNC_SLOW,
854                            CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_SLOW)));
855     }
856     // do a refresh from server sync without additional checks
857     virtual void testRefreshFromServerSync() {
858         doSync(SyncOptions(SYNC_REFRESH_FROM_SERVER,
859                            CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_SERVER)));
860     }
861
862     // do a refresh from client sync without additional checks
863     virtual void testRefreshFromClientSync() {
864         doSync(SyncOptions(SYNC_REFRESH_FROM_CLIENT,
865                            CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_CLIENT)));
866     }
867
868     // delete all items, locally and on server using two-way sync
869     virtual void testDeleteAllSync() {
870         deleteAll(DELETE_ALL_SYNC);
871     }
872
873     virtual void testDeleteAllRefresh();
874     virtual void testRefreshFromClientSemantic();
875     virtual void testRefreshFromServerSemantic();
876     virtual void testRefreshStatus();
877
878     // test that a two-way sync copies an item from one address book into the other
879     void testCopy() {
880         doCopy();
881         compareDatabases();
882     }
883
884     virtual void testUpdate();
885     virtual void testComplexUpdate();
886     virtual void testDelete();
887     virtual void testMerge();
888     virtual void testTwinning();
889     virtual void testOneWayFromServer();
890     virtual void testOneWayFromClient();
891     bool doConversionCallback(bool *success,
892                               EvolutionSyncClient &client,
893                               SyncOptions &options);
894     virtual void testConversion();
895     virtual void testItems();
896     virtual void testItemsXML();
897     virtual void testAddUpdate();
898
899     // test copying with maxMsg and no large object support
900     void testMaxMsg() {
901         doVarSizes(true, false);
902     }
903     // test copying with maxMsg and large object support
904     void testLargeObject() {
905         doVarSizes(true, true);
906     }
907
908     virtual void testManyItems();
909
910     virtual void doInterruptResume(int changes,
911                   boost::shared_ptr<TransportWrapper> wrapper); 
912     enum {
913         CLIENT_ADD = (1<<0),
914         CLIENT_REMOVE = (1<<1),
915         CLIENT_UPDATE = (1<<2),
916         SERVER_ADD = (1<<3),
917         SERVER_REMOVE = (1<<4),
918         SERVER_UPDATE = (1<<5)
919     };
920     virtual void testInterruptResumeClientAdd();
921     virtual void testInterruptResumeClientRemove();
922     virtual void testInterruptResumeClientUpdate();
923     virtual void testInterruptResumeServerAdd();
924     virtual void testInterruptResumeServerRemove();
925     virtual void testInterruptResumeServerUpdate();
926     virtual void testInterruptResumeFull();
927
928     virtual void testUserSuspendClientAdd();
929     virtual void testUserSuspendClientRemove();
930     virtual void testUserSuspendClientUpdate();
931     virtual void testUserSuspendServerAdd();
932     virtual void testUserSuspendServerRemove();
933     virtual void testUserSuspendServerUpdate();
934     virtual void testUserSuspendFull();
935
936
937     /**
938      * implements testMaxMsg(), testLargeObject(), testLargeObjectEncoded()
939      * using a sequence of items with varying sizes
940      */
941     virtual void doVarSizes(bool withMaxMsgSize,
942                             bool withLargeObject);
943
944     /**
945      * executes a sync with the given options,
946      * checks the result and (optionally) the sync report
947      */
948     virtual void doSync(const SyncOptions &options);
949     virtual void doSync(const char *logPrefix,
950                         const SyncOptions &options) {
951         SyncPrefix prefix(logPrefix, *this);
952         doSync(options);
953     }
954 };
955
956 /*
957  * A transport wraper wraps a real transport impl and gives user 
958  * possibility to do additional work before/after transport operation.
959  * We use TransportFaultInjector to emulate a network failure;
960  * We use UserSuspendInjector to emulate a user suspend after receving
961  * a response.
962  */
963 class TransportWrapper : public SyncEvolution::TransportAgent {
964 protected:
965     int m_interruptAtMessage, m_messageCount;
966     boost::shared_ptr<SyncEvolution::TransportAgent> m_wrappedAgent;
967     Status m_status;
968     SyncOptions *m_options;
969 public:
970     TransportWrapper() {
971         m_messageCount = 0;
972         m_interruptAtMessage = -1;
973         m_wrappedAgent = boost::shared_ptr<SyncEvolution::TransportAgent>();
974         m_status = INACTIVE;
975         m_options = NULL;
976     }
977     ~TransportWrapper() {
978     }
979
980     virtual int getMessageCount() { return m_messageCount; }
981
982     virtual void setURL(const std::string &url) { m_wrappedAgent->setURL(url); }
983     virtual void setProxy(const std::string &proxy) { m_wrappedAgent->setProxy(proxy); }
984     virtual void setProxyAuth(const std::string &user,
985                               const std::string &password) { m_wrappedAgent->setProxyAuth(user, password); }
986     virtual void setSSL(const std::string &cacerts,
987                         bool verifyServer,
988                         bool verifyHost) { m_wrappedAgent->setSSL(cacerts, verifyServer, verifyHost); }
989     virtual void setContentType(const std::string &type) { m_wrappedAgent->setContentType(type); }
990     virtual void setUserAgent(const::string &agent) { m_wrappedAgent->setUserAgent(agent); }
991     virtual void setAgent(boost::shared_ptr<SyncEvolution::TransportAgent> agent) {m_wrappedAgent = agent;}
992     virtual void setSyncOptions(SyncOptions *options) {m_options = options;}
993     virtual void setInterruptAtMessage (int interrupt) {m_interruptAtMessage = interrupt;}
994     virtual void cancel() { m_wrappedAgent->cancel(); }
995     virtual void reset() {
996         m_messageCount = 0;
997         m_interruptAtMessage = -1;
998         m_status = INACTIVE;
999         m_options = NULL;
1000     }
1001     virtual Status wait() { return m_status; }
1002 };
1003
1004 /** assert equality, include string in message if unequal */
1005 #define CLIENT_TEST_EQUAL( _prefix, \
1006                            _expected, \
1007                            _actual ) \
1008     CPPUNIT_ASSERT_EQUAL_MESSAGE( std::string(_prefix) + ": " + #_expected + " == " + #_actual, \
1009                                   _expected, \
1010                                   _actual )
1011
1012 /** execute _x and then check the status of the _source pointer */
1013 #define SOURCE_ASSERT_NO_FAILURE(_source, _x) \
1014 { \
1015     CPPUNIT_ASSERT_NO_THROW(_x); \
1016     CPPUNIT_ASSERT((_source) && !(_source)->hasFailed()); \
1017 }
1018
1019 /** check _x for true and then the status of the _source pointer */
1020 #define SOURCE_ASSERT(_source, _x) \
1021 { \
1022     CPPUNIT_ASSERT(_x); \
1023     CPPUNIT_ASSERT((_source) && !(_source)->hasFailed()); \
1024 }
1025
1026 /** check that _x evaluates to a specific value and then the status of the _source pointer */
1027 #define SOURCE_ASSERT_EQUAL(_source, _value, _x) \
1028 { \
1029     CPPUNIT_ASSERT_EQUAL(_value, _x); \
1030     CPPUNIT_ASSERT((_source) && !(_source)->hasFailed()); \
1031 }
1032
1033 /** same as SOURCE_ASSERT() with a specific failure message */
1034 #define SOURCE_ASSERT_MESSAGE(_message, _source, _x)     \
1035 { \
1036     CPPUNIT_ASSERT_MESSAGE((_message), (_x)); \
1037     CPPUNIT_ASSERT((_source) && !(_source)->hasFailed()); \
1038 }
1039
1040
1041 /**
1042  * convenience macro for adding a test name like a function,
1043  * to be used inside addTests() of an instance of that class
1044  *
1045  * @param _class      class which contains the function
1046  * @param _function   a function without parameters in that class
1047  */
1048 #define ADD_TEST(_class, _function) \
1049     ADD_TEST_TO_SUITE(this, _class, _function)
1050
1051 #define ADD_TEST_TO_SUITE(_suite, _class, _function) \
1052     _suite->addTest(FilterTest(new CppUnit::TestCaller<_class>(_suite->getName() + "::" #_function, &_class::_function, *this)))
1053
1054 #endif // ENABLE_INTEGRATION_TESTS
1055 #endif // INCL_TESTSYNCCLIENT