Merge lp:~charlesk/indicator-datetime/lp-1456281-fix-15.04-alarm-regression into lp:indicator-datetime/15.04

Proposed by Charles Kerr
Status: Merged
Approved by: Ted Gould
Approved revision: 428
Merged at revision: 414
Proposed branch: lp:~charlesk/indicator-datetime/lp-1456281-fix-15.04-alarm-regression
Merge into: lp:indicator-datetime/15.04
Diff against target: 1442 lines (+809/-433)
17 files modified
src/engine-eds.cpp (+348/-200)
tests/CMakeLists.txt (+15/-14)
tests/print-to.h (+10/-1)
tests/run-eds-ics-test.sh (+10/-0)
tests/test-eds-ics-all-day-events.cpp (+91/-0)
tests/test-eds-ics-all-day-events.ics (+19/-0)
tests/test-eds-ics-config-files/.config/evolution/sources/system-proxy.source (+21/-0)
tests/test-eds-ics-nonrepeating-events.cpp (+93/-0)
tests/test-eds-ics-nonrepeating-events.ics (+27/-0)
tests/test-eds-ics-repeating-events.cpp (+100/-0)
tests/test-eds-ics-repeating-events.ics (+28/-0)
tests/test-eds-ics-repeating-valarms.ics (+47/-0)
tests/test-eds-tasks-config-files/.config/evolution/sources/system-proxy.source (+0/-21)
tests/test-eds-tasks-config-files/.local/share/evolution/tasks/system/tasks.ics (+0/-28)
tests/test-eds-tasks.cpp (+0/-101)
tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source (+0/-21)
tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics (+0/-47)
To merge this branch: bzr merge lp:~charlesk/indicator-datetime/lp-1456281-fix-15.04-alarm-regression
Reviewer Review Type Date Requested Status
Ted Gould (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+259713@code.launchpad.net

Commit message

Fix regression that caused nonrepeating alarms to go off at the wrong time.

Description of the change

== Description of the Change

So, for the purposes of this description there are two kinds of ECalComponents: the primary one for an event, and instance components that are what you get when you have a repeating event.

Timezones for "floating" times (e.g., an alarm that goes off at 6:30 AM regardless of the current timezone) get distorted when you call e_cal_util_generate_alarms_for_comp() on an instance component. This caused nonrepeating alarms to go off at the wrong time. We need to build the Alarms from primary components.

So, the old code flow was

    for (instance : e_cal_client_generate_instances(client))
        for (alarm : e_cal_util_generate_alarms_for_comp(instance))

The new flow is:

    for (component : e_cal_client_get_object_list_as_comps(client))
        for (alarm : e_cal_util_generate_alarms_for_list(component))

...if only the actual code were that easy :-)

Also, ensure EDS regression tests exist for both repeating and nonrepeating alarms.

== Checklist

> Are there any related MPs required for this MP to build/function as expected? Please list.

No

> Is your branch in sync with latest trunk? (e.g. bzr pull lp:trunk -> no changes)

In sync with indicator-datetime/15.04

> Did the code build without warnings?

Yes

> Did the tests run successfully?

Yes

> Did you perform an exploratory manual test run of your code change and any related functionality?

Yes

> If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

N/A

> What device (or emulator) has your component test plan been executed successfully on?

krillin ubuntu-touch/rc-proposed/bq-aquaris.en r11

> What manual tests are relevant for this MP?

indicator-datetime/disable-one-time-alarms-after-notification

> Did you include a link to the MR Review Checklist Template to make your reviewer's life easier?

https://wiki.ubuntu.com/Process/Merges/Checklists/indicator-datetime

To post a comment you must log in.
424. By Charles Kerr

remove a little more leftover code from the false starts

Revision history for this message
Ted Gould (ted) wrote :

There seems to be a small object leak, but otherwise I can only find faults in the EDS API :-)

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
425. By Charles Kerr

in engine-eds.cpp's get_appointments(), clear the begin_str and end_str variables as soon as we're done with them.

426. By Charles Kerr

in engine-eds.cpp's on_object_list_ready(), always mark the subtask as finished even if the dbus call failed.

427. By Charles Kerr

in run-eds-ics-test.sh, slightly cleaner ics file copying

428. By Charles Kerr

in eds-engine, add a occur-in-time-range sexp to handle events that are interesting but don't require user notification alarms

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :

Looks like good updates. I really like moving the deleter function into the class, makes it way more readable and easier to track the memory.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/engine-eds.cpp'
--- src/engine-eds.cpp 2015-05-08 00:10:37 +0000
+++ src/engine-eds.cpp 2015-05-21 12:47:48 +0000
@@ -79,9 +79,6 @@
79 const Timezone& timezone,79 const Timezone& timezone,
80 std::function<void(const std::vector<Appointment>&)> func)80 std::function<void(const std::vector<Appointment>&)> func)
81 {81 {
82 const auto begin_timet = begin.to_unix();
83 const auto end_timet = end.to_unix();
84
85 const auto b_str = begin.format("%F %T");82 const auto b_str = begin.format("%F %T");
86 const auto e_str = end.format("%F %T");83 const auto e_str = end.format("%F %T");
87 g_debug("getting all appointments from [%s ... %s]", b_str.c_str(), e_str.c_str());84 g_debug("getting all appointments from [%s ... %s]", b_str.c_str(), e_str.c_str());
@@ -106,42 +103,51 @@
106 *** walk through the sources to build the appointment list103 *** walk through the sources to build the appointment list
107 **/104 **/
108105
109 auto task_deleter = [](Task* task){106 auto gtz = default_timezone != nullptr
110 // give the caller the (sorted) finished product107 ? g_time_zone_new(icaltimezone_get_location(default_timezone))
111 auto& a = task->appointments;108 : g_time_zone_new_local();
112 std::sort(a.begin(), a.end(), [](const Appointment& a, const Appointment& b){return a.begin < b.begin;});109 auto main_task = std::make_shared<Task>(this, func, default_timezone, gtz, begin, end);
113 task->func(a);
114 // we're done; delete the task
115 g_debug("time to delete task %p", (void*)task);
116 delete task;
117 };
118
119 std::shared_ptr<Task> main_task(new Task(this, func), task_deleter);
120110
121 for (auto& kv : m_clients)111 for (auto& kv : m_clients)
122 {112 {
123 auto& client = kv.second;113 auto& client = kv.second;
124 if (default_timezone != nullptr)114 if (default_timezone != nullptr)
125 e_cal_client_set_default_timezone(client, default_timezone);115 e_cal_client_set_default_timezone(client, default_timezone);
116 g_debug("calling e_cal_client_generate_instances for %p", (void*)client);
126117
127 // start a new subtask to enumerate all the components in this client.
128 auto& source = kv.first;118 auto& source = kv.first;
129 auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR);119 auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR);
130 const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension));120 const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension));
131 g_debug("calling e_cal_client_generate_instances for %p", (void*)client);121
132 auto subtask = new AppointmentSubtask(main_task,122 auto begin_str = isodate_from_time_t(begin.to_unix());
133 client,123 auto end_str = isodate_from_time_t(end.to_unix());
134 color,124 auto sexp_fmt = g_strdup_printf("(%%s? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str);
135 default_timezone,125 g_clear_pointer(&begin_str, g_free);
136 begin_timet,126 g_clear_pointer(&end_str, g_free);
137 end_timet);127
138 e_cal_client_generate_instances(client,128 // ask EDS about alarms that occur in this window...
139 begin_timet,129 auto sexp = g_strdup_printf(sexp_fmt, "has-alarms-in-range");
140 end_timet,130 g_debug("%s alarm sexp is %s", G_STRLOC, sexp);
141 m_cancellable,131 e_cal_client_get_object_list_as_comps(
142 my_get_appointments_foreach,132 client,
143 subtask,133 sexp,
144 [](gpointer g){delete static_cast<AppointmentSubtask*>(g);});134 m_cancellable,
135 on_alarm_component_list_ready,
136 new ClientSubtask(main_task, client, color));
137 g_clear_pointer(&sexp, g_free);
138
139 // ask EDS about events that occur in this window...
140 sexp = g_strdup_printf(sexp_fmt, "occur-in-time-range");
141 g_debug("%s event sexp is %s", G_STRLOC, sexp);
142 e_cal_client_get_object_list_as_comps(
143 client,
144 sexp,
145 m_cancellable,
146 on_event_component_list_ready,
147 new ClientSubtask(main_task, client, color));
148 g_clear_pointer(&sexp, g_free);
149
150 g_clear_pointer(&sexp_fmt, g_free);
145 }151 }
146 }152 }
147153
@@ -409,30 +415,45 @@
409 {415 {
410 Impl* p;416 Impl* p;
411 appointment_func func;417 appointment_func func;
418 icaltimezone* default_timezone; // pointer owned by libical
419 GTimeZone* gtz;
412 std::vector<Appointment> appointments;420 std::vector<Appointment> appointments;
413 Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {}421 const DateTime begin;
422 const DateTime end;
423
424 Task(Impl* p_in,
425 appointment_func func_in,
426 icaltimezone* tz_in,
427 GTimeZone* gtz_in,
428 const DateTime& begin_in,
429 const DateTime& end_in):
430 p{p_in},
431 func{func_in},
432 default_timezone{tz_in},
433 gtz{gtz_in},
434 begin{begin_in},
435 end{end_in} {}
436
437 ~Task() {
438 g_clear_pointer(&gtz, g_time_zone_unref);
439 // give the caller the sorted finished product
440 auto& a = appointments;
441 std::sort(a.begin(), a.end(), [](const Appointment& a, const Appointment& b){return a.begin < b.begin;});
442 func(a);
443 };
414 };444 };
415445
416 struct AppointmentSubtask446 struct ClientSubtask
417 {447 {
418 std::shared_ptr<Task> task;448 std::shared_ptr<Task> task;
419 ECalClient* client;449 ECalClient* client;
420 std::string color;450 std::string color;
421 icaltimezone* default_timezone;
422 time_t begin;
423 time_t end;
424451
425 AppointmentSubtask(const std::shared_ptr<Task>& task_in,452 ClientSubtask(const std::shared_ptr<Task>& task_in,
426 ECalClient* client_in,453 ECalClient* client_in,
427 const char* color_in,454 const char* color_in):
428 icaltimezone* default_tz,
429 time_t begin_,
430 time_t end_):
431 task(task_in),455 task(task_in),
432 client(client_in),456 client(client_in)
433 default_timezone(default_tz),
434 begin(begin_),
435 end(end_)
436 {457 {
437 if (color_in)458 if (color_in)
438 color = color_in;459 color = color_in;
@@ -482,163 +503,290 @@
482 return ret;503 return ret;
483 }504 }
484505
485 static gboolean506 static void
486 my_get_appointments_foreach(ECalComponent* component,507 on_alarm_component_list_ready(GObject * oclient,
487 time_t begin,508 GAsyncResult * res,
488 time_t end,509 gpointer gsubtask)
489 gpointer gsubtask)510 {
490 {511 GError * error = NULL;
512 GSList * comps_slist = NULL;
513 auto subtask = static_cast<ClientSubtask*>(gsubtask);
514
515 if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient),
516 res,
517 &comps_slist,
518 &error))
519 {
520 // _generate_alarms takes a GList, so make a shallow one
521 GList * comps_list = nullptr;
522 for (auto l=comps_slist; l!=nullptr; l=l->next)
523 comps_list = g_list_prepend(comps_list, l->data);
524
525 constexpr std::array<ECalComponentAlarmAction,1> omit = {
526 (ECalComponentAlarmAction)-1
527 }; // list of action types to omit, terminated with -1
528 GSList * comp_alarms = nullptr;
529 e_cal_util_generate_alarms_for_list(
530 comps_list,
531 subtask->task->begin.to_unix(),
532 subtask->task->end.to_unix(),
533 const_cast<ECalComponentAlarmAction*>(omit.data()),
534 &comp_alarms,
535 e_cal_client_resolve_tzid_cb,
536 oclient,
537 subtask->task->default_timezone);
538
539 // walk the alarms & add them
540 for (auto l=comp_alarms; l!=nullptr; l=l->next)
541 add_alarms_to_subtask(static_cast<ECalComponentAlarms*>(l->data), subtask, subtask->task->gtz);
542
543 // cleanup
544 e_cal_free_alarms(comp_alarms);
545 g_list_free(comps_list);
546 e_cal_client_free_ecalcomp_slist(comps_slist);
547 }
548 else if (error != nullptr)
549 {
550 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
551 g_warning("can't get ecalcomponent list: %s", error->message);
552
553 g_error_free(error);
554 }
555
556 delete subtask;
557 }
558
559 static void
560 on_event_component_list_ready(GObject * oclient,
561 GAsyncResult * res,
562 gpointer gsubtask)
563 {
564 GError * error = NULL;
565 GSList * comps_slist = NULL;
566 auto subtask = static_cast<ClientSubtask*>(gsubtask);
567
568 if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient),
569 res,
570 &comps_slist,
571 &error))
572 {
573 for (auto l=comps_slist; l!=nullptr; l=l->next)
574 add_event_to_subtask(static_cast<ECalComponent*>(l->data), subtask, subtask->task->gtz);
575
576 e_cal_client_free_ecalcomp_slist(comps_slist);
577 }
578 else if (error != nullptr)
579 {
580 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
581 g_warning("can't get ecalcomponent list: %s", error->message);
582
583 g_error_free(error);
584 }
585
586 delete subtask;
587 }
588
589 static DateTime
590 datetime_from_component_date_time(const ECalComponentDateTime & in,
591 GTimeZone * default_timezone)
592 {
593 DateTime out;
594
595 g_return_val_if_fail(in.value != nullptr, out);
596
597 auto gtz = in.tzid == nullptr ? g_time_zone_ref(default_timezone)
598 : g_time_zone_new(in.tzid);
599 out = DateTime(gtz,
600 in.value->year,
601 in.value->month,
602 in.value->day,
603 in.value->hour,
604 in.value->minute,
605 in.value->second);
606 g_time_zone_unref(gtz);
607 return out;
608 }
609
610 static bool
611 is_component_interesting(ECalComponent * component)
612 {
613 // we only want calendar events and vtodos
491 const auto vtype = e_cal_component_get_vtype(component);614 const auto vtype = e_cal_component_get_vtype(component);
492 auto subtask = static_cast<AppointmentSubtask*>(gsubtask);615 if ((vtype != E_CAL_COMPONENT_EVENT) &&
493616 (vtype != E_CAL_COMPONENT_TODO))
494 if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO))617 return false;
495 {618
496 const gchar* uid = nullptr;619 // we're not interested in completed or cancelled components
497 e_cal_component_get_uid(component, &uid);620 auto status = ICAL_STATUS_NONE;
498621 e_cal_component_get_status(component, &status);
499 auto status = ICAL_STATUS_NONE;622 if ((status == ICAL_STATUS_COMPLETED) ||
500 e_cal_component_get_status(component, &status);623 (status == ICAL_STATUS_CANCELLED))
501624 return false;
502 // get the timezone we want to use for generated Appointments/Alarms625
503 const char * location = icaltimezone_get_location(subtask->default_timezone);626 // we don't want disabled alarms
504 auto gtz = g_time_zone_new(location);627 bool disabled = false;
505 g_debug("timezone abbreviation is %s", g_time_zone_get_abbreviation (gtz, 0));628 GSList * categ_list = nullptr;
506629 e_cal_component_get_categories_list (component, &categ_list);
507 const DateTime begin_dt { gtz, begin };630 for (GSList * l=categ_list; l!=nullptr; l=l->next) {
508 const DateTime end_dt { gtz, end };631 auto tag = static_cast<const char*>(l->data);
509 g_debug ("got appointment from %s to %s, uid %s status %d",632 if (!g_strcmp0(tag, TAG_DISABLED))
510 begin_dt.format("%F %T %z").c_str(),633 disabled = true;
511 end_dt.format("%F %T %z").c_str(),634 }
512 uid,635 e_cal_component_free_categories_list(categ_list);
513 (int)status);636 if (disabled)
514637 return false;
515 // look for the in-house tags638
516 bool disabled = false;639 return true;
517 Appointment::Type type = Appointment::EVENT;640 }
518 GSList * categ_list = nullptr;641
519 e_cal_component_get_categories_list (component, &categ_list);642 static Appointment
520 for (GSList * l=categ_list; l!=nullptr; l=l->next) {643 get_appointment(ECalComponent * component, GTimeZone * gtz)
521 auto tag = static_cast<const char*>(l->data);644 {
522 if (!g_strcmp0(tag, TAG_ALARM))645 Appointment baseline;
523 type = Appointment::UBUNTU_ALARM;646
524 if (!g_strcmp0(tag, TAG_DISABLED))647 // get appointment.uid
525 disabled = true;648 const gchar* uid = nullptr;
526 }649 e_cal_component_get_uid(component, &uid);
527 e_cal_component_free_categories_list(categ_list);650 if (uid != nullptr)
528651 baseline.uid = uid;
529 if ((uid != nullptr) &&652
530 (!disabled) &&653 // get appointment.summary
531 (status != ICAL_STATUS_COMPLETED) &&654 ECalComponentText text {};
532 (status != ICAL_STATUS_CANCELLED))655 e_cal_component_get_summary(component, &text);
533 {656 if (text.value)
534 constexpr std::array<ECalComponentAlarmAction,1> omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1657 baseline.summary = text.value;
535 Appointment appointment;658
536659 // get appointment.begin
537 ECalComponentText text {};660 ECalComponentDateTime eccdt_tmp {};
538 e_cal_component_get_summary(component, &text);661 e_cal_component_get_dtstart(component, &eccdt_tmp);
539 if (text.value)662 baseline.begin = datetime_from_component_date_time(eccdt_tmp, gtz);
540 appointment.summary = text.value;663 e_cal_component_free_datetime(&eccdt_tmp);
541664
542 auto icc = e_cal_component_get_icalcomponent(component); // component owns icc665 // get appointment.end
543 if (icc)666 e_cal_component_get_dtend(component, &eccdt_tmp);
544 {667 baseline.end = eccdt_tmp.value != nullptr
545 g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak668 ? datetime_from_component_date_time(eccdt_tmp, gtz)
546669 : baseline.begin;
547 auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY);670 e_cal_component_free_datetime(&eccdt_tmp);
548 while (icalprop)671
549 {672 // get appointment.activation_url from x-props
550 const char * x_name = icalproperty_get_x_name(icalprop);673 auto icc = e_cal_component_get_icalcomponent(component); // icc owned by component
551 if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL))674 auto icalprop = icalcomponent_get_first_property(icc, ICAL_X_PROPERTY);
552 {675 while (icalprop != nullptr) {
553 const char * url = icalproperty_get_value_as_string(icalprop);676 const char * x_name = icalproperty_get_x_name(icalprop);
554 if ((url != nullptr) && appointment.activation_url.empty())677 if ((x_name != nullptr) && !g_ascii_strcasecmp(x_name, X_PROP_ACTIVATION_URL)) {
555 appointment.activation_url = url;678 const char * url = icalproperty_get_value_as_string(icalprop);
556 }679 if ((url != nullptr) && baseline.activation_url.empty())
557680 baseline.activation_url = url;
558 icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY);681 }
559 }682 icalprop = icalcomponent_get_next_property(icc, ICAL_X_PROPERTY);
560 }683 }
561684
562 appointment.begin = begin_dt;685 // get appointment.type
563 appointment.end = end_dt;686 baseline.type = Appointment::EVENT;
564 appointment.color = subtask->color;687 GSList * categ_list = nullptr;
565 appointment.uid = uid;688 e_cal_component_get_categories_list (component, &categ_list);
566 appointment.type = type;689 for (GSList * l=categ_list; l!=nullptr; l=l->next) {
567690 auto tag = static_cast<const char*>(l->data);
568 auto e_alarms = e_cal_util_generate_alarms_for_comp(component,691 if (!g_strcmp0(tag, TAG_ALARM))
569 subtask->begin,692 baseline.type = Appointment::UBUNTU_ALARM;
570 subtask->end,693 }
571 const_cast<ECalComponentAlarmAction*>(omit.data()),694 e_cal_component_free_categories_list(categ_list);
572 e_cal_client_resolve_tzid_cb,695
573 subtask->client,696 g_debug("%s got appointment from %s to %s: %s", G_STRLOC,
574 nullptr);697 baseline.begin.format("%F %T %z").c_str(),
575698 baseline.end.format("%F %T %z").c_str(),
576 std::map<DateTime,Alarm> alarms;699 icalcomponent_as_ical_string(icc) /* string owned by ical */);
577700
578 if (e_alarms != nullptr)701 return baseline;
579 {702 }
580 for (auto l=e_alarms->alarms; l!=nullptr; l=l->next)703
581 {704 static void
582 auto ai = static_cast<ECalComponentAlarmInstance*>(l->data);705 add_event_to_subtask(ECalComponent * component,
583 auto a = e_cal_component_get_alarm(component, ai->auid);706 ClientSubtask * subtask,
584707 GTimeZone * gtz)
585 if (a != nullptr)708 {
586 {709 // events with alarms are covered by add_alarm_to_subtask(),
587 const DateTime alarm_begin{gtz, ai->trigger};710 // so skip them here
588 auto& alarm = alarms[alarm_begin];711 auto auids = e_cal_component_get_alarm_uids(component);
589712 const bool has_alarms = auids != nullptr;
590 if (alarm.text.empty())713 cal_obj_uid_list_free(auids);
591 alarm.text = get_alarm_text(a);714 if (has_alarms)
592 if (alarm.audio_url.empty())715 return;
593 alarm.audio_url = get_alarm_sound_url(a);716
594 if (!alarm.time.is_set())717 // add it. simple, eh?
595 alarm.time = alarm_begin;718 if (is_component_interesting(component))
596719 {
597 e_cal_component_alarm_free(a);720 Appointment appointment = get_appointment(component, gtz);
598 }721 appointment.color = subtask->color;
599 }722 subtask->task->appointments.push_back(appointment);
600723 }
601 e_cal_component_alarms_free(e_alarms);724 }
602 }725
603 // Hm, no alarm triggers? 726 static void
604 // That's a bug in alarms created by some versions of ubuntu-ui-toolkit.727 add_alarms_to_subtask(ECalComponentAlarms * comp_alarms,
605 // If that's what's happening here, let's handle those alarms anyway728 ClientSubtask * subtask,
606 // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S729 GTimeZone * gtz)
607 else if (appointment.is_ubuntu_alarm())730 {
608 {731 auto& component = comp_alarms->comp;
609 Alarm tmp;732
610 tmp.time = appointment.begin;733 if (!is_component_interesting(component))
611734 return;
612 auto auids = e_cal_component_get_alarm_uids(component);735
613 for(auto l=auids; l!=nullptr; l=l->next)736 Appointment baseline = get_appointment(component, gtz);
614 {737 baseline.color = subtask->color;
615 const auto auid = static_cast<const char*>(l->data);738
616 auto a = e_cal_component_get_alarm(component, auid);739 /**
617 if (a != nullptr)740 *** Now loop through comp_alarms to get information that we need
618 {741 *** to build the instance appointments and their alarms.
619 if (tmp.text.empty())742 ***
620 tmp.text = get_alarm_text(a);743 *** Outer map key is the instance component's start + end time.
621 if (tmp.audio_url.empty())744 *** We build Appointment.begin and .end from that.
622 tmp.audio_url = get_alarm_sound_url(a);745 ***
623 e_cal_component_alarm_free(a);746 *** inner map key is the alarm trigger, we build Alarm.time from that.
624 }747 ***
625 }748 *** inner map value is the Alarm.
626 cal_obj_uid_list_free(auids);749 ***
627750 *** We map the alarms based on their trigger time so that we
628 alarms[tmp.time] = tmp;751 *** can fold together multiple valarms that trigger for the
629 }752 *** same componeng at the same time. This is commonplace;
630753 *** e.g. one valarm will have a display action and another
631 appointment.alarms.reserve(alarms.size());754 *** will specify a sound to be played.
632 for (const auto& it : alarms)755 */
633 appointment.alarms.push_back(it.second);756 std::map<std::pair<DateTime,DateTime>,std::map<DateTime,Alarm>> alarms;
634757 for (auto l=comp_alarms->alarms; l!=nullptr; l=l->next)
635 subtask->task->appointments.push_back(appointment);758 {
636 }759 auto ai = static_cast<ECalComponentAlarmInstance*>(l->data);
637760 auto a = e_cal_component_get_alarm(component, ai->auid);
638 g_time_zone_unref(gtz);761 if (a == nullptr)
639 }762 continue;
640 763
641 return G_SOURCE_CONTINUE;764 auto instance_time = std::make_pair(DateTime{gtz, ai->occur_start},
765 DateTime{gtz, ai->occur_end});
766 auto trigger_time = DateTime{gtz, ai->trigger};
767
768 auto& alarm = alarms[instance_time][trigger_time];
769
770 if (alarm.text.empty())
771 alarm.text = get_alarm_text(a);
772 if (alarm.audio_url.empty())
773 alarm.audio_url = get_alarm_sound_url(a);
774 if (!alarm.time.is_set())
775 alarm.time = trigger_time;
776
777 e_cal_component_alarm_free(a);
778 }
779
780 for (auto& i : alarms)
781 {
782 Appointment appointment = baseline;
783 appointment.begin = i.first.first;
784 appointment.end = i.first.second;
785 appointment.alarms.reserve(i.second.size());
786 for (auto& j : i.second)
787 appointment.alarms.push_back(j.second);
788 subtask->task->appointments.push_back(appointment);
789 }
642 }790 }
643791
644 /***792 /***
645793
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2015-05-08 00:08:13 +0000
+++ tests/CMakeLists.txt 2015-05-21 12:47:48 +0000
@@ -78,25 +78,26 @@
78 set(EVOLUTION_SOURCE_SERVICE_NAME "org.gnome.evolution.dataserver.Sources3")78 set(EVOLUTION_SOURCE_SERVICE_NAME "org.gnome.evolution.dataserver.Sources3")
79endif()79endif()
8080
81function(add_eds_test_by_name name)81function(add_eds_ics_test_by_name name)
82 set (TEST_NAME ${name})82 set (TEST_NAME ${name})
83 add_executable(${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled)83 add_executable(${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled)
84 target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})84 target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
85 add_test (${TEST_NAME}85 add_test (${TEST_NAME}
86 ${CMAKE_CURRENT_SOURCE_DIR}/run-eds-test.sh86 ${CMAKE_CURRENT_SOURCE_DIR}/run-eds-ics-test.sh
87 ${DBUS_RUNNER} # arg1: dbus-test-runner exec87 ${DBUS_RUNNER} # arg1: dbus-test-runner exec
88 ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path88 ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path
89 ${TEST_NAME} # arg3: test name89 ${TEST_NAME} # arg3: test name
90 ${EVOLUTION_CALENDAR_FACTORY} # arg4: evolution-calendar-factory exec90 ${EVOLUTION_CALENDAR_FACTORY} # arg4: evolution-calendar-factory exec
91 ${EVOLUTION_SOURCE_SERVICE_NAME} # arg5: dbus name for source registry 91 ${EVOLUTION_SOURCE_SERVICE_NAME} # arg5: dbus name for source registry
92 ${EVOLUTION_SOURCE_REGISTRY} # arg6: evolution-source-registry exec92 ${EVOLUTION_SOURCE_REGISTRY} # arg6: evolution-source-registry exec
93 ${GVFSD} # arg7: gvfsd exec93 ${GVFSD} # arg7: gvfsd exec
94 ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}-config-files) # arg8: canned config files94 ${CMAKE_CURRENT_SOURCE_DIR}/test-eds-ics-config-files # arg8: base directory for config file template
95 ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.ics) # arg9: the ical file for this test
95endfunction()96endfunction()
96add_eds_test_by_name(test-eds-tasks)97add_eds_ics_test_by_name(test-eds-ics-all-day-events)
97add_eds_test_by_name(test-eds-valarms)98add_eds_ics_test_by_name(test-eds-ics-repeating-events)
9899add_eds_ics_test_by_name(test-eds-ics-nonrepeating-events)
99100add_eds_ics_test_by_name(test-eds-ics-repeating-valarms)
100101
101102
102# disabling the timezone unit tests because they require103# disabling the timezone unit tests because they require
103104
=== modified file 'tests/print-to.h'
--- tests/print-to.h 2015-04-06 15:20:49 +0000
+++ tests/print-to.h 2015-05-21 12:47:48 +0000
@@ -33,9 +33,18 @@
33***/33***/
3434
35void35void
36PrintTo(const DateTime& datetime, std::ostream* os)
37{
38 *os << "{time:'" << datetime.format("%F %T %z") << '}';
39}
40
41void
36PrintTo(const Alarm& alarm, std::ostream* os)42PrintTo(const Alarm& alarm, std::ostream* os)
37{43{
38 *os << "{text:'" << alarm.text << "', audio_url:'" << alarm.audio_url << "', time:'"<<alarm.time.format("%F %T")<<"'}";44 *os << '{';
45 *os << "{text:" << alarm.text << '}';
46 PrintTo(alarm.time, os);
47 *os << '}';
39}48}
4049
41} // namespace datetime50} // namespace datetime
4251
=== renamed file 'tests/run-eds-test.sh' => 'tests/run-eds-ics-test.sh'
--- tests/run-eds-test.sh 2015-04-06 20:09:16 +0000
+++ tests/run-eds-ics-test.sh 2015-05-21 12:47:48 +0000
@@ -9,6 +9,7 @@
9echo ARG6=$6 # full exectuable path of evolution-source-registry9echo ARG6=$6 # full exectuable path of evolution-source-registry
10echo ARG7=$7 # full executable path of gvfs10echo ARG7=$7 # full executable path of gvfs
11echo ARG8=$8 # config files11echo ARG8=$8 # config files
12echo ARG8=$9 # ics file
1213
13# set up the tmpdir and tell the shell to purge it when we exit14# set up the tmpdir and tell the shell to purge it when we exit
14export TEST_TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d $3-XXXXXXXXXX) || exit 115export TEST_TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d $3-XXXXXXXXXX) || exit 1
@@ -32,6 +33,9 @@
32export QORGANIZER_EDS_DEBUG=On33export QORGANIZER_EDS_DEBUG=On
33export GIO_USE_VFS=local # needed to ensure GVFS shuts down cleanly after the test is over34export GIO_USE_VFS=local # needed to ensure GVFS shuts down cleanly after the test is over
3435
36export G_MESSAGES_DEBUG=all
37export G_DBUS_DEBUG=messages
38
35echo HOMEDIR=${HOME}39echo HOMEDIR=${HOME}
36rm -rf ${XDG_DATA_HOME}40rm -rf ${XDG_DATA_HOME}
3741
@@ -41,6 +45,12 @@
41 cp --verbose --archive $8/. $HOME45 cp --verbose --archive $8/. $HOME
42fi46fi
4347
48# if there's a specific ics file to test, copy it on top of the canned confilg files
49if [ -e $9 ]; then
50 echo "copying $9 into $HOME"
51 cp --verbose --archive $9 ${XDG_DATA_HOME}/evolution/tasks/system/tasks.ics
52fi
53
44# run dbus-test-runner54# run dbus-test-runner
45$1 --keep-env --max-wait=90 \55$1 --keep-env --max-wait=90 \
46--task $2 --task-name $3 --wait-until-complete --wait-for=org.gnome.evolution.dataserver.Calendar4 \56--task $2 --task-name $3 --wait-until-complete --wait-for=org.gnome.evolution.dataserver.Calendar4 \
4757
=== added file 'tests/test-eds-ics-all-day-events.cpp'
--- tests/test-eds-ics-all-day-events.cpp 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-all-day-events.cpp 2015-05-21 12:47:48 +0000
@@ -0,0 +1,91 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Kerr <charles.kerr@canonical.com>
18 */
19
20#include <algorithm>
21
22#include <datetime/alarm-queue-simple.h>
23#include <datetime/clock-mock.h>
24#include <datetime/engine-eds.h>
25#include <datetime/planner-range.h>
26
27#include <gtest/gtest.h>
28
29#include "glib-fixture.h"
30#include "print-to.h"
31#include "timezone-mock.h"
32#include "wakeup-timer-mock.h"
33
34using namespace unity::indicator::datetime;
35using VAlarmFixture = GlibFixture;
36
37/***
38****
39***/
40
41TEST_F(VAlarmFixture, MultipleAppointments)
42{
43 // start the EDS engine
44 auto engine = std::make_shared<EdsEngine>();
45
46 // we need a consistent timezone for the planner and our local DateTimes
47 constexpr char const * zone_str {"America/Chicago"};
48 auto tz = std::make_shared<MockTimezone>(zone_str);
49 auto gtz = g_time_zone_new(zone_str);
50
51 // make a planner that looks at the first half of 2015 in EDS
52 auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
53 const DateTime range_begin {gtz, 2015,1, 1, 0, 0, 0.0};
54 const DateTime range_end {gtz, 2015,6,31,23,59,59.5};
55 planner->range().set(std::make_pair(range_begin, range_end));
56
57 // give EDS a moment to load
58 if (planner->appointments().get().empty()) {
59 g_message("waiting a moment for EDS to load...");
60 auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
61 g_message("ah, they loaded");
62 if (!appointments.empty())
63 g_main_loop_quit(loop);
64 };
65 core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
66 constexpr int max_wait_sec = 10;
67 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
68 }
69
70 // what we expect to get...
71 Appointment expected_appt;
72 expected_appt.uid = "20150521T111538Z-7449-1000-3572-0@ghidorah";
73 expected_appt.color = "#becedd";
74 expected_appt.summary = "Memorial Day";
75 expected_appt.begin = DateTime{gtz,2015,5,25,0,0,0};
76 expected_appt.end = DateTime{gtz,2015,5,26,0,0,0};
77
78 // compare it to what we actually loaded...
79 const auto appts = planner->appointments().get();
80 ASSERT_EQ(1, appts.size());
81 const auto& appt = appts[0];
82 EXPECT_EQ(expected_appt.begin, appt.begin);
83 EXPECT_EQ(expected_appt.end, appt.end);
84 EXPECT_EQ(expected_appt.uid, appt.uid);
85 EXPECT_EQ(expected_appt.color, appt.color);
86 EXPECT_EQ(expected_appt.summary, appt.summary);
87 EXPECT_EQ(0, appt.alarms.size());
88
89 // cleanup
90 g_time_zone_unref(gtz);
91}
092
=== added file 'tests/test-eds-ics-all-day-events.ics'
--- tests/test-eds-ics-all-day-events.ics 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-all-day-events.ics 2015-05-21 12:47:48 +0000
@@ -0,0 +1,19 @@
1BEGIN:VCALENDAR
2CALSCALE:GREGORIAN
3PRODID:-//Ximian//NONSGML Evolution Calendar//EN
4VERSION:2.0
5X-EVOLUTION-DATA-REVISION:2015-05-07T21:14:49.315443Z(0)
6BEGIN:VEVENT
7UID:20150521T111538Z-7449-1000-3572-0@ghidorah
8DTSTAMP:20150521T001128Z
9DTSTART;VALUE=DATE:20150525
10DTEND;VALUE=DATE:20150526
11TRANSP:TRANSPARENT
12SEQUENCE:2
13SUMMARY:Memorial Day
14DESCRIPTION:Today is Memorial Day
15CLASS:PUBLIC
16CREATED:20150521T111638Z
17LAST-MODIFIED:20150521T111638Z
18END:VEVENT
19END:VCALENDAR
020
=== added directory 'tests/test-eds-ics-config-files'
=== added directory 'tests/test-eds-ics-config-files/.config'
=== added directory 'tests/test-eds-ics-config-files/.config/evolution'
=== added directory 'tests/test-eds-ics-config-files/.config/evolution/sources'
=== added file 'tests/test-eds-ics-config-files/.config/evolution/sources/system-proxy.source'
--- tests/test-eds-ics-config-files/.config/evolution/sources/system-proxy.source 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-config-files/.config/evolution/sources/system-proxy.source 2015-05-21 12:47:48 +0000
@@ -0,0 +1,21 @@
1
2[Data Source]
3DisplayName=Default Proxy Settings
4Enabled=true
5Parent=
6
7[Proxy]
8Method=default
9IgnoreHosts=localhost;127.0.0.0/8;::1;
10AutoconfigUrl=
11FtpHost=
12FtpPort=0
13HttpAuthPassword=
14HttpAuthUser=
15HttpHost=
16HttpPort=8080
17HttpUseAuth=false
18HttpsHost=
19HttpsPort=0
20SocksHost=
21SocksPort=0
022
=== added directory 'tests/test-eds-ics-config-files/.local'
=== added directory 'tests/test-eds-ics-config-files/.local/share'
=== added directory 'tests/test-eds-ics-config-files/.local/share/evolution'
=== added directory 'tests/test-eds-ics-config-files/.local/share/evolution/calendar'
=== added directory 'tests/test-eds-ics-config-files/.local/share/evolution/calendar/system'
=== added directory 'tests/test-eds-ics-config-files/.local/share/evolution/tasks'
=== added directory 'tests/test-eds-ics-config-files/.local/share/evolution/tasks/system'
=== added file 'tests/test-eds-ics-nonrepeating-events.cpp'
--- tests/test-eds-ics-nonrepeating-events.cpp 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-nonrepeating-events.cpp 2015-05-21 12:47:48 +0000
@@ -0,0 +1,93 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Kerr <charles.kerr@canonical.com>
18 */
19
20#include <algorithm>
21
22#include <datetime/alarm-queue-simple.h>
23#include <datetime/clock-mock.h>
24#include <datetime/engine-eds.h>
25#include <datetime/planner-range.h>
26
27#include <gtest/gtest.h>
28
29#include "glib-fixture.h"
30#include "print-to.h"
31#include "timezone-mock.h"
32#include "wakeup-timer-mock.h"
33
34using namespace unity::indicator::datetime;
35using VAlarmFixture = GlibFixture;
36
37/***
38****
39***/
40
41TEST_F(VAlarmFixture, MultipleAppointments)
42{
43 // start the EDS engine
44 auto engine = std::make_shared<EdsEngine>();
45
46 // we need a consistent timezone for the planner and our local DateTimes
47 constexpr char const * zone_str {"America/Chicago"};
48 auto tz = std::make_shared<MockTimezone>(zone_str);
49 auto gtz = g_time_zone_new(zone_str);
50
51 // make a planner that looks at the first half of 2015 in EDS
52 auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
53 const DateTime range_begin {gtz, 2015,1, 1, 0, 0, 0.0};
54 const DateTime range_end {gtz, 2015,6,31,23,59,59.5};
55 planner->range().set(std::make_pair(range_begin, range_end));
56
57 // give EDS a moment to load
58 if (planner->appointments().get().empty()) {
59 g_message("waiting a moment for EDS to load...");
60 auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
61 g_message("ah, they loaded");
62 if (!appointments.empty())
63 g_main_loop_quit(loop);
64 };
65 core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
66 constexpr int max_wait_sec = 10;
67 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
68 }
69
70 // what we expect to get...
71 Appointment expected_appt;
72 expected_appt.uid = "20150520T000726Z-3878-32011-1770-81@ubuntu-phablet";
73 expected_appt.color = "#becedd";
74 expected_appt.summary = "Alarm";
75 std::array<Alarm,1> expected_alarms = {
76 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,20,20,00,0)})
77 };
78
79 // compare it to what we actually loaded...
80 const auto appts = planner->appointments().get();
81 EXPECT_EQ(expected_alarms.size(), appts.size());
82 for (size_t i=0, n=expected_alarms.size(); i<n; i++) {
83 const auto& appt = appts[i];
84 EXPECT_EQ(expected_appt.uid, appt.uid);
85 EXPECT_EQ(expected_appt.color, appt.color);
86 EXPECT_EQ(expected_appt.summary, appt.summary);
87 EXPECT_EQ(1, appt.alarms.size());
88 EXPECT_EQ(expected_alarms[i], appt.alarms[0]);
89 }
90
91 // cleanup
92 g_time_zone_unref(gtz);
93}
094
=== added file 'tests/test-eds-ics-nonrepeating-events.ics'
--- tests/test-eds-ics-nonrepeating-events.ics 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-nonrepeating-events.ics 2015-05-21 12:47:48 +0000
@@ -0,0 +1,27 @@
1BEGIN:VCALENDAR
2CALSCALE:GREGORIAN
3PRODID:-//Ximian//NONSGML Evolution Calendar//EN
4VERSION:2.0
5X-EVOLUTION-DATA-REVISION:2015-05-20T22:39:32.685099Z(1)
6BEGIN:VTODO
7UID:20150520T000726Z-3878-32011-1770-81@ubuntu-phablet
8DTSTAMP:20150520T223932Z
9DTSTART:20150520T200000
10SUMMARY:Alarm
11CATEGORIES:x-canonical-alarm
12SEQUENCE:1
13LAST-MODIFIED:20150520T223932Z
14BEGIN:VALARM
15X-EVOLUTION-ALARM-UID:20150520T223932Z-22506-32011-1771-2@ubuntu-phablet
16ACTION:AUDIO
17ATTACH:file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg
18TRIGGER;VALUE=DURATION;RELATED=START:PT0S
19END:VALARM
20BEGIN:VALARM
21X-EVOLUTION-ALARM-UID:20150520T223932Z-22506-32011-1771-3@ubuntu-phablet
22ACTION:DISPLAY
23DESCRIPTION:Alarm
24TRIGGER;VALUE=DURATION;RELATED=START:PT0S
25END:VALARM
26END:VTODO
27END:VCALENDAR
028
=== added file 'tests/test-eds-ics-repeating-events.cpp'
--- tests/test-eds-ics-repeating-events.cpp 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-repeating-events.cpp 2015-05-21 12:47:48 +0000
@@ -0,0 +1,100 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Kerr <charles.kerr@canonical.com>
18 */
19
20#include <algorithm>
21
22#include <datetime/alarm-queue-simple.h>
23#include <datetime/clock-mock.h>
24#include <datetime/engine-eds.h>
25#include <datetime/planner-range.h>
26
27#include <gtest/gtest.h>
28
29#include "glib-fixture.h"
30#include "print-to.h"
31#include "timezone-mock.h"
32#include "wakeup-timer-mock.h"
33
34using namespace unity::indicator::datetime;
35using VAlarmFixture = GlibFixture;
36
37/***
38****
39***/
40
41TEST_F(VAlarmFixture, MultipleAppointments)
42{
43 // start the EDS engine
44 auto engine = std::make_shared<EdsEngine>();
45
46 // we need a consistent timezone for the planner and our local DateTimes
47 constexpr char const * zone_str {"America/Chicago"};
48 auto tz = std::make_shared<MockTimezone>(zone_str);
49 auto gtz = g_time_zone_new(zone_str);
50
51 // make a planner that looks at the first half of 2015 in EDS
52 auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
53 const DateTime range_begin {gtz, 2015,1, 1, 0, 0, 0.0};
54 const DateTime range_end {gtz, 2015,6,31,23,59,59.5};
55 planner->range().set(std::make_pair(range_begin, range_end));
56
57 // give EDS a moment to load
58 if (planner->appointments().get().empty()) {
59 g_message("waiting a moment for EDS to load...");
60 auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
61 g_message("ah, they loaded");
62 if (!appointments.empty())
63 g_main_loop_quit(loop);
64 };
65 core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
66 constexpr int max_wait_sec = 10;
67 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
68 }
69
70 // what we expect to get...
71 Appointment expected_appt;
72 expected_appt.uid = "20150507T211449Z-4262-32011-1418-1@ubuntu-phablet";
73 expected_appt.color = "#becedd";
74 expected_appt.summary = "Alarm";
75 std::array<Alarm,8> expected_alarms = {
76 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5, 8,16,40,0)}),
77 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,15,16,40,0)}),
78 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,22,16,40,0)}),
79 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,29,16,40,0)}),
80 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6, 5,16,40,0)}),
81 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6,12,16,40,0)}),
82 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6,19,16,40,0)}),
83 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6,26,16,40,0)})
84 };
85
86 // compare it to what we actually loaded...
87 const auto appts = planner->appointments().get();
88 EXPECT_EQ(expected_alarms.size(), appts.size());
89 for (size_t i=0, n=expected_alarms.size(); i<n; i++) {
90 const auto& appt = appts[i];
91 EXPECT_EQ(expected_appt.uid, appt.uid);
92 EXPECT_EQ(expected_appt.color, appt.color);
93 EXPECT_EQ(expected_appt.summary, appt.summary);
94 EXPECT_EQ(1, appt.alarms.size());
95 EXPECT_EQ(expected_alarms[i], appt.alarms[0]);
96 }
97
98 // cleanup
99 g_time_zone_unref(gtz);
100}
0101
=== added file 'tests/test-eds-ics-repeating-events.ics'
--- tests/test-eds-ics-repeating-events.ics 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-repeating-events.ics 2015-05-21 12:47:48 +0000
@@ -0,0 +1,28 @@
1BEGIN:VCALENDAR
2CALSCALE:GREGORIAN
3PRODID:-//Ximian//NONSGML Evolution Calendar//EN
4VERSION:2.0
5X-EVOLUTION-DATA-REVISION:2015-05-07T21:14:49.315443Z(0)
6BEGIN:VTODO
7UID:20150507T211449Z-4262-32011-1418-1@ubuntu-phablet
8DTSTAMP:20150508T211449Z
9DTSTART:20150508T164000
10RRULE:FREQ=WEEKLY;BYDAY=FR
11SUMMARY:Alarm
12CATEGORIES:x-canonical-alarm
13CREATED:20150507T211449Z
14LAST-MODIFIED:20150507T211449Z
15BEGIN:VALARM
16X-EVOLUTION-ALARM-UID:20150507T211449Z-4262-32011-1418-2@ubuntu-phablet
17ACTION:AUDIO
18ATTACH:file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg
19TRIGGER;VALUE=DURATION;RELATED=START:PT0S
20END:VALARM
21BEGIN:VALARM
22X-EVOLUTION-ALARM-UID:20150507T211449Z-4262-32011-1418-3@ubuntu-phablet
23ACTION:DISPLAY
24DESCRIPTION:Alarm
25TRIGGER;VALUE=DURATION;RELATED=START:PT0S
26END:VALARM
27END:VTODO
28END:VCALENDAR
029
=== renamed file 'tests/test-eds-valarms.cpp' => 'tests/test-eds-ics-repeating-valarms.cpp'
=== added file 'tests/test-eds-ics-repeating-valarms.ics'
--- tests/test-eds-ics-repeating-valarms.ics 1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-repeating-valarms.ics 2015-05-21 12:47:48 +0000
@@ -0,0 +1,47 @@
1BEGIN:VCALENDAR
2CALSCALE:GREGORIAN
3PRODID:-//Ximian//NONSGML Evolution Calendar//EN
4VERSION:2.0
5X-EVOLUTION-DATA-REVISION:2015-04-05T21:32:47.354433Z(2)
6BEGIN:VEVENT
7UID:20150405T213247Z-4371-32011-1698-1@ubuntu-phablet
8DTSTAMP:20150405T213247Z
9DTSTART:20150424T183500Z
10DTEND:20150424T193554Z
11X-LIC-ERROR;X-LIC-ERRORTYPE=VALUE-PARSE-ERROR:Can't parse as RECUR value
12 in RRULE property. Removing entire property: ERROR: No Value
13SUMMARY:London Sprint Flight
14CREATED:20150405T213247Z
15LAST-MODIFIED:20150405T213247Z
16BEGIN:VALARM
17X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-2@ubuntu-phablet
18ACTION:AUDIO
19TRIGGER;VALUE=DURATION;RELATED=START:-P1D
20REPEAT:3
21DURATION:PT2M
22END:VALARM
23BEGIN:VALARM
24X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-3@ubuntu-phablet
25ACTION:DISPLAY
26DESCRIPTION:Time to pack!
27TRIGGER;VALUE=DURATION;RELATED=START:-P1D
28REPEAT:3
29DURATION:PT2M
30END:VALARM
31BEGIN:VALARM
32X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-5@ubuntu-phablet
33ACTION:AUDIO
34TRIGGER;VALUE=DURATION;RELATED=START:-PT3H
35REPEAT:3
36DURATION:PT2M
37END:VALARM
38BEGIN:VALARM
39X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-6@ubuntu-phablet
40ACTION:DISPLAY
41DESCRIPTION:Go to the airport!
42TRIGGER;VALUE=DURATION;RELATED=START:-PT3H
43REPEAT:3
44DURATION:PT2M
45END:VALARM
46END:VEVENT
47END:VCALENDAR
048
=== removed directory 'tests/test-eds-tasks-config-files'
=== removed directory 'tests/test-eds-tasks-config-files/.config'
=== removed directory 'tests/test-eds-tasks-config-files/.config/evolution'
=== removed directory 'tests/test-eds-tasks-config-files/.config/evolution/sources'
=== removed file 'tests/test-eds-tasks-config-files/.config/evolution/sources/system-proxy.source'
--- tests/test-eds-tasks-config-files/.config/evolution/sources/system-proxy.source 2015-05-08 00:08:13 +0000
+++ tests/test-eds-tasks-config-files/.config/evolution/sources/system-proxy.source 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
1
2[Data Source]
3DisplayName=Default Proxy Settings
4Enabled=true
5Parent=
6
7[Proxy]
8Method=default
9IgnoreHosts=localhost;127.0.0.0/8;::1;
10AutoconfigUrl=
11FtpHost=
12FtpPort=0
13HttpAuthPassword=
14HttpAuthUser=
15HttpHost=
16HttpPort=8080
17HttpUseAuth=false
18HttpsHost=
19HttpsPort=0
20SocksHost=
21SocksPort=0
220
=== removed directory 'tests/test-eds-tasks-config-files/.local'
=== removed directory 'tests/test-eds-tasks-config-files/.local/share'
=== removed directory 'tests/test-eds-tasks-config-files/.local/share/evolution'
=== removed directory 'tests/test-eds-tasks-config-files/.local/share/evolution/tasks'
=== removed directory 'tests/test-eds-tasks-config-files/.local/share/evolution/tasks/system'
=== removed file 'tests/test-eds-tasks-config-files/.local/share/evolution/tasks/system/tasks.ics'
--- tests/test-eds-tasks-config-files/.local/share/evolution/tasks/system/tasks.ics 2015-05-08 00:08:13 +0000
+++ tests/test-eds-tasks-config-files/.local/share/evolution/tasks/system/tasks.ics 1970-01-01 00:00:00 +0000
@@ -1,28 +0,0 @@
1BEGIN:VCALENDAR
2CALSCALE:GREGORIAN
3PRODID:-//Ximian//NONSGML Evolution Calendar//EN
4VERSION:2.0
5X-EVOLUTION-DATA-REVISION:2015-05-07T21:14:49.315443Z(0)
6BEGIN:VTODO
7UID:20150507T211449Z-4262-32011-1418-1@ubuntu-phablet
8DTSTAMP:20150508T211449Z
9DTSTART:20150508T164000
10RRULE:FREQ=WEEKLY;BYDAY=FR
11SUMMARY:Alarm
12CATEGORIES:x-canonical-alarm
13CREATED:20150507T211449Z
14LAST-MODIFIED:20150507T211449Z
15BEGIN:VALARM
16X-EVOLUTION-ALARM-UID:20150507T211449Z-4262-32011-1418-2@ubuntu-phablet
17ACTION:AUDIO
18ATTACH:file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg
19TRIGGER;VALUE=DURATION;RELATED=START:PT0S
20END:VALARM
21BEGIN:VALARM
22X-EVOLUTION-ALARM-UID:20150507T211449Z-4262-32011-1418-3@ubuntu-phablet
23ACTION:DISPLAY
24DESCRIPTION:Alarm
25TRIGGER;VALUE=DURATION;RELATED=START:PT0S
26END:VALARM
27END:VTODO
28END:VCALENDAR
290
=== removed file 'tests/test-eds-tasks.cpp'
--- tests/test-eds-tasks.cpp 2015-05-08 00:08:13 +0000
+++ tests/test-eds-tasks.cpp 1970-01-01 00:00:00 +0000
@@ -1,101 +0,0 @@
1/*
2 * Copyright 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Kerr <charles.kerr@canonical.com>
18 */
19
20#include <algorithm>
21
22#include <datetime/alarm-queue-simple.h>
23#include <datetime/clock-mock.h>
24#include <datetime/engine-eds.h>
25#include <datetime/planner-range.h>
26
27#include <gtest/gtest.h>
28
29#include "glib-fixture.h"
30#include "print-to.h"
31#include "timezone-mock.h"
32#include "wakeup-timer-mock.h"
33
34using namespace unity::indicator::datetime;
35using VAlarmFixture = GlibFixture;
36
37/***
38****
39***/
40
41TEST_F(VAlarmFixture, MultipleAppointments)
42{
43 // start the EDS engine
44 auto engine = std::make_shared<EdsEngine>();
45
46 // we need a consistent timezone for the planner and our local DateTimes
47 constexpr char const * zone_str {"America/Chicago"};
48 auto tz = std::make_shared<MockTimezone>(zone_str);
49 auto gtz = g_time_zone_new(zone_str);
50
51 // make a planner that looks at the first half of 2015 in EDS
52 auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
53 const DateTime range_begin {gtz, 2015,1, 1, 0, 0, 0.0};
54 const DateTime range_end {gtz, 2015,6,31,23,59,59.5};
55 planner->range().set(std::make_pair(range_begin, range_end));
56
57 // give EDS a moment to load
58 if (planner->appointments().get().empty()) {
59 g_message("waiting a moment for EDS to load...");
60 auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
61 g_message("ah, they loaded");
62 if (!appointments.empty())
63 g_main_loop_quit(loop);
64 };
65 core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
66 constexpr int max_wait_sec = 10;
67 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
68 }
69
70 // what we expect to get...
71 Appointment expected_appt;
72 expected_appt.uid = "20150507T211449Z-4262-32011-1418-1@ubuntu-phablet";
73 expected_appt.color = "#becedd";
74 expected_appt.summary = "Alarm";
75 std::array<Alarm,8> expected_alarms = {
76 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5, 8,16,40,0)}),
77 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,15,16,40,0)}),
78 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,22,16,40,0)}),
79 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,5,29,16,40,0)}),
80 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6, 5,16,40,0)}),
81 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6,12,16,40,0)}),
82 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6,19,16,40,0)}),
83 Alarm({"Alarm", "file:///usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg", DateTime(gtz,2015,6,26,16,40,0)})
84 };
85
86 // compare it to what we actually loaded...
87 const auto appts = planner->appointments().get();
88 EXPECT_EQ(expected_alarms.size(), appts.size());
89 for (size_t i=0, n=expected_alarms.size(); i<n; i++) {
90 const auto& appt = appts[i];
91 EXPECT_EQ(expected_appt.uid, appt.uid);
92 EXPECT_EQ(expected_appt.color, appt.color);
93 EXPECT_EQ(expected_appt.summary, appt.summary);
94 EXPECT_EQ(1, appt.alarms.size());
95 EXPECT_EQ(expected_alarms[i], appt.alarms[0]);
96 }
97
98
99 // cleanup
100 g_time_zone_unref(gtz);
101}
1020
=== removed directory 'tests/test-eds-valarms-config-files'
=== removed directory 'tests/test-eds-valarms-config-files/.config'
=== removed directory 'tests/test-eds-valarms-config-files/.config/evolution'
=== removed directory 'tests/test-eds-valarms-config-files/.config/evolution/sources'
=== removed file 'tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source'
--- tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source 2015-04-06 00:19:01 +0000
+++ tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source 1970-01-01 00:00:00 +0000
@@ -1,21 +0,0 @@
1
2[Data Source]
3DisplayName=Default Proxy Settings
4Enabled=true
5Parent=
6
7[Proxy]
8Method=default
9IgnoreHosts=localhost;127.0.0.0/8;::1;
10AutoconfigUrl=
11FtpHost=
12FtpPort=0
13HttpAuthPassword=
14HttpAuthUser=
15HttpHost=
16HttpPort=8080
17HttpUseAuth=false
18HttpsHost=
19HttpsPort=0
20SocksHost=
21SocksPort=0
220
=== removed directory 'tests/test-eds-valarms-config-files/.local'
=== removed directory 'tests/test-eds-valarms-config-files/.local/share'
=== removed directory 'tests/test-eds-valarms-config-files/.local/share/evolution'
=== removed directory 'tests/test-eds-valarms-config-files/.local/share/evolution/calendar'
=== removed directory 'tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system'
=== removed file 'tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics'
--- tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics 2015-04-06 00:19:01 +0000
+++ tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics 1970-01-01 00:00:00 +0000
@@ -1,47 +0,0 @@
1BEGIN:VCALENDAR
2CALSCALE:GREGORIAN
3PRODID:-//Ximian//NONSGML Evolution Calendar//EN
4VERSION:2.0
5X-EVOLUTION-DATA-REVISION:2015-04-05T21:32:47.354433Z(2)
6BEGIN:VEVENT
7UID:20150405T213247Z-4371-32011-1698-1@ubuntu-phablet
8DTSTAMP:20150405T213247Z
9DTSTART:20150424T183500Z
10DTEND:20150424T193554Z
11X-LIC-ERROR;X-LIC-ERRORTYPE=VALUE-PARSE-ERROR:Can't parse as RECUR value
12 in RRULE property. Removing entire property: ERROR: No Value
13SUMMARY:London Sprint Flight
14CREATED:20150405T213247Z
15LAST-MODIFIED:20150405T213247Z
16BEGIN:VALARM
17X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-2@ubuntu-phablet
18ACTION:AUDIO
19TRIGGER;VALUE=DURATION;RELATED=START:-P1D
20REPEAT:3
21DURATION:PT2M
22END:VALARM
23BEGIN:VALARM
24X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-3@ubuntu-phablet
25ACTION:DISPLAY
26DESCRIPTION:Time to pack!
27TRIGGER;VALUE=DURATION;RELATED=START:-P1D
28REPEAT:3
29DURATION:PT2M
30END:VALARM
31BEGIN:VALARM
32X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-5@ubuntu-phablet
33ACTION:AUDIO
34TRIGGER;VALUE=DURATION;RELATED=START:-PT3H
35REPEAT:3
36DURATION:PT2M
37END:VALARM
38BEGIN:VALARM
39X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-6@ubuntu-phablet
40ACTION:DISPLAY
41DESCRIPTION:Go to the airport!
42TRIGGER;VALUE=DURATION;RELATED=START:-PT3H
43REPEAT:3
44DURATION:PT2M
45END:VALARM
46END:VEVENT
47END:VCALENDAR

Subscribers

People subscribed via source and target branches