Merge lp:~charlesk/indicator-datetime/always-get-initial-tzid-from-timedate1 into lp:indicator-datetime/15.10

Proposed by Charles Kerr
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
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.timedate1:

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-datetime's startup and timedate1's startup. (This is the possible fix)

2. Use G_BUS_NAME_WATCHER_FLAGS_AUTO_START to ensure timedate1 shows up on the bus for us

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

To post a comment you must log in.
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()

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

code looks good, some small inline code comments.

review: Approve
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) :
review: Needs Information
Revision history for this message
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_name_on_connection() for name watching?

review: Needs Information
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

Revision history for this message
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. :)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Antti Kaijanmäki (kaijanmaki) wrote :

lgtm.

review: Approve
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'include/datetime/dbus-shared.h'
--- include/datetime/dbus-shared.h 2014-09-17 16:51:51 +0000
+++ include/datetime/dbus-shared.h 2016-04-12 17:11:17 +0000
@@ -28,5 +28,47 @@
28#define BUS_POWERD_PATH "/com/canonical/powerd"28#define BUS_POWERD_PATH "/com/canonical/powerd"
29#define BUS_POWERD_INTERFACE "com.canonical.powerd"29#define BUS_POWERD_INTERFACE "com.canonical.powerd"
3030
31namespace unity {
32namespace indicator {
33namespace datetime {
34
35namespace Bus
36{
37 namespace Timedate1
38 {
39 static constexpr char const * BUSNAME {"org.freedesktop.timedate1"};
40 static constexpr char const * ADDR {"/org/freedesktop/timedate1"};
41 static constexpr char const * IFACE {"org.freedesktop.timedate1"};
42
43 namespace Properties
44 {
45 static constexpr char const * TIMEZONE {"Timezone"};
46 }
47
48 namespace Methods
49 {
50 static constexpr char const * SET_TIMEZONE {"SetTimezone"};
51 }
52 }
53
54 namespace Properties
55 {
56 static constexpr char const * IFACE {"org.freedesktop.DBus.Properties"};
57
58 namespace Methods
59 {
60 static constexpr char const * GET {"Get"};
61 }
62
63 namespace Signals
64 {
65 static constexpr char const * PROPERTIES_CHANGED {"PropertiesChanged"};
66 }
67 }
68}
69
70} // namespace datetime
71} // namespace indicator
72} // namespace unity
3173
32#endif /* INDICATOR_DATETIME_DBUS_SHARED_H */74#endif /* INDICATOR_DATETIME_DBUS_SHARED_H */
3375
=== modified file 'include/datetime/timezone-timedated.h'
--- include/datetime/timezone-timedated.h 2015-09-03 09:54:20 +0000
+++ include/datetime/timezone-timedated.h 2016-04-12 17:11:17 +0000
@@ -20,10 +20,10 @@
20#ifndef INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H20#ifndef INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H
21#define INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H21#define INDICATOR_DATETIME_TIMEDATED_TIMEZONE_H
2222
23#define DEFAULT_FILENAME "/etc/timezone"
24
25#include <datetime/timezone.h> // base class23#include <datetime/timezone.h> // base class
2624
25#include <gio/gio.h> // GDBusConnection*
26
27#include <string> // std::string27#include <string> // std::string
2828
29namespace unity {29namespace unity {
@@ -31,12 +31,12 @@
31namespace datetime {31namespace datetime {
3232
33/**33/**
34 * \brief A #Timezone that gets its information from monitoring a file, such as /etc/timezone34 * \brief A #Timezone that gets its information from org.freedesktop.timedate1
35 */35 */
36class TimedatedTimezone: public Timezone36class TimedatedTimezone: public Timezone
37{37{
38public:38public:
39 TimedatedTimezone(std::string filename = DEFAULT_FILENAME);39 TimedatedTimezone(GDBusConnection* connection);
40 ~TimedatedTimezone();40 ~TimedatedTimezone();
4141
42private:42private:
4343
=== modified file 'include/datetime/timezones-live.h'
--- include/datetime/timezones-live.h 2015-09-01 09:52:13 +0000
+++ include/datetime/timezones-live.h 2016-04-12 17:11:17 +0000
@@ -23,7 +23,6 @@
23#include <datetime/settings.h>23#include <datetime/settings.h>
24#include <datetime/timezones.h>24#include <datetime/timezones.h>
25#include <datetime/timezone-geoclue.h>25#include <datetime/timezone-geoclue.h>
26#include <datetime/timezone-timedated.h>
2726
28#include <memory> // shared_ptr<>27#include <memory> // shared_ptr<>
2928
@@ -38,13 +37,13 @@
38class LiveTimezones: public Timezones37class LiveTimezones: public Timezones
39{38{
40public:39public:
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);
4241
43private:42private:
44 void update_geolocation();43 void update_geolocation();
45 void update_timezones();44 void update_timezones();
4645
47 TimedatedTimezone m_file;46 std::shared_ptr<Timezone> m_primary_timezone;
48 std::shared_ptr<const Settings> m_settings;47 std::shared_ptr<const Settings> m_settings;
49 std::shared_ptr<GeoclueTimezone> m_geo;48 std::shared_ptr<GeoclueTimezone> m_geo;
50};49};
5150
=== modified file 'src/main.cpp'
--- src/main.cpp 2015-10-13 16:06:35 +0000
+++ src/main.cpp 2016-04-12 17:11:17 +0000
@@ -68,7 +68,7 @@
68 {68 {
69 // create the live objects69 // create the live objects
70 auto live_settings = std::make_shared<LiveSettings>();70 auto live_settings = std::make_shared<LiveSettings>();
71 auto live_timezones = std::make_shared<LiveTimezones>(live_settings);71 auto live_timezones = std::make_shared<LiveTimezones>(live_settings, timezone_);
72 auto live_clock = std::make_shared<LiveClock>(timezone_);72 auto live_clock = std::make_shared<LiveClock>(timezone_);
7373
74 // create a full-month planner currently pointing to the current month74 // create a full-month planner currently pointing to the current month
@@ -127,8 +127,17 @@
127 bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR);127 bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR);
128 textdomain(GETTEXT_PACKAGE);128 textdomain(GETTEXT_PACKAGE);
129129
130 // get the system bus
131 GError* error {};
132 auto system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
133 if (error != nullptr) {
134 g_critical("Unable to get system bus: %s", error->message);
135 g_clear_error(&error);
136 return 0;
137 }
138
130 auto engine = create_engine();139 auto engine = create_engine();
131 auto timezone_ = std::make_shared<TimedatedTimezone>();140 auto timezone_ = std::make_shared<TimedatedTimezone>(system_bus);
132 auto state = create_state(engine, timezone_);141 auto state = create_state(engine, timezone_);
133 auto actions = std::make_shared<LiveActions>(state);142 auto actions = std::make_shared<LiveActions>(state);
134 MenuFactory factory(actions, state);143 MenuFactory factory(actions, state);
@@ -165,5 +174,6 @@
165 g_main_loop_run(loop);174 g_main_loop_run(loop);
166175
167 g_main_loop_unref(loop);176 g_main_loop_unref(loop);
177 g_clear_object(&system_bus);
168 return 0;178 return 0;
169}179}
170180
=== modified file 'src/timezone-timedated.cpp'
--- src/timezone-timedated.cpp 2015-09-03 16:00:36 +0000
+++ src/timezone-timedated.cpp 2016-04-12 17:11:17 +0000
@@ -17,6 +17,7 @@
17 * Charles Kerr <charles.kerr@canonical.com>17 * Charles Kerr <charles.kerr@canonical.com>
18 */18 */
1919
20#include <datetime/dbus-shared.h>
20#include <datetime/timezone-timedated.h>21#include <datetime/timezone-timedated.h>
2122
22#include <gio/gio.h>23#include <gio/gio.h>
@@ -36,166 +37,169 @@
36{37{
37public:38public:
3839
39 Impl(TimedatedTimezone& owner, std::string filename):40 Impl(TimedatedTimezone& owner, GDBusConnection* connection):
40 m_owner(owner),41 m_owner{owner},
41 m_filename(filename)42 m_connection{G_DBUS_CONNECTION(g_object_ref(G_OBJECT(connection)))},
43 m_cancellable{g_cancellable_new()}
42 {44 {
43 g_debug("Filename is '%s'", filename.c_str());45 // set the fallback value
44 monitor_timezone_property();46 m_owner.timezone.set("Etc/Utc");
47
48 // watch for timedate1 on the bus
49 m_watcher_id = g_bus_watch_name_on_connection(
50 m_connection,
51 Bus::Timedate1::BUSNAME,
52 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
53 on_timedate1_appeared,
54 on_timedate1_vanished,
55 this,
56 nullptr);
57
58 // listen for changed properties
59 m_signal_subscription_id = g_dbus_connection_signal_subscribe(
60 m_connection,
61 Bus::Timedate1::IFACE,
62 Bus::Properties::IFACE,
63 Bus::Properties::Signals::PROPERTIES_CHANGED,
64 Bus::Timedate1::ADDR,
65 nullptr,
66 G_DBUS_SIGNAL_FLAGS_NONE,
67 on_properties_changed,
68 this,
69 nullptr);
45 }70 }
4671
47 ~Impl()72 ~Impl()
48 {73 {
49 clear();74 g_cancellable_cancel(m_cancellable);
75 g_clear_object(&m_cancellable);
76
77 g_bus_unwatch_name(m_watcher_id);
78
79 g_dbus_connection_signal_unsubscribe(m_connection, m_signal_subscription_id);
80
81 g_clear_object(&m_connection);
50 }82 }
5183
52private:84private:
5385
54 void clear()86 static void on_timedate1_appeared(GDBusConnection * /*connection*/,
55 {87 const gchar * name,
56 if (m_connection && m_signal_subscription_id)88 const gchar * /*name_owner*/,
57 {89 gpointer gself)
58 g_dbus_connection_signal_unsubscribe (m_connection, m_signal_subscription_id);90 {
59 m_signal_subscription_id = 0;91 g_debug("%s appeared on bus", name);
60 }92
6193 static_cast<Impl*>(gself)->ask_for_timezone();
62 g_clear_object(&m_connection);94 }
63 }95
6496 static void on_timedate1_vanished(GDBusConnection * /*connection*/,
65 static void on_properties_changed (GDBusConnection *connection G_GNUC_UNUSED,97 const gchar * name,
66 const gchar *sender_name G_GNUC_UNUSED,98 gpointer /*gself*/)
67 const gchar *object_path G_GNUC_UNUSED,99 {
68 const gchar *interface_name G_GNUC_UNUSED,100 g_debug("%s not present on bus", name);
69 const gchar *signal_name G_GNUC_UNUSED,101 }
70 GVariant *parameters,102
71 gpointer gself)103 static void on_properties_changed(GDBusConnection * /*connection*/,
104 const gchar * /*sender_name*/,
105 const gchar * /*object_path*/,
106 const gchar * /*interface_name*/,
107 const gchar * /*signal_name*/,
108 GVariant * parameters,
109 gpointer gself)
72 {110 {
73 auto self = static_cast<Impl*>(gself);111 auto self = static_cast<Impl*>(gself);
74 const char *tz;112
75 GVariant *changed_properties;113 GVariant* changed_properties {};
76 gchar **invalidated_properties;114 gchar** invalidated_properties {};
77115 g_variant_get(parameters, "(s@a{sv}^as)", NULL, &changed_properties, &invalidated_properties);
78 g_variant_get (parameters, "(s@a{sv}^as)", NULL, &changed_properties, &invalidated_properties);116
79117 const char* tz {};
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))
81 self->notify_timezone(tz);119 {
82 else if (g_strv_contains (invalidated_properties, "Timezone"))120 if (tz != nullptr)
83 self->notify_timezone(self->get_timezone_from_file(self->m_filename));121 self->set_timezone(tz);
84122 else
85 g_variant_unref (changed_properties);123 g_warning("%s no timezone found", G_STRLOC);
86 g_strfreev (invalidated_properties);124 }
87 }125 else if (g_strv_contains(invalidated_properties, Bus::Timedate1::Properties::TIMEZONE))
88126 {
89 void monitor_timezone_property()127 self->ask_for_timezone();
90 {128 }
91 GError *err = nullptr;129
92130 g_variant_unref(changed_properties);
93 /*131 g_strfreev(invalidated_properties);
94 * There is an unlikely race which happens if there is an activation132 }
95 * and timezone change before our match rule is added.133
96 */134 void ask_for_timezone()
97 notify_timezone(get_timezone_from_file(m_filename));135 {
98136 g_dbus_connection_call(
99 /*137 m_connection,
100 * Make sure the bus is around at least until we add the match rules,138 Bus::Timedate1::BUSNAME,
101 * otherwise things (tests) are sad.139 Bus::Timedate1::ADDR,
102 */140 Bus::Properties::IFACE,
103 m_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM,141 Bus::Properties::Methods::GET,
104 nullptr,142 g_variant_new("(ss)", Bus::Timedate1::IFACE, Bus::Timedate1::Properties::TIMEZONE),
105 &err);143 G_VARIANT_TYPE("(v)"),
106144 G_DBUS_CALL_FLAGS_NONE,
107 if (err)145 -1,
108 {146 m_cancellable,
109 g_warning("Couldn't get bus connection: '%s'", err->message);147 on_get_timezone_ready,
110 g_error_free(err);148 this);
111 return;149 }
112 }150
113151 static void on_get_timezone_ready(GObject * connection,
114 m_signal_subscription_id = g_dbus_connection_signal_subscribe(m_connection,152 GAsyncResult * res,
115 "org.freedesktop.timedate1",153 gpointer gself)
116 "org.freedesktop.DBus.Properties",154 {
117 "PropertiesChanged",155 GError* error {};
118 "/org/freedesktop/timedate1",156 GVariant* v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(connection), res, &error);
119 NULL, G_DBUS_SIGNAL_FLAGS_NONE,
120 on_properties_changed,
121 this, nullptr);
122 }
123
124 void notify_timezone(std::string new_timezone)
125 {
126 g_debug("notify_timezone '%s'", new_timezone.c_str());
127 if (!new_timezone.empty())
128 m_owner.timezone.set(new_timezone);
129 }
130
131 std::string get_timezone_from_file(const std::string& filename)
132 {
133 GError * error;
134 GIOChannel * io_channel;
135 std::string ret;
136
137 // read through filename line-by-line until we fine a nonempty non-comment line
138 error = nullptr;
139 io_channel = g_io_channel_new_file(filename.c_str(), "r", &error);
140 if (error == nullptr)
141 {
142 auto line = g_string_new(nullptr);
143
144 while(ret.empty())
145 {
146 const auto io_status = g_io_channel_read_line_string(io_channel, line, nullptr, &error);
147 if ((io_status == G_IO_STATUS_EOF) || (io_status == G_IO_STATUS_ERROR))
148 break;
149 if (error != nullptr)
150 break;
151
152 g_strstrip(line->str);
153
154 if (!line->len) // skip empty lines
155 continue;
156
157 if (*line->str=='#') // skip comments
158 continue;
159
160 ret = line->str;
161 }
162
163 g_string_free(line, true);
164 } else
165 /* Default to UTC */
166 ret = "Etc/Utc";
167
168 if (io_channel != nullptr)
169 {
170 g_io_channel_shutdown(io_channel, false, nullptr);
171 g_io_channel_unref(io_channel);
172 }
173
174 if (error != nullptr)157 if (error != nullptr)
175 {158 {
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))
177 g_error_free(error);160 g_warning("%s Couldn't get timezone: %s", G_STRLOC, error->message);
178 }161 }
179162 else if (v != nullptr)
180 return ret;163 {
164 GVariant* tzv {};
165 g_variant_get(v, "(v)", &tzv);
166 const char* tz = g_variant_get_string(tzv, nullptr);
167
168 if (tz != nullptr)
169 static_cast<Impl*>(gself)->set_timezone(tz);
170 else
171 g_warning("%s no timezone found", G_STRLOC);
172
173 g_clear_pointer(&tzv, g_variant_unref);
174 g_clear_pointer(&v, g_variant_unref);
175 }
176 }
177
178 void set_timezone(const std::string& tz)
179 {
180 g_return_if_fail(!tz.empty());
181
182 g_debug("set timezone: '%s'", tz.c_str());
183 m_owner.timezone.set(tz);
181 }184 }
182185
183 /***186 /***
184 ****187 ****
185 ***/188 ***/
186189
187 TimedatedTimezone & m_owner;190 TimedatedTimezone& m_owner;
188 GDBusConnection *m_connection = nullptr;191 GDBusConnection* m_connection {};
189 unsigned long m_signal_subscription_id = 0;192 GCancellable* m_cancellable {};
190 std::string m_filename;193 unsigned long m_signal_subscription_id {};
194 unsigned int m_watcher_id {};
191};195};
192196
193/***197/***
194****198****
195***/199***/
196200
197TimedatedTimezone::TimedatedTimezone(std::string filename):201TimedatedTimezone::TimedatedTimezone(GDBusConnection* connection):
198 impl(new Impl{*this, filename})202 impl{new Impl{*this, connection}}
199{203{
200}204}
201205
202206
=== modified file 'src/timezones-live.cpp'
--- src/timezones-live.cpp 2015-08-31 21:08:36 +0000
+++ src/timezones-live.cpp 2016-04-12 17:11:17 +0000
@@ -25,11 +25,14 @@
25namespace indicator {25namespace indicator {
26namespace datetime {26namespace datetime {
2727
28LiveTimezones::LiveTimezones(const std::shared_ptr<const Settings>& settings):28LiveTimezones::LiveTimezones(
29 m_file(),29 const std::shared_ptr<const Settings>& settings,
30 const std::shared_ptr<Timezone>& primary_timezone
31):
32 m_primary_timezone(primary_timezone),
30 m_settings(settings)33 m_settings(settings)
31{34{
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();});
3336
34 m_settings->show_detected_location.changed().connect([this](bool){update_geolocation();});37 m_settings->show_detected_location.changed().connect([this](bool){update_geolocation();});
35 update_geolocation();38 update_geolocation();
@@ -53,7 +56,7 @@
5356
54void LiveTimezones::update_timezones()57void LiveTimezones::update_timezones()
55{58{
56 const auto a = m_file.timezone.get();59 const auto a = m_primary_timezone->timezone.get();
57 const auto b = m_geo ? m_geo->timezone.get() : "";60 const auto b = m_geo ? m_geo->timezone.get() : "";
5861
59 timezone.set(a.empty() ? b : a);62 timezone.set(a.empty() ? b : a);
6063
=== modified file 'tests/test-timezone-timedated.cpp'
--- tests/test-timezone-timedated.cpp 2015-09-09 16:16:41 +0000
+++ tests/test-timezone-timedated.cpp 2016-04-12 17:11:17 +0000
@@ -1,9 +1,5 @@
1
2/*1/*
3 * Copyright 2013 Canonical Ltd.2 * Copyright © 2014-2016 Canonical Ltd.
4 *
5 * Authors:
6 * Charles Kerr <charles.kerr@canonical.com>
7 *3 *
8 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 3, as published5 * under the terms of the GNU General Public License version 3, as published
@@ -16,127 +12,169 @@
16 *12 *
17 * You should have received a copy of the GNU General Public License along13 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Kerr <charles.kerr@canonical.com>
18 * Ted Gould <ted.gould@canonical.com>
19 */19 */
2020
21#include "timedated-fixture.h"21#include "glib-fixture.h"
2222
23#include <datetime/dbus-shared.h>
23#include <datetime/timezone-timedated.h>24#include <datetime/timezone-timedated.h>
2425
25using unity::indicator::datetime::TimedatedTimezone;26#include <gio/gio.h>
27
28
29using namespace unity::indicator::datetime;
30
31
32struct Timedate1Fixture: public GlibFixture
33{
34private:
35
36 typedef GlibFixture super;
37
38protected:
39
40 GDBusConnection* m_bus {};
41 GTestDBus* m_test_bus {};
42
43 void SetUp() override
44 {
45 super::SetUp();
46
47 // use a fake bus
48 m_test_bus = g_test_dbus_new(G_TEST_DBUS_NONE);
49 g_test_dbus_up(m_test_bus);
50 const char * address = g_test_dbus_get_bus_address(m_test_bus);
51 g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true);
52 g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true);
53 g_debug("test_dbus's address is %s", address);
54
55 // get the bus
56 m_bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
57 g_dbus_connection_set_exit_on_close(m_bus, FALSE);
58 }
59
60 void TearDown() override
61 {
62 // tear down the bus
63 bool bus_finished = false;
64 g_object_weak_ref(
65 G_OBJECT(m_bus),
66 [](gpointer gbus_finished, GObject*){*static_cast<bool*>(gbus_finished) = true;},
67 &bus_finished
68 );
69 g_clear_object(&m_bus);
70 EXPECT_TRUE(wait_for([&bus_finished](){return bus_finished;}));
71
72 // tear down test bus
73 g_clear_object(&m_test_bus);
74
75 super::TearDown();
76 }
77
78 void start_timedate1(const std::string& tzid)
79 {
80 // start dbusmock with the timedated template
81 auto json_parameters = g_strdup_printf("{\"Timezone\": \"%s\"}", tzid.c_str());
82 const gchar* child_argv[] = { "python3", "-m", "dbusmock", "--template", "timedated", "--parameters", json_parameters, nullptr };
83 GError* error = nullptr;
84 g_spawn_async(nullptr, (gchar**)child_argv, nullptr, G_SPAWN_SEARCH_PATH, nullptr, nullptr, nullptr, &error);
85 g_assert_no_error(error);
86 g_free(json_parameters);
87
88 // wait for it to appear on the bus
89 wait_for_name_owned(m_bus, Bus::Timedate1::BUSNAME);
90 }
91
92 bool wait_for_tzid(const std::string& tzid, Timezone& tz)
93 {
94 return wait_for([&tzid, &tz](){return tzid == tz.timezone.get();});
95 }
96
97 void set_timedate1_timezone(const std::string& tzid)
98 {
99 GError* error {};
100 auto v = g_dbus_connection_call_sync(
101 m_bus,
102 Bus::Timedate1::BUSNAME,
103 Bus::Timedate1::ADDR,
104 Bus::Timedate1::IFACE,
105 Bus::Timedate1::Methods::SET_TIMEZONE,
106 g_variant_new("(sb)", tzid.c_str(), FALSE),
107 nullptr,
108 G_DBUS_CALL_FLAGS_NONE,
109 -1,
110 nullptr,
111 &error);
112 g_clear_pointer(&v, g_variant_unref);
113 g_assert_no_error(error);
114 }
115};
116
117#define EXPECT_TZID(expected_tzid, tmp) \
118 EXPECT_TRUE(wait_for_tzid(expected_tzid, tmp)) \
119 << "expected " << expected_tzid \
120 << " got " << tmp.timezone.get();
26121
27/***122/***
28****123****
29***/124***/
30125
31#define TIMEZONE_FILE (SANDBOX"/timezone")126TEST_F(Timedate1Fixture, HelloWorld)
32127{
33class TimezoneFixture: public TimedateFixture128}
34{129
35 private:130/**
36131 * Test that the tzid is right if timedated isn't available
37 typedef TimedateFixture super;132 */
38133TEST_F(Timedate1Fixture, DefaultTimezone)
39 protected:134{
40135 const std::string expected_tzid{"Etc/Utc"};
41 void SetUp() override136
42 {137 TimedatedTimezone tmp {m_bus};
43 super::SetUp();138 EXPECT_TZID(expected_tzid, tmp);
44 }139}
45140
46 void TearDown() override141/**
47 {142 * Test that the tzid is right if timedated shows BEFORE we start
48 super::TearDown();143 */
49 }144TEST_F(Timedate1Fixture, Timedate1First)
50145{
51 public:146 const std::string expected_tzid{"America/Chicago"};
52147
53 /* convenience func to set the timezone file */148 start_timedate1(expected_tzid);
54 void set_file(const std::string& text)149 TimedatedTimezone tmp {m_bus};
55 {150 EXPECT_TZID(expected_tzid, tmp);
56 g_debug("set_file %s %s", TIMEZONE_FILE, text.c_str());151}
57 auto fp = fopen(TIMEZONE_FILE, "w+");152
58 fprintf(fp, "%s\n", text.c_str());153/**
59 fclose(fp);154 * Test that the tzid is right if timedated shows AFTER we start
60 sync();155 */
61 }156TEST_F(Timedate1Fixture, Timedate1Last)
62};157{
63158 const std::string expected_tzid("America/Los_Angeles");
64/**159
65 * Test that timezone-timedated warns, but doesn't crash, if the timezone file doesn't exist160 TimedatedTimezone tmp {m_bus};
66 */161 start_timedate1(expected_tzid);
67TEST_F(TimezoneFixture, NoFile)162 EXPECT_TZID(expected_tzid, tmp);
68{163}
69 remove(TIMEZONE_FILE);164
70 ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS));165/**
71166 * Test that the tzid is right if timedated's property changes
72 expectLogMessage(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*No such file or directory*");167 */
73 TimedatedTimezone tz(TIMEZONE_FILE);168TEST_F(Timedate1Fixture, TimezoneChange)
74}169{
75170 const std::vector<std::string> expected_tzids{"America/Los_Angeles", "America/Chicago", "Etc/Utc"};
76/**171
77 * Test that timezone-timedated gives a default of UTC if the file doesn't exist172 TimedatedTimezone tmp {m_bus};
78 */173 start_timedate1("America/New_York");
79TEST_F(TimezoneFixture, DefaultValueNoFile)174
80{175 for(const auto& expected_tzid : expected_tzids)
81 const std::string expected_timezone = "Etc/Utc";176 {
82 remove(TIMEZONE_FILE);177 set_timedate1_timezone(expected_tzid);
83 ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS));178 EXPECT_TZID(expected_tzid, tmp);
84179 }
85 expectLogMessage(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*No such file or directory*");
86 TimedatedTimezone tz(TIMEZONE_FILE);
87 ASSERT_EQ(expected_timezone, tz.timezone.get());
88}
89
90/**
91 * Test that timezone-timedated picks up the initial value
92 */
93TEST_F(TimezoneFixture, InitialValue)
94{
95 const std::string expected_timezone = "America/Chicago";
96 set_file(expected_timezone);
97 TimedatedTimezone tz(TIMEZONE_FILE);
98}
99
100/**
101 * Test that changing the tz after we are running works.
102 */
103TEST_F(TimezoneFixture, ChangedValue)
104{
105 const std::string initial_timezone = "America/Chicago";
106 const std::string changed_timezone = "America/New_York";
107
108 set_file(initial_timezone);
109
110 TimedatedTimezone tz(TIMEZONE_FILE);
111 ASSERT_EQ(initial_timezone, tz.timezone.get());
112
113 bool changed = false;
114 tz.timezone.changed().connect(
115 [&changed, this](const std::string& s){
116 g_message("timezone changed to %s", s.c_str());
117 changed = true;
118 g_main_loop_quit(loop);
119 });
120
121 g_idle_add([](gpointer gself){
122 static_cast<TimedateFixture*>(gself)->set_timezone("America/New_York");
123 return G_SOURCE_REMOVE;
124 }, this);
125
126 g_main_loop_run(loop);
127
128 ASSERT_TRUE(changed);
129 ASSERT_EQ(changed_timezone, tz.timezone.get());
130}
131
132/**
133 * Test that timezone-timedated picks up the initial value
134 */
135TEST_F(TimezoneFixture, IgnoreComments)
136{
137 const std::string comment = "# Created by cloud-init v. 0.7.5 on Thu, 24 Apr 2014 14:03:29 +0000";
138 const std::string expected_timezone = "Europe/Berlin";
139 set_file(comment + "\n" + expected_timezone);
140 TimedatedTimezone tz(TIMEZONE_FILE);
141 ASSERT_EQ(expected_timezone, tz.timezone.get());
142}180}

Subscribers

People subscribed via source and target branches