diff --git a/akonadi/kmime/CMakeLists.txt b/akonadi/kmime/CMakeLists.txt index 0275c7766..db33766bb 100644 --- a/akonadi/kmime/CMakeLists.txt +++ b/akonadi/kmime/CMakeLists.txt @@ -1,33 +1,41 @@ include_directories( ${CMAKE_SOURCE_DIR}/ ${QT_QTDBUS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ) +add_subdirectory( tests ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII ${KDE4_ENABLE_EXCEPTIONS}" ) ########### next target ############### set( kmimeakonadi_LIB_SRC + localfolders.cpp messagemodel.cpp messageparts.cpp messagethreadingattribute.cpp messagethreaderproxymodel.cpp + resourcesynchronizationjob.cpp # copied from playground/pim/akonaditest/resourcetester ) +kde4_add_kcfg_files( kmimeakonadi_LIB_SRC localfolderssettings.kcfgc ) +install( FILES localfolders.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + kde4_add_library( akonadi-kmime SHARED ${kmimeakonadi_LIB_SRC} ) target_link_libraries( akonadi-kmime akonadi-kde kmime ${QT_QTGUI_LIBRARY} ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} ) set_target_properties( akonadi-kmime PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) install(TARGETS akonadi-kmime EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install( FILES + localfolders.h akonadi-kmime_export.h messagemodel.h messageparts.h messagethreadingattribute.h messagethreaderproxymodel.h DESTINATION ${INCLUDE_INSTALL_DIR}/akonadi/kmime COMPONENT Devel ) diff --git a/akonadi/kmime/localfolders.cpp b/akonadi/kmime/localfolders.cpp new file mode 100644 index 000000000..d1c376b4b --- /dev/null +++ b/akonadi/kmime/localfolders.cpp @@ -0,0 +1,436 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "localfolders.h" + +#include "localfolderssettings.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include // copied from playground/pim/akonaditest + +#define DBUS_SERVICE_NAME QLatin1String( "org.kde.pim.LocalFolders" ) + + +using namespace Akonadi; + +typedef LocalFoldersSettings Settings; + + +/** + * Private class that helps to provide binary compatibility between releases. + * @internal + */ +class Akonadi::LocalFoldersPrivate +{ + public: + LocalFoldersPrivate(); + ~LocalFoldersPrivate(); + + LocalFolders *instance; + bool ready; + bool preparing; + bool scheduled; + bool isMainInstance; + Collection outbox; + Collection sentMail; + Collection rootMaildir; + KJob *outboxJob; + KJob *sentMailJob; + Monitor *monitor; + + /** + If this is the main instance, attempts to create the resource and collections + if necessary, then fetches them. + If this is not the main instance, waits for them to be created by the main + instance, and then fetches them. + + Will emit foldersReady() when done + */ + void prepare(); + + /** + Schedules a prepare() in 1 second. + Called when this is not the main instance and we need to wait, or when + something disappeared and needs to be recreated. + */ + void schedulePrepare(); // slot + + /** + Creates the maildir resource, if it is not found. + */ + void createResourceIfNeeded(); + + /** + Creates the outbox and sent-mail collections, if they are not present. + */ + void createCollectionsIfNeeded(); + + /** + Creates a Monitor to watch the resource and connects to its signals. + This is used to watch for evil users deleting the resource / outbox / etc. + */ + void connectMonitor(); + + /** + Fetches the collections of the maildir resource. + There is one root collection, which contains the outbox and sent-mail + collections. + */ + void fetchCollections(); + + void resourceCreateResult( KJob *job ); + void resourceSyncResult( KJob *job ); + void collectionCreateResult( KJob *job ); + void collectionFetchResult( KJob *job ); + +}; + + +K_GLOBAL_STATIC( LocalFoldersPrivate, sInstance ) + + +LocalFolders::LocalFolders( LocalFoldersPrivate *dd ) + : QObject() + , d( dd ) +{ +} + +LocalFolders *LocalFolders::self() +{ + return sInstance->instance; +} + +void LocalFolders::fetch() +{ + d->prepare(); +} + +bool LocalFolders::isReady() const +{ + return d->ready; +} + +Collection LocalFolders::outbox() const +{ + Q_ASSERT( d->ready ); + return d->outbox; +} + +Collection LocalFolders::sentMail() const +{ + Q_ASSERT( d->ready ); + return d->sentMail; +} + + + +LocalFoldersPrivate::LocalFoldersPrivate() + : instance( new LocalFolders(this) ) +{ + isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME ); + + ready = false; + // prepare() expects these + preparing = false; + outboxJob = 0; + sentMailJob = 0; + monitor = 0; + prepare(); +} + +LocalFoldersPrivate::~LocalFoldersPrivate() +{ + delete instance; +} + +void LocalFoldersPrivate::prepare() +{ + if( ready ) { + //kDebug() << "Already ready. Emitting foldersReady()."; + emit instance->foldersReady(); + return; + } + if( preparing ) { + kDebug() << "Already preparing."; + return; + } + kDebug() << "Preparing. isMainInstance" << isMainInstance; + preparing = true; + scheduled = false; + + Q_ASSERT( outboxJob == 0 ); + Q_ASSERT( sentMailJob == 0 ); + Q_ASSERT( monitor == 0); + + rootMaildir = Collection( -1 ); + outbox = Collection( -1 ); + sentMail = Collection( -1 ); + + createResourceIfNeeded(); +} + +void LocalFoldersPrivate::schedulePrepare() +{ + if( scheduled ) { + kDebug() << "Prepare already scheduled."; + return; + } + + kDebug() << "Scheduling prepare."; + + if( monitor ) { + monitor->disconnect( instance ); + monitor->deleteLater(); + monitor = 0; + } + + ready = false; + preparing = false; + scheduled = true; + QTimer::singleShot( 1000, instance, SLOT( prepare() ) ); +} + +void LocalFoldersPrivate::createResourceIfNeeded() +{ + Q_ASSERT( preparing ); + + // Another instance might have created the resource and updated the config. + Settings::self()->readConfig(); + kDebug() << "Resource from config:" << Settings::resourceId(); + + // check that the maildir resource exists + AgentInstance resource = AgentManager::self()->instance( Settings::resourceId() ); + if( !resource.isValid() ) { + // Try to grab main instance status (if previous main instance quit). + if( !isMainInstance ) { + isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME ); + if( isMainInstance ) { + kDebug() << "I have become the main instance."; + } + } + + // Create resource if main instance. + if( !isMainInstance ) { + kDebug() << "Waiting for the main instance to create the resource."; + schedulePrepare(); + } else { + kDebug() << "Creating maildir resource."; + AgentType type = AgentManager::self()->type( QString::fromLatin1( "akonadi_maildir_resource" ) ); + AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type ); + QObject::connect( job, SIGNAL( result( KJob * ) ), + instance, SLOT( resourceCreateResult( KJob * ) ) ); + // this is not an Akonadi::Job, so we must start it ourselves + job->start(); + } + } else { + connectMonitor(); + } +} + +void LocalFoldersPrivate::createCollectionsIfNeeded() +{ + Q_ASSERT( preparing ); // but I may not be the main instance + Q_ASSERT( rootMaildir.isValid() ); + + if( !outbox.isValid() ) { + kDebug() << "Creating outbox collection."; + Collection col; + col.setParent( rootMaildir ); + col.setName( QLatin1String( "outbox" ) ); + col.setContentMimeTypes( QStringList( QLatin1String( "message/rfc822" ) ) ); + Q_ASSERT( outboxJob == 0 ); + outboxJob = new CollectionCreateJob( col ); + QObject::connect( outboxJob, SIGNAL( result( KJob * ) ), + instance, SLOT( collectionCreateResult( KJob * ) ) ); + } + + if( !sentMail.isValid() ) { + kDebug() << "Creating sent-mail collection."; + Collection col; + col.setParent( rootMaildir ); + col.setName( QLatin1String( "sent-mail" ) ); + col.setContentMimeTypes( QStringList( QLatin1String( "message/rfc822" ) ) ); + Q_ASSERT( sentMailJob == 0 ); + sentMailJob = new CollectionCreateJob( col ); + QObject::connect( sentMailJob, SIGNAL( result( KJob * ) ), + instance, SLOT( collectionCreateResult( KJob * ) ) ); + } + + if( outboxJob == 0 && sentMailJob == 0 ) { + // Everything is ready (created and fetched). + kDebug() << "Local folders ready. resourceId" << Settings::resourceId() + << "outbox id" << outbox.id() << "sentMail id" << sentMail.id(); + + Q_ASSERT( !ready ); + ready = true; + preparing = false; + Settings::self()->writeConfig(); + emit instance->foldersReady(); + } +} + +void LocalFoldersPrivate::connectMonitor() +{ + Q_ASSERT( preparing ); // but I may not be the main instance + Q_ASSERT( monitor == 0 ); + monitor = new Monitor( instance ); + monitor->setResourceMonitored( Settings::resourceId().toAscii() ); + QObject::connect( monitor, SIGNAL( collectionRemoved( Akonadi::Collection ) ), + instance, SLOT( schedulePrepare() ) ); + kDebug() << "Connected monitor."; + fetchCollections(); +} + +void LocalFoldersPrivate::fetchCollections() +{ + Q_ASSERT( preparing ); // but I may not be the main instance + kDebug() << "Fetching collections in maildir resource."; + + CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive ); + job->setResource( Settings::resourceId() ); // limit search + QObject::connect( job, SIGNAL( result( KJob * ) ), + instance, SLOT( collectionFetchResult( KJob * ) ) ); +} + +void LocalFoldersPrivate::resourceCreateResult( KJob *job ) +{ + Q_ASSERT( isMainInstance ); + Q_ASSERT( preparing ); + if( job->error() ) { + kFatal() << "AgentInstanceCreateJob failed to make a maildir resource for us."; + } + + AgentInstanceCreateJob *createJob = static_cast( job ); + AgentInstance agent = createJob->instance(); + Settings::setResourceId( agent.identifier() ); + kDebug() << "Created maildir resource with id" << Settings::resourceId(); + + // configure the resource + agent.setName( i18n( "Local Mail Folders" ) ); + QDBusInterface conf( QString::fromLatin1( "org.freedesktop.Akonadi.Resource." ) + Settings::resourceId(), + QString::fromLatin1( "/Settings" ), + QString::fromLatin1( "org.kde.Akonadi.Maildir.Settings" ) ); + QDBusReply reply = conf.call( QString::fromLatin1( "setPath" ), + KGlobal::dirs()->localxdgdatadir() + QString::fromLatin1( "mail" ) ); + if( !reply.isValid() ) { + kFatal() << "Failed to set the root maildir."; + } + agent.reconfigure(); + + // sync the resource + ResourceSynchronizationJob *sjob = new ResourceSynchronizationJob( agent ); + QObject::connect( sjob, SIGNAL( result( KJob* ) ), + instance, SLOT( resourceSyncResult( KJob* ) ) ); + sjob->start(); // non-Akonadi +} + +void LocalFoldersPrivate::resourceSyncResult( KJob *job ) +{ + Q_ASSERT( isMainInstance ); + Q_ASSERT( preparing ); + if( job->error() ) { + kFatal() << "ResourceSynchronizationJob failed."; + } + + connectMonitor(); +} + +void LocalFoldersPrivate::collectionCreateResult( KJob *job ) +{ + Q_ASSERT( isMainInstance ); + if( job->error() ) { + kFatal() << "CollectionCreateJob failed to make a collection for us."; + } + + CollectionCreateJob *createJob = static_cast( job ); + if( job == outboxJob ) { + outboxJob = 0; + outbox = createJob->collection(); + kDebug() << "Created outbox collection with id" << outbox.id(); + } else if( job == sentMailJob ) { + sentMailJob = 0; + sentMail = createJob->collection(); + kDebug() << "Created sent-mail collection with id" << sentMail.id(); + } else { + kFatal() << "Got a result for a job I don't know about."; + } + + if( outboxJob == 0 && sentMailJob == 0 ) { + // Done creating. Refetch everything. + fetchCollections(); + } +} + +void LocalFoldersPrivate::collectionFetchResult( KJob *job ) +{ + Q_ASSERT( preparing ); // but I may not be the main instance + CollectionFetchJob *fetchJob = static_cast( job ); + Collection::List cols = fetchJob->collections(); + + kDebug() << "CollectionFetchJob fetched" << cols.count() << "collections."; + + outbox = Collection( -1 ); + sentMail = Collection( -1 ); + Q_FOREACH( const Collection &col, cols ) { + if( col.parent() == Collection::root().id() ) { + rootMaildir = col; + kDebug() << "Fetched root maildir collection."; + } else if( col.name() == QLatin1String( "outbox" ) ) { + Q_ASSERT( outbox.id() == -1 ); + outbox = col; + kDebug() << "Fetched outbox collection."; + } else if( col.name() == QLatin1String( "sent-mail" ) ) { + Q_ASSERT( sentMail.id() == -1 ); + sentMail = col; + kDebug() << "Fetched sent-mail collection."; + } else { + kWarning() << "Extraneous collection" << col.name() << "with id" + << col.id() << "found."; + } + } + + if( !rootMaildir.isValid() ) { + kFatal() << "Failed to fetch root maildir collection."; + } + + createCollectionsIfNeeded(); +} + + +#include "localfolders.moc" diff --git a/akonadi/kmime/localfolders.h b/akonadi/kmime/localfolders.h new file mode 100644 index 000000000..cad2f0923 --- /dev/null +++ b/akonadi/kmime/localfolders.h @@ -0,0 +1,118 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_LOCALFOLDERS_H +#define AKONADI_LOCALFOLDERS_H + +#include + +#include "akonadi-kmime_export.h" + +class KJob; + +namespace Akonadi { + class Collection; +} + + +namespace Akonadi { + + +class LocalFoldersPrivate; + + +/** + Creates and monitors the Outbox and Sent-Mail collections for the mail + dispatcher agent. + + The first time it is used, or when you call fetch(), this class checks for + the following: + * a maildir resource with name 'Local Mail Folders' + * 'outbox' and 'sent-mail' collections under that resource + If they do not exist, this class creates them, and then emits foldersReady(). + + Do not store the collections returned by outbox() and sentMail(). They can + become invalid if e.g. the user deletes them and LocalFolders creates them + again, so you should always use LocalFolders::outbox() instead of storing + its return value. + +*/ +class AKONADI_KMIME_EXPORT LocalFolders : public QObject +{ + Q_OBJECT + + public: + /** + Returns the LocalFolders instance. + Does a fetch() when first called. + */ + static LocalFolders *self(); + + /** + Begins creating / fetching the resource and collections. + Emits foldersReady() when done. + */ + void fetch(); + + /** + Returns whether the outbox and sent-mail collections have been + fetched and are ready to be used via outbox() and sentMail(). + */ + bool isReady() const; + + public Q_SLOTS: + /** + Returns the outbox collection. + */ + Akonadi::Collection outbox() const; + + /** + Returns the sent-mail collection. + */ + Akonadi::Collection sentMail() const; + + Q_SIGNALS: + /** + Emitted when the outbox and sent-mail collections have been fetched and + are ready to be used via outbox() and sentMail(). + */ + void foldersReady(); + + private: + friend class LocalFoldersPrivate; + + // singleton class; the only instance resides in sInstance->instance + LocalFolders( LocalFoldersPrivate *dd ); + + LocalFoldersPrivate *const d; + + Q_PRIVATE_SLOT( d, void prepare() ) + Q_PRIVATE_SLOT( d, void schedulePrepare() ) + Q_PRIVATE_SLOT( d, void resourceCreateResult( KJob * ) ) + Q_PRIVATE_SLOT( d, void resourceSyncResult( KJob * ) ) + Q_PRIVATE_SLOT( d, void collectionCreateResult( KJob * ) ) + Q_PRIVATE_SLOT( d, void collectionFetchResult( KJob * ) ) + +}; + + +} + + +#endif diff --git a/akonadi/kmime/localfolders.kcfg b/akonadi/kmime/localfolders.kcfg new file mode 100644 index 000000000..3b6ecc510 --- /dev/null +++ b/akonadi/kmime/localfolders.kcfg @@ -0,0 +1,22 @@ + + + + + + + Id of the maildir resource containing the outbox and sent-mail collections. + "" + + + + -1 + + + + -1 + + + diff --git a/akonadi/kmime/localfolderssettings.kcfgc b/akonadi/kmime/localfolderssettings.kcfgc new file mode 100644 index 000000000..d630d162e --- /dev/null +++ b/akonadi/kmime/localfolderssettings.kcfgc @@ -0,0 +1,7 @@ +File=localfolders.kcfg +ClassName=LocalFoldersSettings +NameSpace=Akonadi +Singleton=true +ItemAccessors=true +Mutators=true +SetUserTextx=true diff --git a/akonadi/kmime/resourcesynchronizationjob.h b/akonadi/kmime/resourcesynchronizationjob.h new file mode 100644 index 000000000..6ed04db2f --- /dev/null +++ b/akonadi/kmime/resourcesynchronizationjob.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009 Volker Krause + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef AKONADI_RESOURCESYNCJOB_H +#define AKONADI_RESOURCESYNCJOB_H + +#include + +namespace Akonadi { + +class AgentInstance; +class ResourceSynchronizationJobPrivate; + +/** + Synchronizes a given resource. +*/ +class ResourceSynchronizationJob : public KJob +{ + Q_OBJECT + public: + /** + Create a new synchronization job for the given agent. + */ + ResourceSynchronizationJob( const AgentInstance &instance, QObject *parent = 0 ); + + /** + Destructor. + */ + ~ResourceSynchronizationJob(); + + /* reimpl */ + void start(); + + private slots: + void slotSynchronized(); + void slotTimeout(); + + private: + ResourceSynchronizationJobPrivate* const d; +}; + +} + +#endif diff --git a/akonadi/kmime/tests/CMakeLists.txt b/akonadi/kmime/tests/CMakeLists.txt new file mode 100644 index 000000000..4bba839bf --- /dev/null +++ b/akonadi/kmime/tests/CMakeLists.txt @@ -0,0 +1,54 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +# TODO apparently QTEST_AKONADIMAIN doesn't like NO_CASTs... +remove_definitions( -DQT_NO_CAST_FROM_ASCII ) +remove_definitions( -DQT_NO_CAST_TO_ASCII ) + +set( requester_srcs foldersrequester.cpp ) +kde4_add_executable( requester TEST ${requester_srcs} ) +target_link_libraries( requester akonadi-kmime ) +# for racetest +set( requester_exe "QLatin1String( \"${CMAKE_CURRENT_BINARY_DIR}/requester\" )" ) +add_definitions( -DREQUESTER_EXE='${requester_exe}' ) + + + + +macro(add_akonadi_isolated_test _source) + get_filename_component(_targetName ${_source} NAME_WE) + set(_srcList ${_source} ) + + kde4_add_executable(${_targetName} TEST ${_srcList}) + target_link_libraries(${_targetName} + ${QT_QTTEST_LIBRARY} + ${QT_QTGUI_LIBRARY} + ${KDE4_AKONADI_LIBS} + ${KDE4_KDECORE_LIBS} + ${KDE4_MAILTRANSPORT_LIBS} + ${KDE4_KMIME_LIBS} + ${QT_QTCORE_LIBRARY} + ${QT_QTDBUS_LIBRARY} + akonadi-kmime + ) + + # based on kde4_add_unit_test + if (WIN32) + get_target_property( _loc ${_targetName} LOCATION ) + set(_executable ${_loc}.bat) + else (WIN32) + set(_executable ${EXECUTABLE_OUTPUT_PATH}/${_targetName}) + endif (WIN32) + if (UNIX) + set(_executable ${_executable}.shell) + endif (UNIX) + + find_program(_testrunner akonaditest) + + add_test( akonadikmime-${_targetName} ${_testrunner} -c ${CMAKE_CURRENT_SOURCE_DIR}/unittestenv/config.xml ${_executable} ) +endmacro(add_akonadi_isolated_test) + + + + +add_akonadi_isolated_test( racetest.cpp ) + diff --git a/akonadi/kmime/tests/TODO b/akonadi/kmime/tests/TODO new file mode 100644 index 000000000..a571f6720 --- /dev/null +++ b/akonadi/kmime/tests/TODO @@ -0,0 +1,6 @@ +LocalFolders: +* LocalFolders should be able to recover from any of the following situations: +- maildir resource and collection were removed +- maildir resource was removed, but collections are still there +- one or both collections have been removed + diff --git a/akonadi/kmime/tests/foldersrequester.cpp b/akonadi/kmime/tests/foldersrequester.cpp new file mode 100644 index 000000000..2abff4c00 --- /dev/null +++ b/akonadi/kmime/tests/foldersrequester.cpp @@ -0,0 +1,67 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "foldersrequester.h" + +#include +#include +#include + +#include +#include +#include + +using namespace Akonadi; + + +Requester::Requester() +{ + Control::start(); + + connect( LocalFolders::self(), SIGNAL( foldersReady() ), + this, SLOT( checkFolders() ) ); + LocalFolders::self()->fetch(); +} + +void Requester::checkFolders() +{ + Collection outbox = LocalFolders::self()->outbox(); + Collection sentMail = LocalFolders::self()->sentMail(); + + kDebug() << "Got outbox" << outbox.id() << "sent-mail" << sentMail.id(); + + if( !outbox.isValid() || !sentMail.isValid() ) { + KApplication::exit( 1 ); + } else { + KApplication::exit( 2 ); + } +} + +int main( int argc, char **argv ) +{ + KCmdLineArgs::init( argc, argv, "foldersrequester", 0, + ki18n( "foldersrequester" ), "0", + ki18n( "An app that requests LocalFolders" ) ); + KApplication app; + new Requester(); + return app.exec(); +} + + +#include "foldersrequester.moc" diff --git a/akonadi/kmime/tests/foldersrequester.h b/akonadi/kmime/tests/foldersrequester.h new file mode 100644 index 000000000..cee1c7bc2 --- /dev/null +++ b/akonadi/kmime/tests/foldersrequester.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef FOLDERSREQUESTER_H +#define FOLDERSREQUESTER_H + +#include + +/** + This class requests the LocalFolders, then exits the app with a status of 2 + if they were delivered OK, or 1 if they were not. + + NOTE: The non-standard exit status 2 in case of success is to make feel more + comfortable than checking for zero (I actually had a bug causing it to always + return zero). +*/ +class Requester : public QObject +{ + Q_OBJECT + + public: + Requester(); + + private slots: + void checkFolders(); + +}; + + +#endif diff --git a/akonadi/kmime/tests/racetest.cpp b/akonadi/kmime/tests/racetest.cpp new file mode 100644 index 000000000..76c76ce10 --- /dev/null +++ b/akonadi/kmime/tests/racetest.cpp @@ -0,0 +1,164 @@ +/* + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "racetest.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_SECONDS 20 +#define MAXCOUNT 10 +// NOTE: REQUESTER_EXE is defined by cmake. + +using namespace Akonadi; + + +void RaceTest::initTestCase() +{ + QVERIFY( Control::start() ); + QTest::qWait( 1000 ); // give the MDA time to start so that we can kill it in peace +} + +void RaceTest::testMultipleProcesses_data() +{ + QTest::addColumn( "count" ); // how many processes to create + QTest::addColumn( "delay" ); // number of ms to wait before starting next process + + QTest::newRow( "1-nodelay" ) << 1 << 0; + QTest::newRow( "2-nodelay" ) << 2 << 0; + QTest::newRow( "5-nodelay" ) << 5 << 0; + QTest::newRow( "10-nodelay" ) << 10 << 0; + QTest::newRow( "2-shortdelay" ) << 2 << 100; + QTest::newRow( "5-shortdelay" ) << 5 << 100; + QTest::newRow( "10-shortdelay" ) << 10 << 100; + QTest::newRow( "2-longdelay" ) << 2 << 1000; + QTest::newRow( "5-longdelay" ) << 5 << 1000; + QTest::newRow( "5-verylongdelay" ) << 5 << 4000; + Q_ASSERT( 10 <= MAXCOUNT ); +} + +void RaceTest::testMultipleProcesses() +{ + QFETCH( int, count ); + QFETCH( int, delay ); + + killZombies(); + + // Remove all maildir instances (at most 1 really) and MDAs (which use LocalFolders). + // (This is to ensure that one of *our* instances is the main instance.) + AgentType::List types; + types.append( AgentManager::self()->type( QLatin1String( "akonadi_maildir_resource" ) ) ); + types.append( AgentManager::self()->type( QLatin1String( "akonadi_maildispatcher_agent" ) ) ); + AgentInstance::List instances = AgentManager::self()->instances(); + foreach( const AgentInstance &instance, instances ) { + if( types.contains( instance.type() ) ) { + kDebug() << "Removing instance of type" << instance.type().identifier(); + AgentManager::self()->removeInstance( instance ); + QTest::kWaitForSignal( AgentManager::self(), SIGNAL( instanceRemoved( const Akonadi::AgentInstance& ) ) ); + } + } + instances = AgentManager::self()->instances(); + foreach( const AgentInstance &instance, instances ) { + QVERIFY( !types.contains( instance.type() ) ); + } + + QSignalSpy *errorSpy[ MAXCOUNT ]; + QSignalSpy *finishedSpy[ MAXCOUNT ]; + for( int i = 0; i < count; i++ ) { + kDebug() << "Starting process" << i + 1 << "of" << count; + KProcess *proc = new KProcess; + procs.append( proc ); + proc->setProgram( REQUESTER_EXE ); + errorSpy[i] = new QSignalSpy( proc, SIGNAL( error( QProcess::ProcessError ) ) ); + finishedSpy[i] = new QSignalSpy( proc, SIGNAL( finished( int, QProcess::ExitStatus ) ) ); + proc->start(); + QTest::qWait( delay ); + } + kDebug() << "Launched" << count << "processes."; + + int seconds = 0; + int error, finished; + while( true ) { + seconds++; + QTest::qWait( 1000 ); + + error = 0; + finished = 0; + for( int i = 0; i < count; i++ ) { + if( errorSpy[i]->count() > 0 ) + error++; + if( finishedSpy[i]->count() > 0 ) + finished++; + } + kDebug() << seconds << "seconds elapsed." << error << "processes error'd," + << finished << "processes finished."; + + if( error + finished >= count ) + break; + +#if 0 + if( seconds >= TIMEOUT_SECONDS ) { + kDebug() << "Timeout, gdb master!"; + QTest::qWait( 1000*1000 ); + } +#endif + QVERIFY2( seconds < TIMEOUT_SECONDS, "Timeout" ); + } + + QCOMPARE( error, 0 ); + QCOMPARE( finished, count ); + for( int i = 0; i < count; i++ ) { + kDebug() << "Checking exit status of process" << i + 1 << "of" << count; + QCOMPARE( finishedSpy[i]->count(), 1 ); + QList args = finishedSpy[i]->takeFirst(); + QCOMPARE( args[0].toInt(), 2 ); + } + + while( !procs.isEmpty() ) { + KProcess *proc = procs.takeFirst(); + QCOMPARE( proc->exitStatus(), QProcess::NormalExit ); + QCOMPARE( proc->exitCode(), 2 ); + delete proc; + } + QVERIFY( procs.isEmpty() ); +} + +void RaceTest::killZombies() +{ + while( !procs.isEmpty() ) { + // These processes probably hung, and will never recover, so we need to kill them. + // (This happens if the last test failed.) + kDebug() << "Killing zombies from the past."; + KProcess *proc = procs.takeFirst(); + proc->kill(); + proc->deleteLater(); + } +} + +QTEST_AKONADIMAIN( RaceTest, NoGUI ) + +#include "racetest.moc" diff --git a/akonadi/kmime/tests/racetest.h b/akonadi/kmime/tests/racetest.h new file mode 100644 index 000000000..b075af4b0 --- /dev/null +++ b/akonadi/kmime/tests/racetest.h @@ -0,0 +1,51 @@ +/* + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef RACETEST_H +#define RACETEST_H + +#include +#include + +class KProcess; + + +/** + This tests the ability of LocalFolders to exist peacefully in multiple processes. + The main instance (normally the first one created) is supposed to create the + resource and collections, while the other instances are supposed to wait and + then just fetch the collections. + */ +class RaceTest : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase(); + void testMultipleProcesses_data(); + void testMultipleProcesses(); + void killZombies(); + + private: + QList procs; + +}; + + +#endif diff --git a/akonadi/kmime/tests/unittestenv/config.xml b/akonadi/kmime/tests/unittestenv/config.xml new file mode 100644 index 000000000..47d7cb0b8 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/config.xml @@ -0,0 +1,5 @@ + + kdehome + xdgconfig + xdglocal + diff --git a/akonadi/kmime/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc b/akonadi/kmime/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc new file mode 100644 index 000000000..1cac492a3 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc @@ -0,0 +1,3 @@ +[ProcessedDefaults] +defaultaddressbook=done +defaultcalendar=done diff --git a/akonadi/kmime/tests/unittestenv/kdehome/share/config/kdebugrc b/akonadi/kmime/tests/unittestenv/kdehome/share/config/kdebugrc new file mode 100644 index 000000000..32317f745 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/kdehome/share/config/kdebugrc @@ -0,0 +1,110 @@ +[0] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5250] +InfoOutput=2 + +[5251] +InfoOutput=2 + +[5252] +InfoOutput=2 + +[5253] +InfoOutput=2 + +[5254] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5255] +InfoOutput=2 + +[5256] +InfoOutput=2 + +[5257] +InfoOutput=2 + +[5258] +InfoOutput=2 + +[5259] +InfoOutput=2 + +[5260] +InfoOutput=2 + +[5261] +InfoOutput=2 + +[5262] +InfoOutput=2 + +[5263] +InfoOutput=2 + +[5264] +InfoOutput=2 + +[5265] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5266] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5295] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5324] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[7129] +InfoOutput=2 diff --git a/akonadi/kmime/tests/unittestenv/kdehome/share/config/kwalletrc b/akonadi/kmime/tests/unittestenv/kdehome/share/config/kwalletrc new file mode 100644 index 000000000..8ba29ca12 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/kdehome/share/config/kwalletrc @@ -0,0 +1,2 @@ +[Wallet] +Enabled=false diff --git a/akonadi/kmime/tests/unittestenv/kdehome/share/config/mailtransports b/akonadi/kmime/tests/unittestenv/kdehome/share/config/mailtransports new file mode 100644 index 000000000..bc9c63f07 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/kdehome/share/config/mailtransports @@ -0,0 +1,17 @@ +[$Version] +update_info=mailtransports.upd:initial-kmail-migration,mailtransports.upd:initial-knode-migration + +[General] +default-transport=549190884 + +[Transport 549190884] +auth=true +encryption=SSL +host=smtp.gmail.com +id=549190884 +name=idanoka2-stored +password=ᄒᄡᄚᄆᄒᄏᄊᆱᄎᆲᆱ +port=465 +storepass=true +user=idanoka2@gmail.com + diff --git a/akonadi/kmime/tests/unittestenv/kdehome/share/config/qttestrc b/akonadi/kmime/tests/unittestenv/kdehome/share/config/qttestrc new file mode 100644 index 000000000..2e2f28ea1 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/kdehome/share/config/qttestrc @@ -0,0 +1,2 @@ +[Notification Messages] +WalletMigrate=false diff --git a/akonadi/kmime/tests/unittestenv/xdgconfig/akonadi/akonadiserverrc b/akonadi/kmime/tests/unittestenv/xdgconfig/akonadi/akonadiserverrc new file mode 100644 index 000000000..7f738ce21 --- /dev/null +++ b/akonadi/kmime/tests/unittestenv/xdgconfig/akonadi/akonadiserverrc @@ -0,0 +1,4 @@ +[%General] + +[Search] +Manager=Dummy