=== modified file 'debian/changelog'
--- debian/changelog	2015-06-18 19:25:41 +0000
+++ debian/changelog	2015-07-10 17:29:43 +0000
@@ -1,3 +1,26 @@
+indicator-datetime (13.10.0+15.10.20150624-0ubuntu1) wily; urgency=medium
+
+  [ Charles Kerr ]
+  * Speedups/cleanups to the EDS regression tests
+
+ -- CI Train Bot <ci-train-bot@canonical.com>  Wed, 24 Jun 2015 12:51:27 +0000
+
+indicator-datetime (13.10.0+15.10.20150622-0ubuntu1) wily; urgency=medium
+
+  [ Charles Kerr ]
+  * Fix bugs relating to timezones and triggers from clock-app alarms.
+    (LP: #1456281, #1465806)
+
+ -- CI Train Bot <ci-train-bot@canonical.com>  Mon, 22 Jun 2015 18:03:38 +0000
+
+indicator-datetime (13.10.0+15.10.20150515-0ubuntu1) wily; urgency=medium
+
+  [ Charles Kerr ]
+  * Honor the 'other vibrations' setting to enable/disable vibrations
+    when alarm notifications are shown.
+
+ -- CI Train Bot <ci-train-bot@canonical.com>  Fri, 15 May 2015 16:41:41 +0000
+
 indicator-datetime (13.10.0+15.04.20150618-0ubuntu1) vivid; urgency=medium
 
   [ Charles Kerr ]
@@ -5,33 +28,6 @@
 
  -- CI Train Bot <ci-train-bot@canonical.com>  Thu, 18 Jun 2015 19:25:41 +0000
 
-indicator-datetime (13.10.0+15.04.20150521-0ubuntu1) vivid; urgency=medium
-
-  [ Charles Kerr ]
-  * Fix regression that caused nonrepeating alarms to go off at the
-    wrong time. (LP: #1456281)
-
- -- CI Train Bot <ci-train-bot@canonical.com>  Thu, 21 May 2015 15:24:31 +0000
-
-indicator-datetime (13.10.0+15.04.20150515-0ubuntu1) vivid; urgency=medium
-
-  [ Charles Kerr ]
-  * Honor the 'other vibrations' setting to enable/disable vibrations
-    when alarm notifications are shown.
-
- -- CI Train Bot <ci-train-bot@canonical.com>  Fri, 15 May 2015 19:33:54 +0000
-
-indicator-datetime (13.10.0+15.04.20150508.2-0ubuntu1) vivid; urgency=medium
-
-  [ CI Train Bot ]
-  * New rebuild forced.
-
-  [ Charles Kerr ]
-  * If the "X-CANONICAL-ACTIVATION-URL" x-prop is defined in the VTODO
-    or VEVENT, use it for url delegation. (LP: #1426519)
-
- -- CI Train Bot <ci-train-bot@canonical.com>  Fri, 08 May 2015 20:01:09 +0000
-
 indicator-datetime (13.10.0+15.04.20150406-0ubuntu1) vivid; urgency=medium
 
   [ Charles Kerr ]

=== modified file 'src/engine-eds.cpp'
--- src/engine-eds.cpp	2015-06-18 04:53:52 +0000
+++ src/engine-eds.cpp	2015-07-10 17:29:43 +0000
@@ -47,16 +47,21 @@
 {
 public:
 
-    Impl():
-        m_cancellable(g_cancellable_new())
+    Impl()
     {
-        e_source_registry_new(m_cancellable, on_source_registry_ready, this);
+        auto cancellable_deleter = [](GCancellable * c) {
+            g_cancellable_cancel(c);
+            g_clear_object(&c);
+        };
+
+        m_cancellable = std::shared_ptr<GCancellable>(g_cancellable_new(), cancellable_deleter);
+
+        e_source_registry_new(m_cancellable.get(), on_source_registry_ready, this);
     }
 
     ~Impl()
     {
-        g_cancellable_cancel(m_cancellable);
-        g_clear_object(&m_cancellable);
+        m_cancellable.reset();
 
         while(!m_sources.empty())
             remove_source(*m_sources.begin());
@@ -131,9 +136,9 @@
             e_cal_client_get_object_list_as_comps(
                 client,
                 sexp,
-                m_cancellable,
+                m_cancellable.get(),
                 on_alarm_component_list_ready,
-                new ClientSubtask(main_task, client, color));
+                new ClientSubtask(main_task, client, m_cancellable, color));
             g_clear_pointer(&sexp, g_free);
 
             // ask EDS about events that occur in this window...
@@ -142,9 +147,9 @@
             e_cal_client_get_object_list_as_comps(
                 client,
                 sexp,
-                m_cancellable,
+                m_cancellable.get(),
                 on_event_component_list_ready,
-                new ClientSubtask(main_task, client, color));
+                new ClientSubtask(main_task, client, m_cancellable, color));
             g_clear_pointer(&sexp, g_free);
 
             g_clear_pointer(&sexp_fmt, g_free);
@@ -160,7 +165,7 @@
                 e_cal_client_get_object(kv.second,
                                         appointment.uid.c_str(),
                                         nullptr,
-                                        m_cancellable,
+                                        m_cancellable.get(),
                                         on_object_ready_for_disable,
                                         this);
             }
@@ -271,7 +276,7 @@
             g_debug("%s connecting a client to source %s", G_STRFUNC, source_uid);
             e_cal_client_connect(source,
                                  source_type,
-                                 self->m_cancellable,
+                                 self->m_cancellable.get(),
                                  on_client_connected,
                                  gself);
         }
@@ -306,7 +311,7 @@
             // now create a view for it so that we can listen for changes
             e_cal_client_get_view (ecc,
                                    "#t", // match all
-                                   self->m_cancellable,
+                                   self->m_cancellable.get(),
                                    on_client_view_ready,
                                    self);
 
@@ -429,7 +434,7 @@
         e_cal_client_get_object_list_as_comps(
             client,
             sexp,
-            m_cancellable,
+            m_cancellable.get(),
             ensure_client_alarms_have_triggers_async_cb,
             this);
 
@@ -510,7 +515,7 @@
             e_cal_client_modify_objects(client,
                                         modify_slist,
                                         E_CAL_OBJ_MOD_ALL,
-                                        m_cancellable,
+                                        m_cancellable.get(),
                                         ensure_canonical_alarms_have_triggers_async_cb,
                                         this);
 
@@ -580,13 +585,16 @@
     {
         std::shared_ptr<Task> task;
         ECalClient* client;
+        std::shared_ptr<GCancellable> cancellable;
         std::string color;
 
         ClientSubtask(const std::shared_ptr<Task>& task_in,
                       ECalClient* client_in,
+                      const std::shared_ptr<GCancellable>& cancellable_in,
                       const char* color_in):
             task(task_in),
-            client(client_in)
+            client(client_in),
+            cancellable(cancellable_in)
         {
             if (color_in)
                 color = color_in;
@@ -720,15 +728,44 @@
     }
 
     static DateTime
-    datetime_from_component_date_time(const ECalComponentDateTime & in,
-                                      GTimeZone                   * default_timezone)
+    datetime_from_component_date_time(ECalClient                     * client,
+                                      std::shared_ptr<GCancellable>  & cancellable,
+                                      const ECalComponentDateTime    & in,
+                                      GTimeZone                      * default_timezone)
     {
         DateTime out;
-
         g_return_val_if_fail(in.value != nullptr, out);
 
-        auto gtz = in.tzid == nullptr ? g_time_zone_ref(default_timezone)
-                                      : g_time_zone_new(in.tzid);
+        GTimeZone * gtz {};
+        if (in.tzid != nullptr)
+        {
+            auto itz = icaltimezone_get_builtin_timezone_from_tzid(in.tzid); // usually works
+
+            if (itz == nullptr) // fallback
+                itz = icaltimezone_get_builtin_timezone(in.tzid);
+
+            if (itz == nullptr) // ok we have a strange tzid... ask EDS to look it up in VTIMEZONES
+                e_cal_client_get_timezone_sync(client, in.tzid, &itz, cancellable.get(), nullptr);
+
+            const char * tzid;
+            if (itz != nullptr)
+            {
+                tzid = icaltimezone_get_location(itz);
+            }
+            else
+            {
+                g_warning("Unrecognized TZID: '%s'", in.tzid);
+                tzid = nullptr;
+            }
+
+            gtz = g_time_zone_new(tzid);
+            g_debug("%s eccdt.tzid -> offset is %d", G_STRLOC, in.tzid, (int)g_time_zone_get_offset(gtz,0));
+        }
+        else
+        {
+            gtz = g_time_zone_ref(default_timezone);
+        }
+
         out = DateTime(gtz,
                        in.value->year,
                        in.value->month,
@@ -773,7 +810,10 @@
     }
 
     static Appointment
-    get_appointment(ECalComponent * component, GTimeZone * gtz)
+    get_appointment(ECalClient                    * client,
+                    std::shared_ptr<GCancellable> & cancellable,
+                    ECalComponent                 * component,
+                    GTimeZone                     * gtz)
     {
         Appointment baseline;
 
@@ -792,13 +832,13 @@
         // get appointment.begin
         ECalComponentDateTime eccdt_tmp {};
         e_cal_component_get_dtstart(component, &eccdt_tmp);
-        baseline.begin = datetime_from_component_date_time(eccdt_tmp, gtz);
+        baseline.begin = datetime_from_component_date_time(client, cancellable, eccdt_tmp, gtz);
         e_cal_component_free_datetime(&eccdt_tmp);
 
         // get appointment.end
         e_cal_component_get_dtend(component, &eccdt_tmp);
         baseline.end = eccdt_tmp.value != nullptr
-                                  ? datetime_from_component_date_time(eccdt_tmp, gtz)
+                                  ? datetime_from_component_date_time(client, cancellable, eccdt_tmp, gtz)
                                   : baseline.begin;
         e_cal_component_free_datetime(&eccdt_tmp);
 
@@ -839,7 +879,7 @@
                          ClientSubtask * subtask,
                          GTimeZone     * gtz)
     {
-        // events with alarms are covered by add_alarm_to_subtask(),
+        // events with alarms are covered by add_alarms_to_subtask(),
         // so skip them here
         auto auids = e_cal_component_get_alarm_uids(component);
         const bool has_alarms = auids != nullptr;
@@ -850,7 +890,7 @@
         // add it. simple, eh?
         if (is_component_interesting(component))
         {
-            Appointment appointment = get_appointment(component, gtz);
+            Appointment appointment = get_appointment(subtask->client, subtask->cancellable, component, gtz);
             appointment.color = subtask->color;
             subtask->task->appointments.push_back(appointment);
         }
@@ -866,7 +906,7 @@
         if (!is_component_interesting(component))
             return;
 
-        Appointment baseline = get_appointment(component, gtz);
+        Appointment baseline = get_appointment(subtask->client, subtask->cancellable, component, gtz);
         baseline.color = subtask->color;
 
         /**
@@ -958,7 +998,7 @@
                     e_cal_client_modify_object(E_CAL_CLIENT(client),
                                                e_cal_component_get_icalcomponent(ecc),
                                                E_CAL_OBJ_MOD_THIS,
-                                               static_cast<Impl*>(gself)->m_cancellable,
+                                               static_cast<Impl*>(gself)->m_cancellable.get(),
                                                on_disable_done,
                                                nullptr);
 
@@ -990,7 +1030,7 @@
     std::set<ESource*> m_sources;
     std::map<ESource*,ECalClient*> m_clients;
     std::map<ESource*,ECalClientView*> m_views;
-    GCancellable* m_cancellable {};
+    std::shared_ptr<GCancellable> m_cancellable;
     ESourceRegistry* m_source_registry {};
     guint m_rebuild_tag {};
     time_t m_rebuild_deadline {};

=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt	2015-06-18 04:52:52 +0000
+++ tests/CMakeLists.txt	2015-07-10 17:29:43 +0000
@@ -70,13 +70,6 @@
 ## 
 
 find_program(DBUS_RUNNER dbus-test-runner)
-find_program(EVOLUTION_CALENDAR_FACTORY evolution-calendar-factory PATHS /usr/lib/evolution/)
-find_program(EVOLUTION_SOURCE_REGISTRY evolution-source-registry PATHS /usr/lib/evolution/)
-find_program(GVFSD gvfsd PATHS /usr/lib/gvfs/)
-OPTION(EVOLUTION_SOURCE_SERVICE_NAME "DBus name for source registry")
-if(NOT EVOLUTION_SOURCE_SERVICE_NAME)
-    set(EVOLUTION_SOURCE_SERVICE_NAME "org.gnome.evolution.dataserver.Sources3")
-endif()
 
 function(add_eds_ics_test_by_name name)
   set (TEST_NAME ${name})
@@ -84,21 +77,19 @@
   target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
   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
-            ${EVOLUTION_CALENDAR_FACTORY}                             # arg4: evolution-calendar-factory exec
-            ${EVOLUTION_SOURCE_SERVICE_NAME}                          # arg5: dbus name for source registry 
-            ${EVOLUTION_SOURCE_REGISTRY}                              # arg6: evolution-source-registry exec
-            ${GVFSD}                                                  # arg7: gvfsd exec
-            ${CMAKE_CURRENT_SOURCE_DIR}/test-eds-ics-config-files     # arg8: base directory for config file template
-            ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.ics)             # arg9: the ical file for this test
+            ${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_SOURCE_DIR}/${TEST_NAME}.ics)          # arg5: the ical file for this test
 endfunction()
 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)
 add_eds_ics_test_by_name(test-eds-ics-repeating-valarms)
 add_eds_ics_test_by_name(test-eds-ics-missing-trigger)
+add_eds_ics_test_by_name(test-eds-ics-tzids)
+add_eds_ics_test_by_name(test-eds-ics-tzids-2)
 
 
 # disabling the timezone unit tests because they require

=== modified file 'tests/run-eds-ics-test.sh'
--- tests/run-eds-ics-test.sh	2015-05-21 11:09:52 +0000
+++ tests/run-eds-ics-test.sh	2015-07-10 17:29:43 +0000
@@ -1,19 +1,22 @@
 #!/bin/sh
 
-echo ARG0=$0 # this script
-echo ARG1=$1 # full executable path of dbus-test-runner
-echo ARG2=$2 # full executable path of test app
-echo ARG3=$3 # test name
-echo ARG4=$4 # full executable path of evolution-calendar-factory
-echo ARG5=$5 # bus service name of calendar factory
-echo ARG6=$6 # full exectuable path of evolution-source-registry
-echo ARG7=$7 # full executable path of gvfs
-echo ARG8=$8 # config files
-echo ARG8=$9 # ics file
-
-# set up the tmpdir and tell the shell to purge it when we exit
-export TEST_TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d $3-XXXXXXXXXX) || exit 1
-echo "running test '$3' in ${TEST_TMP_DIR}"
+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
+CONFIG_DIR=$4  # config files
+ICS_FILE=$5    # ical file holding test data
+
+echo "this script: ${SELF}"
+echo "test-runner: ${TEST_RUNNER}"
+echo "test-exec: ${TEST_EXEC}"
+echo "test-name: ${TEST_NAME}"
+echo "config-dir: ${CONFIG_DIR}"
+echo "ics-file: ${ICS_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
@@ -40,23 +43,19 @@
 rm -rf ${XDG_DATA_HOME}
 
 # if there are canned config files for this test, move them into place now
-if [ -d $8 ]; then
-  echo "copying files from $8 to $HOME"
-  cp --verbose --archive $8/. $HOME
-fi
-
-# if there's a specific ics file to test, copy it on top of the canned confilg files
-if [ -e $9 ]; then
-  echo "copying $9 into $HOME"
-  cp --verbose --archive $9 ${XDG_DATA_HOME}/evolution/tasks/system/tasks.ics
-fi
-
-# run dbus-test-runner
-$1 --keep-env --max-wait=90 \
---task $2 --task-name $3 --wait-until-complete --wait-for=org.gnome.evolution.dataserver.Calendar4 \
---task $4 --task-name "evolution" --wait-until-complete -r
-#--task $6 --task-name "source-registry" --wait-for=org.gtk.vfs.Daemon -r \
-#--task $7 --task-name "gvfsd" -r
+if [ -d ${CONFIG_DIR} ]; then
+  echo "copying files from ${CONFIG_DIR} to $HOME"
+  cp --verbose --archive ${CONFIG_DIR}/. $HOME
+fi
+
+# if there's a specific ics file to test, copy it on top of the canned config files
+if [ -e ${ICS_FILE} ]; then
+  echo "copying ${ICS_FILE} into $HOME"
+  cp --verbose --archive ${ICS_FILE} ${XDG_DATA_HOME}/evolution/tasks/system/tasks.ics
+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

=== added file 'tests/test-eds-ics-tzids-2.cpp'
--- tests/test-eds-ics-tzids-2.cpp	1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-tzids-2.cpp	2015-07-10 17:29:43 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Canonical Ltd.
+ *
+ * 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:
+ *   Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <algorithm>
+
+#include <datetime/alarm-queue-simple.h>
+#include <datetime/clock-mock.h>
+#include <datetime/engine-eds.h>
+#include <datetime/planner-range.h>
+
+#include <gtest/gtest.h>
+
+#include "glib-fixture.h"
+#include "print-to.h"
+#include "timezone-mock.h"
+#include "wakeup-timer-mock.h"
+
+using namespace unity::indicator::datetime;
+using VAlarmFixture = GlibFixture;
+
+/***
+****
+***/
+
+TEST_F(VAlarmFixture, MultipleAppointments)
+{
+    // start the EDS engine
+    auto engine = std::make_shared<EdsEngine>();
+
+    // we need a consistent timezone for the planner and our local DateTimes
+    constexpr char const * zone_str {"America/Los_Angeles"};
+    auto tz = std::make_shared<MockTimezone>(zone_str);
+    auto gtz = g_time_zone_new(zone_str);
+
+    // make a planner that looks at the first half of 2015 in EDS
+    auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
+    const DateTime range_begin {gtz, 2006,1, 1, 0, 0, 0.0};
+    const DateTime range_end   {gtz, 2015,12,31,23,59,59.5};
+    planner->range().set(std::make_pair(range_begin, range_end));
+
+    // give EDS a moment to load
+    if (planner->appointments().get().empty()) {
+        g_message("waiting a moment for EDS to load...");
+        auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
+            g_message("ah, they loaded");
+            if (!appointments.empty())
+                g_main_loop_quit(loop);
+        };
+        core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
+        constexpr int max_wait_sec = 10;
+        wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
+    }
+
+    // what we expect to get...
+    std::array<Appointment,1> expected_appts;
+    auto appt = &expected_appts[0];
+    appt->uid = "109264742";
+    appt->color = "#becedd";
+    appt->summary = "National Incubator Initiative for Clean Energy (NIICE) FOA: Pre-Concept Paper Informational Webinar";
+    appt->begin = DateTime{gtz,2014,1,21,11,0,0};
+    appt->end = DateTime{gtz,2014,1,21,13,0,0};
+    appt->alarms = std::vector<Alarm>{ Alarm({"Reminder", "", DateTime(gtz,2014,1,21,10,45,0)}) };
+
+    // compare it to what we actually loaded...
+    const auto appts = planner->appointments().get();
+    EXPECT_EQ(expected_appts.size(), appts.size());
+    for (size_t i=0, n=std::min(appts.size(),expected_appts.size()); i<n; i++)
+        EXPECT_EQ(expected_appts[i], appts[i]);
+
+    // cleanup
+    g_time_zone_unref(gtz);
+}

=== added file 'tests/test-eds-ics-tzids-2.ics'
--- tests/test-eds-ics-tzids-2.ics	1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-tzids-2.ics	2015-07-10 17:29:43 +0000
@@ -0,0 +1,38 @@
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Ximian//NONSGML Evolution Calendar//EN
+VERSION:2.0
+X-EVOLUTION-DATA-REVISION:2015-07-09T19:41:50.123217Z(3)
+BEGIN:VTIMEZONE
+TZID:Pacific Time (US & Canada)
+BEGIN:STANDARD
+DTSTART:20061105T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+TZNAME:Daylight Savings Time
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTART;TZID=Pacific Time (US & Canada):20140121T110000
+DTEND;TZID=Pacific Time (US & Canada):20140121T130000
+UID:109264742
+DTSTAMP:20140120T000718Z
+DESCRIPTION: Event description
+SUMMARY;ENCODING=QUOTED-PRINTABLE:National Incubator Initiative for Clean 
+ Energy (NIICE) FOA: Pre-Concept Paper Informational Webinar
+CREATED:20140120T000741Z
+LAST-MODIFIED:20140120T000741Z
+BEGIN:VALARM
+TRIGGER;VALUE=DURATION:-PT15M
+ACTION:DISPLAY
+DESCRIPTION:Reminder
+END:VALARM
+END:VEVENT
+END:VCALENDAR

=== added file 'tests/test-eds-ics-tzids.cpp'
--- tests/test-eds-ics-tzids.cpp	1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-tzids.cpp	2015-07-10 17:29:43 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 Canonical Ltd.
+ *
+ * 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:
+ *   Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <algorithm>
+
+#include <datetime/alarm-queue-simple.h>
+#include <datetime/clock-mock.h>
+#include <datetime/engine-eds.h>
+#include <datetime/planner-range.h>
+
+#include <gtest/gtest.h>
+
+#include "glib-fixture.h"
+#include "print-to.h"
+#include "timezone-mock.h"
+#include "wakeup-timer-mock.h"
+
+using namespace unity::indicator::datetime;
+using VAlarmFixture = GlibFixture;
+
+/***
+****
+***/
+
+TEST_F(VAlarmFixture, MultipleAppointments)
+{
+    // start the EDS engine
+    auto engine = std::make_shared<EdsEngine>();
+
+    // we need a consistent timezone for the planner and our local DateTimes
+    constexpr char const * zone_str {"Europe/Berlin"};
+    auto tz = std::make_shared<MockTimezone>(zone_str);
+    auto gtz = g_time_zone_new(zone_str);
+
+    // make a planner that looks at the first half of 2015 in EDS
+    auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
+    const DateTime range_begin {gtz, 2015,7, 1, 0, 0, 0.0};
+    const DateTime range_end   {gtz, 2015,7,31,23,59,59.5};
+    planner->range().set(std::make_pair(range_begin, range_end));
+
+    // give EDS a moment to load
+    if (planner->appointments().get().empty()) {
+        g_message("waiting a moment for EDS to load...");
+        auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
+            g_message("ah, they loaded");
+            if (!appointments.empty())
+                g_main_loop_quit(loop);
+        };
+        core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
+        constexpr int max_wait_sec = 10;
+        wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
+    }
+
+    // what we expect to get...
+    std::array<Appointment,1> expected_appts;
+    auto appt = &expected_appts[0];
+    appt->uid = "8ggc30kh89qql8vjumgtug7l14@google.com";
+    appt->color = "#becedd";
+    appt->summary = "Hello";
+    appt->begin = DateTime{gtz,2015,7,1,20,0,0};
+    appt->end = DateTime{gtz,2015,7,1,22,0,0};
+
+    // compare it to what we actually loaded...
+    const auto appts = planner->appointments().get();
+    EXPECT_EQ(expected_appts.size(), appts.size());
+    for (size_t i=0, n=std::min(appts.size(),expected_appts.size()); i<n; i++)
+        EXPECT_EQ(expected_appts[i], appts[i]);
+
+    // cleanup
+    g_time_zone_unref(gtz);
+}

=== added file 'tests/test-eds-ics-tzids.ics'
--- tests/test-eds-ics-tzids.ics	1970-01-01 00:00:00 +0000
+++ tests/test-eds-ics-tzids.ics	2015-07-10 17:29:43 +0000
@@ -0,0 +1,56 @@
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Ximian//NONSGML Evolution Calendar//EN
+VERSION:2.0
+X-EVOLUTION-DATA-REVISION:2015-07-09T17:27:58.908570Z(0)
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/America/Sao_Paulo
+X-LIC-LOCATION:America/Sao_Paulo
+BEGIN:STANDARD
+TZNAME:BRT
+DTSTART:19700222T000000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=2
+TZOFFSETFROM:-0200
+TZOFFSETTO:-0300
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:BRST
+DTSTART:19701018T000000
+RRULE:FREQ=YEARLY;BYDAY=3SU;BYMONTH=10
+TZOFFSETFROM:-0300
+TZOFFSETTO:-0200
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VTIMEZONE
+TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Berlin
+X-LIC-LOCATION:Europe/Berlin
+BEGIN:STANDARD
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VEVENT
+STATUS:CONFIRMED
+DTSTAMP:20150708T095820Z
+CREATED:20150708T095820Z
+UID:8ggc30kh89qql8vjumgtug7l14@google.com
+SEQUENCE:0
+TRANSP:OPAQUE
+SUMMARY:Hello
+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Berlin:
+ 20150701T200000
+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Berlin:
+ 20150701T220000
+LAST-MODIFIED:20150708T095820Z
+END:VEVENT
+END:VCALENDAR

