Merge lp:~charlesk/indicator-datetime/always-get-initial-tzid-from-timedate1 into lp:indicator-datetime/15.10
- always-get-initial-tzid-from-timedate1
- Merge into trunk.15.10
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Renato Araujo Oliveira Filho | ||||
Approved revision: | 448 | ||||
Merged at revision: | 442 | ||||
Proposed branch: | lp:~charlesk/indicator-datetime/always-get-initial-tzid-from-timedate1 | ||||
Merge into: | lp:indicator-datetime/15.10 | ||||
Diff against target: |
796 lines (+362/-266) 7 files modified
include/datetime/dbus-shared.h (+42/-0) include/datetime/timezone-timedated.h (+4/-4) include/datetime/timezones-live.h (+2/-3) src/main.cpp (+12/-2) src/timezone-timedated.cpp (+139/-135) src/timezones-live.cpp (+7/-4) tests/test-timezone-timedated.cpp (+156/-118) |
||||
To merge this branch: | bzr merge lp:~charlesk/indicator-datetime/always-get-initial-tzid-from-timedate1 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Renato Araujo Oliveira Filho (community) | Approve | ||
Antti Kaijanmäki (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+291421@code.launchpad.net |
Commit message
Fix possible startup timing issues when watching for a timezone from timedate1
Description of the change
Possible fix to bug #1567940, fixes a couple of bugs in the code that got tzid changes from org.freedesktop
1. Watch for timedate1 to show up on the bus, and query it when it does, so that there's no possible timing issue between indicator-
2. Use G_BUS_NAME_
3. Get the initial value by querying timedate1's properties instead of reading from /etc/timezone
4. If 'Timezone' shows up in timedate1's invalidated properties signal, again query for the value instead of reading /etc/timezone
- 444. By Charles Kerr
-
in tests/test-
timezone- timedated, fix copy-paste error in comments - 445. By Charles Kerr
-
in timezone-timedated, check for error!=nullptr before passing it to g_error_matches()
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:445
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
code looks good, some small inline code comments.
Renato Araujo Oliveira Filho (renatofilho) : | # |
Antti Kaijanmäki (kaijanmaki) wrote : | # |
code looks good, but the juggling with m_connection looks a bit brittle.
How about initializing m_connection in the constructor with g_bus_get_sync(), unreffing it in the destructor and using g_bus_watch_
- 446. By Charles Kerr
-
in TimedatedTimezone, take a GDBusConnection argument in the ctor to simplify state management
- 447. By Charles Kerr
-
in LiveTimezones, pass the primary timezone to it on construction. We used to create it implicitly but can't do that anymore now that TimedatedTimezone takes its own ctor argument.
- 448. By Charles Kerr
-
sync tests to new ctor arguments for TimedatedTimezone and LiveTimezones
Charles Kerr (charlesk) wrote : | # |
Thanks for the feedback. Since both of you commented on the clumsiness of m_connection and m_subscription's state management, I've cleaned it up in r446..448 by passing the GDBusConnection* in the ctor.
This way we can unconditionally watch & subscribe in the ctor, then unconditionally unsubscribe and unwatch in the dtor, no edge cases or having to check to see if the pointers exist yet. :)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:448
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
looks good.
Preview Diff
1 | === modified file 'include/datetime/dbus-shared.h' | |||
2 | --- include/datetime/dbus-shared.h 2014-09-17 16:51:51 +0000 | |||
3 | +++ include/datetime/dbus-shared.h 2016-04-12 17:11:17 +0000 | |||
4 | @@ -28,5 +28,47 @@ | |||
5 | 28 | #define BUS_POWERD_PATH "/com/canonical/powerd" | 28 | #define BUS_POWERD_PATH "/com/canonical/powerd" |
6 | 29 | #define BUS_POWERD_INTERFACE "com.canonical.powerd" | 29 | #define BUS_POWERD_INTERFACE "com.canonical.powerd" |
7 | 30 | 30 | ||
8 | 31 | namespace unity { | ||
9 | 32 | namespace indicator { | ||
10 | 33 | namespace datetime { | ||
11 | 34 | |||
12 | 35 | namespace Bus | ||
13 | 36 | { | ||
14 | 37 | namespace Timedate1 | ||
15 | 38 | { | ||
16 | 39 | static constexpr char const * BUSNAME {"org.freedesktop.timedate1"}; | ||
17 | 40 | static constexpr char const * ADDR {"/org/freedesktop/timedate1"}; | ||
18 | 41 | static constexpr char const * IFACE {"org.freedesktop.timedate1"}; | ||
19 | 42 | |||
20 | 43 | namespace Properties | ||
21 | 44 | { | ||
22 | 45 | static constexpr char const * TIMEZONE {"Timezone"}; | ||
23 | 46 | } | ||
24 | 47 | |||
25 | 48 | namespace Methods | ||
26 | 49 | { | ||
27 | 50 | static constexpr char const * SET_TIMEZONE {"SetTimezone"}; | ||
28 | 51 | } | ||
29 | 52 | } | ||
30 | 53 | |||
31 | 54 | namespace Properties | ||
32 | 55 | { | ||
33 | 56 | static constexpr char const * IFACE {"org.freedesktop.DBus.Properties"}; | ||
34 | 57 | |||
35 | 58 | namespace Methods | ||
36 | 59 | { | ||
37 | 60 | static constexpr char const * GET {"Get"}; | ||
38 | 61 | } | ||
39 | 62 | |||
40 | 63 | namespace Signals | ||
41 | 64 | { | ||
42 | 65 | static constexpr char const * PROPERTIES_CHANGED {"PropertiesChanged"}; | ||
43 | 66 | } | ||
44 | 67 | } | ||
45 | 68 | } | ||
46 | 69 | |||
47 | 70 | } // namespace datetime | ||
48 | 71 | } // namespace indicator | ||
49 | 72 | } // namespace unity | ||
50 | 31 | 73 | ||
51 | 32 | #endif /* INDICATOR_DATETIME_DBUS_SHARED_H */ | 74 | #endif /* INDICATOR_DATETIME_DBUS_SHARED_H */ |
52 | 33 | 75 | ||
53 | === modified file 'include/datetime/timezone-timedated.h' | |||
54 | --- include/datetime/timezone-timedated.h 2015-09-03 09:54:20 +0000 | |||
55 | +++ include/datetime/timezone-timedated.h 2016-04-12 17:11:17 +0000 | |||
56 | @@ -20,10 +20,10 @@ | |||
57 | 20 | #ifndef INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H | 20 | #ifndef INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H |
58 | 21 | #define INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H | 21 | #define INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H |
59 | 22 | 22 | ||
60 | 23 | #define DEFAULT_FILENAME "/etc/timezone" | ||
61 | 24 | |||
62 | 25 | #include <datetime/timezone.h> // base class | 23 | #include <datetime/timezone.h> // base class |
63 | 26 | 24 | ||
64 | 25 | #include <gio/gio.h> // GDBusConnection* | ||
65 | 26 | |||
66 | 27 | #include <string> // std::string | 27 | #include <string> // std::string |
67 | 28 | 28 | ||
68 | 29 | namespace unity { | 29 | namespace unity { |
69 | @@ -31,12 +31,12 @@ | |||
70 | 31 | namespace datetime { | 31 | namespace datetime { |
71 | 32 | 32 | ||
72 | 33 | /** | 33 | /** |
74 | 34 | * \brief A #Timezone that gets its information from monitoring a file, such as /etc/timezone | 34 | * \brief A #Timezone that gets its information from org.freedesktop.timedate1 |
75 | 35 | */ | 35 | */ |
76 | 36 | class TimedatedTimezone: public Timezone | 36 | class TimedatedTimezone: public Timezone |
77 | 37 | { | 37 | { |
78 | 38 | public: | 38 | public: |
80 | 39 | TimedatedTimezone(std::string filename = DEFAULT_FILENAME); | 39 | TimedatedTimezone(GDBusConnection* connection); |
81 | 40 | ~TimedatedTimezone(); | 40 | ~TimedatedTimezone(); |
82 | 41 | 41 | ||
83 | 42 | private: | 42 | private: |
84 | 43 | 43 | ||
85 | === modified file 'include/datetime/timezones-live.h' | |||
86 | --- include/datetime/timezones-live.h 2015-09-01 09:52:13 +0000 | |||
87 | +++ include/datetime/timezones-live.h 2016-04-12 17:11:17 +0000 | |||
88 | @@ -23,7 +23,6 @@ | |||
89 | 23 | #include <datetime/settings.h> | 23 | #include <datetime/settings.h> |
90 | 24 | #include <datetime/timezones.h> | 24 | #include <datetime/timezones.h> |
91 | 25 | #include <datetime/timezone-geoclue.h> | 25 | #include <datetime/timezone-geoclue.h> |
92 | 26 | #include <datetime/timezone-timedated.h> | ||
93 | 27 | 26 | ||
94 | 28 | #include <memory> // shared_ptr<> | 27 | #include <memory> // shared_ptr<> |
95 | 29 | 28 | ||
96 | @@ -38,13 +37,13 @@ | |||
97 | 38 | class LiveTimezones: public Timezones | 37 | class LiveTimezones: public Timezones |
98 | 39 | { | 38 | { |
99 | 40 | public: | 39 | public: |
101 | 41 | LiveTimezones(const std::shared_ptr<const Settings>& settings); | 40 | LiveTimezones(const std::shared_ptr<const Settings>& settings, const std::shared_ptr<Timezone>& primary_timezone); |
102 | 42 | 41 | ||
103 | 43 | private: | 42 | private: |
104 | 44 | void update_geolocation(); | 43 | void update_geolocation(); |
105 | 45 | void update_timezones(); | 44 | void update_timezones(); |
106 | 46 | 45 | ||
108 | 47 | TimedatedTimezone m_file; | 46 | std::shared_ptr<Timezone> m_primary_timezone; |
109 | 48 | std::shared_ptr<const Settings> m_settings; | 47 | std::shared_ptr<const Settings> m_settings; |
110 | 49 | std::shared_ptr<GeoclueTimezone> m_geo; | 48 | std::shared_ptr<GeoclueTimezone> m_geo; |
111 | 50 | }; | 49 | }; |
112 | 51 | 50 | ||
113 | === modified file 'src/main.cpp' | |||
114 | --- src/main.cpp 2015-10-13 16:06:35 +0000 | |||
115 | +++ src/main.cpp 2016-04-12 17:11:17 +0000 | |||
116 | @@ -68,7 +68,7 @@ | |||
117 | 68 | { | 68 | { |
118 | 69 | // create the live objects | 69 | // create the live objects |
119 | 70 | auto live_settings = std::make_shared<LiveSettings>(); | 70 | auto live_settings = std::make_shared<LiveSettings>(); |
121 | 71 | auto live_timezones = std::make_shared<LiveTimezones>(live_settings); | 71 | auto live_timezones = std::make_shared<LiveTimezones>(live_settings, timezone_); |
122 | 72 | auto live_clock = std::make_shared<LiveClock>(timezone_); | 72 | auto live_clock = std::make_shared<LiveClock>(timezone_); |
123 | 73 | 73 | ||
124 | 74 | // create a full-month planner currently pointing to the current month | 74 | // create a full-month planner currently pointing to the current month |
125 | @@ -127,8 +127,17 @@ | |||
126 | 127 | bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); | 127 | bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); |
127 | 128 | textdomain(GETTEXT_PACKAGE); | 128 | textdomain(GETTEXT_PACKAGE); |
128 | 129 | 129 | ||
129 | 130 | // get the system bus | ||
130 | 131 | GError* error {}; | ||
131 | 132 | auto system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error); | ||
132 | 133 | if (error != nullptr) { | ||
133 | 134 | g_critical("Unable to get system bus: %s", error->message); | ||
134 | 135 | g_clear_error(&error); | ||
135 | 136 | return 0; | ||
136 | 137 | } | ||
137 | 138 | |||
138 | 130 | auto engine = create_engine(); | 139 | auto engine = create_engine(); |
140 | 131 | auto timezone_ = std::make_shared<TimedatedTimezone>(); | 140 | auto timezone_ = std::make_shared<TimedatedTimezone>(system_bus); |
141 | 132 | auto state = create_state(engine, timezone_); | 141 | auto state = create_state(engine, timezone_); |
142 | 133 | auto actions = std::make_shared<LiveActions>(state); | 142 | auto actions = std::make_shared<LiveActions>(state); |
143 | 134 | MenuFactory factory(actions, state); | 143 | MenuFactory factory(actions, state); |
144 | @@ -165,5 +174,6 @@ | |||
145 | 165 | g_main_loop_run(loop); | 174 | g_main_loop_run(loop); |
146 | 166 | 175 | ||
147 | 167 | g_main_loop_unref(loop); | 176 | g_main_loop_unref(loop); |
148 | 177 | g_clear_object(&system_bus); | ||
149 | 168 | return 0; | 178 | return 0; |
150 | 169 | } | 179 | } |
151 | 170 | 180 | ||
152 | === modified file 'src/timezone-timedated.cpp' | |||
153 | --- src/timezone-timedated.cpp 2015-09-03 16:00:36 +0000 | |||
154 | +++ src/timezone-timedated.cpp 2016-04-12 17:11:17 +0000 | |||
155 | @@ -17,6 +17,7 @@ | |||
156 | 17 | * Charles Kerr <charles.kerr@canonical.com> | 17 | * Charles Kerr <charles.kerr@canonical.com> |
157 | 18 | */ | 18 | */ |
158 | 19 | 19 | ||
159 | 20 | #include <datetime/dbus-shared.h> | ||
160 | 20 | #include <datetime/timezone-timedated.h> | 21 | #include <datetime/timezone-timedated.h> |
161 | 21 | 22 | ||
162 | 22 | #include <gio/gio.h> | 23 | #include <gio/gio.h> |
163 | @@ -36,166 +37,169 @@ | |||
164 | 36 | { | 37 | { |
165 | 37 | public: | 38 | public: |
166 | 38 | 39 | ||
170 | 39 | Impl(TimedatedTimezone& owner, std::string filename): | 40 | Impl(TimedatedTimezone& owner, GDBusConnection* connection): |
171 | 40 | m_owner(owner), | 41 | m_owner{owner}, |
172 | 41 | m_filename(filename) | 42 | m_connection{G_DBUS_CONNECTION(g_object_ref(G_OBJECT(connection)))}, |
173 | 43 | m_cancellable{g_cancellable_new()} | ||
174 | 42 | { | 44 | { |
177 | 43 | g_debug("Filename is '%s'", filename.c_str()); | 45 | // set the fallback value |
178 | 44 | monitor_timezone_property(); | 46 | m_owner.timezone.set("Etc/Utc"); |
179 | 47 | |||
180 | 48 | // watch for timedate1 on the bus | ||
181 | 49 | m_watcher_id = g_bus_watch_name_on_connection( | ||
182 | 50 | m_connection, | ||
183 | 51 | Bus::Timedate1::BUSNAME, | ||
184 | 52 | G_BUS_NAME_WATCHER_FLAGS_AUTO_START, | ||
185 | 53 | on_timedate1_appeared, | ||
186 | 54 | on_timedate1_vanished, | ||
187 | 55 | this, | ||
188 | 56 | nullptr); | ||
189 | 57 | |||
190 | 58 | // listen for changed properties | ||
191 | 59 | m_signal_subscription_id = g_dbus_connection_signal_subscribe( | ||
192 | 60 | m_connection, | ||
193 | 61 | Bus::Timedate1::IFACE, | ||
194 | 62 | Bus::Properties::IFACE, | ||
195 | 63 | Bus::Properties::Signals::PROPERTIES_CHANGED, | ||
196 | 64 | Bus::Timedate1::ADDR, | ||
197 | 65 | nullptr, | ||
198 | 66 | G_DBUS_SIGNAL_FLAGS_NONE, | ||
199 | 67 | on_properties_changed, | ||
200 | 68 | this, | ||
201 | 69 | nullptr); | ||
202 | 45 | } | 70 | } |
203 | 46 | 71 | ||
204 | 47 | ~Impl() | 72 | ~Impl() |
205 | 48 | { | 73 | { |
207 | 49 | clear(); | 74 | g_cancellable_cancel(m_cancellable); |
208 | 75 | g_clear_object(&m_cancellable); | ||
209 | 76 | |||
210 | 77 | g_bus_unwatch_name(m_watcher_id); | ||
211 | 78 | |||
212 | 79 | g_dbus_connection_signal_unsubscribe(m_connection, m_signal_subscription_id); | ||
213 | 80 | |||
214 | 81 | g_clear_object(&m_connection); | ||
215 | 50 | } | 82 | } |
216 | 51 | 83 | ||
217 | 52 | private: | 84 | private: |
218 | 53 | 85 | ||
237 | 54 | void clear() | 86 | static void on_timedate1_appeared(GDBusConnection * /*connection*/, |
238 | 55 | { | 87 | const gchar * name, |
239 | 56 | if (m_connection && m_signal_subscription_id) | 88 | const gchar * /*name_owner*/, |
240 | 57 | { | 89 | gpointer gself) |
241 | 58 | g_dbus_connection_signal_unsubscribe (m_connection, m_signal_subscription_id); | 90 | { |
242 | 59 | m_signal_subscription_id = 0; | 91 | g_debug("%s appeared on bus", name); |
243 | 60 | } | 92 | |
244 | 61 | 93 | static_cast<Impl*>(gself)->ask_for_timezone(); | |
245 | 62 | g_clear_object(&m_connection); | 94 | } |
246 | 63 | } | 95 | |
247 | 64 | 96 | static void on_timedate1_vanished(GDBusConnection * /*connection*/, | |
248 | 65 | static void on_properties_changed (GDBusConnection *connection G_GNUC_UNUSED, | 97 | const gchar * name, |
249 | 66 | const gchar *sender_name G_GNUC_UNUSED, | 98 | gpointer /*gself*/) |
250 | 67 | const gchar *object_path G_GNUC_UNUSED, | 99 | { |
251 | 68 | const gchar *interface_name G_GNUC_UNUSED, | 100 | g_debug("%s not present on bus", name); |
252 | 69 | const gchar *signal_name G_GNUC_UNUSED, | 101 | } |
253 | 70 | GVariant *parameters, | 102 | |
254 | 71 | gpointer gself) | 103 | static void on_properties_changed(GDBusConnection * /*connection*/, |
255 | 104 | const gchar * /*sender_name*/, | ||
256 | 105 | const gchar * /*object_path*/, | ||
257 | 106 | const gchar * /*interface_name*/, | ||
258 | 107 | const gchar * /*signal_name*/, | ||
259 | 108 | GVariant * parameters, | ||
260 | 109 | gpointer gself) | ||
261 | 72 | { | 110 | { |
262 | 73 | auto self = static_cast<Impl*>(gself); | 111 | auto self = static_cast<Impl*>(gself); |
363 | 74 | const char *tz; | 112 | |
364 | 75 | GVariant *changed_properties; | 113 | GVariant* changed_properties {}; |
365 | 76 | gchar **invalidated_properties; | 114 | gchar** invalidated_properties {}; |
366 | 77 | 115 | g_variant_get(parameters, "(s@a{sv}^as)", NULL, &changed_properties, &invalidated_properties); | |
367 | 78 | g_variant_get (parameters, "(s@a{sv}^as)", NULL, &changed_properties, &invalidated_properties); | 116 | |
368 | 79 | 117 | const char* tz {}; | |
369 | 80 | if (g_variant_lookup(changed_properties, "Timezone", "&s", &tz, NULL)) | 118 | if (g_variant_lookup(changed_properties, Bus::Timedate1::Properties::TIMEZONE, "&s", &tz, NULL)) |
370 | 81 | self->notify_timezone(tz); | 119 | { |
371 | 82 | else if (g_strv_contains (invalidated_properties, "Timezone")) | 120 | if (tz != nullptr) |
372 | 83 | self->notify_timezone(self->get_timezone_from_file(self->m_filename)); | 121 | self->set_timezone(tz); |
373 | 84 | 122 | else | |
374 | 85 | g_variant_unref (changed_properties); | 123 | g_warning("%s no timezone found", G_STRLOC); |
375 | 86 | g_strfreev (invalidated_properties); | 124 | } |
376 | 87 | } | 125 | else if (g_strv_contains(invalidated_properties, Bus::Timedate1::Properties::TIMEZONE)) |
377 | 88 | 126 | { | |
378 | 89 | void monitor_timezone_property() | 127 | self->ask_for_timezone(); |
379 | 90 | { | 128 | } |
380 | 91 | GError *err = nullptr; | 129 | |
381 | 92 | 130 | g_variant_unref(changed_properties); | |
382 | 93 | /* | 131 | g_strfreev(invalidated_properties); |
383 | 94 | * There is an unlikely race which happens if there is an activation | 132 | } |
384 | 95 | * and timezone change before our match rule is added. | 133 | |
385 | 96 | */ | 134 | void ask_for_timezone() |
386 | 97 | notify_timezone(get_timezone_from_file(m_filename)); | 135 | { |
387 | 98 | 136 | g_dbus_connection_call( | |
388 | 99 | /* | 137 | m_connection, |
389 | 100 | * Make sure the bus is around at least until we add the match rules, | 138 | Bus::Timedate1::BUSNAME, |
390 | 101 | * otherwise things (tests) are sad. | 139 | Bus::Timedate1::ADDR, |
391 | 102 | */ | 140 | Bus::Properties::IFACE, |
392 | 103 | m_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, | 141 | Bus::Properties::Methods::GET, |
393 | 104 | nullptr, | 142 | g_variant_new("(ss)", Bus::Timedate1::IFACE, Bus::Timedate1::Properties::TIMEZONE), |
394 | 105 | &err); | 143 | G_VARIANT_TYPE("(v)"), |
395 | 106 | 144 | G_DBUS_CALL_FLAGS_NONE, | |
396 | 107 | if (err) | 145 | -1, |
397 | 108 | { | 146 | m_cancellable, |
398 | 109 | g_warning("Couldn't get bus connection: '%s'", err->message); | 147 | on_get_timezone_ready, |
399 | 110 | g_error_free(err); | 148 | this); |
400 | 111 | return; | 149 | } |
401 | 112 | } | 150 | |
402 | 113 | 151 | static void on_get_timezone_ready(GObject * connection, | |
403 | 114 | m_signal_subscription_id = g_dbus_connection_signal_subscribe(m_connection, | 152 | GAsyncResult * res, |
404 | 115 | "org.freedesktop.timedate1", | 153 | gpointer gself) |
405 | 116 | "org.freedesktop.DBus.Properties", | 154 | { |
406 | 117 | "PropertiesChanged", | 155 | GError* error {}; |
407 | 118 | "/org/freedesktop/timedate1", | 156 | GVariant* v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(connection), res, &error); |
308 | 119 | NULL, G_DBUS_SIGNAL_FLAGS_NONE, | ||
309 | 120 | on_properties_changed, | ||
310 | 121 | this, nullptr); | ||
311 | 122 | } | ||
312 | 123 | |||
313 | 124 | void notify_timezone(std::string new_timezone) | ||
314 | 125 | { | ||
315 | 126 | g_debug("notify_timezone '%s'", new_timezone.c_str()); | ||
316 | 127 | if (!new_timezone.empty()) | ||
317 | 128 | m_owner.timezone.set(new_timezone); | ||
318 | 129 | } | ||
319 | 130 | |||
320 | 131 | std::string get_timezone_from_file(const std::string& filename) | ||
321 | 132 | { | ||
322 | 133 | GError * error; | ||
323 | 134 | GIOChannel * io_channel; | ||
324 | 135 | std::string ret; | ||
325 | 136 | |||
326 | 137 | // read through filename line-by-line until we fine a nonempty non-comment line | ||
327 | 138 | error = nullptr; | ||
328 | 139 | io_channel = g_io_channel_new_file(filename.c_str(), "r", &error); | ||
329 | 140 | if (error == nullptr) | ||
330 | 141 | { | ||
331 | 142 | auto line = g_string_new(nullptr); | ||
332 | 143 | |||
333 | 144 | while(ret.empty()) | ||
334 | 145 | { | ||
335 | 146 | const auto io_status = g_io_channel_read_line_string(io_channel, line, nullptr, &error); | ||
336 | 147 | if ((io_status == G_IO_STATUS_EOF) || (io_status == G_IO_STATUS_ERROR)) | ||
337 | 148 | break; | ||
338 | 149 | if (error != nullptr) | ||
339 | 150 | break; | ||
340 | 151 | |||
341 | 152 | g_strstrip(line->str); | ||
342 | 153 | |||
343 | 154 | if (!line->len) // skip empty lines | ||
344 | 155 | continue; | ||
345 | 156 | |||
346 | 157 | if (*line->str=='#') // skip comments | ||
347 | 158 | continue; | ||
348 | 159 | |||
349 | 160 | ret = line->str; | ||
350 | 161 | } | ||
351 | 162 | |||
352 | 163 | g_string_free(line, true); | ||
353 | 164 | } else | ||
354 | 165 | /* Default to UTC */ | ||
355 | 166 | ret = "Etc/Utc"; | ||
356 | 167 | |||
357 | 168 | if (io_channel != nullptr) | ||
358 | 169 | { | ||
359 | 170 | g_io_channel_shutdown(io_channel, false, nullptr); | ||
360 | 171 | g_io_channel_unref(io_channel); | ||
361 | 172 | } | ||
362 | 173 | |||
408 | 174 | if (error != nullptr) | 157 | if (error != nullptr) |
409 | 175 | { | 158 | { |
415 | 176 | g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, filename.c_str(), error->message); | 159 | if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
416 | 177 | g_error_free(error); | 160 | g_warning("%s Couldn't get timezone: %s", G_STRLOC, error->message); |
417 | 178 | } | 161 | } |
418 | 179 | 162 | else if (v != nullptr) | |
419 | 180 | return ret; | 163 | { |
420 | 164 | GVariant* tzv {}; | ||
421 | 165 | g_variant_get(v, "(v)", &tzv); | ||
422 | 166 | const char* tz = g_variant_get_string(tzv, nullptr); | ||
423 | 167 | |||
424 | 168 | if (tz != nullptr) | ||
425 | 169 | static_cast<Impl*>(gself)->set_timezone(tz); | ||
426 | 170 | else | ||
427 | 171 | g_warning("%s no timezone found", G_STRLOC); | ||
428 | 172 | |||
429 | 173 | g_clear_pointer(&tzv, g_variant_unref); | ||
430 | 174 | g_clear_pointer(&v, g_variant_unref); | ||
431 | 175 | } | ||
432 | 176 | } | ||
433 | 177 | |||
434 | 178 | void set_timezone(const std::string& tz) | ||
435 | 179 | { | ||
436 | 180 | g_return_if_fail(!tz.empty()); | ||
437 | 181 | |||
438 | 182 | g_debug("set timezone: '%s'", tz.c_str()); | ||
439 | 183 | m_owner.timezone.set(tz); | ||
440 | 181 | } | 184 | } |
441 | 182 | 185 | ||
442 | 183 | /*** | 186 | /*** |
443 | 184 | **** | 187 | **** |
444 | 185 | ***/ | 188 | ***/ |
445 | 186 | 189 | ||
450 | 187 | TimedatedTimezone & m_owner; | 190 | TimedatedTimezone& m_owner; |
451 | 188 | GDBusConnection *m_connection = nullptr; | 191 | GDBusConnection* m_connection {}; |
452 | 189 | unsigned long m_signal_subscription_id = 0; | 192 | GCancellable* m_cancellable {}; |
453 | 190 | std::string m_filename; | 193 | unsigned long m_signal_subscription_id {}; |
454 | 194 | unsigned int m_watcher_id {}; | ||
455 | 191 | }; | 195 | }; |
456 | 192 | 196 | ||
457 | 193 | /*** | 197 | /*** |
458 | 194 | **** | 198 | **** |
459 | 195 | ***/ | 199 | ***/ |
460 | 196 | 200 | ||
463 | 197 | TimedatedTimezone::TimedatedTimezone(std::string filename): | 201 | TimedatedTimezone::TimedatedTimezone(GDBusConnection* connection): |
464 | 198 | impl(new Impl{*this, filename}) | 202 | impl{new Impl{*this, connection}} |
465 | 199 | { | 203 | { |
466 | 200 | } | 204 | } |
467 | 201 | 205 | ||
468 | 202 | 206 | ||
469 | === modified file 'src/timezones-live.cpp' | |||
470 | --- src/timezones-live.cpp 2015-08-31 21:08:36 +0000 | |||
471 | +++ src/timezones-live.cpp 2016-04-12 17:11:17 +0000 | |||
472 | @@ -25,11 +25,14 @@ | |||
473 | 25 | namespace indicator { | 25 | namespace indicator { |
474 | 26 | namespace datetime { | 26 | namespace datetime { |
475 | 27 | 27 | ||
478 | 28 | LiveTimezones::LiveTimezones(const std::shared_ptr<const Settings>& settings): | 28 | LiveTimezones::LiveTimezones( |
479 | 29 | m_file(), | 29 | const std::shared_ptr<const Settings>& settings, |
480 | 30 | const std::shared_ptr<Timezone>& primary_timezone | ||
481 | 31 | ): | ||
482 | 32 | m_primary_timezone(primary_timezone), | ||
483 | 30 | m_settings(settings) | 33 | m_settings(settings) |
484 | 31 | { | 34 | { |
486 | 32 | m_file.timezone.changed().connect([this](const std::string&){update_timezones();}); | 35 | m_primary_timezone->timezone.changed().connect([this](const std::string&){update_timezones();}); |
487 | 33 | 36 | ||
488 | 34 | m_settings->show_detected_location.changed().connect([this](bool){update_geolocation();}); | 37 | m_settings->show_detected_location.changed().connect([this](bool){update_geolocation();}); |
489 | 35 | update_geolocation(); | 38 | update_geolocation(); |
490 | @@ -53,7 +56,7 @@ | |||
491 | 53 | 56 | ||
492 | 54 | void LiveTimezones::update_timezones() | 57 | void LiveTimezones::update_timezones() |
493 | 55 | { | 58 | { |
495 | 56 | const auto a = m_file.timezone.get(); | 59 | const auto a = m_primary_timezone->timezone.get(); |
496 | 57 | const auto b = m_geo ? m_geo->timezone.get() : ""; | 60 | const auto b = m_geo ? m_geo->timezone.get() : ""; |
497 | 58 | 61 | ||
498 | 59 | timezone.set(a.empty() ? b : a); | 62 | timezone.set(a.empty() ? b : a); |
499 | 60 | 63 | ||
500 | === modified file 'tests/test-timezone-timedated.cpp' | |||
501 | --- tests/test-timezone-timedated.cpp 2015-09-09 16:16:41 +0000 | |||
502 | +++ tests/test-timezone-timedated.cpp 2016-04-12 17:11:17 +0000 | |||
503 | @@ -1,9 +1,5 @@ | |||
504 | 1 | |||
505 | 2 | /* | 1 | /* |
510 | 3 | * Copyright 2013 Canonical Ltd. | 2 | * Copyright © 2014-2016 Canonical Ltd. |
507 | 4 | * | ||
508 | 5 | * Authors: | ||
509 | 6 | * Charles Kerr <charles.kerr@canonical.com> | ||
511 | 7 | * | 3 | * |
512 | 8 | * This program is free software: you can redistribute it and/or modify it | 4 | * This program is free software: you can redistribute it and/or modify it |
513 | 9 | * under the terms of the GNU General Public License version 3, as published | 5 | * under the terms of the GNU General Public License version 3, as published |
514 | @@ -16,127 +12,169 @@ | |||
515 | 16 | * | 12 | * |
516 | 17 | * You should have received a copy of the GNU General Public License along | 13 | * You should have received a copy of the GNU General Public License along |
517 | 18 | * with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. |
518 | 15 | * | ||
519 | 16 | * Authors: | ||
520 | 17 | * Charles Kerr <charles.kerr@canonical.com> | ||
521 | 18 | * Ted Gould <ted.gould@canonical.com> | ||
522 | 19 | */ | 19 | */ |
523 | 20 | 20 | ||
525 | 21 | #include "timedated-fixture.h" | 21 | #include "glib-fixture.h" |
526 | 22 | 22 | ||
527 | 23 | #include <datetime/dbus-shared.h> | ||
528 | 23 | #include <datetime/timezone-timedated.h> | 24 | #include <datetime/timezone-timedated.h> |
529 | 24 | 25 | ||
531 | 25 | using unity::indicator::datetime::TimedatedTimezone; | 26 | #include <gio/gio.h> |
532 | 27 | |||
533 | 28 | |||
534 | 29 | using namespace unity::indicator::datetime; | ||
535 | 30 | |||
536 | 31 | |||
537 | 32 | struct Timedate1Fixture: public GlibFixture | ||
538 | 33 | { | ||
539 | 34 | private: | ||
540 | 35 | |||
541 | 36 | typedef GlibFixture super; | ||
542 | 37 | |||
543 | 38 | protected: | ||
544 | 39 | |||
545 | 40 | GDBusConnection* m_bus {}; | ||
546 | 41 | GTestDBus* m_test_bus {}; | ||
547 | 42 | |||
548 | 43 | void SetUp() override | ||
549 | 44 | { | ||
550 | 45 | super::SetUp(); | ||
551 | 46 | |||
552 | 47 | // use a fake bus | ||
553 | 48 | m_test_bus = g_test_dbus_new(G_TEST_DBUS_NONE); | ||
554 | 49 | g_test_dbus_up(m_test_bus); | ||
555 | 50 | const char * address = g_test_dbus_get_bus_address(m_test_bus); | ||
556 | 51 | g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); | ||
557 | 52 | g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); | ||
558 | 53 | g_debug("test_dbus's address is %s", address); | ||
559 | 54 | |||
560 | 55 | // get the bus | ||
561 | 56 | m_bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); | ||
562 | 57 | g_dbus_connection_set_exit_on_close(m_bus, FALSE); | ||
563 | 58 | } | ||
564 | 59 | |||
565 | 60 | void TearDown() override | ||
566 | 61 | { | ||
567 | 62 | // tear down the bus | ||
568 | 63 | bool bus_finished = false; | ||
569 | 64 | g_object_weak_ref( | ||
570 | 65 | G_OBJECT(m_bus), | ||
571 | 66 | [](gpointer gbus_finished, GObject*){*static_cast<bool*>(gbus_finished) = true;}, | ||
572 | 67 | &bus_finished | ||
573 | 68 | ); | ||
574 | 69 | g_clear_object(&m_bus); | ||
575 | 70 | EXPECT_TRUE(wait_for([&bus_finished](){return bus_finished;})); | ||
576 | 71 | |||
577 | 72 | // tear down test bus | ||
578 | 73 | g_clear_object(&m_test_bus); | ||
579 | 74 | |||
580 | 75 | super::TearDown(); | ||
581 | 76 | } | ||
582 | 77 | |||
583 | 78 | void start_timedate1(const std::string& tzid) | ||
584 | 79 | { | ||
585 | 80 | // start dbusmock with the timedated template | ||
586 | 81 | auto json_parameters = g_strdup_printf("{\"Timezone\": \"%s\"}", tzid.c_str()); | ||
587 | 82 | const gchar* child_argv[] = { "python3", "-m", "dbusmock", "--template", "timedated", "--parameters", json_parameters, nullptr }; | ||
588 | 83 | GError* error = nullptr; | ||
589 | 84 | g_spawn_async(nullptr, (gchar**)child_argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error); | ||
590 | 85 | g_assert_no_error(error); | ||
591 | 86 | g_free(json_parameters); | ||
592 | 87 | |||
593 | 88 | // wait for it to appear on the bus | ||
594 | 89 | wait_for_name_owned(m_bus, Bus::Timedate1::BUSNAME); | ||
595 | 90 | } | ||
596 | 91 | |||
597 | 92 | bool wait_for_tzid(const std::string& tzid, Timezone& tz) | ||
598 | 93 | { | ||
599 | 94 | return wait_for([&tzid, &tz](){return tzid == tz.timezone.get();}); | ||
600 | 95 | } | ||
601 | 96 | |||
602 | 97 | void set_timedate1_timezone(const std::string& tzid) | ||
603 | 98 | { | ||
604 | 99 | GError* error {}; | ||
605 | 100 | auto v = g_dbus_connection_call_sync( | ||
606 | 101 | m_bus, | ||
607 | 102 | Bus::Timedate1::BUSNAME, | ||
608 | 103 | Bus::Timedate1::ADDR, | ||
609 | 104 | Bus::Timedate1::IFACE, | ||
610 | 105 | Bus::Timedate1::Methods::SET_TIMEZONE, | ||
611 | 106 | g_variant_new("(sb)", tzid.c_str(), FALSE), | ||
612 | 107 | nullptr, | ||
613 | 108 | G_DBUS_CALL_FLAGS_NONE, | ||
614 | 109 | -1, | ||
615 | 110 | nullptr, | ||
616 | 111 | &error); | ||
617 | 112 | g_clear_pointer(&v, g_variant_unref); | ||
618 | 113 | g_assert_no_error(error); | ||
619 | 114 | } | ||
620 | 115 | }; | ||
621 | 116 | |||
622 | 117 | #define EXPECT_TZID(expected_tzid, tmp) \ | ||
623 | 118 | EXPECT_TRUE(wait_for_tzid(expected_tzid, tmp)) \ | ||
624 | 119 | << "expected " << expected_tzid \ | ||
625 | 120 | << " got " << tmp.timezone.get(); | ||
626 | 26 | 121 | ||
627 | 27 | /*** | 122 | /*** |
628 | 28 | **** | 123 | **** |
629 | 29 | ***/ | 124 | ***/ |
630 | 30 | 125 | ||
742 | 31 | #define TIMEZONE_FILE (SANDBOX"/timezone") | 126 | TEST_F(Timedate1Fixture, HelloWorld) |
743 | 32 | 127 | { | |
744 | 33 | class TimezoneFixture: public TimedateFixture | 128 | } |
745 | 34 | { | 129 | |
746 | 35 | private: | 130 | /** |
747 | 36 | 131 | * Test that the tzid is right if timedated isn't available | |
748 | 37 | typedef TimedateFixture super; | 132 | */ |
749 | 38 | 133 | TEST_F(Timedate1Fixture, DefaultTimezone) | |
750 | 39 | protected: | 134 | { |
751 | 40 | 135 | const std::string expected_tzid{"Etc/Utc"}; | |
752 | 41 | void SetUp() override | 136 | |
753 | 42 | { | 137 | TimedatedTimezone tmp {m_bus}; |
754 | 43 | super::SetUp(); | 138 | EXPECT_TZID(expected_tzid, tmp); |
755 | 44 | } | 139 | } |
756 | 45 | 140 | ||
757 | 46 | void TearDown() override | 141 | /** |
758 | 47 | { | 142 | * Test that the tzid is right if timedated shows BEFORE we start |
759 | 48 | super::TearDown(); | 143 | */ |
760 | 49 | } | 144 | TEST_F(Timedate1Fixture, Timedate1First) |
761 | 50 | 145 | { | |
762 | 51 | public: | 146 | const std::string expected_tzid{"America/Chicago"}; |
763 | 52 | 147 | ||
764 | 53 | /* convenience func to set the timezone file */ | 148 | start_timedate1(expected_tzid); |
765 | 54 | void set_file(const std::string& text) | 149 | TimedatedTimezone tmp {m_bus}; |
766 | 55 | { | 150 | EXPECT_TZID(expected_tzid, tmp); |
767 | 56 | g_debug("set_file %s %s", TIMEZONE_FILE, text.c_str()); | 151 | } |
768 | 57 | auto fp = fopen(TIMEZONE_FILE, "w+"); | 152 | |
769 | 58 | fprintf(fp, "%s\n", text.c_str()); | 153 | /** |
770 | 59 | fclose(fp); | 154 | * Test that the tzid is right if timedated shows AFTER we start |
771 | 60 | sync(); | 155 | */ |
772 | 61 | } | 156 | TEST_F(Timedate1Fixture, Timedate1Last) |
773 | 62 | }; | 157 | { |
774 | 63 | 158 | const std::string expected_tzid("America/Los_Angeles"); | |
775 | 64 | /** | 159 | |
776 | 65 | * Test that timezone-timedated warns, but doesn't crash, if the timezone file doesn't exist | 160 | TimedatedTimezone tmp {m_bus}; |
777 | 66 | */ | 161 | start_timedate1(expected_tzid); |
778 | 67 | TEST_F(TimezoneFixture, NoFile) | 162 | EXPECT_TZID(expected_tzid, tmp); |
779 | 68 | { | 163 | } |
780 | 69 | remove(TIMEZONE_FILE); | 164 | |
781 | 70 | ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); | 165 | /** |
782 | 71 | 166 | * Test that the tzid is right if timedated's property changes | |
783 | 72 | expectLogMessage(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*No such file or directory*"); | 167 | */ |
784 | 73 | TimedatedTimezone tz(TIMEZONE_FILE); | 168 | TEST_F(Timedate1Fixture, TimezoneChange) |
785 | 74 | } | 169 | { |
786 | 75 | 170 | const std::vector<std::string> expected_tzids{"America/Los_Angeles", "America/Chicago", "Etc/Utc"}; | |
787 | 76 | /** | 171 | |
788 | 77 | * Test that timezone-timedated gives a default of UTC if the file doesn't exist | 172 | TimedatedTimezone tmp {m_bus}; |
789 | 78 | */ | 173 | start_timedate1("America/New_York"); |
790 | 79 | TEST_F(TimezoneFixture, DefaultValueNoFile) | 174 | |
791 | 80 | { | 175 | for(const auto& expected_tzid : expected_tzids) |
792 | 81 | const std::string expected_timezone = "Etc/Utc"; | 176 | { |
793 | 82 | remove(TIMEZONE_FILE); | 177 | set_timedate1_timezone(expected_tzid); |
794 | 83 | ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); | 178 | EXPECT_TZID(expected_tzid, tmp); |
795 | 84 | 179 | } | |
685 | 85 | expectLogMessage(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*No such file or directory*"); | ||
686 | 86 | TimedatedTimezone tz(TIMEZONE_FILE); | ||
687 | 87 | ASSERT_EQ(expected_timezone, tz.timezone.get()); | ||
688 | 88 | } | ||
689 | 89 | |||
690 | 90 | /** | ||
691 | 91 | * Test that timezone-timedated picks up the initial value | ||
692 | 92 | */ | ||
693 | 93 | TEST_F(TimezoneFixture, InitialValue) | ||
694 | 94 | { | ||
695 | 95 | const std::string expected_timezone = "America/Chicago"; | ||
696 | 96 | set_file(expected_timezone); | ||
697 | 97 | TimedatedTimezone tz(TIMEZONE_FILE); | ||
698 | 98 | } | ||
699 | 99 | |||
700 | 100 | /** | ||
701 | 101 | * Test that changing the tz after we are running works. | ||
702 | 102 | */ | ||
703 | 103 | TEST_F(TimezoneFixture, ChangedValue) | ||
704 | 104 | { | ||
705 | 105 | const std::string initial_timezone = "America/Chicago"; | ||
706 | 106 | const std::string changed_timezone = "America/New_York"; | ||
707 | 107 | |||
708 | 108 | set_file(initial_timezone); | ||
709 | 109 | |||
710 | 110 | TimedatedTimezone tz(TIMEZONE_FILE); | ||
711 | 111 | ASSERT_EQ(initial_timezone, tz.timezone.get()); | ||
712 | 112 | |||
713 | 113 | bool changed = false; | ||
714 | 114 | tz.timezone.changed().connect( | ||
715 | 115 | [&changed, this](const std::string& s){ | ||
716 | 116 | g_message("timezone changed to %s", s.c_str()); | ||
717 | 117 | changed = true; | ||
718 | 118 | g_main_loop_quit(loop); | ||
719 | 119 | }); | ||
720 | 120 | |||
721 | 121 | g_idle_add([](gpointer gself){ | ||
722 | 122 | static_cast<TimedateFixture*>(gself)->set_timezone("America/New_York"); | ||
723 | 123 | return G_SOURCE_REMOVE; | ||
724 | 124 | }, this); | ||
725 | 125 | |||
726 | 126 | g_main_loop_run(loop); | ||
727 | 127 | |||
728 | 128 | ASSERT_TRUE(changed); | ||
729 | 129 | ASSERT_EQ(changed_timezone, tz.timezone.get()); | ||
730 | 130 | } | ||
731 | 131 | |||
732 | 132 | /** | ||
733 | 133 | * Test that timezone-timedated picks up the initial value | ||
734 | 134 | */ | ||
735 | 135 | TEST_F(TimezoneFixture, IgnoreComments) | ||
736 | 136 | { | ||
737 | 137 | const std::string comment = "# Created by cloud-init v. 0.7.5 on Thu, 24 Apr 2014 14:03:29 +0000"; | ||
738 | 138 | const std::string expected_timezone = "Europe/Berlin"; | ||
739 | 139 | set_file(comment + "\n" + expected_timezone); | ||
740 | 140 | TimedatedTimezone tz(TIMEZONE_FILE); | ||
741 | 141 | ASSERT_EQ(expected_timezone, tz.timezone.get()); | ||
796 | 142 | } | 180 | } |
PASSED: Continuous integration, rev:443 jenkins. qa.ubuntu. com/job/ indicator- datetime- ci/370/ jenkins. qa.ubuntu. com/job/ indicator- datetime- wily-amd64- ci/60 jenkins. qa.ubuntu. com/job/ indicator- datetime- wily-armhf- ci/60 jenkins. qa.ubuntu. com/job/ indicator- datetime- wily-armhf- ci/60/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- datetime- ci/370/ rebuild
http://