aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt20
-rw-r--r--src/CMakeLists.txt13
-rw-r--r--src/engine-mkcal.cpp632
-rw-r--r--src/planner-snooze.cpp6
-rw-r--r--tests/CMakeLists.txt51
-rw-r--r--tests/run-mkcal-db-test.sh68
-rwxr-xr-xtests/test-eds-ics-alarm-custom-sound.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-all-day-events.cpp8
-rw-r--r--tests/test-eds-ics-all-day-events.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-missing-trigger.cpp20
-rwxr-xr-xtests/test-eds-ics-missing-trigger.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-non-attending-alarms.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-nonrepeating-events.cpp10
-rw-r--r--tests/test-eds-ics-nonrepeating-events.dbbin0 -> 110592 bytes
-rwxr-xr-xtests/test-eds-ics-repeating-events-with-individual-change.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-repeating-events.cpp10
-rw-r--r--tests/test-eds-ics-repeating-events.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-repeating-valarms.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-tzids-2.cpp12
-rw-r--r--tests/test-eds-ics-tzids-2.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-tzids-utc.cpp12
-rw-r--r--tests/test-eds-ics-tzids-utc.dbbin0 -> 110592 bytes
-rw-r--r--tests/test-eds-ics-tzids.cpp12
-rw-r--r--tests/test-eds-ics-tzids.dbbin0 -> 110592 bytes
24 files changed, 842 insertions, 32 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f3d6987..ed0fe1d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.13)
+cmake_minimum_required (VERSION 3.16)
project (ayatana-indicator-datetime VERSION 24.5.1 LANGUAGES C CXX)
list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
@@ -13,7 +13,7 @@ set (PACKAGE ${CMAKE_PROJECT_NAME})
option(ENABLE_TESTS "Enable all tests and checks" OFF)
option(ENABLE_COVERAGE "Enable coverage reports (includes enabling all tests and checks)" OFF)
option(ENABLE_WERROR "Treat all build warnings as errors" OFF)
-option(ENABLE_LOMIRI_FEATURES "Build with Lomiri-specific libraries, schemas and media" OFF)
+option(ENABLE_LOMIRI_FEATURES "Build with Lomiri-specific libraries, schemas, media and backend" OFF)
if(ENABLE_COVERAGE)
set(ENABLE_TESTS ON)
@@ -61,9 +61,6 @@ set (
libayatana-common>=0.9.3
glib-2.0>=2.36
gio-unix-2.0>=2.36
- libical>=0.48
- libecal-2.0>=3.16
- libedataserver-1.2>=3.5
gstreamer-1.0>=1.2
libnotify>=0.7.6
properties-cpp>=0.0.1
@@ -79,6 +76,7 @@ if (ENABLE_LOMIRI_FEATURES)
lomiri-url-dispatcher>=0
lomiri-sounds
lomiri-schemas
+ libmkcal-qt5
)
pkg_get_variable(ALARM_DEFAULT_SOUND lomiri-sounds alarm_default_sound)
@@ -91,7 +89,19 @@ if (ENABLE_LOMIRI_FEATURES)
-DALARM_DEFAULT_SOUND="${ALARM_DEFAULT_SOUND}"
-DCALENDAR_DEFAULT_SOUND="${CALENDAR_DEFAULT_SOUND}"
)
+
+ find_package (ECM REQUIRED NO_MODULE)
+ list (APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
+ find_package (KF5 COMPONENTS CalendarCore REQUIRED)
else ()
+ list (
+ APPEND
+ SERVICE_DEPS
+ libecal-2.0>=3.16
+ libedataserver-1.2>=3.5
+ libical>=0.48
+ )
+
add_definitions (
-DALARM_DEFAULT_SOUND="dummy"
-DCALENDAR_DEFAULT_SOUND="dummy"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2305fdf..e13a4f1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -15,7 +15,6 @@ set (SERVICE_CXX_SOURCES
clock.cpp
clock-live.cpp
date-time.cpp
- engine-eds.cpp
exporter.cpp
formatter.cpp
formatter-desktop.cpp
@@ -41,6 +40,12 @@ set (SERVICE_CXX_SOURCES
wakeup-timer-mainloop.cpp
wakeup-timer-powerd.cpp)
+if (ENABLE_LOMIRI_FEATURES)
+ list (APPEND SERVICE_CXX_SOURCES engine-mkcal.cpp)
+else ()
+ list (APPEND SERVICE_CXX_SOURCES engine-eds.cpp)
+endif()
+
# generated sources
include (GdbusCodegen)
set(SERVICE_GENERATED_SOURCES)
@@ -69,6 +74,12 @@ endif()
include_directories (${CMAKE_SOURCE_DIR})
link_directories (${SERVICE_DEPS_LIBRARY_DIRS})
+if (ENABLE_LOMIRI_FEATURES)
+ set_source_files_properties (engine-mkcal.cpp PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
+ target_link_libraries (${SERVICE_LIB} KF5::CalendarCore)
+ target_compile_definitions (${SERVICE_LIB} PRIVATE -DQT_DEBUG -DQT_MESSAGELOGCONTEXT)
+endif ()
+
add_executable (${SERVICE_EXEC} main.cpp)
target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES})
install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
diff --git a/src/engine-mkcal.cpp b/src/engine-mkcal.cpp
new file mode 100644
index 0000000..56e4821
--- /dev/null
+++ b/src/engine-mkcal.cpp
@@ -0,0 +1,632 @@
+/*
+ * Copyright 2024-2025 Robert Tari
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Robert Tari <robert@tari.in>
+ */
+
+#include <gio/gio.h>
+#include <datetime/engine-eds.h>
+#include <datetime/myself.h>
+#include <extendedstorage.h>
+#include <QDebug>
+
+namespace ayatana
+{
+ namespace indicator
+ {
+ namespace datetime
+ {
+ class EdsEngine::Impl
+ {
+ public:
+
+ explicit Impl (const std::shared_ptr<Myself> &myself) : pMyself (myself)
+ {
+ this->pMyself->emails ().changed ().connect ([this] (const std::set<std::string> &)
+ {
+ setDirtySoon ();
+ });
+
+ // mKCal::ExtendedStorageObserver does not work
+ const gchar* sHome = g_get_home_dir ();
+ gchar *sDatabase = g_strdup_printf ("%s/.local/share/system/privileged/Calendar/mkcal/db.changed", sHome);
+ GFile *pFile = g_file_new_for_path (sDatabase);
+ g_free (sDatabase);
+ GError *pError = NULL;
+ this->pMonitor = g_file_monitor_file (pFile, G_FILE_MONITOR_NONE, NULL, &pError);
+ g_object_unref (pFile);
+
+ if (!this->pMonitor)
+ {
+ g_warning ("Panic: Error creating file monitor: %s", pError->message);
+ g_error_free (pError);
+ }
+ else
+ {
+ g_signal_connect (this->pMonitor, "changed", G_CALLBACK (onDatabaseChanged), this);
+ }
+ }
+
+ ~Impl ()
+ {
+ if (this->pMonitor)
+ {
+ g_object_unref (this->pMonitor);
+ }
+ }
+
+ core::Signal<>& changed ()
+ {
+ return this->pChanged;
+ }
+
+ void addAlarm (KCalendarCore::Alarm::Ptr pAlarm, std::map<std::pair<DateTime, DateTime>, std::map<DateTime, Alarm>> *pAlarms, QDateTime *pTriggerTime, QTimeZone *pTimeZone, KCalendarCore::Incidence::Ptr pIncidence, bool bAlarm)
+ {
+ /*
+ Loop through lAlarms to get information that we need to build the instance appointments and their alarms.
+
+ Outer map key is the instance component's start + end time. We build Appointment.begin and .end from that.
+
+ Inner map key is the alarm trigger, we build Alarm.time from that.
+
+ Inner map value is the Alarm.
+
+ We map the alarms based on their trigger time so that we can fold together multiple valarms that trigger for the same component at the same time. This is commonplace;
+ e.g. one valarm will have a display action and another will specify a sound to be played.
+ */
+ DateTime cBeginTime = datetimeFromQDateTime (pTriggerTime, pTimeZone);
+ QDateTime cQEndTime = {};
+ const bool bTime = pAlarm->hasTime ();
+
+ if (!bTime)
+ {
+ cQEndTime = *pTriggerTime;
+ }
+ else
+ {
+ cQEndTime = pAlarm->endTime ();
+ }
+
+ DateTime cEndTime = datetimeFromQDateTime (&cQEndTime, pTimeZone);
+ auto dInstanceTime = std::make_pair (cBeginTime, cEndTime);
+ DateTime cTriggerTime = datetimeFromQDateTime (pTriggerTime, pTimeZone);
+ auto &cAlarm = (*pAlarms)[dInstanceTime][cTriggerTime];
+ bool bTextEmpty = cAlarm.text.empty ();
+
+ if (bTextEmpty)
+ {
+ const KCalendarCore::Alarm::Type eType = pAlarm->type ();
+
+ if (eType == KCalendarCore::Alarm::Type::Display)
+ {
+ const QString sText = pAlarm->text ();
+ bool bEmpty = sText.isEmpty ();
+
+ if (!bEmpty)
+ {
+ cAlarm.text = sText.toStdString ();
+ }
+ }
+ }
+
+ bool bAudioEmpty = cAlarm.audio_url.empty ();
+
+ if (bAudioEmpty)
+ {
+ QString sUri = {};
+ const KCalendarCore::Alarm::Type eType = pAlarm->type ();
+
+ if (eType == KCalendarCore::Alarm::Type::Audio)
+ {
+ const QString sFile = pAlarm->audioFile ();
+ bool bEmpty = sFile.isEmpty ();
+
+ if (bEmpty)
+ {
+ cAlarm.audio_url = bAlarm ? "file://" ALARM_DEFAULT_SOUND : "file://" CALENDAR_DEFAULT_SOUND;
+ }
+ else
+ {
+ cAlarm.audio_url = sFile.toStdString ();
+ }
+ }
+ }
+
+ bool bTimeSet = cAlarm.time.is_set ();
+
+ if (!bTimeSet)
+ {
+ cAlarm.time = cTriggerTime;
+ }
+ }
+
+ void getAppointments (const QDateTime *pDateTimeBegin, const QDateTime *pDateTimeEnd, QTimeZone *pTimezone, std::function<void (const std::vector<Appointment>&)> pOnAppointments)
+ {
+ qDebug () << "Getting all appointments from " << *pDateTimeBegin << " to " << *pDateTimeEnd;
+
+ const QDate cDateBegin = pDateTimeBegin->date ();
+ const QDate cDateEnd = pDateTimeEnd->date ();
+
+ // Load the incidences
+ QTimeZone cSystemTimeZone = QTimeZone::systemTimeZone ();
+ mKCal::ExtendedCalendar *pCalendar = new mKCal::ExtendedCalendar (cSystemTimeZone);
+ mKCal::ExtendedCalendar::Ptr pCalendarPtr = mKCal::ExtendedCalendar::Ptr (pCalendar);
+ mKCal::ExtendedStorage::Ptr pStoragePtr = mKCal::ExtendedCalendar::defaultStorage (pCalendarPtr);
+ pStoragePtr->open ();
+ pStoragePtr->load (cDateBegin, cDateEnd);
+
+ // Get the notebooks
+ mKCal::Notebook::List lNotebooks = pStoragePtr->notebooks ();
+ std::map<QString, mKCal::Notebook::Ptr> dNotebooks;
+
+ for (mKCal::Notebook::Ptr pNotebook : lNotebooks)
+ {
+ QString sUid = pNotebook->uid ();
+ dNotebooks[sUid] = pNotebook;
+ }
+
+ std::vector<Appointment> lAppointments;
+ KCalendarCore::Incidence::List lIncidences = pCalendarPtr->incidences ();
+
+ std::sort (lIncidences.begin (), lIncidences.end (), [](const KCalendarCore::Incidence::Ptr &pIncidence1, const KCalendarCore::Incidence::Ptr &pIncidence2)
+ {
+ return (pIncidence1->dtStart () < pIncidence2->dtStart ());
+ });
+
+ // Walk through the incidences to build the appointment list
+ for (KCalendarCore::Incidence::Ptr pIncidence : lIncidences)
+ {
+ QString sCalendar = pCalendarPtr->notebook (pIncidence);
+ bool bAlarm = dNotebooks[sCalendar]->name () == "Alarms";
+ const QString sUid = pIncidence->uid ();
+ bool bEmpty = sUid.isEmpty ();
+
+ if (bEmpty == false)
+ {
+ bool bIncidenceInteresting = isIncidenceInteresting (pIncidence);
+
+ if (bIncidenceInteresting)
+ {
+ const QString sColor = dNotebooks[sCalendar]->color ();
+ std::string sColorStd = sColor.toStdString ();
+ Appointment cAppointment = getAppointment (pIncidence, pTimezone, bAlarm, sColorStd);
+ Appointment *pAppointment = nullptr;
+
+ if (!bAlarm)
+ {
+ // Walk the recurrences and add them
+ const KCalendarCore::Recurrence *pRecurrence = pIncidence->recurrence ();
+ const bool bRecurs = pRecurrence->recurs ();
+
+ if (bRecurs)
+ {
+ QDateTime cRecurrenceStart = pRecurrence->getNextDateTime (*pDateTimeBegin);
+ bool bRecurrenceValid = cRecurrenceStart.isValid ();
+
+ while (bRecurrenceValid && cRecurrenceStart <= *pDateTimeEnd)
+ {
+ Appointment cRecurringAppointment = cAppointment;
+ cRecurringAppointment.begin = datetimeFromQDateTime (&cRecurrenceStart, pTimezone);
+ /*const int64_t nAppointmentEnd = cRecurringAppointment.end.to_unix ();
+ QDateTime cRecurrenceEnd = QDateTime::fromSecsSinceEpoch (nAppointmentEnd);
+ const qint64 nRecurrenceStart = cRecurrenceStart.toSecsSinceEpoch ();
+ QDateTime cIncidenceStart = pIncidence->dtStart ();
+ const qint64 nIncidenceStart = cIncidenceStart.toSecsSinceEpoch ();
+ cRecurrenceEnd = cRecurrenceEnd.addSecs (nRecurrenceStart - nIncidenceStart);*/
+ const QDateTime cRecurrenceEnd = pIncidence->endDateForStart (cRecurrenceStart);
+ cRecurringAppointment.end = datetimeFromQDateTime (&cRecurrenceEnd, pTimezone);
+ lAppointments.push_back (cRecurringAppointment);
+ qDebug () << "Recurrence from " << cRecurringAppointment.begin.format ("%F %T %z").c_str () << " to " << cRecurringAppointment.end.format ("%F %T %z").c_str () << ":" << cRecurringAppointment.summary.c_str ();
+ cRecurrenceStart = pRecurrence->getNextDateTime (cRecurrenceStart);
+ bRecurrenceValid = cRecurrenceStart.isValid ();
+ }
+ }
+ else
+ {
+ lAppointments.push_back (cAppointment);
+ qDebug () << "Event from " << cAppointment.begin.format ("%F %T %z").c_str () << " to " << cAppointment.end.format ("%F %T %z").c_str () << ":" << cAppointment.summary.c_str ();
+
+ }
+
+ pAppointment = &lAppointments.back ();
+ }
+
+ // Generate alarms
+ KCalendarCore::Alarm::List lAlarms = pIncidence->alarms ();
+ std::map<std::pair<DateTime, DateTime>, std::map<DateTime, Alarm>> dAlarms;
+
+ // Walk the alarms and add them
+ for (KCalendarCore::Alarm::Ptr pAlarm : lAlarms)
+ {
+ // we only care about AUDIO or DISPLAY alarms, other kind of alarm will not generate a notification
+ bool bAlarmInteresting = isAlarmInteresting (pAlarm);
+
+ if (bAlarmInteresting)
+ {
+ const int nRepeat = pAlarm->repeatCount ();
+ const KCalendarCore::Duration cSnoozeTime = pAlarm->snoozeTime ();
+ const KCalendarCore::Duration cStartOffset = pAlarm->startOffset ();
+ const int nStartOffset = cStartOffset.asSeconds ();
+
+ if (nRepeat && cSnoozeTime)
+ {
+ const int nSnoozeTime = cSnoozeTime.asSeconds ();
+
+ for (int nIter = 0; nIter < nRepeat + 1; nIter++)
+ {
+ QDateTime cStartDateTime = pIncidence->dtStart ();
+ QDateTime cTriggerTime = cStartDateTime.addSecs (nStartOffset + (nIter * nSnoozeTime));
+ const Qt::TimeSpec cTimeSpec = cTriggerTime.timeSpec ();
+
+ if (cTimeSpec != Qt::LocalTime)
+ {
+ cTriggerTime = cTriggerTime.toTimeZone (*pTimezone);
+ }
+
+ this->addAlarm (pAlarm, &dAlarms, &cTriggerTime, pTimezone, pIncidence, bAlarm);
+ }
+ }
+ else
+ {
+ QDateTime cTriggerTime = (*pDateTimeBegin).addSecs (nStartOffset);
+ cTriggerTime = pAlarm->nextRepetition (cTriggerTime);
+ bool bValid = cTriggerTime.isValid ();
+
+ while (bValid && cTriggerTime <= *pDateTimeEnd)
+ {
+ const Qt::TimeSpec cTimeSpec = cTriggerTime.timeSpec ();
+
+ if (cTimeSpec != Qt::LocalTime)
+ {
+ cTriggerTime = cTriggerTime.toTimeZone (*pTimezone);
+ }
+
+ this->addAlarm (pAlarm, &dAlarms, &cTriggerTime, pTimezone, pIncidence, bAlarm);
+ cTriggerTime = pAlarm->nextRepetition (cTriggerTime);
+ bValid = cTriggerTime.isValid ();
+ }
+ }
+ }
+ }
+
+ for (auto &cAlarm : dAlarms)
+ {
+ if (bAlarm)
+ {
+ pAppointment = new Appointment (cAppointment);
+ pAppointment->begin = cAlarm.first.first;
+ pAppointment->end = cAlarm.first.second;
+ }
+
+ int nAlarms = cAlarm.second.size ();
+ pAppointment->alarms.reserve (nAlarms);
+
+ for (auto &cAlarmSecond : cAlarm.second)
+ {
+ bool bText = cAlarmSecond.second.has_text ();
+ bool bSound = cAlarmSecond.second.has_sound ();
+
+ if (bText || bSound)
+ {
+ pAppointment->alarms.push_back (cAlarmSecond.second);
+ qDebug () << "Alarm at " << cAlarmSecond.second.time.format ("%F %T %z").c_str ();
+ }
+ }
+
+ if (bAlarm)
+ {
+ lAppointments.push_back (*pAppointment);
+ qDebug () << "Alarm from " << (*pAppointment).begin.format ("%F %T %z").c_str () << " to " << (*pAppointment).end.format ("%F %T %z").c_str () << ":" << (*pAppointment).summary.c_str ();
+ delete pAppointment;
+ }
+ }
+ }
+ }
+ else
+ {
+ qWarning () << "Panic: incidence has no UID" << pIncidence;
+ }
+ }
+
+ // Give the caller the sorted finished product
+ auto &pAppointments = lAppointments;
+ std::sort (pAppointments.begin (), pAppointments.end (), [](const Appointment &pAppointment1, const Appointment &pAppointment2){return pAppointment1.begin < pAppointment2.begin;});
+ pOnAppointments (pAppointments);
+
+ qDebug () << "Returning appointments: " << lAppointments.size ();
+
+ pStoragePtr.clear ();
+ }
+
+ private:
+
+ static void onDatabaseChanged (GFileMonitor *pMonitor, GFile *pFile, GFile *OtherFile, GFileMonitorEvent eEvent, gpointer pData)
+ {
+ auto pSelf = static_cast<Impl*> (pData);
+
+ if (eEvent == G_FILE_MONITOR_EVENT_CHANGED)
+ {
+ pSelf->setDirtySoon ();
+ }
+ }
+
+ void setDirtyNow ()
+ {
+ this->pChanged ();
+ }
+
+ static gboolean setDirtyNowStatic (gpointer pSelfPtr)
+ {
+ auto pSelf = static_cast<Impl*> (pSelfPtr);
+ pSelf->nRebuildTag = 0;
+ pSelf->nRebuildDeadline = 0;
+ pSelf->setDirtyNow ();
+
+ return G_SOURCE_REMOVE;
+ }
+
+ void setDirtySoon ()
+ {
+ static constexpr int MIN_BATCH_SEC = 1;
+ static constexpr int MAX_BATCH_SEC = 60;
+ static_assert (MIN_BATCH_SEC <= MAX_BATCH_SEC, "bad boundaries");
+ const auto nNow = time (nullptr);
+
+ // First pass
+ if (this->nRebuildDeadline == 0)
+ {
+ this->nRebuildDeadline = nNow + MAX_BATCH_SEC;
+ this->nRebuildTag = g_timeout_add_seconds (MIN_BATCH_SEC, setDirtyNowStatic, this);
+ }
+ else if (nNow < this->nRebuildDeadline)
+ {
+ g_source_remove (this->nRebuildTag);
+ this->nRebuildTag = g_timeout_add_seconds (MIN_BATCH_SEC, setDirtyNowStatic, this);
+ }
+ }
+
+ static bool isAlarmInteresting (KCalendarCore::Alarm::Ptr pAlarm)
+ {
+ const KCalendarCore::Alarm::Type eType = pAlarm->type ();
+
+ if ((eType == KCalendarCore::Alarm::Type::Audio) || (eType == KCalendarCore::Alarm::Type::Display))
+ {
+ // We don't want disabled alarms
+ const bool bEnabled = pAlarm->enabled ();
+
+ return bEnabled;
+ }
+
+ return false;
+ }
+
+ static DateTime datetimeFromQDateTime (const QDateTime *pDateTime, QTimeZone *pTimeZone)
+ {
+ DateTime cDateTimeOut = {};
+ bool bValid = pDateTime->isValid ();
+
+ if (!bValid)
+ {
+ return cDateTimeOut;
+ }
+
+ const QByteArray sId = pTimeZone->id ();
+ const char *sIdData = sId.constData ();
+ GTimeZone *pGTimeZone = g_time_zone_new_identifier (sIdData);
+ const QDate cDate = pDateTime->date ();
+ const QTime cTime = pDateTime->time ();
+ const int nYear = cDate.year ();
+ const int nMonth = cDate.month ();
+ const int nDay = cDate.day ();
+ const int nHour = cTime.hour ();
+ const int nMinute = cTime.minute ();
+ const int nSecond = cTime.second ();
+ cDateTimeOut = DateTime (pGTimeZone, nYear, nMonth, nDay, nHour, nMinute, nSecond);
+ g_time_zone_unref (pGTimeZone);
+
+ return cDateTimeOut;
+ }
+
+ bool isIncidenceInteresting (KCalendarCore::Incidence::Ptr pIncidence)
+ {
+ // We only want calendar events and todos
+ const KCalendarCore::IncidenceBase::IncidenceType eType = pIncidence->type ();
+
+ if ((eType != KCalendarCore::IncidenceBase::IncidenceType::TypeEvent) && (eType != KCalendarCore::IncidenceBase::IncidenceType::TypeTodo))
+ {
+ return false;
+ }
+
+ // We're not interested in completed or cancelled incidences
+ const KCalendarCore::Incidence::Status eIncidenceStatus = pIncidence->status ();
+
+ if ((eIncidenceStatus == KCalendarCore::Incidence::Status::StatusCompleted) || (eIncidenceStatus == KCalendarCore::Incidence::Status::StatusCanceled))
+ {
+ return false;
+ }
+
+ // We don't want not attending alarms
+ const KCalendarCore::Attendee::List lAttendees = pIncidence->attendees ();
+
+ for (KCalendarCore::Attendee cAttendee : lAttendees)
+ {
+ const QString sEmail = cAttendee.email ();
+ const std::string sEmailStd = sEmail.toStdString ();
+ bool bMyEmail = this->pMyself->isMyEmail (sEmailStd);
+
+ // Check if the user is part of the attendee list
+ if (bMyEmail)
+ {
+ // Check the status
+ const KCalendarCore::Attendee::PartStat eAttendeeStatus = cAttendee.status ();
+ bool bDeclined = (eAttendeeStatus == KCalendarCore::Attendee::PartStat::Declined);
+
+ return !bDeclined;
+ }
+ }
+
+ return true;
+ }
+
+ static Appointment getAppointment (KCalendarCore::Incidence::Ptr pIncidence, QTimeZone *pTimeZone, bool bAlarm, std::string sColor)
+ {
+ Appointment cAppointment;
+
+ // Get Appointment.uid
+ const QString sUid = pIncidence->uid ();
+ cAppointment.uid = sUid.toStdString ();
+
+ // Get Appointment.summary
+ const QString sSummary = pIncidence->summary ();
+ cAppointment.summary = sSummary.toStdString ();
+
+ // Get Appointment.begin
+ QDateTime cBegin = pIncidence->dtStart ();
+ const Qt::TimeSpec cTimeSpecBegin = cBegin.timeSpec ();
+
+ if (cTimeSpecBegin != Qt::LocalTime)
+ {
+ cBegin = cBegin.toTimeZone (*pTimeZone);
+ }
+
+ cAppointment.begin = datetimeFromQDateTime (&cBegin, pTimeZone);
+
+ // Get Appointment.end
+ QDateTime cEnd = {};
+ const KCalendarCore::IncidenceBase::IncidenceType eType = pIncidence->type ();
+
+ if (eType == KCalendarCore::IncidenceBase::IncidenceType::TypeEvent)
+ {
+ KCalendarCore::Event::Ptr pEvent = qSharedPointerCast<KCalendarCore::Event> (pIncidence);
+ cEnd = pEvent->dtEnd ();
+ const Qt::TimeSpec cTimeSpecEnd = cEnd.timeSpec ();
+
+ if (cTimeSpecEnd != Qt::LocalTime)
+ {
+ cEnd = cEnd.toTimeZone (*pTimeZone);
+ }
+
+ // Check for all day event
+ bool bHasEndDate = pEvent->hasEndDate ();
+ bool bAllDay = pIncidence->allDay ();
+
+ if (!bHasEndDate && bAllDay)
+ {
+ cEnd = cBegin.addDays (1);
+ }
+ }
+
+ const bool bValid = cEnd.isValid ();
+
+ if (bValid)
+ {
+ cAppointment.end = datetimeFromQDateTime (&cEnd, pTimeZone);
+ }
+ else
+ {
+ cAppointment.end = cAppointment.begin;
+ }
+
+ // Get Appointment.type
+ if (bAlarm)
+ {
+ cAppointment.type = Appointment::ALARM;
+ }
+ else
+ {
+ cAppointment.type = Appointment::EVENT;
+ }
+
+ // Get Appointment.color
+ cAppointment.color = sColor;
+
+ return cAppointment;
+ }
+
+ core::Signal<> pChanged;
+ guint nRebuildTag {};
+ time_t nRebuildDeadline {};
+ std::shared_ptr<Myself> pMyself;
+ GFileMonitor *pMonitor;
+ };
+
+ EdsEngine::EdsEngine (const std::shared_ptr<Myself> &myself): p (new Impl (myself))
+ {
+ }
+
+ EdsEngine::~EdsEngine () = default;
+
+ core::Signal<>& EdsEngine::changed ()
+ {
+ return this->p->changed ();
+ }
+
+ void EdsEngine::get_appointments (const DateTime &pDateTimeBegin, const DateTime &pDateTimeEnd, const Timezone &pTimezone, std::function<void (const std::vector<Appointment>&)> pFunc)
+ {
+ qint64 nDateTimeBegin = pDateTimeBegin.to_unix ();
+ QDateTime cDateTimeBegin = QDateTime::fromSecsSinceEpoch (nDateTimeBegin, Qt::UTC);
+ qint64 nDateTimeEnd = pDateTimeEnd.to_unix ();
+ QDateTime cDateTimeEnd = QDateTime::fromSecsSinceEpoch (nDateTimeEnd, Qt::UTC);
+ QTimeZone cTimeZone = QTimeZone ();
+ std::string sTimeZone = pTimezone.timezone.get ();
+ const char *sTimeZoneData = sTimeZone.c_str ();
+ QTimeZone cTimezone = QTimeZone (sTimeZoneData);
+ this->p->getAppointments (&cDateTimeBegin, &cDateTimeEnd, &cTimezone, pFunc);
+ }
+
+ void EdsEngine::disable_alarm (const Appointment &pAppointment)
+ {
+ bool bAlarm = pAppointment.is_alarm ();
+
+ if (bAlarm)
+ {
+ QTimeZone cSystemTimeZone = QTimeZone::systemTimeZone ();
+ mKCal::ExtendedCalendar *pCalendar = new mKCal::ExtendedCalendar (cSystemTimeZone);
+ mKCal::ExtendedCalendar::Ptr pCalendarPtr = mKCal::ExtendedCalendar::Ptr (pCalendar);
+ mKCal::ExtendedStorage::Ptr pStoragePtr = mKCal::ExtendedCalendar::defaultStorage (pCalendarPtr);
+ pStoragePtr->open ();
+ const char *sUid = pAppointment.uid.c_str ();
+ pStoragePtr->load (sUid);
+ KCalendarCore::Incidence::List lIncidences = pCalendarPtr->incidences ();
+ bool bChanged = false;
+
+ for (KCalendarCore::Incidence::Ptr pIncidence : lIncidences)
+ {
+ KCalendarCore::Alarm::List lAlarms = pIncidence->alarms ();
+
+ for (KCalendarCore::Alarm::Ptr pAlarm : lAlarms)
+ {
+ const bool bEnabled = pAlarm->enabled ();
+
+ if (bEnabled)
+ {
+ pAlarm->setEnabled (false);
+ bChanged = true;
+ }
+ }
+ }
+
+ if (bChanged)
+ {
+ pStoragePtr->save ();
+ }
+
+ pStoragePtr.clear ();
+ }
+ }
+ }
+ }
+}
diff --git a/src/planner-snooze.cpp b/src/planner-snooze.cpp
index b81c912..3cbd740 100644
--- a/src/planner-snooze.cpp
+++ b/src/planner-snooze.cpp
@@ -1,5 +1,6 @@
/*
* Copyright 2014 Canonical Ltd.
+ * Copyright 2024 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -15,12 +16,11 @@
*
* Authors:
* Charles Kerr <charles.kerr@canonical.com>
+ * Robert Tari <robert@tari.in>
*/
#include <datetime/planner-snooze.h>
-#include <libedataserver/libedataserver.h> // e_uid_new()
-
namespace ayatana {
namespace indicator {
namespace datetime {
@@ -67,7 +67,7 @@ public:
appt.alarms[0].time += offset;
// give it a new ID
- gchar* uid = e_uid_new();
+ gchar* uid = g_uuid_string_random ();
appt.uid = uid;
g_free(uid);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 13dd0d8..4aaee45 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -77,23 +77,40 @@ target_link_libraries (${TEST_NAME} indicatordatetimeservice ${SERVICE_DEPS_LIBR
find_program(DBUS_RUNNER dbus-test-runner)
-function(add_eds_ics_test_by_name name)
- set (TEST_NAME ${name})
- set (COVERAGE_TEST_TARGETS ${COVERAGE_TEST_TARGETS} ${TEST_NAME} PARENT_SCOPE)
- configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.ics.in"
- "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.ics")
- add_executable(${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled)
- target_link_options(${TEST_NAME} PRIVATE -no-pie)
- target_link_libraries (${TEST_NAME} indicatordatetimeservice ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES})
- add_test (${TEST_NAME}
- ${CMAKE_CURRENT_SOURCE_DIR}/run-eds-ics-test.sh
- ${DBUS_RUNNER} # arg1: dbus-test-runner exec
- ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path
- ${TEST_NAME} # arg3: test name
- ${CMAKE_CURRENT_SOURCE_DIR}/test-eds-ics-config-files # arg4: base directory for config file template
- ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.ics # arg5: the ical file for this test
- ${CMAKE_CURRENT_SOURCE_DIR}/accounts.db) # arg6: online accounts database
-endfunction()
+if (ENABLE_LOMIRI_FEATURES)
+ function(add_eds_ics_test_by_name name)
+ set (TEST_NAME ${name})
+ set (COVERAGE_TEST_TARGETS ${COVERAGE_TEST_TARGETS} ${TEST_NAME} PARENT_SCOPE)
+ add_executable(${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled)
+ target_link_options(${TEST_NAME} PRIVATE -no-pie)
+ target_link_libraries (${TEST_NAME} indicatordatetimeservice ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES})
+ add_test (${TEST_NAME}
+ ${CMAKE_CURRENT_SOURCE_DIR}/run-mkcal-db-test.sh
+ ${DBUS_RUNNER} # arg1: dbus-test-runner exec
+ ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path
+ ${TEST_NAME} # arg3: test name
+ ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.db # arg4: the database file for this test
+ ${CMAKE_CURRENT_SOURCE_DIR}/accounts.db) # arg5: online accounts database
+ endfunction()
+else ()
+ function(add_eds_ics_test_by_name name)
+ set (TEST_NAME ${name})
+ set (COVERAGE_TEST_TARGETS ${COVERAGE_TEST_TARGETS} ${TEST_NAME} PARENT_SCOPE)
+ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.ics.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.ics")
+ add_executable(${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled)
+ target_link_options(${TEST_NAME} PRIVATE -no-pie)
+ target_link_libraries (${TEST_NAME} indicatordatetimeservice ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES})
+ add_test (${TEST_NAME}
+ ${CMAKE_CURRENT_SOURCE_DIR}/run-eds-ics-test.sh
+ ${DBUS_RUNNER} # arg1: dbus-test-runner exec
+ ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path
+ ${TEST_NAME} # arg3: test name
+ ${CMAKE_CURRENT_SOURCE_DIR}/test-eds-ics-config-files # arg4: base directory for config file template
+ ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.ics # arg5: the ical file for this test
+ ${CMAKE_CURRENT_SOURCE_DIR}/accounts.db) # arg6: online accounts database
+ endfunction()
+endif ()
add_eds_ics_test_by_name(test-eds-ics-all-day-events)
add_eds_ics_test_by_name(test-eds-ics-repeating-events)
add_eds_ics_test_by_name(test-eds-ics-nonrepeating-events)
diff --git a/tests/run-mkcal-db-test.sh b/tests/run-mkcal-db-test.sh
new file mode 100644
index 0000000..114b9bd
--- /dev/null
+++ b/tests/run-mkcal-db-test.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+SELF=$0 # this script
+TEST_RUNNER=$1 # full executable path of dbus-test-runner
+TEST_EXEC=$2 # full executable path of test app
+TEST_NAME=$3 # test name
+DB_FILE=$4 # database file holding test data
+ACCOUNTS_DB=$5 # online account database
+
+echo "this script: ${SELF}"
+echo "test-runner: ${TEST_RUNNER}"
+echo "test-exec: ${TEST_EXEC}"
+echo "test-name: ${TEST_NAME}"
+echo "db-file: ${DB_FILE}"
+
+# set up the tmpdir
+export TEST_TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d ${TEST_NAME}-XXXXXXXXXX) || exit 1
+echo "running test '${TEST_NAME}' in ${TEST_TMP_DIR}"
+
+# set up the environment variables
+export QT_QPA_PLATFORM=minimal
+export HOME=${TEST_TMP_DIR}
+export XDG_RUNTIME_DIR=${TEST_TMP_DIR}
+export XDG_CACHE_HOME=${TEST_TMP_DIR}/.cache
+export XDG_CONFIG_HOME=${TEST_TMP_DIR}/.config
+export XDG_DATA_HOME=${TEST_TMP_DIR}/.local/share
+export XDG_DESKTOP_DIR=${TEST_TMP_DIR}
+export XDG_DOCUMENTS_DIR=${TEST_TMP_DIR}
+export XDG_DOWNLOAD_DIR=${TEST_TMP_DIR}
+export XDG_MUSIC_DIR=${TEST_TMP_DIR}
+export XDG_PICTURES_DIR=${TEST_TMP_DIR}
+export XDG_PUBLICSHARE_DIR=${TEST_TMP_DIR}
+export XDG_TEMPLATES_DIR=${TEST_TMP_DIR}
+export XDG_VIDEOS_DIR=${TEST_TMP_DIR}
+export GIO_USE_VFS=local # needed to ensure GVFS shuts down cleanly after the test is over
+
+export G_MESSAGES_DEBUG=all
+export G_DBUS_DEBUG=messages
+
+echo HOMEDIR=${HOME}
+rm -rf ${XDG_DATA_HOME}
+
+# if there's a specific db file to test, copy it
+if [ -e "${DB_FILE}" ]; then
+ echo "copying ${DB_FILE} into $HOME"
+ mkdir -p ${XDG_DATA_HOME}/system/privileged/Calendar/mkcal/
+ cp --verbose --archive "${DB_FILE}" ${XDG_DATA_HOME}/system/privileged/Calendar/mkcal/db
+fi
+
+# prepare online accounts database
+if [ -e "${ACCOUNTS_DB}" ]; then
+ echo "copying ${ACCOUNTS_DB} into $HOME"
+ mkdir -p ${XDG_CONFIG_HOME}/libaccounts-glib/
+ cp --verbose --archive "${ACCOUNTS_DB}" ${XDG_CONFIG_HOME}/libaccounts-glib/accounts.db
+fi
+
+# run the test
+${TEST_RUNNER} --keep-env --max-wait=90 --task "${TEST_EXEC}" --task-name ${TEST_NAME} --wait-until-complete
+rv=$?
+
+# if the test passed, blow away the tmpdir
+if [ $rv -eq 0 ]; then
+ sleep 5
+ rm -rf $TEST_TMP_DIR
+fi
+
+# pass the test's return code to the caller.
+exit "$rv"
diff --git a/tests/test-eds-ics-alarm-custom-sound.db b/tests/test-eds-ics-alarm-custom-sound.db
new file mode 100755
index 0000000..f5ba86d
--- /dev/null
+++ b/tests/test-eds-ics-alarm-custom-sound.db
Binary files differ
diff --git a/tests/test-eds-ics-all-day-events.cpp b/tests/test-eds-ics-all-day-events.cpp
index 0fa40fd..e38c7c7 100644
--- a/tests/test-eds-ics-all-day-events.cpp
+++ b/tests/test-eds-ics-all-day-events.cpp
@@ -80,7 +80,11 @@ TEST_F(VAlarmFixture, MultipleAppointments)
// what we expect to get...
Appointment expected_appt;
+#ifndef LOMIRI_FEATURES_ENABLED
expected_appt.uid = "20150521T111538Z-7449-1000-3572-0@ghidorah";
+#else
+ expected_appt.uid = "51340540-a924-468e-b3ee-0c0f222cd0f8";
+#endif
expected_appt.summary = "Memorial Day";
expected_appt.begin = DateTime{gtz,2015,5,25,0,0,0};
expected_appt.end = DateTime{gtz,2015,5,26,0,0,0};
@@ -98,7 +102,11 @@ TEST_F(VAlarmFixture, MultipleAppointments)
EXPECT_PRED3([](auto sColourIn, auto sColourExpected1, auto sColourExpected2)
{
return sColourIn == sColourExpected1 || sColourIn == sColourExpected2;
+#ifndef LOMIRI_FEATURES_ENABLED
}, appt.color, "#becedd", "#62a0ea");
+#else
+ }, appt.color, "#0000FF", "");
+#endif
// cleanup
g_time_zone_unref(gtz);
diff --git a/tests/test-eds-ics-all-day-events.db b/tests/test-eds-ics-all-day-events.db
new file mode 100644
index 0000000..f297366
--- /dev/null
+++ b/tests/test-eds-ics-all-day-events.db
Binary files differ
diff --git a/tests/test-eds-ics-missing-trigger.cpp b/tests/test-eds-ics-missing-trigger.cpp
index 4030999..69ca109 100644
--- a/tests/test-eds-ics-missing-trigger.cpp
+++ b/tests/test-eds-ics-missing-trigger.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Canonical Ltd.
- * Copyright 2021-2024 Robert Tari
+ * Copyright 2021-2025 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -82,8 +82,13 @@ TEST_F(VAlarmFixture, MissingTriggers)
std::vector<Appointment> expected1;
Appointment a1;
a1.type = Appointment::ALARM;
+#ifndef LOMIRI_FEATURES_ENABLED
a1.uid = "20150617T211838Z-6217-32011-2036-1@lomiri-phablet";
a1.color = "#becedd";
+#else
+ a1.uid = "a0121159-8810-434f-9066-cc3b71e3793f";
+ a1.color = "#0000FF";
+#endif
a1.summary = "One Time Alarm";
a1.begin = DateTime { gtz, 2015, 6, 18, 10, 0, 0};
a1.end = a1.begin;
@@ -94,7 +99,11 @@ TEST_F(VAlarmFixture, MissingTriggers)
expected1.push_back(a1);
// build expected: recurring alarm 1
+#ifndef LOMIRI_FEATURES_ENABLED
a1.uid = "20150617T211913Z-6217-32011-2036-5@lomiri-phablet";
+#else
+ a1.uid = "3b45cbc9-d5c3-49a4-ad29-acc776818259";
+#endif
a1.summary = "Recurring Alarm";
a1.alarms[0].text = a1.summary;
std::array<DateTime,13> recurrences {
@@ -121,8 +130,13 @@ TEST_F(VAlarmFixture, MissingTriggers)
std::vector<Appointment> expected2;
Appointment a2;
a2.type = Appointment::ALARM;
+#ifndef LOMIRI_FEATURES_ENABLED
a2.uid = "20150617T211838Z-6217-32011-2036-1@lomiri-phablet";
a2.color = "#62a0ea";
+#else
+ a2.uid = "a0121159-8810-434f-9066-cc3b71e3793f";
+ a2.color = "";
+#endif
a2.summary = "One Time Alarm";
a2.begin = DateTime { gtz, 2015, 6, 18, 10, 0, 0};
a2.end = a2.begin;
@@ -133,7 +147,11 @@ TEST_F(VAlarmFixture, MissingTriggers)
expected2.push_back(a2);
// build expected: recurring alarm 2
+#ifndef LOMIRI_FEATURES_ENABLED
a2.uid = "20150617T211913Z-6217-32011-2036-5@lomiri-phablet";
+#else
+ a2.uid = "3b45cbc9-d5c3-49a4-ad29-acc776818259";
+#endif
a2.summary = "Recurring Alarm";
a2.alarms[0].text = a2.summary;
for (const auto& time : recurrences) {
diff --git a/tests/test-eds-ics-missing-trigger.db b/tests/test-eds-ics-missing-trigger.db
new file mode 100755
index 0000000..0be974d
--- /dev/null
+++ b/tests/test-eds-ics-missing-trigger.db
Binary files differ
diff --git a/tests/test-eds-ics-non-attending-alarms.db b/tests/test-eds-ics-non-attending-alarms.db
new file mode 100644
index 0000000..fae3aaf
--- /dev/null
+++ b/tests/test-eds-ics-non-attending-alarms.db
Binary files differ
diff --git a/tests/test-eds-ics-nonrepeating-events.cpp b/tests/test-eds-ics-nonrepeating-events.cpp
index 49fc9be..c126dba 100644
--- a/tests/test-eds-ics-nonrepeating-events.cpp
+++ b/tests/test-eds-ics-nonrepeating-events.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Canonical Ltd.
- * Copyright 2021-2024 Robert Tari
+ * Copyright 2021-2025 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -80,7 +80,11 @@ TEST_F(VAlarmFixture, MultipleAppointments)
// what we expect to get...
Appointment expected_appt;
+#ifndef LOMIRI_FEATURES_ENABLED
expected_appt.uid = "20150520T000726Z-3878-32011-1770-81@lomiri-phablet";
+#else
+ expected_appt.uid = "d7aeb192-8b2c-4427-834f-f30388e9e73c";
+#endif
expected_appt.summary = "Alarm";
std::array<Alarm,1> expected_alarms = {
Alarm({"Alarm", "file://" ALARM_DEFAULT_SOUND, DateTime(gtz,2015,5,20,20,00,0)})
@@ -99,7 +103,11 @@ TEST_F(VAlarmFixture, MultipleAppointments)
EXPECT_PRED3([](auto sColourIn, auto sColourExpected1, auto sColourExpected2)
{
return sColourIn == sColourExpected1 || sColourIn == sColourExpected2;
+ #ifndef LOMIRI_FEATURES_ENABLED
}, appt.color, "#becedd", "#62a0ea");
+ #else
+ }, appt.color, "#0000FF", "");
+ #endif
}
// cleanup
diff --git a/tests/test-eds-ics-nonrepeating-events.db b/tests/test-eds-ics-nonrepeating-events.db
new file mode 100644
index 0000000..4170aff
--- /dev/null
+++ b/tests/test-eds-ics-nonrepeating-events.db
Binary files differ
diff --git a/tests/test-eds-ics-repeating-events-with-individual-change.db b/tests/test-eds-ics-repeating-events-with-individual-change.db
new file mode 100755
index 0000000..6ca01a7
--- /dev/null
+++ b/tests/test-eds-ics-repeating-events-with-individual-change.db
Binary files differ
diff --git a/tests/test-eds-ics-repeating-events.cpp b/tests/test-eds-ics-repeating-events.cpp
index a570d0f..701f8da 100644
--- a/tests/test-eds-ics-repeating-events.cpp
+++ b/tests/test-eds-ics-repeating-events.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Canonical Ltd.
- * Copyright 2021-2024 Robert Tari
+ * Copyright 2021-2025 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -80,7 +80,11 @@ TEST_F(VAlarmFixture, MultipleAppointments)
// what we expect to get...
Appointment expected_appt;
+#ifndef LOMIRI_FEATURES_ENABLED
expected_appt.uid = "20150507T211449Z-4262-32011-1418-1@lomiri-phablet";
+#else
+ expected_appt.uid = "840ab899-1b0e-4697-9514-dcd336a5e125";
+#endif
expected_appt.summary = "Alarm";
std::array<Alarm,8> expected_alarms = {
Alarm({"Alarm", "file://" ALARM_DEFAULT_SOUND, DateTime(gtz,2015,5, 8,16,40,0)}),
@@ -106,7 +110,11 @@ TEST_F(VAlarmFixture, MultipleAppointments)
EXPECT_PRED3([](auto sColourIn, auto sColourExpected1, auto sColourExpected2)
{
return sColourIn == sColourExpected1 || sColourIn == sColourExpected2;
+ #ifndef LOMIRI_FEATURES_ENABLED
}, appt.color, "#becedd", "#62a0ea");
+ #else
+ }, appt.color, "#0000FF", "");
+ #endif
}
// cleanup
diff --git a/tests/test-eds-ics-repeating-events.db b/tests/test-eds-ics-repeating-events.db
new file mode 100644
index 0000000..ac9a0ea
--- /dev/null
+++ b/tests/test-eds-ics-repeating-events.db
Binary files differ
diff --git a/tests/test-eds-ics-repeating-valarms.db b/tests/test-eds-ics-repeating-valarms.db
new file mode 100644
index 0000000..361fffc
--- /dev/null
+++ b/tests/test-eds-ics-repeating-valarms.db
Binary files differ
diff --git a/tests/test-eds-ics-tzids-2.cpp b/tests/test-eds-ics-tzids-2.cpp
index 1bef68b..aff9de7 100644
--- a/tests/test-eds-ics-tzids-2.cpp
+++ b/tests/test-eds-ics-tzids-2.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Canonical Ltd.
- * Copyright 2021-2024 Robert Tari
+ * Copyright 2021-2025 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -81,8 +81,13 @@ TEST_F(VAlarmFixture, MultipleAppointments)
// what we expect to get...
std::array<Appointment,1> expected_appts1;
auto appt1 = &expected_appts1[0];
+#ifndef LOMIRI_FEATURES_ENABLED
appt1->uid = "109264742";
appt1->color = "#becedd";
+#else
+ appt1->uid = "4eade898-ffd4-49d6-a43a-c6ca9c20aace";
+ appt1->color = "#0000FF";
+#endif
appt1->summary = "National Incubator Initiative for Clean Energy (NIICE) FOA: Pre-Concept Paper Informational Webinar";
appt1->begin = DateTime{gtz,2014,1,21,11,0,0};
appt1->end = DateTime{gtz,2014,1,21,13,0,0};
@@ -90,8 +95,13 @@ TEST_F(VAlarmFixture, MultipleAppointments)
std::array<Appointment,1> expected_appts2;
auto appt2 = &expected_appts2[0];
+#ifndef LOMIRI_FEATURES_ENABLED
appt2->uid = "109264742";
appt2->color = "#62a0ea";
+#else
+ appt2->uid = "4eade898-ffd4-49d6-a43a-c6ca9c20aace";
+ appt2->color = "";
+#endif
appt2->summary = "National Incubator Initiative for Clean Energy (NIICE) FOA: Pre-Concept Paper Informational Webinar";
appt2->begin = DateTime{gtz,2014,1,21,11,0,0};
appt2->end = DateTime{gtz,2014,1,21,13,0,0};
diff --git a/tests/test-eds-ics-tzids-2.db b/tests/test-eds-ics-tzids-2.db
new file mode 100644
index 0000000..5e2b62d
--- /dev/null
+++ b/tests/test-eds-ics-tzids-2.db
Binary files differ
diff --git a/tests/test-eds-ics-tzids-utc.cpp b/tests/test-eds-ics-tzids-utc.cpp
index d88e95d..8226061 100644
--- a/tests/test-eds-ics-tzids-utc.cpp
+++ b/tests/test-eds-ics-tzids-utc.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Canonical Ltd.
- * Copyright 2021-2024 Robert Tari
+ * Copyright 2021-2025 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -82,16 +82,26 @@ TEST_F(VAlarmFixture, UTCAppointments)
// what we expect to get...
std::array<Appointment,1> expected_appts1;
auto appt1 = &expected_appts1[0];
+#ifndef LOMIRI_FEATURES_ENABLED
appt1->uid = "20160322T132738Z";
appt1->color = "#becedd";
+#else
+ appt1->uid = "17ea8f73-4965-4a9c-9add-4a026a86ec60";
+ appt1->color = "#0000FF";
+#endif
appt1->summary = "UTC event";
appt1->begin = DateTime{gtz,2016,3,22,15,0,0};
appt1->end = DateTime{gtz,2016,3,22,16,0,0};
std::array<Appointment,1> expected_appts2;
auto appt2 = &expected_appts2[0];
+#ifndef LOMIRI_FEATURES_ENABLED
appt2->uid = "20160322T132738Z";
appt2->color = "#62a0ea";
+#else
+ appt2->uid = "17ea8f73-4965-4a9c-9add-4a026a86ec60";
+ appt2->color = "";
+#endif
appt2->summary = "UTC event";
appt2->begin = DateTime{gtz,2016,3,22,15,0,0};
appt2->end = DateTime{gtz,2016,3,22,16,0,0};
diff --git a/tests/test-eds-ics-tzids-utc.db b/tests/test-eds-ics-tzids-utc.db
new file mode 100644
index 0000000..5bc6bad
--- /dev/null
+++ b/tests/test-eds-ics-tzids-utc.db
Binary files differ
diff --git a/tests/test-eds-ics-tzids.cpp b/tests/test-eds-ics-tzids.cpp
index 4999e66..e676001 100644
--- a/tests/test-eds-ics-tzids.cpp
+++ b/tests/test-eds-ics-tzids.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Canonical Ltd.
- * Copyright 2021-2024 Robert Tari
+ * Copyright 2021-2025 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
@@ -81,16 +81,26 @@ TEST_F(VAlarmFixture, MultipleAppointments)
// what we expect to get...
std::array<Appointment,1> expected_appts1;
auto appt1 = &expected_appts1[0];
+#ifndef LOMIRI_FEATURES_ENABLED
appt1->uid = "8ggc30kh89qql8vjumgtug7l14@google.com";
appt1->color = "#becedd";
+#else
+ appt1->uid = "01fd35a6-8fbb-4c31-97e1-71d920190f18";
+ appt1->color = "#0000FF";
+#endif
appt1->summary = "Hello";
appt1->begin = DateTime{gtz,2015,7,1,20,0,0};
appt1->end = DateTime{gtz,2015,7,1,22,0,0};
std::array<Appointment,1> expected_appts2;
auto appt2 = &expected_appts2[0];
+#ifndef LOMIRI_FEATURES_ENABLED
appt2->uid = "8ggc30kh89qql8vjumgtug7l14@google.com";
appt2->color = "#62a0ea";
+#else
+ appt2->uid = "01fd35a6-8fbb-4c31-97e1-71d920190f18";
+ appt2->color = "";
+#endif
appt2->summary = "Hello";
appt2->begin = DateTime{gtz,2015,7,1,20,0,0};
appt2->end = DateTime{gtz,2015,7,1,22,0,0};
diff --git a/tests/test-eds-ics-tzids.db b/tests/test-eds-ics-tzids.db
new file mode 100644
index 0000000..cf18ab3
--- /dev/null
+++ b/tests/test-eds-ics-tzids.db
Binary files differ