diff --git a/kcal/incidence.cpp b/kcal/incidence.cpp index 1481a020f..8a1a01b3b 100644 --- a/kcal/incidence.cpp +++ b/kcal/incidence.cpp @@ -1,950 +1,952 @@ /* This file is part of the kcal library. Copyright (c) 2001 Cornelius Schumacher Copyright (C) 2003-2004 Reinhold Kainhofer 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. */ /** @file This file is part of the API for handling calendar data and defines the Incidence class. @brief Provides the class common to non-FreeBusy (Events, To-dos, Journals) calendar components known as incidences. @author Cornelius Schumacher \ @author Reinhold Kainhofer \ */ #include "incidence.h" #include "calformat.h" #include #include #include #include #include // for Qt::escape() and Qt::mightBeRichText() using namespace KCal; /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE class KCal::Incidence::Private { public: Private() : mRecurrence( 0 ), mStatus( StatusNone ), mSecrecy( SecrecyPublic ), mPriority( 0 ), mRelatedTo( 0 ) { mAlarms.setAutoDelete( true ); mAttachments.setAutoDelete( true ); } Private( const Private &p ) : mCreated( p.mCreated ), mRevision( p.mRevision ), mDescription( p.mDescription ), mDescriptionIsRich( p.mDescriptionIsRich ), mSummary( p.mSummary ), mSummaryIsRich( p.mSummaryIsRich ), mLocation( p.mLocation ), mLocationIsRich( p.mLocationIsRich ), mCategories( p.mCategories ), mResources( p.mResources ), mStatus( p.mStatus ), mStatusString( p.mStatusString ), mSecrecy( p.mSecrecy ), mPriority( p.mPriority ), mSchedulingID( p.mSchedulingID ), mRelatedTo( 0 ), mRelatedToUid( p.mRelatedToUid ) // TODO: reenable attributes currently commented out. // Incidence *mRelatedTo; Incidence *mRelatedTo; // Incidence::List mRelations; Incidence::List mRelations; { mAlarms.setAutoDelete( true ); mAttachments.setAutoDelete( true ); } void clear() { mAlarms.clearAll(); mAttachments.clearAll(); delete mRecurrence; } KDateTime mCreated; // creation datetime int mRevision; // revision number QString mDescription; // description string bool mDescriptionIsRich; // description string is richtext. QString mSummary; // summary string bool mSummaryIsRich; // summary string is richtext. QString mLocation; // location string bool mLocationIsRich; // location string is richtext. QStringList mCategories; // category list mutable Recurrence *mRecurrence; // recurrence Attachment::List mAttachments; // attachments list Alarm::List mAlarms; // alarms list QStringList mResources; // resources list (not calendar resources) Status mStatus; // status QString mStatusString; // status string, for custom status Secrecy mSecrecy; // secrecy int mPriority; // priority: 1 = highest, 2 = less, etc. QString mSchedulingID; // ID for scheduling mails Incidence *mRelatedTo; // incidence this is related to QString mRelatedToUid; // incidence (by Uid) this is related to Incidence::List mRelations; // a list of incidences this is related to }; //@endcond Incidence::Incidence() : IncidenceBase(), d( new KCal::Incidence::Private ) { recreate(); } Incidence::Incidence( const Incidence &i ) : IncidenceBase( i ), Recurrence::RecurrenceObserver(), d( new KCal::Incidence::Private( *i.d ) ) { init( i ); } void Incidence::init( const Incidence &i ) { // TODO: reenable attributes currently commented out. d->mRevision = i.d->mRevision; d->mCreated = i.d->mCreated; d->mDescription = i.d->mDescription; d->mSummary = i.d->mSummary; d->mCategories = i.d->mCategories; // Incidence *mRelatedTo; Incidence *mRelatedTo; d->mRelatedTo = 0; d->mRelatedToUid = i.d->mRelatedToUid; // Incidence::List mRelations; Incidence::List mRelations; d->mResources = i.d->mResources; d->mStatusString = i.d->mStatusString; d->mStatus = i.d->mStatus; d->mSecrecy = i.d->mSecrecy; d->mPriority = i.d->mPriority; d->mLocation = i.d->mLocation; // Alarms and Attachments are stored in ListBase<...>, which is a QValueList<...*>. // We need to really duplicate the objects stored therein, otherwise deleting // i will also delete all attachments from this object (setAutoDelete...) foreach ( Alarm *alarm, i.d->mAlarms ) { Alarm *b = new Alarm( *alarm ); b->setParent( this ); d->mAlarms.append( b ); } foreach ( Attachment *attachment, i.d->mAttachments ) { Attachment *a = new Attachment( *attachment ); d->mAttachments.append( a ); } if ( i.d->mRecurrence ) { d->mRecurrence = new Recurrence( *( i.d->mRecurrence ) ); d->mRecurrence->addObserver( this ); } else { d->mRecurrence = 0; } } Incidence::~Incidence() { Incidence::List relations = d->mRelations; foreach ( Incidence *incidence, relations ) { if ( incidence->relatedTo() == this ) { incidence->setRelatedTo( 0 ); } } if ( relatedTo() ) { relatedTo()->removeRelation( this ); } delete d->mRecurrence; delete d; } //@cond PRIVATE // A string comparison that considers that null and empty are the same static bool stringCompare( const QString &s1, const QString &s2 ) { return ( s1.isEmpty() && s2.isEmpty() ) || ( s1 == s2 ); } //@endcond Incidence &Incidence::operator=( const Incidence &other ) { // check for self assignment if ( &other == this ) { return *this; } d->clear(); //TODO: should relations be cleared out, as in destructor??? IncidenceBase::operator=( other ); init( other ); return *this; } bool Incidence::operator==( const Incidence &i2 ) const { if ( alarms().count() != i2.alarms().count() ) { return false; // no need to check further } Alarm::List::ConstIterator a1 = alarms().constBegin(); Alarm::List::ConstIterator a1end = alarms().constEnd(); Alarm::List::ConstIterator a2 = i2.alarms().begin(); Alarm::List::ConstIterator a2end = i2.alarms().constEnd(); for ( ; a1 != a1end && a2 != a2end; ++a1, ++a2 ) { if ( **a1 == **a2 ) { continue; } else { return false; } } if ( !IncidenceBase::operator==( i2 ) ) { return false; } bool recurrenceEqual = ( d->mRecurrence == 0 && i2.d->mRecurrence == 0 ); if ( !recurrenceEqual ) { recurrenceEqual = d->mRecurrence != 0 && i2.d->mRecurrence != 0 && *d->mRecurrence == *i2.d->mRecurrence; } return recurrenceEqual && created() == i2.created() && stringCompare( description(), i2.description() ) && stringCompare( summary(), i2.summary() ) && categories() == i2.categories() && // no need to compare mRelatedTo stringCompare( relatedToUid(), i2.relatedToUid() ) && relations() == i2.relations() && attachments() == i2.attachments() && resources() == i2.resources() && d->mStatus == i2.d->mStatus && ( d->mStatus == StatusNone || stringCompare( d->mStatusString, i2.d->mStatusString ) ) && secrecy() == i2.secrecy() && priority() == i2.priority() && stringCompare( location(), i2.location() ) && stringCompare( schedulingID(), i2.schedulingID() ); } void Incidence::recreate() { KDateTime nowUTC = KDateTime::currentUtcDateTime(); setCreated( nowUTC ); setUid( CalFormat::createUniqueId() ); setSchedulingID( QString() ); setRevision( 0 ); setLastModified( nowUTC ); } void Incidence::setReadOnly( bool readOnly ) { IncidenceBase::setReadOnly( readOnly ); if ( d->mRecurrence ) { d->mRecurrence->setRecurReadOnly( readOnly ); } } void Incidence::setAllDay( bool allDay ) { if ( mReadOnly ) { return; } if ( recurrence() ) { recurrence()->setAllDay( allDay ); } IncidenceBase::setAllDay( allDay ); } void Incidence::setCreated( const KDateTime &created ) { if ( mReadOnly ) { return; } d->mCreated = created.toUtc(); // FIXME: Shouldn't we call updated for the creation date, too? // updated(); } KDateTime Incidence::created() const { return d->mCreated; } void Incidence::setRevision( int rev ) { if ( mReadOnly ) { return; } d->mRevision = rev; updated(); } int Incidence::revision() const { return d->mRevision; } void Incidence::setDtStart( const KDateTime &dt ) { if ( d->mRecurrence ) { d->mRecurrence->setStartDateTime( dt ); d->mRecurrence->setAllDay( allDay() ); } IncidenceBase::setDtStart( dt ); } KDateTime Incidence::dtEnd() const { return KDateTime(); } void Incidence::shiftTimes( const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec ) { IncidenceBase::shiftTimes( oldSpec, newSpec ); if ( d->mRecurrence ) { d->mRecurrence->shiftTimes( oldSpec, newSpec ); } for ( int i = 0, end = d->mAlarms.count(); i < end; ++i ) { d->mAlarms[i]->shiftTimes( oldSpec, newSpec ); } } void Incidence::setDescription( const QString &description, bool isRich ) { if ( mReadOnly ) { return; } d->mDescription = description; d->mDescriptionIsRich = isRich; updated(); } void Incidence::setDescription( const QString &description ) { setDescription( description, Qt::mightBeRichText( description ) ); } QString Incidence::description() const { return d->mDescription; } QString Incidence::richDescription() const { if ( descriptionIsRich() ) { return d->mDescription; } else { return Qt::escape( d->mDescription ).replace( '\n', "
" ); } } bool Incidence::descriptionIsRich() const { return d->mDescriptionIsRich; } void Incidence::setSummary( const QString &summary, bool isRich ) { if ( mReadOnly ) { return; } d->mSummary = summary; d->mSummaryIsRich = isRich; updated(); } void Incidence::setSummary( const QString &summary ) { setSummary( summary, Qt::mightBeRichText( summary ) ); } QString Incidence::summary() const { return d->mSummary; } QString Incidence::richSummary() const { if ( summaryIsRich() ) { return d->mSummary; } else { return Qt::escape( d->mSummary ).replace( '\n', "
" ); } } bool Incidence::summaryIsRich() const { return d->mSummaryIsRich; } void Incidence::setCategories( const QStringList &categories ) { if ( mReadOnly ) { return; } d->mCategories = categories; updated(); } void Incidence::setCategories( const QString &catStr ) { if ( mReadOnly ) { return; } d->mCategories.clear(); if ( catStr.isEmpty() ) { return; } d->mCategories = catStr.split( ',' ); QStringList::Iterator it; for ( it = d->mCategories.begin();it != d->mCategories.end(); ++it ) { *it = (*it).trimmed(); } updated(); } QStringList Incidence::categories() const { return d->mCategories; } QString Incidence::categoriesStr() const { return d->mCategories.join( "," ); } void Incidence::setRelatedToUid( const QString &relatedToUid ) { if ( d->mRelatedToUid == relatedToUid ) { return; } d->mRelatedToUid = relatedToUid; updated(); } QString Incidence::relatedToUid() const { return d->mRelatedToUid; } void Incidence::setRelatedTo( Incidence *incidence ) { if ( d->mRelatedTo == incidence ) { return; } if ( d->mRelatedTo ) { d->mRelatedTo->removeRelation( this ); } d->mRelatedTo = incidence; if ( d->mRelatedTo ) { d->mRelatedTo->addRelation( this ); if ( d->mRelatedTo->uid() != d->mRelatedToUid ) { setRelatedToUid( d->mRelatedTo->uid() ); } } else { setRelatedToUid( QString() ); } } Incidence *Incidence::relatedTo() const { return d->mRelatedTo; } Incidence::List Incidence::relations() const { return d->mRelations; } void Incidence::addRelation( Incidence *incidence ) { if ( !d->mRelations.contains( incidence ) ) { d->mRelations.append( incidence ); } } void Incidence::removeRelation( Incidence *incidence ) { d->mRelations.removeRef( incidence ); - d->mRelatedToUid.clear(); + if ( d->mRelatedToUid == incidence->uid() ) { + d->mRelatedToUid.clear(); + } // if (incidence->getRelatedTo() == this) incidence->setRelatedTo(0); } // %%%%%%%%%%%% Recurrence-related methods %%%%%%%%%%%%%%%%%%%% Recurrence *Incidence::recurrence() const { if ( !d->mRecurrence ) { d->mRecurrence = new Recurrence(); d->mRecurrence->setStartDateTime( IncidenceBase::dtStart() ); d->mRecurrence->setAllDay( allDay() ); d->mRecurrence->setRecurReadOnly( mReadOnly ); d->mRecurrence->addObserver( const_cast( this ) ); } return d->mRecurrence; } void Incidence::clearRecurrence() { delete d->mRecurrence; d->mRecurrence = 0; } ushort Incidence::recurrenceType() const { if ( d->mRecurrence ) { return d->mRecurrence->recurrenceType(); } else { return Recurrence::rNone; } } bool Incidence::recurs() const { if ( d->mRecurrence ) { return d->mRecurrence->recurs(); } else { return false; } } bool Incidence::recursOn( const QDate &date, const KDateTime::Spec &timeSpec ) const { return d->mRecurrence && d->mRecurrence->recursOn( date, timeSpec ); } bool Incidence::recursAt( const KDateTime &qdt ) const { return d->mRecurrence && d->mRecurrence->recursAt( qdt ); } QList Incidence::startDateTimesForDate( const QDate &date, const KDateTime::Spec &timeSpec ) const { KDateTime start = dtStart(); KDateTime end = endDateRecurrenceBase(); QList result; // TODO_Recurrence: Also work if only due date is given... if ( !start.isValid() && ! end.isValid() ) { return result; } // if the incidence doesn't recur, KDateTime kdate( date, timeSpec ); if ( !recurs() ) { if ( !( start > kdate || end < kdate ) ) { result << start; } return result; } int days = start.daysTo( end ); // Account for possible recurrences going over midnight, while the original event doesn't QDate tmpday( date.addDays( -days - 1 ) ); KDateTime tmp; while ( tmpday <= date ) { if ( recurrence()->recursOn( tmpday, timeSpec ) ) { QList times = recurrence()->recurTimesOn( tmpday, timeSpec ); foreach ( const QTime &time, times ) { tmp = KDateTime( tmpday, time, start.timeSpec() ); if ( endDateForStart( tmp ) >= kdate ) { result << tmp; } } } tmpday = tmpday.addDays( 1 ); } return result; } QList Incidence::startDateTimesForDateTime( const KDateTime &datetime ) const { KDateTime start = dtStart(); KDateTime end = endDateRecurrenceBase(); QList result; // TODO_Recurrence: Also work if only due date is given... if ( !start.isValid() && ! end.isValid() ) { return result; } // if the incidence doesn't recur, if ( !recurs() ) { if ( !( start > datetime || end < datetime ) ) { result << start; } return result; } int days = start.daysTo( end ); // Account for possible recurrences going over midnight, while the original event doesn't QDate tmpday( datetime.date().addDays( -days - 1 ) ); KDateTime tmp; while ( tmpday <= datetime.date() ) { if ( recurrence()->recursOn( tmpday, datetime.timeSpec() ) ) { // Get the times during the day (in start date's time zone) when recurrences happen QList times = recurrence()->recurTimesOn( tmpday, start.timeSpec() ); foreach ( const QTime &time, times ) { tmp = KDateTime( tmpday, time, start.timeSpec() ); if ( !( tmp > datetime || endDateForStart( tmp ) < datetime ) ) { result << tmp; } } } tmpday = tmpday.addDays( 1 ); } return result; } KDateTime Incidence::endDateForStart( const KDateTime &startDt ) const { KDateTime start = dtStart(); KDateTime end = endDateRecurrenceBase(); if ( !end.isValid() ) { return start; } if ( !start.isValid() ) { return end; } return startDt.addSecs( start.secsTo( end ) ); } void Incidence::addAttachment( Attachment *attachment ) { if ( mReadOnly || !attachment ) { return; } d->mAttachments.append( attachment ); updated(); } void Incidence::deleteAttachment( Attachment *attachment ) { d->mAttachments.removeRef( attachment ); } void Incidence::deleteAttachments( const QString &mime ) { Attachment::List::Iterator it = d->mAttachments.begin(); while ( it != d->mAttachments.end() ) { if ( (*it)->mimeType() == mime ) { d->mAttachments.removeRef( it ); } else { ++it; } } } Attachment::List Incidence::attachments() const { return d->mAttachments; } Attachment::List Incidence::attachments( const QString &mime ) const { Attachment::List attachments; Attachment::List::ConstIterator it; foreach ( Attachment *attachment, d->mAttachments ) { if ( attachment->mimeType() == mime ) { attachments.append( attachment ); } } return attachments; } void Incidence::clearAttachments() { d->mAttachments.clearAll(); } void Incidence::setResources( const QStringList &resources ) { if ( mReadOnly ) { return; } d->mResources = resources; updated(); } QStringList Incidence::resources() const { return d->mResources; } void Incidence::setPriority( int priority ) { if ( mReadOnly ) { return; } d->mPriority = priority; updated(); } int Incidence::priority() const { return d->mPriority; } void Incidence::setStatus( Incidence::Status status ) { if ( mReadOnly || status == StatusX ) { return; } d->mStatus = status; d->mStatusString.clear(); updated(); } void Incidence::setCustomStatus( const QString &status ) { if ( mReadOnly ) { return; } d->mStatus = status.isEmpty() ? StatusNone : StatusX; d->mStatusString = status; updated(); } Incidence::Status Incidence::status() const { return d->mStatus; } QString Incidence::statusStr() const { if ( d->mStatus == StatusX ) { return d->mStatusString; } return statusName( d->mStatus ); } QString Incidence::statusName( Incidence::Status status ) { switch ( status ) { case StatusTentative: return i18nc( "@item event is tentative", "Tentative" ); case StatusConfirmed: return i18nc( "@item event is definite", "Confirmed" ); case StatusCompleted: return i18nc( "@item to-do is complete", "Completed" ); case StatusNeedsAction: return i18nc( "@item to-do needs action", "Needs-Action" ); case StatusCanceled: return i18nc( "@item event orto-do is canceled; journal is removed", "Canceled" ); case StatusInProcess: return i18nc( "@item to-do is in process", "In-Process" ); case StatusDraft: return i18nc( "@item journal is in draft form", "Draft" ); case StatusFinal: return i18nc( "@item journal is in final form", "Final" ); case StatusX: case StatusNone: default: return QString(); } } void Incidence::setSecrecy( Incidence::Secrecy secrecy ) { if ( mReadOnly ) { return; } d->mSecrecy = secrecy; updated(); } Incidence::Secrecy Incidence::secrecy() const { return d->mSecrecy; } QString Incidence::secrecyStr() const { return secrecyName( d->mSecrecy ); } QString Incidence::secrecyName( Incidence::Secrecy secrecy ) { switch ( secrecy ) { case SecrecyPublic: return i18nc( "@item incidence access if for everyone", "Public" ); case SecrecyPrivate: return i18nc( "@item incidence access is by owner only", "Private" ); case SecrecyConfidential: return i18nc( "@item incidence access is by owner and a controlled group", "Confidential" ); default: return QString(); // to make compilers happy } } QStringList Incidence::secrecyList() { QStringList list; list << secrecyName( SecrecyPublic ); list << secrecyName( SecrecyPrivate ); list << secrecyName( SecrecyConfidential ); return list; } const Alarm::List &Incidence::alarms() const { return d->mAlarms; } Alarm *Incidence::newAlarm() { Alarm *alarm = new Alarm( this ); d->mAlarms.append( alarm ); return alarm; } void Incidence::addAlarm( Alarm *alarm ) { d->mAlarms.append( alarm ); updated(); } void Incidence::removeAlarm( Alarm *alarm ) { d->mAlarms.removeRef( alarm ); updated(); } void Incidence::clearAlarms() { d->mAlarms.clearAll(); updated(); } bool Incidence::isAlarmEnabled() const { foreach ( Alarm *alarm, d->mAlarms ) { if ( alarm->enabled() ) { return true; } } return false; } void Incidence::setLocation( const QString &location, bool isRich ) { if ( mReadOnly ) { return; } d->mLocation = location; d->mLocationIsRich = isRich; updated(); } void Incidence::setLocation( const QString &location ) { setLocation( location, Qt::mightBeRichText( location ) ); } QString Incidence::location() const { return d->mLocation; } QString Incidence::richLocation() const { if ( locationIsRich() ) { return d->mLocation; } else { return Qt::escape( d->mLocation ).replace( '\n', "
" ); } } bool Incidence::locationIsRich() const { return d->mLocationIsRich; } void Incidence::setSchedulingID( const QString &sid ) { d->mSchedulingID = sid; } QString Incidence::schedulingID() const { if ( d->mSchedulingID.isNull() ) { // Nothing set, so use the normal uid return uid(); } return d->mSchedulingID; } /** Observer interface for the recurrence class. If the recurrence is changed, this method will be called for the incidence the recurrence object belongs to. */ void Incidence::recurrenceUpdated( Recurrence *recurrence ) { if ( recurrence == d->mRecurrence ) { updated(); } } diff --git a/kcal/tests/CMakeLists.txt b/kcal/tests/CMakeLists.txt index 679f6f2b1..018ca3e8f 100644 --- a/kcal/tests/CMakeLists.txt +++ b/kcal/tests/CMakeLists.txt @@ -1,120 +1,121 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ${CMAKE_SOURCE_DIR}/kcal ${LIBICAL_INCLUDE_DIRS} ) MACRO(KCAL_UNIT_TESTS) FOREACH(_testname ${ARGN}) kde4_add_unit_test(${_testname} NOGUI ${_testname}.cpp) target_link_libraries(${_testname} ${KDE4_KDECORE_LIBS} kcal ${QT_QTTEST_LIBRARY} ${QT_QTGUI_LIBRARY} ${LIBICAL_LIBRARIES}) ENDFOREACH(_testname) ENDMACRO(KCAL_UNIT_TESTS) MACRO(KCAL_EXECUTABLE_TESTS) FOREACH(_testname ${ARGN}) kde4_add_executable(${_testname} NOGUI TEST ${_testname}.cpp) target_link_libraries(${_testname} ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} kcal ${QT_QTTEST_LIBRARY} kresources) ENDFOREACH(_testname) ENDMACRO(KCAL_EXECUTABLE_TESTS) KCAL_UNIT_TESTS( testalarm testassignmentvisitor testattachment testattendee testcalendarlocal testcalendarnull # testcalendarresources disable for now because it cause akonadi to start testcalfilter testduration testerrorformat testevent testfilestorage testfreebusy + testincidencerelation testjournal testkresult testperiod testperson testsortablelist testtodo ) # this test cannot work with msvc because libical should not be altered # and therefore we can't add KCAL_EXPORT there # it should work fine with mingw because of the auto-import feature if(NOT MSVC) KCAL_UNIT_TESTS(testicaltimezones) endif(NOT MSVC) KCAL_EXECUTABLE_TESTS( convertqtopia incidencestest loadcalendar fbrecurring readandwrite testfb testrecurprevious testrecurrence testrecurrencetype testrecurson testresource testtostring testvcalexport ) ########### next target ############### set(testfields_SRCS testfields.cpp) set(srcfile "${CMAKE_SOURCE_DIR}/kcal/tests/data/test_pilot.ics") add_definitions( -D_TESTINPUT="\\"${srcfile}\\"" ) kde4_add_executable(testfields TEST ${testfields_SRCS}) target_link_libraries(testfields ${KDE4_KDECORE_LIBS} kcal ) ########### Tests ####################### FILE( GLOB_RECURSE testFiles data/RecurrenceRule/*.ics ) FILE( GLOB_RECURSE compatFiles data/Compat/*.ics ) FILE( GLOB_RECURSE vCalFilesAsIcal data/vCalendar/*.ics ) FILE( GLOB_RECURSE vCalFiles data/vCalendar/*.vcs ) FOREACH( file ${testFiles} ) GET_FILENAME_COMPONENT( fn ${file} NAME) ADD_TEST( RecurNext-${fn} ${CMAKE_CURRENT_SOURCE_DIR}/runsingletestcase.pl ${EXECUTABLE_OUTPUT_PATH}/testrecurrence.shell "next" ${file} ) ENDFOREACH(file) FOREACH( file ${testFiles} ) GET_FILENAME_COMPONENT( fn ${file} NAME) ADD_TEST( RecurPrev-${fn} ${CMAKE_CURRENT_SOURCE_DIR}/runsingletestcase.pl ${EXECUTABLE_OUTPUT_PATH}/testrecurprevious.shell "prev" ${file} ) ENDFOREACH(file) FOREACH( file ${testFiles} ) GET_FILENAME_COMPONENT( fn ${file} NAME) ADD_TEST( RecursOn-${fn} ${CMAKE_CURRENT_SOURCE_DIR}/runsingletestcase.pl ${EXECUTABLE_OUTPUT_PATH}/testrecurson.shell "recurson" ${file} ) ENDFOREACH(file) FOREACH( file ${compatFiles} ) GET_FILENAME_COMPONENT( fn ${file} NAME) ADD_TEST( Compat-${fn} ${CMAKE_CURRENT_SOURCE_DIR}/runsingletestcase.pl ${EXECUTABLE_OUTPUT_PATH}/readandwrite.shell "ical" ${file} ) ENDFOREACH(file) FOREACH( file ${vCalFilesAsIcal} ) GET_FILENAME_COMPONENT( fn ${file} NAME) ADD_TEST( VCalOut-${fn} ${CMAKE_CURRENT_SOURCE_DIR}/runsingletestcase.pl ${EXECUTABLE_OUTPUT_PATH}/testvcalexport.shell "vcal" ${file} ) ENDFOREACH(file) FOREACH( file ${vCalFiles} ) GET_FILENAME_COMPONENT( fn ${file} NAME) ADD_TEST( VCalIn-${fn} ${CMAKE_CURRENT_SOURCE_DIR}/runsingletestcase.pl ${EXECUTABLE_OUTPUT_PATH}/readandwrite.shell "ical" ${file} ) ENDFOREACH(file) # ADD_TEST( TestRecurrence runtestcase.pl testrecurrence "next" ${CMAKE_CURRENT_SOURCE_DIR} data/RecurrenceRule/ "*.ics" ) # ADD_TEST( TestRecPrevious runtestcase.pl testrecurprevious "prev" ${CMAKE_CURRENT_SOURCE_DIR} data/RecurrenceRule/ "*.ics" ) # ADD_TEST( TestRecursOn runtestcase.pl testrecurson "recurson" ${CMAKE_CURRENT_SOURCE_DIR} data/RecurrenceRule/ "*.ics" ) # ADD_TEST( TestCompat runtestcase.pl readandwrite "ical" ${CMAKE_CURRENT_SOURCE_DIR} data/Compat/ "*.ics" ) # ADD_TEST( TestVCal runtestcase.pl testvcalexport "vcal" ${CMAKE_CURRENT_SOURCE_DIR} data/vCalendar/ "*.ics" ) # ADD_TEST( TestFromVCal runtestcase.pl readandwrite "ical" ${CMAKE_CURRENT_SOURCE_DIR} data/vCalendar/ "*.vcs" ) diff --git a/kcal/tests/testincidencerelation.cpp b/kcal/tests/testincidencerelation.cpp new file mode 100644 index 000000000..6349964ec --- /dev/null +++ b/kcal/tests/testincidencerelation.cpp @@ -0,0 +1,62 @@ +/* + This file is part of the kcal library. + Copyright (c) 2009 Thomas McGuire + + 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 "testincidencerelation.h" +#include "testincidencerelation.moc" +#include + +QTEST_KDEMAIN( IncidenceRelationTest, NoGUI ) + +#include "kcal/todo.h" +using namespace KCal; + +void IncidenceRelationTest::testRelations() +{ + // Build the following tree: + // todo1 + // \- todo2 + // \- todo3 + + // Then make todo3 independent: + // todo3 + // todo1 + // \- todo2 + + Todo *todo1 = new Todo(); + todo1->setSummary( "todo" ); + + Todo *todo2 = new Todo(); + todo2->setSummary( "sub-todo" ); + + Todo *todo3 = new Todo(); + todo3->setSummary( "sub-sub-todo" ); + + todo3->setRelatedTo( todo2 ); + todo2->setRelatedTo( todo1 ); + + QCOMPARE( todo3->relatedToUid(), todo2->uid() ); + QCOMPARE( todo2->relatedToUid(), todo1->uid() ); + QCOMPARE( todo1->relatedToUid(), QString() ); + + todo3->setRelatedTo( 0 ); + + QCOMPARE( todo3->relatedToUid(), QString() ); + QCOMPARE( todo2->relatedToUid(), todo1->uid() ); + QCOMPARE( todo1->relatedToUid(), QString() ); +} diff --git a/kcal/tests/testincidencerelation.h b/kcal/tests/testincidencerelation.h new file mode 100644 index 000000000..f99ab05f0 --- /dev/null +++ b/kcal/tests/testincidencerelation.h @@ -0,0 +1,33 @@ +/* + This file is part of the kcal library. + Copyright (c) 2009 Thomas McGuire + + 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 TESTINCIDENCERELATION_H +#define TESTINCIDENCERELATION_H + +#include + +class IncidenceRelationTest : public QObject +{ + Q_OBJECT + private Q_SLOTS: + void testRelations(); +}; + +#endif