Skip to content
This repository has been archived by the owner on Nov 11, 2021. It is now read-only.

Commit

Permalink
Merge branch 'jb43708v2' into 'master'
Browse files Browse the repository at this point in the history
[kcalcore] Cherry pick interval fix from upstream. Contributes to JB#43708

See merge request mer-core/kcalcore!9
  • Loading branch information
chriadam committed Oct 17, 2019
2 parents 2ee568f + 2dd640e commit 487a9b9
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 8 deletions.
4 changes: 3 additions & 1 deletion kcalcore.pro
@@ -1,3 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += \
kcalcore
kcalcore \
kcalcore/tests
OTHER_FILES += rpm/kcalcore-qt5.spec
28 changes: 21 additions & 7 deletions kcalcore/recurrencerule.cpp
Expand Up @@ -1761,13 +1761,22 @@ DateTimeList RecurrenceRule::timesInInterval( const KDateTime &dtStart,

if ( d->mTimedRepetition ) {
// It's a simple sub-daily recurrence with no constraints
int n = static_cast<int>( ( d->mDateStart.secsTo_long( start ) - 1 ) % d->mTimedRepetition );
KDateTime dt = start.addSecs( d->mTimedRepetition - n );
if ( dt < enddt ) {
n = static_cast<int>( ( dt.secsTo_long( enddt ) - 1 ) / d->mTimedRepetition ) + 1;
// limit n by a sane value else we can "explode".
n = qMin( n, LOOP_LIMIT );
for ( int i = 0; i < n; dt = dt.addSecs( d->mTimedRepetition ), ++i ) {

//Seconds to add to interval start, to get first occurrence which is within interval
qint64 offsetFromNextOccurrence;
if (d->mDateStart < start) {
offsetFromNextOccurrence = d->mTimedRepetition - (d->mDateStart.secsTo_long( start ) % d->mTimedRepetition);
} else {
offsetFromNextOccurrence = -(d->mDateStart.secsTo_long( start ) % d->mTimedRepetition);
}
KDateTime dt = start.addSecs( offsetFromNextOccurrence );
if ( dt <= enddt ) {
quint64 numberOfOccurrencesWithinInterval = ( dt.secsTo_long( enddt ) / d->mTimedRepetition ) + 1;
// limit numberOfOccurrencesWithinInterval by a sane value else we can "explode".
numberOfOccurrencesWithinInterval = numberOfOccurrencesWithinInterval < LOOP_LIMIT
? numberOfOccurrencesWithinInterval
: LOOP_LIMIT;
for ( quint64 i = 0; i < numberOfOccurrencesWithinInterval; dt = dt.addSecs( d->mTimedRepetition ), ++i ) {
result += dt;
}
}
Expand Down Expand Up @@ -1999,6 +2008,11 @@ DateTimeList RecurrenceRule::Private::datesForInterval( const Constraint &interv
// We have a valid constraint, so get all datetimes that match it andd
// append it to all date/times of this interval
QList<KDateTime> lstnew = merged.dateTimes( type );
lstnew.erase(std::remove_if(lstnew.begin(), lstnew.end(),
[this](const KDateTime &dt) {
return dt < mDateStart;
}),
lstnew.end());
lst += lstnew;
}
}
Expand Down
2 changes: 2 additions & 0 deletions kcalcore/tests/tests.pro
@@ -0,0 +1,2 @@
TEMPLATE=subdirs
SUBDIRS+=testtimesininterval.pro
154 changes: 154 additions & 0 deletions kcalcore/tests/testtimesininterval.cpp
Expand Up @@ -3,6 +3,7 @@
Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
Author: Sergio Martins <sergio.martins@kdab.com>
Copyright (C) 2019 Open Mobile Platform LLC
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
Expand All @@ -19,6 +20,7 @@
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/

#include "testtimesininterval.h"
#include "../event.h"

Expand Down Expand Up @@ -83,3 +85,155 @@ void TimesInIntervalTest::test()
//------------------------------------------------------------------------------------------------
}

//Test that interval start and end are inclusive
void TimesInIntervalTest::testSubDailyRecurrenceIntervalInclusive()
{
const KDateTime start(QDate(2013, 03, 10), QTime(10, 0, 0), KDateTime::UTC);
const KDateTime end(QDate(2013, 03, 10), QTime(11, 0, 0), KDateTime::UTC);

KCalCore::Event::Ptr event(new KCalCore::Event());
event->setUid("event");
event->setDtStart(start);
event->recurrence()->setHourly(1);
event->recurrence()->setDuration(2);

QList<KDateTime> expectedEventOccurrences;
expectedEventOccurrences << start << start.addSecs(60*60);

const DateTimeList timesInInterval = event->recurrence()->timesInInterval(start, end);
// kDebug() << "timesInInterval " << timesInInterval;
foreach (const KDateTime &dt, timesInInterval) {
// kDebug() << dt;
QCOMPARE(expectedEventOccurrences.removeAll(dt), 1);
}
QCOMPARE(expectedEventOccurrences.size(), 0);
}

//Test that the recurrence dtStart is used for calculation and not the interval start date
void TimesInIntervalTest::testSubDailyRecurrence2()
{
const KDateTime start(QDate(2013, 03, 10), QTime(10, 2, 3), KDateTime::UTC);
const KDateTime end(QDate(2013, 03, 10), QTime(13, 4, 5), KDateTime::UTC);

KCalCore::Event::Ptr event(new KCalCore::Event());
event->setUid("event");
event->setDtStart(start);
event->recurrence()->setHourly(1);
event->recurrence()->setDuration(2);

QList<KDateTime> expectedEventOccurrences;
expectedEventOccurrences << start << start.addSecs(60*60);

const DateTimeList timesInInterval = event->recurrence()->timesInInterval(start.addSecs(-20), end.addSecs(20));
// kDebug() << "timesInInterval " << timesInInterval;
foreach (const KDateTime &dt, timesInInterval) {
// kDebug() << dt;
QCOMPARE(expectedEventOccurrences.removeAll(dt), 1);
}
QCOMPARE(expectedEventOccurrences.size(), 0);
}

void TimesInIntervalTest::testSubDailyRecurrenceIntervalLimits()
{
const KDateTime start(QDate(2013, 03, 10), QTime(10, 2, 3), KDateTime::UTC);
const KDateTime end(QDate(2013, 03, 10), QTime(12, 2, 3), KDateTime::UTC);

KCalCore::Event::Ptr event(new KCalCore::Event());
event->setUid("event");
event->setDtStart(start);
event->recurrence()->setHourly(1);
event->recurrence()->setDuration(3);

QList<KDateTime> expectedEventOccurrences;
expectedEventOccurrences << start.addSecs(60*60);

const DateTimeList timesInInterval = event->recurrence()->timesInInterval(start.addSecs(1), end.addSecs(-1));
// kDebug() << "timesInInterval " << timesInInterval;
foreach (const KDateTime &dt, timesInInterval) {
// kDebug() << dt;
QCOMPARE(expectedEventOccurrences.removeAll(dt), 1);
}
QCOMPARE(expectedEventOccurrences.size(), 0);
}

//Test that the recurrence dtStart is used for calculation and not the interval start date
void TimesInIntervalTest::testDailyRecurrenceDtStart()
{
const int days = 7;
const KDateTime start(QDate(2013, 03, 10), QTime(0, 0, 0), KDateTime::ClockTime);
const KDateTime intervalEnd = start.addDays(days);
const KDateTime intervalStart = start.addDays(-days);

KCalCore::Event::Ptr event(new KCalCore::Event());
event->setUid("event");
event->setDtStart(start);
event->setDtEnd( start.addDays( 1 ) );
event->setAllDay( true );
event->setSummary( "Event2 Summary" );

event->recurrence()->setDaily( 1 );

QList<KDateTime> expectedEventOccurrences;
for (int i = 0; i < days; ++i) {
expectedEventOccurrences << start.addDays(i);
}

const DateTimeList timesInInterval = event->recurrence()->timesInInterval(intervalStart, intervalEnd);
foreach (const KDateTime &dt, timesInInterval) {
QCOMPARE(expectedEventOccurrences.removeAll(dt), 1);
}

QCOMPARE(expectedEventOccurrences.size(), 0);
}

//Test that the recurrence dtStart is used for calculation and not the interval start date
void TimesInIntervalTest::testWeeklyDayOfWeekRecurrenceDtStart()
{
// Create an all-day event which occurs every weekday of every week,
// starting from Friday the 11th of October.
KCalCore::Event::Ptr event = KCalCore::Event::Ptr(new KCalCore::Event());
event->startUpdates();
event->setUid("event");
event->setLocation(QStringLiteral("Test location"));
event->setAllDay(true);
event->setDescription(QStringLiteral("Test description"));
event->setDtStart(KDateTime::fromString(QStringLiteral("2019-10-11T00:00:00+10:00")));
event->setDtEnd(KDateTime::fromString(QStringLiteral("2019-10-12T00:00:00+10:00")));
event->setSummary(QStringLiteral("Test event summary"));
event->setCategories(QStringList() << QStringLiteral("Category One"));

RecurrenceRule * const rule = new RecurrenceRule();
rule->setRecurrenceType(RecurrenceRule::rWeekly);
rule->setStartDt(event->dtStart());
rule->setFrequency(1);
rule->setByDays(QList<RecurrenceRule::WDayPos>() << RecurrenceRule::WDayPos(0, 1) // monday
<< RecurrenceRule::WDayPos(0, 2) // tuesday
<< RecurrenceRule::WDayPos(0, 3) // wednesday
<< RecurrenceRule::WDayPos(0, 4) // thursday
<< RecurrenceRule::WDayPos(0, 5)); // friday

event->recurrence()->addRRule(rule);
event->endUpdates();

// Expand the events and within a wider interval, but ensure that the
// expansion does not include e.g. Wednesday the 9th of October.
const int days = 7;
const DateTimeList timesInInterval = event->recurrence()->timesInInterval(
event->dtStart().addDays(-days),
event->dtEnd().addDays(days));

QList<KDateTime> expectedEventOccurrences;
for (int i = 0; i <= days; ++i) {
// skip the saturday and sunday as those should not be expected
if (i != 1 && i != 2) {
expectedEventOccurrences << event->dtStart().addDays(i);
}
}

QCOMPARE(expectedEventOccurrences.size(), timesInInterval.size());
foreach (const KDateTime &dt, timesInInterval) {
QCOMPARE(expectedEventOccurrences.removeAll(dt), 1);
}

QCOMPARE(expectedEventOccurrences.size(), 0);
}
5 changes: 5 additions & 0 deletions kcalcore/tests/testtimesininterval.h
Expand Up @@ -30,6 +30,11 @@ class TimesInIntervalTest : public QObject
Q_OBJECT
private Q_SLOTS:
void test();
void testSubDailyRecurrenceIntervalInclusive();
void testSubDailyRecurrence2();
void testSubDailyRecurrenceIntervalLimits();
void testDailyRecurrenceDtStart();
void testWeeklyDayOfWeekRecurrenceDtStart();
};

#endif
19 changes: 19 additions & 0 deletions kcalcore/tests/testtimesininterval.pro
@@ -0,0 +1,19 @@
TEMPLATE = app
TARGET = tst_timesininterval

QT += testlib
CONFIG += link_pkgconfig
PKGCONFIG += libical uuid

DEPENDPATH += . $$PWD/.. $$PWD/../versit $$PWD/../klibport $$PWD/../kdedate
INCLUDEPATH += . $$PWD/.. $$PWD/../versit $$PWD/../klibport $$PWD/../kdedate /usr/include/libical

QMAKE_LIBDIR += $$PWD/..
LIBS += -lkcalcoren-qt5

HEADERS += testtimesininterval.h
SOURCES += testtimesininterval.cpp

target.path = /opt/tests/kcalcore-qt5/

INSTALLS += target
13 changes: 13 additions & 0 deletions rpm/kcalcore-qt5.spec
Expand Up @@ -29,6 +29,14 @@ Requires: %{name} = %{version}-%{release}
This package contains the files necessary to develop
applications using kcalcore

%package tests
Summary: Unit tests for kcalcore
Group: Development/Libraries
Requires: %{name} = %{version}-%{release}

%description tests
This package contains unit tests for kcalcore.

%prep
%setup -q -n %{name}-%{version}

Expand All @@ -53,3 +61,8 @@ rm -rf %{buildroot}
%{_includedir}/kcalcoren-qt5/*
%{_libdir}/libkcalcoren-qt5.so
%{_libdir}/pkgconfig/*.pc

%files tests
%defattr(-,root,root,-)
/opt/tests/kcalcore-qt5/tst_timesininterval
%dir /opt/tests/kcalcore-qt5

0 comments on commit 487a9b9

Please sign in to comment.