Merge lp:~charlesk/indicator-datetime/lp-1419001-honor-ical-valarms into lp:indicator-datetime/15.04
- lp-1419001-honor-ical-valarms
- Merge into trunk.15.04
Status: | Merged |
---|---|
Approved by: | Ted Gould |
Approved revision: | 430 |
Merged at revision: | 408 |
Proposed branch: | lp:~charlesk/indicator-datetime/lp-1419001-honor-ical-valarms |
Merge into: | lp:indicator-datetime/15.04 |
Diff against target: |
1791 lines (+834/-269) 33 files modified
debian/control (+11/-8) include/datetime/alarm-queue-simple.h (+6/-12) include/datetime/alarm-queue.h (+1/-1) include/datetime/appointment.h (+19/-4) include/datetime/clock-mock.h (+1/-1) include/datetime/clock.h (+1/-1) include/datetime/date-time.h (+5/-1) include/datetime/planner-snooze.h (+1/-2) include/datetime/snap.h (+2/-1) include/datetime/wakeup-timer-mainloop.h (+2/-2) include/datetime/wakeup-timer-powerd.h (+2/-2) include/datetime/wakeup-timer.h (+1/-1) src/actions-live.cpp (+13/-6) src/actions.cpp (+2/-2) src/alarm-queue-simple.cpp (+140/-109) src/appointment.cpp (+9/-3) src/date-time.cpp (+31/-9) src/engine-eds.cpp (+153/-54) src/main.cpp (+7/-5) src/planner-snooze.cpp (+10/-6) src/snap.cpp (+9/-7) tests/CMakeLists.txt (+33/-4) tests/manual-test-snap.cpp (+5/-5) tests/print-to.h (+45/-0) tests/run-eds-test.sh (+57/-0) tests/test-alarm-queue.cpp (+10/-7) tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source (+21/-0) tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics (+47/-0) tests/test-eds-valarms.cpp (+101/-0) tests/test-live-actions.cpp (+0/-6) tests/test-snap.cpp (+10/-10) tests/timezone-mock.h (+1/-0) tests/wakeup-timer-mock.h (+78/-0) |
To merge this branch: | bzr merge lp:~charlesk/indicator-datetime/lp-1419001-honor-ical-valarms |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Ted Gould (community) | Approve | ||
Review via email: mp+255249@code.launchpad.net |
Commit message
Improve valarm support to honor calendar events' valarm triggers.
Description of the change
== Description of the Change
Improve our valarm support for better calendar reminders, adding support for (1) multiple valarms per event, and (2) honor the valarms' trigger times, rather than unconditionally reminding when the event is reached.
A use case that exercises both of these features is an airplane flight vevent that has a "pack your bags" valarm set to be triggered the day before and a "go to the airport now" valarm to be triggered a few hours before.
At the code level, the Appointment class has been refactored to own a container of Alarm objects, which correspond to ical valarms. There are changes in the AlarmQueue and in the EDS backend to accommodate this.
The patch also adds EDS tests to confirm that we can get a set of Alarms correctly from an ical file loaded by evolution. A lot of the EDS/dbus-
== Checklist
> Are there any related MPs required for this MP to build/function as expected? Please list.
No other MPs needed
> Is your branch in sync with latest trunk? (e.g. bzr pull lp:trunk -> no changes)
Yes
> 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?
Mako r164
> What manual tests are relevant for this MP?
indicator-
> Did you include a link to the MR Review Checklist Template to make your reviewer's life easier?
https:/
- 416. By Charles Kerr
-
in tests/run-
eds-test. sh, improve the comments - 417. By Charles Kerr
-
remove some new bits that turned out to be unneeded after all
PS Jenkins bot (ps-jenkins) wrote : | # |
- 418. By Charles Kerr
-
in debian/control, add evolution-
data-server to the build-dep now that we're using it for live EDS tests. - 419. By Charles Kerr
-
in new code, use std::array rather than C style arrays
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:417
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 420. By Charles Kerr
-
in the unit tests, add a PrintTo function for Alarms so that GTest can represent it as a string
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:417
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Ted Gould (ted) wrote : | # |
Comments and a couple of questions, but I don't see any blockers.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:420
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 421. By Charles Kerr
-
in DateTime class, make it harder to accidentally mix local and nonlocal timezones by replacing DateTime:
:DateTime( time_t) with two methods, DateTime: :Local( time_t) and DateTime( GTimeZone* , time_t) - 422. By Charles Kerr
-
in new EDS tests, use timezones consistently
- 423. By Charles Kerr
-
in new EDS code, use timezones consistently
- 424. By Charles Kerr
-
in Actions, sync DateTime API use by calling DateTime:
:Local( time_t) instead of DateTime: :DateTime( time_t) - 425. By Charles Kerr
-
in SimpleAlarmQueue, reduce a lambda capture to only the fields it needs
- 426. By Charles Kerr
-
in SimpleAlarmQueue, make the signature for find_next_alarm() and appointment_
get_current_ alarm() suck less. - 427. By Charles Kerr
-
in SimpleAlarmQueue, add a new method 'bool already_triggered() const' to reduce code overlapl between find_next_alarm() and appointment_
get_current_ alarm() - 428. By Charles Kerr
-
in EngineEds, make the ECalComponentAl
armAction 'omit' array a constexpr. - 429. By Charles Kerr
-
in the EDS engine, give a better explanation in the comments how we handle alarms with no triggers, and why
- 430. By Charles Kerr
-
in tests/run-
eds-test. sh, only delete the tmpdir if the test passed.
Charles Kerr (charlesk) wrote : | # |
> You know you want to make these into a template :-)
Yes but not today. :-)
> Seems like you only need this here? No need to get references to the parameters.
Fixed r425
>> bool find_next_
> I think in c++11 it makes more sense to return a tuple
> than to have a return in a parameter. Then you can bring
> it back with std::tie() in the caller to make it not suck
Returning a tuple of {success flag, Alarm} means we'd
still return an Alarm even if nothing was found.
Since we're walking through memory owned by the caller,
we could return a simple const pointer that's nullptr
if no match is found. That would make the API less screwy.
Fixed r426
> Seems like these two (the function above) could be combined
> into a shared function easily, just to reduce the number of
> search functions. If you "find_next_alarm()" and then is_same_minute
> the result I think that would provide more shared code.
They don't really fit, since one needs to loop through all appointments
and the other only walks through one appointment's alarms.
I could extract-method the "is this triggered already?" logic and
have both methods call it though.
Fixed r427
> Is there no #define for this? Scary. But we should probably static
> this array. No reason to build it on the stack each time.
Yeah, it's a strange interface.
Changed to a constexpr in r428.
> Not sure why we're checking the alarm to see if the text is set,
> why don't we always want the data structure to match what is
> being returned in a. Seems we want a sync here, even clearing
> the text if it was no longer in a.
There can be multiple valarms attached to an event, even to trigger
at the same time. e.g., events generated by calendar-app will have
one valarm specifying the sound action nd another specifying the
display action. We're iterating through all the valarms here, so we
have to be careful to pick up display text only if it's actually there.
> Confused at what this is doing, it seems like if the component
> didn't have any alarms, it also wouldn't have any UIDs for alarms
> that we could get text from.
It's not that it has no alarms; it's that the alarms had no triggers
specifying when they were supposed to go off. We need to special-case
that scenario because ubuntu-ui-toolkit used to generate alarms that
had no trigger.
Explained in comments better in r429.
> I think you actually only want to delete the temp directory if the
> test passes. That way it can end up still around so you can figure
> out what happened and/or picked up by Jenkins.
Good idea! Fixed r430.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:420
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2014-08-19 04:03:38 +0000 |
3 | +++ debian/control 2015-04-06 20:13:56 +0000 |
4 | @@ -2,18 +2,10 @@ |
5 | Section: misc |
6 | Priority: optional |
7 | Maintainer: Ubuntu Desktop Team <ubuntu-desktop@lists.ubuntu.com> |
8 | -# g++-4.9: since we use c++11 features, explicitly select a g++ version |
9 | -# to protect from ABI breaks in libstdc++ |
10 | -# language-pack-en-base: needed so unit tests can use 12h and 24h locales |
11 | Build-Depends: cmake, |
12 | - g++-4.9, |
13 | dbus, |
14 | - dbus-test-runner, |
15 | - python3-dbusmock, |
16 | debhelper (>= 9), |
17 | dh-translations, |
18 | - language-pack-touch-en | language-pack-en-base, |
19 | - libgtest-dev, |
20 | libglib2.0-dev (>= 2.35.4), |
21 | libnotify-dev (>= 0.7.6), |
22 | libgstreamer1.0-dev, |
23 | @@ -22,8 +14,19 @@ |
24 | libedataserver1.2-dev (>= 3.5), |
25 | liburl-dispatcher1-dev, |
26 | libproperties-cpp-dev, |
27 | +# for safeguarding against ABI breaks in libstdc++, explicitly select a g++ version |
28 | + g++-4.9, |
29 | +# for the test harness: |
30 | + libgtest-dev, |
31 | libdbustest1-dev, |
32 | + dbus-test-runner, |
33 | + python3-dbusmock, |
34 | +# for 12h/24h locale unit tests: |
35 | locales, |
36 | + language-pack-touch-en | language-pack-en-base, |
37 | +# for running live EDS tests: |
38 | + evolution-data-server, |
39 | + gvfs-daemons, |
40 | Standards-Version: 3.9.3 |
41 | Homepage: https://launchpad.net/indicator-datetime |
42 | # If you aren't a member of ~indicator-applet-developers but need to upload |
43 | |
44 | === modified file 'include/datetime/alarm-queue-simple.h' |
45 | --- include/datetime/alarm-queue-simple.h 2014-06-10 16:56:32 +0000 |
46 | +++ include/datetime/alarm-queue-simple.h 2015-04-06 20:13:56 +0000 |
47 | @@ -20,6 +20,8 @@ |
48 | #ifndef INDICATOR_DATETIME_ALARM_QUEUE_SIMPLE_H |
49 | #define INDICATOR_DATETIME_ALARM_QUEUE_SIMPLE_H |
50 | |
51 | +#include <memory> // std::shared_ptr |
52 | + |
53 | #include <datetime/alarm-queue.h> |
54 | #include <datetime/clock.h> |
55 | #include <datetime/planner.h> |
56 | @@ -39,20 +41,12 @@ |
57 | const std::shared_ptr<Planner>& upcoming_planner, |
58 | const std::shared_ptr<WakeupTimer>& timer); |
59 | ~SimpleAlarmQueue(); |
60 | - core::Signal<const Appointment&>& alarm_reached(); |
61 | + core::Signal<const Appointment&, const Alarm&>& alarm_reached() override; |
62 | |
63 | private: |
64 | - void requeue(); |
65 | - bool find_next_alarm(Appointment& setme) const; |
66 | - std::vector<Appointment> find_current_alarms() const; |
67 | - void check_alarms(); |
68 | - |
69 | - std::set<std::pair<std::string,DateTime>> m_triggered; |
70 | - const std::shared_ptr<Clock> m_clock; |
71 | - const std::shared_ptr<Planner> m_planner; |
72 | - const std::shared_ptr<WakeupTimer> m_timer; |
73 | - core::Signal<const Appointment&> m_alarm_reached; |
74 | - DateTime m_datetime; |
75 | + class Impl; |
76 | + friend class Impl; |
77 | + std::unique_ptr<Impl> impl; |
78 | }; |
79 | |
80 | |
81 | |
82 | === modified file 'include/datetime/alarm-queue.h' |
83 | --- include/datetime/alarm-queue.h 2014-04-25 03:34:42 +0000 |
84 | +++ include/datetime/alarm-queue.h 2015-04-06 20:13:56 +0000 |
85 | @@ -45,7 +45,7 @@ |
86 | public: |
87 | AlarmQueue() =default; |
88 | virtual ~AlarmQueue() =default; |
89 | - virtual core::Signal<const Appointment&>& alarm_reached() = 0; |
90 | + virtual core::Signal<const Appointment&, const Alarm&>& alarm_reached() =0; |
91 | }; |
92 | |
93 | /*** |
94 | |
95 | === modified file 'include/datetime/appointment.h' |
96 | --- include/datetime/appointment.h 2014-12-08 02:38:44 +0000 |
97 | +++ include/datetime/appointment.h 2015-04-06 20:13:56 +0000 |
98 | @@ -21,14 +21,28 @@ |
99 | #define INDICATOR_DATETIME_APPOINTMENT_H |
100 | |
101 | #include <datetime/date-time.h> |
102 | + |
103 | #include <string> |
104 | +#include <vector> |
105 | |
106 | namespace unity { |
107 | namespace indicator { |
108 | namespace datetime { |
109 | |
110 | /** |
111 | - * \brief Plain Old Data Structure that represents a calendar appointment. |
112 | + * \brief Basic information required to raise a notification about some Appointment. |
113 | + */ |
114 | +struct Alarm |
115 | +{ |
116 | + std::string text; |
117 | + std::string audio_url; |
118 | + DateTime time; |
119 | + |
120 | + bool operator== (const Alarm& that) const; |
121 | +}; |
122 | + |
123 | +/** |
124 | + * \brief An instance of an appointment; e.g. a calendar event or clock-app alarm |
125 | * |
126 | * @see Planner |
127 | */ |
128 | @@ -39,14 +53,15 @@ |
129 | Type type = EVENT; |
130 | bool is_ubuntu_alarm() const { return type == UBUNTU_ALARM; } |
131 | |
132 | + std::string uid; |
133 | std::string color; |
134 | std::string summary; |
135 | - std::string url; |
136 | - std::string uid; |
137 | - std::string audio_url; |
138 | + std::string activation_url; |
139 | DateTime begin; |
140 | DateTime end; |
141 | |
142 | + std::vector<Alarm> alarms; |
143 | + |
144 | bool operator== (const Appointment& that) const; |
145 | }; |
146 | |
147 | |
148 | === modified file 'include/datetime/clock-mock.h' |
149 | --- include/datetime/clock-mock.h 2014-09-17 16:51:51 +0000 |
150 | +++ include/datetime/clock-mock.h 2015-04-06 20:13:56 +0000 |
151 | @@ -39,7 +39,7 @@ |
152 | explicit MockClock(const DateTime& dt): m_localtime(dt) {} |
153 | ~MockClock() =default; |
154 | |
155 | - DateTime localtime() const { return m_localtime; } |
156 | + DateTime localtime() const override { return m_localtime; } |
157 | |
158 | void set_localtime(const DateTime& dt) |
159 | { |
160 | |
161 | === modified file 'include/datetime/clock.h' |
162 | --- include/datetime/clock.h 2014-09-19 14:34:32 +0000 |
163 | +++ include/datetime/clock.h 2015-04-06 20:13:56 +0000 |
164 | @@ -76,7 +76,7 @@ |
165 | public: |
166 | LiveClock (const std::shared_ptr<const Timezone>& zones); |
167 | virtual ~LiveClock(); |
168 | - virtual DateTime localtime() const; |
169 | + virtual DateTime localtime() const override; |
170 | |
171 | private: |
172 | class Impl; |
173 | |
174 | === modified file 'include/datetime/date-time.h' |
175 | --- include/datetime/date-time.h 2015-03-16 20:07:54 +0000 |
176 | +++ include/datetime/date-time.h 2015-04-06 20:13:56 +0000 |
177 | @@ -22,6 +22,7 @@ |
178 | |
179 | #include <glib.h> // GDateTime |
180 | |
181 | +#include <chrono> |
182 | #include <ctime> // time_t |
183 | #include <memory> // std::shared_ptr |
184 | |
185 | @@ -36,13 +37,16 @@ |
186 | { |
187 | public: |
188 | static DateTime NowLocal(); |
189 | + static DateTime Local(time_t); |
190 | static DateTime Local(int year, int month, int day, int hour, int minute, double seconds); |
191 | |
192 | DateTime(); |
193 | - explicit DateTime(time_t t); |
194 | + DateTime(GTimeZone* tz, time_t t); |
195 | DateTime(GTimeZone* tz, GDateTime* dt); |
196 | DateTime(GTimeZone* tz, int year, int month, int day, int hour, int minute, double seconds); |
197 | DateTime& operator=(const DateTime& in); |
198 | + DateTime& operator+=(const std::chrono::minutes&); |
199 | + DateTime& operator+=(const std::chrono::seconds&); |
200 | DateTime to_timezone(const std::string& zone) const; |
201 | DateTime start_of_month() const; |
202 | DateTime start_of_day() const; |
203 | |
204 | === modified file 'include/datetime/planner-snooze.h' |
205 | --- include/datetime/planner-snooze.h 2014-09-03 04:37:02 +0000 |
206 | +++ include/datetime/planner-snooze.h 2015-04-06 20:13:56 +0000 |
207 | @@ -39,9 +39,8 @@ |
208 | SnoozePlanner(const std::shared_ptr<Settings>&, |
209 | const std::shared_ptr<Clock>&); |
210 | ~SnoozePlanner(); |
211 | - void add(const Appointment&); |
212 | - |
213 | core::Property<std::vector<Appointment>>& appointments() override; |
214 | + void add(const Appointment&, const Alarm&); |
215 | |
216 | protected: |
217 | class Impl; |
218 | |
219 | === modified file 'include/datetime/snap.h' |
220 | --- include/datetime/snap.h 2014-09-02 16:16:01 +0000 |
221 | +++ include/datetime/snap.h 2015-04-06 20:13:56 +0000 |
222 | @@ -42,8 +42,9 @@ |
223 | const std::shared_ptr<const Settings>& settings); |
224 | virtual ~Snap(); |
225 | |
226 | - typedef std::function<void(const Appointment&)> appointment_func; |
227 | + typedef std::function<void(const Appointment&, const Alarm&)> appointment_func; |
228 | void operator()(const Appointment& appointment, |
229 | + const Alarm& alarm, |
230 | appointment_func snooze, |
231 | appointment_func ok); |
232 | |
233 | |
234 | === modified file 'include/datetime/wakeup-timer-mainloop.h' |
235 | --- include/datetime/wakeup-timer-mainloop.h 2014-09-17 16:51:51 +0000 |
236 | +++ include/datetime/wakeup-timer-mainloop.h 2015-04-06 20:13:56 +0000 |
237 | @@ -41,8 +41,8 @@ |
238 | public: |
239 | explicit MainloopWakeupTimer(const std::shared_ptr<Clock>&); |
240 | ~MainloopWakeupTimer(); |
241 | - void set_wakeup_time (const DateTime&); |
242 | - core::Signal<>& timeout(); |
243 | + void set_wakeup_time (const DateTime&) override; |
244 | + core::Signal<>& timeout() override; |
245 | |
246 | private: |
247 | MainloopWakeupTimer(const MainloopWakeupTimer&) =delete; |
248 | |
249 | === modified file 'include/datetime/wakeup-timer-powerd.h' |
250 | --- include/datetime/wakeup-timer-powerd.h 2014-09-17 16:51:51 +0000 |
251 | +++ include/datetime/wakeup-timer-powerd.h 2015-04-06 20:13:56 +0000 |
252 | @@ -41,8 +41,8 @@ |
253 | public: |
254 | explicit PowerdWakeupTimer(const std::shared_ptr<Clock>&); |
255 | ~PowerdWakeupTimer(); |
256 | - void set_wakeup_time(const DateTime&); |
257 | - core::Signal<>& timeout(); |
258 | + void set_wakeup_time(const DateTime&) override; |
259 | + core::Signal<>& timeout() override; |
260 | |
261 | private: |
262 | PowerdWakeupTimer(const PowerdWakeupTimer&) =delete; |
263 | |
264 | === modified file 'include/datetime/wakeup-timer.h' |
265 | --- include/datetime/wakeup-timer.h 2014-04-25 03:43:15 +0000 |
266 | +++ include/datetime/wakeup-timer.h 2015-04-06 20:13:56 +0000 |
267 | @@ -41,7 +41,7 @@ |
268 | WakeupTimer() =default; |
269 | virtual ~WakeupTimer() =default; |
270 | virtual void set_wakeup_time (const DateTime&) =0; |
271 | - virtual core::Signal<>& timeout() = 0; |
272 | + virtual core::Signal<>& timeout() =0; |
273 | }; |
274 | |
275 | /*** |
276 | |
277 | === modified file 'src/actions-live.cpp' |
278 | --- src/actions-live.cpp 2015-03-16 16:51:47 +0000 |
279 | +++ src/actions-live.cpp 2015-04-06 20:13:56 +0000 |
280 | @@ -135,12 +135,19 @@ |
281 | |
282 | void LiveActions::phone_open_appointment(const Appointment& appt) |
283 | { |
284 | - if (!appt.url.empty()) |
285 | - dispatch_url(appt.url); |
286 | - else if (appt.is_ubuntu_alarm()) |
287 | - phone_open_alarm_app(); |
288 | - else |
289 | - phone_open_calendar_app(DateTime::NowLocal()); |
290 | + if (!appt.activation_url.empty()) |
291 | + { |
292 | + dispatch_url(appt.activation_url); |
293 | + } |
294 | + else switch (appt.type) |
295 | + { |
296 | + case Appointment::UBUNTU_ALARM: |
297 | + phone_open_alarm_app(); |
298 | + break; |
299 | + |
300 | + default: |
301 | + phone_open_calendar_app(appt.begin); |
302 | + } |
303 | } |
304 | |
305 | void LiveActions::phone_open_calendar_app(const DateTime&) |
306 | |
307 | === modified file 'src/actions.cpp' |
308 | --- src/actions.cpp 2015-03-15 03:42:06 +0000 |
309 | +++ src/actions.cpp 2015-04-06 20:13:56 +0000 |
310 | @@ -43,7 +43,7 @@ |
311 | t = g_variant_get_int64(v); |
312 | |
313 | if (t != 0) |
314 | - return DateTime(t); |
315 | + return DateTime::Local(t); |
316 | else |
317 | return DateTime::NowLocal(); |
318 | } |
319 | @@ -143,7 +143,7 @@ |
320 | |
321 | g_return_if_fail(t != 0); |
322 | |
323 | - auto dt = DateTime(t).start_of_day(); |
324 | + auto dt = DateTime::Local(t).start_of_day(); |
325 | static_cast<Actions*>(gself)->set_calendar_date(dt); |
326 | } |
327 | |
328 | |
329 | === modified file 'src/alarm-queue-simple.cpp' |
330 | --- src/alarm-queue-simple.cpp 2015-03-15 02:31:42 +0000 |
331 | +++ src/alarm-queue-simple.cpp 2015-04-06 20:13:56 +0000 |
332 | @@ -20,134 +20,165 @@ |
333 | #include <datetime/alarm-queue-simple.h> |
334 | |
335 | #include <cmath> |
336 | +#include <set> |
337 | |
338 | namespace unity { |
339 | namespace indicator { |
340 | namespace datetime { |
341 | |
342 | /*** |
343 | +**** |
344 | +***/ |
345 | + |
346 | +class SimpleAlarmQueue::Impl |
347 | +{ |
348 | +public: |
349 | + |
350 | + Impl(const std::shared_ptr<Clock>& clock, |
351 | + const std::shared_ptr<Planner>& planner, |
352 | + const std::shared_ptr<WakeupTimer>& timer): |
353 | + m_clock{clock}, |
354 | + m_planner{planner}, |
355 | + m_timer{timer}, |
356 | + m_datetime{clock->localtime()} |
357 | + { |
358 | + m_planner->appointments().changed().connect([this](const std::vector<Appointment>&){ |
359 | + g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); |
360 | + requeue(); |
361 | + }); |
362 | + |
363 | + m_clock->minute_changed.connect([this]{ |
364 | + const auto now = m_clock->localtime(); |
365 | + constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; |
366 | + const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; |
367 | + m_datetime = now; |
368 | + if (clock_jumped) { |
369 | + g_debug("AlarmQueue %p calling requeue() due to clock skew", this); |
370 | + requeue(); |
371 | + } |
372 | + }); |
373 | + |
374 | + m_timer->timeout().connect([this](){ |
375 | + g_debug("AlarmQueue %p calling requeue() due to timeout", this); |
376 | + requeue(); |
377 | + }); |
378 | + |
379 | + requeue(); |
380 | + } |
381 | + |
382 | + ~Impl() |
383 | + { |
384 | + } |
385 | + |
386 | + core::Signal<const Appointment&, const Alarm&>& alarm_reached() |
387 | + { |
388 | + return m_alarm_reached; |
389 | + } |
390 | + |
391 | +private: |
392 | + |
393 | + void requeue() |
394 | + { |
395 | + const auto appointments = m_planner->appointments().get(); |
396 | + const Alarm* alarm; |
397 | + |
398 | + // kick any current alarms |
399 | + for (const auto& appointment : appointments) |
400 | + { |
401 | + if ((alarm = appointment_get_current_alarm(appointment))) |
402 | + { |
403 | + m_triggered.insert(std::make_pair(appointment.uid, alarm->time)); |
404 | + m_alarm_reached(appointment, *alarm); |
405 | + } |
406 | + } |
407 | + |
408 | + // idle until the next alarm |
409 | + if ((alarm = find_next_alarm(appointments))) |
410 | + { |
411 | + g_debug ("setting timer to wake up for next appointment '%s' at %s", |
412 | + alarm->text.c_str(), |
413 | + alarm->time.format("%F %T").c_str()); |
414 | + |
415 | + m_timer->set_wakeup_time(alarm->time); |
416 | + } |
417 | + } |
418 | + |
419 | + bool already_triggered (const Appointment& appt, const Alarm& alarm) const |
420 | + { |
421 | + const std::pair<const std::string&,const DateTime&> key{appt.uid, alarm.time}; |
422 | + return m_triggered.count(key) != 0; |
423 | + } |
424 | + |
425 | + // return the next Alarm (if any) that will kick now or in the future |
426 | + const Alarm* find_next_alarm(const std::vector<Appointment>& appointments) const |
427 | + { |
428 | + const Alarm* best {}; |
429 | + const auto now = m_clock->localtime(); |
430 | + const auto beginning_of_minute = now.start_of_minute(); |
431 | + |
432 | + g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); |
433 | + |
434 | + for(const auto& appointment : appointments) |
435 | + { |
436 | + for(const auto& alarm : appointment.alarms) |
437 | + { |
438 | + if (already_triggered(appointment, alarm)) |
439 | + continue; |
440 | + |
441 | + if (alarm.time < beginning_of_minute) // has this one already passed? |
442 | + continue; |
443 | + |
444 | + if (best && (best->time < alarm.time)) // do we already have a better match? |
445 | + continue; |
446 | + |
447 | + best = &alarm; |
448 | + } |
449 | + } |
450 | + |
451 | + return best; |
452 | + } |
453 | + |
454 | + // return the Appointment's current Alarm (if any) |
455 | + const Alarm* appointment_get_current_alarm(const Appointment& appointment) const |
456 | + { |
457 | + const auto now = m_clock->localtime(); |
458 | + |
459 | + for (const auto& alarm : appointment.alarms) |
460 | + if (!already_triggered(appointment, alarm) && DateTime::is_same_minute(now, alarm.time)) |
461 | + return &alarm; |
462 | + |
463 | + return nullptr; |
464 | + } |
465 | + |
466 | + |
467 | + std::set<std::pair<std::string,DateTime>> m_triggered; |
468 | + const std::shared_ptr<Clock> m_clock; |
469 | + const std::shared_ptr<Planner> m_planner; |
470 | + const std::shared_ptr<WakeupTimer> m_timer; |
471 | + core::Signal<const Appointment&, const Alarm&> m_alarm_reached; |
472 | + DateTime m_datetime; |
473 | +}; |
474 | + |
475 | +/*** |
476 | **** Public API |
477 | ***/ |
478 | |
479 | + |
480 | SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr<Clock>& clock, |
481 | const std::shared_ptr<Planner>& planner, |
482 | const std::shared_ptr<WakeupTimer>& timer): |
483 | - m_clock(clock), |
484 | - m_planner(planner), |
485 | - m_timer(timer), |
486 | - m_datetime(clock->localtime()) |
487 | + impl{new Impl{clock, planner, timer}} |
488 | { |
489 | - m_planner->appointments().changed().connect([this](const std::vector<Appointment>&){ |
490 | - g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); |
491 | - requeue(); |
492 | - }); |
493 | - |
494 | - m_clock->minute_changed.connect([=]{ |
495 | - const auto now = m_clock->localtime(); |
496 | - constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; |
497 | - const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; |
498 | - m_datetime = now; |
499 | - if (clock_jumped) { |
500 | - g_debug("AlarmQueue %p calling requeue() due to clock skew", this); |
501 | - requeue(); |
502 | - } |
503 | - }); |
504 | - |
505 | - m_timer->timeout().connect([this](){ |
506 | - g_debug("AlarmQueue %p calling requeue() due to timeout", this); |
507 | - requeue(); |
508 | - }); |
509 | - |
510 | - requeue(); |
511 | } |
512 | |
513 | SimpleAlarmQueue::~SimpleAlarmQueue() |
514 | { |
515 | } |
516 | |
517 | -core::Signal<const Appointment&>& SimpleAlarmQueue::alarm_reached() |
518 | -{ |
519 | - return m_alarm_reached; |
520 | -} |
521 | - |
522 | -/*** |
523 | -**** |
524 | -***/ |
525 | - |
526 | -void SimpleAlarmQueue::requeue() |
527 | -{ |
528 | - // kick any current alarms |
529 | - for (auto current : find_current_alarms()) |
530 | - { |
531 | - const std::pair<std::string,DateTime> trig {current.uid, current.begin}; |
532 | - m_triggered.insert(trig); |
533 | - m_alarm_reached(current); |
534 | - } |
535 | - |
536 | - // idle until the next alarm |
537 | - Appointment next; |
538 | - if (find_next_alarm(next)) |
539 | - { |
540 | - g_debug ("setting timer to wake up for next appointment '%s' at %s", |
541 | - next.summary.c_str(), |
542 | - next.begin.format("%F %T").c_str()); |
543 | - |
544 | - m_timer->set_wakeup_time(next.begin); |
545 | - } |
546 | -} |
547 | - |
548 | -// find the next alarm that will kick now or in the future |
549 | -bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const |
550 | -{ |
551 | - bool found = false; |
552 | - Appointment tmp; |
553 | - const auto now = m_clock->localtime(); |
554 | - const auto beginning_of_minute = now.start_of_minute(); |
555 | - |
556 | - const auto appointments = m_planner->appointments().get(); |
557 | - g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); |
558 | - |
559 | - for(const auto& walk : appointments) |
560 | - { |
561 | - const std::pair<std::string,DateTime> trig {walk.uid, walk.begin}; |
562 | - if (m_triggered.count(trig)) |
563 | - continue; |
564 | - |
565 | - if (walk.begin < beginning_of_minute) // has this one already passed? |
566 | - continue; |
567 | - |
568 | - if (found && (tmp.begin < walk.begin)) // do we already have a better match? |
569 | - continue; |
570 | - |
571 | - tmp = walk; |
572 | - found = true; |
573 | - } |
574 | - |
575 | - if (found) |
576 | - setme = tmp; |
577 | - |
578 | - return found; |
579 | -} |
580 | - |
581 | -// find the alarm(s) that should kick right now |
582 | -std::vector<Appointment> SimpleAlarmQueue::find_current_alarms() const |
583 | -{ |
584 | - std::vector<Appointment> appointments; |
585 | - |
586 | - const auto now = m_clock->localtime(); |
587 | - |
588 | - for(const auto& walk : m_planner->appointments().get()) |
589 | - { |
590 | - const std::pair<std::string,DateTime> trig {walk.uid, walk.begin}; |
591 | - if (m_triggered.count(trig)) // did we already use this one? |
592 | - continue; |
593 | - if (!DateTime::is_same_minute(now, walk.begin)) |
594 | - continue; |
595 | - |
596 | - appointments.push_back(walk); |
597 | - } |
598 | - |
599 | - return appointments; |
600 | +core::Signal<const Appointment&, const Alarm&>& |
601 | +SimpleAlarmQueue::alarm_reached() |
602 | +{ |
603 | + return impl->alarm_reached(); |
604 | } |
605 | |
606 | /*** |
607 | |
608 | === modified file 'src/appointment.cpp' |
609 | --- src/appointment.cpp 2014-12-08 02:38:44 +0000 |
610 | +++ src/appointment.cpp 2015-04-06 20:13:56 +0000 |
611 | @@ -27,16 +27,22 @@ |
612 | ***** |
613 | ****/ |
614 | |
615 | +bool Alarm::operator==(const Alarm& that) const |
616 | +{ |
617 | + return (text==that.text) |
618 | + && (audio_url==that.audio_url) |
619 | + && (this->time==that.time); |
620 | +} |
621 | + |
622 | bool Appointment::operator==(const Appointment& that) const |
623 | { |
624 | return (type==that.type) |
625 | && (uid==that.uid) |
626 | && (color==that.color) |
627 | && (summary==that.summary) |
628 | - && (url==that.url) |
629 | - && (audio_url==that.audio_url) |
630 | && (begin==that.begin) |
631 | - && (end==that.end); |
632 | + && (end==that.end) |
633 | + && (alarms==that.alarms); |
634 | } |
635 | |
636 | /**** |
637 | |
638 | === modified file 'src/date-time.cpp' |
639 | --- src/date-time.cpp 2015-03-16 20:07:54 +0000 |
640 | +++ src/date-time.cpp 2015-04-06 20:13:56 +0000 |
641 | @@ -55,13 +55,23 @@ |
642 | return *this; |
643 | } |
644 | |
645 | -DateTime::DateTime(time_t t) |
646 | -{ |
647 | - auto gtz = g_time_zone_new_local(); |
648 | - auto gdt = g_date_time_new_from_unix_local(t); |
649 | +DateTime& DateTime::operator+=(const std::chrono::minutes& minutes) |
650 | +{ |
651 | + return (*this = add_full(0, 0, 0, 0, minutes.count(), 0)); |
652 | +} |
653 | + |
654 | +DateTime& DateTime::operator+=(const std::chrono::seconds& seconds) |
655 | +{ |
656 | + return (*this = add_full(0, 0, 0, 0, 0, seconds.count())); |
657 | +} |
658 | + |
659 | +DateTime::DateTime(GTimeZone* gtz, time_t t) |
660 | +{ |
661 | + auto utc = g_date_time_new_from_unix_utc(t); |
662 | + auto gdt = g_date_time_to_timezone (utc, gtz); |
663 | reset(gtz, gdt); |
664 | - g_time_zone_unref(gtz); |
665 | g_date_time_unref(gdt); |
666 | + g_date_time_unref(utc); |
667 | } |
668 | |
669 | DateTime DateTime::NowLocal() |
670 | @@ -74,6 +84,16 @@ |
671 | return dt; |
672 | } |
673 | |
674 | +DateTime DateTime::Local(time_t t) |
675 | +{ |
676 | + auto gtz = g_time_zone_new_local(); |
677 | + auto gdt = g_date_time_new_from_unix_local(t); |
678 | + DateTime dt(gtz, gdt); |
679 | + g_time_zone_unref(gtz); |
680 | + g_date_time_unref(gdt); |
681 | + return dt; |
682 | +} |
683 | + |
684 | DateTime DateTime::Local(int year, int month, int day, int hour, int minute, double seconds) |
685 | { |
686 | auto gtz = g_time_zone_new_local(); |
687 | @@ -244,10 +264,12 @@ |
688 | if (!a.m_dt || !b.m_dt) |
689 | return false; |
690 | |
691 | - const auto adt = a.get(); |
692 | - const auto bdt = b.get(); |
693 | - return (g_date_time_get_year(adt) == g_date_time_get_year(bdt)) |
694 | - && (g_date_time_get_day_of_year(adt) == g_date_time_get_day_of_year(bdt)); |
695 | + int ay, am, ad; |
696 | + int by, bm, bd; |
697 | + g_date_time_get_ymd(a.get(), &ay, &am, &ad); |
698 | + g_date_time_get_ymd(b.get(), &by, &bm, &bd); |
699 | + |
700 | + return (ay==by) && (am==bm) && (ad==bd); |
701 | } |
702 | |
703 | bool DateTime::is_same_minute(const DateTime& a, const DateTime& b) |
704 | |
705 | === modified file 'src/engine-eds.cpp' |
706 | --- src/engine-eds.cpp 2015-01-15 19:15:27 +0000 |
707 | +++ src/engine-eds.cpp 2015-04-06 20:13:56 +0000 |
708 | @@ -25,6 +25,7 @@ |
709 | #include <libedataserver/libedataserver.h> |
710 | |
711 | #include <algorithm> // std::sort() |
712 | +#include <array> |
713 | #include <ctime> // time() |
714 | #include <map> |
715 | #include <set> |
716 | @@ -126,12 +127,18 @@ |
717 | auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); |
718 | const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); |
719 | g_debug("calling e_cal_client_generate_instances for %p", (void*)client); |
720 | + auto subtask = new AppointmentSubtask(main_task, |
721 | + client, |
722 | + color, |
723 | + default_timezone, |
724 | + begin_timet, |
725 | + end_timet); |
726 | e_cal_client_generate_instances(client, |
727 | begin_timet, |
728 | end_timet, |
729 | m_cancellable, |
730 | my_get_appointments_foreach, |
731 | - new AppointmentSubtask (main_task, client, color), |
732 | + subtask, |
733 | [](gpointer g){delete static_cast<AppointmentSubtask*>(g);}); |
734 | } |
735 | } |
736 | @@ -409,14 +416,70 @@ |
737 | std::shared_ptr<Task> task; |
738 | ECalClient* client; |
739 | std::string color; |
740 | - AppointmentSubtask(const std::shared_ptr<Task>& task_in, ECalClient* client_in, const char* color_in): |
741 | - task(task_in), client(client_in) |
742 | + icaltimezone* default_timezone; |
743 | + time_t begin; |
744 | + time_t end; |
745 | + |
746 | + AppointmentSubtask(const std::shared_ptr<Task>& task_in, |
747 | + ECalClient* client_in, |
748 | + const char* color_in, |
749 | + icaltimezone* default_tz, |
750 | + time_t begin_, |
751 | + time_t end_): |
752 | + task(task_in), |
753 | + client(client_in), |
754 | + default_timezone(default_tz), |
755 | + begin(begin_), |
756 | + end(end_) |
757 | { |
758 | if (color_in) |
759 | color = color_in; |
760 | } |
761 | }; |
762 | |
763 | + static std::string get_alarm_text(ECalComponentAlarm * alarm) |
764 | + { |
765 | + std::string ret; |
766 | + |
767 | + ECalComponentAlarmAction action; |
768 | + e_cal_component_alarm_get_action(alarm, &action); |
769 | + if (action == E_CAL_COMPONENT_ALARM_DISPLAY) |
770 | + { |
771 | + ECalComponentText text {}; |
772 | + e_cal_component_alarm_get_description(alarm, &text); |
773 | + if (text.value) |
774 | + ret = text.value; |
775 | + } |
776 | + |
777 | + return ret; |
778 | + } |
779 | + |
780 | + static std::string get_alarm_sound_url(ECalComponentAlarm * alarm) |
781 | + { |
782 | + std::string ret; |
783 | + |
784 | + ECalComponentAlarmAction action; |
785 | + e_cal_component_alarm_get_action(alarm, &action); |
786 | + if (action == E_CAL_COMPONENT_ALARM_AUDIO) |
787 | + { |
788 | + icalattach* attach = nullptr; |
789 | + e_cal_component_alarm_get_attach(alarm, &attach); |
790 | + if (attach != nullptr) |
791 | + { |
792 | + if (icalattach_get_is_url (attach)) |
793 | + { |
794 | + const char* url = icalattach_get_url(attach); |
795 | + if (url != nullptr) |
796 | + ret = url; |
797 | + } |
798 | + |
799 | + icalattach_unref(attach); |
800 | + } |
801 | + } |
802 | + |
803 | + return ret; |
804 | + } |
805 | + |
806 | static gboolean |
807 | my_get_appointments_foreach(ECalComponent* component, |
808 | time_t begin, |
809 | @@ -434,11 +497,16 @@ |
810 | auto status = ICAL_STATUS_NONE; |
811 | e_cal_component_get_status(component, &status); |
812 | |
813 | - const auto begin_dt = DateTime(begin); |
814 | - const auto end_dt = DateTime(end); |
815 | + // get the timezone we want to use for generated Appointments/Alarms |
816 | + const char * location = icaltimezone_get_location(subtask->default_timezone); |
817 | + auto gtz = g_time_zone_new(location); |
818 | + g_debug("timezone abbreviation is %s", g_time_zone_get_abbreviation (gtz, 0)); |
819 | + |
820 | + const DateTime begin_dt { gtz, begin }; |
821 | + const DateTime end_dt { gtz, end }; |
822 | g_debug ("got appointment from %s to %s, uid %s status %d", |
823 | - begin_dt.format("%F %T").c_str(), |
824 | - end_dt.format("%F %T").c_str(), |
825 | + begin_dt.format("%F %T %z").c_str(), |
826 | + end_dt.format("%F %T %z").c_str(), |
827 | uid, |
828 | (int)status); |
829 | |
830 | @@ -461,10 +529,10 @@ |
831 | (status != ICAL_STATUS_COMPLETED) && |
832 | (status != ICAL_STATUS_CANCELLED)) |
833 | { |
834 | + constexpr std::array<ECalComponentAlarmAction,1> omit = { (ECalComponentAlarmAction)-1 }; // list of action types to omit, terminated with -1 |
835 | Appointment appointment; |
836 | |
837 | - ECalComponentText text; |
838 | - text.value = nullptr; |
839 | + ECalComponentText text {}; |
840 | e_cal_component_get_summary(component, &text); |
841 | if (text.value) |
842 | appointment.summary = text.value; |
843 | @@ -475,49 +543,80 @@ |
844 | appointment.uid = uid; |
845 | appointment.type = type; |
846 | |
847 | - // Look through all of this component's alarms |
848 | - // for DISPLAY or AUDIO url attachments. |
849 | - // If we find any, use them for appointment.url and audio_sound |
850 | - auto alarm_uids = e_cal_component_get_alarm_uids(component); |
851 | - for(auto walk=alarm_uids; appointment.url.empty() && walk!=nullptr; walk=walk->next) |
852 | - { |
853 | - auto alarm = e_cal_component_get_alarm(component, static_cast<const char*>(walk->data)); |
854 | - |
855 | - ECalComponentAlarmAction action; |
856 | - e_cal_component_alarm_get_action(alarm, &action); |
857 | - if ((action == E_CAL_COMPONENT_ALARM_DISPLAY) || (action == E_CAL_COMPONENT_ALARM_AUDIO)) |
858 | - { |
859 | - icalattach* attach = nullptr; |
860 | - e_cal_component_alarm_get_attach(alarm, &attach); |
861 | - if (attach != nullptr) |
862 | - { |
863 | - if (icalattach_get_is_url (attach)) |
864 | - { |
865 | - const char* url = icalattach_get_url(attach); |
866 | - if (url != nullptr) |
867 | - { |
868 | - if ((action == E_CAL_COMPONENT_ALARM_DISPLAY) && appointment.url.empty()) |
869 | - { |
870 | - appointment.url = url; |
871 | - } |
872 | - else if ((action == E_CAL_COMPONENT_ALARM_AUDIO) && appointment.audio_url.empty()) |
873 | - { |
874 | - appointment.audio_url = url; |
875 | - } |
876 | - } |
877 | - } |
878 | - |
879 | - icalattach_unref(attach); |
880 | - } |
881 | - } |
882 | - |
883 | - e_cal_component_alarm_free(alarm); |
884 | - } |
885 | - cal_obj_uid_list_free(alarm_uids); |
886 | - |
887 | - g_debug("adding appointment '%s' '%s'", appointment.summary.c_str(), appointment.url.c_str()); |
888 | + icalcomponent * icc = e_cal_component_get_icalcomponent(component); |
889 | + g_debug("%s", icalcomponent_as_ical_string(icc)); // libical owns this string; no leak |
890 | + |
891 | + auto e_alarms = e_cal_util_generate_alarms_for_comp(component, |
892 | + subtask->begin, |
893 | + subtask->end, |
894 | + const_cast<ECalComponentAlarmAction*>(omit.data()), |
895 | + e_cal_client_resolve_tzid_cb, |
896 | + subtask->client, |
897 | + subtask->default_timezone); |
898 | + |
899 | + std::map<DateTime,Alarm> alarms; |
900 | + |
901 | + if (e_alarms != nullptr) |
902 | + { |
903 | + for (auto l=e_alarms->alarms; l!=nullptr; l=l->next) |
904 | + { |
905 | + auto ai = static_cast<ECalComponentAlarmInstance*>(l->data); |
906 | + auto a = e_cal_component_get_alarm(component, ai->auid); |
907 | + |
908 | + if (a != nullptr) |
909 | + { |
910 | + const DateTime alarm_begin{gtz, ai->trigger}; |
911 | + auto& alarm = alarms[alarm_begin]; |
912 | + |
913 | + if (alarm.text.empty()) |
914 | + alarm.text = get_alarm_text(a); |
915 | + if (alarm.audio_url.empty()) |
916 | + alarm.audio_url = get_alarm_sound_url(a); |
917 | + if (!alarm.time.is_set()) |
918 | + alarm.time = alarm_begin; |
919 | + |
920 | + e_cal_component_alarm_free(a); |
921 | + } |
922 | + } |
923 | + |
924 | + e_cal_component_alarms_free(e_alarms); |
925 | + } |
926 | + // Hm, no alarm triggers? |
927 | + // That's a bug in alarms created by some versions of ubuntu-ui-toolkit. |
928 | + // If that's what's happening here, let's handle those alarms anyway |
929 | + // by effectively injecting a TRIGGER;VALUE=DURATION;RELATED=START:PT0S |
930 | + else if (appointment.is_ubuntu_alarm()) |
931 | + { |
932 | + Alarm tmp; |
933 | + tmp.time = appointment.begin; |
934 | + |
935 | + auto auids = e_cal_component_get_alarm_uids(component); |
936 | + for(auto l=auids; l!=nullptr; l=l->next) |
937 | + { |
938 | + const auto auid = static_cast<const char*>(l->data); |
939 | + auto a = e_cal_component_get_alarm(component, auid); |
940 | + if (a != nullptr) |
941 | + { |
942 | + if (tmp.text.empty()) |
943 | + tmp.text = get_alarm_text(a); |
944 | + if (tmp.audio_url.empty()) |
945 | + tmp.audio_url = get_alarm_sound_url(a); |
946 | + e_cal_component_alarm_free(a); |
947 | + } |
948 | + } |
949 | + cal_obj_uid_list_free(auids); |
950 | + |
951 | + alarms[tmp.time] = tmp; |
952 | + } |
953 | + |
954 | + appointment.alarms.reserve(alarms.size()); |
955 | + for (const auto& it : alarms) |
956 | + appointment.alarms.push_back(it.second); |
957 | + |
958 | subtask->task->appointments.push_back(appointment); |
959 | } |
960 | + |
961 | + g_time_zone_unref(gtz); |
962 | } |
963 | |
964 | return G_SOURCE_CONTINUE; |
965 | @@ -591,10 +690,10 @@ |
966 | std::set<ESource*> m_sources; |
967 | std::map<ESource*,ECalClient*> m_clients; |
968 | std::map<ESource*,ECalClientView*> m_views; |
969 | - GCancellable* m_cancellable = nullptr; |
970 | - ESourceRegistry* m_source_registry = nullptr; |
971 | - guint m_rebuild_tag = 0; |
972 | - time_t m_rebuild_deadline = 0; |
973 | + GCancellable* m_cancellable {}; |
974 | + ESourceRegistry* m_source_registry {}; |
975 | + guint m_rebuild_tag {}; |
976 | + time_t m_rebuild_deadline {}; |
977 | }; |
978 | |
979 | /*** |
980 | |
981 | === modified file 'src/main.cpp' |
982 | --- src/main.cpp 2015-02-28 18:37:07 +0000 |
983 | +++ src/main.cpp 2015-04-06 20:13:56 +0000 |
984 | @@ -138,11 +138,13 @@ |
985 | auto notification_engine = std::make_shared<uin::Engine>("indicator-datetime-service"); |
986 | std::unique_ptr<Snap> snap (new Snap(notification_engine, state->settings)); |
987 | auto alarm_queue = create_simple_alarm_queue(state->clock, snooze_planner, engine, timezone_); |
988 | - auto on_snooze = [snooze_planner](const Appointment& a) {snooze_planner->add(a);}; |
989 | - auto on_ok = [](const Appointment&){}; |
990 | - auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& a) { |
991 | - (*snap)(a, on_snooze, on_ok); |
992 | - engine->disable_ubuntu_alarm(a); |
993 | + auto on_snooze = [snooze_planner](const Appointment& appointment, const Alarm& alarm) { |
994 | + snooze_planner->add(appointment, alarm); |
995 | + }; |
996 | + auto on_ok = [](const Appointment&, const Alarm&){}; |
997 | + auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& appointment, const Alarm& alarm) { |
998 | + (*snap)(appointment, alarm, on_snooze, on_ok); |
999 | + engine->disable_ubuntu_alarm(appointment); |
1000 | }; |
1001 | alarm_queue->alarm_reached().connect(on_alarm_reached); |
1002 | |
1003 | |
1004 | === modified file 'src/planner-snooze.cpp' |
1005 | --- src/planner-snooze.cpp 2014-09-17 16:51:51 +0000 |
1006 | +++ src/planner-snooze.cpp 2015-04-06 20:13:56 +0000 |
1007 | @@ -51,14 +51,18 @@ |
1008 | return m_appointments; |
1009 | } |
1010 | |
1011 | - void add(const Appointment& appt_in) |
1012 | + void add(const Appointment& appt_in, const Alarm& alarm) |
1013 | { |
1014 | + // make a copy of the appointment with only this alarm |
1015 | Appointment appt = appt_in; |
1016 | + appt.alarms.clear(); |
1017 | + appt.alarms.push_back(alarm); |
1018 | |
1019 | // reschedule the alarm to go off N minutes from now |
1020 | - const auto alarm_duration_secs = appt.end - appt.begin; |
1021 | - appt.begin = m_clock->localtime().add_full(0,0,0,0,m_settings->snooze_duration.get(),0); |
1022 | - appt.end = appt.begin.add_full(0,0,0,0,0,alarm_duration_secs); |
1023 | + const auto offset = std::chrono::minutes(m_settings->snooze_duration.get()); |
1024 | + appt.begin += offset; |
1025 | + appt.end += offset; |
1026 | + appt.alarms[0].time += offset; |
1027 | |
1028 | // give it a new ID |
1029 | gchar* uid = e_uid_new(); |
1030 | @@ -95,9 +99,9 @@ |
1031 | } |
1032 | |
1033 | void |
1034 | -SnoozePlanner::add(const Appointment& appointment) |
1035 | +SnoozePlanner::add(const Appointment& appointment, const Alarm& alarm) |
1036 | { |
1037 | - impl->add(appointment); |
1038 | + impl->add(appointment, alarm); |
1039 | } |
1040 | |
1041 | core::Property<std::vector<Appointment>>& |
1042 | |
1043 | === modified file 'src/snap.cpp' |
1044 | --- src/snap.cpp 2015-03-31 18:54:26 +0000 |
1045 | +++ src/snap.cpp 2015-04-06 20:13:56 +0000 |
1046 | @@ -79,6 +79,7 @@ |
1047 | } |
1048 | |
1049 | void operator()(const Appointment& appointment, |
1050 | + const Alarm& alarm, |
1051 | appointment_func snooze, |
1052 | appointment_func ok) |
1053 | { |
1054 | @@ -96,7 +97,7 @@ |
1055 | if (appointment.is_ubuntu_alarm() || !silent_mode()) { |
1056 | // create the sound. |
1057 | const auto role = appointment.is_ubuntu_alarm() ? "alarm" : "alert"; |
1058 | - const auto uri = get_alarm_uri(appointment, m_settings); |
1059 | + const auto uri = get_alarm_uri(alarm, m_settings); |
1060 | const auto volume = m_settings->alarm_volume.get(); |
1061 | const bool loop = interactive; |
1062 | sound = std::make_shared<uin::Sound>(role, uri, volume, loop); |
1063 | @@ -140,12 +141,12 @@ |
1064 | // add 'sound', 'haptic', and 'awake' objects to the capture so |
1065 | // they stay alive until the closed callback is called; i.e., |
1066 | // for the lifespan of the notficiation |
1067 | - b.set_closed_callback([appointment, snooze, ok, sound, awake, haptic] |
1068 | + b.set_closed_callback([appointment, alarm, snooze, ok, sound, awake, haptic] |
1069 | (const std::string& action){ |
1070 | if (action == "snooze") |
1071 | - snooze(appointment); |
1072 | + snooze(appointment, alarm); |
1073 | else |
1074 | - ok(appointment); |
1075 | + ok(appointment, alarm); |
1076 | }); |
1077 | |
1078 | const auto key = m_engine->show(b); |
1079 | @@ -180,12 +181,12 @@ |
1080 | && (accounts_service_sound_get_silent_mode(m_accounts_service_sound_proxy)); |
1081 | } |
1082 | |
1083 | - std::string get_alarm_uri(const Appointment& appointment, |
1084 | + std::string get_alarm_uri(const Alarm& alarm, |
1085 | const std::shared_ptr<const Settings>& settings) const |
1086 | { |
1087 | const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; |
1088 | |
1089 | - const std::string candidates[] = { appointment.audio_url, |
1090 | + const std::string candidates[] = { alarm.audio_url, |
1091 | settings->alarm_sound.get(), |
1092 | FALLBACK }; |
1093 | |
1094 | @@ -236,10 +237,11 @@ |
1095 | |
1096 | void |
1097 | Snap::operator()(const Appointment& appointment, |
1098 | + const Alarm& alarm, |
1099 | appointment_func show, |
1100 | appointment_func ok) |
1101 | { |
1102 | - (*impl)(appointment, show, ok); |
1103 | + (*impl)(appointment, alarm, show, ok); |
1104 | } |
1105 | |
1106 | /*** |
1107 | |
1108 | === modified file 'tests/CMakeLists.txt' |
1109 | --- tests/CMakeLists.txt 2015-03-16 20:07:54 +0000 |
1110 | +++ tests/CMakeLists.txt 2015-04-06 20:13:56 +0000 |
1111 | @@ -39,12 +39,10 @@ |
1112 | |
1113 | add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") |
1114 | |
1115 | - |
1116 | function(add_test_by_name name) |
1117 | set (TEST_NAME ${name}) |
1118 | add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) |
1119 | add_test (${TEST_NAME} ${TEST_NAME}) |
1120 | - add_dependencies (${TEST_NAME} libindicatordatetimeservice) |
1121 | target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) |
1122 | endfunction() |
1123 | add_test_by_name(test-datetime) |
1124 | @@ -65,9 +63,41 @@ |
1125 | |
1126 | set (TEST_NAME manual-test-snap) |
1127 | add_executable (${TEST_NAME} ${TEST_NAME}.cpp) |
1128 | -add_dependencies (${TEST_NAME} libindicatordatetimeservice) |
1129 | target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) |
1130 | |
1131 | +## |
1132 | +## EDS Tests |
1133 | +## |
1134 | + |
1135 | +find_program(DBUS_RUNNER dbus-test-runner) |
1136 | +find_program(EVOLUTION_CALENDAR_FACTORY evolution-calendar-factory PATHS /usr/lib/evolution/) |
1137 | +find_program(EVOLUTION_SOURCE_REGISTRY evolution-source-registry PATHS /usr/lib/evolution/) |
1138 | +find_program(GVFSD gvfsd PATHS /usr/lib/gvfs/) |
1139 | +OPTION(EVOLUTION_SOURCE_SERVICE_NAME "DBus name for source registry") |
1140 | +if(NOT EVOLUTION_SOURCE_SERVICE_NAME) |
1141 | + set(EVOLUTION_SOURCE_SERVICE_NAME "org.gnome.evolution.dataserver.Sources3") |
1142 | +endif() |
1143 | + |
1144 | +function(add_eds_test_by_name name) |
1145 | + set (TEST_NAME ${name}) |
1146 | + add_executable(${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) |
1147 | + target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) |
1148 | + add_test (${TEST_NAME} |
1149 | + ${CMAKE_CURRENT_SOURCE_DIR}/run-eds-test.sh |
1150 | + ${DBUS_RUNNER} # arg1: dbus-test-runner exec |
1151 | + ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path |
1152 | + ${TEST_NAME} # arg3: test name |
1153 | + ${EVOLUTION_CALENDAR_FACTORY} # arg4: evolution-calendar-factory exec |
1154 | + ${EVOLUTION_SOURCE_SERVICE_NAME} # arg5: dbus name for source registry |
1155 | + ${EVOLUTION_SOURCE_REGISTRY} # arg6: evolution-source-registry exec |
1156 | + ${GVFSD} # arg7: gvfsd exec |
1157 | + ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}-config-files) # arg8: canned config files |
1158 | +endfunction() |
1159 | +add_eds_test_by_name(test-eds-valarms) |
1160 | + |
1161 | + |
1162 | + |
1163 | + |
1164 | # disabling the timezone unit tests because they require |
1165 | # https://code.launchpad.net/~ted/dbus-test-runner/multi-interface-test/+merge/199724 |
1166 | # which hasn't landed yet. These can be re-enabled as soon as that lands. |
1167 | @@ -75,7 +105,6 @@ |
1168 | # set (TEST_NAME ${name}) |
1169 | # add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) |
1170 | # add_test (${TEST_NAME} ${TEST_NAME}) |
1171 | -# add_dependencies (${TEST_NAME} libindicatordatetimeservice) |
1172 | # target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) |
1173 | #endfunction() |
1174 | #add_dbusmock_test_by_name(test-timezone-geoclue) |
1175 | |
1176 | === modified file 'tests/manual-test-snap.cpp' |
1177 | --- tests/manual-test-snap.cpp 2015-03-16 20:07:54 +0000 |
1178 | +++ tests/manual-test-snap.cpp 2015-04-06 20:13:56 +0000 |
1179 | @@ -67,18 +67,18 @@ |
1180 | Appointment a; |
1181 | a.color = "green"; |
1182 | a.summary = "Alarm"; |
1183 | - a.url = "alarm:///hello-world"; |
1184 | a.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; |
1185 | a.type = Appointment::UBUNTU_ALARM; |
1186 | a.begin = DateTime::Local(2014, 12, 25, 0, 0, 0); |
1187 | a.end = a.begin.end_of_day(); |
1188 | + a.alarms.push_back(Alarm{"Alarm Text", "", a.begin}); |
1189 | |
1190 | auto loop = g_main_loop_new(nullptr, false); |
1191 | - auto on_snooze = [loop](const Appointment& appt){ |
1192 | - g_message("You clicked 'Snooze' for appt url '%s'", appt.url.c_str()); |
1193 | + auto on_snooze = [loop](const Appointment& appt, const Alarm&){ |
1194 | + g_message("You clicked 'Snooze' for appt url '%s'", appt.summary.c_str()); |
1195 | g_idle_add(quit_idle, loop); |
1196 | }; |
1197 | - auto on_ok = [loop](const Appointment&){ |
1198 | + auto on_ok = [loop](const Appointment&, const Alarm&){ |
1199 | g_message("You clicked 'OK'"); |
1200 | g_idle_add(quit_idle, loop); |
1201 | }; |
1202 | @@ -93,7 +93,7 @@ |
1203 | |
1204 | auto notification_engine = std::make_shared<uin::Engine>("indicator-datetime-service"); |
1205 | Snap snap (notification_engine, settings); |
1206 | - snap(a, on_snooze, on_ok); |
1207 | + snap(a, a.alarms.front(), on_snooze, on_ok); |
1208 | g_main_loop_run(loop); |
1209 | |
1210 | g_main_loop_unref(loop); |
1211 | |
1212 | === added file 'tests/print-to.h' |
1213 | --- tests/print-to.h 1970-01-01 00:00:00 +0000 |
1214 | +++ tests/print-to.h 2015-04-06 20:13:56 +0000 |
1215 | @@ -0,0 +1,45 @@ |
1216 | +/* |
1217 | + * Copyright 2015 Canonical Ltd. |
1218 | + * |
1219 | + * This program is free software: you can redistribute it and/or modify it |
1220 | + * under the terms of the GNU General Public License version 3, as published |
1221 | + * by the Free Software Foundation. |
1222 | + * |
1223 | + * This program is distributed in the hope that it will be useful, but |
1224 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1225 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1226 | + * PURPOSE. See the GNU General Public License for more details. |
1227 | + * |
1228 | + * You should have received a copy of the GNU General Public License along |
1229 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1230 | + * |
1231 | + * Authors: |
1232 | + * Charles Kerr <charles.kerr@canonical.com> |
1233 | + */ |
1234 | + |
1235 | +#ifndef INDICATOR_DATETIME_TESTS_PRINT_TO |
1236 | +#define INDICATOR_DATETIME_TESTS_PRINT_TO |
1237 | + |
1238 | +#include <algorithm> |
1239 | + |
1240 | +#include <datetime/appointment.h> |
1241 | + |
1242 | +namespace unity { |
1243 | +namespace indicator { |
1244 | +namespace datetime { |
1245 | + |
1246 | +/*** |
1247 | +**** PrintTo() functions for GTest to represent objects as strings |
1248 | +***/ |
1249 | + |
1250 | +void |
1251 | +PrintTo(const Alarm& alarm, std::ostream* os) |
1252 | +{ |
1253 | + *os << "{text:'" << alarm.text << "', audio_url:'" << alarm.audio_url << "', time:'"<<alarm.time.format("%F %T")<<"'}"; |
1254 | +} |
1255 | + |
1256 | +} // namespace datetime |
1257 | +} // namespace indicator |
1258 | +} // namespace unity |
1259 | + |
1260 | +#endif |
1261 | |
1262 | === added file 'tests/run-eds-test.sh' |
1263 | --- tests/run-eds-test.sh 1970-01-01 00:00:00 +0000 |
1264 | +++ tests/run-eds-test.sh 2015-04-06 20:13:56 +0000 |
1265 | @@ -0,0 +1,57 @@ |
1266 | +#!/bin/sh |
1267 | + |
1268 | +echo ARG0=$0 # this script |
1269 | +echo ARG1=$1 # full executable path of dbus-test-runner |
1270 | +echo ARG2=$2 # full executable path of test app |
1271 | +echo ARG3=$3 # test name |
1272 | +echo ARG4=$4 # full executable path of evolution-calendar-factory |
1273 | +echo ARG5=$5 # bus service name of calendar factory |
1274 | +echo ARG6=$6 # full exectuable path of evolution-source-registry |
1275 | +echo ARG7=$7 # full executable path of gvfs |
1276 | +echo ARG8=$8 # config files |
1277 | + |
1278 | +# set up the tmpdir and tell the shell to purge it when we exit |
1279 | +export TEST_TMP_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d $3-XXXXXXXXXX) || exit 1 |
1280 | +echo "running test '$3' in ${TEST_TMP_DIR}" |
1281 | + |
1282 | +# set up the environment variables |
1283 | +export QT_QPA_PLATFORM=minimal |
1284 | +export HOME=${TEST_TMP_DIR} |
1285 | +export XDG_RUNTIME_DIR=${TEST_TMP_DIR} |
1286 | +export XDG_CACHE_HOME=${TEST_TMP_DIR}/.cache |
1287 | +export XDG_CONFIG_HOME=${TEST_TMP_DIR}/.config |
1288 | +export XDG_DATA_HOME=${TEST_TMP_DIR}/.local/share |
1289 | +export XDG_DESKTOP_DIR=${TEST_TMP_DIR} |
1290 | +export XDG_DOCUMENTS_DIR=${TEST_TMP_DIR} |
1291 | +export XDG_DOWNLOAD_DIR=${TEST_TMP_DIR} |
1292 | +export XDG_MUSIC_DIR=${TEST_TMP_DIR} |
1293 | +export XDG_PICTURES_DIR=${TEST_TMP_DIR} |
1294 | +export XDG_PUBLICSHARE_DIR=${TEST_TMP_DIR} |
1295 | +export XDG_TEMPLATES_DIR=${TEST_TMP_DIR} |
1296 | +export XDG_VIDEOS_DIR=${TEST_TMP_DIR} |
1297 | +export QORGANIZER_EDS_DEBUG=On |
1298 | +export GIO_USE_VFS=local # needed to ensure GVFS shuts down cleanly after the test is over |
1299 | + |
1300 | +echo HOMEDIR=${HOME} |
1301 | +rm -rf ${XDG_DATA_HOME} |
1302 | + |
1303 | +# if there are canned config files for this test, move them into place now |
1304 | +if [ -d $8 ]; then |
1305 | + echo "copying files from $8 to $HOME" |
1306 | + cp --verbose --archive $8/. $HOME |
1307 | +fi |
1308 | + |
1309 | +# run dbus-test-runner |
1310 | +$1 --keep-env --max-wait=90 \ |
1311 | +--task $2 --task-name $3 --wait-until-complete --wait-for=org.gnome.evolution.dataserver.Calendar4 \ |
1312 | +--task $4 --task-name "evolution" --wait-until-complete -r |
1313 | +#--task $6 --task-name "source-registry" --wait-for=org.gtk.vfs.Daemon -r \ |
1314 | +#--task $7 --task-name "gvfsd" -r |
1315 | +rv=$? |
1316 | + |
1317 | +# if the test passed, blow away the tmpdir |
1318 | +if [ $rv -eq 0 ]; then |
1319 | + rm -rf $TEST_TMP_DIR |
1320 | +fi |
1321 | + |
1322 | +return $rv |
1323 | |
1324 | === modified file 'tests/test-alarm-queue.cpp' |
1325 | --- tests/test-alarm-queue.cpp 2015-03-16 20:07:54 +0000 |
1326 | +++ tests/test-alarm-queue.cpp 2015-04-06 20:13:56 +0000 |
1327 | @@ -48,7 +48,7 @@ |
1328 | m_range_planner.reset(new MockRangePlanner); |
1329 | m_upcoming.reset(new UpcomingPlanner(m_range_planner, m_state->clock->localtime())); |
1330 | m_watcher.reset(new SimpleAlarmQueue(m_state->clock, m_upcoming, m_wakeup_timer)); |
1331 | - m_watcher->alarm_reached().connect([this](const Appointment& appt){ |
1332 | + m_watcher->alarm_reached().connect([this](const Appointment& appt, const Alarm& /*alarm*/){ |
1333 | m_triggered.push_back(appt.uid); |
1334 | }); |
1335 | |
1336 | @@ -71,7 +71,7 @@ |
1337 | const auto tomorrow_begin = now.add_days(1).start_of_day(); |
1338 | const auto tomorrow_end = tomorrow_begin.end_of_day(); |
1339 | |
1340 | - Appointment a1; // an alarm clock appointment |
1341 | + Appointment a1; // an ubuntu alarm |
1342 | a1.color = "red"; |
1343 | a1.summary = "Alarm"; |
1344 | a1.summary = "http://www.example.com/"; |
1345 | @@ -79,18 +79,20 @@ |
1346 | a1.type = Appointment::UBUNTU_ALARM; |
1347 | a1.begin = tomorrow_begin; |
1348 | a1.end = tomorrow_end; |
1349 | + a1.alarms.push_back(Alarm{"Alarm Text", "", a1.begin}); |
1350 | |
1351 | const auto ubermorgen_begin = now.add_days(2).start_of_day(); |
1352 | const auto ubermorgen_end = ubermorgen_begin.end_of_day(); |
1353 | |
1354 | - Appointment a2; // a non-alarm appointment |
1355 | + Appointment a2; // something else |
1356 | a2.color = "green"; |
1357 | a2.summary = "Other Text"; |
1358 | a2.summary = "http://www.monkey.com/"; |
1359 | a2.uid = "monkey"; |
1360 | - a1.type = Appointment::EVENT; |
1361 | + a2.type = Appointment::EVENT; |
1362 | a2.begin = ubermorgen_begin; |
1363 | a2.end = ubermorgen_end; |
1364 | + a2.alarms.push_back(Alarm{"Alarm Text", "", a2.begin}); |
1365 | |
1366 | return std::vector<Appointment>({a1, a2}); |
1367 | } |
1368 | @@ -105,7 +107,7 @@ |
1369 | // Add some appointments to the planner. |
1370 | // One of these matches our state's localtime, so that should get triggered. |
1371 | std::vector<Appointment> a = build_some_appointments(); |
1372 | - a[0].begin = m_state->clock->localtime(); |
1373 | + a[0].begin = a[0].alarms.front().time = m_state->clock->localtime(); |
1374 | m_range_planner->appointments().set(a); |
1375 | |
1376 | // Confirm that it got fired |
1377 | @@ -135,7 +137,8 @@ |
1378 | { |
1379 | const auto now = m_state->clock->localtime(); |
1380 | std::vector<Appointment> a = build_some_appointments(); |
1381 | - a[0].begin = a[1].begin = now; |
1382 | + a[0].alarms.front().time = now; |
1383 | + a[1].alarms.front().time = now; |
1384 | m_range_planner->appointments().set(a); |
1385 | |
1386 | ASSERT_EQ(2, m_triggered.size()); |
1387 | @@ -151,7 +154,7 @@ |
1388 | const std::vector<Appointment> appointments = build_some_appointments(); |
1389 | std::vector<Appointment> a; |
1390 | a.push_back(appointments[0]); |
1391 | - a[0].begin = now; |
1392 | + a[0].alarms.front().time = now; |
1393 | m_range_planner->appointments().set(a); |
1394 | ASSERT_EQ(1, m_triggered.size()); |
1395 | EXPECT_EQ(a[0].uid, m_triggered[0]); |
1396 | |
1397 | === added directory 'tests/test-eds-valarms-config-files' |
1398 | === added directory 'tests/test-eds-valarms-config-files/.config' |
1399 | === added directory 'tests/test-eds-valarms-config-files/.config/evolution' |
1400 | === added directory 'tests/test-eds-valarms-config-files/.config/evolution/sources' |
1401 | === added file 'tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source' |
1402 | --- tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source 1970-01-01 00:00:00 +0000 |
1403 | +++ tests/test-eds-valarms-config-files/.config/evolution/sources/system-proxy.source 2015-04-06 20:13:56 +0000 |
1404 | @@ -0,0 +1,21 @@ |
1405 | + |
1406 | +[Data Source] |
1407 | +DisplayName=Default Proxy Settings |
1408 | +Enabled=true |
1409 | +Parent= |
1410 | + |
1411 | +[Proxy] |
1412 | +Method=default |
1413 | +IgnoreHosts=localhost;127.0.0.0/8;::1; |
1414 | +AutoconfigUrl= |
1415 | +FtpHost= |
1416 | +FtpPort=0 |
1417 | +HttpAuthPassword= |
1418 | +HttpAuthUser= |
1419 | +HttpHost= |
1420 | +HttpPort=8080 |
1421 | +HttpUseAuth=false |
1422 | +HttpsHost= |
1423 | +HttpsPort=0 |
1424 | +SocksHost= |
1425 | +SocksPort=0 |
1426 | |
1427 | === added directory 'tests/test-eds-valarms-config-files/.local' |
1428 | === added directory 'tests/test-eds-valarms-config-files/.local/share' |
1429 | === added directory 'tests/test-eds-valarms-config-files/.local/share/evolution' |
1430 | === added directory 'tests/test-eds-valarms-config-files/.local/share/evolution/calendar' |
1431 | === added directory 'tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system' |
1432 | === added file 'tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics' |
1433 | --- tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics 1970-01-01 00:00:00 +0000 |
1434 | +++ tests/test-eds-valarms-config-files/.local/share/evolution/calendar/system/calendar.ics 2015-04-06 20:13:56 +0000 |
1435 | @@ -0,0 +1,47 @@ |
1436 | +BEGIN:VCALENDAR |
1437 | +CALSCALE:GREGORIAN |
1438 | +PRODID:-//Ximian//NONSGML Evolution Calendar//EN |
1439 | +VERSION:2.0 |
1440 | +X-EVOLUTION-DATA-REVISION:2015-04-05T21:32:47.354433Z(2) |
1441 | +BEGIN:VEVENT |
1442 | +UID:20150405T213247Z-4371-32011-1698-1@ubuntu-phablet |
1443 | +DTSTAMP:20150405T213247Z |
1444 | +DTSTART:20150424T183500Z |
1445 | +DTEND:20150424T193554Z |
1446 | +X-LIC-ERROR;X-LIC-ERRORTYPE=VALUE-PARSE-ERROR:Can't parse as RECUR value |
1447 | + in RRULE property. Removing entire property: ERROR: No Value |
1448 | +SUMMARY:London Sprint Flight |
1449 | +CREATED:20150405T213247Z |
1450 | +LAST-MODIFIED:20150405T213247Z |
1451 | +BEGIN:VALARM |
1452 | +X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-2@ubuntu-phablet |
1453 | +ACTION:AUDIO |
1454 | +TRIGGER;VALUE=DURATION;RELATED=START:-P1D |
1455 | +REPEAT:3 |
1456 | +DURATION:PT2M |
1457 | +END:VALARM |
1458 | +BEGIN:VALARM |
1459 | +X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-3@ubuntu-phablet |
1460 | +ACTION:DISPLAY |
1461 | +DESCRIPTION:Time to pack! |
1462 | +TRIGGER;VALUE=DURATION;RELATED=START:-P1D |
1463 | +REPEAT:3 |
1464 | +DURATION:PT2M |
1465 | +END:VALARM |
1466 | +BEGIN:VALARM |
1467 | +X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-5@ubuntu-phablet |
1468 | +ACTION:AUDIO |
1469 | +TRIGGER;VALUE=DURATION;RELATED=START:-PT3H |
1470 | +REPEAT:3 |
1471 | +DURATION:PT2M |
1472 | +END:VALARM |
1473 | +BEGIN:VALARM |
1474 | +X-EVOLUTION-ALARM-UID:20150405T213247Z-4371-32011-1698-6@ubuntu-phablet |
1475 | +ACTION:DISPLAY |
1476 | +DESCRIPTION:Go to the airport! |
1477 | +TRIGGER;VALUE=DURATION;RELATED=START:-PT3H |
1478 | +REPEAT:3 |
1479 | +DURATION:PT2M |
1480 | +END:VALARM |
1481 | +END:VEVENT |
1482 | +END:VCALENDAR |
1483 | |
1484 | === added file 'tests/test-eds-valarms.cpp' |
1485 | --- tests/test-eds-valarms.cpp 1970-01-01 00:00:00 +0000 |
1486 | +++ tests/test-eds-valarms.cpp 2015-04-06 20:13:56 +0000 |
1487 | @@ -0,0 +1,101 @@ |
1488 | +/* |
1489 | + * Copyright 2015 Canonical Ltd. |
1490 | + * |
1491 | + * This program is free software: you can redistribute it and/or modify it |
1492 | + * under the terms of the GNU General Public License version 3, as published |
1493 | + * by the Free Software Foundation. |
1494 | + * |
1495 | + * This program is distributed in the hope that it will be useful, but |
1496 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1497 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1498 | + * PURPOSE. See the GNU General Public License for more details. |
1499 | + * |
1500 | + * You should have received a copy of the GNU General Public License along |
1501 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1502 | + * |
1503 | + * Authors: |
1504 | + * Charles Kerr <charles.kerr@canonical.com> |
1505 | + */ |
1506 | + |
1507 | +#include <algorithm> |
1508 | + |
1509 | +#include <datetime/alarm-queue-simple.h> |
1510 | +#include <datetime/clock-mock.h> |
1511 | +#include <datetime/engine-eds.h> |
1512 | +#include <datetime/planner-range.h> |
1513 | + |
1514 | +#include <gtest/gtest.h> |
1515 | + |
1516 | +#include "glib-fixture.h" |
1517 | +#include "print-to.h" |
1518 | +#include "timezone-mock.h" |
1519 | +#include "wakeup-timer-mock.h" |
1520 | + |
1521 | +using namespace unity::indicator::datetime; |
1522 | +using VAlarmFixture = GlibFixture; |
1523 | + |
1524 | +/*** |
1525 | +**** |
1526 | +***/ |
1527 | + |
1528 | +TEST_F(VAlarmFixture, MultipleAppointments) |
1529 | +{ |
1530 | + // start the EDS engine |
1531 | + auto engine = std::make_shared<EdsEngine>(); |
1532 | + |
1533 | + // we need a consistent timezone for the planner and our local DateTimes |
1534 | + constexpr char const * zone_str {"America/Chicago"}; |
1535 | + auto tz = std::make_shared<MockTimezone>(zone_str); |
1536 | + auto gtz = g_time_zone_new(zone_str); |
1537 | + |
1538 | + // make a planner that looks at the first half of 2015 in EDS |
1539 | + auto planner = std::make_shared<SimpleRangePlanner>(engine, tz); |
1540 | + const DateTime range_begin {gtz, 2015,1, 1, 0, 0, 0.0}; |
1541 | + const DateTime range_end {gtz, 2015,6,31,23,59,59.5}; |
1542 | + planner->range().set(std::make_pair(range_begin, range_end)); |
1543 | + |
1544 | + // give EDS a moment to load |
1545 | + if (planner->appointments().get().empty()) { |
1546 | + g_message("waiting a moment for EDS to load..."); |
1547 | + auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){ |
1548 | + g_message("ah, they loaded"); |
1549 | + if (!appointments.empty()) |
1550 | + g_main_loop_quit(loop); |
1551 | + }; |
1552 | + core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed)); |
1553 | + constexpr int max_wait_sec = 10; |
1554 | + wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND); |
1555 | + } |
1556 | + |
1557 | + // the planner should match what we've got in the calendar.ics file |
1558 | + const auto appts = planner->appointments().get(); |
1559 | + ASSERT_EQ(1, appts.size()); |
1560 | + const auto& appt = appts.front(); |
1561 | + ASSERT_EQ(8, appt.alarms.size()); |
1562 | + EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,35,0)}), appt.alarms[0]); |
1563 | + EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,37,0)}), appt.alarms[1]); |
1564 | + EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,39,0)}), appt.alarms[2]); |
1565 | + EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,41,0)}), appt.alarms[3]); |
1566 | + EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,35,0)}), appt.alarms[4]); |
1567 | + EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,37,0)}), appt.alarms[5]); |
1568 | + EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,39,0)}), appt.alarms[6]); |
1569 | + EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,41,0)}), appt.alarms[7]); |
1570 | + |
1571 | + // now let's try this out with AlarmQueue... |
1572 | + // hook the planner up to a SimpleAlarmQueue and confirm that it triggers for each of the reminders |
1573 | + auto mock_clock = std::make_shared<MockClock>(range_begin); |
1574 | + std::shared_ptr<Clock> clock = mock_clock; |
1575 | + std::shared_ptr<WakeupTimer> wakeup_timer = std::make_shared<MockWakeupTimer>(clock); |
1576 | + auto alarm_queue = std::make_shared<SimpleAlarmQueue>(clock, planner, wakeup_timer); |
1577 | + int triggered_count = 0; |
1578 | + alarm_queue->alarm_reached().connect([&triggered_count, appt](const Appointment&, const Alarm& active_alarm) { |
1579 | + EXPECT_TRUE(std::find(appt.alarms.begin(), appt.alarms.end(), active_alarm) != appt.alarms.end()); |
1580 | + ++triggered_count; |
1581 | + }); |
1582 | + for (auto now=range_begin; now<range_end; now+=std::chrono::minutes{1}) |
1583 | + mock_clock->set_localtime(now); |
1584 | + EXPECT_EQ(appt.alarms.size(), triggered_count); |
1585 | + |
1586 | + // cleanup |
1587 | + g_time_zone_unref(gtz); |
1588 | +} |
1589 | |
1590 | === modified file 'tests/test-live-actions.cpp' |
1591 | --- tests/test-live-actions.cpp 2015-03-16 20:07:54 +0000 |
1592 | +++ tests/test-live-actions.cpp 2015-04-06 20:13:56 +0000 |
1593 | @@ -319,10 +319,6 @@ |
1594 | a.type = Appointment::UBUNTU_ALARM; |
1595 | m_actions->phone_open_appointment(a); |
1596 | EXPECT_EQ(clock_app_url, m_live_actions->last_url); |
1597 | - |
1598 | - a.url = "appid://blah"; |
1599 | - m_actions->phone_open_appointment(a); |
1600 | - EXPECT_EQ(a.url, m_live_actions->last_url); |
1601 | } |
1602 | |
1603 | TEST_F(LiveActionsFixture, PhoneOpenCalendarApp) |
1604 | @@ -392,7 +388,6 @@ |
1605 | Appointment a1; |
1606 | a1.color = "green"; |
1607 | a1.summary = "write unit tests"; |
1608 | - a1.url = "http://www.ubuntu.com/"; |
1609 | a1.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; |
1610 | a1.begin = tomorrow_begin; |
1611 | a1.end = tomorrow_end; |
1612 | @@ -403,7 +398,6 @@ |
1613 | Appointment a2; |
1614 | a2.color = "orange"; |
1615 | a2.summary = "code review"; |
1616 | - a2.url = "http://www.ubuntu.com/"; |
1617 | a2.uid = "2756ff7de3745bbffd65d2e4779c37c7ca60d843"; |
1618 | a2.begin = ubermorgen_begin; |
1619 | a2.end = ubermorgen_end; |
1620 | |
1621 | === modified file 'tests/test-snap.cpp' |
1622 | --- tests/test-snap.cpp 2015-03-16 20:07:54 +0000 |
1623 | +++ tests/test-snap.cpp 2015-04-06 20:13:56 +0000 |
1624 | @@ -106,12 +106,12 @@ |
1625 | // init the Appointment |
1626 | appt.color = "green"; |
1627 | appt.summary = "Alarm"; |
1628 | - appt.url = "alarm:///hello-world"; |
1629 | appt.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; |
1630 | appt.type = Appointment::EVENT; |
1631 | const auto christmas = DateTime::Local(2015,12,25,0,0,0); |
1632 | appt.begin = christmas.start_of_day(); |
1633 | appt.end = christmas.end_of_day(); |
1634 | + appt.alarms.push_back(Alarm{"Alarm Text", "", appt.begin}); |
1635 | |
1636 | service = dbus_test_service_new(nullptr); |
1637 | |
1638 | @@ -343,8 +343,8 @@ |
1639 | make_interactive(); |
1640 | |
1641 | // call the Snap Decision |
1642 | - auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; |
1643 | - snap(appt, func, func); |
1644 | + auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);}; |
1645 | + snap(appt, appt.alarms.front(), func, func); |
1646 | |
1647 | // confirm that Notify got called once |
1648 | guint len = 0; |
1649 | @@ -393,8 +393,8 @@ |
1650 | make_interactive(); |
1651 | |
1652 | // invoke the notification |
1653 | - auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; |
1654 | - (*snap)(appt, func, func); |
1655 | + auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);}; |
1656 | + (*snap)(appt, appt.alarms.front(), func, func); |
1657 | |
1658 | wait_msec(1000); |
1659 | |
1660 | @@ -448,8 +448,8 @@ |
1661 | make_interactive(); |
1662 | |
1663 | // invoke the notification |
1664 | - auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; |
1665 | - (*snap)(appt, func, func); |
1666 | + auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);}; |
1667 | + (*snap)(appt, appt.alarms.front(), func, func); |
1668 | |
1669 | wait_msec(1000); |
1670 | |
1671 | @@ -484,14 +484,14 @@ |
1672 | { |
1673 | auto settings = std::make_shared<Settings>(); |
1674 | auto ne = std::make_shared<unity::indicator::notifications::Engine>(APP_NAME); |
1675 | - auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; |
1676 | + auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);}; |
1677 | GError * error = nullptr; |
1678 | |
1679 | // invoke a snap decision while haptic feedback is set to "pulse", |
1680 | // confirm that VibratePattern got called |
1681 | settings->alarm_haptic.set("pulse"); |
1682 | auto snap = new Snap (ne, settings); |
1683 | - (*snap)(appt, func, func); |
1684 | + (*snap)(appt, appt.alarms.front(), func, func); |
1685 | wait_msec(100); |
1686 | EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (haptic_mock, |
1687 | haptic_obj, |
1688 | @@ -506,7 +506,7 @@ |
1689 | dbus_test_dbus_mock_object_clear_method_calls (haptic_mock, haptic_obj, &error); |
1690 | settings->alarm_haptic.set("none"); |
1691 | snap = new Snap (ne, settings); |
1692 | - (*snap)(appt, func, func); |
1693 | + (*snap)(appt, appt.alarms.front(), func, func); |
1694 | wait_msec(100); |
1695 | EXPECT_FALSE (dbus_test_dbus_mock_object_check_method_call (haptic_mock, |
1696 | haptic_obj, |
1697 | |
1698 | === modified file 'tests/timezone-mock.h' |
1699 | --- tests/timezone-mock.h 2014-03-09 17:48:18 +0000 |
1700 | +++ tests/timezone-mock.h 2015-04-06 20:13:56 +0000 |
1701 | @@ -30,6 +30,7 @@ |
1702 | { |
1703 | public: |
1704 | MockTimezone() =default; |
1705 | + explicit MockTimezone(const std::string& zone) {timezone.set(zone);} |
1706 | ~MockTimezone() =default; |
1707 | }; |
1708 | |
1709 | |
1710 | === added file 'tests/wakeup-timer-mock.h' |
1711 | --- tests/wakeup-timer-mock.h 1970-01-01 00:00:00 +0000 |
1712 | +++ tests/wakeup-timer-mock.h 2015-04-06 20:13:56 +0000 |
1713 | @@ -0,0 +1,78 @@ |
1714 | +/* |
1715 | + * Copyright 2015 Canonical Ltd. |
1716 | + * |
1717 | + * This program is free software: you can redistribute it and/or modify it |
1718 | + * under the terms of the GNU General Public License version 3, as published |
1719 | + * by the Free Software Foundation. |
1720 | + * |
1721 | + * This program is distributed in the hope that it will be useful, but |
1722 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1723 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1724 | + * PURPOSE. See the GNU General Public License for more details. |
1725 | + * |
1726 | + * You should have received a copy of the GNU General Public License along |
1727 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1728 | + * |
1729 | + * Authors: |
1730 | + * Charles Kerr <charles.kerr@canonical.com> |
1731 | + */ |
1732 | + |
1733 | +#ifndef INDICATOR_DATETIME_WAKEUP_TIMER_MOCK_H |
1734 | +#define INDICATOR_DATETIME_WAKEUP_TIMER_MOCK_H |
1735 | + |
1736 | +#include <datetime/clock.h> |
1737 | +#include <datetime/wakeup-timer.h> |
1738 | + |
1739 | +namespace unity { |
1740 | +namespace indicator { |
1741 | +namespace datetime { |
1742 | + |
1743 | +/*** |
1744 | +**** |
1745 | +***/ |
1746 | + |
1747 | +/** |
1748 | + * \brief A one-shot timer that emits a signal when its timeout is reached. |
1749 | + */ |
1750 | +class MockWakeupTimer: public WakeupTimer |
1751 | +{ |
1752 | +public: |
1753 | + explicit MockWakeupTimer(const std::shared_ptr<Clock>& clock): |
1754 | + m_clock(clock) |
1755 | + { |
1756 | + m_clock->minute_changed.connect([this](){ |
1757 | + test_for_wakeup(); |
1758 | + }); |
1759 | + } |
1760 | + |
1761 | + virtual ~MockWakeupTimer() =default; |
1762 | + |
1763 | + virtual void set_wakeup_time (const DateTime& wakeup_time) override { |
1764 | + m_wakeup_time = wakeup_time; |
1765 | + test_for_wakeup(); |
1766 | + } |
1767 | + |
1768 | + virtual core::Signal<>& timeout() override { return m_timeout; } |
1769 | + |
1770 | +private: |
1771 | + |
1772 | + void test_for_wakeup() |
1773 | + { |
1774 | + if (DateTime::is_same_minute(m_clock->localtime(), m_wakeup_time)) |
1775 | + m_timeout(); |
1776 | + } |
1777 | + |
1778 | + core::Signal<> m_timeout; |
1779 | + const std::shared_ptr<Clock>& m_clock; |
1780 | + DateTime m_wakeup_time; |
1781 | +}; |
1782 | + |
1783 | +/*** |
1784 | +**** |
1785 | +***/ |
1786 | + |
1787 | +} // namespace datetime |
1788 | +} // namespace indicator |
1789 | +} // namespace unity |
1790 | + |
1791 | +#endif // INDICATOR_DATETIME_WAKEUP_TIMER_MOCK_H |
FAILED: Continuous integration, rev:417 jenkins. qa.ubuntu. com/job/ indicator- datetime- ci/304/ jenkins. qa.ubuntu. com/job/ indicator- datetime- vivid-amd64- ci/18/console jenkins. qa.ubuntu. com/job/ indicator- datetime- vivid-armhf- ci/18/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- datetime- ci/304/ rebuild
http://