From 060bc69d8bdfe9860a71109ecf92810beb99f4da Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 21:59:43 -0600 Subject: add clock + tests --- tests/test-clock.cc | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 tests/test-clock.cc (limited to 'tests') diff --git a/tests/test-clock.cc b/tests/test-clock.cc new file mode 100644 index 0000000..a0b4360 --- /dev/null +++ b/tests/test-clock.cc @@ -0,0 +1,244 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include "glib-fixture.h" + +/*** +**** +***/ + +using unity::indicator::datetime::Clock; +using unity::indicator::datetime::LiveClock; +using unity::indicator::datetime::Timezones; + +class ClockFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + self->system_bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->system_bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + GTestDBus * test_dbus; + GDBusConnection * system_bus; + + virtual void SetUp () + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + } + + virtual void TearDown () + { + // close the system bus + g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); + g_main_loop_run (loop); + g_clear_object (&system_bus); + + // tear down the test dbus + g_test_dbus_down (test_dbus); + g_clear_object (&test_dbus); + + super::TearDown (); + } + + public: + + void emitPrepareForSleep () + { + g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr), + NULL, + "/org/freedesktop/login1", // object path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + g_variant_new("(b)", FALSE), + NULL); + } +}; + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +TEST_F (ClockFixture, HelloFixture) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + +#if 0 + GTimeZone * tz_nyc = g_time_zone_new (file_timezone.c_str()); + GDateTime * now_nyc = g_date_time_new_now (tz_nyc); + GDateTime * now = clock.localtime(); + EXPECT_EQ (g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); + EXPECT_LE (abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); + g_date_time_unref (now); + g_date_time_unref (now_nyc); + g_time_zone_unref (tz_nyc); + + /// change the timezones! + clock.skewDetected.connect([this](){ + g_main_loop_quit(loop); + }); + file_timezone = "America/Los_Angeles"; + g_idle_add ([](gpointer str){ + set_file(static_cast(str)); + return G_SOURCE_REMOVE; + }, const_cast(file_timezone.c_str())); + g_main_loop_run (loop); + + GTimeZone * tz_la = g_time_zone_new (file_timezone.c_str()); + GDateTime * now_la = g_date_time_new_now (tz_la); + now = clock.localtime(); + EXPECT_EQ (g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); + EXPECT_LE (abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); + g_date_time_unref (now); + g_date_time_unref (now_la); + g_time_zone_unref (tz_la); +#endif +} + + +TEST_F (ClockFixture, TimezoneChangeTriggersSkew) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + //std::string file_timezone = "America/New_York"; + //set_file (file_timezone); + //std::shared_ptr detector (new TimezoneDetector(TIMEZONE_FILE)); + //LiveClock clock (detector); + + GTimeZone * tz_nyc = g_time_zone_new ("America/New_York"); + GDateTime * now_nyc = g_date_time_new_now (tz_nyc); + GDateTime * now = clock.localtime(); + EXPECT_EQ (g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); + EXPECT_LE (abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); + g_date_time_unref (now); + g_date_time_unref (now_nyc); + g_time_zone_unref (tz_nyc); + + /// change the timezones! + clock.skewDetected.connect([this](){ + g_main_loop_quit(loop); + }); + g_idle_add ([](gpointer gs){ + static_cast(gs)->timezone.set("America/Los_Angeles"); + return G_SOURCE_REMOVE; + }, zones.get()); + g_main_loop_run (loop); + + GTimeZone * tz_la = g_time_zone_new ("America/Los_Angeles"); + GDateTime * now_la = g_date_time_new_now (tz_la); + now = clock.localtime(); + EXPECT_EQ (g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); + EXPECT_LE (abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); + g_date_time_unref (now); + g_date_time_unref (now_la); + g_time_zone_unref (tz_la); +} + +/** + * Confirm that a "PrepareForSleep" event wil trigger a skew event + */ +TEST_F (ClockFixture, SleepTriggersSkew) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed, this](){ + skewed = true; + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; + }); + + g_idle_add ([](gpointer gself){ + static_cast(gself)->emitPrepareForSleep(); + return G_SOURCE_REMOVE; + }, this); + + wait_msec (1000); + EXPECT_TRUE(skewed); +} + +/** + * Confirm that normal time passing doesn't trigger a skew event. + * that idling changing the clock's time triggers a skew event + */ +TEST_F (ClockFixture, IdleDoesNotTriggerSkew) +{ + std::shared_ptr zones (new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock (zones); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 4; + clock.skewTestIntervalSec.set(intervalSec); + wait_msec (intervalSec * 2.5 * 1000); + EXPECT_FALSE (skewed); +} -- cgit v1.2.3 From 36d8e8ee2d9f2b7967cef6c74c6f779263727eb9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:00:32 -0600 Subject: add timezone-file + tests --- include/datetime/timezone-file.h | 60 ++++++++++++++++++ include/datetime/timezone.h | 46 ++++++++++++++ src/timezone-file.cpp | 89 ++++++++++++++++++++++++++ tests/test-timezone-file.cc | 133 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+) create mode 100644 include/datetime/timezone-file.h create mode 100644 include/datetime/timezone.h create mode 100644 src/timezone-file.cpp create mode 100644 tests/test-timezone-file.cc (limited to 'tests') diff --git a/include/datetime/timezone-file.h b/include/datetime/timezone-file.h new file mode 100644 index 0000000..39a3d83 --- /dev/null +++ b/include/datetime/timezone-file.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_FILE_TIMEZONE_H +#define INDICATOR_DATETIME_FILE_TIMEZONE_H + +#include // base class + +#include // std::string + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A #Timezone that gets its information from monitoring a file, such as /etc/timezone + */ +class FileTimezone: public Timezone +{ +public: + FileTimezone() {} + FileTimezone(const std::string& filename) { setFilename(filename); } + ~FileTimezone() {clear();} + +private: + void setFilename(const std::string& filename); + void clear(); + static void onFileChanged(gpointer gself); + void reload(); + + std::string filename_; + GFileMonitor * monitor_ = nullptr; + unsigned long monitor_handler_id_ = 0; +}; + + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_FILE_TIMEZONE_H diff --git a/include/datetime/timezone.h b/include/datetime/timezone.h new file mode 100644 index 0000000..218e219 --- /dev/null +++ b/include/datetime/timezone.h @@ -0,0 +1,46 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_TIMEZONE_H +#define INDICATOR_DATETIME_TIMEZONE_H + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** \brief Base class for objects that use various means to detect the system's timezone */ +class Timezone +{ +protected: + Timezone() {} + +public: + //virtual ~Timezone() {} + core::Property timezone; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_TIMEZONE_H diff --git a/src/timezone-file.cpp b/src/timezone-file.cpp new file mode 100644 index 0000000..f1d0dca --- /dev/null +++ b/src/timezone-file.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +void +FileTimezone::clear() +{ + if (monitor_handler_id_) + g_signal_handler_disconnect(monitor_, monitor_handler_id_); + + g_clear_object (&monitor_); + + filename_.clear(); +} + +void +FileTimezone::setFilename(const std::string& filename) +{ + clear(); + + filename_ = filename; + + auto file = g_file_new_for_path(filename.c_str()); + GError * err = nullptr; + monitor_ = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); + g_object_unref(file); + if (err) + { + g_warning("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message); + g_error_free(err); + } + else + { + monitor_handler_id_ = g_signal_connect_swapped(monitor_, "changed", G_CALLBACK(onFileChanged), this); + g_debug("%s Monitoring timezone file '%s'", G_STRLOC, filename.c_str()); + } + + reload(); +} + +void +FileTimezone::onFileChanged(gpointer gself) +{ + static_cast(gself)->reload(); +} + +void +FileTimezone::reload() +{ + GError * err = nullptr; + gchar * str = nullptr; + + if (!g_file_get_contents(filename_.c_str(), &str, nullptr, &err)) + { + g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, filename_.c_str(), err->message); + g_error_free(err); + } + else + { + g_strstrip(str); + timezone.set(str); + g_free(str); + } +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-timezone-file.cc b/tests/test-timezone-file.cc new file mode 100644 index 0000000..510d12c --- /dev/null +++ b/tests/test-timezone-file.cc @@ -0,0 +1,133 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include // fopen() +//#include // chmod() +#include // sync() + +using unity::indicator::datetime::FileTimezone; + + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +class TimezoneFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + virtual void SetUp () + { + super::SetUp (); + } + + virtual void TearDown () + { + super::TearDown (); + } + + public: + + /* convenience func to set the timezone file */ + void set_file (const std::string& text) + { + FILE * fp = fopen (TIMEZONE_FILE, "w+"); + fprintf (fp, "%s\n", text.c_str()); + fclose (fp); + sync (); + } +}; + + +/** + * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist + */ +TEST_F (TimezoneFixture, NoFile) +{ + remove (TIMEZONE_FILE); + ASSERT_FALSE (g_file_test (TIMEZONE_FILE, G_FILE_TEST_EXISTS)); + + FileTimezone tz (TIMEZONE_FILE); + testLogCount (G_LOG_LEVEL_WARNING, 1); +} + + +/** + * Test that timezone-file picks up the initial value + */ +TEST_F (TimezoneFixture, InitialValue) +{ + const std::string expected_timezone = "America/Chicago"; + set_file (expected_timezone); + FileTimezone tz (TIMEZONE_FILE); + ASSERT_EQ (expected_timezone, tz.timezone.get()); +} + + +/** + * Test that clearing the timezone results in an empty string + */ +TEST_F (TimezoneFixture, ChangedValue) +{ + const std::string initial_timezone = "America/Chicago"; + const std::string changed_timezone = "America/New_York"; + set_file (initial_timezone); + + FileTimezone tz (TIMEZONE_FILE); + ASSERT_EQ (initial_timezone, tz.timezone.get()); + + bool changed = false; + auto connection = tz.timezone.changed().connect( + [&changed, this](const std::string& s){ + g_message ("timezone changed to %s", s.c_str()); + changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gself){ + static_cast(gself)->set_file ("America/New_York"); + // static_cast(gtz)->timezone.set ("America/New_York"); + return G_SOURCE_REMOVE; + }, this);//&tz); + + g_main_loop_run (loop); + + ASSERT_TRUE (changed); + ASSERT_EQ (changed_timezone, tz.timezone.get()); +} -- cgit v1.2.3 From a1cb4d7802e6e024c842f2a4fa41b91b67162698 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:00:51 -0600 Subject: add timezone-geoclue + tests --- include/datetime/timezone-geoclue.h | 65 ++++++++++ src/timezone-geoclue.cpp | 248 ++++++++++++++++++++++++++++++++++++ tests/test-timezone-geoclue.cc | 97 ++++++++++++++ 3 files changed, 410 insertions(+) create mode 100644 include/datetime/timezone-geoclue.h create mode 100644 src/timezone-geoclue.cpp create mode 100644 tests/test-timezone-geoclue.cc (limited to 'tests') diff --git a/include/datetime/timezone-geoclue.h b/include/datetime/timezone-geoclue.h new file mode 100644 index 0000000..382b3cc --- /dev/null +++ b/include/datetime/timezone-geoclue.h @@ -0,0 +1,65 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_GEOCLUE_TIMEZONE_H +#define INDICATOR_DATETIME_GEOCLUE_TIMEZONE_H + +#include // base class + +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A #Timezone that gets its information from asking GeoClue + */ +class GeoclueTimezone: public Timezone +{ +public: + GeoclueTimezone(); + ~GeoclueTimezone(); + +private: + static void on_bus_got (GObject*, GAsyncResult*, gpointer); + static void on_client_created (GObject*, GAsyncResult*, gpointer); + static void on_address_changed (GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + static void on_requirements_set (GObject*, GAsyncResult*, gpointer); + static void on_address_started (GObject*, GAsyncResult*, gpointer); + static void on_address_got (GObject*, GAsyncResult*, gpointer); + void setTimezoneFromAddressVariant (GVariant*); + static GVariant * call_finish (GObject*, GAsyncResult*); + + GCancellable * cancellable_ = nullptr; + GDBusConnection * connection_ = nullptr; + std::string client_object_path_; + guint signal_subscription_ = 0; +}; + + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_GEOCLUE_TIMEZONE_H + diff --git a/src/timezone-geoclue.cpp b/src/timezone-geoclue.cpp new file mode 100644 index 0000000..f6d1f5e --- /dev/null +++ b/src/timezone-geoclue.cpp @@ -0,0 +1,248 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#define GEOCLUE_BUS_NAME "org.freedesktop.Geoclue.Master" + +namespace unity { +namespace indicator { +namespace datetime { + + +GeoclueTimezone::GeoclueTimezone(): + cancellable_(g_cancellable_new()) +{ + g_bus_get(G_BUS_TYPE_SESSION, cancellable_, on_bus_got, this); +} + +GeoclueTimezone::~GeoclueTimezone() +{ + g_cancellable_cancel(cancellable_); + g_object_unref(cancellable_); + + if (signal_subscription_) + g_dbus_connection_signal_unsubscribe(connection_, signal_subscription_); + + g_object_unref(connection_); +} + +/*** +**** +***/ + +void +GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * error; + GDBusConnection * connection; + + error = nullptr; + connection = g_bus_get_finish(res, &error); + if (error) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Couldn't get bus: %s", error->message); + + g_error_free(error); + } + else + { + auto self = static_cast(gself); + + self->connection_ = connection; + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + "/org/freedesktop/Geoclue/Master", + "org.freedesktop.Geoclue.Master", + "Create", + nullptr, // parameters + G_VARIANT_TYPE("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable_, + on_client_created, + self); + } +} + +void +GeoclueTimezone::on_client_created(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + auto self = static_cast(gself); + + GVariant * child = g_variant_get_child_value(result, 0); + self->client_object_path_ = g_variant_get_string(child, nullptr); + g_variant_unref(child); + g_variant_unref(result); + + self->signal_subscription_ = g_dbus_connection_signal_subscribe( + self->connection_, + GEOCLUE_BUS_NAME, + "org.freedesktop.Geoclue.Address", // inteface + "AddressChanged", // signal name + self->client_object_path_.c_str(), // object path + nullptr, // arg0 + G_DBUS_SIGNAL_FLAGS_NONE, + on_address_changed, + self, + nullptr); + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + self->client_object_path_.c_str(), + "org.freedesktop.Geoclue.MasterClient", + "SetRequirements", + g_variant_new("(iibi)", 2, 0, FALSE, 1023), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable_, + on_requirements_set, + self); + } +} + +void +GeoclueTimezone::on_address_changed(GDBusConnection * connection G_GNUC_UNUSED, + const gchar * sender_name G_GNUC_UNUSED, + const gchar * object_path G_GNUC_UNUSED, + const gchar * interface_name G_GNUC_UNUSED, + const gchar * signal_name G_GNUC_UNUSED, + GVariant * parameters, + gpointer gself) +{ + static_cast(gself)->setTimezoneFromAddressVariant(parameters); +} + +void +GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + auto self = static_cast(gself); + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + self->client_object_path_.c_str(), + "org.freedesktop.Geoclue.MasterClient", + "AddressStart", + nullptr, + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable_, + on_address_started, + self); + + g_variant_unref(result); + } +} + +void +GeoclueTimezone::on_address_started(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + auto self = static_cast(gself); + + g_dbus_connection_call(self->connection_, + GEOCLUE_BUS_NAME, + self->client_object_path_.c_str(), + "org.freedesktop.Geoclue.Address", + "GetAddress", + nullptr, + G_VARIANT_TYPE("(ia{ss}(idd))"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable_, + on_address_got, + self); + + g_variant_unref(result); + } +} + +void +GeoclueTimezone::on_address_got(GObject * source, GAsyncResult * res, gpointer gself) +{ + GVariant * result; + + if ((result = call_finish(source, res))) + { + static_cast(gself)->setTimezoneFromAddressVariant(result); + g_variant_unref(result); + } +} + +void +GeoclueTimezone::setTimezoneFromAddressVariant(GVariant * variant) +{ + g_return_if_fail(g_variant_is_of_type(variant, G_VARIANT_TYPE("(ia{ss}(idd))"))); + + const gchar * timezone_string = nullptr; + GVariant * dict = g_variant_get_child_value(variant, 1); + if (dict) + { + if (g_variant_lookup(dict, "timezone", "&s", &timezone_string)) + timezone.set(timezone_string); + + g_variant_unref(dict); + } +} + +GVariant* +GeoclueTimezone::call_finish(GObject * source, GAsyncResult * res) +{ + GError * error; + GVariant * result; + + error = nullptr; + result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); + + if (error) + { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("AddressStart() failed: %s", error->message); + + g_error_free(error); + + g_clear_pointer(&result, g_variant_unref); + } + + return result; +} + +/**** +***** +****/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + diff --git a/tests/test-timezone-geoclue.cc b/tests/test-timezone-geoclue.cc new file mode 100644 index 0000000..a577fbd --- /dev/null +++ b/tests/test-timezone-geoclue.cc @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "geoclue-fixture.h" + +#include + +//#include + +using unity::indicator::datetime::GeoclueTimezone; + +/*** +**** +***/ + +class TimezoneGeoclueFixture : public GeoclueFixture +{ +}; + +#if 0 +namespace +{ + struct EmitAddressChangedData + { + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + std::string timezone; + EmitAddressChangedData(DbusTestDbusMock * mock_, + DbusTestDbusMockObject * obj_client_, + const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + }; + + gboolean emit_address_changed_idle (gpointer gdata) + { + auto data = static_cast(gdata); + + GError * error = nullptr; + dbus_test_dbus_mock_object_emit_signal (data->mock, data->obj_client, + "org.freedesktop.Geoclue.Address", + "AddressChanged", + G_VARIANT_TYPE("(ia{ss}(idd))"), + g_variant_new_parsed ("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), + &error); + if (error) + { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + delete data; + return G_SOURCE_REMOVE; + } +} +#endif + +TEST_F (TimezoneGeoclueFixture, ChangeDetected) +{ +// const std::string timezone_1 = "America/Denver"; + const std::string timezone_2 = "America/Chicago"; + + GeoclueTimezone tz; + wait_msec (500); // wait for the bus to get set up + EXPECT_EQ (timezone_1, tz.timezone.get()); + + // start listening for a timezone change, then change the timezone + + bool changed = false; + auto connection = tz.timezone.changed().connect( + [&changed, this](const std::string& s){ + g_debug ("timezone changed to %s", s.c_str()); + changed = true; + g_main_loop_quit (loop); + }); + + setGeoclueTimezoneOnIdle (timezone_2); + //g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); + g_main_loop_run (loop); + EXPECT_EQ (timezone_2, tz.timezone.get()); +} + + -- cgit v1.2.3 From 172246c997d7b20d7e98fc25a7b23f4a79a0f6a6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:02:06 -0600 Subject: add formatter + tests --- include/datetime/formatter.h | 118 +++++++++++++++++++ src/formatter-desktop.cpp | 210 +++++++++++++++++++++++++++++++++ tests/test-formatter.cc | 270 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 598 insertions(+) create mode 100644 include/datetime/formatter.h create mode 100644 src/formatter-desktop.cpp create mode 100644 tests/test-formatter.cc (limited to 'tests') diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h new file mode 100644 index 0000000..66dc212 --- /dev/null +++ b/include/datetime/formatter.h @@ -0,0 +1,118 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_FORMATTER_H +#define INDICATOR_DATETIME_FORMATTER_H + +#include +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class Clock; +class DateTime; + +/*** +**** +***/ + +/** + * \brief Provides the right time format strings based on the profile and user's settings + */ +class Formatter +{ +public: + + /** \brief The time format string for the menu header */ + core::Property headerFormat; + + /** \brief The time string for the menu header. (eg, the headerFormat + the clock's time */ + core::Property header; + + /** \brief Signal to denote when the relativeFormat has changed. + When this is emitted, clients will want to rebuild their + menuitems that contain relative time strings */ + core::Signal<> relativeFormatChanged; + + /** \brief Generate a relative time format for some time (or time range) + from the current clock's value. For example, a full-day interval + starting at the end of the current clock's day yields "Tomorrow" */ + std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; + +protected: + + Formatter(const std::shared_ptr&); + virtual ~Formatter(); + + /** \brief Returns true if the current locale prefers 12h display instead of 24h */ + static bool is_locale_12h(); + + static const char * getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); + + /** \brief Translate the string based on LC_TIME instead of LC_MESSAGES. + The intent of this is to let users set LC_TIME to override + their other locale settings when generating time format string */ + static const char * T_(const char * fmt); + +private: + + Formatter(const Formatter&) =delete; + Formatter& operator=(const Formatter&) =delete; + + class Impl; + std::unique_ptr p; +}; + + +/** + * \brief A Formatter for the Desktop and DesktopGreeter profiles. + */ +class DesktopFormatter: public Formatter +{ +public: + DesktopFormatter(const std::shared_ptr&); + ~DesktopFormatter(); + +private: + class Impl; + friend Impl; + std::unique_ptr p; +}; + + +/** + * \brief A Formatter for Phone and PhoneGreeter profiles. + */ +class PhoneFormatter: public Formatter +{ +public: + PhoneFormatter(const std::shared_ptr& clock): Formatter(clock) { + headerFormat.set(getDefaultHeaderTimeFormat(is_locale_12h(), false)); + } +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_CLOCK_H diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp new file mode 100644 index 0000000..8390967 --- /dev/null +++ b/src/formatter-desktop.cpp @@ -0,0 +1,210 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +class DesktopFormatter::Impl +{ +public: + + Impl (DesktopFormatter * owner, const std::shared_ptr& clock): + owner_(owner), + clock_(clock), + settings_(g_settings_new(SETTINGS_INTERFACE)) + { + const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, + "changed::" SETTINGS_SHOW_DAY_S, + "changed::" SETTINGS_SHOW_DATE_S, + "changed::" SETTINGS_SHOW_YEAR_S }; + for (guint i=0, n=G_N_ELEMENTS(keys); i(gself)->rebuildHeaderFormat(); + } + + void rebuildHeaderFormat() + { + gchar * fmt = getHeaderLabelFormatString (settings_); + owner_->headerFormat.set(fmt); + g_free (fmt); + } + +private: + + gchar* getHeaderLabelFormatString (GSettings * s) const + { + char * fmt; + const TimeFormatMode mode = (TimeFormatMode) g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S); + + if (mode == TIME_FORMAT_MODE_CUSTOM) + { + fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S); + } + else + { + const bool show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S); + const bool show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S); + const bool show_year = show_date && g_settings_get_boolean (s, SETTINGS_SHOW_YEAR_S); + const char * date_fmt = getDateFormat (show_day, show_date, show_year); + const char * time_fmt = getFullTimeFormatString (s); + fmt = joinDateAndTimeFormatStrings (date_fmt, time_fmt); + } + + return fmt; + } + + const gchar* T_(const gchar* in) const + { + return owner_->T_(in); + } + + const gchar* getDateFormat (bool show_day, bool show_date, bool show_year) const + { + const char * fmt; + + if (show_day && show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ + fmt = T_("%a %b %e %Y"); + else if (show_day && show_date) + fmt = T_("%a %b %e"); + else if (show_day && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ + fmt = T_("%a %Y"); + else if (show_day) + /* TRANSLATORS: a strftime(3) format showing the weekday. */ + fmt = T_("%a"); + else if (show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the date and year */ + fmt = T_("%b %e %Y"); + else if (show_date) + /* TRANSLATORS: a strftime(3) format showing the date */ + fmt = T_("%b %e"); + else if (show_year) + /* TRANSLATORS: a strftime(3) format showing the year */ + fmt = T_("%Y"); + else + fmt = nullptr; + + return fmt; + } + + const gchar * getFullTimeFormatString (GSettings * settings) const + { + const bool show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S); + + bool twelvehour; + switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S)) + { + case TIME_FORMAT_MODE_LOCALE_DEFAULT: + twelvehour = is_locale_12h(); + break; + + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = FALSE; + break; + + default: + twelvehour = TRUE; + break; + } + + return owner_->getDefaultHeaderTimeFormat (twelvehour, show_seconds); + } + + gchar* joinDateAndTimeFormatStrings (const char * date_string, const char * time_string) const + { + gchar * str; + + if (date_string && time_string) + { + /* TRANSLATORS: This is a format string passed to strftime to combine the + * date and the time. The value of "%s\u2003%s" will result in a + * string like this in US English 12-hour time: 'Fri Jul 16 11:50 AM'. + * The space in between date and time is a Unicode en space + * (E28082 in UTF-8 hex). */ + str = g_strdup_printf ("%s\u2003%s", date_string, time_string); + } + else if (date_string) + { + str = g_strdup (date_string); + } + else // time_string + { + str = g_strdup (time_string); + } + + return str; + } + +private: + + DesktopFormatter * const owner_; + std::shared_ptr clock_; + GSettings * settings_; +}; + +/*** +**** +***/ + +DesktopFormatter::DesktopFormatter (const std::shared_ptr& clock): + Formatter(clock), + p(new Impl(this, clock)) +{ +} + +DesktopFormatter::~DesktopFormatter() = default; + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc new file mode 100644 index 0000000..641338b --- /dev/null +++ b/tests/test-formatter.cc @@ -0,0 +1,270 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include +#include + +#include + +#include +#include + +using unity::indicator::datetime::Clock; +using unity::indicator::datetime::MockClock; +using unity::indicator::datetime::PhoneFormatter; +using unity::indicator::datetime::DesktopFormatter; + +/*** +**** +***/ + +class FormatterFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + gchar * original_locale = nullptr; + + protected: + + GSettings * settings = nullptr; + + virtual void SetUp () + { + super::SetUp (); + + settings = g_settings_new (SETTINGS_INTERFACE); + + original_locale = g_strdup (setlocale (LC_TIME, NULL)); + } + + virtual void TearDown () + { + g_clear_object (&settings); + + setlocale (LC_TIME, original_locale); + g_clear_pointer (&original_locale, g_free); + + super::TearDown (); + } + + bool SetLocale (const char * expected_locale, const char * name) + { + setlocale (LC_TIME, expected_locale); + const char * actual_locale = setlocale (LC_TIME, NULL); + if (!g_strcmp0 (expected_locale, actual_locale)) + { + return true; + } + else + { + g_warning ("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); + return false; + } + } + + inline bool Set24hLocale () { return SetLocale ("C", "24h"); } + inline bool Set12hLocale () { return SetLocale ("en_US.utf8", "12h"); } +}; + + +/** + * Test the phone header format + */ +TEST_F (FormatterFixture, TestPhoneHeader) +{ + GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + std::shared_ptr mock (new MockClock(now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + + // test the default value in a 24h locale + if (Set24hLocale ()) + { + PhoneFormatter formatter (clock); + EXPECT_EQ (std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ (std::string("18:30"), formatter.header.get()); + } + + // test the default value in a 12h locale + if (Set12hLocale ()) + { + PhoneFormatter formatter (clock); + EXPECT_EQ (std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ (std::string(" 6:30 PM"), formatter.header.get()); + } +} + +#define EM_SPACE "\u2003" + +/** + * Test the default values of the desktop header format + */ +TEST_F (FormatterFixture, TestDesktopHeader) +{ + struct { + bool is_12h; + bool show_day; + bool show_date; + bool show_year; + const char * expected_format_string; + } test_cases[] = { + { false, false, false, false, "%H:%M" }, + { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false + { false, false, true, false, "%b %e" EM_SPACE "%H:%M" }, + { false, false, true, true, "%b %e %Y" EM_SPACE "%H:%M" }, + { false, true, false, false, "%a" EM_SPACE "%H:%M" }, + { false, true, false, true, "%a" EM_SPACE "%H:%M" }, // show_year is ignored iff show_date is false + { false, true, true, false, "%a %b %e" EM_SPACE "%H:%M" }, + { false, true, true, true, "%a %b %e %Y" EM_SPACE "%H:%M" }, + { true, false, false, false, "%l:%M %p" }, + { true, false, false, true, "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, false, true, false, "%b %e" EM_SPACE "%l:%M %p" }, + { true, false, true, true, "%b %e %Y" EM_SPACE "%l:%M %p" }, + { true, true, false, false, "%a" EM_SPACE "%l:%M %p" }, + { true, true, false, true, "%a" EM_SPACE "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, true, true, false, "%a %b %e" EM_SPACE "%l:%M %p" }, + { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } + }; + + GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + std::shared_ptr mock (new MockClock(now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + + for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(test_cases[i].now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + DesktopFormatter f (clock); + + std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); + ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); + + g_clear_pointer (&test_cases[i].now, g_date_time_unref); + g_clear_pointer (&test_cases[i].then, g_date_time_unref); + g_clear_pointer (&test_cases[i].then_end, g_date_time_unref); + } + } + + g_date_time_unref (a); +} + + +/** + * Test the default values of the desktop header format + */ +TEST_F (FormatterFixture, TestEventTimes) +{ + GDateTime * day = g_date_time_new_local (2013, 1, 1, 13, 0, 0); + GDateTime * day_begin = g_date_time_new_local (2013, 1, 1, 13, 0, 0); + GDateTime * day_end = g_date_time_add_days (day_begin, 1); + GDateTime * tomorrow_begin = g_date_time_add_days (day_begin, 1); + GDateTime * tomorrow_end = g_date_time_add_days (tomorrow_begin, 1); + + struct { + bool is_12h; + GDateTime * now; + GDateTime * then; + GDateTime * then_end; + const char * expected_format_string; + } test_cases[] = { + { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { false, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") }, + { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } + }; + + for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(test_cases[i].now)); + std::shared_ptr clock = std::dynamic_pointer_cast(mock); + DesktopFormatter f (clock); + + std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); + ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); + + g_clear_pointer (&test_cases[i].now, g_date_time_unref); + g_clear_pointer (&test_cases[i].then, g_date_time_unref); + g_clear_pointer (&test_cases[i].then_end, g_date_time_unref); + } + } + + g_date_time_unref (tomorrow_end); + g_date_time_unref (tomorrow_begin); + g_date_time_unref (day_end); + g_date_time_unref (day_begin); + g_date_time_unref (day); +} + + -- cgit v1.2.3 From 38b878589ffb08d2272169d2529703e887933be9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:03:12 -0600 Subject: add planner + tests --- include/datetime/planner-eds.h | 49 +++++ include/datetime/planner.h | 71 +++++++ src/planner-eds.cpp | 419 +++++++++++++++++++++++++++++++++++++++++ tests/test-planner-eds.cc | 70 +++++++ tests/test-planner.cc | 68 +++++++ 5 files changed, 677 insertions(+) create mode 100644 include/datetime/planner-eds.h create mode 100644 include/datetime/planner.h create mode 100644 src/planner-eds.cpp create mode 100644 tests/test-planner-eds.cc create mode 100644 tests/test-planner.cc (limited to 'tests') diff --git a/include/datetime/planner-eds.h b/include/datetime/planner-eds.h new file mode 100644 index 0000000..43f222e --- /dev/null +++ b/include/datetime/planner-eds.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_EDS_H +#define INDICATOR_DATETIME_PLANNER_EDS_H + +#include + +#include // unique_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Planner which uses EDS as its backend + */ +class PlannerEds: public Planner +{ +public: + PlannerEds(); + virtual ~PlannerEds(); + +private: + class Impl; + std::unique_ptr impl_; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_EDS_H diff --git a/include/datetime/planner.h b/include/datetime/planner.h new file mode 100644 index 0000000..4d27a2b --- /dev/null +++ b/include/datetime/planner.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_H +#define INDICATOR_DATETIME_PLANNER_H + +#include +#include + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Simple appointment book + */ +class Planner +{ +public: + virtual ~Planner() =default; + + /** + * \brief Timestamp used to determine the appointments in the `upcoming' and `thisMonth' properties. + * Setting this value will cause the planner to re-query its backend and + * update the `upcoming' and `thisMonth' properties. + */ + core::Property time; + + /** + * \brief The next few appointments that follow the time specified in the time property. + */ + core::Property> upcoming; + + /** + * \brief The appointments that occur in the same month as the time property + */ + core::Property> thisMonth; + +protected: + Planner() =default; + +private: + Planner(const Planner&) =delete; + Planner& operator=(const Planner&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_H diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp new file mode 100644 index 0000000..804d98e --- /dev/null +++ b/src/planner-eds.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +#include +#include +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +G_DEFINE_QUARK ("source-client", source_client) + + +class PlannerEds::Impl +{ +public: + + Impl (PlannerEds& owner): + owner_(owner), + cancellable_(g_cancellable_new()) + { + e_source_registry_new (cancellable_, on_source_registry_ready, this); + + owner_.time.changed().connect([this](const DateTime& dt) { + g_message ("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); + rebuildSoon(); + }); + + rebuildSoon(); + } + + ~Impl() + { + g_cancellable_cancel (cancellable_); + g_clear_object (&cancellable_); + + if (rebuild_tag_) + g_source_remove (rebuild_tag_); + + if (source_registry_) + g_signal_handlers_disconnect_by_data (source_registry_, this); + g_clear_object (&source_registry_); + } + +private: + + static void on_source_registry_ready (GObject* /*source*/, GAsyncResult* res, gpointer gself) + { + GError * error = NULL; + auto r = e_source_registry_new_finish (res, &error); + if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message); + + g_error_free (error); + } + else + { + auto self = static_cast(gself); + + g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self); + g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self); + g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self); + g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self); + g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self); + + self->source_registry_ = r; + + GList* sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR); + for (auto l=sources; l!=nullptr; l=l->next) + on_source_added (r, E_SOURCE(l->data), gself); + g_list_free_full (sources, g_object_unref); + } + } + + static void on_source_added(ESourceRegistry* registry, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + + self->sources_.insert(E_SOURCE(g_object_ref(source))); + + if (e_source_get_enabled(source)) + on_source_enabled(registry, source, gself); + } + + static void on_source_enabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + + e_cal_client_connect (source, + E_CAL_CLIENT_SOURCE_TYPE_EVENTS, + self->cancellable_, + on_client_connected, + gself); + } + + static void on_client_connected (GObject* /*source*/, GAsyncResult * res, gpointer gself) + { + GError * error = nullptr; + EClient * client = e_cal_client_connect_finish (res, &error); + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message); + + g_error_free (error); + } + else + { + // we've got a new connected ECalClient, so store it & notify clients + g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)), + source_client_quark(), + client, + g_object_unref); + + g_message ("client connected; calling rebuildSoon()"); + static_cast(gself)->rebuildSoon(); + } + } + + static void on_source_disabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + { + gpointer e_cal_client; + + // if this source has a connected ECalClient, remove it & notify clients + if ((e_cal_client = g_object_steal_qdata (G_OBJECT(source), source_client_quark()))) + { + g_object_unref (e_cal_client); + + g_message ("source disabled; calling rebuildSoon()"); + static_cast(gself)->rebuildSoon(); + } + } + + static void on_source_removed (ESourceRegistry* registry, ESource* source, gpointer gself) + { + auto self = static_cast(gself); + + on_source_disabled (registry, source, gself); + + self->sources_.erase (source); + g_object_unref (source); + } + + static void on_source_changed (ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) + { + g_message ("source changed; calling rebuildSoon()"); + static_cast(gself)->rebuildSoon(); + } + +private: + + typedef std::function&)> appointment_func; + + struct Task + { + Impl* impl_; + appointment_func func_; + std::vector appointments_; + Task (Impl* impl, const appointment_func& func): impl_(impl), func_(func) {} + }; + + struct AppointmentSubtask + { + std::shared_ptr task_; + ECalClient * client_; + std::string color_; + AppointmentSubtask (const std::shared_ptr& task, ECalClient* client, const char* color): + task_(task), client_(client), color_(color) {} + }; + + void rebuildSoon() + { + const static guint ARBITRARY_INTERVAL_SECS = 2; + + if (rebuild_tag_ == 0) + rebuild_tag_ = g_timeout_add_seconds (ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); + } + + static gboolean rebuildNowStatic (gpointer gself) + { + auto self = static_cast(gself); + self->rebuild_tag_ = 0; + self->rebuildNow(); + return G_SOURCE_REMOVE; + } + + void rebuildNow() + { + GDateTime* calendar_date = owner_.time.get().get(); + GDateTime* begin; + GDateTime* end; + int y, m, d; + g_message ("in rebuildNow"); + + // get all the appointments in the calendar month + g_date_time_get_ymd(calendar_date, &y, &m, &d); + begin = g_date_time_new_local(y, m, 1, 0, 0, 0.1); + end = g_date_time_new_local(y, m, g_date_get_days_in_month(GDateMonth(m),GDateYear(y)), 23, 59, 59.9); + if (begin && end) + { + getAppointments(begin, end, [this](const std::vector& appointments) { + g_message ("got %d appointments in this calendar month", (int)appointments.size()); + }); + } + g_clear_pointer(&begin, g_date_time_unref); + g_clear_pointer(&end, g_date_time_unref); + + // get the upcoming appointments + begin = g_date_time_ref(calendar_date); + end = g_date_time_add_months(begin, 1); + if (begin && end) + { + getAppointments(begin, end, [this](const std::vector& appointments) { + g_message ("got %d upcoming appointments", (int)appointments.size()); + }); + } + g_clear_pointer(&begin, g_date_time_unref); + g_clear_pointer(&end, g_date_time_unref); + g_clear_pointer(&calendar_date, g_date_time_unref); + } + + void getAppointments(GDateTime* begin_dt, GDateTime* end_dt, appointment_func func) + { + const auto begin = g_date_time_to_unix(begin_dt); + const auto end = g_date_time_to_unix(end_dt); + g_message ("getting all appointments from [%s ... %s]", g_date_time_format (begin_dt, "%F %T"), + g_date_time_format (end_dt, "%F %T")); + + /** + *** init the default timezone + **/ + + icaltimezone * default_timezone = nullptr; + + const auto tz = g_date_time_get_timezone_abbreviation (owner_.time.get().get()); + g_message ("%s tz is %s", G_STRLOC, tz); + if (tz && *tz) + { + default_timezone = icaltimezone_get_builtin_timezone (tz); + + if (default_timezone == nullptr) // maybe str is a tzid? + default_timezone = icaltimezone_get_builtin_timezone_from_tzid (tz); + + g_debug ("default_timezone is %p", default_timezone); + } + + /** + *** walk through the sources to build the appointment list + **/ + + std::shared_ptr main_task (new Task(this, func), [](Task* task){ + g_message("time to delete task %p", task); + task->func_(task->appointments_); + }); + + for (auto& source : sources_) + { + auto client = E_CAL_CLIENT (g_object_get_qdata (G_OBJECT(source), source_client_quark())); + if (client == nullptr) + continue; + + if (default_timezone != nullptr) + e_cal_client_set_default_timezone (client, default_timezone); + + // start a new subtask to enumerate all the components in this client. + auto extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); + const auto color = e_source_selectable_get_color (E_SOURCE_SELECTABLE(extension)); + g_message ("calling e_cal_client_generate_instances for %p", client); + e_cal_client_generate_instances (client, + begin, + end, + cancellable_, + my_get_appointments_foreach, + new AppointmentSubtask (main_task, client, color), + [](gpointer g){delete static_cast(g);}); + } + } + + struct UrlSubtask + { + std::shared_ptr task_; + Appointment appointment_; + UrlSubtask (const std::shared_ptr& task, const Appointment& appointment): task_(task), appointment_(appointment) {} + }; + + static gboolean + my_get_appointments_foreach (ECalComponent* component, + time_t begin, + time_t end, + gpointer gsubtask) + { + const auto vtype = e_cal_component_get_vtype(component); + auto subtask = static_cast(gsubtask); + + if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) + { + const gchar* uid = NULL; + e_cal_component_get_uid (component, &uid); + + auto status = ICAL_STATUS_NONE; + e_cal_component_get_status (component, &status); + + if ((uid != NULL) && + (status != ICAL_STATUS_COMPLETED) && + (status != ICAL_STATUS_CANCELLED)) + { + Appointment appointment; + + /* Determine whether this is a recurring event. + NB: icalrecurrencetype supports complex recurrence patterns; + however, since design only allows daily recurrence, + that's all we support here. */ + GSList * recur_list; + e_cal_component_get_rrule_list (component, &recur_list); + for (auto l=recur_list; l!=NULL; l=l->next) + { + const auto recur = static_cast(l->data); + appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) + && (recur->interval == 1)); + } + e_cal_component_free_recur_list (recur_list); + + ECalComponentText text; + text.value = ""; + e_cal_component_get_summary (component, &text); + + appointment.begin = g_date_time_new_from_unix_local (begin); + appointment.end = g_date_time_new_from_unix_local (end); + appointment.color = subtask->color_; + appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; + appointment.summary = text.value; + appointment.uid = uid; + + GList * alarm_uids = e_cal_component_get_alarm_uids (component); + appointment.has_alarms = alarm_uids != nullptr; + cal_obj_uid_list_free (alarm_uids); + + e_cal_client_get_attachment_uris (subtask->client_, + uid, + NULL, + subtask->task_->impl_->cancellable_, + on_appointment_uris_ready, + new UrlSubtask(subtask->task_, appointment)); + } + } + + return G_SOURCE_CONTINUE; + } + + static void on_appointment_uris_ready (GObject* client, GAsyncResult* res, gpointer gsubtask) + { + auto subtask = static_cast(gsubtask); + + GSList * uris = nullptr; + GError * error = nullptr; + e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); + if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Error getting appointment uris: %s", error->message); + + g_error_free (error); + } + else if (uris != NULL) + { + subtask->appointment_.url = (const char*) uris->data; // copy the first URL + g_debug ("found url '%s' for appointment '%s'", subtask->appointment_.url.c_str(), subtask->appointment_.summary.c_str()); + e_client_util_free_string_slist (uris); + } + + g_message ("adding appointment '%s' '%s'", subtask->appointment_.summary.c_str(), subtask->appointment_.url.c_str()); + subtask->task_->appointments_.push_back (subtask->appointment_); + delete subtask; + } + +private: + + PlannerEds& owner_; + std::set sources_; + GCancellable * cancellable_ = nullptr; + ESourceRegistry * source_registry_ = nullptr; + guint rebuild_tag_ = 0; +}; + +PlannerEds::PlannerEds(): impl_(new Impl(*this)) {} + +PlannerEds::~PlannerEds() =default; + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-planner-eds.cc b/tests/test-planner-eds.cc new file mode 100644 index 0000000..986a45e --- /dev/null +++ b/tests/test-planner-eds.cc @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include + +#include + +#include +#include + +using unity::indicator::datetime::Appointment; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::PlannerEds; + +/*** +**** +***/ + +class PlannerEdsFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + virtual void SetUp () + { + super::SetUp (); + } + + virtual void TearDown () + { + super::TearDown (); + } +}; + +/*** +**** +***/ + +TEST_F (PlannerEdsFixture, HelloWorld) +{ + DateTime dt; + dt = g_date_time_new_now_local(); + + PlannerEds planner; + planner.time.set (dt); + g_main_loop_run (loop); +} + diff --git a/tests/test-planner.cc b/tests/test-planner.cc new file mode 100644 index 0000000..8c74366 --- /dev/null +++ b/tests/test-planner.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include +#include + +#include + +#include +#include + +using unity::indicator::datetime::Appointment; + +/*** +**** +***/ + +typedef GlibFixture PlannerFixture; + + +TEST_F (PlannerFixture, HelloWorld) +{ + GDateTime * halloween = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + GDateTime * christmas = g_date_time_new_local (2020, 12, 25, 0, 0, 0); + + Appointment a; + a.summary = "Test"; + a.begin = halloween; + a.end = g_date_time_add_hours (halloween, 1); + const Appointment b = a; + a.summary = "Foo"; + + EXPECT_EQ (a.summary, "Foo"); + EXPECT_EQ (b.summary, "Test"); + EXPECT_EQ (0, g_date_time_compare (a.begin(), b.begin())); + EXPECT_EQ (0, g_date_time_compare (a.end(), b.end())); + + Appointment c; + c.begin = christmas; + c.end = g_date_time_add_hours (christmas, 1); + Appointment d; + d = c; + EXPECT_EQ (0, g_date_time_compare (c.begin(), d.begin())); + EXPECT_EQ (0, g_date_time_compare (c.end(), d.end())); + a = d; + EXPECT_EQ (0, g_date_time_compare (d.begin(), a.begin())); + EXPECT_EQ (0, g_date_time_compare (d.end(), a.end())); +} + -- cgit v1.2.3 From db7b874bbf1a98f85ffe511be230164c40fe080d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:03:43 -0600 Subject: add locations + tests --- include/datetime/locations-settings.h | 60 ++++++++++++ include/datetime/locations.h | 71 ++++++++++++++ src/locations-settings.cpp | 105 +++++++++++++++++++++ src/locations.cpp | 41 ++++++++ tests/test-locations.cc | 172 ++++++++++++++++++++++++++++++++++ 5 files changed, 449 insertions(+) create mode 100644 include/datetime/locations-settings.h create mode 100644 include/datetime/locations.h create mode 100644 src/locations-settings.cpp create mode 100644 src/locations.cpp create mode 100644 tests/test-locations.cc (limited to 'tests') diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h new file mode 100644 index 0000000..d343fe3 --- /dev/null +++ b/include/datetime/locations-settings.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_LOCATIONS_H +#define INDICATOR_DATETIME_SETTINGS_LOCATIONS_H + +#include // base class + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class Timezones; + +/** + * \brief An ordered list of Location objects found from + * the system timezone and from the user's GSettings + */ +class SettingsLocations: public Locations +{ +public: + /** + * @param[in] schemaId the settings schema to load + * @param[in] timezones the timezones to always show first in the list + */ + SettingsLocations (const std::string& schemaId, const std::shared_ptr& timezones); + +protected: + std::unique_ptr> settings_; + std::shared_ptr timezones_; + +private: + static void onSettingsChanged (gpointer gself); + void reload(); +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_LOCATIONS_H diff --git a/include/datetime/locations.h b/include/datetime/locations.h new file mode 100644 index 0000000..a06d1cc --- /dev/null +++ b/include/datetime/locations.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_LOCATIONS_H +#define INDICATOR_DATETIME_LOCATIONS_H + +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A physical place and its timezone; eg, "America/Chicago" + "Oklahoma City" + */ +struct Location +{ + /** timezone; eg, "America/Chicago" */ + std::string zone; + + /* human-readable location name; eg, "Oklahoma City" */ + std::string name; + + /** offset from UTC in microseconds */ + int64_t offset = 0; + + bool operator== (const Location& that) const + { + return (name == that.name) && (zone == that.zone) && (offset == that.offset); + } + + Location (const std::string& zone, const std::string& name); +}; + +/** + * A container for an ordered list of Locations. + */ +class Locations +{ +public: + Locations() =default; + virtual ~Locations() =default; + + /** \brief an ordered list of Location items */ + core::Property > locations; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_LOCATIONS_H diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp new file mode 100644 index 0000000..ed8f998 --- /dev/null +++ b/src/locations-settings.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include +#include + +#include // std::find() + +namespace unity { +namespace indicator { +namespace datetime { + +SettingsLocations::SettingsLocations (const std::string& schemaId, + const std::shared_ptr& timezones): + timezones_(timezones) +{ + auto deleter = [&](GSettings* s){g_object_unref(s);}; + settings_ = std::unique_ptr>(g_settings_new(schemaId.c_str()), deleter); + const char * keys[] = { "changed::" SETTINGS_LOCATIONS_S, + "changed::" SETTINGS_SHOW_LOCATIONS_S }; + for (int i=0, n=G_N_ELEMENTS(keys); itimezone.changed().connect([this](const std::string&){reload();}); + timezones->timezones.changed().connect([this](const std::set&){reload();}); + + reload(); +} + +void +SettingsLocations::onSettingsChanged (gpointer gself) +{ + static_cast(gself)->reload(); +} + +void +SettingsLocations::reload() +{ + std::vector v; + + // add the primary timezone first + std::string zone = timezones_->timezone.get(); + if (!zone.empty()) + { + gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); + Location l (zone, name); + v.push_back (l); + g_free (name); + } + + // add the other detected timezones + for (const auto& zone : timezones_->timezones.get()) + { + gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); + Location l (zone, name); + if (std::find (v.begin(), v.end(), l) == v.end()) + v.push_back (l); + g_free (name); + } + + // maybe add the user-specified locations + if (g_settings_get_boolean (settings_.get(), SETTINGS_SHOW_LOCATIONS_S)) + { + gchar ** user_locations = g_settings_get_strv (settings_.get(), SETTINGS_LOCATIONS_S); + + for (int i=0; user_locations[i]; i++) + { + gchar * zone; + gchar * name; + split_settings_location (user_locations[i], &zone, &name); + Location l (zone, name); + if (std::find (v.begin(), v.end(), l) == v.end()) + v.push_back (l); + g_free (name); + g_free (zone); + } + + g_strfreev (user_locations); + } + + locations.set (v); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/locations.cpp b/src/locations.cpp new file mode 100644 index 0000000..5c3c52b --- /dev/null +++ b/src/locations.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +Location::Location(const std::string& zone_, const std::string& name_): + zone(zone_), + name(name_) +{ + GTimeZone * gzone = g_time_zone_new (zone.c_str()); + GDateTime * gtime = g_date_time_new_now (gzone); + offset = g_date_time_get_utc_offset (gtime); + g_date_time_unref (gtime); + g_time_zone_unref (gzone); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/test-locations.cc b/tests/test-locations.cc new file mode 100644 index 0000000..c833bed --- /dev/null +++ b/tests/test-locations.cc @@ -0,0 +1,172 @@ + + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "clock-mock.h" +#include "glib-fixture.h" + +#include +#include +#include + +#include + +#include +#include + +using unity::indicator::datetime::Location; +using unity::indicator::datetime::Locations; +using unity::indicator::datetime::SettingsLocations; +using unity::indicator::datetime::Timezones; + +/*** +**** +***/ + +class LocationsFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + GSettings * settings = nullptr; + std::shared_ptr timezones; + const std::string nyc = "America/New_York"; + const std::string chicago = "America/Chicago"; + + virtual void SetUp () + { + super::SetUp (); + + settings = g_settings_new (SETTINGS_INTERFACE); + const gchar * location_strv[] = { "America/Los_Angeles Oakland", "America/Chicago Chicago", "America/Chicago Oklahoma City", "America/Toronto Toronto", "Europe/London London", "Europe/Berlin Berlin", NULL }; + g_settings_set_strv (settings, SETTINGS_LOCATIONS_S, location_strv); + g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, true); + + timezones.reset (new Timezones); + timezones->timezone.set (chicago); + timezones->timezones.set (std::set({ nyc, chicago })); + } + + virtual void TearDown () + { + //timezones.reset (nullptr); + g_clear_object (&settings); + + super::TearDown (); + } +}; + +TEST_F (LocationsFixture, Timezones) +{ + g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, false); + + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + std::vector l = locations.locations.get(); + EXPECT_EQ (2, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); +} + +TEST_F (LocationsFixture, SettingsLocations) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + std::vector l = locations.locations.get(); + EXPECT_EQ (7, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ ("Oakland", l[2].name); + EXPECT_EQ ("America/Los_Angeles", l[2].zone); + EXPECT_EQ ("Oklahoma City", l[3].name); + EXPECT_EQ ("America/Chicago", l[3].zone); + EXPECT_EQ ("Toronto", l[4].name); + EXPECT_EQ ("America/Toronto", l[4].zone); + EXPECT_EQ ("London", l[5].name); + EXPECT_EQ ("Europe/London", l[5].zone); + EXPECT_EQ ("Berlin", l[6].name); + EXPECT_EQ ("Europe/Berlin", l[6].zone); +} + +TEST_F (LocationsFixture, ChangeLocationStrings) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gsettings){ + const gchar * strv[] = { "America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin", NULL }; + g_settings_set_strv (static_cast(gsettings), SETTINGS_LOCATIONS_S, strv); + return G_SOURCE_REMOVE; + }, settings); + + g_main_loop_run (loop); + + EXPECT_TRUE (locations_changed); + std::vector l = locations.locations.get(); + EXPECT_EQ (5, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ ("Oakland", l[2].name); + EXPECT_EQ ("America/Los_Angeles", l[2].zone); + EXPECT_EQ ("London", l[3].name); + EXPECT_EQ ("Europe/London", l[3].zone); + EXPECT_EQ ("Berlin", l[4].name); + EXPECT_EQ ("Europe/Berlin", l[4].zone); + locations_changed = false; +} + +TEST_F (LocationsFixture, ChangeLocationVisibility) +{ + SettingsLocations locations (SETTINGS_INTERFACE, timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gsettings){ + g_settings_set_boolean (static_cast(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + return G_SOURCE_REMOVE; + }, settings); + + g_main_loop_run (loop); + + EXPECT_TRUE (locations_changed); + std::vector l = locations.locations.get(); + EXPECT_EQ (2, l.size()); + EXPECT_EQ ("Chicago", l[0].name); + EXPECT_EQ (chicago, l[0].zone); + EXPECT_EQ ("New York", l[1].name); + EXPECT_EQ (nyc, l[1].zone); +} -- cgit v1.2.3 From 3b8833efe6ab21387b6f73b4a4ef757445801623 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 17 Dec 2013 22:10:18 -0600 Subject: add geoclue, glib test fixtures --- include/CMakeLists.txt | 2 + src/CMakeLists.txt | 50 ++-- src/clock-live.c | 278 --------------------- src/clock-live.h | 73 ------ src/clock.c | 110 -------- src/clock.h | 76 ------ src/dbus-shared.h | 24 -- src/formatter.cpp | 462 ++++++++++++++++++++++++++++++++++ src/planner-eds.c | 653 ------------------------------------------------ src/planner.c | 281 --------------------- src/service.h | 84 ------- src/settings-shared.h | 50 ---- src/timezone-file.c | 212 ---------------- src/timezone-file.h | 58 ----- src/timezone-geoclue.c | 227 ----------------- src/timezone-geoclue.h | 57 ----- src/timezone.c | 134 ---------- src/timezone.h | 72 ------ src/timezones-live.cpp | 69 +++++ src/utils.c | 453 --------------------------------- src/utils.cpp | 140 +++++++++++ src/utils.h | 66 ----- tests/CMakeLists.txt | 76 +++++- tests/geoclue-fixture.h | 142 +++++++++++ tests/glib-fixture.h | 131 ++++++++++ tests/test-core.cc | 148 +++++++++++ tests/test-skew.cc | 209 ++++++++++++++++ tests/test-timezones.cc | 122 +++++++++ 28 files changed, 1520 insertions(+), 2939 deletions(-) create mode 100644 include/CMakeLists.txt delete mode 100644 src/clock-live.c delete mode 100644 src/clock-live.h delete mode 100644 src/clock.c delete mode 100644 src/clock.h delete mode 100644 src/dbus-shared.h create mode 100644 src/formatter.cpp delete mode 100644 src/planner-eds.c delete mode 100644 src/planner.c delete mode 100644 src/service.h delete mode 100644 src/settings-shared.h delete mode 100644 src/timezone-file.c delete mode 100644 src/timezone-file.h delete mode 100644 src/timezone-geoclue.c delete mode 100644 src/timezone-geoclue.h delete mode 100644 src/timezone.c delete mode 100644 src/timezone.h create mode 100644 src/timezones-live.cpp delete mode 100644 src/utils.c create mode 100644 src/utils.cpp delete mode 100644 src/utils.h create mode 100644 tests/geoclue-fixture.h create mode 100644 tests/glib-fixture.h create mode 100644 tests/test-core.cc create mode 100644 tests/test-skew.cc create mode 100644 tests/test-timezones.cc (limited to 'tests') diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..cfef04b --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(core) +add_subdirectory(datetime) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d51385..2c847ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,8 @@ -set (SERVICE_LIB "libindicatordatetimeservice") +set (SERVICE_LIB "indicatordatetimeservice") set (SERVICE_EXEC "indicator-datetime-service") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") + add_definitions (-DTIMEZONE_FILE="/etc/timezone" -DG_LOG_DOMAIN="Indicator-Datetime") @@ -10,36 +12,28 @@ if (BUILD_PANEL) endif () add_library (${SERVICE_LIB} STATIC - clock.c - clock.h - clock-live.c - clock-live.h - planner.c - planner.h - planner-eds.c - planner-eds.h - service.c - service.h - timezone.c - timezone.h - timezone-file.c - timezone-file.h - timezone-geoclue.c - timezone-geoclue.h - utils.c - utils.h - dbus-shared.h - settings-shared.h) -include_directories (${SERVICE_DEPS_INCLUDE_DIRS}) + clock.cpp + clock-live.cpp + formatter.cpp + formatter-desktop.cpp + locations.cpp + locations-settings.cpp + planner-eds.cpp + timezone-file.cpp + timezone-geoclue.cpp + timezones-live.cpp + utils.cpp) +include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) -add_executable (${SERVICE_EXEC} main.c) -target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) -install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) + +#add_executable (${SERVICE_EXEC} main.c) +#target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) +#install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) # common properties -set_property (TARGET ${SERVICE_LIB} ${SERVICE_EXEC} - APPEND_STRING PROPERTY COMPILE_FLAGS - " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") +#set_property (TARGET ${SERVICE_LIB} ${SERVICE_EXEC} +# APPEND_STRING PROPERTY COMPILE_FLAGS +# " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") diff --git a/src/clock-live.c b/src/clock-live.c deleted file mode 100644 index 2a375fd..0000000 --- a/src/clock-live.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include - -#include "clock-live.h" -#include "settings-shared.h" -#include "timezone-file.h" -#include "timezone-geoclue.h" - -/*** -**** private struct -***/ - -struct _IndicatorDatetimeClockLivePriv -{ - GSettings * settings; - - GSList * timezones; /* IndicatorDatetimeTimezone */ - gchar ** timezones_strv; - GTimeZone * localtime_zone; -}; - -typedef IndicatorDatetimeClockLivePriv priv_t; - -/*** -**** GObject boilerplate -***/ - -static void indicator_datetime_clock_interface_init ( - IndicatorDatetimeClockInterface * iface); - -G_DEFINE_TYPE_WITH_CODE ( - IndicatorDatetimeClockLive, - indicator_datetime_clock_live, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_DATETIME_CLOCK, - indicator_datetime_clock_interface_init)) - -/*** -**** Timezones -***/ - -static void -on_current_timezone_changed (IndicatorDatetimeClockLive * self) -{ - priv_t * p = self->priv; - - /* Invalidate the timezone information. - These fields will be lazily regenerated by rebuild_timezones() */ - g_clear_pointer (&p->timezones_strv, g_strfreev); - g_clear_pointer (&p->localtime_zone, g_time_zone_unref); - - indicator_datetime_clock_emit_changed (INDICATOR_DATETIME_CLOCK (self)); -} - -static void -set_detect_location_enabled (IndicatorDatetimeClockLive * self, gboolean enabled) -{ - GSList * l; - priv_t * p = self->priv; - gboolean changed = FALSE; - - /* clear out the old timezone objects */ - if (p->timezones != NULL) - { - for (l=p->timezones; l!=NULL; l=l->next) - { - g_signal_handlers_disconnect_by_func (l->data, on_current_timezone_changed, self); - g_object_unref (l->data); - } - - g_slist_free (p->timezones); - p->timezones = NULL; - changed = TRUE; - } - - /* maybe add new timezone objects */ - if (enabled) - { - p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_geoclue_new ()); - p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_file_new (TIMEZONE_FILE)); - - for (l=p->timezones; l!=NULL; l=l->next) - { - g_signal_connect_swapped (l->data, "notify::timezone", - G_CALLBACK(on_current_timezone_changed), self); - } - - changed = TRUE; - } - - if (changed) - on_current_timezone_changed (self); -} - -/* When the 'auto-detect timezone' boolean setting changes, - start or stop watching geoclue and /etc/timezone */ -static void -on_detect_location_changed (IndicatorDatetimeClockLive * self) -{ - const gboolean enabled = g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_DETECTED_S); - set_detect_location_enabled (self, enabled); -} - -/*** -**** IndicatorDatetimeClock virtual functions -***/ - -static void -rebuild_timezones (IndicatorDatetimeClockLive * self) -{ - priv_t * p; - GHashTable * hash; - GSList * l; - int i; - GHashTableIter iter; - gpointer key; - - p = self->priv; - - /* Build a hashtable of timezone strings. - This will weed out duplicates. */ - hash = g_hash_table_new (g_str_hash, g_str_equal); - for (l=p->timezones; l!=NULL; l=l->next) - { - const gchar * tz = indicator_datetime_timezone_get_timezone (l->data); - if (tz && *tz) - g_hash_table_add (hash, (gpointer) tz); - } - - /* rebuild p->timezone_strv */ - g_strfreev (p->timezones_strv); - p->timezones_strv = g_new0 (gchar*, g_hash_table_size(hash) + 1); - i = 0; - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, &key, NULL)) - p->timezones_strv[i++] = g_strdup (key); - - /* rebuild localtime_zone */ - g_clear_pointer (&p->localtime_zone, g_time_zone_unref); - p->localtime_zone = g_time_zone_new (p->timezones_strv ? p->timezones_strv[0] : NULL); - - /* cleanup */ - g_hash_table_unref (hash); -} - -static const gchar ** -my_get_timezones (IndicatorDatetimeClock * clock) -{ - IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock); - priv_t * p = self->priv; - - if (G_UNLIKELY (p->timezones_strv == NULL)) - rebuild_timezones (self); - - return (const gchar **) p->timezones_strv; -} - -static GDateTime * -my_get_localtime (IndicatorDatetimeClock * clock) -{ - IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock); - priv_t * p = self->priv; - - if (G_UNLIKELY (p->localtime_zone == NULL)) - rebuild_timezones (self); - - return g_date_time_new_now (p->localtime_zone); -} - -/*** -**** GObject virtual functions -***/ - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimeClockLive * self; - priv_t * p; - - self = INDICATOR_DATETIME_CLOCK_LIVE(o); - p = self->priv; - - if (p->settings != NULL) - { - g_signal_handlers_disconnect_by_data (p->settings, self); - g_clear_object (&p->settings); - } - - set_detect_location_enabled (self, FALSE); - - G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeClockLive * self; - priv_t * p; - - self = INDICATOR_DATETIME_CLOCK_LIVE(o); - p = self->priv; - - g_clear_pointer (&p->localtime_zone, g_time_zone_unref); - g_strfreev (p->timezones_strv); - - G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_clock_live_class_init (IndicatorDatetimeClockLiveClass * klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = my_dispose; - object_class->finalize = my_finalize; - - g_type_class_add_private (klass, - sizeof (IndicatorDatetimeClockLivePriv)); -} - -static void -indicator_datetime_clock_interface_init (IndicatorDatetimeClockInterface * iface) -{ - iface->get_localtime = my_get_localtime; - iface->get_timezones = my_get_timezones; -} - -static void -indicator_datetime_clock_live_init (IndicatorDatetimeClockLive * self) -{ - IndicatorDatetimeClockLivePriv * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_CLOCK_LIVE, - IndicatorDatetimeClockLivePriv); - self->priv = p; - - p->settings = g_settings_new (SETTINGS_INTERFACE); - g_signal_connect_swapped (p->settings, "changed::" SETTINGS_SHOW_DETECTED_S, - G_CALLBACK(on_detect_location_changed), self); - - on_detect_location_changed (self); -} - -/*** -**** Public API -***/ - -IndicatorDatetimeClock * -indicator_datetime_clock_live_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_CLOCK_LIVE, NULL); - - return INDICATOR_DATETIME_CLOCK (o); -} diff --git a/src/clock-live.h b/src/clock-live.h deleted file mode 100644 index 4425f5b..0000000 --- a/src/clock-live.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_CLOCK_LIVE__H__ -#define __INDICATOR_DATETIME_CLOCK_LIVE__H__ - -#include /* parent class */ - -#include "clock.h" - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_CLOCK_LIVE \ - (indicator_datetime_clock_live_get_type()) - -#define INDICATOR_DATETIME_CLOCK_LIVE(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), \ - INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \ - IndicatorDatetimeClockLive)) - -#define INDICATOR_DATETIME_CLOCK_LIVE_GET_CLASS(o) \ - (G_TYPE_INSTANCE_GET_CLASS ((o), \ - INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \ - IndicatorDatetimeClockLiveClass)) - -#define INDICATOR_IS_DATETIME_CLOCK_LIVE(o) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((o), \ - INDICATOR_TYPE_DATETIME_CLOCK_LIVE)) - -typedef struct _IndicatorDatetimeClockLive - IndicatorDatetimeClockLive; -typedef struct _IndicatorDatetimeClockLivePriv - IndicatorDatetimeClockLivePriv; -typedef struct _IndicatorDatetimeClockLiveClass - IndicatorDatetimeClockLiveClass; - -/** - * An IndicatorDatetimeClock which gives live clock times - * from timezones determined by geoclue and /etc/timezone - */ -struct _IndicatorDatetimeClockLive -{ - GObject parent_instance; - - IndicatorDatetimeClockLivePriv * priv; -}; - -struct _IndicatorDatetimeClockLiveClass -{ - GObjectClass parent_class; -}; - -IndicatorDatetimeClock * indicator_datetime_clock_live_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_CLOCK_LIVE__H__ */ diff --git a/src/clock.c b/src/clock.c deleted file mode 100644 index 2c2fec2..0000000 --- a/src/clock.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "clock.h" - -enum -{ - SIGNAL_CHANGED, - SIGNAL_LAST -}; - -static guint signals[SIGNAL_LAST] = { 0 }; - -G_DEFINE_INTERFACE (IndicatorDatetimeClock, - indicator_datetime_clock, - G_TYPE_OBJECT); - -static void -indicator_datetime_clock_default_init (IndicatorDatetimeClockInterface * klass) -{ - signals[SIGNAL_CHANGED] = g_signal_new ( - "changed", - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (IndicatorDatetimeClockInterface, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -/*** -**** PUBLIC API -***/ - -/** - * Get a strv of timezones. - * - * Return value: (element-type char*) - * (transfer full): - * array of timezone strings - */ -const gchar ** -indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * self) -{ - const gchar ** timezones; - IndicatorDatetimeClockInterface * iface; - - g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL); - iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self); - - if (iface->get_timezones != NULL) - timezones = iface->get_timezones (self); - else - timezones = NULL; - - return timezones; -} - -/** - * Get the current time. - * - * Return value: (element-type GDateTime*) - * (transfer full): - * the current time. - */ -GDateTime * -indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * self) -{ - GDateTime * now; - IndicatorDatetimeClockInterface * iface; - - g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL); - iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self); - - if (iface->get_localtime != NULL) - now = iface->get_localtime (self); - else - now = NULL; - - return now; -} - -/** - * Emits the "changed" signal. - * - * This should only be called by subclasses. - */ -void -indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_CLOCK (self)); - - g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL); -} diff --git a/src/clock.h b/src/clock.h deleted file mode 100644 index 40cdf1c..0000000 --- a/src/clock.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_CLOCK__H__ -#define __INDICATOR_DATETIME_CLOCK__H__ - -#include - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_CLOCK \ - (indicator_datetime_clock_get_type ()) - -#define INDICATOR_DATETIME_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - INDICATOR_TYPE_DATETIME_CLOCK, \ - IndicatorDatetimeClock)) - -#define INDICATOR_IS_DATETIME_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_DATETIME_CLOCK)) - -#define INDICATOR_DATETIME_CLOCK_GET_INTERFACE(inst) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ - INDICATOR_TYPE_DATETIME_CLOCK, \ - IndicatorDatetimeClockInterface)) - -typedef struct _IndicatorDatetimeClock - IndicatorDatetimeClock; - -typedef struct _IndicatorDatetimeClockInterface - IndicatorDatetimeClockInterface; - -struct _IndicatorDatetimeClockInterface -{ - GTypeInterface parent_iface; - - /* signals */ - void (*changed) (IndicatorDatetimeClock * self); - - /* virtual functions */ - const gchar** (*get_timezones) (IndicatorDatetimeClock * self); - GDateTime* (*get_localtime) (IndicatorDatetimeClock * self); -}; - -GType indicator_datetime_clock_get_type (void); - -/*** -**** -***/ - -const gchar ** indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * clock); - -GDateTime * indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * clock); - -void indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * clock); - - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_CLOCK__H__ */ diff --git a/src/dbus-shared.h b/src/dbus-shared.h deleted file mode 100644 index 24319e3..0000000 --- a/src/dbus-shared.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -An indicator to show date and time information. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -#define BUS_NAME "com.canonical.indicator.datetime" -#define BUS_PATH "/com/canonical/indicator/datetime" - diff --git a/src/formatter.cpp b/src/formatter.cpp new file mode 100644 index 0000000..4e2f582 --- /dev/null +++ b/src/formatter.cpp @@ -0,0 +1,462 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +#include +#include + +#include // setlocale() +#include // nl_langinfo() +#include // strstr() + +namespace +{ +void clearTimer (guint& tag) +{ + if (tag) + { + g_source_remove (tag); + tag = 0; + } +} + +guint calculate_milliseconds_until_next_minute (GDateTime * now) +{ + GDateTime * next; + GDateTime * start_of_next; + GTimeSpan interval_usec; + guint interval_msec; + + next = g_date_time_add_minutes(now, 1); + start_of_next = g_date_time_new_local(g_date_time_get_year (next), + g_date_time_get_month (next), + g_date_time_get_day_of_month (next), + g_date_time_get_hour (next), + g_date_time_get_minute (next), + 0.1); + + interval_usec = g_date_time_difference(start_of_next, now); + interval_msec = (interval_usec + 999) / 1000; + + g_date_time_unref(start_of_next); + g_date_time_unref(next); + return interval_msec; +} + +gint calculate_milliseconds_until_next_second (GDateTime * now) +{ + gint interval_usec; + guint interval_msec; + + interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond (now); + interval_msec = (interval_usec + 999) / 1000; + return interval_msec; +} + +/* + * We periodically rebuild the sections that have time format strings + * that are dependent on the current time: + * + * 1. appointment menuitems' time format strings depend on the + * current time; for example, they don't show the day of week + * if the appointment is today. + * + * 2. location menuitems' time format strings depend on the + * current time; for example, they don't show the day of the week + * if the local date and location date are the same. + * + * 3. the "local date" menuitem in the calendar section is, + * obviously, dependent on the local time. + * + * In short, we want to update whenever the number of days between two zone + * might have changed. We do that by updating when either zone's day changes. + * + * Since not all UTC offsets are evenly divisible by hours + * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour + * is not enough. We need to refresh at HH:00, HH:15, HH:30, and HH:45. + */ +guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) +{ + char * str; + gint minute; + guint seconds; + GTimeSpan diff; + GDateTime * next; + GDateTime * start_of_next; + + minute = g_date_time_get_minute(now); + minute = 15 - (minute % 15); + next = g_date_time_add_minutes(now, minute); + start_of_next = g_date_time_new_local(g_date_time_get_year (next), + g_date_time_get_month (next), + g_date_time_get_day_of_month (next), + g_date_time_get_hour (next), + g_date_time_get_minute (next), + 0.1); + + str = g_date_time_format(start_of_next, "%F %T"); + g_debug ("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); + g_free (str); + + diff = g_date_time_difference(start_of_next, now); + seconds = (diff + (G_TIME_SPAN_SECOND-1)) / G_TIME_SPAN_SECOND; + + g_date_time_unref(start_of_next); + g_date_time_unref(next); + + return seconds; +} +} // anonymous namespace + + + +namespace unity { +namespace indicator { +namespace datetime { + +class Formatter::Impl +{ +public: + + Impl (Formatter* owner, const std::shared_ptr& clock): + owner_(owner), + clock_(clock) + { + owner_->headerFormat.changed().connect([this](const std::string& fmt G_GNUC_UNUSED){updateHeader();}); + updateHeader(); + + restartRelativeTimer(); + } + + ~Impl() + { + clearTimer(header_timer_); + } + +private: + + void updateHeader() + { + GDateTime * now = clock_->localtime(); + const time_t unix = g_date_time_to_unix (now); + struct tm tm; + localtime_r (&unix, &tm); + char str[512]; + strftime (str, sizeof(str), owner_->headerFormat.get().c_str(), &tm); + owner_->header.set (str); + g_date_time_unref (now); + + restartHeaderTimer(); + } + + void restartHeaderTimer() + { + clearTimer(header_timer_); + + const std::string fmt = owner_->headerFormat.get(); + const bool header_shows_seconds = (fmt.find("%s") != std::string::npos) + || (fmt.find("%S") != std::string::npos) + || (fmt.find("%T") != std::string::npos) + || (fmt.find("%X") != std::string::npos) + || (fmt.find("%c") != std::string::npos); + + guint interval_msec; + GDateTime * now = clock_->localtime(); + if (header_shows_seconds) + interval_msec = calculate_milliseconds_until_next_second (now); + else + interval_msec = calculate_milliseconds_until_next_minute (now); + g_date_time_unref (now); + + interval_msec += 50; // add a small margin to ensure the callback + // fires /after/ next is reached + + header_timer_ = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); + } + + static gboolean onHeaderTimer(gpointer gself) + { + static_cast(gself)->updateHeader(); + return G_SOURCE_REMOVE; + } + +private: + + void restartRelativeTimer() + { + clearTimer(relative_timer_); + + GDateTime * now = clock_->localtime(); + const guint seconds = calculate_seconds_until_next_fifteen_minutes(now); + relative_timer_ = g_timeout_add_seconds(seconds, onRelativeTimer, this); + g_date_time_unref(now); + } + + static gboolean onRelativeTimer(gpointer gself) + { + auto self = static_cast(gself); + self->owner_->relativeFormatChanged(); + self->restartRelativeTimer(); + return G_SOURCE_REMOVE; + } + +private: + Formatter * const owner_; + guint header_timer_ = 0; + guint relative_timer_ = 0; + +public: + std::shared_ptr clock_; +}; + +/*** +**** +***/ + +Formatter::Formatter(const std::shared_ptr& clock): + p (new Formatter::Impl(this, clock)) +{ +} + +Formatter::~Formatter() +{ +} + +bool +Formatter::is_locale_12h() +{ + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr}; + const char *t_fmt = nl_langinfo (T_FMT); + int i; + + for (i=0; formats_24h[i]; ++i) + if (strstr (t_fmt, formats_24h[i])) + return false; + + return true; +} + +const char * +Formatter::T_(const char *msg) +{ + /* General strategy here is to make sure LANGUAGE is empty (since that + trumps all LC_* vars) and then to temporarily swap LC_TIME and + LC_MESSAGES. Then have gettext translate msg. + + We strdup the strings because the setlocale & *env functions do not + guarantee anything about the storage used for the string, and thus + the string may not be portably safe after multiple calls. + + Note that while you might think g_dcgettext would do the trick here, + that actually looks in /usr/share/locale/XX/LC_TIME, not the + LC_MESSAGES directory, so we won't find any translation there. + */ + + char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); + const char *time_locale = setlocale (LC_TIME, nullptr); + char *language = g_strdup(g_getenv("LANGUAGE")); + const char *rv; + + if (language) + g_unsetenv("LANGUAGE"); + setlocale(LC_MESSAGES, time_locale); + + /* Get the LC_TIME version */ + rv = _(msg); + + /* Put everything back the way it was */ + setlocale(LC_MESSAGES, message_locale); + if (language) + g_setenv("LANGUAGE", language, TRUE); + + g_free(message_locale); + g_free(language); + return rv; +} + +const char * +Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) +{ + const char * fmt; + + if (twelvehour && show_seconds) + /* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */ + fmt = T_("%l:%M:%S %p"); + else if (twelvehour) + /* TRANSLATORS: a strftime(3) format for 12hr time */ + fmt = T_("%l:%M %p"); + else if (show_seconds) + /* TRANSLATORS: a strftime(3) format for 24hr time w/seconds */ + fmt = T_("%H:%M:%S"); + else + /* TRANSLATORS: a strftime(3) format for 24hr time */ + fmt = T_("%H:%M"); + + return fmt; +} + +/*** +**** +***/ + +namespace +{ +typedef enum +{ + DATE_PROXIMITY_TODAY, + DATE_PROXIMITY_TOMORROW, + DATE_PROXIMITY_WEEK, + DATE_PROXIMITY_FAR +} +date_proximity_t; + +date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) +{ + date_proximity_t prox = DATE_PROXIMITY_FAR; + gint now_year, now_month, now_day; + gint time_year, time_month, time_day; + + // does it happen today? + g_date_time_get_ymd (now, &now_year, &now_month, &now_day); + g_date_time_get_ymd (time, &time_year, &time_month, &time_day); + if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) + prox = DATE_PROXIMITY_TODAY; + + // does it happen tomorrow? + if (prox == DATE_PROXIMITY_FAR) + { + GDateTime * tomorrow; + gint tom_year, tom_month, tom_day; + + tomorrow = g_date_time_add_days (now, 1); + g_date_time_get_ymd (tomorrow, &tom_year, &tom_month, &tom_day); + if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) + prox = DATE_PROXIMITY_TOMORROW; + + g_date_time_unref (tomorrow); + } + + // does it happen this week? + if (prox == DATE_PROXIMITY_FAR) + { + GDateTime * week; + GDateTime * week_bound; + + week = g_date_time_add_days (now, 6); + week_bound = g_date_time_new_local (g_date_time_get_year(week), + g_date_time_get_month (week), + g_date_time_get_day_of_month(week), + 23, 59, 59.9); + + if (g_date_time_compare (time, week_bound) <= 0) + prox = DATE_PROXIMITY_WEEK; + + g_date_time_unref (week_bound); + g_date_time_unref (week); + } + + return prox; +} +} // anonymous namespace + +/** + * _ a time today should be shown as just the time (e.g. “3:55 PM”) + * _ a full-day event today should be shown as “Today” + * _ a time any other day this week should be shown as the short version of the + * day and time (e.g. “Wed 3:55 PM”) + * _ a full-day event tomorrow should be shown as “Tomorrow” + * _ a full-day event another day this week should be shown as the + * weekday (e.g. “Friday”) + * _ a time after this week should be shown as the short version of the day, + * date, and time (e.g. “Wed 21 Apr 3:55 PM”) + * _ a full-day event after this week should be shown as the short version of + * the day and date (e.g. “Wed 21 Apr”). + * _ in addition, when presenting the times of upcoming events, the time should + * be followed by the timezone if it is different from the one the computer + * is currently set to. For example, “Wed 3:55 PM UTC−5”. + */ +std::string +Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const +{ + std::string ret; + GDateTime * now = p->clock_->localtime(); + + if (then != nullptr) + { + const bool full_day = then_end && (g_date_time_difference (then_end, then) >= G_TIME_SPAN_DAY); + const auto prox = getDateProximity (now, then); + + if (full_day) + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: ret = _("Today"); break; + case DATE_PROXIMITY_TOMORROW: ret = _("Tomorrow"); break; + case DATE_PROXIMITY_WEEK: ret = T_("%A"); break; + case DATE_PROXIMITY_FAR: ret = T_("%a %d %b"); break; + } + } + else if (is_locale_12h()) + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: ret = T_("%l:%M %p"); break; + case DATE_PROXIMITY_TOMORROW: ret = T_("Tomorrow\u2003%l:%M %p"); break; + case DATE_PROXIMITY_WEEK: ret = T_("%a\u2003%l:%M %p"); break; + case DATE_PROXIMITY_FAR: ret = T_("%a %d %b\u2003%l:%M %p"); break; + } + } + else + { + switch (prox) + { + case DATE_PROXIMITY_TODAY: ret = T_("%H:%M"); break; + case DATE_PROXIMITY_TOMORROW: ret = T_("Tomorrow\u2003%H:%M"); break; + case DATE_PROXIMITY_WEEK: ret = T_("%a\u2003%H:%M"); break; + case DATE_PROXIMITY_FAR: ret = T_("%a %d %b\u2003%H:%M"); break; + } + } + + /* if it's an appointment in a different timezone (and doesn't run for a full day) + then the time should be followed by its timezone. */ + if ((then_end != nullptr) && + (!full_day) && + ((g_date_time_get_utc_offset (now) != g_date_time_get_utc_offset (then)))) + { + ret += ' '; + ret += g_date_time_get_timezone_abbreviation (then); + } + } + + g_date_time_unref (now); + return ret; +} + +/*** +**** +***/ + +} // namespace datetime + +} // namespace indicator + +} // namespace unity diff --git a/src/planner-eds.c b/src/planner-eds.c deleted file mode 100644 index cc2b8c5..0000000 --- a/src/planner-eds.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "planner-eds.h" - -struct _IndicatorDatetimePlannerEdsPriv -{ - GSList * sources; - GCancellable * cancellable; - ESourceRegistry * source_registry; -}; - -typedef IndicatorDatetimePlannerEdsPriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimePlannerEds, - indicator_datetime_planner_eds, - INDICATOR_TYPE_DATETIME_PLANNER) - -G_DEFINE_QUARK ("source-client", source_client) - -/*** -**** -**** my_get_appointments() helpers -**** -***/ - -/* whole-task data that all the subtasks can see */ -struct appointment_task_data -{ - /* a ref to the planner's cancellable */ - GCancellable * cancellable; - - /* how many subtasks are still running on */ - int subtask_count; - - /* the list of appointments to be returned */ - GSList * appointments; -}; - -static struct appointment_task_data * -appointment_task_data_new (GCancellable * cancellable) -{ - struct appointment_task_data * data; - - data = g_slice_new0 (struct appointment_task_data); - data->cancellable = g_object_ref (cancellable); - return data; -} - -static void -appointment_task_data_free (gpointer gdata) -{ - struct appointment_task_data * data = gdata; - - g_object_unref (data->cancellable); - - g_slice_free (struct appointment_task_data, data); -} - -static void -appointment_task_done (GTask * task) -{ - struct appointment_task_data * data = g_task_get_task_data (task); - - g_task_return_pointer (task, data->appointments, NULL); - g_object_unref (task); -} - -static void -appointment_task_decrement_subtasks (GTask * task) -{ - struct appointment_task_data * data = g_task_get_task_data (task); - - if (g_atomic_int_dec_and_test (&data->subtask_count)) - appointment_task_done (task); -} - -static void -appointment_task_increment_subtasks (GTask * task) -{ - struct appointment_task_data * data = g_task_get_task_data (task); - - g_atomic_int_inc (&data->subtask_count); -} - -/** -*** get-the-appointment's-uri subtasks -**/ - -struct appointment_uri_subtask_data -{ - /* The parent task */ - GTask * task; - - /* The appointment whose uri we're looking for. - This pointer is owned by the Task and isn't reffed/unreffed by the subtask */ - struct IndicatorDatetimeAppt * appt; -}; - -static void -appointment_uri_subtask_done (struct appointment_uri_subtask_data * subdata) -{ - GTask * task = subdata->task; - - /* free the subtask data */ - g_slice_free (struct appointment_uri_subtask_data, subdata); - - appointment_task_decrement_subtasks (task); -} - -static struct appointment_uri_subtask_data * -appointment_uri_subtask_data_new (GTask * task, struct IndicatorDatetimeAppt * appt) -{ - struct appointment_uri_subtask_data * subdata; - - appointment_task_increment_subtasks (task); - - subdata = g_slice_new0 (struct appointment_uri_subtask_data); - subdata->task = task; - subdata->appt = appt; - return subdata; -} - -static void -on_appointment_uris_ready (GObject * client, - GAsyncResult * res, - gpointer gsubdata) -{ - GSList * uris; - GError * error; - struct appointment_uri_subtask_data * subdata = gsubdata; - - uris = NULL; - error = NULL; - e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); - if (error != NULL) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Error getting appointment uris: %s", error->message); - - g_error_free (error); - } - else if (uris != NULL) - { - struct IndicatorDatetimeAppt * appt = subdata->appt; - appt->url = g_strdup (uris->data); /* copy the first URL */ - g_debug ("found url '%s' for appointment '%s'", appt->url, appt->summary); - e_client_util_free_string_slist (uris); - } - - appointment_uri_subtask_done (subdata); -} - -/** -*** enumerate-the-components subtasks -**/ - -/* data struct for the enumerate-components subtask */ -struct appointment_component_subtask_data -{ - /* The parent task */ - GTask * task; - - /* The client we're walking through. The subtask owns a ref to this */ - ECalClient * client; - - /* The appointment's color coding. The subtask owns this string */ - gchar * color; -}; - -static void -on_appointment_component_subtask_done (gpointer gsubdata) -{ - struct appointment_component_subtask_data * subdata = gsubdata; - GTask * task = subdata->task; - - /* free the subtask data */ - g_free (subdata->color); - g_object_unref (subdata->client); - g_slice_free (struct appointment_component_subtask_data, subdata); - - appointment_task_decrement_subtasks (task); -} - -static struct appointment_component_subtask_data * -appointment_component_subtask_data_new (GTask * task, ECalClient * client, const gchar * color) -{ - struct appointment_component_subtask_data * subdata; - - appointment_task_increment_subtasks (task); - - subdata = g_slice_new0 (struct appointment_component_subtask_data); - subdata->task = task; - subdata->client = g_object_ref (client); - subdata->color = g_strdup (color); - return subdata; -} - -static gboolean -my_get_appointments_foreach (ECalComponent * component, - time_t begin, - time_t end, - gpointer gsubdata) -{ - const ECalComponentVType vtype = e_cal_component_get_vtype (component); - struct appointment_component_subtask_data * subdata = gsubdata; - struct appointment_task_data * data = g_task_get_task_data (subdata->task); - - if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) - { - const gchar * uid = NULL; - icalproperty_status status = 0; - - e_cal_component_get_uid (component, &uid); - e_cal_component_get_status (component, &status); - - if ((uid != NULL) && - (status != ICAL_STATUS_COMPLETED) && - (status != ICAL_STATUS_CANCELLED)) - { - GList * alarm_uids; - GSList * l; - GSList * recur_list; - ECalComponentText text; - struct IndicatorDatetimeAppt * appt; - struct appointment_uri_subtask_data * uri_subdata; - - appt = g_slice_new0 (struct IndicatorDatetimeAppt); - - /* Determine whether this is a recurring event. - NB: icalrecurrencetype supports complex recurrence patterns; - however, since design only allows daily recurrence, - that's all we support here. */ - e_cal_component_get_rrule_list (component, &recur_list); - for (l=recur_list; l!=NULL; l=l->next) - { - const struct icalrecurrencetype * recur = l->data; - appt->is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) - && (recur->interval == 1)); - } - e_cal_component_free_recur_list (recur_list); - - text.value = ""; - e_cal_component_get_summary (component, &text); - - appt->begin = g_date_time_new_from_unix_local (begin); - appt->end = g_date_time_new_from_unix_local (end); - appt->color = g_strdup (subdata->color); - appt->is_event = vtype == E_CAL_COMPONENT_EVENT; - appt->summary = g_strdup (text.value); - appt->uid = g_strdup (uid); - - alarm_uids = e_cal_component_get_alarm_uids (component); - appt->has_alarms = alarm_uids != NULL; - cal_obj_uid_list_free (alarm_uids); - - data->appointments = g_slist_prepend (data->appointments, appt); - - /* start a new subtask to get the associated URIs */ - uri_subdata = appointment_uri_subtask_data_new (subdata->task, appt); - e_cal_client_get_attachment_uris (subdata->client, - uid, - NULL, - data->cancellable, - on_appointment_uris_ready, - uri_subdata); - } - } - - return G_SOURCE_CONTINUE; -} - -/*** -**** IndicatorDatetimePlanner virtual funcs -***/ - -static void -my_get_appointments (IndicatorDatetimePlanner * planner, - GDateTime * begin_datetime, - GDateTime * end_datetime, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSList * l; - priv_t * p; - const char * str; - icaltimezone * default_timezone; - const int64_t begin = g_date_time_to_unix (begin_datetime); - const int64_t end = g_date_time_to_unix (end_datetime); - GTask * task; - gboolean subtasks_added; - - p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv; - - /** - *** init the default timezone - **/ - - default_timezone = NULL; - - if ((str = indicator_datetime_planner_get_timezone (planner))) - { - default_timezone = icaltimezone_get_builtin_timezone (str); - - if (default_timezone == NULL) /* maybe str is a tzid? */ - default_timezone = icaltimezone_get_builtin_timezone_from_tzid (str); - } - - /** - *** walk through the sources to build the appointment list - **/ - - task = g_task_new (planner, p->cancellable, callback, user_data); - g_task_set_task_data (task, - appointment_task_data_new (p->cancellable), - appointment_task_data_free); - - subtasks_added = FALSE; - for (l=p->sources; l!=NULL; l=l->next) - { - ESource * source; - ECalClient * client; - const char * color; - struct appointment_component_subtask_data * subdata; - - source = l->data; - client = g_object_get_qdata (l->data, source_client_quark()); - if (client == NULL) - continue; - - if (default_timezone != NULL) - e_cal_client_set_default_timezone (client, default_timezone); - - /* start a new subtask to enumerate all the components in this client. */ - color = e_source_selectable_get_color (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR)); - subdata = appointment_component_subtask_data_new (task, client, color); - subtasks_added = TRUE; - e_cal_client_generate_instances (client, - begin, - end, - p->cancellable, - my_get_appointments_foreach, - subdata, - on_appointment_component_subtask_done); - } - - if (!subtasks_added) - appointment_task_done (task); -} - -static GSList * -my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GAsyncResult * res, - GError ** error) -{ - return g_task_propagate_pointer (G_TASK(res), error); -} - -static gboolean -my_is_configured (IndicatorDatetimePlanner * planner) -{ - IndicatorDatetimePlannerEds * self; - - /* confirm that it's installed... */ - gchar *evo = g_find_program_in_path ("evolution"); - if (evo == NULL) - return FALSE; - - g_debug ("found calendar app: '%s'", evo); - g_free (evo); - - /* see if there are any calendar sources */ - self = INDICATOR_DATETIME_PLANNER_EDS (planner); - return self->priv->sources != NULL; -} - -static void -my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED) -{ - GError * error = NULL; - const char * const command = "evolution -c calendar"; - - if (!g_spawn_command_line_async (command, &error)) - { - g_warning ("Unable to start %s: %s", command, error->message); - g_error_free (error); - } -} - -static void -my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GDateTime * activate_time) -{ - gchar * isodate; - gchar * command; - GError * err; - - isodate = g_date_time_format (activate_time, "%Y%m%d"); - command = g_strdup_printf ("evolution \"calendar:///?startdate=%s\"", isodate); - err = 0; - if (!g_spawn_command_line_async (command, &err)) - { - g_warning ("Unable to start %s: %s", command, err->message); - g_error_free (err); - } - - g_free (command); - g_free (isodate); -} - -/*** -**** Source / Client Wrangling -***/ - -static void -on_client_connected (GObject * unused G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gself) -{ - GError * error; - EClient * client; - - error = NULL; - client = e_cal_client_connect_finish (res, &error); - if (error != NULL) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message); - - g_error_free (error); - } - else - { - /* we've got a new connected ECalClient, so store it & notify clients */ - - g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)), - source_client_quark(), - client, - g_object_unref); - - indicator_datetime_planner_emit_appointments_changed (gself); - } -} - -static void -on_source_enabled (ESourceRegistry * registry G_GNUC_UNUSED, - ESource * source, - gpointer gself) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself); - priv_t * p = self->priv; - - e_cal_client_connect (source, - E_CAL_CLIENT_SOURCE_TYPE_EVENTS, - p->cancellable, - on_client_connected, - self); -} - -static void -on_source_added (ESourceRegistry * registry, - ESource * source, - gpointer gself) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself); - priv_t * p = self->priv; - - p->sources = g_slist_prepend (p->sources, g_object_ref(source)); - - if (e_source_get_enabled (source)) - on_source_enabled (registry, source, gself); -} - -static void -on_source_disabled (ESourceRegistry * registry G_GNUC_UNUSED, - ESource * source, - gpointer gself) -{ - ECalClient * client; - - /* If this source has a connected ECalClient, remove it & notify clients */ - if ((client = g_object_steal_qdata (G_OBJECT(source), source_client_quark()))) - { - g_object_unref (client); - indicator_datetime_planner_emit_appointments_changed (gself); - } -} - -static void -on_source_removed (ESourceRegistry * registry, - ESource * source, - gpointer gself) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself); - priv_t * p = self->priv; - - on_source_disabled (registry, source, gself); - - p->sources = g_slist_remove (p->sources, source); - g_object_unref (source); -} - -static void -on_source_changed (ESourceRegistry * registry G_GNUC_UNUSED, - ESource * source G_GNUC_UNUSED, - gpointer gself) -{ - indicator_datetime_planner_emit_appointments_changed (gself); -} - -static void -on_source_registry_ready (GObject * source_object G_GNUC_UNUSED, - GAsyncResult * res, - gpointer gself) -{ - GError * error; - ESourceRegistry * r; - - error = NULL; - r = e_source_registry_new_finish (res, &error); - if (error != NULL) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message); - - g_error_free (error); - } - else - { - IndicatorDatetimePlannerEds * self; - priv_t * p; - GList * l; - GList * sources; - - self = INDICATOR_DATETIME_PLANNER_EDS (gself); - p = self->priv; - - g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self); - g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self); - g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self); - g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self); - g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self); - - p->source_registry = r; - - sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR); - for (l=sources; l!=NULL; l=l->next) - on_source_added (r, l->data, self); - g_list_free_full (sources, g_object_unref); - } -} - -/*** -**** GObject virtual funcs -***/ - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o); - priv_t * p = self->priv; - - if (p->cancellable != NULL) - { - g_cancellable_cancel (p->cancellable); - g_clear_object (&p->cancellable); - } - - if (p->source_registry != NULL) - { - g_signal_handlers_disconnect_by_func (p->source_registry, - indicator_datetime_planner_emit_appointments_changed, - self); - - g_clear_object (&self->priv->source_registry); - } - - G_OBJECT_CLASS (indicator_datetime_planner_eds_parent_class)->dispose (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_planner_eds_class_init (IndicatorDatetimePlannerEdsClass * klass) -{ - GObjectClass * object_class; - IndicatorDatetimePlannerClass * planner_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - - planner_class = INDICATOR_DATETIME_PLANNER_CLASS (klass); - planner_class->is_configured = my_is_configured; - planner_class->activate = my_activate; - planner_class->activate_time = my_activate_time; - planner_class->get_appointments = my_get_appointments; - planner_class->get_appointments_finish = my_get_appointments_finish; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv)); -} - -static void -indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_PLANNER_EDS, - IndicatorDatetimePlannerEdsPriv); - - self->priv = p; - - p->cancellable = g_cancellable_new (); - - e_source_registry_new (p->cancellable, - on_source_registry_ready, - self); -} - -/*** -**** Public -***/ - -IndicatorDatetimePlanner * -indicator_datetime_planner_eds_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_EDS, NULL); - - return INDICATOR_DATETIME_PLANNER (o); -} diff --git a/src/planner.c b/src/planner.c deleted file mode 100644 index 9b9a77f..0000000 --- a/src/planner.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "planner.h" - -/** -*** Signals Boilerplate -**/ - -enum -{ - SIGNAL_APPTS_CHANGED, - SIGNAL_LAST -}; - -static guint signals[SIGNAL_LAST] = { 0 }; - -/** -*** Properties Boilerplate -**/ - -enum -{ - PROP_0, - PROP_TIMEZONE, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -/** -*** GObject Boilerplate -**/ - -G_DEFINE_TYPE (IndicatorDatetimePlanner, - indicator_datetime_planner, - G_TYPE_OBJECT) - -struct _IndicatorDatetimePlannerPriv -{ - char * timezone; -}; - -/*** -**** GObjectClass virtual funcs -***/ - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o); - - switch (property_id) - { - case PROP_TIMEZONE: - g_value_set_string (value, self->priv->timezone); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_set_property (GObject * o, - guint property_id, - const GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o); - - switch (property_id) - { - case PROP_TIMEZONE: - indicator_datetime_planner_set_timezone (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER(o); - - g_free (self->priv->timezone); - - G_OBJECT_CLASS (indicator_datetime_planner_parent_class)->finalize (o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_planner_class_init (IndicatorDatetimePlannerClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerPriv)); - - object_class = G_OBJECT_CLASS (klass); - object_class->finalize = my_finalize; - object_class->get_property = my_get_property; - object_class->set_property = my_set_property; - - klass->get_appointments = NULL; - - signals[SIGNAL_APPTS_CHANGED] = g_signal_new ("appointments-changed", - G_TYPE_FROM_CLASS(klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (IndicatorDatetimePlannerClass, appointments_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - /* install properties */ - - properties[PROP_0] = NULL; - - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", - "Timezone", - "Default timezone for the EDS appointments", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_planner_init (IndicatorDatetimePlanner * self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_PLANNER, - IndicatorDatetimePlannerPriv); -} - -/*** -**** Public API -***/ - -void -indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - g_signal_emit (self, signals[SIGNAL_APPTS_CHANGED], 0, NULL); -} - -static gint -compare_appointments_by_start_time (gconstpointer ga, gconstpointer gb) -{ - const struct IndicatorDatetimeAppt * a = ga; - const struct IndicatorDatetimeAppt * b = gb; - - return g_date_time_compare (a->begin, b->begin); -} - -void -indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data) -{ - IndicatorDatetimePlannerClass * klass; - - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - g_return_val_if_fail (begin != NULL, NULL); - g_return_val_if_fail (end != NULL, NULL); - - klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self); - g_return_if_fail (klass->get_appointments != NULL); - klass->get_appointments (self, begin, end, callback, user_data); -} - -GSList * -indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error) -{ - IndicatorDatetimePlannerClass * klass; - GSList * appointments; - - g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL); - - klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self); - g_return_val_if_fail (klass->get_appointments_finish != NULL, NULL); - appointments = klass->get_appointments_finish (self, res, error); - return g_slist_sort (appointments, compare_appointments_by_start_time); -} - -void -indicator_datetime_planner_free_appointments (GSList * l) -{ - g_slist_free_full (l, (GDestroyNotify)indicator_datetime_appt_free); -} - -gboolean -indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), FALSE); - - return INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->is_configured (self); -} - -void -indicator_datetime_planner_activate (IndicatorDatetimePlanner * self) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate (self); -} - -void -indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate_time (self, time); -} - -void -indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone) -{ - g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self)); - - g_free (self->priv->timezone); - self->priv->timezone = g_strdup (timezone); - g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); -} - -const char * -indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL); - - return self->priv->timezone; -} - -/*** -**** -***/ - -void -indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt) -{ - if (appt != NULL) - { - g_date_time_unref (appt->end); - g_date_time_unref (appt->begin); - g_free (appt->color); - g_free (appt->summary); - g_free (appt->url); - g_free (appt->uid); - g_slice_free (struct IndicatorDatetimeAppt, appt); - } -} - diff --git a/src/service.h b/src/service.h deleted file mode 100644 index d38db72..0000000 --- a/src/service.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_SERVICE_H__ -#define __INDICATOR_DATETIME_SERVICE_H__ - -#include -#include - -#include "clock.h" -#include "planner.h" - -G_BEGIN_DECLS - -/* standard GObject macros */ -#define INDICATOR_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeService)) -#define INDICATOR_TYPE_DATETIME_SERVICE (indicator_datetime_service_get_type()) -#define INDICATOR_IS_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_SERVICE)) - -typedef struct _IndicatorDatetimeService IndicatorDatetimeService; -typedef struct _IndicatorDatetimeServiceClass IndicatorDatetimeServiceClass; -typedef struct _IndicatorDatetimeServicePrivate IndicatorDatetimeServicePrivate; - -/* signal keys */ -#define INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST "name-lost" - -/** - * The Indicator Datetime Service. - */ -struct _IndicatorDatetimeService -{ - /*< private >*/ - GObject parent; - IndicatorDatetimeServicePrivate * priv; -}; - -struct _IndicatorDatetimeServiceClass -{ - GObjectClass parent_class; - - /* signals */ - - void (* name_lost)(IndicatorDatetimeService * self); -}; - -/*** -**** -***/ - -GType indicator_datetime_service_get_type (void); - -IndicatorDatetimeService * indicator_datetime_service_new (IndicatorDatetimeClock * clock, - IndicatorDatetimePlanner * planner); - -void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self, - GDateTime * date); - -void indicator_datetime_service_set_planner (IndicatorDatetimeService * self, - IndicatorDatetimePlanner * planner); - - -void indicator_datetime_service_set_clock (IndicatorDatetimeService * self, - IndicatorDatetimeClock * clock); - - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_SERVICE_H__ */ diff --git a/src/settings-shared.h b/src/settings-shared.h deleted file mode 100644 index 4615fe8..0000000 --- a/src/settings-shared.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -An indicator to show date and time information. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -#ifndef __DATETIME_SETTINGS_SHARED_H__ -#define __DATETIME_SETTINGS_SHARED_H__ - -typedef enum -{ - TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM -} -TimeFormatMode; - -#define SETTINGS_INTERFACE "com.canonical.indicator.datetime" -#define SETTINGS_SHOW_CLOCK_S "show-clock" -#define SETTINGS_TIME_FORMAT_S "time-format" -#define SETTINGS_SHOW_SECONDS_S "show-seconds" -#define SETTINGS_SHOW_DAY_S "show-day" -#define SETTINGS_SHOW_DATE_S "show-date" -#define SETTINGS_SHOW_YEAR_S "show-year" -#define SETTINGS_CUSTOM_TIME_FORMAT_S "custom-time-format" -#define SETTINGS_SHOW_CALENDAR_S "show-calendar" -#define SETTINGS_SHOW_WEEK_NUMBERS_S "show-week-numbers" -#define SETTINGS_SHOW_EVENTS_S "show-events" -#define SETTINGS_SHOW_LOCATIONS_S "show-locations" -#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" -#define SETTINGS_LOCATIONS_S "locations" -#define SETTINGS_TIMEZONE_NAME_S "timezone-name" - -#endif diff --git a/src/timezone-file.c b/src/timezone-file.c deleted file mode 100644 index ddc4256..0000000 --- a/src/timezone-file.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include /* GFile, GFileMonitor */ - -#include "timezone-file.h" - -enum -{ - PROP_0, - PROP_FILENAME, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0 }; - -struct _IndicatorDatetimeTimezoneFilePriv -{ - gchar * filename; - GFileMonitor * monitor; -}; - -typedef IndicatorDatetimeTimezoneFilePriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimeTimezoneFile, - indicator_datetime_timezone_file, - INDICATOR_TYPE_DATETIME_TIMEZONE) - -/*** -**** -***/ - -static void -reload (IndicatorDatetimeTimezoneFile * self) -{ - priv_t * p = self->priv; - - GError * err = NULL; - gchar * timezone = NULL; - - if (!g_file_get_contents (p->filename, &timezone, NULL, &err)) - { - g_warning ("%s Unable to read timezone file '%s': %s", G_STRLOC, p->filename, err->message); - g_error_free (err); - } - else - { - g_strstrip (timezone); - indicator_datetime_timezone_set_timezone (INDICATOR_DATETIME_TIMEZONE(self), timezone); - g_free (timezone); - } -} - -static void -set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename) -{ - GError * err; - GFile * file; - priv_t * p = self->priv; - - g_clear_object (&p->monitor); - g_free (p->filename); - - p->filename = g_strdup (filename); - err = NULL; - file = g_file_new_for_path (p->filename); - p->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &err); - g_object_unref (file); - if (err != NULL) - { - g_warning ("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message); - g_error_free (err); - } - else - { - g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self); - g_debug ("%s Monitoring timezone file '%s'", G_STRLOC, p->filename); - } - - reload (self); -} - -/*** -**** GObjectClass funcs -***/ - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - - switch (property_id) - { - case PROP_FILENAME: - g_value_set_string (value, self->priv->filename); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_set_property (GObject * o, - guint property_id, - const GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - - switch (property_id) - { - case PROP_FILENAME: - set_filename (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_dispose (GObject * o) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - priv_t * p = self->priv; - - g_clear_object (&p->monitor); - - G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->dispose (o); -} - -static void -my_finalize (GObject * o) -{ - IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o); - priv_t * p = self->priv; - - g_free (p->filename); - - G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->finalize (o); -} - -/*** -**** -***/ - -static void -indicator_datetime_timezone_file_class_init (IndicatorDatetimeTimezoneFileClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - object_class->finalize = my_finalize; - object_class->set_property = my_set_property; - object_class->get_property = my_get_property; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneFilePriv)); - - /* install properties */ - - properties[PROP_0] = NULL; - - properties[PROP_FILENAME] = g_param_spec_string ("filename", - "Filename", - "Filename to monitor for TZ changes", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_timezone_file_init (IndicatorDatetimeTimezoneFile * self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, - IndicatorDatetimeTimezoneFilePriv); -} - -/*** -**** Public -***/ - -IndicatorDatetimeTimezone * -indicator_datetime_timezone_file_new (const char * filename) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, "filename", filename, NULL); - - return INDICATOR_DATETIME_TIMEZONE (o); -} diff --git a/src/timezone-file.h b/src/timezone-file.h deleted file mode 100644 index b02abe1..0000000 --- a/src/timezone-file.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_TIMEZONE_FILE__H__ -#define __INDICATOR_DATETIME_TIMEZONE_FILE__H__ - -#include "timezone.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_TIMEZONE_FILE (indicator_datetime_timezone_file_get_type()) -#define INDICATOR_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFile)) -#define INDICATOR_DATETIME_TIMEZONE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFileClass)) -#define INDICATOR_IS_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE)) - -typedef struct _IndicatorDatetimeTimezoneFile IndicatorDatetimeTimezoneFile; -typedef struct _IndicatorDatetimeTimezoneFilePriv IndicatorDatetimeTimezoneFilePriv; -typedef struct _IndicatorDatetimeTimezoneFileClass IndicatorDatetimeTimezoneFileClass; - -GType indicator_datetime_timezone_file_get_type (void); - -/** - * An IndicatorDatetimeTimezone which uses a local file, - * such as /etc/timezone, to determine the timezone. - */ -struct _IndicatorDatetimeTimezoneFile -{ - /*< private >*/ - IndicatorDatetimeTimezone parent; - IndicatorDatetimeTimezoneFilePriv * priv; -}; - -struct _IndicatorDatetimeTimezoneFileClass -{ - IndicatorDatetimeTimezoneClass parent_class; -}; - -IndicatorDatetimeTimezone * indicator_datetime_timezone_file_new (const char * filename); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_TIMEZONE_FILE__H__ */ diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c deleted file mode 100644 index ac23b93..0000000 --- a/src/timezone-geoclue.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include - -#include "timezone-geoclue.h" - -struct _IndicatorDatetimeTimezoneGeocluePriv -{ - GeoclueMaster * master; - GeoclueMasterClient * client; - GeoclueAddress * address; -}; - -typedef IndicatorDatetimeTimezoneGeocluePriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimeTimezoneGeoclue, - indicator_datetime_timezone_geoclue, - INDICATOR_TYPE_DATETIME_TIMEZONE) - -static void geo_restart (IndicatorDatetimeTimezoneGeoclue * self); - -/*** -**** -***/ - -static void -on_address_changed (GeoclueAddress * address G_GNUC_UNUSED, - int timestamp G_GNUC_UNUSED, - GHashTable * addy_data, - GeoclueAccuracy * accuracy G_GNUC_UNUSED, - GError * error, - gpointer gself) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); - const char * timezone = g_hash_table_lookup (addy_data, "timezone"); - indicator_datetime_timezone_set_timezone (INDICATOR_DATETIME_TIMEZONE(self), timezone); - } -} - -/* The signal doesn't have the parameter for an error, so it ends up needing - a NULL inserted. */ -static void -on_address_changed_sig (GeoclueAddress * address G_GNUC_UNUSED, - int timestamp G_GNUC_UNUSED, - GHashTable * addy_data, - GeoclueAccuracy * accuracy G_GNUC_UNUSED, - gpointer gself) -{ - on_address_changed(address, timestamp, addy_data, accuracy, NULL, gself); -} - -static void -on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED, - GeoclueAddress * address, - GError * error, - gpointer gself) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - priv_t * p = INDICATOR_DATETIME_TIMEZONE_GEOCLUE(gself)->priv; - - g_assert (p->address == NULL); - p->address = g_object_ref (address); - - geoclue_address_get_address_async (address, on_address_changed, gself); - g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed_sig), gself); - } -} - -static void -on_requirements_set (GeoclueMasterClient * master G_GNUC_UNUSED, - GError * error, - gpointer user_data G_GNUC_UNUSED) -{ - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } -} - -static void -on_client_created (GeoclueMaster * master G_GNUC_UNUSED, - GeoclueMasterClient * client, - gchar * path, - GError * error, - gpointer gself) -{ - g_debug ("Created Geoclue client at: %s", path); - - if (error != NULL) - { - g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message); - } - else - { - IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself); - priv_t * p = self->priv; - - g_clear_object (&p->client); - p->client = g_object_ref (client); - g_signal_connect_swapped (p->client, "invalidated", G_CALLBACK(geo_restart), gself); - - geoclue_master_client_set_requirements_async (p->client, - GEOCLUE_ACCURACY_LEVEL_REGION, - 0, - FALSE, - GEOCLUE_RESOURCE_ALL, - on_requirements_set, - NULL); - - geoclue_master_client_create_address_async (p->client, on_address_created, gself); - } -} - -static void -geo_start (IndicatorDatetimeTimezoneGeoclue * self) -{ - priv_t * p = self->priv; - - g_assert (p->master == NULL); - p->master = geoclue_master_get_default (); - geoclue_master_create_client_async (p->master, on_client_created, self); -} - -static void -geo_stop (IndicatorDatetimeTimezoneGeoclue * self) -{ - priv_t * p = self->priv; - - if (p->address != NULL) - { - g_signal_handlers_disconnect_by_func (p->address, on_address_changed_sig, self); - g_clear_object (&p->address); - } - - if (p->client != NULL) - { - g_signal_handlers_disconnect_by_func (p->client, geo_restart, self); - g_clear_object (&p->client); - } - - g_clear_object (&p->master); -} - -static void -geo_restart (IndicatorDatetimeTimezoneGeoclue * self) -{ - geo_stop (self); - geo_start (self); -} - -/*** -**** -***/ - -static void -my_dispose (GObject * o) -{ - geo_stop (INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o)); - - G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->dispose (o); -} - -static void -indicator_datetime_timezone_geoclue_class_init (IndicatorDatetimeTimezoneGeoclueClass * klass) -{ - GObjectClass * object_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneGeocluePriv)); -} - -static void -indicator_datetime_timezone_geoclue_init (IndicatorDatetimeTimezoneGeoclue * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, - IndicatorDatetimeTimezoneGeocluePriv); - - self->priv = p; - - geo_start (self); -} - -/*** -**** Public -***/ - -IndicatorDatetimeTimezone * -indicator_datetime_timezone_geoclue_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, NULL); - - return INDICATOR_DATETIME_TIMEZONE (o); -} diff --git a/src/timezone-geoclue.h b/src/timezone-geoclue.h deleted file mode 100644 index 059bd81..0000000 --- a/src/timezone-geoclue.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ -#define __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ - -#include "timezone.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE (indicator_datetime_timezone_geoclue_get_type()) -#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclue)) -#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclueClass)) -#define INDICATOR_IS_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE)) - -typedef struct _IndicatorDatetimeTimezoneGeoclue IndicatorDatetimeTimezoneGeoclue; -typedef struct _IndicatorDatetimeTimezoneGeocluePriv IndicatorDatetimeTimezoneGeocluePriv; -typedef struct _IndicatorDatetimeTimezoneGeoclueClass IndicatorDatetimeTimezoneGeoclueClass; - -GType indicator_datetime_timezone_geoclue_get_type (void); - -/** - * An IndicatorDatetimeTimezone which uses GeoClue to determine the timezone. - */ -struct _IndicatorDatetimeTimezoneGeoclue -{ - /*< private >*/ - IndicatorDatetimeTimezone parent; - IndicatorDatetimeTimezoneGeocluePriv * priv; -}; - -struct _IndicatorDatetimeTimezoneGeoclueClass -{ - IndicatorDatetimeTimezoneClass parent_class; -}; - -IndicatorDatetimeTimezone * indicator_datetime_timezone_geoclue_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ */ diff --git a/src/timezone.c b/src/timezone.c deleted file mode 100644 index 4f8addc..0000000 --- a/src/timezone.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "timezone.h" - -G_DEFINE_TYPE (IndicatorDatetimeTimezone, - indicator_datetime_timezone, - G_TYPE_OBJECT) - -enum -{ - PROP_0, - PROP_TIMEZONE, - PROP_LAST -}; - -static GParamSpec * properties[PROP_LAST] = { 0, }; - -struct _IndicatorDatetimeTimezonePriv -{ - GString * timezone; -}; - -typedef struct _IndicatorDatetimeTimezonePriv priv_t; - -static void -my_get_property (GObject * o, - guint property_id, - GValue * value, - GParamSpec * pspec) -{ - IndicatorDatetimeTimezone * self = INDICATOR_DATETIME_TIMEZONE (o); - - switch (property_id) - { - case PROP_TIMEZONE: - g_value_set_string (value, indicator_datetime_timezone_get_timezone (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); - } -} - -static void -my_finalize (GObject * o) -{ - priv_t * p = INDICATOR_DATETIME_TIMEZONE(o)->priv; - - g_string_free (p->timezone, TRUE); - - G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->finalize (o); -} - -static void -/* cppcheck-suppress unusedFunction */ -indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass) -{ - GObjectClass * object_class; - const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezonePriv)); - - object_class = G_OBJECT_CLASS (klass); - object_class->get_property = my_get_property; - object_class->finalize = my_finalize; - - properties[PROP_TIMEZONE] = g_param_spec_string ("timezone", - "Timezone", - "Timezone", - "", - flags); - - g_object_class_install_properties (object_class, PROP_LAST, properties); -} - -static void -indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_TIMEZONE, - IndicatorDatetimeTimezonePriv); - - p->timezone = g_string_new (NULL); - - self->priv = p; -} - -/*** -**** -***/ - -const char * -indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone * self) -{ - g_return_val_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self), NULL); - - return self->priv->timezone->str; -} - -void -indicator_datetime_timezone_set_timezone (IndicatorDatetimeTimezone * self, - const char * timezone) -{ - priv_t * p = self->priv; - - if (g_strcmp0 (p->timezone->str, timezone)) - { - if (timezone != NULL) - g_string_assign (p->timezone, timezone); - else - g_string_set_size (p->timezone, 0); - g_debug ("%s new timezone set: '%s'", G_STRLOC, p->timezone->str); - g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]); - } -} diff --git a/src/timezone.h b/src/timezone.h deleted file mode 100644 index fa6593d..0000000 --- a/src/timezone.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_TIMEZONE__H__ -#define __INDICATOR_DATETIME_TIMEZONE__H__ - -#include -#include /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_TIMEZONE (indicator_datetime_timezone_get_type()) -#define INDICATOR_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezone)) -#define INDICATOR_DATETIME_TIMEZONE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass)) -#define INDICATOR_DATETIME_TIMEZONE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass)) -#define INDICATOR_IS_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE)) - -typedef struct _IndicatorDatetimeTimezone IndicatorDatetimeTimezone; -typedef struct _IndicatorDatetimeTimezonePriv IndicatorDatetimeTimezonePriv; -typedef struct _IndicatorDatetimeTimezoneClass IndicatorDatetimeTimezoneClass; - -GType indicator_datetime_timezone_get_type (void); - -/** - * Abstract Base Class for objects that provide a timezone. - * - * We use this in datetime to determine the user's current timezone - * for display in the 'locations' section of the datetime indicator. - * - * This class has a 'timezone' property that clients can watch - * for change notifications. - */ -struct _IndicatorDatetimeTimezone -{ - /*< private >*/ - GObject parent; - IndicatorDatetimeTimezonePriv * priv; -}; - -struct _IndicatorDatetimeTimezoneClass -{ - GObjectClass parent_class; -}; - -/*** -**** -***/ - -const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *); - -void indicator_datetime_timezone_set_timezone (IndicatorDatetimeTimezone *, - const char * new_timezone); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_TIMEZONE__H__ */ diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp new file mode 100644 index 0000000..cb5e2bc --- /dev/null +++ b/src/timezones-live.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +LiveTimezones::LiveTimezones (const std::string& filename): + file_ (filename) +{ + file_.timezone.changed().connect([this](const std::string&){updateTimezones();}); + + geolocationEnabled.changed().connect([this](bool){updateGeolocation();}); + updateGeolocation(); + + updateTimezones(); +} + +void +LiveTimezones::updateGeolocation() +{ + geo_.reset(); + + if (geolocationEnabled.get()) + { + GeoclueTimezone * geo = new GeoclueTimezone(); + geo->timezone.changed().connect([this](const std::string&){updateTimezones();}); + geo_.reset(geo); + } +} + +void +LiveTimezones::updateTimezones() +{ + const std::string a = file_.timezone.get(); + const std::string b = geo_ ? geo_->timezone.get() : ""; + + timezone.set (a.empty() ? b : a); + + std::set zones; + if (!a.empty()) + zones.insert(a); + if (!b.empty()) + zones.insert(b); + timezones.set(zones); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 5539c5c..0000000 --- a/src/utils.c +++ /dev/null @@ -1,453 +0,0 @@ -/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- - -A dialog for setting time and date preferences. - -Copyright 2010 Canonical Ltd. - -Authors: - Michael Terry - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include "utils.h" -#include "settings-shared.h" - -/* Check the system locale setting to see if the format is 24-hour - time or 12-hour time */ -gboolean -is_locale_12h (void) -{ - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; - - for (i = 0; formats_24h[i]; ++i) { - if (strstr (t_fmt, formats_24h[i])) { - return FALSE; - } - } - - return TRUE; -} - -void -split_settings_location (const gchar * location, gchar ** zone, gchar ** name) -{ - gchar * location_dup; - gchar * first; - - location_dup = g_strdup (location); - g_strstrip (location_dup); - - if ((first = strchr (location_dup, ' '))) - *first = '\0'; - - if (zone != NULL) - { - *zone = location_dup; - } - - if (name != NULL) - { - gchar * after = first ? g_strstrip (first + 1) : NULL; - - if (after && *after) - { - *name = g_strdup (after); - } - else /* make the name from zone */ - { - gchar * chr = strrchr (location_dup, '/'); - after = g_strdup (chr ? chr + 1 : location_dup); - - /* replace underscores with spaces */ - for (chr=after; chr && *chr; chr++) - if (*chr == '_') - *chr = ' '; - - *name = after; - } - } -} - -gchar * -get_current_zone_name (const gchar * location, GSettings * settings) -{ - gchar * new_zone, * new_name; - gchar * tz_name; - gchar * old_zone, * old_name; - gchar * rv; - - split_settings_location (location, &new_zone, &new_name); - - tz_name = g_settings_get_string (settings, SETTINGS_TIMEZONE_NAME_S); - split_settings_location (tz_name, &old_zone, &old_name); - g_free (tz_name); - - /* new_name is always just a sanitized version of a timezone. - old_name is potentially a saved "pretty" version of a timezone name from - geonames. So we prefer to use it if available and the zones match. */ - - if (g_strcmp0 (old_zone, new_zone) == 0) { - rv = old_name; - old_name = NULL; - } - else { - rv = new_name; - new_name = NULL; - } - - g_free (new_zone); - g_free (old_zone); - g_free (new_name); - g_free (old_name); - - return rv; -} - -/* Translate msg according to the locale specified by LC_TIME */ -static const char * -T_(const char *msg) -{ - /* General strategy here is to make sure LANGUAGE is empty (since that - trumps all LC_* vars) and then to temporarily swap LC_TIME and - LC_MESSAGES. Then have gettext translate msg. - - We strdup the strings because the setlocale & *env functions do not - guarantee anything about the storage used for the string, and thus - the string may not be portably safe after multiple calls. - - Note that while you might think g_dcgettext would do the trick here, - that actually looks in /usr/share/locale/XX/LC_TIME, not the - LC_MESSAGES directory, so we won't find any translation there. - */ - char *message_locale = g_strdup(setlocale(LC_MESSAGES, NULL)); - const char *time_locale = setlocale (LC_TIME, NULL); - char *language = g_strdup(g_getenv("LANGUAGE")); - const char *rv; - if (language) - g_unsetenv("LANGUAGE"); - setlocale(LC_MESSAGES, time_locale); - - /* Get the LC_TIME version */ - rv = _(msg); - - /* Put everything back the way it was */ - setlocale(LC_MESSAGES, message_locale); - if (language) - g_setenv("LANGUAGE", language, TRUE); - g_free(message_locale); - g_free(language); - return rv; -} - -gchar * -join_date_and_time_format_strings (const char * date_string, - const char * time_string) -{ - gchar * str; - - if (date_string && time_string) - { - /* TRANSLATORS: This is a format string passed to strftime to combine the - * date and the time. The value of "%s\xE2\x80\x82%s" will result in a - * string like this in US English 12-hour time: 'Fri Jul 16 11:50 AM'. - * The space in between date and time is a Unicode en space - * (E28082 in UTF-8 hex). */ - str = g_strdup_printf (T_("%s\xE2\x80\x82%s"), date_string, time_string); - } - else if (date_string) - { - str = g_strdup_printf (T_("%s"), date_string); - } - else /* time_string */ - { - str = g_strdup_printf (T_("%s"), time_string); - } - - return str; -} - -/*** -**** -***/ - -typedef enum -{ - DATE_PROXIMITY_TODAY, - DATE_PROXIMITY_TOMORROW, - DATE_PROXIMITY_WEEK, - DATE_PROXIMITY_FAR -} -date_proximity_t; - -static date_proximity_t -get_date_proximity (GDateTime * now, GDateTime * time) -{ - date_proximity_t prox = DATE_PROXIMITY_FAR; - gint now_year, now_month, now_day; - gint time_year, time_month, time_day; - - /* does it happen today? */ - g_date_time_get_ymd (now, &now_year, &now_month, &now_day); - g_date_time_get_ymd (time, &time_year, &time_month, &time_day); - if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) - prox = DATE_PROXIMITY_TODAY; - - /* does it happen tomorrow? */ - if (prox == DATE_PROXIMITY_FAR) - { - GDateTime * tomorrow; - gint tom_year, tom_month, tom_day; - - tomorrow = g_date_time_add_days (now, 1); - g_date_time_get_ymd (tomorrow, &tom_year, &tom_month, &tom_day); - if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) - prox = DATE_PROXIMITY_TOMORROW; - - g_date_time_unref (tomorrow); - } - - /* does it happen this week? */ - if (prox == DATE_PROXIMITY_FAR) - { - GDateTime * week; - GDateTime * week_bound; - - week = g_date_time_add_days (now, 6); - week_bound = g_date_time_new_local (g_date_time_get_year(week), - g_date_time_get_month (week), - g_date_time_get_day_of_month(week), - 23, 59, 59.9); - - if (g_date_time_compare (time, week_bound) <= 0) - prox = DATE_PROXIMITY_WEEK; - - g_date_time_unref (week_bound); - g_date_time_unref (week); - } - - return prox; -} - - -/* - * "Terse" time & date format strings - * - * Used on the phone menu where space is at a premium, these strings - * express the time and date in as brief a form as possible. - * - * Examples from spec: - * 1. "Daily 6:30 AM" - * 2. "5:07 PM" (note date is omitted; today's date is implicit) - * 3. "Daily 12 PM" (note minutes are omitted for on-the-hour times) - * 4. "Tomorrow 7 AM" (note "Tomorrow" is used instead of a day of week) - */ - -static const gchar * -get_terse_date_format_string (date_proximity_t proximity) -{ - const gchar * fmt; - - switch (proximity) - { - case DATE_PROXIMITY_TODAY: - /* 'Today' is implicit in the terse case, so no string needed */ - fmt = NULL; - break; - - case DATE_PROXIMITY_TOMORROW: - fmt = T_("Tomorrow"); - break; - - case DATE_PROXIMITY_WEEK: - /* a strftime(3) fmt string for abbreviated day of week */ - fmt = T_("%a"); - break; - - default: - /* a strftime(3) fmt string for day-of-month and abbreviated month */ - fmt = T_("%d %b"); - break; - } - - return fmt; -} - -const gchar* -get_terse_header_time_format_string (void) -{ - /* a strftime(3) fmt string for a H:MM 12 hour time, eg "6:59 PM" */ - return T_("%l:%M %p"); -} - -const gchar * -get_terse_time_format_string (GDateTime * time) -{ - const gchar * fmt; - - if (g_date_time_get_minute (time) != 0) - { - fmt = get_terse_header_time_format_string (); - } - else - { - /* a strftime(3) fmt string for a 12 hour on-the-hour time, eg "7 PM" */ - fmt = T_("%l %p"); - } - - return fmt; -} - -gchar * -generate_terse_format_string_at_time (GDateTime * now, GDateTime * time) -{ - const date_proximity_t prox = get_date_proximity (now, time); - const gchar * date_fmt = get_terse_date_format_string (prox); - const gchar * time_fmt = get_terse_time_format_string (time); - return join_date_and_time_format_strings (date_fmt, time_fmt); -} - -/*** -**** FULL -***/ - -static const gchar * -get_full_date_format_string (gboolean show_day, gboolean show_date, gboolean show_year) -{ - const char * fmt; - - if (show_day && show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ - fmt = T_("%a %b %e %Y"); - else if (show_day && show_date) - /* TRANSLATORS: a strftime(3) format showing the weekday and date */ - fmt = T_("%a %b %e"); - else if (show_day && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ - fmt = T_("%a %Y"); - else if (show_day) - /* TRANSLATORS: a strftime(3) format showing the weekday. */ - fmt = T_("%a"); - else if (show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the date and year */ - fmt = T_("%b %e %Y"); - else if (show_date) - /* TRANSLATORS: a strftime(3) format showing the date */ - fmt = T_("%b %e"); - else if (show_year) - /* TRANSLATORS: a strftime(3) format showing the year */ - fmt = T_("%Y"); - else - fmt = NULL; - - return fmt; -} - - -/* - * "Full" time & date format strings - * - * These are used on the desktop menu & header and honors the - * GSettings entries for 12/24hr mode and whether or not to show seconds. - * - */ - -const gchar * -get_full_time_format_string (GSettings * settings) -{ - gboolean twelvehour; - gboolean show_seconds; - const gchar * fmt; - - g_return_val_if_fail (settings != NULL, NULL); - - show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S); - - switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S)) - { - case TIME_FORMAT_MODE_LOCALE_DEFAULT: - twelvehour = is_locale_12h(); - break; - - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = FALSE; - break; - - default: - twelvehour = TRUE; - break; - } - - if (twelvehour && show_seconds) - /* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */ - fmt = T_("%l:%M:%S %p"); - else if (twelvehour) - /* TRANSLATORS: a strftime(3) format for 12hr time */ - fmt = T_("%l:%M %p"); - else if (show_seconds) - /* TRANSLATORS: a strftime(3) format for 24hr time w/seconds */ - fmt = T_("%H:%M:%S"); - else - /* TRANSLATORS: a strftime(3) format for 24hr time */ - fmt = T_("%H:%M"); - - return fmt; -} - -gchar * -generate_full_format_string (gboolean show_day, gboolean show_date, gboolean show_year, GSettings * settings) -{ - const gchar * date_fmt = get_full_date_format_string (show_day, show_date, show_year); - const gchar * time_fmt = get_full_time_format_string (settings); - return join_date_and_time_format_strings (date_fmt, time_fmt); -} - -gchar * -generate_full_format_string_at_time (GDateTime * now, GDateTime * time, GSettings * settings) -{ - gboolean show_day; - gboolean show_date; - - g_return_val_if_fail (now != NULL, NULL); - g_return_val_if_fail (time != NULL, NULL); - g_return_val_if_fail (settings != NULL, NULL); - - switch (get_date_proximity (now, time)) - { - case DATE_PROXIMITY_TODAY: - show_day = FALSE; - show_date = FALSE; - break; - - case DATE_PROXIMITY_TOMORROW: - case DATE_PROXIMITY_WEEK: - show_day = FALSE; - show_date = TRUE; - break; - - default: - show_day = TRUE; - show_date = TRUE; - break; - } - - return generate_full_format_string (show_day, show_date, FALSE, settings); -} - diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..42e034e --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,140 @@ +/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- + +A dialog for setting time and date preferences. + +Copyright 2010 Canonical Ltd. + +Authors: + Michael Terry + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, as published +by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +*/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* Check the system locale setting to see if the format is 24-hour + time or 12-hour time */ +gboolean +is_locale_12h (void) +{ + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; + const char *t_fmt = nl_langinfo (T_FMT); + int i; + + for (i = 0; formats_24h[i]; ++i) { + if (strstr (t_fmt, formats_24h[i])) { + return FALSE; + } + } + + return TRUE; +} + +void +split_settings_location (const gchar * location, gchar ** zone, gchar ** name) +{ + gchar * location_dup; + gchar * first; + + location_dup = g_strdup (location); + g_strstrip (location_dup); + + if ((first = strchr (location_dup, ' '))) + *first = '\0'; + + if (zone != NULL) + { + *zone = location_dup; + } + + if (name != NULL) + { + gchar * after = first ? g_strstrip (first + 1) : NULL; + + if (after && *after) + { + *name = g_strdup (after); + } + else /* make the name from zone */ + { + gchar * chr = strrchr (location_dup, '/'); + after = g_strdup (chr ? chr + 1 : location_dup); + + /* replace underscores with spaces */ + for (chr=after; chr && *chr; chr++) + if (*chr == '_') + *chr = ' '; + + *name = after; + } + } +} + +gchar * +get_current_zone_name (const gchar * location, GSettings * settings) +{ + gchar * new_zone, * new_name; + gchar * tz_name; + gchar * old_zone, * old_name; + gchar * rv; + + split_settings_location (location, &new_zone, &new_name); + + tz_name = g_settings_get_string (settings, SETTINGS_TIMEZONE_NAME_S); + split_settings_location (tz_name, &old_zone, &old_name); + g_free (tz_name); + + /* new_name is always just a sanitized version of a timezone. + old_name is potentially a saved "pretty" version of a timezone name from + geonames. So we prefer to use it if available and the zones match. */ + + if (g_strcmp0 (old_zone, new_zone) == 0) { + rv = old_name; + old_name = NULL; + } + else { + rv = new_name; + new_name = NULL; + } + + g_free (new_zone); + g_free (old_zone); + g_free (new_name); + g_free (old_name); + + return rv; +} + +gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) +{ + using unity::indicator::datetime::Clock; + using unity::indicator::datetime::MockClock; + using unity::indicator::datetime::DesktopFormatter; + + std::shared_ptr clock(new MockClock(now)); + DesktopFormatter formatter(clock); + return g_strdup (formatter.getRelativeFormat(then).c_str()); +} + diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 5eacce5..0000000 --- a/src/utils.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C; coding: utf-8; indent-tabs-mode: nil; tab-width: 2 -*- - -A dialog for setting time and date preferences. - -Copyright 2010 Canonical Ltd. - -Authors: - Michael Terry - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -#ifndef __DATETIME_UTILS_H__ -#define __DATETIME_UTILS_H__ - -#include -#include /* GSettings */ - -G_BEGIN_DECLS - -gboolean is_locale_12h (void); - -void split_settings_location (const char * location, - char ** zone, - char ** name); - -gchar * get_current_zone_name (const char * location, - GSettings * settings); - -gchar* join_date_and_time_format_strings (const char * date_fmt, - const char * time_fmt); -/*** -**** -***/ - -const gchar * get_terse_time_format_string (GDateTime * time); - -const gchar * get_terse_header_time_format_string (void); - -const gchar * get_full_time_format_string (GSettings * settings); - -gchar * generate_terse_format_string_at_time (GDateTime * now, - GDateTime * time); - -gchar * generate_full_format_string (gboolean show_day, - gboolean show_date, - gboolean show_year, - GSettings * settings); - -gchar * generate_full_format_string_at_time (GDateTime * now, - GDateTime * time, - GSettings * settings); - -G_END_DECLS - -#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 682896b..a424858 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,12 @@ +# build libgtest +add_library (gtest STATIC + ${GTEST_SOURCE_DIR}/gtest-all.cc + ${GTEST_SOURCE_DIR}/gtest_main.cc) +set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR}) +set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w) + +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -g ${CC_WARNING_ARGS}") + # build the necessary schemas set_directory_properties (PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled) @@ -12,12 +21,73 @@ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compil OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE OUTPUT_STRIP_TRAILING_WHITESPACE) add_custom_command (OUTPUT gschemas.compiled - DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.session.gschema.xml + DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.datetime.gschema.xml COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) -# look for hearder in our src dir, and also in the directories where we autogenerate files... +# look for headers in our src dir, and also in the directories where we autogenerate files... include_directories (${CMAKE_SOURCE_DIR}/src) -include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS}) +include_directories (${CMAKE_CURRENT_BINARY_DIR}) +include_directories (${DBUSTEST_INCLUDE_DIRS}) + +add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") + +# test-core +set (TEST_NAME test-core) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-timezone-file +set (TEST_NAME test-timezone-file) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-timezone-geoclue +set (TEST_NAME test-timezone-geoclue) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) + +# test-timezones +set (TEST_NAME test-timezones) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) + +# test-clock +set (TEST_NAME test-clock) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-formatter +set (TEST_NAME test-formatter) +add_executable (${TEST_NAME} test-formatter.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-planner +set (TEST_NAME test-planner) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-planner-eds +set (TEST_NAME test-planner-eds) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-locations +set (TEST_NAME test-locations) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) diff --git a/tests/geoclue-fixture.h b/tests/geoclue-fixture.h new file mode 100644 index 0000000..890204a --- /dev/null +++ b/tests/geoclue-fixture.h @@ -0,0 +1,142 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include + +class GeoclueFixture : public GlibFixture +{ + private: + + typedef GlibFixture super; + + GDBusConnection * bus = nullptr; + + protected: + + DbusTestService * service = nullptr; + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_master = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + const std::string timezone_1 = "America/Denver"; + + void SetUp () + { + super::SetUp(); + + GError * error = nullptr; + const gchar * const client_path = "/org/freedesktop/Geoclue/Master/client0"; + GString * gstr = g_string_new (nullptr); + + service = dbus_test_service_new (nullptr); + mock = dbus_test_dbus_mock_new ("org.freedesktop.Geoclue.Master"); + + obj_master = dbus_test_dbus_mock_get_object (mock, + "/org/freedesktop/Geoclue/Master", + "org.freedesktop.Geoclue.Master", + nullptr); + g_string_printf (gstr, "ret = '%s'", client_path); + dbus_test_dbus_mock_object_add_method (mock, obj_master, nullptr, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error); + + obj_client = dbus_test_dbus_mock_get_object (mock, client_path, "org.freedesktop.Geoclue.MasterClient", nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error); + dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "AddressStart", nullptr, nullptr, "", &error); + dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue", "AddReference", nullptr, nullptr, "", &error); + g_string_printf (gstr, "ret = (1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", timezone_1.c_str()); + dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error); + + dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); + dbus_test_service_start_tasks(service); + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_set_exit_on_close (bus, FALSE); + g_object_add_weak_pointer (G_OBJECT(bus), (gpointer*)&bus); + + g_string_free (gstr, TRUE); + } + + virtual void TearDown () + { + g_clear_object (&mock); + g_clear_object (&service); + g_object_unref (bus); + + unsigned int cleartry = 0; + while (bus != nullptr && cleartry < 10) + { + wait_msec (100); + cleartry++; + } + + // I've looked and can't find where this extra ref is coming from. + // is there an unbalanced ref to the bus in the test harness?! + while (bus != NULL) + { + g_object_unref (bus); + wait_msec (1000); + } + + super::TearDown (); + } + +private: + + struct EmitAddressChangedData + { + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + std::string timezone; + EmitAddressChangedData(DbusTestDbusMock * mock_, + DbusTestDbusMockObject * obj_client_, + const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + }; + + static gboolean emit_address_changed_idle (gpointer gdata) + { + auto data = static_cast(gdata); + auto fmt = g_strdup_printf ("(1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", data->timezone.c_str()); + + GError * error = nullptr; + dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, + "org.freedesktop.Geoclue.Address", + "AddressChanged", + G_VARIANT_TYPE("(ia{ss}(idd))"), + g_variant_new_parsed (fmt), + &error); + if (error) + { + g_warning("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + g_free (fmt); + delete data; + return G_SOURCE_REMOVE; + } + +public: + + void setGeoclueTimezoneOnIdle (const std::string& newZone) + { + g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, newZone.c_str())); + } + +}; + diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h new file mode 100644 index 0000000..043b7e3 --- /dev/null +++ b/tests/glib-fixture.h @@ -0,0 +1,131 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include + +class GlibFixture : public ::testing::Test +{ + private: + + //GLogFunc realLogHandler; + + protected: + + std::map logCounts; + + void testLogCount (GLogLevelFlags log_level, int expected G_GNUC_UNUSED) + { +#if 0 + EXPECT_EQ (expected, logCounts[log_level]); +#endif + + logCounts.erase (log_level); + } + + private: + + static void default_log_handler (const gchar * log_domain, + GLogLevelFlags log_level, + const gchar * message, + gpointer self) + { + g_print ("%s - %d - %s\n", log_domain, (int)log_level, message); + static_cast(self)->logCounts[log_level]++; + } + + protected: + + virtual void SetUp () + { + loop = g_main_loop_new (NULL, FALSE); + + //g_log_set_default_handler (default_log_handler, this); + + // only use local, temporary settings + g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); + g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); + g_debug ("SCHEMA_DIR is %s", SCHEMA_DIR); + } + + virtual void TearDown() + { +#if 0 + // confirm there aren't any unexpected log messages + EXPECT_EQ (0, logCounts[G_LOG_LEVEL_ERROR]); + EXPECT_EQ (0, logCounts[G_LOG_LEVEL_CRITICAL]); + EXPECT_EQ (0, logCounts[G_LOG_LEVEL_WARNING]); + EXPECT_EQ (0, logCounts[G_LOG_LEVEL_MESSAGE]); + EXPECT_EQ (0, logCounts[G_LOG_LEVEL_INFO]); +#endif + + // revert to glib's log handler + //g_log_set_default_handler (realLogHandler, this); + + g_clear_pointer (&loop, g_main_loop_unref); + } + + private: + + static gboolean + wait_for_signal__timeout (gpointer name) + { + g_error ("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); + return G_SOURCE_REMOVE; + } + + static gboolean + wait_msec__timeout (gpointer loop) + { + g_main_loop_quit (static_cast(loop)); + return G_SOURCE_CONTINUE; + } + + protected: + + /* convenience func to loop while waiting for a GObject's signal */ + void wait_for_signal (gpointer o, const gchar * signal, const int timeout_seconds=5) + { + // wait for the signal or for timeout, whichever comes first + const auto handler_id = g_signal_connect_swapped (o, signal, + G_CALLBACK(g_main_loop_quit), + loop); + const auto timeout_id = g_timeout_add_seconds (timeout_seconds, + wait_for_signal__timeout, + loop); + g_main_loop_run (loop); + g_source_remove (timeout_id); + g_signal_handler_disconnect (o, handler_id); + } + + /* convenience func to loop for N msec */ + void wait_msec (int msec=50) + { + const auto id = g_timeout_add (msec, wait_msec__timeout, loop); + g_main_loop_run (loop); + g_source_remove (id); + } + + GMainLoop * loop; +}; diff --git a/tests/test-core.cc b/tests/test-core.cc new file mode 100644 index 0000000..7ed38a9 --- /dev/null +++ b/tests/test-core.cc @@ -0,0 +1,148 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "glib-fixture.h" + +/*** +**** +***/ + +class CoreFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + virtual void SetUp () + { + super::SetUp (); + } + + virtual void TearDown () + { + super::TearDown (); + } +}; + +namespace +{ +struct EventLoop +{ + typedef std::function Handler; + + void stop() + { + stop_requested = true; + } + + void run() + { + while (!stop_requested) + { + std::unique_lock ul(guard); + wait_condition.wait_for( + ul, + std::chrono::milliseconds{500}, + [this]() { return handlers.size() > 0; }); + + std::cerr << "handlers.size() is " << handlers.size() << std::endl; + while (handlers.size() > 0) + { + std::cerr << "gaba begin" << std::endl; + handlers.front()(); + std::cerr << "gaba end" << std::endl; + handlers.pop(); + } + } + } + + void dispatch(const Handler& h) + { +std::cerr << "in dispatch" << std::endl; + std::lock_guard lg(guard); + handlers.push(h); + } + + bool stop_requested = false; + std::queue handlers; + std::mutex guard; + std::condition_variable wait_condition; +}; +} + + +TEST_F (CoreFixture, HelloWorld) +{ + // We instantiate an event loop and run it on a different thread than the main one. + EventLoop dispatcher; + std::thread dispatcher_thread{[&dispatcher]() { dispatcher.run(); }}; + std::thread::id dispatcher_thread_id = dispatcher_thread.get_id(); + + // The signal that we want to dispatch via the event loop. + core::Signal s; + + static const int expected_invocation_count = 10000; + + // Setup the connection. For each invocation we check that the id of the + // thread the handler is being called upon equals the thread that the + // event loop is running upon. + auto connection = s.connect( + [&dispatcher, dispatcher_thread_id](int value, double d) + { + std::cerr << "this is the lambda" << std::endl; + EXPECT_EQ(dispatcher_thread_id, + std::this_thread::get_id()); + + std::cout << d << std::endl; + + if (value == expected_invocation_count) + dispatcher.stop(); + }); + + // Route the connection via the dispatcher + connection.dispatch_via( + std::bind( + &EventLoop::dispatch, + std::ref(dispatcher), + std::placeholders::_1)); + + // Invoke the signal from the main thread. + for (unsigned int i = 1; i <= expected_invocation_count; i++) + s(i, 42.); + + if (dispatcher_thread.joinable()) + dispatcher_thread.join(); +} diff --git a/tests/test-skew.cc b/tests/test-skew.cc new file mode 100644 index 0000000..90c0164 --- /dev/null +++ b/tests/test-skew.cc @@ -0,0 +1,209 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include "Clock.h" +#include "MockClock.h" + +/*** +**** +***/ + +using unity::indicator::datetime::Clock; +using unity::indicator::datetime::MockClock; +using unity::indicator::datetime::SkewDetector; + +class SkewFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + self->system_bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->system_bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + std::shared_ptr mockClock; + GTestDBus * test_dbus; + GDBusConnection * system_bus; + + virtual void SetUp () + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + + // create a clock + GDateTime * now = g_date_time_new_now_local (); + mockClock.reset (new MockClock (now)); + g_date_time_unref (now); + } + + virtual void TearDown () + { + mockClock.reset(); + + // close the system bus + g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); + g_main_loop_run (loop); + g_clear_object (&system_bus); + + // tear down the test dbus + g_test_dbus_down (test_dbus); + g_clear_object (&test_dbus); + + super::TearDown (); + } + + public: + + void emitPrepareForSleep () + { + g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr), + NULL, + "/org/freedesktop/login1", // object path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + g_variant_new("(b)", FALSE), + NULL); + } +}; + + +/** + * A simple "hello world" style test. + */ +TEST_F (SkewFixture, CanInstantiate) +{ + SkewDetector skew (std::dynamic_pointer_cast(mockClock)); + wait_msec (500); // wait for the bus to set up +} + + +/** + * Confirm that changing the clock's timezone triggers a skew event + */ +TEST_F (SkewFixture, ChangingTimezonesTriggersEvent) +{ + SkewDetector skew (std::dynamic_pointer_cast(mockClock)); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + skew.skewDetected.connect([&skewed, this](){ + skewed = true; + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; + }); + + g_idle_add([](gpointer gclock){ + GDateTime * arbitrary = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + static_cast(gclock)->setLocaltime (arbitrary); + g_date_time_unref (arbitrary); + return G_SOURCE_REMOVE; + }, mockClock.get()); + + wait_msec (1000); + + EXPECT_TRUE (skewed); + GDateTime * expected = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + GDateTime * actual = mockClock->localtime(); + EXPECT_EQ (0, g_date_time_compare (expected, actual)); + g_date_time_unref (actual); + g_date_time_unref (expected); +} + +/** + * Confirm that a "PrepareForSleep" event wil trigger a skew event + */ +TEST_F (SkewFixture, PrepareForSleep) +{ + SkewDetector skew (std::dynamic_pointer_cast(mockClock)); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + skew.skewDetected.connect([&skewed, this](){ + skewed = true; + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; + }); + + g_idle_add ([](gpointer gself){ + static_cast(gself)->emitPrepareForSleep(); + return G_SOURCE_REMOVE; + }, this); + + wait_msec (1000); + EXPECT_TRUE(skewed); +} + + +/** + * Confirm that normal time passing doesn't trigger a skew event. + * that idling changing the clock's time triggers a skew event + */ +TEST_F (SkewFixture, IdleDoesNotTriggerEvent) +{ + SkewDetector skew (std::dynamic_pointer_cast(mockClock)); + wait_msec (500); // wait for the bus to set up + + bool skewed = false; + skew.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + //abort(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 4; + skew.intervalSec.set(intervalSec); + wait_msec (intervalSec * 2.5 * 1000); + EXPECT_FALSE (skewed); +} diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc new file mode 100644 index 0000000..cda53a6 --- /dev/null +++ b/tests/test-timezones.cc @@ -0,0 +1,122 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include "geoclue-fixture.h" + +#include + +#include // fopen() +#include // sync() + +using unity::indicator::datetime::LiveTimezones; + +typedef GeoclueFixture TimezonesFixture; + +#define TIMEZONE_FILE (SANDBOX "/timezone") + +namespace +{ + /* convenience func to set the timezone file */ + void set_file (const std::string& text) + { + FILE * fp = fopen (TIMEZONE_FILE, "w+"); + fprintf (fp, "%s\n", text.c_str()); + fclose (fp); + sync (); + } +} + + +TEST_F (TimezonesFixture, ManagerTest) +{ + std::string timezone_file = "America/New_York"; + std::string timezone_geo = "America/Denver"; + + set_file (timezone_file); + LiveTimezones z (TIMEZONE_FILE); + wait_msec (500); // wait for the bus to get set up + EXPECT_EQ (timezone_file, z.timezone.get()); + std::set zones = z.timezones.get(); + EXPECT_EQ (1, zones.size()); + EXPECT_EQ (1, zones.count(timezone_file)); + + bool zone_changed = false; + auto zone_connection = z.timezone.changed().connect([&zone_changed, this](const std::string&) { + zone_changed = true; + g_main_loop_quit (loop); + }); + + // start listening for a timezone change, then change the timezone + bool zones_changed = false; + auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { + zones_changed = true; + zones = timezones; + g_main_loop_quit (loop); + }); + + g_idle_add ([](gpointer gz) { + auto az = static_cast(gz); + g_message ("geolocation was %d", (int)az->geolocationEnabled.get()); + g_message ("turning geolocation on"); + az->geolocationEnabled.set(true); + return G_SOURCE_REMOVE; + }, &z); + + // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property + g_main_loop_run (loop); + EXPECT_TRUE (zones_changed); + EXPECT_EQ (timezone_file, z.timezone.get()); + EXPECT_EQ (2, zones.size()); + EXPECT_EQ (1, zones.count(timezone_file)); + EXPECT_EQ (1, zones.count(timezone_geo)); + zones_changed = false; + + // now tweak the geoclue value... the geoclue-detected timezone should change, + // causing the 'timezones' property to change + zone_changed = false; + zones_changed = false; + timezone_geo = "America/Chicago"; + setGeoclueTimezoneOnIdle (timezone_geo); + g_main_loop_run (loop); + EXPECT_FALSE (zone_changed); + EXPECT_TRUE (zones_changed); + EXPECT_EQ (timezone_file, z.timezone.get()); + EXPECT_EQ (2, zones.size()); + EXPECT_EQ (1, zones.count(timezone_file)); + EXPECT_EQ (1, zones.count(timezone_geo)); + + // now set the file value... this should change both the primary property and set property + zone_changed = false; + zones_changed = false; + timezone_file = "America/Los_Angeles"; + EXPECT_EQ (0, zones.count(timezone_file)); + g_idle_add ([](gpointer str) {set_file(static_cast(str)); return G_SOURCE_REMOVE;}, const_cast(timezone_file.c_str())); + g_main_loop_run (loop); + EXPECT_TRUE (zone_changed); + EXPECT_TRUE (zones_changed); + EXPECT_EQ (timezone_file, z.timezone.get()); + EXPECT_EQ (2, zones.size()); + EXPECT_EQ (1, zones.count(timezone_file)); + EXPECT_EQ (1, zones.count(timezone_geo)); + + + +} + + -- cgit v1.2.3 From ee64bb2698adfe27e55615a8856b0e2c78ad8469 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 14 Jan 2014 23:07:10 -0600 Subject: Function: add fully-tested ActionGroups, per-profile Menus, state object. Form: Add code annotations/comments. Remove dead code. Use Mir style guide. Todo: GSettings toggles, sync with new dbus-test-runner API, get GNOME Panel building again --- CMakeLists.txt | 2 +- include/datetime/actions-live.h | 57 ++++ include/datetime/actions.h | 70 +++++ include/datetime/appointment.h | 4 +- include/datetime/clock-mock.h | 28 +- include/datetime/clock.h | 25 +- include/datetime/date-time.h | 103 +++++-- include/datetime/dbus-shared.h | 37 +-- include/datetime/formatter.h | 29 +- include/datetime/locations-settings.h | 11 +- include/datetime/locations.h | 38 ++- include/datetime/menu.h | 82 ++++++ include/datetime/planner-eds.h | 2 +- include/datetime/planner-mock.h | 44 +++ include/datetime/planner.h | 3 + include/datetime/service.h | 72 +++++ include/datetime/settings-live.h | 57 ++++ include/datetime/settings-shared.h | 6 +- include/datetime/settings.h | 63 +++++ include/datetime/state.h | 68 +++++ include/datetime/timezone-file.h | 13 +- include/datetime/timezone-geoclue.h | 12 +- include/datetime/timezone.h | 4 +- include/datetime/timezones-live.h | 13 +- include/datetime/timezones.h | 4 +- include/datetime/utils.h | 28 +- panel/CMakeLists.txt | 2 + src/CMakeLists.txt | 11 +- src/actions-live.cpp | 84 ++++++ src/actions.cpp | 215 ++++++++++++++ src/clock-live.cpp | 84 +++--- src/clock.cpp | 32 +-- src/formatter-desktop.cpp | 258 ++++++++--------- src/formatter.cpp | 146 +++++----- src/locations-settings.cpp | 59 ++-- src/locations.cpp | 33 ++- src/main.c | 83 ------ src/main.cpp | 81 ++++++ src/menu.cpp | 518 ++++++++++++++++++++++++++++++++++ src/planner-eds.cpp | 283 ++++++++++--------- src/planner-eds.h | 58 ---- src/planner.h | 167 ----------- src/service.cpp | 140 +++++++++ src/settings-live.cpp | 293 +++++++++++++++++++ src/timezone-file.cpp | 18 +- src/timezone-geoclue.cpp | 66 ++--- src/timezones-live.cpp | 34 ++- src/utils.cpp | 139 +++++---- tests/CMakeLists.txt | 27 +- tests/Makefile.am.strings | 38 --- tests/actions-mock.h | 88 ++++++ tests/glib-fixture.h | 85 +++--- tests/planner-mock.c | 34 +-- tests/state-fixture.h | 75 +++++ tests/test-actions.cc | 173 ++++++++++++ tests/test-clock.cc | 176 ++++++------ tests/test-core.cc | 148 ---------- tests/test-dbus-fixture.h | 100 +++++++ tests/test-formatter.cc | 61 ++-- tests/test-indicator.cc | 92 ------ tests/test-locations.cc | 136 ++++----- tests/test-menus.cc | 406 ++++++++++++++++++++++++++ tests/test-planner-eds.cc | 70 ----- tests/test-planner.cc | 49 ++-- tests/test-skew.cc | 209 -------------- tests/test-timezone-file.cc | 60 ++-- tests/test-timezone-geoclue.cc | 28 +- tests/test-timezones.cc | 83 +++--- tests/test-utils.cc | 153 +++++----- 69 files changed, 3962 insertions(+), 2008 deletions(-) create mode 100644 include/datetime/actions-live.h create mode 100644 include/datetime/actions.h create mode 100644 include/datetime/menu.h create mode 100644 include/datetime/planner-mock.h create mode 100644 include/datetime/service.h create mode 100644 include/datetime/settings-live.h create mode 100644 include/datetime/settings.h create mode 100644 include/datetime/state.h create mode 100644 src/actions-live.cpp create mode 100644 src/actions.cpp delete mode 100644 src/main.c create mode 100644 src/main.cpp create mode 100644 src/menu.cpp delete mode 100644 src/planner-eds.h delete mode 100644 src/planner.h create mode 100644 src/service.cpp create mode 100644 src/settings-live.cpp delete mode 100644 tests/Makefile.am.strings create mode 100644 tests/actions-mock.h create mode 100644 tests/state-fixture.h create mode 100644 tests/test-actions.cc delete mode 100644 tests/test-core.cc create mode 100644 tests/test-dbus-fixture.h delete mode 100644 tests/test-indicator.cc create mode 100644 tests/test-menus.cc delete mode 100644 tests/test-planner-eds.cc delete mode 100644 tests/test-skew.cc (limited to 'tests') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fdd4a3..da97a6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ add_custom_target (cppcheck COMMAND cppcheck --enable=all -q --error-exitcode=2 ## set (CC_WARNING_ARGS " -Wall -Wshadow -Wextra -Wunused -Wformat=2 -Wno-missing-field-initializers") -set (CXX_WARNING_ARGS " -Wall -Wextra -pedantic") +set (CXX_WARNING_ARGS " -Wall -Wextra -pedantic -Wno-missing-field-initializers") include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories (${CMAKE_CURRENT_BINARY_DIR}/include) diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h new file mode 100644 index 0000000..2059a4d --- /dev/null +++ b/include/datetime/actions-live.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ACTIONS_LIVE_H +#define INDICATOR_DATETIME_ACTIONS_LIVE_H + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Production implentation of the Actions interface. + * + * Delegates URLs, sets the timezone via org.freedesktop.timedate1, etc. + * + * @see MockActions + */ +class LiveActions: public Actions +{ +public: + LiveActions(std::shared_ptr& state): Actions(state) {} + ~LiveActions() =default; + + void open_desktop_settings(); + void open_phone_settings(); + void open_phone_clock(); + void open_phone_planner(); + void open_planner_at(const DateTime&); + void open_calendar_at(const DateTime&); + void open_appointment(const std::string& uid); + void set_location(const std::string& zone, const std::string& name); + void set_calendar_date(const DateTime&); +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ACTIONS_H diff --git a/include/datetime/actions.h b/include/datetime/actions.h new file mode 100644 index 0000000..ed45c3c --- /dev/null +++ b/include/datetime/actions.h @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ACTIONS_H +#define INDICATOR_DATETIME_ACTIONS_H + +#include +#include + +#include // shared_ptr +#include + +#include // GSimpleActionGroup + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * Interface for all the actions that can be activated by users via the menu. + */ +class Actions +{ +public: + virtual void open_desktop_settings() =0; + virtual void open_phone_settings() =0; + virtual void open_phone_clock_app() =0; + virtual void open_planner() =0; + virtual void open_planner_at(const DateTime&) =0; + virtual void open_appointment(const std::string& uid) =0; + virtual void set_location(const std::string& zone, const std::string& name)=0; + virtual void set_calendar_date(const DateTime&) =0; + + GActionGroup* action_group() { return G_ACTION_GROUP(m_actions); } + std::shared_ptr state() { return m_state; } + +protected: + Actions(std::shared_ptr& state); + virtual ~Actions(); + +private: + std::shared_ptr m_state; + GSimpleActionGroup* m_actions = nullptr; + + // we've got raw pointers in here, so disable copying + Actions(const Actions&) =delete; + Actions& operator=(const Actions&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ACTIONS_H diff --git a/include/datetime/appointment.h b/include/datetime/appointment.h index 098b0d0..e034c08 100644 --- a/include/datetime/appointment.h +++ b/include/datetime/appointment.h @@ -28,7 +28,9 @@ namespace indicator { namespace datetime { /** - * PODS representing a calendar appointment + * \brief Plain Old Data Structure that represents a calendar appointment. + * + * @see Planner */ struct Appointment { diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index 814b29a..19a859b 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -30,30 +30,28 @@ namespace datetime { **** ***/ +/** + * \brief A clock that uses a client-provided time instead of the system time. + */ class MockClock: public Clock { public: + MockClock(const DateTime& dt): m_localtime(dt) {} + ~MockClock() =default; - MockClock(GDateTime * dt) { setLocaltime(dt); } - - ~MockClock() { - g_clear_pointer(&localtime_, g_date_time_unref); - } - - GDateTime* localtime() const { - g_assert (localtime_ != nullptr); - return g_date_time_ref(localtime_); - } + DateTime localtime() const { return m_localtime; } - void setLocaltime(GDateTime* dt) { - g_clear_pointer(&localtime_, g_date_time_unref); - localtime_ = g_date_time_ref(dt); + void set_localtime(const DateTime& dt) { + const auto old_day = m_localtime.day_of_year(); + m_localtime = dt; skewDetected(); + const auto new_day = m_localtime.day_of_year(); + if (old_day != new_day) + dateChanged(); } private: - - GDateTime * localtime_ = nullptr; + DateTime m_localtime; }; } // namespace datetime diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 978be27..c8e6c16 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -20,12 +20,12 @@ #ifndef INDICATOR_DATETIME_CLOCK_H #define INDICATOR_DATETIME_CLOCK_H +#include #include #include #include -#include #include #include @@ -45,9 +45,10 @@ class Clock { public: virtual ~Clock(); - virtual GDateTime* localtime() const = 0; - core::Property > timezones; + virtual DateTime localtime() const =0; + core::Property> timezones; core::Signal<> skewDetected; + core::Signal<> dateChanged; protected: Clock(); @@ -56,12 +57,13 @@ private: static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_system_bus = nullptr; + unsigned int m_sleep_subscription_id = 0; + + // we've got raw pointers in here, so disable copying Clock(const Clock&) =delete; Clock& operator=(const Clock&) =delete; - - GCancellable * cancellable_ = nullptr; - GDBusConnection * system_bus_ = nullptr; - unsigned int sleep_subscription_id_ = 0; }; /*** @@ -71,16 +73,17 @@ private: /** * \brief A live clock that provides the actual system time. * - * Adds another clock skew detection test: wakes up every - * skewTestIntervalSec seconds to see how much time has passed - * since the last time it checked. + * This subclass also adds another clock skew detection test: + * it wakes up every skewTestIntervalSec seconds to see how + * much time has passed since the last wakeup. If the answer + * isn't what it expected, the skewDetected signal is triggered. */ class LiveClock: public Clock { public: LiveClock (const std::shared_ptr& zones); virtual ~LiveClock(); - virtual GDateTime* localtime() const; + virtual DateTime localtime() const; core::Property skewTestIntervalSec; private: diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index bbcd00a..5b9421f 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -22,6 +22,7 @@ #include // GDateTime +#include // time_t #include // std::shared_ptr namespace unity { @@ -29,61 +30,111 @@ namespace indicator { namespace datetime { /** - * C++ wrapper class for GDateTime + * \brief A simple C++ wrapper for GDateTime to simplify ownership/refcounts */ class DateTime { public: - GDateTime* get() const - { - return dt_.get(); + explicit DateTime(GDateTime* in=nullptr) { reset(in); } + + explicit DateTime(time_t t) { reset(g_date_time_new_from_unix_local(t)); } + + static DateTime NowLocal() { + GDateTime * gdt = g_date_time_new_now_local(); + DateTime dt(gdt); + g_date_time_unref(gdt); + return dt; + } + + DateTime to_timezone(const std::string& zone) const { + auto gtz = g_time_zone_new(zone.c_str()); + auto gdt = g_date_time_to_timezone(get(), gtz); + DateTime dt(gdt); + g_time_zone_unref(gtz); + g_date_time_unref(gdt); + return dt; } - GDateTime* operator()() const - { + + GDateTime* get() const { + g_assert(m_dt); + return m_dt.get(); + } + + GDateTime* operator()() const { return get(); } - void set (GDateTime* in) { - auto deleter = [](GDateTime* dt){g_date_time_unref(dt);}; - dt_ = std::shared_ptr(g_date_time_ref(in), deleter); + + std::string format(const std::string& fmt) const { + auto str = g_date_time_format(get(), fmt.c_str()); + std::string ret = str; + g_free(str); + return ret; + } + + int day_of_month() const { return g_date_time_get_day_of_month(get()); } + + int64_t to_unix() const { return g_date_time_to_unix(get()); } + + int day_of_year() const { return m_dt ? g_date_time_get_day_of_year(get()) : -1; } + + void reset(GDateTime* in=nullptr) { + if (in) { + auto deleter = [](GDateTime* dt){g_date_time_unref(dt);}; + m_dt = std::shared_ptr(g_date_time_ref(in), deleter); + g_assert(m_dt); + } else { + m_dt.reset(); + } } - DateTime& operator=(GDateTime* in) - { - set (in); + DateTime& operator=(GDateTime* in) { + reset(in); return *this; } - DateTime& operator=(const DateTime& in) - { - set (in.get()); + DateTime& operator=(const DateTime& in) { + m_dt = in.m_dt; return *this; } - bool operator<(const DateTime& that) const - { - return g_date_time_compare (get(), that.get()) < 0; + gint64 difference(const DateTime& that) const { + const auto dt = get(); + const auto tdt = that.get(); + + gint64 ret; + if (dt && tdt) + ret = g_date_time_difference(dt, tdt); + else if (dt) + ret = to_unix(); + else if (tdt) + ret = that.to_unix(); + else + ret = 0; + return ret; + } + + bool operator<(const DateTime& that) const { + return g_date_time_compare(get(), that.get()) < 0; } - bool operator!=(const DateTime& that) const - { - return !(*this == that); + bool operator!=(const DateTime& that) const { + // return true if this isn't set, or if it's not equal + return (!m_dt) || !(*this == that); } - bool operator==(const DateTime& that) const - { + bool operator==(const DateTime& that) const { GDateTime * dt = get(); GDateTime * tdt = that.get(); if (!dt && !tdt) return true; if (!dt || !tdt) return false; - return g_date_time_compare (get(), that.get()) == 0; + return g_date_time_compare(get(), that.get()) == 0; } private: - - std::shared_ptr dt_; + std::shared_ptr m_dt; }; } // namespace datetime diff --git a/include/datetime/dbus-shared.h b/include/datetime/dbus-shared.h index 24319e3..c5ff6ab 100644 --- a/include/datetime/dbus-shared.h +++ b/include/datetime/dbus-shared.h @@ -1,24 +1,25 @@ /* -An indicator to show date and time information. + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Ted Gould + * Charles Kerr + */ -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ #define BUS_NAME "com.canonical.indicator.datetime" + #define BUS_PATH "/com/canonical/indicator/datetime" diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index 66dc212..09ed035 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -37,7 +37,26 @@ class DateTime; ***/ /** - * \brief Provides the right time format strings based on the profile and user's settings + * \brief Provide the strftime() format strings + * + * This mission's been moved out into its own class because there are + * a lot of options and edge cases: + * + * - The default time format can change based on the locale. + * + * - The user's settings can change or completely override the format string. + * + * - The time formats are different on the Phone and Desktop profiles. + * + * - The time format string in the Locations' menuitems uses (mostly) + * the same time format as the header, except for some changes. + * + * - The 'current time' format string in the Locations' menuitems also + * prepends the string 'Yesterday' or 'Today' if it differs from the + * local time, so Formatter needs to have a Clock for its state. + * + * So the Formatter monitors system settings, the current timezone, etc. + * and upate its time format properties appropriately. */ class Formatter { @@ -51,7 +70,8 @@ public: /** \brief Signal to denote when the relativeFormat has changed. When this is emitted, clients will want to rebuild their - menuitems that contain relative time strings */ + menuitems that contain relative time strings + (ie, the Appointments and Locations menuitems) */ core::Signal<> relativeFormatChanged; /** \brief Generate a relative time format for some time (or time range) @@ -60,19 +80,18 @@ public: std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; protected: - Formatter(const std::shared_ptr&); virtual ~Formatter(); /** \brief Returns true if the current locale prefers 12h display instead of 24h */ static bool is_locale_12h(); - static const char * getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); + static const char* getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); /** \brief Translate the string based on LC_TIME instead of LC_MESSAGES. The intent of this is to let users set LC_TIME to override their other locale settings when generating time format string */ - static const char * T_(const char * fmt); + static const char* T_(const char * fmt); private: diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h index d343fe3..eaabf73 100644 --- a/include/datetime/locations-settings.h +++ b/include/datetime/locations-settings.h @@ -32,8 +32,8 @@ namespace datetime { class Timezones; /** - * \brief An ordered list of Location objects found from - * the system timezone and from the user's GSettings + * \brief Settings implentation which builds its list from the + * user's GSettings and from the Timezones passed in the ctor. */ class SettingsLocations: public Locations { @@ -42,11 +42,12 @@ public: * @param[in] schemaId the settings schema to load * @param[in] timezones the timezones to always show first in the list */ - SettingsLocations (const std::string& schemaId, const std::shared_ptr& timezones); + SettingsLocations (const std::string& schemaId, + const std::shared_ptr& timezones); protected: - std::unique_ptr> settings_; - std::shared_ptr timezones_; + std::unique_ptr> m_settings; + std::shared_ptr m_timezones; private: static void onSettingsChanged (gpointer gself); diff --git a/include/datetime/locations.h b/include/datetime/locations.h index a06d1cc..ee67615 100644 --- a/include/datetime/locations.h +++ b/include/datetime/locations.h @@ -20,6 +20,8 @@ #ifndef INDICATOR_DATETIME_LOCATIONS_H #define INDICATOR_DATETIME_LOCATIONS_H +#include + #include #include @@ -31,28 +33,42 @@ namespace datetime { /** * \brief A physical place and its timezone; eg, "America/Chicago" + "Oklahoma City" + * + * @see Locations */ -struct Location +class Location { - /** timezone; eg, "America/Chicago" */ - std::string zone; - - /* human-readable location name; eg, "Oklahoma City" */ - std::string name; +public: + const std::string& zone() const { return m_zone; } - /** offset from UTC in microseconds */ - int64_t offset = 0; + const std::string& name() const { return m_name; } bool operator== (const Location& that) const { - return (name == that.name) && (zone == that.zone) && (offset == that.offset); + return (name() == that.name()) && + (zone() == that.zone()) && + (m_offset == that.m_offset); } Location (const std::string& zone, const std::string& name); + +private: + + /** timezone; eg, "America/Chicago" */ + std::string m_zone; + + /* human-readable location name; eg, "Oklahoma City" */ + std::string m_name; + + /** offset from UTC in microseconds */ + int64_t m_offset = 0; }; /** - * A container for an ordered list of Locations. + * Container which holds an ordered list of Locations + * + * @see Location + * @see State */ class Locations { @@ -61,7 +77,7 @@ public: virtual ~Locations() =default; /** \brief an ordered list of Location items */ - core::Property > locations; + core::Property> locations; }; } // namespace datetime diff --git a/include/datetime/menu.h b/include/datetime/menu.h new file mode 100644 index 0000000..0bc3781 --- /dev/null +++ b/include/datetime/menu.h @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_MENU_H +#define INDICATOR_DATETIME_MENU_H + +#include +#include + +#include // std::shared_ptr +#include + +#include // GMenuModel + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief A menu for a specific profile; eg, Desktop or Phone. + * + * @see MenuFactory + */ +class Menu +{ +public: + enum Profile { Desktop, DesktopGreeter, Phone, PhoneGreeter, NUM_PROFILES }; + enum Section { Calendar, Appointments, Locations, Settings, NUM_SECTIONS }; + const std::string& name() const { return m_name; } + Profile profile() const { return m_profile; } + GMenuModel* menu_model() { return G_MENU_MODEL(m_menu); } + +protected: + Menu (Profile profile_in, const std::string& name_in): m_profile(profile_in), m_name(name_in) {} + virtual ~Menu() =default; + GMenu* m_menu = nullptr; + +private: + const Profile m_profile; + const std::string m_name; + + // we've got raw pointers in here, so disable copying + Menu(const Menu&) =delete; + Menu& operator=(const Menu&) =delete; +}; + +/** + * \brief Builds a Menu for a given state and profile + */ +class MenuFactory +{ +public: + MenuFactory (std::shared_ptr& actions, std::shared_ptr& state); + std::shared_ptr buildMenu(Menu::Profile profile); + std::shared_ptr state() { return m_state; } + +private: + std::shared_ptr m_actions; + std::shared_ptr m_state; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_MENU_H diff --git a/include/datetime/planner-eds.h b/include/datetime/planner-eds.h index 43f222e..f3abce0 100644 --- a/include/datetime/planner-eds.h +++ b/include/datetime/planner-eds.h @@ -39,7 +39,7 @@ public: private: class Impl; - std::unique_ptr impl_; + std::unique_ptr p; }; } // namespace datetime diff --git a/include/datetime/planner-mock.h b/include/datetime/planner-mock.h new file mode 100644 index 0000000..bb3ff53 --- /dev/null +++ b/include/datetime/planner-mock.h @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_PLANNER_MOCK_H +#define INDICATOR_DATETIME_PLANNER_MOCK_H + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Planner which does nothing on its own and requires + * its client to set its appointments property. + */ +class MockPlanner: public Planner +{ +public: + MockPlanner() =default; + virtual ~MockPlanner() =default; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_PLANNER_MOCK_H diff --git a/include/datetime/planner.h b/include/datetime/planner.h index 4d27a2b..198b6fa 100644 --- a/include/datetime/planner.h +++ b/include/datetime/planner.h @@ -33,6 +33,9 @@ namespace datetime { /** * \brief Simple appointment book + * + * @see EdsPlanner + * @see State */ class Planner { diff --git a/include/datetime/service.h b/include/datetime/service.h new file mode 100644 index 0000000..c7171b7 --- /dev/null +++ b/include/datetime/service.h @@ -0,0 +1,72 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_EXPORTER_H +#define INDICATOR_DATETIME_EXPORTER_H + +#include + +#include + +#include // GActionGroup + +#include // std::shared_ptr +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Exports actions and menus to DBus. + */ +class Service +{ +public: + Service() =default; + ~Service(); + + core::Signal<> name_lost; + + void publish (GActionGroup* actions, std::vector>& menus); + +private: + static void on_bus_acquired(GDBusConnection*, const gchar *name, gpointer gthis); + void on_bus_acquired(GDBusConnection*, const gchar *name); + + static void on_name_lost(GDBusConnection*, const gchar *name, gpointer gthis); + void on_name_lost(GDBusConnection*, const gchar *name); + + std::set m_exported_menu_ids; + guint m_own_id = 0; + guint m_exported_actions_id = 0; + GDBusConnection * m_dbus_connection = nullptr; + GActionGroup* m_actions = nullptr; + std::vector> m_menus; + + // we've got raw pointers and gsignal tags in here, so disable copying + Service(const Service&) =delete; + Service& operator=(const Service&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_EXPORTER_H diff --git a/include/datetime/settings-live.h b/include/datetime/settings-live.h new file mode 100644 index 0000000..44e27b0 --- /dev/null +++ b/include/datetime/settings-live.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include // parent class + +#include // GSettings + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief #Settings implementation which uses GSettings. + */ +class LiveSettings: public Settings +{ +public: + LiveSettings(); + virtual ~LiveSettings(); + +private: + void update_show_clock(); + + void update_key(const std::string& key); + static void on_changed(GSettings*, gchar*, gpointer); + + GSettings* m_settings; + + // we've got a raw pointer here, so disable copying + LiveSettings(const LiveSettings&) =delete; + LiveSettings& operator=(const LiveSettings&) =delete; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H diff --git a/include/datetime/settings-shared.h b/include/datetime/settings-shared.h index 896db95..17a8ef0 100644 --- a/include/datetime/settings-shared.h +++ b/include/datetime/settings-shared.h @@ -18,8 +18,8 @@ * Charles Kerr */ -#ifndef __DATETIME_SETTINGS_SHARED_H__ -#define __DATETIME_SETTINGS_SHARED_H__ +#ifndef INDICATOR_DATETIME_SETTINGS_SHARED +#define INDICATOR_DATETIME_SETTINGS_SHARED typedef enum { @@ -46,4 +46,4 @@ TimeFormatMode; #define SETTINGS_LOCATIONS_S "locations" #define SETTINGS_TIMEZONE_NAME_S "timezone-name" -#endif /* __DATETIME_SETTINGS_SHARED_H__ */ +#endif // INDICATOR_DATETIME_SETTINGS_SHARED diff --git a/include/datetime/settings.h b/include/datetime/settings.h new file mode 100644 index 0000000..3d4bc33 --- /dev/null +++ b/include/datetime/settings.h @@ -0,0 +1,63 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Interface that represents user-configurable settings. + * + * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml + * For more information. + */ +class Settings +{ +public: + Settings() =default; + virtual ~Settings() =default; + + core::Property time_format_mode; + core::Property show_clock; + core::Property show_day; + core::Property show_year; + core::Property show_seconds; + core::Property custom_time_format; + core::Property show_calendar; + core::Property show_events; + core::Property show_locations; + core::Property show_auto_detected_location; + core::Property> locations; + core::Property timezone_name; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_SETTINGS_H diff --git a/include/datetime/state.h b/include/datetime/state.h new file mode 100644 index 0000000..a82bb4b --- /dev/null +++ b/include/datetime/state.h @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_STATE_H +#define INDICATOR_DATETIME_STATE_H + +#include +#include +#include +#include + +#include + +#include // std::shared_ptr + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Aggregates all the classes that represent the backend state. + * + * This is where the app comes together. It's a model that aggregates + * all of the backend appointments/alarms, locations, timezones, + * system time, and so on. The "view" code (ie, the Menus) need to + * respond to Signals from the State and update themselves accordingly. + * + * @see Menu + * @see MenuFactory + * @see Timezones + * @see Clock + * @see Planner + * @see Locations + */ +struct State +{ + std::shared_ptr timezones; + std::shared_ptr clock; + std::shared_ptr planner; + std::shared_ptr locations; + + core::Property show_events; + core::Property show_clock; + core::Property calendar_day; + core::Property show_week_numbers; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_STATE_H diff --git a/include/datetime/timezone-file.h b/include/datetime/timezone-file.h index 39a3d83..7f47df6 100644 --- a/include/datetime/timezone-file.h +++ b/include/datetime/timezone-file.h @@ -43,15 +43,18 @@ public: private: void setFilename(const std::string& filename); - void clear(); static void onFileChanged(gpointer gself); + void clear(); void reload(); - std::string filename_; - GFileMonitor * monitor_ = nullptr; - unsigned long monitor_handler_id_ = 0; -}; + std::string m_filename; + GFileMonitor * m_monitor = nullptr; + unsigned long m_monitor_handler_id = 0; + // we have raw pointers and glib tags in here, so disable copying + FileTimezone(const FileTimezone&) =delete; + FileTimezone& operator=(const FileTimezone&) =delete; +}; } // namespace datetime } // namespace indicator diff --git a/include/datetime/timezone-geoclue.h b/include/datetime/timezone-geoclue.h index 382b3cc..4a5b726 100644 --- a/include/datetime/timezone-geoclue.h +++ b/include/datetime/timezone-geoclue.h @@ -50,10 +50,14 @@ private: void setTimezoneFromAddressVariant (GVariant*); static GVariant * call_finish (GObject*, GAsyncResult*); - GCancellable * cancellable_ = nullptr; - GDBusConnection * connection_ = nullptr; - std::string client_object_path_; - guint signal_subscription_ = 0; + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_connection = nullptr; + std::string m_client_object_path; + guint m_signal_subscription = 0; + + // we've got pointers and gsignal tags in here, so don't allow copying + GeoclueTimezone(const GeoclueTimezone&) =delete; + GeoclueTimezone& operator=(const GeoclueTimezone&) =delete; }; diff --git a/include/datetime/timezone.h b/include/datetime/timezone.h index 218e219..ffa5a84 100644 --- a/include/datetime/timezone.h +++ b/include/datetime/timezone.h @@ -28,11 +28,11 @@ namespace unity { namespace indicator { namespace datetime { -/** \brief Base class for objects that use various means to detect the system's timezone */ +/** \brief Base a timezone, such as "America/Chicago". */ class Timezone { protected: - Timezone() {} + Timezone() =default; public: //virtual ~Timezone() {} diff --git a/include/datetime/timezones-live.h b/include/datetime/timezones-live.h index 3075bd8..5f2cb3e 100644 --- a/include/datetime/timezones-live.h +++ b/include/datetime/timezones-live.h @@ -31,7 +31,7 @@ namespace indicator { namespace datetime { /** - * \brief Timezones object that uses a #FileTimezone and #GeoclueTimezone + * \brief #Timezones object that uses a #FileTimezone and #GeoclueTimezone * to detect what timezone we're in */ class LiveTimezones: public Timezones @@ -40,13 +40,14 @@ public: LiveTimezones(const std::string& filename); /** \brief Whether or not to track location by IP address */ - core::Property geolocationEnabled = core::Property(false); + core::Property geolocation_enabled = core::Property(false); private: - FileTimezone file_; - std::shared_ptr geo_; - void updateGeolocation(); - void updateTimezones(); + void update_geolocation(); + void update_timezones(); + + FileTimezone m_file; + std::shared_ptr m_geo; }; } // namespace datetime diff --git a/include/datetime/timezones.h b/include/datetime/timezones.h index 0b97683..10c4e97 100644 --- a/include/datetime/timezones.h +++ b/include/datetime/timezones.h @@ -28,13 +28,11 @@ namespace unity { namespace indicator { namespace datetime { -/** \brief Aggregates one or more timezone detectors and decides which to use */ +/** \brief Aggregates one or more timezone detectors and decides which to give precedence to */ class Timezones { public: - Timezones() =default; - virtual ~Timezones() =default; /** diff --git a/include/datetime/utils.h b/include/datetime/utils.h index bd2e132..fbc80d7 100644 --- a/include/datetime/utils.h +++ b/include/datetime/utils.h @@ -20,8 +20,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef __DATETIME_UTILS_H__ -#define __DATETIME_UTILS_H__ +#ifndef INDICATOR_DATETIME_UTILS_H +#define INDICATOR_DATETIME_UTILS_H #include #include /* GSettings */ @@ -37,31 +37,9 @@ void split_settings_location (const char * location, gchar * get_current_zone_name (const char * location, GSettings * settings); -#if 0 -gchar* join_date_and_time_format_strings (const char * date_fmt, - const char * time_fmt); -/*** -**** -***/ - -const gchar * get_terse_time_format_string (GDateTime * time); - -const gchar * get_terse_header_time_format_string (void); - -const gchar * get_full_time_format_string (GSettings * settings); - -gchar * generate_terse_format_string_at_time (GDateTime * now, - GDateTime * time); - -gchar * generate_full_format_string (gboolean show_day, - gboolean show_date, - gboolean show_year, - GSettings * settings); -#endif - gchar * generate_full_format_string_at_time (GDateTime * now, GDateTime * time); G_END_DECLS -#endif +#endif /* INDICATOR_DATETIME_UTILS_H */ diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 4ac3289..c165326 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -2,6 +2,8 @@ set (PANEL_LIB "indicator-datetime") add_definitions (-DPKGDATADIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") + add_library (${PANEL_LIB} SHARED datetime-prefs.c datetime-prefs-locations.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c847ff..976adc3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,13 +12,18 @@ if (BUILD_PANEL) endif () add_library (${SERVICE_LIB} STATIC + actions.cpp + actions-live.cpp clock.cpp clock-live.cpp formatter.cpp formatter-desktop.cpp locations.cpp locations-settings.cpp + menu.cpp planner-eds.cpp + service.cpp + settings-live.cpp timezone-file.cpp timezone-geoclue.cpp timezones-live.cpp @@ -27,9 +32,9 @@ include_directories (${CMAKE_SOURCE_DIR}) link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) -#add_executable (${SERVICE_EXEC} main.c) -#target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) -#install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) +add_executable (${SERVICE_EXEC} main.cpp) +target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) +install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) # common properties #set_property (TARGET ${SERVICE_LIB} ${SERVICE_EXEC} diff --git a/src/actions-live.cpp b/src/actions-live.cpp new file mode 100644 index 0000000..08e1466 --- /dev/null +++ b/src/actions-live.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +void LiveActions::open_desktop_settings() +{ + g_message ("%s", G_STRFUNC); +} + +void LiveActions::open_phone_settings() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_phone_clock() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_phone_planner() +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_planner_at(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_calendar_at(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + +void LiveActions::open_appointment(const std::string& uid) +{ + g_message("%s - %s", G_STRFUNC, uid.c_str()); +} + +void LiveActions::set_location(const std::string& zone, const std::string& name) +{ + g_message("%s - %s %s", G_STRFUNC, zone.c_str(), name.c_str()); +} + +void LiveActions::set_calendar_date(const DateTime&) +{ + g_message("%s", G_STRFUNC); +} + + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/actions.cpp b/src/actions.cpp new file mode 100644 index 0000000..4efe950 --- /dev/null +++ b/src/actions.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include // split_settings_location() + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +namespace +{ + +void on_desktop_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast(gself)->open_desktop_settings(); +} + +void on_phone_settings_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast(gself)->open_phone_settings(); +} + +void on_phone_clock_activated(GSimpleAction * /*action*/, + GVariant * /*param*/, + gpointer gself) +{ + static_cast(gself)->open_phone_clock_app(); +} + +void on_activate_appointment(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + const auto uid = g_variant_get_string(param, nullptr); + auto self = static_cast(gself); + + g_return_if_fail(uid && *uid); + + // find url of the upcoming appointment with this uid + for (auto& appt : self->state()->planner->upcoming.get()) + { + if (appt.uid == uid) + { + const auto url = appt.url; + g_debug("%s: uid[%s] -> url[%s]", G_STRFUNC, uid, url.c_str()); + self->open_appointment(url); + break; + } + } +} + +void on_activate_planner(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + const auto at = g_variant_get_int64(param); + auto self = static_cast(gself); + + if (at) + { + auto gdt = g_date_time_new_from_unix_local(at); + self->open_planner_at(DateTime(gdt)); + g_date_time_unref(gdt); + } + else // no time specified... + { + self->open_planner(); + } +} + +void on_set_location(GSimpleAction * /*action*/, + GVariant * param, + gpointer gself) +{ + char * zone; + char * name; + split_settings_location(g_variant_get_string(param, nullptr), &zone, &name); + static_cast(gself)->set_location(zone, name); + g_free(name); + g_free(zone); +} + +static void on_calendar_activated(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) +{ + const time_t t = g_variant_get_int64(state); + + g_return_if_fail(t != 0); + + static_cast(gself)->set_calendar_date(DateTime(t)); +} + +GVariant* create_default_header_state() +{ + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_string("accessible-desc")); + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string("label")); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string("title")); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(true)); + return g_variant_builder_end(&b); +} + +GVariant* create_calendar_state(std::shared_ptr& state) +{ + gboolean days[32] = { 0 }; + for(const auto& appt : state->planner->thisMonth.get()) + days[appt.begin.day_of_month()] = true; + + GVariantBuilder day_builder; + g_variant_builder_init(&day_builder, G_VARIANT_TYPE("ai")); + for (int i=0; icalendar_day.get().to_unix()); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + key = "show-week-numbers"; + v = g_variant_new_boolean(state->show_week_numbers.get()); + g_variant_builder_add(&dict_builder, "{sv}", key, v); + + return g_variant_builder_end(&dict_builder); +} +} // anonymous namespace + +/*** +**** +***/ + +Actions::Actions(std::shared_ptr& state): + m_state(state), + m_actions(g_simple_action_group_new()) +{ + GActionEntry entries[] = { + { "activate-desktop-settings", on_desktop_settings_activated }, + { "activate-phone-settings", on_phone_settings_activated }, + { "activate-phone-clock-app", on_phone_clock_activated }, + { "activate-appointment", on_activate_appointment, "s", nullptr }, + { "activate-planner", on_activate_planner, "x", nullptr }, + { "set-location", on_set_location, "s" } + }; + + g_action_map_add_action_entries(G_ACTION_MAP(m_actions), + entries, + G_N_ELEMENTS(entries), + this); + + // add the header actions + auto gam = G_ACTION_MAP(m_actions); + auto v = create_default_header_state(); + auto a = g_simple_action_new_stateful("desktop-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("desktop-greeter-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("phone-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + a = g_simple_action_new_stateful("phone-greeter-header", nullptr, v); + g_action_map_add_action(gam, G_ACTION(a)); + + // add the calendar action + v = create_calendar_state(state); + a = g_simple_action_new_stateful("calendar", G_VARIANT_TYPE_INT64, v); + g_action_map_add_action(gam, G_ACTION(a)); + g_signal_connect(a, "activate", G_CALLBACK(on_calendar_activated), this); + //m_calendar_action = a; + + // FIXME: rebuild the calendar state when show-week-number changes +} + +Actions::~Actions() +{ + g_clear_object(&m_actions); +} + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 80e91a3..1fadfe8 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -28,53 +28,56 @@ class LiveClock::Impl public: Impl(LiveClock& owner, const std::shared_ptr& tzd): - owner_(owner), - timezones_(tzd) + m_owner(owner), + m_timezones(tzd) { - if (timezones_) + if (m_timezones) { - timezones_->timezone.changed().connect ([this](const std::string& z) {setTimezone(z);}); - setTimezone(timezones_->timezone.get()); + m_timezones->timezone.changed().connect([this](const std::string& z) {setTimezone(z);}); + setTimezone(m_timezones->timezone.get()); } - owner_.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); - setInterval(owner_.skewTestIntervalSec.get()); + m_owner.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); + setInterval(m_owner.skewTestIntervalSec.get()); } ~Impl() { clearTimer(); - g_clear_pointer (&timezone_, g_time_zone_unref); + g_clear_pointer(&m_timezone, g_time_zone_unref); } - GDateTime* localtime() const + DateTime localtime() const { - g_assert (timezone_ != nullptr); + g_assert(m_timezone != nullptr); - return g_date_time_new_now (timezone_); + auto gdt = g_date_time_new_now(m_timezone); + DateTime ret(gdt); + g_date_time_unref(gdt); + return ret; } private: - void setTimezone (const std::string& str) + void setTimezone(const std::string& str) { - g_clear_pointer (&timezone_, g_time_zone_unref); - timezone_= g_time_zone_new (str.c_str()); - owner_.skewDetected(); + g_clear_pointer(&m_timezone, g_time_zone_unref); + m_timezone= g_time_zone_new(str.c_str()); + m_owner.skewDetected(); } private: void clearTimer() { - if (skew_timeout_id_) + if (m_skew_timeout_id) { - g_source_remove(skew_timeout_id_); - skew_timeout_id_ = 0; + g_source_remove(m_skew_timeout_id); + m_skew_timeout_id = 0; } - g_clear_pointer(&prev_datetime_, g_date_time_unref); + m_prev_datetime.reset(); } void setInterval(unsigned int seconds) @@ -83,50 +86,53 @@ private: if (seconds > 0) { - prev_datetime_ = owner_.localtime(); - skew_timeout_id_ = g_timeout_add_seconds(seconds, onTimerPulse, this); + m_prev_datetime = localtime(); + m_skew_timeout_id = g_timeout_add_seconds(seconds, onTimerPulse, this); } } static gboolean onTimerPulse(gpointer gself) { - auto self = static_cast(gself); + static_cast(gself)->onTimerPulse(); + return G_SOURCE_CONTINUE; + } + void onTimerPulse() + { // check to see if too much time passed since the last check */ - GDateTime * now = self->owner_.localtime(); - const GTimeSpan diff = g_date_time_difference(now, self->prev_datetime_); + const auto now = localtime(); + const auto diff = now.difference (m_prev_datetime); const GTimeSpan fuzz = 5; - const GTimeSpan max = (self->owner_.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; + const GTimeSpan max = (m_owner.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; if (abs(diff) > max) - self->owner_.skewDetected(); + m_owner.skewDetected(); - // update prev_datetime - g_clear_pointer(&self->prev_datetime_, g_date_time_unref); - self->prev_datetime_ = now; + // check to see if the day has changed + if (now.day_of_year() != m_prev_datetime.day_of_year()) + m_owner.dateChanged(); - return G_SOURCE_CONTINUE; + // update m_prev_datetime + m_prev_datetime = now; } protected: - LiveClock& owner_; - GTimeZone * timezone_ = nullptr; - std::shared_ptr timezones_; + LiveClock& m_owner; + GTimeZone * m_timezone = nullptr; + std::shared_ptr m_timezones; - GDateTime * prev_datetime_ = nullptr; - unsigned int skew_timeout_id_ = 0; - unsigned int sleep_subscription_id_ = 0; + DateTime m_prev_datetime; + unsigned int m_skew_timeout_id = 0; }; LiveClock::LiveClock(const std::shared_ptr& tzd): - p (new Impl (*this, tzd)) + p(new Impl(*this, tzd)) { } LiveClock::~LiveClock() =default; -GDateTime * -LiveClock::localtime() const +DateTime LiveClock::localtime() const { return p->localtime(); } diff --git a/src/clock.cpp b/src/clock.cpp index 4a75ceb..f3f5d70 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -31,9 +31,9 @@ namespace datetime { ***/ Clock::Clock(): - cancellable_(g_cancellable_new()) + m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SYSTEM, cancellable_, onSystemBusReady, this); + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, onSystemBusReady, this); timezones.changed().connect([this](const std::set& timezones){ g_message ("timezones changed... new count is %d", (int)timezones.size()); @@ -43,13 +43,13 @@ Clock::Clock(): Clock::~Clock() { - g_cancellable_cancel(cancellable_); - g_clear_object(&cancellable_); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); - if (sleep_subscription_id_) - g_dbus_connection_signal_unsubscribe(system_bus_ , sleep_subscription_id_); + if (m_sleep_subscription_id) + g_dbus_connection_signal_unsubscribe(m_system_bus, m_sleep_subscription_id); - g_clear_object(&system_bus_); + g_clear_object(&m_system_bus); } void @@ -61,9 +61,9 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) { auto self = static_cast(gself); - self->system_bus_ = system_bus; + self->m_system_bus = system_bus; - self->sleep_subscription_id_ = g_dbus_connection_signal_subscribe( + self->m_sleep_subscription_id = g_dbus_connection_signal_subscribe( system_bus, nullptr, "org.freedesktop.login1.Manager", // interface @@ -78,13 +78,13 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) } void -Clock::onPrepareForSleep(GDBusConnection * connection G_GNUC_UNUSED, - const gchar * sender_name G_GNUC_UNUSED, - const gchar * object_path G_GNUC_UNUSED, - const gchar * interface_name G_GNUC_UNUSED, - const gchar * signal_name G_GNUC_UNUSED, - GVariant * parameters G_GNUC_UNUSED, - gpointer gself) +Clock::onPrepareForSleep(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* /*parameters*/, + gpointer gself) { static_cast(gself)->skewDetected(); } diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 8390967..3f942f4 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -35,165 +35,167 @@ class DesktopFormatter::Impl { public: - Impl (DesktopFormatter * owner, const std::shared_ptr& clock): - owner_(owner), - clock_(clock), - settings_(g_settings_new(SETTINGS_INTERFACE)) - { - const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, - "changed::" SETTINGS_SHOW_DAY_S, - "changed::" SETTINGS_SHOW_DATE_S, - "changed::" SETTINGS_SHOW_YEAR_S }; - for (guint i=0, n=G_N_ELEMENTS(keys); i& clock): + m_owner(owner), + m_clock(clock), + m_settings(g_settings_new(SETTINGS_INTERFACE)) +{ + const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_TIME_FORMAT_S, + "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, + "changed::" SETTINGS_SHOW_DAY_S, + "changed::" SETTINGS_SHOW_DATE_S, + "changed::" SETTINGS_SHOW_YEAR_S }; + for(const auto& key : keys) + g_signal_connect(m_settings, key, G_CALLBACK(onSettingsChanged), this); + + rebuildHeaderFormat(); +} - ~Impl() - { - g_signal_handlers_disconnect_by_data (settings_, this); - g_object_unref (settings_); - } +~Impl() +{ + g_signal_handlers_disconnect_by_data(m_settings, this); + g_object_unref(m_settings); +} private: - static void onSettingsChanged (GSettings * changed G_GNUC_UNUSED, - const gchar * key G_GNUC_UNUSED, - gpointer gself) - { - static_cast(gself)->rebuildHeaderFormat(); - } +static void onSettingsChanged(GSettings * /*changed*/, + const gchar * /*key*/, + gpointer gself) +{ + static_cast(gself)->rebuildHeaderFormat(); +} - void rebuildHeaderFormat() - { - gchar * fmt = getHeaderLabelFormatString (settings_); - owner_->headerFormat.set(fmt); - g_free (fmt); - } +void rebuildHeaderFormat() +{ + auto fmt = getHeaderLabelFormatString(m_settings); + m_owner->headerFormat.set(fmt); + g_free(fmt); +} private: - gchar* getHeaderLabelFormatString (GSettings * s) const - { - char * fmt; - const TimeFormatMode mode = (TimeFormatMode) g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S); - - if (mode == TIME_FORMAT_MODE_CUSTOM) - { - fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S); - } - else - { - const bool show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S); - const bool show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S); - const bool show_year = show_date && g_settings_get_boolean (s, SETTINGS_SHOW_YEAR_S); - const char * date_fmt = getDateFormat (show_day, show_date, show_year); - const char * time_fmt = getFullTimeFormatString (s); - fmt = joinDateAndTimeFormatStrings (date_fmt, time_fmt); - } - - return fmt; - } +gchar* getHeaderLabelFormatString(GSettings* s) const +{ + char * fmt; + const auto mode = g_settings_get_enum(s, SETTINGS_TIME_FORMAT_S); - const gchar* T_(const gchar* in) const + if (mode == TIME_FORMAT_MODE_CUSTOM) { - return owner_->T_(in); + fmt = g_settings_get_string(s, SETTINGS_CUSTOM_TIME_FORMAT_S); } - - const gchar* getDateFormat (bool show_day, bool show_date, bool show_year) const + else { - const char * fmt; - - if (show_day && show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ - fmt = T_("%a %b %e %Y"); - else if (show_day && show_date) - fmt = T_("%a %b %e"); - else if (show_day && show_year) - /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ - fmt = T_("%a %Y"); - else if (show_day) - /* TRANSLATORS: a strftime(3) format showing the weekday. */ - fmt = T_("%a"); - else if (show_date && show_year) - /* TRANSLATORS: a strftime(3) format showing the date and year */ - fmt = T_("%b %e %Y"); - else if (show_date) - /* TRANSLATORS: a strftime(3) format showing the date */ - fmt = T_("%b %e"); - else if (show_year) - /* TRANSLATORS: a strftime(3) format showing the year */ - fmt = T_("%Y"); - else - fmt = nullptr; - - return fmt; + const auto show_day = g_settings_get_boolean(s, SETTINGS_SHOW_DAY_S); + const auto show_date = g_settings_get_boolean(s, SETTINGS_SHOW_DATE_S); + const auto show_year = show_date && g_settings_get_boolean(s, SETTINGS_SHOW_YEAR_S); + const auto date_fmt = getDateFormat(show_day, show_date, show_year); + const auto time_fmt = getFullTimeFormatString(s); + fmt = joinDateAndTimeFormatStrings(date_fmt, time_fmt); } - const gchar * getFullTimeFormatString (GSettings * settings) const - { - const bool show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S); + return fmt; +} + +const gchar* T_(const gchar* in) const +{ + return m_owner->T_(in); +} - bool twelvehour; - switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S)) - { - case TIME_FORMAT_MODE_LOCALE_DEFAULT: - twelvehour = is_locale_12h(); - break; +const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const +{ + const char * fmt; + + if (show_day && show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday, date, and year */ + fmt = T_("%a %b %e %Y"); + else if (show_day && show_date) + /* TRANSLATORS: a strftime(3) format showing the weekday and date */ + fmt = T_("%a %b %e"); + else if (show_day && show_year) + /* TRANSLATORS: a strftime(3) format showing the weekday and year. */ + fmt = T_("%a %Y"); + else if (show_day) + /* TRANSLATORS: a strftime(3) format showing the weekday. */ + fmt = T_("%a"); + else if (show_date && show_year) + /* TRANSLATORS: a strftime(3) format showing the date and year */ + fmt = T_("%b %e %Y"); + else if (show_date) + /* TRANSLATORS: a strftime(3) format showing the date */ + fmt = T_("%b %e"); + else if (show_year) + /* TRANSLATORS: a strftime(3) format showing the year */ + fmt = T_("%Y"); + else + fmt = nullptr; + + return fmt; +} - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = FALSE; - break; +const gchar* getFullTimeFormatString(GSettings* settings) const +{ + auto show_seconds = g_settings_get_boolean(settings, SETTINGS_SHOW_SECONDS_S); + + bool twelvehour; + switch (g_settings_get_enum(settings, SETTINGS_TIME_FORMAT_S)) + { + case TIME_FORMAT_MODE_LOCALE_DEFAULT: + twelvehour = is_locale_12h(); + break; - default: - twelvehour = TRUE; - break; - } + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = false; + break; - return owner_->getDefaultHeaderTimeFormat (twelvehour, show_seconds); + default: + twelvehour = true; + break; } - gchar* joinDateAndTimeFormatStrings (const char * date_string, const char * time_string) const + return m_owner->getDefaultHeaderTimeFormat(twelvehour, show_seconds); +} + +gchar* joinDateAndTimeFormatStrings(const char* date_string, + const char* time_string) const +{ + gchar * str; + + if (date_string && time_string) + { + /* TRANSLATORS: This is a format string passed to strftime to + * combine the date and the time. The value of "%s\u2003%s" + * will result in a string like this in US English 12-hour time: + * 'Fri Jul 16 11:50 AM'. The space in between date and time is + * a Unicode en space (E28082 in UTF-8 hex). */ + str = g_strdup_printf("%s\u2003%s", date_string, time_string); + } + else if (date_string) + { + str = g_strdup(date_string); + } + else // time_string { - gchar * str; - - if (date_string && time_string) - { - /* TRANSLATORS: This is a format string passed to strftime to combine the - * date and the time. The value of "%s\u2003%s" will result in a - * string like this in US English 12-hour time: 'Fri Jul 16 11:50 AM'. - * The space in between date and time is a Unicode en space - * (E28082 in UTF-8 hex). */ - str = g_strdup_printf ("%s\u2003%s", date_string, time_string); - } - else if (date_string) - { - str = g_strdup (date_string); - } - else // time_string - { - str = g_strdup (time_string); - } - - return str; + str = g_strdup(time_string); } + return str; +} + private: - DesktopFormatter * const owner_; - std::shared_ptr clock_; - GSettings * settings_; +DesktopFormatter * const m_owner; +std::shared_ptr m_clock; +GSettings * m_settings; }; /*** **** ***/ -DesktopFormatter::DesktopFormatter (const std::shared_ptr& clock): +DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock): Formatter(clock), p(new Impl(this, clock)) { diff --git a/src/formatter.cpp b/src/formatter.cpp index 4e2f582..1f26cc7 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -30,16 +30,16 @@ namespace { -void clearTimer (guint& tag) +void clearTimer(guint& tag) { if (tag) { - g_source_remove (tag); + g_source_remove(tag); tag = 0; } } -guint calculate_milliseconds_until_next_minute (GDateTime * now) +guint calculate_milliseconds_until_next_minute(GDateTime * now) { GDateTime * next; GDateTime * start_of_next; @@ -47,12 +47,12 @@ guint calculate_milliseconds_until_next_minute (GDateTime * now) guint interval_msec; next = g_date_time_add_minutes(now, 1); - start_of_next = g_date_time_new_local(g_date_time_get_year (next), - g_date_time_get_month (next), - g_date_time_get_day_of_month (next), - g_date_time_get_hour (next), - g_date_time_get_minute (next), - 0.1); + start_of_next = g_date_time_new_local(g_date_time_get_year(next), + g_date_time_get_month(next), + g_date_time_get_day_of_month(next), + g_date_time_get_hour(next), + g_date_time_get_minute(next), + 0.1); interval_usec = g_date_time_difference(start_of_next, now); interval_msec = (interval_usec + 999) / 1000; @@ -62,12 +62,12 @@ guint calculate_milliseconds_until_next_minute (GDateTime * now) return interval_msec; } -gint calculate_milliseconds_until_next_second (GDateTime * now) +gint calculate_milliseconds_until_next_second(GDateTime * now) { gint interval_usec; guint interval_msec; - interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond (now); + interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond(now); interval_msec = (interval_usec + 999) / 1000; return interval_msec; } @@ -94,7 +94,7 @@ gint calculate_milliseconds_until_next_second (GDateTime * now) * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour * is not enough. We need to refresh at HH:00, HH:15, HH:30, and HH:45. */ -guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) +guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) { char * str; gint minute; @@ -106,23 +106,22 @@ guint calculate_seconds_until_next_fifteen_minutes (GDateTime * now) minute = g_date_time_get_minute(now); minute = 15 - (minute % 15); next = g_date_time_add_minutes(now, minute); - start_of_next = g_date_time_new_local(g_date_time_get_year (next), - g_date_time_get_month (next), - g_date_time_get_day_of_month (next), - g_date_time_get_hour (next), - g_date_time_get_minute (next), + start_of_next = g_date_time_new_local(g_date_time_get_year(next), + g_date_time_get_month(next), + g_date_time_get_day_of_month(next), + g_date_time_get_hour(next), + g_date_time_get_minute(next), 0.1); str = g_date_time_format(start_of_next, "%F %T"); - g_debug ("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); - g_free (str); + g_debug("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str); + g_free(str); diff = g_date_time_difference(start_of_next, now); seconds = (diff + (G_TIME_SPAN_SECOND-1)) / G_TIME_SPAN_SECOND; g_date_time_unref(start_of_next); g_date_time_unref(next); - return seconds; } } // anonymous namespace @@ -137,11 +136,12 @@ class Formatter::Impl { public: - Impl (Formatter* owner, const std::shared_ptr& clock): - owner_(owner), - clock_(clock) + Impl(Formatter* owner, const std::shared_ptr& clock): + m_owner(owner), + m_clock(clock) { - owner_->headerFormat.changed().connect([this](const std::string& fmt G_GNUC_UNUSED){updateHeader();}); + m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){updateHeader();}); + m_clock->skewDetected.connect([this](){updateHeader();}); updateHeader(); restartRelativeTimer(); @@ -149,30 +149,25 @@ public: ~Impl() { - clearTimer(header_timer_); + clearTimer(m_header_timer); } private: void updateHeader() { - GDateTime * now = clock_->localtime(); - const time_t unix = g_date_time_to_unix (now); - struct tm tm; - localtime_r (&unix, &tm); - char str[512]; - strftime (str, sizeof(str), owner_->headerFormat.get().c_str(), &tm); - owner_->header.set (str); - g_date_time_unref (now); + const auto fmt = m_owner->headerFormat.get(); + const auto str = m_clock->localtime().format(fmt); + m_owner->header.set(str); restartHeaderTimer(); } void restartHeaderTimer() { - clearTimer(header_timer_); + clearTimer(m_header_timer); - const std::string fmt = owner_->headerFormat.get(); + const std::string fmt = m_owner->headerFormat.get(); const bool header_shows_seconds = (fmt.find("%s") != std::string::npos) || (fmt.find("%S") != std::string::npos) || (fmt.find("%T") != std::string::npos) @@ -180,17 +175,17 @@ private: || (fmt.find("%c") != std::string::npos); guint interval_msec; - GDateTime * now = clock_->localtime(); + const auto now = m_clock->localtime(); + auto str = now.format("%F %T"); if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second (now); + interval_msec = calculate_milliseconds_until_next_second(now.get()); else - interval_msec = calculate_milliseconds_until_next_minute (now); - g_date_time_unref (now); + interval_msec = calculate_milliseconds_until_next_minute(now.get()); interval_msec += 50; // add a small margin to ensure the callback // fires /after/ next is reached - header_timer_ = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); + m_header_timer = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); } static gboolean onHeaderTimer(gpointer gself) @@ -203,29 +198,28 @@ private: void restartRelativeTimer() { - clearTimer(relative_timer_); + clearTimer(m_relative_timer); - GDateTime * now = clock_->localtime(); - const guint seconds = calculate_seconds_until_next_fifteen_minutes(now); - relative_timer_ = g_timeout_add_seconds(seconds, onRelativeTimer, this); - g_date_time_unref(now); + const auto now = m_clock->localtime(); + const auto seconds = calculate_seconds_until_next_fifteen_minutes(now.get()); + m_relative_timer = g_timeout_add_seconds(seconds, onRelativeTimer, this); } static gboolean onRelativeTimer(gpointer gself) { auto self = static_cast(gself); - self->owner_->relativeFormatChanged(); + self->m_owner->relativeFormatChanged(); self->restartRelativeTimer(); return G_SOURCE_REMOVE; } private: - Formatter * const owner_; - guint header_timer_ = 0; - guint relative_timer_ = 0; + Formatter * const m_owner; + guint m_header_timer = 0; + guint m_relative_timer = 0; public: - std::shared_ptr clock_; + std::shared_ptr m_clock; }; /*** @@ -233,7 +227,7 @@ public: ***/ Formatter::Formatter(const std::shared_ptr& clock): - p (new Formatter::Impl(this, clock)) + p(new Formatter::Impl(this, clock)) { } @@ -244,12 +238,11 @@ Formatter::~Formatter() bool Formatter::is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; + const auto t_fmt = nl_langinfo(T_FMT); - for (i=0; formats_24h[i]; ++i) - if (strstr (t_fmt, formats_24h[i])) + for (const auto& needle : formats_24h) + if (strstr(t_fmt, needle)) return false; return true; @@ -272,7 +265,7 @@ Formatter::T_(const char *msg) */ char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); - const char *time_locale = setlocale (LC_TIME, nullptr); + const char *time_locale = setlocale(LC_TIME, nullptr); char *language = g_strdup(g_getenv("LANGUAGE")); const char *rv; @@ -329,15 +322,15 @@ typedef enum } date_proximity_t; -date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) +date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) { date_proximity_t prox = DATE_PROXIMITY_FAR; gint now_year, now_month, now_day; gint time_year, time_month, time_day; // does it happen today? - g_date_time_get_ymd (now, &now_year, &now_month, &now_day); - g_date_time_get_ymd (time, &time_year, &time_month, &time_day); + g_date_time_get_ymd(now, &now_year, &now_month, &now_day); + g_date_time_get_ymd(time, &time_year, &time_month, &time_day); if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) prox = DATE_PROXIMITY_TODAY; @@ -347,12 +340,12 @@ date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) GDateTime * tomorrow; gint tom_year, tom_month, tom_day; - tomorrow = g_date_time_add_days (now, 1); - g_date_time_get_ymd (tomorrow, &tom_year, &tom_month, &tom_day); + tomorrow = g_date_time_add_days(now, 1); + g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day); if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) prox = DATE_PROXIMITY_TOMORROW; - g_date_time_unref (tomorrow); + g_date_time_unref(tomorrow); } // does it happen this week? @@ -361,17 +354,17 @@ date_proximity_t getDateProximity (GDateTime * now, GDateTime * time) GDateTime * week; GDateTime * week_bound; - week = g_date_time_add_days (now, 6); - week_bound = g_date_time_new_local (g_date_time_get_year(week), - g_date_time_get_month (week), - g_date_time_get_day_of_month(week), - 23, 59, 59.9); + week = g_date_time_add_days(now, 6); + week_bound = g_date_time_new_local(g_date_time_get_year(week), + g_date_time_get_month(week), + g_date_time_get_day_of_month(week), + 23, 59, 59.9); - if (g_date_time_compare (time, week_bound) <= 0) + if (g_date_time_compare(time, week_bound) <= 0) prox = DATE_PROXIMITY_WEEK; - g_date_time_unref (week_bound); - g_date_time_unref (week); + g_date_time_unref(week_bound); + g_date_time_unref(week); } return prox; @@ -398,12 +391,12 @@ std::string Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const { std::string ret; - GDateTime * now = p->clock_->localtime(); + auto now = p->m_clock->localtime().get(); if (then != nullptr) { - const bool full_day = then_end && (g_date_time_difference (then_end, then) >= G_TIME_SPAN_DAY); - const auto prox = getDateProximity (now, then); + const bool full_day = then_end && (g_date_time_difference(then_end, then) >= G_TIME_SPAN_DAY); + const auto prox = getDateProximity(now, then); if (full_day) { @@ -440,14 +433,13 @@ Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const then the time should be followed by its timezone. */ if ((then_end != nullptr) && (!full_day) && - ((g_date_time_get_utc_offset (now) != g_date_time_get_utc_offset (then)))) + ((g_date_time_get_utc_offset(now) != g_date_time_get_utc_offset(then)))) { ret += ' '; - ret += g_date_time_get_timezone_abbreviation (then); + ret += g_date_time_get_timezone_abbreviation(then); } } - g_date_time_unref (now); return ret; } diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index ed8f998..646a360 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -29,16 +29,16 @@ namespace unity { namespace indicator { namespace datetime { -SettingsLocations::SettingsLocations (const std::string& schemaId, - const std::shared_ptr& timezones): - timezones_(timezones) +SettingsLocations::SettingsLocations(const std::string& schemaId, + const std::shared_ptr& timezones): + m_timezones(timezones) { - auto deleter = [&](GSettings* s){g_object_unref(s);}; - settings_ = std::unique_ptr>(g_settings_new(schemaId.c_str()), deleter); + auto deleter = [](GSettings* s){g_object_unref(s);}; + m_settings = std::unique_ptr>(g_settings_new(schemaId.c_str()), deleter); const char * keys[] = { "changed::" SETTINGS_LOCATIONS_S, "changed::" SETTINGS_SHOW_LOCATIONS_S }; - for (int i=0, n=G_N_ELEMENTS(keys); itimezone.changed().connect([this](const std::string&){reload();}); timezones->timezones.changed().connect([this](const std::set&){reload();}); @@ -47,7 +47,7 @@ SettingsLocations::SettingsLocations (const std::string& schemaId, } void -SettingsLocations::onSettingsChanged (gpointer gself) +SettingsLocations::onSettingsChanged(gpointer gself) { static_cast(gself)->reload(); } @@ -56,48 +56,49 @@ void SettingsLocations::reload() { std::vector v; + auto settings = m_settings.get(); // add the primary timezone first - std::string zone = timezones_->timezone.get(); + auto zone = m_timezones->timezone.get(); if (!zone.empty()) { - gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); - Location l (zone, name); - v.push_back (l); - g_free (name); + gchar * name = get_current_zone_name(zone.c_str(), settings); + Location l(zone, name); + v.push_back(l); + g_free(name); } // add the other detected timezones - for (const auto& zone : timezones_->timezones.get()) + for (const auto& zone : m_timezones->timezones.get()) { - gchar * name = get_current_zone_name (zone.c_str(), settings_.get()); - Location l (zone, name); - if (std::find (v.begin(), v.end(), l) == v.end()) - v.push_back (l); - g_free (name); + gchar * name = get_current_zone_name(zone.c_str(), settings); + Location l(zone, name); + if (std::find(v.begin(), v.end(), l) == v.end()) + v.push_back(l); + g_free(name); } // maybe add the user-specified locations - if (g_settings_get_boolean (settings_.get(), SETTINGS_SHOW_LOCATIONS_S)) + if (g_settings_get_boolean(settings, SETTINGS_SHOW_LOCATIONS_S)) { - gchar ** user_locations = g_settings_get_strv (settings_.get(), SETTINGS_LOCATIONS_S); + gchar ** user_locations = g_settings_get_strv(settings, SETTINGS_LOCATIONS_S); for (int i=0; user_locations[i]; i++) { gchar * zone; gchar * name; - split_settings_location (user_locations[i], &zone, &name); - Location l (zone, name); - if (std::find (v.begin(), v.end(), l) == v.end()) - v.push_back (l); - g_free (name); - g_free (zone); + split_settings_location(user_locations[i], &zone, &name); + Location l(zone, name); + if (std::find(v.begin(), v.end(), l) == v.end()) + v.push_back(l); + g_free(name); + g_free(zone); } - g_strfreev (user_locations); + g_strfreev(user_locations); } - locations.set (v); + locations.set(v); } } // namespace datetime diff --git a/src/locations.cpp b/src/locations.cpp index 5c3c52b..d6ab73a 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -26,16 +26,39 @@ namespace indicator { namespace datetime { Location::Location(const std::string& zone_, const std::string& name_): - zone(zone_), - name(name_) + m_zone(zone_), + m_name(name_) { - GTimeZone * gzone = g_time_zone_new (zone.c_str()); - GDateTime * gtime = g_date_time_new_now (gzone); - offset = g_date_time_get_utc_offset (gtime); + auto gzone = g_time_zone_new (zone().c_str()); + auto gtime = g_date_time_new_now (gzone); + m_offset = g_date_time_get_utc_offset (gtime); g_date_time_unref (gtime); g_time_zone_unref (gzone); } +#if 0 +DateTime Location::localtime(const DateTime& reference_point) const +{ +GDateTime * g_date_time_to_timezone (GDateTime *datetime, + GTimeZone *tz); + auto gzone = g_time_zone_new(zone().c_str()); + const auto gtime = reference_point.get(); + auto glocal = g_date_time_new (gzone, + g_date_time_get_year(gtime), + g_date_time_get_month(gtime), + g_date_time_get_day_of_month(gtime), + g_date_time_get_hour(gtime), + g_date_time_get_minute(gtime), + g_date_time_get_seconds(gtime)); + DateTime local(glocal); + g_date_time_unref(glocal); + g_message("reference: %zu", (size_t)reference_point.to_unix(), (size_t)local.to_unix()); + //g_date_time_unref(gtime); + g_time_zone_unref(gzone); + return local; +} +#endif + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 868d41b..0000000 --- a/src/main.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include /* exit() */ - -#include -#include -#include - -#include "clock-live.h" -#include "planner-eds.h" -#include "service.h" - -/*** -**** -***/ - -static void -on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) -{ - g_message ("exiting: service couldn't acquire or lost ownership of busname"); - - g_main_loop_quit ((GMainLoop*)loop); -} - -int -main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) -{ - IndicatorDatetimeClock * clock; - IndicatorDatetimePlanner * planner; - IndicatorDatetimeService * service; - GMainLoop * loop; - - /* Work around a deadlock in glib's type initialization. It can be - * removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is - * fixed. - */ - g_type_ensure (G_TYPE_DBUS_CONNECTION); - - /* boilerplate i18n */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - /* init libnotify */ - if (!notify_init ("indicator-datetime-service")) - g_critical ("libnotify initialization failed"); - - /* create the service */ - clock = indicator_datetime_clock_live_new (); - planner = indicator_datetime_planner_eds_new (); - service = indicator_datetime_service_new (clock, planner); - - /* run */ - loop = g_main_loop_new (NULL, FALSE); - g_signal_connect (service, INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST, - G_CALLBACK(on_name_lost), loop); - g_main_loop_run (loop); - g_main_loop_unref (loop); - - /* cleanup */ - g_object_unref (service); - g_object_unref (planner); - g_object_unref (clock); - return 0; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c246296 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include /* exit() */ + +using namespace unity::indicator::datetime; + +int +main(int /*argc*/, char** /*argv*/) +{ + // Work around a deadlock in glib's type initialization. + // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed. + g_type_ensure(G_TYPE_DBUS_CONNECTION); + + // boilerplate i18n + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain(GETTEXT_PACKAGE); + + // init libnotify + if(!notify_init("indicator-datetime-service")) + g_critical("libnotify initialization failed"); + + // build the menu factory + GActionGroup * actions = G_ACTION_GROUP(g_simple_action_group_new()); + std::shared_ptr timezones(new LiveTimezones(TIMEZONE_FILE)); + std::shared_ptr clock(new LiveClock(timezones)); + std::shared_ptr planner(new PlannerEds); + std::shared_ptr locations(new SettingsLocations(SETTINGS_INTERFACE, timezones)); + planner->time = clock->localtime(); + MenuFactory factory(actions, timezones, clock, planner, locations); + + // create the menus + std::vector> menus; + menus.push_back(factory.buildMenu(MenuFactory::Desktop)); + + // export them + auto loop = g_main_loop_new(nullptr, false); + Service service; + service.name_lost.connect([loop](){ + g_message("exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit(loop); + }); + service.publish(actions, menus); + g_main_loop_run(loop); + g_main_loop_unref(loop); + return 0; +} diff --git a/src/menu.cpp b/src/menu.cpp new file mode 100644 index 0000000..76306cc --- /dev/null +++ b/src/menu.cpp @@ -0,0 +1,518 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/**** +***** +****/ + +#define ALARM_CLOCK_ICON_NAME "alarm-clock" +#define CALENDAR_ICON_NAME "calendar" + +class MenuImpl: public Menu +{ +protected: + MenuImpl(const Menu::Profile profile_in, + const std::string& name_in, + std::shared_ptr& state, + std::shared_ptr& actions, + std::shared_ptr formatter): + Menu(profile_in, name_in), + m_state(state), + m_actions(actions), + m_formatter(formatter) + { + // initialize the menu + create_gmenu(); + for (int i=0; iheader.changed().connect([this](const std::string&){ + update_header(); + }); + m_formatter->headerFormat.changed().connect([this](const std::string&){ + update_section(Locations); // need to update x-canonical-time-format + }); + m_formatter->relativeFormatChanged.connect([this](){ + update_section(Appointments); // uses formatter.getRelativeFormat() + update_section(Locations); // uses formatter.getRelativeFormat() + }); + m_state->show_clock.changed().connect([this](bool){ + update_header(); // update header's label + update_section(Locations); // locations' relative time may have changed + }); + m_state->show_events.changed().connect([this](bool){ + update_section(Appointments); // showing events got toggled + }); + m_state->planner->upcoming.changed().connect([this](const std::vector&){ + update_section(Appointments); // "upcoming" is the list of Appointments we show + }); + m_state->clock->dateChanged.connect([this](){ + update_section(Calendar); // need to update the Date menuitem + update_section(Locations); // locations' relative time may have changed + }); + m_state->locations->locations.changed().connect([this](const std::vector&) { + update_section(Locations); // "locations" is the list of Locations we show + }); + } + + virtual ~MenuImpl() + { + g_clear_object(&m_menu); + g_clear_pointer(&m_serialized_alarm_icon, g_variant_unref); + g_clear_pointer(&m_serialized_calendar_icon, g_variant_unref); + } + + virtual GVariant* create_header_state() =0; + + void update_header() + { + auto action_group = m_actions->action_group(); + auto action_name = name() + "-header"; + auto state = create_header_state(); + g_action_group_change_action_state(action_group, action_name.c_str(), state); + } + + std::shared_ptr m_state; + std::shared_ptr m_actions; + std::shared_ptr m_formatter; + GMenu* m_submenu = nullptr; + + GVariant* get_serialized_alarm_icon() + { + if (G_UNLIKELY(m_serialized_alarm_icon == nullptr)) + { + auto i = g_themed_icon_new_with_default_fallbacks(ALARM_CLOCK_ICON_NAME); + m_serialized_alarm_icon = g_icon_serialize(i); + g_object_unref(i); + } + + return m_serialized_alarm_icon; + } + +private: + + GVariant* get_serialized_calendar_icon() + { + if (G_UNLIKELY(m_serialized_calendar_icon == nullptr)) + { + auto i = g_themed_icon_new_with_default_fallbacks(CALENDAR_ICON_NAME); + m_serialized_calendar_icon = g_icon_serialize(i); + g_object_unref(i); + } + + return m_serialized_calendar_icon; + } + + void create_gmenu() + { + g_assert(m_submenu == nullptr); + + m_submenu = g_menu_new(); + + // build placeholders for the sections + for(int i=0; iclock->localtime().format(_("%A, %e %B %Y")); + auto item = g_menu_item_new (label.c_str(), nullptr); + auto v = get_serialized_calendar_icon(); + g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_ICON, v); + if (allow_activation) + { + v = g_variant_new_int64(0); + const char* action = "indicator.activate-planner"; + g_menu_item_set_action_and_target_value (item, action, v); + } + g_menu_append_item(menu, item); + g_object_unref(item); + + // add calendar + if (show_calendar) + { + item = g_menu_item_new ("[calendar]", NULL); + v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value (item, "indicator.calendar", v); + g_menu_item_set_attribute (item, "x-canonical-type", + "s", "com.canonical.indicator.calendar"); + if (allow_activation) + { + g_menu_item_set_attribute (item, "activation-action", + "s", "indicator.activate-planner"); + } + g_menu_append_item (menu, item); + g_object_unref (item); + } + + return G_MENU_MODEL(menu); + } + + void add_appointments(GMenu* menu, Profile profile) + { + int n = 0; + const int MAX_APPTS = 5; + std::set added; + + for (const auto& appt : m_state->planner->upcoming.get()) + { + if (n++ >= MAX_APPTS) + break; + + if (added.count(appt.uid)) + continue; + + added.insert(appt.uid); + + GDateTime* begin = appt.begin(); + GDateTime* end = appt.end(); + auto fmt = m_formatter->getRelativeFormat(begin, end); + auto unix_time = g_date_time_to_unix(begin); + + auto menu_item = g_menu_item_new (appt.summary.c_str(), nullptr); + g_menu_item_set_attribute (menu_item, "x-canonical-time", "x", unix_time); + g_menu_item_set_attribute (menu_item, "x-canonical-time-format", "s", fmt.c_str()); + + if (appt.has_alarms) + { + g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.alarm"); + g_menu_item_set_attribute_value(menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); + } + else + { + g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.appointment"); + + if (!appt.color.empty()) + g_menu_item_set_attribute (menu_item, "x-canonical-color", "s", appt.color.c_str()); + } + + if (profile == Phone) + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-appointment", + g_variant_new_string (appt.uid.c_str())); + else + g_menu_item_set_action_and_target_value (menu_item, + "indicator.activate-planner", + g_variant_new_int64 (unix_time)); + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); + } + } + + GMenuModel* create_appointments_section(Profile profile) + { + auto menu = g_menu_new(); + + if (((profile==Phone) || (profile==Desktop)) && m_state->show_events.get()) + { + add_appointments (menu, profile); + + // add the 'Add Event…' menuitem + auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); + const gchar* action_name = "indicator.activate_planner"; + auto v = g_variant_new_int64(0); + g_menu_item_set_action_and_target_value(menu_item, action_name, v); + g_menu_append_item(menu, menu_item); + g_object_unref(menu_item); + } + + return G_MENU_MODEL(menu); + } + + GMenuModel* create_locations_section(Profile profile) + { + GMenu* menu = g_menu_new(); + + if (profile == Desktop) + { + const auto now = m_state->clock->localtime(); + + for(const auto& location : m_state->locations->locations.get()) + { + const auto& zone = location.zone(); + const auto& name = location.name(); + const auto zone_now = now.to_timezone(zone); + const auto fmt = m_formatter->getRelativeFormat(zone_now.get()); + auto detailed_action = g_strdup_printf("indicator.set-location::%s %s", zone.c_str(), name.c_str()); + auto i = g_menu_item_new (name.c_str(), detailed_action); + g_menu_item_set_attribute(i, "x-canonical-type", "s", "com.canonical.indicator.location"); + g_menu_item_set_attribute(i, "x-canonical-timezone", "s", zone.c_str()); + g_menu_item_set_attribute(i, "x-canonical-time-format", "s", fmt.c_str()); + g_menu_append_item (menu, i); + g_object_unref(i); + g_free(detailed_action); + } + } + + return G_MENU_MODEL(menu); + } + + GMenuModel* create_settings_section(Profile profile) + { + auto menu = g_menu_new(); + + if (profile == Desktop) + { + g_menu_append (menu, _("Date & Time Settings…"), "indicator.activate-desktop-settings"); + } + else if (profile == Phone) + { + g_menu_append (menu, _("Time & Date settings…"), "indicator.activate-phone-settings"); + } + + return G_MENU_MODEL (menu); + } + + void update_section(Section section) + { + GMenuModel * model; + const auto p = profile(); + + switch (section) + { + case Calendar: model = create_calendar_section(p); break; + case Appointments: model = create_appointments_section(p); break; + case Locations: model = create_locations_section(p); break; + case Settings: model = create_settings_section(p); break; + default: g_warn_if_reached(); + } + + if (model) + { + g_menu_remove(m_submenu, section); + g_menu_insert_section(m_submenu, section, nullptr, model); + g_object_unref(model); + } + } + +//private: + GVariant * m_serialized_alarm_icon = nullptr; + GVariant * m_serialized_calendar_icon = nullptr; + +}; // class MenuImpl + + + +/*** +**** +***/ + +class DesktopBaseMenu: public MenuImpl +{ +protected: + DesktopBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr& state_, + std::shared_ptr& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr(new DesktopFormatter(state_->clock))) + { + update_header(); + } + + GVariant* create_header_state() + { + const auto visible = m_state->show_clock.get(); + const auto title = _("Date and Time"); + auto label = g_variant_new_string(m_formatter->header.get().c_str()); + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "accessible-desc", label); + g_variant_builder_add(&b, "{sv}", "label", label); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string(title)); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean(visible)); + return g_variant_builder_end(&b); + } +}; + +class DesktopMenu: public DesktopBaseMenu +{ +public: + DesktopMenu(std::shared_ptr& state_, std::shared_ptr& actions_): + DesktopBaseMenu(Desktop,"desktop", state_, actions_) {} +}; + +class DesktopGreeterMenu: public DesktopBaseMenu +{ +public: + DesktopGreeterMenu(std::shared_ptr& state_, std::shared_ptr& actions_): + DesktopBaseMenu(DesktopGreeter,"desktop-greeter", state_, actions_) {} +}; + +class PhoneBaseMenu: public MenuImpl +{ +protected: + PhoneBaseMenu(Menu::Profile profile_, + const std::string& name_, + std::shared_ptr& state_, + std::shared_ptr& actions_): + MenuImpl(profile_, name_, state_, actions_, + std::shared_ptr(new PhoneFormatter(state_->clock))) + { + update_header(); + } + + GVariant* create_header_state() + { + // are there alarms? + bool has_alarms = false; + for(const auto& appointment : m_state->planner->upcoming.get()) + if((has_alarms = appointment.has_alarms)) + break; + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&b, "{sv}", "title", g_variant_new_string (_("Upcoming"))); + g_variant_builder_add(&b, "{sv}", "visible", g_variant_new_boolean (TRUE)); + if (has_alarms) + { + auto label = m_formatter->header.get(); + auto a11y = g_strdup_printf(_("%s (has alarms)"), label.c_str()); + g_variant_builder_add(&b, "{sv}", "label", g_variant_new_string(label.c_str())); + g_variant_builder_add(&b, "{sv}", "accessible-desc", g_variant_new_take_string(a11y)); + g_variant_builder_add(&b, "{sv}", "icon", get_serialized_alarm_icon()); + } + else + { + auto v = g_variant_new_string(m_formatter->header.get().c_str()); + g_variant_builder_add(&b, "{sv}", "label", v); + g_variant_builder_add(&b, "{sv}", "accessible-desc", v); + } + return g_variant_builder_end (&b); + } +}; + +class PhoneMenu: public PhoneBaseMenu +{ +public: + PhoneMenu(std::shared_ptr& state_, + std::shared_ptr& actions_): + PhoneBaseMenu(Phone, "phone", state_, actions_) {} +}; + +class PhoneGreeterMenu: public PhoneBaseMenu +{ +public: + PhoneGreeterMenu(std::shared_ptr& state_, + std::shared_ptr& actions_): + PhoneBaseMenu(PhoneGreeter, "phone-greeter", state_, actions_) {} +}; + +/**** +***** +****/ + +MenuFactory::MenuFactory(std::shared_ptr& actions_, + std::shared_ptr& state_): + m_actions(actions_), + m_state(state_) +{ +} + +std::shared_ptr +MenuFactory::buildMenu(Menu::Profile profile) +{ + std::shared_ptr menu; + m_state->show_clock.set (true); // FIXME + + //std::shared_ptr state(new State); + //state->clock = clock; + //state->planner = planner; + //state->locations = locations; + + switch (profile) + { + case Menu::Desktop: + m_state->show_events.set(true); // FIXME + menu.reset(new DesktopMenu(m_state, m_actions)); + break; + + case Menu::DesktopGreeter: + m_state->show_events.set(true); // FIXME + menu.reset(new DesktopGreeterMenu(m_state, m_actions)); + break; + + case Menu::Phone: + m_state->show_events.set(true); // FIXME + menu.reset(new PhoneMenu(m_state, m_actions)); + break; + + case Menu::PhoneGreeter: + m_state->show_events.set(false); // FIXME + menu.reset(new PhoneGreeterMenu(m_state, m_actions)); + break; + + default: + g_warn_if_reached(); + break; + } + + return menu; +} + +/**** +***** +****/ + +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 804d98e..6abaf3e 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -34,21 +34,21 @@ namespace datetime { ***** ****/ -G_DEFINE_QUARK ("source-client", source_client) +G_DEFINE_QUARK("source-client", source_client) class PlannerEds::Impl { public: - Impl (PlannerEds& owner): - owner_(owner), - cancellable_(g_cancellable_new()) + Impl(PlannerEds& owner): + m_owner(owner), + m_cancellable(g_cancellable_new()) { - e_source_registry_new (cancellable_, on_source_registry_ready, this); + e_source_registry_new(m_cancellable, on_source_registry_ready, this); - owner_.time.changed().connect([this](const DateTime& dt) { - g_message ("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); + m_owner.time.changed().connect([this](const DateTime& dt) { + g_message("planner's datetime property changed to %s; calling rebuildSoon()", g_date_time_format(dt.get(), "%F %T")); rebuildSoon(); }); @@ -57,46 +57,46 @@ public: ~Impl() { - g_cancellable_cancel (cancellable_); - g_clear_object (&cancellable_); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); - if (rebuild_tag_) - g_source_remove (rebuild_tag_); + if (m_rebuild_tag) + g_source_remove(m_rebuild_tag); - if (source_registry_) - g_signal_handlers_disconnect_by_data (source_registry_, this); - g_clear_object (&source_registry_); + if (m_source_registry) + g_signal_handlers_disconnect_by_data(m_source_registry, this); + g_clear_object(&m_source_registry); } private: - static void on_source_registry_ready (GObject* /*source*/, GAsyncResult* res, gpointer gself) + static void on_source_registry_ready(GObject* /*source*/, GAsyncResult* res, gpointer gself) { - GError * error = NULL; - auto r = e_source_registry_new_finish (res, &error); - if (error != NULL) + GError * error = nullptr; + auto r = e_source_registry_new_finish(res, &error); + if (error != nullptr) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot show EDS appointments: %s", error->message); - g_error_free (error); + g_error_free(error); } else { auto self = static_cast(gself); - g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self); - g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self); - g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self); - g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self); - g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self); + g_signal_connect(r, "source-added", G_CALLBACK(on_source_added), self); + g_signal_connect(r, "source-removed", G_CALLBACK(on_source_removed), self); + g_signal_connect(r, "source-changed", G_CALLBACK(on_source_changed), self); + g_signal_connect(r, "source-disabled", G_CALLBACK(on_source_disabled), self); + g_signal_connect(r, "source-enabled", G_CALLBACK(on_source_enabled), self); - self->source_registry_ = r; + self->m_source_registry = r; - GList* sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR); + GList* sources = e_source_registry_list_sources(r, E_SOURCE_EXTENSION_CALENDAR); for (auto l=sources; l!=nullptr; l=l->next) - on_source_added (r, E_SOURCE(l->data), gself); - g_list_free_full (sources, g_object_unref); + on_source_added(r, E_SOURCE(l->data), gself); + g_list_free_full(sources, g_object_unref); } } @@ -104,74 +104,74 @@ private: { auto self = static_cast(gself); - self->sources_.insert(E_SOURCE(g_object_ref(source))); + self->m_sources.insert(E_SOURCE(g_object_ref(source))); if (e_source_get_enabled(source)) on_source_enabled(registry, source, gself); } - static void on_source_enabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + static void on_source_enabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) { auto self = static_cast(gself); - e_cal_client_connect (source, - E_CAL_CLIENT_SOURCE_TYPE_EVENTS, - self->cancellable_, - on_client_connected, - gself); + e_cal_client_connect(source, + E_CAL_CLIENT_SOURCE_TYPE_EVENTS, + self->m_cancellable, + on_client_connected, + gself); } - static void on_client_connected (GObject* /*source*/, GAsyncResult * res, gpointer gself) + static void on_client_connected(GObject* /*source*/, GAsyncResult * res, gpointer gself) { GError * error = nullptr; - EClient * client = e_cal_client_connect_finish (res, &error); + EClient * client = e_cal_client_connect_finish(res, &error); if (error) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("indicator-datetime cannot connect to EDS source: %s", error->message); - g_error_free (error); + g_error_free(error); } else { // we've got a new connected ECalClient, so store it & notify clients - g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)), - source_client_quark(), - client, - g_object_unref); + g_object_set_qdata_full(G_OBJECT(e_client_get_source(client)), + source_client_quark(), + client, + g_object_unref); - g_message ("client connected; calling rebuildSoon()"); + g_message("client connected; calling rebuildSoon()"); static_cast(gself)->rebuildSoon(); } } - static void on_source_disabled (ESourceRegistry* /*registry*/, ESource* source, gpointer gself) + static void on_source_disabled(ESourceRegistry* /*registry*/, ESource* source, gpointer gself) { gpointer e_cal_client; // if this source has a connected ECalClient, remove it & notify clients - if ((e_cal_client = g_object_steal_qdata (G_OBJECT(source), source_client_quark()))) + if ((e_cal_client = g_object_steal_qdata(G_OBJECT(source), source_client_quark()))) { - g_object_unref (e_cal_client); + g_object_unref(e_cal_client); - g_message ("source disabled; calling rebuildSoon()"); + g_message("source disabled; calling rebuildSoon()"); static_cast(gself)->rebuildSoon(); } } - static void on_source_removed (ESourceRegistry* registry, ESource* source, gpointer gself) + static void on_source_removed(ESourceRegistry* registry, ESource* source, gpointer gself) { auto self = static_cast(gself); - on_source_disabled (registry, source, gself); + on_source_disabled(registry, source, gself); - self->sources_.erase (source); - g_object_unref (source); + self->m_sources.erase(source); + g_object_unref(source); } - static void on_source_changed (ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) + static void on_source_changed(ESourceRegistry* /*registry*/, ESource* /*source*/, gpointer gself) { - g_message ("source changed; calling rebuildSoon()"); + g_message("source changed; calling rebuildSoon()"); static_cast(gself)->rebuildSoon(); } @@ -181,44 +181,44 @@ private: struct Task { - Impl* impl_; - appointment_func func_; - std::vector appointments_; - Task (Impl* impl, const appointment_func& func): impl_(impl), func_(func) {} + Impl* p; + appointment_func func; + std::vector appointments; + Task(Impl* p_in, const appointment_func& func_in): p(p_in), func(func_in) {} }; struct AppointmentSubtask { - std::shared_ptr task_; - ECalClient * client_; - std::string color_; - AppointmentSubtask (const std::shared_ptr& task, ECalClient* client, const char* color): - task_(task), client_(client), color_(color) {} + std::shared_ptr task; + ECalClient* client; + std::string color; + AppointmentSubtask(const std::shared_ptr& task_in, ECalClient* client_in, const char* color_in): + task(task_in), client(client_in), color(color_in) {} }; void rebuildSoon() { const static guint ARBITRARY_INTERVAL_SECS = 2; - if (rebuild_tag_ == 0) - rebuild_tag_ = g_timeout_add_seconds (ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); + if (m_rebuild_tag == 0) + m_rebuild_tag = g_timeout_add_seconds(ARBITRARY_INTERVAL_SECS, rebuildNowStatic, this); } - static gboolean rebuildNowStatic (gpointer gself) + static gboolean rebuildNowStatic(gpointer gself) { auto self = static_cast(gself); - self->rebuild_tag_ = 0; + self->m_rebuild_tag = 0; self->rebuildNow(); return G_SOURCE_REMOVE; } void rebuildNow() { - GDateTime* calendar_date = owner_.time.get().get(); + auto calendar_date = m_owner.time.get().get(); GDateTime* begin; GDateTime* end; int y, m, d; - g_message ("in rebuildNow"); + g_message("in rebuildNow"); // get all the appointments in the calendar month g_date_time_get_ymd(calendar_date, &y, &m, &d); @@ -227,7 +227,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector& appointments) { - g_message ("got %d appointments in this calendar month", (int)appointments.size()); + g_message("got %d appointments in this calendar month", (int)appointments.size()); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -239,7 +239,7 @@ private: if (begin && end) { getAppointments(begin, end, [this](const std::vector& appointments) { - g_message ("got %d upcoming appointments", (int)appointments.size()); + g_message("got %d upcoming appointments", (int)appointments.size()); }); } g_clear_pointer(&begin, g_date_time_unref); @@ -251,8 +251,8 @@ private: { const auto begin = g_date_time_to_unix(begin_dt); const auto end = g_date_time_to_unix(end_dt); - g_message ("getting all appointments from [%s ... %s]", g_date_time_format (begin_dt, "%F %T"), - g_date_time_format (end_dt, "%F %T")); + g_message("getting all appointments from [%s ... %s]", g_date_time_format(begin_dt, "%F %T"), + g_date_time_format(end_dt, "%F %T")); /** *** init the default timezone @@ -260,75 +260,76 @@ private: icaltimezone * default_timezone = nullptr; - const auto tz = g_date_time_get_timezone_abbreviation (owner_.time.get().get()); - g_message ("%s tz is %s", G_STRLOC, tz); + const auto tz = g_date_time_get_timezone_abbreviation(m_owner.time.get().get()); + g_message("%s tz is %s", G_STRLOC, tz); if (tz && *tz) { - default_timezone = icaltimezone_get_builtin_timezone (tz); + default_timezone = icaltimezone_get_builtin_timezone(tz); if (default_timezone == nullptr) // maybe str is a tzid? - default_timezone = icaltimezone_get_builtin_timezone_from_tzid (tz); + default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz); - g_debug ("default_timezone is %p", default_timezone); + g_debug("default_timezone is %p", default_timezone); } /** *** walk through the sources to build the appointment list **/ - std::shared_ptr main_task (new Task(this, func), [](Task* task){ + std::shared_ptr main_task(new Task(this, func), [](Task* task){ g_message("time to delete task %p", task); - task->func_(task->appointments_); + task->func(task->appointments); }); - for (auto& source : sources_) + for (auto& source : m_sources) { - auto client = E_CAL_CLIENT (g_object_get_qdata (G_OBJECT(source), source_client_quark())); + auto client = E_CAL_CLIENT(g_object_get_qdata(G_OBJECT(source), source_client_quark())); if (client == nullptr) continue; if (default_timezone != nullptr) - e_cal_client_set_default_timezone (client, default_timezone); + e_cal_client_set_default_timezone(client, default_timezone); // start a new subtask to enumerate all the components in this client. - auto extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); - const auto color = e_source_selectable_get_color (E_SOURCE_SELECTABLE(extension)); - g_message ("calling e_cal_client_generate_instances for %p", client); - e_cal_client_generate_instances (client, - begin, - end, - cancellable_, - my_get_appointments_foreach, - new AppointmentSubtask (main_task, client, color), - [](gpointer g){delete static_cast(g);}); + auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR); + const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension)); + g_message("calling e_cal_client_generate_instances for %p", client); + e_cal_client_generate_instances(client, + begin, + end, + m_cancellable, + my_get_appointments_foreach, + new AppointmentSubtask (main_task, client, color), + [](gpointer g){delete static_cast(g);}); } } struct UrlSubtask { - std::shared_ptr task_; - Appointment appointment_; - UrlSubtask (const std::shared_ptr& task, const Appointment& appointment): task_(task), appointment_(appointment) {} + std::shared_ptr task; + Appointment appointment; + UrlSubtask(const std::shared_ptr& task_in, const Appointment& appointment_in): + task(task_in), appointment(appointment_in) {} }; static gboolean - my_get_appointments_foreach (ECalComponent* component, - time_t begin, - time_t end, - gpointer gsubtask) + my_get_appointments_foreach(ECalComponent* component, + time_t begin, + time_t end, + gpointer gsubtask) { const auto vtype = e_cal_component_get_vtype(component); auto subtask = static_cast(gsubtask); if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO)) { - const gchar* uid = NULL; - e_cal_component_get_uid (component, &uid); + const gchar* uid = nullptr; + e_cal_component_get_uid(component, &uid); auto status = ICAL_STATUS_NONE; - e_cal_component_get_status (component, &status); + e_cal_component_get_status(component, &status); - if ((uid != NULL) && + if ((uid != nullptr) && (status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED)) { @@ -339,78 +340,78 @@ private: however, since design only allows daily recurrence, that's all we support here. */ GSList * recur_list; - e_cal_component_get_rrule_list (component, &recur_list); - for (auto l=recur_list; l!=NULL; l=l->next) + e_cal_component_get_rrule_list(component, &recur_list); + for (auto l=recur_list; l!=nullptr; l=l->next) { const auto recur = static_cast(l->data); appointment.is_daily |= ((recur->freq == ICAL_DAILY_RECURRENCE) && (recur->interval == 1)); } - e_cal_component_free_recur_list (recur_list); + e_cal_component_free_recur_list(recur_list); ECalComponentText text; text.value = ""; - e_cal_component_get_summary (component, &text); + e_cal_component_get_summary(component, &text); - appointment.begin = g_date_time_new_from_unix_local (begin); - appointment.end = g_date_time_new_from_unix_local (end); - appointment.color = subtask->color_; + appointment.begin = g_date_time_new_from_unix_local(begin); + appointment.end = g_date_time_new_from_unix_local(end); + appointment.color = subtask->color; appointment.is_event = vtype == E_CAL_COMPONENT_EVENT; appointment.summary = text.value; appointment.uid = uid; - GList * alarm_uids = e_cal_component_get_alarm_uids (component); + GList * alarm_uids = e_cal_component_get_alarm_uids(component); appointment.has_alarms = alarm_uids != nullptr; - cal_obj_uid_list_free (alarm_uids); - - e_cal_client_get_attachment_uris (subtask->client_, - uid, - NULL, - subtask->task_->impl_->cancellable_, - on_appointment_uris_ready, - new UrlSubtask(subtask->task_, appointment)); + cal_obj_uid_list_free(alarm_uids); + + e_cal_client_get_attachment_uris(subtask->client, + uid, + nullptr, + subtask->task->p->m_cancellable, + on_appointment_uris_ready, + new UrlSubtask(subtask->task, appointment)); } } return G_SOURCE_CONTINUE; } - static void on_appointment_uris_ready (GObject* client, GAsyncResult* res, gpointer gsubtask) + static void on_appointment_uris_ready(GObject* client, GAsyncResult* res, gpointer gsubtask) { auto subtask = static_cast(gsubtask); GSList * uris = nullptr; GError * error = nullptr; - e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error); - if (error != NULL) + e_cal_client_get_attachment_uris_finish(E_CAL_CLIENT(client), res, &uris, &error); + if (error != nullptr) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Error getting appointment uris: %s", error->message); + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Error getting appointment uris: %s", error->message); - g_error_free (error); + g_error_free(error); } - else if (uris != NULL) + else if (uris != nullptr) { - subtask->appointment_.url = (const char*) uris->data; // copy the first URL - g_debug ("found url '%s' for appointment '%s'", subtask->appointment_.url.c_str(), subtask->appointment_.summary.c_str()); - e_client_util_free_string_slist (uris); + subtask->appointment.url = (const char*) uris->data; // copy the first URL + g_debug("found url '%s' for appointment '%s'", subtask->appointment.url.c_str(), subtask->appointment.summary.c_str()); + e_client_util_free_string_slist(uris); } - g_message ("adding appointment '%s' '%s'", subtask->appointment_.summary.c_str(), subtask->appointment_.url.c_str()); - subtask->task_->appointments_.push_back (subtask->appointment_); + g_message("adding appointment '%s' '%s'", subtask->appointment.summary.c_str(), subtask->appointment.url.c_str()); + subtask->task->appointments.push_back(subtask->appointment); delete subtask; } private: - PlannerEds& owner_; - std::set sources_; - GCancellable * cancellable_ = nullptr; - ESourceRegistry * source_registry_ = nullptr; - guint rebuild_tag_ = 0; + PlannerEds& m_owner; + std::set m_sources; + GCancellable * m_cancellable = nullptr; + ESourceRegistry * m_source_registry = nullptr; + guint m_rebuild_tag = 0; }; -PlannerEds::PlannerEds(): impl_(new Impl(*this)) {} +PlannerEds::PlannerEds(): p(new Impl(*this)) {} PlannerEds::~PlannerEds() =default; diff --git a/src/planner-eds.h b/src/planner-eds.h deleted file mode 100644 index dea9371..0000000 --- a/src/planner-eds.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_PLANNER_EDS__H__ -#define __INDICATOR_DATETIME_PLANNER_EDS__H__ - -#include "planner.h" /* parent class */ - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_PLANNER_EDS (indicator_datetime_planner_eds_get_type()) -#define INDICATOR_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEds)) -#define INDICATOR_DATETIME_PLANNER_EDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEdsClass)) -#define INDICATOR_IS_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS)) - -typedef struct _IndicatorDatetimePlannerEds IndicatorDatetimePlannerEds; -typedef struct _IndicatorDatetimePlannerEdsPriv IndicatorDatetimePlannerEdsPriv; -typedef struct _IndicatorDatetimePlannerEdsClass IndicatorDatetimePlannerEdsClass; - -GType indicator_datetime_planner_eds_get_type (void); - -/** - * An IndicatorDatetimePlanner which uses Evolution Data Server - * to get its list of appointments. - */ -struct _IndicatorDatetimePlannerEds -{ - /*< private >*/ - IndicatorDatetimePlanner parent; - IndicatorDatetimePlannerEdsPriv * priv; -}; - -struct _IndicatorDatetimePlannerEdsClass -{ - IndicatorDatetimePlannerClass parent_class; -}; - -IndicatorDatetimePlanner * indicator_datetime_planner_eds_new (void); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_PLANNER_EDS__H__ */ diff --git a/src/planner.h b/src/planner.h deleted file mode 100644 index ffe8937..0000000 --- a/src/planner.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __INDICATOR_DATETIME_PLANNER__H__ -#define __INDICATOR_DATETIME_PLANNER__H__ - -#include -#include /* parent class */ -#include - -G_BEGIN_DECLS - -#define INDICATOR_TYPE_DATETIME_PLANNER (indicator_datetime_planner_get_type()) -#define INDICATOR_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlanner)) -#define INDICATOR_DATETIME_PLANNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) -#define INDICATOR_DATETIME_PLANNER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass)) -#define INDICATOR_IS_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER)) - -typedef struct _IndicatorDatetimePlanner IndicatorDatetimePlanner; -typedef struct _IndicatorDatetimePlannerPriv IndicatorDatetimePlannerPriv; -typedef struct _IndicatorDatetimePlannerClass IndicatorDatetimePlannerClass; - -GType indicator_datetime_planner_get_type (void); - -struct IndicatorDatetimeAppt -{ - gchar * color; - gchar * summary; - gchar * url; - gchar * uid; - GDateTime * begin; - GDateTime * end; - gboolean is_event; - gboolean is_daily; - gboolean has_alarms; -}; - -/** - * Abstract Base Class for objects that provides appointments and events. - * - * These will be listed in the appointments section of indicator-datetime's menu. - */ -struct _IndicatorDatetimePlanner -{ - /*< private >*/ - GObject parent; - IndicatorDatetimePlannerPriv * priv; -}; - -struct _IndicatorDatetimePlannerClass -{ - GObjectClass parent_class; - - /* signals */ - - void (*appointments_changed) (IndicatorDatetimePlanner * self); - - /* virtual functions */ - - void (*get_appointments) (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data); - - GSList* (*get_appointments_finish) (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error); - - - gboolean (*is_configured) (IndicatorDatetimePlanner * self); - void (*activate) (IndicatorDatetimePlanner * self); - void (*activate_time) (IndicatorDatetimePlanner * self, GDateTime *); -}; - -/*** -**** -***/ - -void indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt); - -/** - * Get a list of appointments, sorted by start time. - */ -void indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, - GDateTime * begin, - GDateTime * end, - GAsyncReadyCallback callback, - gpointer user_data); - -/** - * Finishes the async call begun with indicator_datetime_planner_get_appointments() - * - * To free the list properly, use indicator_datetime_planner_free_appointments() - * - * Return value: (element-type IndicatorDatetimeAppt) - * (transfer full): - * list of appointments - */ -GSList * indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self, - GAsyncResult * res, - GError ** error); - -/** - * Convenience function for freeing a GSList of IndicatorDatetimeAppt. - * - * Equivalent to g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free); - */ -void indicator_datetime_planner_free_appointments (GSList *); - - -/** - * Returns false if the planner's backend is not configured. - * - * This can be used on startup to determine whether or not to use this planner. - */ -gboolean indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self); - -/** - * Activate this planner. - * - * This is used to activate the planner's backend's event editor. - */ -void indicator_datetime_planner_activate (IndicatorDatetimePlanner * self); - -/** - * Activate this planner. - * - * This is used to activate the planner's backend's event editor, - * with an added hint of the specific time that the user would like to edit. - */ -void indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time); - -/** - * Set the timezone. - * - * This is used as a default timezone if the backend's events don't provide their own. - */ -void indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone); - -const char * indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self); - - -/** - * Emits the "appointments-changed" signal. This should only be called by subclasses. - */ -void indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self); - -G_END_DECLS - -#endif /* __INDICATOR_DATETIME_PLANNER__H__ */ diff --git a/src/service.cpp b/src/service.cpp new file mode 100644 index 0000000..0671c61 --- /dev/null +++ b/src/service.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +Service::~Service() +{ + if (m_dbus_connection != nullptr) + { + for(auto& id : m_exported_menu_ids) + g_dbus_connection_unexport_menu_model(m_dbus_connection, id); + + if (m_exported_actions_id) + g_dbus_connection_unexport_action_group(m_dbus_connection, m_exported_actions_id); + } + + if (m_own_id) + g_bus_unown_name(m_own_id); + + g_clear_object(&m_dbus_connection); +} + +/*** +**** +***/ + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("bus acquired: %s", name); + static_cast(gthis)->on_bus_acquired(connection, name); +} + +void +Service::on_bus_acquired(GDBusConnection* connection, const gchar* /*name*/) +{ + m_dbus_connection = static_cast(g_object_ref(G_OBJECT(connection))); + + // export the actions + GError * error = nullptr; + const auto id = g_dbus_connection_export_action_group(m_dbus_connection, BUS_PATH, m_actions, &error); + if (id) + { + m_exported_actions_id = id; + } + else + { + g_warning("cannot export action group: %s", error->message); + g_clear_error(&error); + } + + // export the menus + for(auto& menu : m_menus) + { + const auto path = std::string(BUS_PATH) + "/" + menu->name(); + const auto id = g_dbus_connection_export_menu_model(m_dbus_connection, path.c_str(), menu->menu_model(), &error); + if (id) + { + m_exported_menu_ids.insert(id); + } + else + { + g_warning("cannot export %s menu: %s", menu->name().c_str(), error->message); + g_clear_error(&error); + } + } +} + +/*** +**** +***/ + +void +Service::on_name_lost(GDBusConnection* connection, const gchar* name, gpointer gthis) +{ + g_debug("name lost: %s", name); + static_cast(gthis)->on_name_lost(connection, name); +} + +void +Service::on_name_lost(GDBusConnection* /*connection*/, const gchar* /*name*/) +{ + name_lost(); +} + +/*** +**** +***/ + +void +Service::publish(GActionGroup* actions, std::vector>& menus) +{ + m_actions = actions; + m_menus = menus; + m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + nullptr, + on_name_lost, + this, + nullptr); +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + diff --git a/src/settings-live.cpp b/src/settings-live.cpp new file mode 100644 index 0000000..74085a9 --- /dev/null +++ b/src/settings-live.cpp @@ -0,0 +1,293 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + +LiveSettings::~LiveSettings() +{ + g_clear_object(&m_settings); +} + +LiveSettings::LiveSettings(): + m_settings(g_settings_new(SETTINGS_INTERFACE)) +{ + g_signal_connect (m_settings, "changed", G_CALLBACK(on_changed), this); + + update_show_clock(); +} + +void LiveSettings::update_show_clock() +{ + auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S); + show_clock.set(val); +} + +void LiveSettings::on_changed(GSettings* /*settings*/, + gchar* key, + gpointer gself) +{ + static_cast(gself)->update_key(key); +} + +void LiveSettings::update_key(const std::string& key) +{ + if (key == SETTINGS_SHOW_CLOCK_S) + { + update_show_clock(); + } +} + +/*** +**** +***/ + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#if 0 + else if (!g_strcmp0(key, TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_SECONDS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DAY_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DATE_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_YEAR_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_CUSTOM_TIME_FORMAT_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_CALENDAR_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_WEEK_NUMBERS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_EVENTS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_LOCATIONS_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_TIMEZONE_NAME_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_SHOW_DETECTED_S)) + { + } + else if (!g_strcmp0(key, SETTINGS_LOCATIONS_S)) + { + } +#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" +#define SETTINGS_LOCATIONS_S "locations" +#define SETTINGS_TIMEZONE_NAME_S "timezone-name" + zzz + "show-clock" + +void user_function (GSettings *settings, + gchar *key, + gpointer user_data) : Has Details + + for (i=0, n=G_N_ELEMENTS(header_settings); isettings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + + + const char * const calendar_settings[] = { + SETTINGS_SHOW_CALENDAR_S, + SETTINGS_SHOW_WEEK_NUMBERS_S + }; + const char * const appointment_settings[] = { + SETTINGS_SHOW_EVENTS_S, + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S + }; + const char * const location_settings[] = { + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S, + SETTINGS_CUSTOM_TIME_FORMAT_S, + SETTINGS_SHOW_LOCATIONS_S, + SETTINGS_LOCATIONS_S, + SETTINGS_SHOW_DETECTED_S, + SETTINGS_TIMEZONE_NAME_S + }; + const char * const time_format_string_settings[] = { + SETTINGS_TIME_FORMAT_S, + SETTINGS_SHOW_SECONDS_S, + SETTINGS_CUSTOM_TIME_FORMAT_S + }; + + + /*** + **** Listen for settings changes + ***/ + + for (i=0, n=G_N_ELEMENTS(header_settings); isettings, gstr->str, + G_CALLBACK(rebuild_header_soon), self); + } + +} + + +#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H +#define INDICATOR_DATETIME_SETTINGS_LIVE_H + +#include // parent class + +#include // GSettings + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief #Settings implementation which uses GSettings. + */ +class LiveSettings +{ +public: + LiveSettings(); + virtual ~LiveSettings(); + +private: + GSettings* m_settings; + + // we've got a raw pointer here, so disable copying + LiveSettings(const LiveSettings&) =delete; + LiveSettings& operator=(const LiveSettings&) =delete; +}; + + +#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H +/* + * Copyright 2010 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Ted Gould + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_SHARED +#define INDICATOR_DATETIME_SETTINGS_SHARED + +typedef enum +{ + TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM +} +TimeFormatMode; + + +#endif // INDICATOR_DATETIME_SETTINGS_SHARED +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_SETTINGS_H +#define INDICATOR_DATETIME_SETTINGS_H + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +/** + * \brief Interface that represents user-configurable settings. + * + * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml + * For more information. + */ +class Settings +{ +public: + Settings() =default; + virtual ~Settings =default; + + core::Property time_format_mode; + core::Property show_clock; + core::Property show_day; + core::Property show_year; + core::Property show_seconds; + core::Property custom_time_format; + core::Property show_calendar; + core::Property show_events; + core::Property show_locations; + core::Property show_auto_detected_location; + core::Property> locations; + core::Property timezone_name; +}; + +#endif // INDICATOR_DATETIME_SETTINGS_H +#endif diff --git a/src/timezone-file.cpp b/src/timezone-file.cpp index f1d0dca..3a0c240 100644 --- a/src/timezone-file.cpp +++ b/src/timezone-file.cpp @@ -26,12 +26,12 @@ namespace datetime { void FileTimezone::clear() { - if (monitor_handler_id_) - g_signal_handler_disconnect(monitor_, monitor_handler_id_); + if (m_monitor_handler_id) + g_signal_handler_disconnect(m_monitor, m_monitor_handler_id); - g_clear_object (&monitor_); + g_clear_object (&m_monitor); - filename_.clear(); + m_filename.clear(); } void @@ -39,11 +39,11 @@ FileTimezone::setFilename(const std::string& filename) { clear(); - filename_ = filename; + m_filename = filename; auto file = g_file_new_for_path(filename.c_str()); GError * err = nullptr; - monitor_ = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); + m_monitor = g_file_monitor_file(file, G_FILE_MONITOR_NONE, nullptr, &err); g_object_unref(file); if (err) { @@ -52,7 +52,7 @@ FileTimezone::setFilename(const std::string& filename) } else { - monitor_handler_id_ = g_signal_connect_swapped(monitor_, "changed", G_CALLBACK(onFileChanged), this); + m_monitor_handler_id = g_signal_connect_swapped(m_monitor, "changed", G_CALLBACK(onFileChanged), this); g_debug("%s Monitoring timezone file '%s'", G_STRLOC, filename.c_str()); } @@ -71,9 +71,9 @@ FileTimezone::reload() GError * err = nullptr; gchar * str = nullptr; - if (!g_file_get_contents(filename_.c_str(), &str, nullptr, &err)) + if (!g_file_get_contents(m_filename.c_str(), &str, nullptr, &err)) { - g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, filename_.c_str(), err->message); + g_warning("%s Unable to read timezone file '%s': %s", G_STRLOC, m_filename.c_str(), err->message); g_error_free(err); } else diff --git a/src/timezone-geoclue.cpp b/src/timezone-geoclue.cpp index f6d1f5e..b8847a4 100644 --- a/src/timezone-geoclue.cpp +++ b/src/timezone-geoclue.cpp @@ -27,20 +27,20 @@ namespace datetime { GeoclueTimezone::GeoclueTimezone(): - cancellable_(g_cancellable_new()) + m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SESSION, cancellable_, on_bus_got, this); + g_bus_get(G_BUS_TYPE_SESSION, m_cancellable, on_bus_got, this); } GeoclueTimezone::~GeoclueTimezone() { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); + g_cancellable_cancel(m_cancellable); + g_object_unref(m_cancellable); - if (signal_subscription_) - g_dbus_connection_signal_unsubscribe(connection_, signal_subscription_); + if (m_signal_subscription) + g_dbus_connection_signal_unsubscribe(m_connection, m_signal_subscription); - g_object_unref(connection_); + g_object_unref(m_connection); } /*** @@ -48,7 +48,9 @@ GeoclueTimezone::~GeoclueTimezone() ***/ void -GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +GeoclueTimezone::on_bus_got(GObject* /*source*/, + GAsyncResult* res, + gpointer gself) { GError * error; GDBusConnection * connection; @@ -66,9 +68,9 @@ GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, { auto self = static_cast(gself); - self->connection_ = connection; + self->m_connection = connection; - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, "/org/freedesktop/Geoclue/Master", "org.freedesktop.Geoclue.Master", @@ -77,7 +79,7 @@ GeoclueTimezone::on_bus_got(GObject * source G_GNUC_UNUSED, GAsyncResult * res, G_VARIANT_TYPE("(o)"), G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_client_created, self); } @@ -93,51 +95,51 @@ GeoclueTimezone::on_client_created(GObject * source, GAsyncResult * res, gpointe auto self = static_cast(gself); GVariant * child = g_variant_get_child_value(result, 0); - self->client_object_path_ = g_variant_get_string(child, nullptr); + self->m_client_object_path = g_variant_get_string(child, nullptr); g_variant_unref(child); g_variant_unref(result); - self->signal_subscription_ = g_dbus_connection_signal_subscribe( - self->connection_, + self->m_signal_subscription = g_dbus_connection_signal_subscribe( + self->m_connection, GEOCLUE_BUS_NAME, "org.freedesktop.Geoclue.Address", // inteface "AddressChanged", // signal name - self->client_object_path_.c_str(), // object path + self->m_client_object_path.c_str(), // object path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, on_address_changed, self, nullptr); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.MasterClient", "SetRequirements", g_variant_new("(iibi)", 2, 0, FALSE, 1023), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_requirements_set, self); } } void -GeoclueTimezone::on_address_changed(GDBusConnection * connection G_GNUC_UNUSED, - const gchar * sender_name G_GNUC_UNUSED, - const gchar * object_path G_GNUC_UNUSED, - const gchar * interface_name G_GNUC_UNUSED, - const gchar * signal_name G_GNUC_UNUSED, - GVariant * parameters, - gpointer gself) +GeoclueTimezone::on_address_changed(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* parameters, + gpointer gself) { static_cast(gself)->setTimezoneFromAddressVariant(parameters); } void -GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpointer gself) +GeoclueTimezone::on_requirements_set(GObject* source, GAsyncResult* res, gpointer gself) { GVariant * result; @@ -145,16 +147,16 @@ GeoclueTimezone::on_requirements_set(GObject * source, GAsyncResult * res, gpoin { auto self = static_cast(gself); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.MasterClient", "AddressStart", nullptr, nullptr, G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_address_started, self); @@ -171,16 +173,16 @@ GeoclueTimezone::on_address_started(GObject * source, GAsyncResult * res, gpoint { auto self = static_cast(gself); - g_dbus_connection_call(self->connection_, + g_dbus_connection_call(self->m_connection, GEOCLUE_BUS_NAME, - self->client_object_path_.c_str(), + self->m_client_object_path.c_str(), "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), G_DBUS_CALL_FLAGS_NONE, -1, - self->cancellable_, + self->m_cancellable, on_address_got, self); diff --git a/src/timezones-live.cpp b/src/timezones-live.cpp index cb5e2bc..dc14021 100644 --- a/src/timezones-live.cpp +++ b/src/timezones-live.cpp @@ -24,37 +24,35 @@ namespace unity { namespace indicator { namespace datetime { -LiveTimezones::LiveTimezones (const std::string& filename): - file_ (filename) +LiveTimezones::LiveTimezones(const std::string& filename): + m_file(filename) { - file_.timezone.changed().connect([this](const std::string&){updateTimezones();}); + m_file.timezone.changed().connect([this](const std::string&){update_timezones();}); - geolocationEnabled.changed().connect([this](bool){updateGeolocation();}); - updateGeolocation(); + geolocation_enabled.changed().connect([this](bool){update_geolocation();}); + update_geolocation(); - updateTimezones(); + update_timezones(); } -void -LiveTimezones::updateGeolocation() +void LiveTimezones::update_geolocation() { - geo_.reset(); + m_geo.reset(); - if (geolocationEnabled.get()) + if(geolocation_enabled.get()) { - GeoclueTimezone * geo = new GeoclueTimezone(); - geo->timezone.changed().connect([this](const std::string&){updateTimezones();}); - geo_.reset(geo); + auto geo = new GeoclueTimezone(); + geo->timezone.changed().connect([this](const std::string&){update_timezones();}); + m_geo.reset(geo); } } -void -LiveTimezones::updateTimezones() +void LiveTimezones::update_timezones() { - const std::string a = file_.timezone.get(); - const std::string b = geo_ ? geo_->timezone.get() : ""; + const auto a = m_file.timezone.get(); + const auto b = m_geo ? m_geo->timezone.get() : ""; - timezone.set (a.empty() ? b : a); + timezone.set(a.empty() ? b : a); std::set zones; if (!a.empty()) diff --git a/src/utils.cpp b/src/utils.cpp index 42e034e..acd9796 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -37,104 +37,101 @@ with this program. If not, see . /* Check the system locale setting to see if the format is 24-hour time or 12-hour time */ gboolean -is_locale_12h (void) +is_locale_12h() { - static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", NULL}; - const char *t_fmt = nl_langinfo (T_FMT); - int i; + const char *t_fmt = nl_langinfo(T_FMT); - for (i = 0; formats_24h[i]; ++i) { - if (strstr (t_fmt, formats_24h[i])) { - return FALSE; - } - } + static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k"}; + for(const auto& format : formats_24h) + if(strstr(t_fmt, format) != nullptr) + return false; - return TRUE; + return true; } void -split_settings_location (const gchar * location, gchar ** zone, gchar ** name) +split_settings_location(const gchar* location, gchar** zone, gchar** name) { - gchar * location_dup; - gchar * first; + auto location_dup = g_strdup(location); + g_strstrip(location_dup); - location_dup = g_strdup (location); - g_strstrip (location_dup); + gchar* first; + if((first = strchr(location_dup, ' '))) + *first = '\0'; - if ((first = strchr (location_dup, ' '))) - *first = '\0'; - - if (zone != NULL) + if(zone) { - *zone = location_dup; + *zone = location_dup; } - if (name != NULL) + if(name != nullptr) { - gchar * after = first ? g_strstrip (first + 1) : NULL; + gchar* after = first ? g_strstrip(first + 1) : nullptr; - if (after && *after) + if(after && *after) { - *name = g_strdup (after); + *name = g_strdup(after); } - else /* make the name from zone */ + else // make the name from zone { - gchar * chr = strrchr (location_dup, '/'); - after = g_strdup (chr ? chr + 1 : location_dup); + gchar * chr = strrchr(location_dup, '/'); + after = g_strdup(chr ? chr + 1 : location_dup); - /* replace underscores with spaces */ - for (chr=after; chr && *chr; chr++) - if (*chr == '_') - *chr = ' '; + // replace underscores with spaces + for(chr=after; chr && *chr; chr++) + if(*chr == '_') + *chr = ' '; - *name = after; + *name = after; } } } -gchar * -get_current_zone_name (const gchar * location, GSettings * settings) +gchar* +get_current_zone_name(const gchar* location, GSettings* settings) { - gchar * new_zone, * new_name; - gchar * tz_name; - gchar * old_zone, * old_name; - gchar * rv; - - split_settings_location (location, &new_zone, &new_name); - - tz_name = g_settings_get_string (settings, SETTINGS_TIMEZONE_NAME_S); - split_settings_location (tz_name, &old_zone, &old_name); - g_free (tz_name); - - /* new_name is always just a sanitized version of a timezone. - old_name is potentially a saved "pretty" version of a timezone name from - geonames. So we prefer to use it if available and the zones match. */ - - if (g_strcmp0 (old_zone, new_zone) == 0) { - rv = old_name; - old_name = NULL; - } - else { - rv = new_name; - new_name = NULL; - } - - g_free (new_zone); - g_free (old_zone); - g_free (new_name); - g_free (old_name); - - return rv; + gchar* new_zone; + gchar* new_name; + split_settings_location(location, &new_zone, &new_name); + + auto tz_name = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); + gchar* old_zone; + gchar* old_name; + split_settings_location(tz_name, &old_zone, &old_name); + g_free(tz_name); + + /* new_name is always just a sanitized version of a timezone. + old_name is potentially a saved "pretty" version of a timezone name from + geonames. So we prefer to use it if available and the zones match. */ + + gchar* rv; + if (g_strcmp0(old_zone, new_zone) == 0) + { + rv = old_name; + old_name = nullptr; + } + else + { + rv = new_name; + new_name = nullptr; + } + + g_free(new_zone); + g_free(old_zone); + g_free(new_name); + g_free(old_name); + return rv; } gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) { - using unity::indicator::datetime::Clock; - using unity::indicator::datetime::MockClock; - using unity::indicator::datetime::DesktopFormatter; - - std::shared_ptr clock(new MockClock(now)); - DesktopFormatter formatter(clock); - return g_strdup (formatter.getRelativeFormat(then).c_str()); + using unity::indicator::datetime::Clock; + using unity::indicator::datetime::DateTime; + using unity::indicator::datetime::MockClock; + using unity::indicator::datetime::DesktopFormatter; + + std::shared_ptr clock(new MockClock(DateTime(now))); + DesktopFormatter formatter(clock); + return g_strdup(formatter.getRelativeFormat(then).c_str()); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a424858..fb55f5a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,12 +32,6 @@ include_directories (${DBUSTEST_INCLUDE_DIRS}) add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") -# test-core -set (TEST_NAME test-core) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - # test-timezone-file set (TEST_NAME test-timezone-file) add_executable (${TEST_NAME} ${TEST_NAME}.cc) @@ -76,18 +70,31 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) -# test-planner-eds -set (TEST_NAME test-planner-eds) +# test-locations +set (TEST_NAME test-locations) add_executable (${TEST_NAME} ${TEST_NAME}.cc) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) -# test-locations -set (TEST_NAME test-locations) +# test-actions +set (TEST_NAME test-actions) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-menus +set (TEST_NAME test-menus) add_executable (${TEST_NAME} ${TEST_NAME}.cc) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-desktop +#set (TEST_NAME test-desktop) +#add_executable (${TEST_NAME} ${TEST_NAME}.cc) +#add_test (${TEST_NAME} ${TEST_NAME}) +#add_dependencies (${TEST_NAME} libindicatordatetimeservice) +#target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) diff --git a/tests/Makefile.am.strings b/tests/Makefile.am.strings deleted file mode 100644 index 4a89e8f..0000000 --- a/tests/Makefile.am.strings +++ /dev/null @@ -1,38 +0,0 @@ -TESTS += \ - test-ellipsis \ - test-space-ellipsis \ - test-ascii-quotes - -##### -# Tests for there being proper ellipsis instead of three periods in a row -##### -test-ellipsis: $(top_srcdir)/po - @echo "#!/bin/bash" > $@ - @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@ - @echo "grep -c -e \"^msgid.*\.\.\.\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"Ellipsis found in user visible strings\" >&2 && exit 1" >> $@ - @echo "exit 0" >> $@ - @chmod +x $@ - -##### -# Tests for there being a space before an ellipsis -##### -test-space-ellipsis: $(top_srcdir)/po - @echo "#!/bin/bash" > $@ - @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@ - @echo "grep -c -e \"^msgid.* …\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"Space before ellipsis found in user visible strings\" >&2 && exit 1" >> $@ - @echo "exit 0" >> $@ - @chmod +x $@ - -##### -# Tests for ASCII quote types -##### -test-ascii-quotes: $(top_srcdir)/po - @echo "#!/bin/bash" > $@ - @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@ - @echo "grep -c -e \"^msgid \\\".*'.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII apostrophe found in user visible strings\" >&2 && exit 1" >> $@ - @echo "grep -c -e \"^msgid \\\".*\\\".*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII quote found in user visible strings\" >&2 && exit 1" >> $@ - @echo "grep -c -e \"^msgid \\\".*\\\`.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII backtick found in user visible strings\" >&2 && exit 1" >> $@ - @echo "exit 0" >> $@ - @chmod +x $@ - -CLEANFILES += $(TESTS) diff --git a/tests/actions-mock.h b/tests/actions-mock.h new file mode 100644 index 0000000..112900b --- /dev/null +++ b/tests/actions-mock.h @@ -0,0 +1,88 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#ifndef INDICATOR_DATETIME_ACTIONS_MOCK_H +#define INDICATOR_DATETIME_ACTIONS_MOCK_H + +#include + +#include + +namespace unity { +namespace indicator { +namespace datetime { + +class MockActions: public Actions +{ +public: + MockActions(std::shared_ptr& state_in): Actions(state_in) {} + ~MockActions() =default; + + enum Action { OpenDesktopSettings, OpenPhoneSettings, OpenPhoneClockApp, + OpenPlanner, OpenPlannerAt, OpenAppointment, + SetLocation, SetCalendarDate }; + const std::vector& history() const { return m_history; } + const DateTime& date_time() const { return m_date_time; } + const std::string& zone() const { return m_zone; } + const std::string& name() const { return m_name; } + const std::string& url() const { return m_url; } + void clear() { m_history.clear(); m_zone.clear(); m_name.clear(); } + + void open_desktop_settings() { m_history.push_back(OpenDesktopSettings); } + + void open_phone_settings() { m_history.push_back(OpenPhoneSettings); } + + void open_phone_clock_app() { m_history.push_back(OpenPhoneClockApp); } + + void open_planner() { m_history.push_back(OpenPlanner); } + + void open_planner_at(const DateTime& date_time_) { + m_history.push_back(OpenPlannerAt); + m_date_time = date_time_; + } + + void set_location(const std::string& zone_, const std::string& name_) { + m_history.push_back(SetLocation); + m_zone = zone_; + m_name = name_; + } + + void open_appointment(const std::string& url_) { + m_history.push_back(OpenAppointment); + m_url = url_; + } + + void set_calendar_date(const DateTime& date_time_) { + m_history.push_back(SetCalendarDate); + m_date_time = date_time_; + } + +private: + std::string m_url; + std::string m_zone; + std::string m_name; + DateTime m_date_time; + std::vector m_history; +}; + +} // namespace datetime +} // namespace indicator +} // namespace unity + +#endif // INDICATOR_DATETIME_ACTIONS_MOCK_H diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 043b7e3..3f517d4 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -25,6 +25,8 @@ #include +#include // setlocale() + class GlibFixture : public ::testing::Test { private: @@ -35,96 +37,101 @@ class GlibFixture : public ::testing::Test std::map logCounts; - void testLogCount (GLogLevelFlags log_level, int expected G_GNUC_UNUSED) + void testLogCount(GLogLevelFlags log_level, int /*expected*/) { #if 0 - EXPECT_EQ (expected, logCounts[log_level]); + EXPECT_EQ(expected, logCounts[log_level]); #endif - logCounts.erase (log_level); + logCounts.erase(log_level); } private: - static void default_log_handler (const gchar * log_domain, - GLogLevelFlags log_level, - const gchar * message, - gpointer self) + static void default_log_handler(const gchar * log_domain, + GLogLevelFlags log_level, + const gchar * message, + gpointer self) { - g_print ("%s - %d - %s\n", log_domain, (int)log_level, message); + g_print("%s - %d - %s\n", log_domain, (int)log_level, message); static_cast(self)->logCounts[log_level]++; } protected: - virtual void SetUp () + virtual void SetUp() { - loop = g_main_loop_new (NULL, FALSE); + setlocale(LC_ALL, ""); + + loop = g_main_loop_new(nullptr, false); - //g_log_set_default_handler (default_log_handler, this); + //g_log_set_default_handler(default_log_handler, this); // only use local, temporary settings - g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); - g_setenv ("GSETTINGS_BACKEND", "memory", TRUE); - g_debug ("SCHEMA_DIR is %s", SCHEMA_DIR); + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); + + g_unsetenv("DISPLAY"); + } virtual void TearDown() { #if 0 // confirm there aren't any unexpected log messages - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_ERROR]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_CRITICAL]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_WARNING]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_MESSAGE]); - EXPECT_EQ (0, logCounts[G_LOG_LEVEL_INFO]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_ERROR]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_CRITICAL]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_WARNING]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_MESSAGE]); + EXPECT_EQ(0, logCounts[G_LOG_LEVEL_INFO]); #endif // revert to glib's log handler - //g_log_set_default_handler (realLogHandler, this); + //g_log_set_default_handler(realLogHandler, this); - g_clear_pointer (&loop, g_main_loop_unref); + g_clear_pointer(&loop, g_main_loop_unref); } private: static gboolean - wait_for_signal__timeout (gpointer name) + wait_for_signal__timeout(gpointer name) { - g_error ("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); + g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); return G_SOURCE_REMOVE; } static gboolean - wait_msec__timeout (gpointer loop) + wait_msec__timeout(gpointer loop) { - g_main_loop_quit (static_cast(loop)); + g_main_loop_quit(static_cast(loop)); return G_SOURCE_CONTINUE; } protected: /* convenience func to loop while waiting for a GObject's signal */ - void wait_for_signal (gpointer o, const gchar * signal, const int timeout_seconds=5) + void wait_for_signal(gpointer o, const gchar * signal, const int timeout_seconds=5) { // wait for the signal or for timeout, whichever comes first - const auto handler_id = g_signal_connect_swapped (o, signal, - G_CALLBACK(g_main_loop_quit), - loop); - const auto timeout_id = g_timeout_add_seconds (timeout_seconds, - wait_for_signal__timeout, - loop); - g_main_loop_run (loop); - g_source_remove (timeout_id); - g_signal_handler_disconnect (o, handler_id); + const auto handler_id = g_signal_connect_swapped(o, signal, + G_CALLBACK(g_main_loop_quit), + loop); + const auto timeout_id = g_timeout_add_seconds(timeout_seconds, + wait_for_signal__timeout, + loop); + g_main_loop_run(loop); + g_source_remove(timeout_id); + g_signal_handler_disconnect(o, handler_id); } /* convenience func to loop for N msec */ - void wait_msec (int msec=50) + void wait_msec(int msec=50) { - const auto id = g_timeout_add (msec, wait_msec__timeout, loop); - g_main_loop_run (loop); - g_source_remove (id); + const auto id = g_timeout_add(msec, wait_msec__timeout, loop); + g_main_loop_run(loop); + g_source_remove(id); } GMainLoop * loop; diff --git a/tests/planner-mock.c b/tests/planner-mock.c index e67ad7e..df5413e 100644 --- a/tests/planner-mock.c +++ b/tests/planner-mock.c @@ -39,7 +39,7 @@ G_DEFINE_TYPE (IndicatorDatetimePlannerMock, static void my_get_appointments (IndicatorDatetimePlanner * planner, GDateTime * begin_datetime, - GDateTime * end_datetime G_GNUC_UNUSED, + GDateTime * /*end_datetime*/, GAsyncReadyCallback callback, gpointer user_data) { @@ -88,34 +88,34 @@ my_get_appointments (IndicatorDatetimePlanner * planner, } static GSList * -my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GAsyncResult * res, - GError ** error) +my_get_appointments_finish (IndicatorDatetimePlanner* /*self*/, + GAsyncResult* res, + GError** error) { - return g_task_propagate_pointer (G_TASK(res), error); + return g_task_propagate_pointer(G_TASK(res), error); } static gboolean -my_is_configured (IndicatorDatetimePlanner * planner) +my_is_configured(IndicatorDatetimePlanner* planner) { IndicatorDatetimePlannerMock * self; - self = INDICATOR_DATETIME_PLANNER_MOCK (planner); + self = INDICATOR_DATETIME_PLANNER_MOCK(planner); return self->priv->is_configured; } static void -my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED) +my_activate(IndicatorDatetimePlanner* /*self*/) { - g_message ("%s %s", G_STRLOC, G_STRFUNC); + g_message("%s %s", G_STRLOC, G_STRFUNC); } static void -my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, - GDateTime * activate_time) +my_activate_time(IndicatorDatetimePlanner* /*self*/, + GDateTime* activate_time) { - gchar * str = g_date_time_format (activate_time, "%F %T"); - g_message ("%s %s: %s", G_STRLOC, G_STRFUNC, str); - g_free (str); + gchar * str = g_date_time_format(activate_time, "%F %T"); + g_message("%s %s: %s", G_STRLOC, G_STRFUNC, str); + g_free(str); } /*** @@ -123,9 +123,9 @@ my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED, ***/ static void -my_dispose (GObject * o) +my_dispose(GObject * o) { - G_OBJECT_CLASS (indicator_datetime_planner_mock_parent_class)->dispose (o); + G_OBJECT_CLASS(indicator_datetime_planner_mock_parent_class)->dispose(o); } /*** @@ -133,7 +133,7 @@ my_dispose (GObject * o) ***/ static void -indicator_datetime_planner_mock_class_init (IndicatorDatetimePlannerMockClass * klass) +indicator_datetime_planner_mock_class_init(IndicatorDatetimePlannerMockClass* klass) { GObjectClass * object_class; IndicatorDatetimePlannerClass * planner_class; diff --git a/tests/state-fixture.h b/tests/state-fixture.h new file mode 100644 index 0000000..0286ea9 --- /dev/null +++ b/tests/state-fixture.h @@ -0,0 +1,75 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include "glib-fixture.h" +#include "actions-mock.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace unity::indicator::datetime; + +class StateFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + std::shared_ptr m_clock; + std::shared_ptr m_state; + std::shared_ptr m_mock_actions; + std::shared_ptr m_actions; + + virtual void SetUp() + { + super::SetUp(); + + // first, build a mock backend state + const DateTime now = DateTime::NowLocal(); + m_clock.reset(new MockClock(now)); + m_state.reset(new State); + m_state->timezones.reset(new Timezones); + m_state->clock = std::dynamic_pointer_cast(m_clock); + m_state->planner.reset(new MockPlanner); + m_state->planner->time = now; + m_state->locations.reset(new Locations); + m_state->calendar_day = now; + + // build the actions on top of the state + m_mock_actions.reset(new MockActions(m_state)); + m_actions = std::dynamic_pointer_cast(m_mock_actions); + } + + virtual void TearDown() + { + m_actions.reset(); + m_mock_actions.reset(); + m_state.reset(); + m_clock.reset(); + + super::TearDown(); + } +}; + diff --git a/tests/test-actions.cc b/tests/test-actions.cc new file mode 100644 index 0000000..4329608 --- /dev/null +++ b/tests/test-actions.cc @@ -0,0 +1,173 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include "state-fixture.h" + +using namespace unity::indicator::datetime; + +typedef StateFixture ActionsFixture; + +TEST_F(ActionsFixture, ActionsExist) +{ + EXPECT_TRUE(m_actions != nullptr); + + const char* names[] = { "desktop-header", + "calendar", + "set-location", + "activate-planner", + "activate-appointment", + "activate-phone-settings", + "activate-phone-clock-app", + "activate-desktop-settings" }; + for(const auto& name: names) + { + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), name)); + } +} + +TEST_F(ActionsFixture, ActivateDesktopSettings) +{ + const auto action_name = "activate-desktop-settings"; + const auto expected_action = MockActions::OpenDesktopSettings; + + auto action_group = m_actions->action_group(); + auto history = m_mock_actions->history(); + EXPECT_EQ(0, history.size()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePhoneSettings) +{ + const auto action_name = "activate-phone-settings"; + const auto expected_action = MockActions::OpenPhoneSettings; + + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePhoneClockApp) +{ + const auto action_name = "activate-phone-clock-app"; + const auto expected_action = MockActions::OpenPhoneClockApp; + + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePlanner) +{ + const auto action_name = "activate-planner"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + const auto expected_action = MockActions::OpenPlanner; + auto v = g_variant_new_int64(0); + g_action_group_activate_action(action_group, action_name, v); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePlannerAt) +{ + const auto action_name = "activate-planner"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + const auto now = DateTime::NowLocal(); + auto v = g_variant_new_int64(now.to_unix()); + g_action_group_activate_action(action_group, action_name, v); + const auto a = MockActions::OpenPlannerAt; + EXPECT_EQ(std::vector({a}), m_mock_actions->history()); + EXPECT_EQ(now.to_unix(), m_mock_actions->date_time().to_unix()); +} + +TEST_F(ActionsFixture, SetLocation) +{ + const auto action_name = "set-location"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto v = g_variant_new_string("America/Chicago Oklahoma City"); + g_action_group_activate_action(action_group, action_name, v); + const auto expected_action = MockActions::SetLocation; + ASSERT_EQ(1, m_mock_actions->history().size()); + EXPECT_EQ(expected_action, m_mock_actions->history()[0]); + EXPECT_EQ("America/Chicago", m_mock_actions->zone()); + EXPECT_EQ("Oklahoma City", m_mock_actions->name()); +} + +TEST_F(ActionsFixture, SetCalendarDate) +{ + const auto action_name = "calendar"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto unix = m_state->clock->localtime().to_unix(); + auto v = g_variant_new_int64(unix); + g_action_group_activate_action(action_group, action_name, v); + const auto expected_action = MockActions::SetCalendarDate; + ASSERT_EQ(1, m_mock_actions->history().size()); + EXPECT_EQ(expected_action, m_mock_actions->history()[0]); + EXPECT_EQ(unix, m_mock_actions->date_time().to_unix()); +} + +TEST_F(ActionsFixture, OpenAppointment) +{ + Appointment appt; + appt.uid = "some arbitrary uid"; + appt.url = "http://www.canonical.com/"; + m_state->planner->upcoming.set(std::vector({appt})); + + const auto action_name = "activate-appointment"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto v = g_variant_new_string(appt.uid.c_str()); + g_action_group_activate_action(action_group, action_name, v); + const auto a = MockActions::OpenAppointment; + ASSERT_EQ(1, m_mock_actions->history().size()); + ASSERT_EQ(a, m_mock_actions->history()[0]); + EXPECT_EQ(appt.url, m_mock_actions->url()); +} + diff --git a/tests/test-clock.cc b/tests/test-clock.cc index a0b4360..7d3a35e 100644 --- a/tests/test-clock.cc +++ b/tests/test-clock.cc @@ -37,27 +37,27 @@ class ClockFixture: public GlibFixture typedef GlibFixture super; static void - on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + on_bus_opened(GObject* /*object*/, GAsyncResult* res, gpointer gself) { auto self = static_cast(gself); GError * err = 0; - self->system_bus = g_bus_get_finish (res, &err); - g_assert_no_error (err); + self->system_bus = g_bus_get_finish(res, &err); + g_assert_no_error(err); - g_main_loop_quit (self->loop); + g_main_loop_quit(self->loop); } static void - on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) + on_bus_closed(GObject* /*object*/, GAsyncResult* res, gpointer gself) { auto self = static_cast(gself); GError * err = 0; - g_dbus_connection_close_finish (self->system_bus, res, &err); - g_assert_no_error (err); + g_dbus_connection_close_finish(self->system_bus, res, &err); + g_assert_no_error(err); - g_main_loop_quit (self->loop); + g_main_loop_quit(self->loop); } protected: @@ -65,41 +65,41 @@ class ClockFixture: public GlibFixture GTestDBus * test_dbus; GDBusConnection * system_bus; - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); // pull up a test dbus - test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - g_test_dbus_up (test_dbus); - const char * address = g_test_dbus_get_bus_address (test_dbus); - g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); - g_debug ("test_dbus's address is %s", address); + test_dbus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(test_dbus); + const char * address = g_test_dbus_get_bus_address(test_dbus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); + g_debug("test_dbus's address is %s", address); // wait for the GDBusConnection before returning - g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run (loop); + g_bus_get(G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run(loop); } - virtual void TearDown () + virtual void TearDown() { // close the system bus - g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); - g_main_loop_run (loop); - g_clear_object (&system_bus); + g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&system_bus); // tear down the test dbus - g_test_dbus_down (test_dbus); - g_clear_object (&test_dbus); + g_test_dbus_down(test_dbus); + g_clear_object(&test_dbus); - super::TearDown (); + super::TearDown(); } public: - void emitPrepareForSleep () + void emitPrepareForSleep() { - g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr), + g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), NULL, "/org/freedesktop/login1", // object path "org.freedesktop.login1.Manager", // interface @@ -115,93 +115,91 @@ class ClockFixture: public GlibFixture #define TIMEZONE_FILE (SANDBOX"/timezone") -TEST_F (ClockFixture, HelloFixture) +TEST_F(ClockFixture, HelloFixture) { - std::shared_ptr zones (new Timezones); + std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); + LiveClock clock(zones); #if 0 - GTimeZone * tz_nyc = g_time_zone_new (file_timezone.c_str()); - GDateTime * now_nyc = g_date_time_new_now (tz_nyc); + GTimeZone * tz_nyc = g_time_zone_new(file_timezone.c_str()); + GDateTime * now_nyc = g_date_time_new_now(tz_nyc); GDateTime * now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_nyc); - g_time_zone_unref (tz_nyc); + EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); + EXPECT_LE(abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); + g_date_time_unref(now); + g_date_time_unref(now_nyc); + g_time_zone_unref(tz_nyc); /// change the timezones! clock.skewDetected.connect([this](){ g_main_loop_quit(loop); }); file_timezone = "America/Los_Angeles"; - g_idle_add ([](gpointer str){ + g_idle_add([](gpointer str){ set_file(static_cast(str)); return G_SOURCE_REMOVE; }, const_cast(file_timezone.c_str())); - g_main_loop_run (loop); + g_main_loop_run(loop); - GTimeZone * tz_la = g_time_zone_new (file_timezone.c_str()); - GDateTime * now_la = g_date_time_new_now (tz_la); + GTimeZone * tz_la = g_time_zone_new(file_timezone.c_str()); + GDateTime * now_la = g_date_time_new_now(tz_la); now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_la); - g_time_zone_unref (tz_la); + EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); + EXPECT_LE(abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); + g_date_time_unref(now); + g_date_time_unref(now_la); + g_time_zone_unref(tz_la); #endif } -TEST_F (ClockFixture, TimezoneChangeTriggersSkew) +TEST_F(ClockFixture, TimezoneChangeTriggersSkew) { - std::shared_ptr zones (new Timezones); + std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); + LiveClock clock(zones); //std::string file_timezone = "America/New_York"; - //set_file (file_timezone); - //std::shared_ptr detector (new TimezoneDetector(TIMEZONE_FILE)); - //LiveClock clock (detector); - - GTimeZone * tz_nyc = g_time_zone_new ("America/New_York"); - GDateTime * now_nyc = g_date_time_new_now (tz_nyc); - GDateTime * now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_nyc); - g_time_zone_unref (tz_nyc); + //set_file(file_timezone); + //std::shared_ptr detector(new TimezoneDetector(TIMEZONE_FILE)); + //LiveClock clock(detector); + + auto tz_nyc = g_time_zone_new("America/New_York"); + auto now_nyc = g_date_time_new_now(tz_nyc); + auto now = clock.localtime(); + EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now.get())); + EXPECT_LE(abs(g_date_time_difference(now_nyc,now.get())), G_USEC_PER_SEC); + g_date_time_unref(now_nyc); + g_time_zone_unref(tz_nyc); /// change the timezones! clock.skewDetected.connect([this](){ - g_main_loop_quit(loop); - }); - g_idle_add ([](gpointer gs){ - static_cast(gs)->timezone.set("America/Los_Angeles"); - return G_SOURCE_REMOVE; - }, zones.get()); - g_main_loop_run (loop); - - GTimeZone * tz_la = g_time_zone_new ("America/Los_Angeles"); - GDateTime * now_la = g_date_time_new_now (tz_la); + g_main_loop_quit(loop); + }); + g_idle_add([](gpointer gs){ + static_cast(gs)->timezone.set("America/Los_Angeles"); + return G_SOURCE_REMOVE; + }, zones.get()); + g_main_loop_run(loop); + + auto tz_la = g_time_zone_new("America/Los_Angeles"); + auto now_la = g_date_time_new_now(tz_la); now = clock.localtime(); - EXPECT_EQ (g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); - EXPECT_LE (abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); - g_date_time_unref (now); - g_date_time_unref (now_la); - g_time_zone_unref (tz_la); + EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now.get())); + EXPECT_LE(abs(g_date_time_difference(now_la,now.get())), G_USEC_PER_SEC); + g_date_time_unref(now_la); + g_time_zone_unref(tz_la); } /** * Confirm that a "PrepareForSleep" event wil trigger a skew event */ -TEST_F (ClockFixture, SleepTriggersSkew) +TEST_F(ClockFixture, SleepTriggersSkew) { - std::shared_ptr zones (new Timezones); + std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); - wait_msec (500); // wait for the bus to set up + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up bool skewed = false; clock.skewDetected.connect([&skewed, this](){ @@ -210,12 +208,12 @@ TEST_F (ClockFixture, SleepTriggersSkew) return G_SOURCE_REMOVE; }); - g_idle_add ([](gpointer gself){ - static_cast(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; + g_idle_add([](gpointer gself){ + static_cast(gself)->emitPrepareForSleep(); + return G_SOURCE_REMOVE; }, this); - wait_msec (1000); + wait_msec(1000); EXPECT_TRUE(skewed); } @@ -223,12 +221,12 @@ TEST_F (ClockFixture, SleepTriggersSkew) * Confirm that normal time passing doesn't trigger a skew event. * that idling changing the clock's time triggers a skew event */ -TEST_F (ClockFixture, IdleDoesNotTriggerSkew) +TEST_F(ClockFixture, IdleDoesNotTriggerSkew) { - std::shared_ptr zones (new Timezones); + std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); - LiveClock clock (zones); - wait_msec (500); // wait for the bus to set up + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up bool skewed = false; clock.skewDetected.connect([&skewed](){ @@ -239,6 +237,6 @@ TEST_F (ClockFixture, IdleDoesNotTriggerSkew) const unsigned int intervalSec = 4; clock.skewTestIntervalSec.set(intervalSec); - wait_msec (intervalSec * 2.5 * 1000); - EXPECT_FALSE (skewed); + wait_msec(intervalSec * 2.5 * 1000); + EXPECT_FALSE(skewed); } diff --git a/tests/test-core.cc b/tests/test-core.cc deleted file mode 100644 index 7ed38a9..0000000 --- a/tests/test-core.cc +++ /dev/null @@ -1,148 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -#include "glib-fixture.h" - -/*** -**** -***/ - -class CoreFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - virtual void SetUp () - { - super::SetUp (); - } - - virtual void TearDown () - { - super::TearDown (); - } -}; - -namespace -{ -struct EventLoop -{ - typedef std::function Handler; - - void stop() - { - stop_requested = true; - } - - void run() - { - while (!stop_requested) - { - std::unique_lock ul(guard); - wait_condition.wait_for( - ul, - std::chrono::milliseconds{500}, - [this]() { return handlers.size() > 0; }); - - std::cerr << "handlers.size() is " << handlers.size() << std::endl; - while (handlers.size() > 0) - { - std::cerr << "gaba begin" << std::endl; - handlers.front()(); - std::cerr << "gaba end" << std::endl; - handlers.pop(); - } - } - } - - void dispatch(const Handler& h) - { -std::cerr << "in dispatch" << std::endl; - std::lock_guard lg(guard); - handlers.push(h); - } - - bool stop_requested = false; - std::queue handlers; - std::mutex guard; - std::condition_variable wait_condition; -}; -} - - -TEST_F (CoreFixture, HelloWorld) -{ - // We instantiate an event loop and run it on a different thread than the main one. - EventLoop dispatcher; - std::thread dispatcher_thread{[&dispatcher]() { dispatcher.run(); }}; - std::thread::id dispatcher_thread_id = dispatcher_thread.get_id(); - - // The signal that we want to dispatch via the event loop. - core::Signal s; - - static const int expected_invocation_count = 10000; - - // Setup the connection. For each invocation we check that the id of the - // thread the handler is being called upon equals the thread that the - // event loop is running upon. - auto connection = s.connect( - [&dispatcher, dispatcher_thread_id](int value, double d) - { - std::cerr << "this is the lambda" << std::endl; - EXPECT_EQ(dispatcher_thread_id, - std::this_thread::get_id()); - - std::cout << d << std::endl; - - if (value == expected_invocation_count) - dispatcher.stop(); - }); - - // Route the connection via the dispatcher - connection.dispatch_via( - std::bind( - &EventLoop::dispatch, - std::ref(dispatcher), - std::placeholders::_1)); - - // Invoke the signal from the main thread. - for (unsigned int i = 1; i <= expected_invocation_count; i++) - s(i, 42.); - - if (dispatcher_thread.joinable()) - dispatcher_thread.join(); -} diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h new file mode 100644 index 0000000..fc7ab5a --- /dev/null +++ b/tests/test-dbus-fixture.h @@ -0,0 +1,100 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include "glib-fixture.h" + +/*** +**** +***/ + +class TestDBusFixture: public GlibFixture +{ + public: + + TestDBusFixture() {} + + TestDBusFixture(const std::vector& service_dirs_in): service_dirs(service_dirs_in) {} + + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + self->system_bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->system_bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + GTestDBus * test_dbus; + GDBusConnection * system_bus; + const std::vector service_dirs; + + virtual void SetUp () + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + for (const auto& dir : service_dirs) + g_test_dbus_add_service_dir (test_dbus, dir.c_str()); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + } + + virtual void TearDown () + { + // close the system bus + g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); + g_main_loop_run (loop); + g_clear_object (&system_bus); + + // tear down the test dbus + g_test_dbus_down (test_dbus); + g_clear_object (&test_dbus); + + super::TearDown (); + } +}; diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc index 641338b..42c828c 100644 --- a/tests/test-formatter.cc +++ b/tests/test-formatter.cc @@ -18,9 +18,9 @@ * with this program. If not, see . */ -#include "clock-mock.h" #include "glib-fixture.h" +#include #include #include @@ -30,9 +30,10 @@ #include using unity::indicator::datetime::Clock; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::DesktopFormatter; using unity::indicator::datetime::MockClock; using unity::indicator::datetime::PhoneFormatter; -using unity::indicator::datetime::DesktopFormatter; /*** **** @@ -94,7 +95,8 @@ class FormatterFixture: public GlibFixture TEST_F (FormatterFixture, TestPhoneHeader) { GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - std::shared_ptr mock (new MockClock(now)); + std::shared_ptr mock (new MockClock(DateTime(now))); + g_date_time_unref(now); std::shared_ptr clock = std::dynamic_pointer_cast(mock); // test the default value in a 24h locale @@ -146,8 +148,9 @@ TEST_F (FormatterFixture, TestDesktopHeader) { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } }; - GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - std::shared_ptr mock (new MockClock(now)); + GDateTime * now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr mock(new MockClock(DateTime(now))); + g_date_time_unref(now); std::shared_ptr clock = std::dynamic_pointer_cast(mock); for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(test_cases[i].now)); + DateTime tmp(test_cases[i].now); + tmp.get(); + std::shared_ptr mock (new MockClock(tmp));//DateTime(test_cases[i].now))); std::shared_ptr clock = std::dynamic_pointer_cast(mock); DesktopFormatter f (clock); - std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); + std::string fmt = f.getRelativeFormat (test_cases[i].then); ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); g_clear_pointer (&test_cases[i].now, g_date_time_unref); g_clear_pointer (&test_cases[i].then, g_date_time_unref); - g_clear_pointer (&test_cases[i].then_end, g_date_time_unref); } } @@ -224,11 +227,11 @@ TEST_F (FormatterFixture, TestUpcomingTimes) */ TEST_F (FormatterFixture, TestEventTimes) { - GDateTime * day = g_date_time_new_local (2013, 1, 1, 13, 0, 0); - GDateTime * day_begin = g_date_time_new_local (2013, 1, 1, 13, 0, 0); - GDateTime * day_end = g_date_time_add_days (day_begin, 1); - GDateTime * tomorrow_begin = g_date_time_add_days (day_begin, 1); - GDateTime * tomorrow_end = g_date_time_add_days (tomorrow_begin, 1); + auto day = g_date_time_new_local (2013, 1, 1, 13, 0, 0); + auto day_begin = g_date_time_new_local (2013, 1, 1, 13, 0, 0); + auto day_end = g_date_time_add_days (day_begin, 1); + auto tomorrow_begin = g_date_time_add_days (day_begin, 1); + auto tomorrow_end = g_date_time_add_days (tomorrow_begin, 1); struct { bool is_12h; @@ -247,7 +250,7 @@ TEST_F (FormatterFixture, TestEventTimes) { if (test_cases[i].is_12h ? Set12hLocale() : Set24hLocale()) { - std::shared_ptr mock (new MockClock(test_cases[i].now)); + std::shared_ptr mock (new MockClock(DateTime(test_cases[i].now))); std::shared_ptr clock = std::dynamic_pointer_cast(mock); DesktopFormatter f (clock); diff --git a/tests/test-indicator.cc b/tests/test-indicator.cc deleted file mode 100644 index 2480c94..0000000 --- a/tests/test-indicator.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2012 Canonical Ltd. - -Authors: - Charles Kerr - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -#include - -#include - -/*** -**** -***/ - -namespace -{ - void ensure_glib_initialized () - { - static bool initialized = false; - - if (G_UNLIKELY(!initialized)) - { - initialized = true; - g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); - } - } -} - -/*** -**** -***/ - -class IndicatorTest : public ::testing::Test -{ - private: - - guint log_handler_id; - - int log_count_ipower_actual; - - static void log_count_func (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) - { - reinterpret_cast(user_data)->log_count_ipower_actual++; - } - - protected: - - int log_count_ipower_expected; - - protected: - - virtual void SetUp() - { - const GLogLevelFlags flags = GLogLevelFlags(G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING); - log_handler_id = g_log_set_handler ("Indicator-Power", flags, log_count_func, this); - log_count_ipower_expected = 0; - log_count_ipower_actual = 0; - - ensure_glib_initialized (); - } - - virtual void TearDown() - { - ASSERT_EQ (log_count_ipower_expected, log_count_ipower_actual); - g_log_remove_handler ("Indicator-Power", log_handler_id); - } -}; - -/*** -**** -***/ - -TEST_F(IndicatorTest, HelloWorld) -{ - ASSERT_TRUE (TRUE); -} diff --git a/tests/test-locations.cc b/tests/test-locations.cc index c833bed..edaac69 100644 --- a/tests/test-locations.cc +++ b/tests/test-locations.cc @@ -19,9 +19,9 @@ * with this program. If not, see . */ -#include "clock-mock.h" #include "glib-fixture.h" +#include #include #include #include @@ -53,120 +53,120 @@ class LocationsFixture: public GlibFixture const std::string nyc = "America/New_York"; const std::string chicago = "America/Chicago"; - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); - settings = g_settings_new (SETTINGS_INTERFACE); + settings = g_settings_new(SETTINGS_INTERFACE); const gchar * location_strv[] = { "America/Los_Angeles Oakland", "America/Chicago Chicago", "America/Chicago Oklahoma City", "America/Toronto Toronto", "Europe/London London", "Europe/Berlin Berlin", NULL }; - g_settings_set_strv (settings, SETTINGS_LOCATIONS_S, location_strv); - g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, true); + g_settings_set_strv(settings, SETTINGS_LOCATIONS_S, location_strv); + g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, true); - timezones.reset (new Timezones); - timezones->timezone.set (chicago); - timezones->timezones.set (std::set({ nyc, chicago })); + timezones.reset(new Timezones); + timezones->timezone.set(chicago); + timezones->timezones.set(std::set({ nyc, chicago })); } - virtual void TearDown () + virtual void TearDown() { - //timezones.reset (nullptr); - g_clear_object (&settings); + //timezones.reset(nullptr); + g_clear_object(&settings); - super::TearDown (); + super::TearDown(); } }; -TEST_F (LocationsFixture, Timezones) +TEST_F(LocationsFixture, Timezones) { - g_settings_set_boolean (settings, SETTINGS_SHOW_LOCATIONS_S, false); + g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, false); - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + SettingsLocations locations(SETTINGS_INTERFACE, timezones); std::vector l = locations.locations.get(); - EXPECT_EQ (2, l.size()); - EXPECT_EQ ("Chicago", l[0].name); - EXPECT_EQ (chicago, l[0].zone); - EXPECT_EQ ("New York", l[1].name); - EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ(2, l.size()); + EXPECT_EQ("Chicago", l[0].name); + EXPECT_EQ(chicago, l[0].zone); + EXPECT_EQ("New York", l[1].name); + EXPECT_EQ(nyc, l[1].zone); } -TEST_F (LocationsFixture, SettingsLocations) +TEST_F(LocationsFixture, SettingsLocations) { - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + SettingsLocations locations(SETTINGS_INTERFACE, timezones); std::vector l = locations.locations.get(); - EXPECT_EQ (7, l.size()); - EXPECT_EQ ("Chicago", l[0].name); - EXPECT_EQ (chicago, l[0].zone); - EXPECT_EQ ("New York", l[1].name); - EXPECT_EQ (nyc, l[1].zone); - EXPECT_EQ ("Oakland", l[2].name); - EXPECT_EQ ("America/Los_Angeles", l[2].zone); - EXPECT_EQ ("Oklahoma City", l[3].name); - EXPECT_EQ ("America/Chicago", l[3].zone); - EXPECT_EQ ("Toronto", l[4].name); - EXPECT_EQ ("America/Toronto", l[4].zone); - EXPECT_EQ ("London", l[5].name); - EXPECT_EQ ("Europe/London", l[5].zone); - EXPECT_EQ ("Berlin", l[6].name); - EXPECT_EQ ("Europe/Berlin", l[6].zone); + EXPECT_EQ(7, l.size()); + EXPECT_EQ("Chicago", l[0].name); + EXPECT_EQ(chicago, l[0].zone); + EXPECT_EQ("New York", l[1].name); + EXPECT_EQ(nyc, l[1].zone); + EXPECT_EQ("Oakland", l[2].name); + EXPECT_EQ("America/Los_Angeles", l[2].zone); + EXPECT_EQ("Oklahoma City", l[3].name); + EXPECT_EQ("America/Chicago", l[3].zone); + EXPECT_EQ("Toronto", l[4].name); + EXPECT_EQ("America/Toronto", l[4].zone); + EXPECT_EQ("London", l[5].name); + EXPECT_EQ("Europe/London", l[5].zone); + EXPECT_EQ("Berlin", l[6].name); + EXPECT_EQ("Europe/Berlin", l[6].zone); } -TEST_F (LocationsFixture, ChangeLocationStrings) +TEST_F(LocationsFixture, ChangeLocationStrings) { - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + SettingsLocations locations(SETTINGS_INTERFACE, timezones); bool locations_changed = false; locations.locations.changed().connect([&locations_changed, this](const std::vector&){ locations_changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - g_idle_add ([](gpointer gsettings){ + g_idle_add([](gpointer gsettings){ const gchar * strv[] = { "America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin", NULL }; - g_settings_set_strv (static_cast(gsettings), SETTINGS_LOCATIONS_S, strv); + g_settings_set_strv(static_cast(gsettings), SETTINGS_LOCATIONS_S, strv); return G_SOURCE_REMOVE; }, settings); - g_main_loop_run (loop); + g_main_loop_run(loop); - EXPECT_TRUE (locations_changed); + EXPECT_TRUE(locations_changed); std::vector l = locations.locations.get(); - EXPECT_EQ (5, l.size()); - EXPECT_EQ ("Chicago", l[0].name); - EXPECT_EQ (chicago, l[0].zone); - EXPECT_EQ ("New York", l[1].name); - EXPECT_EQ (nyc, l[1].zone); - EXPECT_EQ ("Oakland", l[2].name); - EXPECT_EQ ("America/Los_Angeles", l[2].zone); - EXPECT_EQ ("London", l[3].name); - EXPECT_EQ ("Europe/London", l[3].zone); - EXPECT_EQ ("Berlin", l[4].name); - EXPECT_EQ ("Europe/Berlin", l[4].zone); + EXPECT_EQ(5, l.size()); + EXPECT_EQ("Chicago", l[0].name); + EXPECT_EQ(chicago, l[0].zone); + EXPECT_EQ("New York", l[1].name); + EXPECT_EQ(nyc, l[1].zone); + EXPECT_EQ("Oakland", l[2].name); + EXPECT_EQ("America/Los_Angeles", l[2].zone); + EXPECT_EQ("London", l[3].name); + EXPECT_EQ("Europe/London", l[3].zone); + EXPECT_EQ("Berlin", l[4].name); + EXPECT_EQ("Europe/Berlin", l[4].zone); locations_changed = false; } -TEST_F (LocationsFixture, ChangeLocationVisibility) +TEST_F(LocationsFixture, ChangeLocationVisibility) { - SettingsLocations locations (SETTINGS_INTERFACE, timezones); + SettingsLocations locations(SETTINGS_INTERFACE, timezones); bool locations_changed = false; locations.locations.changed().connect([&locations_changed, this](const std::vector&){ locations_changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - g_idle_add ([](gpointer gsettings){ - g_settings_set_boolean (static_cast(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + g_idle_add([](gpointer gsettings){ + g_settings_set_boolean(static_cast(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); return G_SOURCE_REMOVE; }, settings); - g_main_loop_run (loop); + g_main_loop_run(loop); - EXPECT_TRUE (locations_changed); + EXPECT_TRUE(locations_changed); std::vector l = locations.locations.get(); - EXPECT_EQ (2, l.size()); - EXPECT_EQ ("Chicago", l[0].name); - EXPECT_EQ (chicago, l[0].zone); - EXPECT_EQ ("New York", l[1].name); - EXPECT_EQ (nyc, l[1].zone); + EXPECT_EQ(2, l.size()); + EXPECT_EQ("Chicago", l[0].name); + EXPECT_EQ(chicago, l[0].zone); + EXPECT_EQ("New York", l[1].name); + EXPECT_EQ(nyc, l[1].zone); } diff --git a/tests/test-menus.cc b/tests/test-menus.cc new file mode 100644 index 0000000..88e4706 --- /dev/null +++ b/tests/test-menus.cc @@ -0,0 +1,406 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + + +#include "actions-mock.h" +#include "state-fixture.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace unity::indicator::datetime; + +class MenuFixture: public StateFixture +{ +private: + typedef StateFixture super; + +protected: + std::shared_ptr m_menu_factory; + std::vector> m_menus; + + virtual void SetUp() + { + super::SetUp(); + + // build the menus on top of the actions and state + m_menu_factory.reset(new MenuFactory(m_actions, m_state)); + for(int i=0; ibuildMenu(Menu::Profile(i))); + } + + virtual void TearDown() + { + m_menus.clear(); + m_menu_factory.reset(); + + super::TearDown(); + } + + void InspectHeader(GMenuModel* menu_model, const std::string& name) + { + // check that there's a header menuitem + EXPECT_EQ(1,g_menu_model_get_n_items(menu_model)); + gchar* str = nullptr; + g_menu_model_get_item_attribute(menu_model, 0, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.root", str); + g_clear_pointer(&str, g_free); + g_menu_model_get_item_attribute(menu_model, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + const auto action_name = name + "-header"; + EXPECT_EQ(std::string("indicator.")+action_name, str); + g_clear_pointer(&str, g_free); + + // check the header + auto dict = g_action_group_get_action_state(m_actions->action_group(), action_name.c_str()); + EXPECT_TRUE(dict != nullptr); + EXPECT_TRUE(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); + auto v = g_variant_lookup_value(dict, "accessible-desc", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "label", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "title", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "visible", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + g_variant_unref(dict); + } + + void InspectCalendar(GMenuModel* menu_model, Menu::Profile profile) + { + gchar* str = nullptr; + const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); + const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); + + // get the calendar section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); + + // should be one or two items: a date label and maybe a calendar + ASSERT_TRUE(section != nullptr); + auto n_expected = calendar_expected ? 2 : 1; + EXPECT_EQ(n_expected, g_menu_model_get_n_items(section)); + + // look at the date menuitem + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); + const auto now = m_state->clock->localtime(); + EXPECT_EQ(now.format("%A, %e %B %Y"), str); + + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + if (actions_expected) + EXPECT_STREQ("indicator.activate-planner", str); + else + EXPECT_TRUE(str == nullptr); + g_clear_pointer(&str, g_free); + + // look at the calendar menuitem + if (calendar_expected) + { + g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.calendar", str); + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 1, G_MENU_ATTRIBUTE_ACTION, "s", &str); + EXPECT_STREQ("indicator.calendar", str); + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 1, "activation-action", "s", &str); + if (actions_expected) + EXPECT_STREQ("indicator.activate-planner", str); + else + EXPECT_TRUE(str == nullptr); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + + // now change the clock and see if the date label changes appropriately + + auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); + auto tomorrow = DateTime(gdt_tomorrow); + g_date_time_unref(gdt_tomorrow); + m_clock->set_localtime(tomorrow); + wait_msec(); + + section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); + EXPECT_EQ(tomorrow.format("%A, %e %B %Y"), str); + g_clear_pointer(&str, g_free); + g_clear_object(§ion); + + // cleanup + g_object_unref(submenu); + } + + void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) + { + const bool appointments_expected = (profile == Menu::Desktop) + || (profile == Menu::Phone); + + // get the Appointments section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + + // there shouldn't be any menuitems when "show events" is false + m_state->show_events.set(false); + wait_msec(); + auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(0, g_menu_model_get_n_items(section)); + g_clear_object(§ion); + + // when "show_events" is true, + // there should be an "add event" button even if there aren't any appointments + std::vector appointments; + m_state->show_events.set(true); + m_state->planner->upcoming.set(appointments); + wait_msec(); + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + int expected_n = appointments_expected ? 1 : 0; + EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + g_clear_object(§ion); + + // try adding a few appointments and see if the menu updates itself + + const auto now = m_state->clock->localtime(); + auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); + const auto tomorrow = DateTime(gdt_tomorrow); + g_date_time_unref(gdt_tomorrow); + + Appointment a1; // an alarm clock appointment + a1.color = "red"; + a1.summary = "Alarm"; + a1.summary = "http://www.example.com/"; + a1.uid = "example"; + a1.has_alarms = true; + a1.begin = a1.end = tomorrow; + appointments.push_back(a1); + + Appointment a2; // a non-alarm appointment + a2.color = "green"; + a2.summary = "Other Text"; + a2.summary = "http://www.monkey.com/"; + a2.uid = "monkey"; + a2.has_alarms = false; + a2.begin = a2.end = tomorrow; + appointments.push_back(a2); + + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + expected_n = appointments_expected ? 3 : 0; + EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + if (appointments_expected) + { + gchar * str = nullptr; + + // test the alarm + // - confirm it has an x-canonical-type of "alarm" + g_menu_model_get_item_attribute(section, 0, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.alarm", str); + g_clear_pointer(&str, g_free); + // - confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + // - confirm it has a serialized icon attribute + auto v = g_menu_model_get_item_attribute_value(section, 0, G_MENU_ATTRIBUTE_ICON, nullptr); + EXPECT_TRUE(v != nullptr); + auto icon = g_icon_deserialize(v); + EXPECT_TRUE(icon != nullptr); + g_clear_object(&icon); + g_clear_pointer(&v, g_variant_unref); + + // test the appointment + // - confirm it has an x-canonical-type of "appointment" + g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.appointment", str); + g_clear_pointer(&str, g_free); + // - confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + // - confirm its color matches the one we fed the appointments vector + g_menu_model_get_item_attribute(section, 1, "x-canonical-color", "s", &str); + EXPECT_EQ(a2.color, str); + g_clear_pointer(&str, g_free); + } + g_clear_object(§ion); + + g_object_unref(submenu); + } + + void CompareLocationsTo(GMenuModel* menu_model, const std::vector& locations) + { + // get the Locations section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Locations, G_MENU_LINK_SECTION); + + // confirm that section's menuitems mirror the "locations" vector + const auto n = locations.size(); + ASSERT_EQ(n, g_menu_model_get_n_items(section)); + for (guint i=0; i empty; + m_state->locations->locations.set(empty); + wait_msec(); + CompareLocationsTo(menu_model, empty); + + // add some locations and confirm the menu picked up our changes + Location l1 ("America/Chicago", "Dallas"); + Location l2 ("America/Arizona", "Phoenix"); + std::vector locations({l1, l2}); + m_state->locations->locations.set(locations); + wait_msec(); + CompareLocationsTo(menu_model, locations_expected ? locations : empty); + + // now remove one of the locations... + locations.pop_back(); + m_state->locations->locations.set(locations); + wait_msec(); + CompareLocationsTo(menu_model, locations_expected ? locations : empty); + } + + void InspectSettings(GMenuModel* menu_model, Menu::Profile profile) + { + std::string expected_action; + + if (profile == Menu::Desktop) + expected_action = "indicator.activate-desktop-settings"; + else if (profile == Menu::Phone) + expected_action = "indicator.activate-phone-settings"; + + // get the Settings section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Settings, G_MENU_LINK_SECTION); + + if (expected_action.empty()) + { + EXPECT_EQ(0, g_menu_model_get_n_items(section)); + } + else + { + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* str = nullptr; + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + EXPECT_EQ(expected_action, str); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + g_object_unref(submenu); + } +}; + + +TEST_F(MenuFixture, HelloWorld) +{ + EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size()); + for (int i=0; imenu_model() != nullptr); + EXPECT_EQ(i, m_menus[i]->profile()); + } + EXPECT_EQ(m_menus[Menu::Desktop]->name(), "desktop"); +} + +TEST_F(MenuFixture, Header) +{ + for(auto& menu : m_menus) + InspectHeader(menu->menu_model(), menu->name()); +} + +TEST_F(MenuFixture, Sections) +{ + for(auto& menu : m_menus) + { + // check that the header has a submenu + auto menu_model = menu->menu_model(); + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + EXPECT_TRUE(submenu != nullptr); + EXPECT_EQ(Menu::NUM_SECTIONS, g_menu_model_get_n_items(submenu)); + g_object_unref(submenu); + } +} + +TEST_F(MenuFixture, Calendar) +{ + for(auto& menu : m_menus) + InspectCalendar(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Appointments) +{ + for(auto& menu : m_menus) + InspectAppointments(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Locations) +{ + for(auto& menu : m_menus) + InspectLocations(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Settings) +{ + for(auto& menu : m_menus) + InspectSettings(menu->menu_model(), menu->profile()); +} + + diff --git a/tests/test-planner-eds.cc b/tests/test-planner-eds.cc deleted file mode 100644 index 986a45e..0000000 --- a/tests/test-planner-eds.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "clock-mock.h" -#include "glib-fixture.h" - -#include - -#include - -#include -#include - -using unity::indicator::datetime::Appointment; -using unity::indicator::datetime::DateTime; -using unity::indicator::datetime::PlannerEds; - -/*** -**** -***/ - -class PlannerEdsFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - virtual void SetUp () - { - super::SetUp (); - } - - virtual void TearDown () - { - super::TearDown (); - } -}; - -/*** -**** -***/ - -TEST_F (PlannerEdsFixture, HelloWorld) -{ - DateTime dt; - dt = g_date_time_new_now_local(); - - PlannerEds planner; - planner.time.set (dt); - g_main_loop_run (loop); -} - diff --git a/tests/test-planner.cc b/tests/test-planner.cc index 8c74366..3072aea 100644 --- a/tests/test-planner.cc +++ b/tests/test-planner.cc @@ -17,18 +17,20 @@ * with this program. If not, see . */ -#include "clock-mock.h" #include "glib-fixture.h" #include +#include +#include #include - -#include +#include #include #include using unity::indicator::datetime::Appointment; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::PlannerEds; /*** **** @@ -36,33 +38,48 @@ using unity::indicator::datetime::Appointment; typedef GlibFixture PlannerFixture; +TEST_F(PlannerFixture, EDS) +{ + PlannerEds planner; + wait_msec(100); + + auto now = g_date_time_new_now_local(); + planner.time.set(DateTime(now)); + wait_msec(2500); + + std::vector thisMonth = planner.thisMonth.get(); + std::cerr << thisMonth.size() << " appointments this month" << std::endl; + for(const auto& a : thisMonth) + std::cerr << a.summary << std::endl; +} + -TEST_F (PlannerFixture, HelloWorld) +TEST_F(PlannerFixture, HelloWorld) { - GDateTime * halloween = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - GDateTime * christmas = g_date_time_new_local (2020, 12, 25, 0, 0, 0); + auto halloween = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + auto christmas = g_date_time_new_local(2020, 12, 25, 0, 0, 0); Appointment a; a.summary = "Test"; a.begin = halloween; - a.end = g_date_time_add_hours (halloween, 1); + a.end = g_date_time_add_hours(halloween, 1); const Appointment b = a; a.summary = "Foo"; - EXPECT_EQ (a.summary, "Foo"); - EXPECT_EQ (b.summary, "Test"); - EXPECT_EQ (0, g_date_time_compare (a.begin(), b.begin())); - EXPECT_EQ (0, g_date_time_compare (a.end(), b.end())); + EXPECT_EQ(a.summary, "Foo"); + EXPECT_EQ(b.summary, "Test"); + EXPECT_EQ(0, g_date_time_compare(a.begin(), b.begin())); + EXPECT_EQ(0, g_date_time_compare(a.end(), b.end())); Appointment c; c.begin = christmas; - c.end = g_date_time_add_hours (christmas, 1); + c.end = g_date_time_add_hours(christmas, 1); Appointment d; d = c; - EXPECT_EQ (0, g_date_time_compare (c.begin(), d.begin())); - EXPECT_EQ (0, g_date_time_compare (c.end(), d.end())); + EXPECT_EQ(0, g_date_time_compare(c.begin(), d.begin())); + EXPECT_EQ(0, g_date_time_compare(c.end(), d.end())); a = d; - EXPECT_EQ (0, g_date_time_compare (d.begin(), a.begin())); - EXPECT_EQ (0, g_date_time_compare (d.end(), a.end())); + EXPECT_EQ(0, g_date_time_compare(d.begin(), a.begin())); + EXPECT_EQ(0, g_date_time_compare(d.end(), a.end())); } diff --git a/tests/test-skew.cc b/tests/test-skew.cc deleted file mode 100644 index 90c0164..0000000 --- a/tests/test-skew.cc +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "glib-fixture.h" - -#include "Clock.h" -#include "MockClock.h" - -/*** -**** -***/ - -using unity::indicator::datetime::Clock; -using unity::indicator::datetime::MockClock; -using unity::indicator::datetime::SkewDetector; - -class SkewFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - static void - on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - self->system_bus = g_bus_get_finish (res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - static void - on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - g_dbus_connection_close_finish (self->system_bus, res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - protected: - - std::shared_ptr mockClock; - GTestDBus * test_dbus; - GDBusConnection * system_bus; - - virtual void SetUp () - { - super::SetUp (); - - // pull up a test dbus - test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - g_test_dbus_up (test_dbus); - const char * address = g_test_dbus_get_bus_address (test_dbus); - g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); - g_debug ("test_dbus's address is %s", address); - - // wait for the GDBusConnection before returning - g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run (loop); - - // create a clock - GDateTime * now = g_date_time_new_now_local (); - mockClock.reset (new MockClock (now)); - g_date_time_unref (now); - } - - virtual void TearDown () - { - mockClock.reset(); - - // close the system bus - g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); - g_main_loop_run (loop); - g_clear_object (&system_bus); - - // tear down the test dbus - g_test_dbus_down (test_dbus); - g_clear_object (&test_dbus); - - super::TearDown (); - } - - public: - - void emitPrepareForSleep () - { - g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr), - NULL, - "/org/freedesktop/login1", // object path - "org.freedesktop.login1.Manager", // interface - "PrepareForSleep", // signal name - g_variant_new("(b)", FALSE), - NULL); - } -}; - - -/** - * A simple "hello world" style test. - */ -TEST_F (SkewFixture, CanInstantiate) -{ - SkewDetector skew (std::dynamic_pointer_cast(mockClock)); - wait_msec (500); // wait for the bus to set up -} - - -/** - * Confirm that changing the clock's timezone triggers a skew event - */ -TEST_F (SkewFixture, ChangingTimezonesTriggersEvent) -{ - SkewDetector skew (std::dynamic_pointer_cast(mockClock)); - wait_msec (500); // wait for the bus to set up - - bool skewed = false; - skew.skewDetected.connect([&skewed, this](){ - skewed = true; - g_main_loop_quit(loop); - return G_SOURCE_REMOVE; - }); - - g_idle_add([](gpointer gclock){ - GDateTime * arbitrary = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - static_cast(gclock)->setLocaltime (arbitrary); - g_date_time_unref (arbitrary); - return G_SOURCE_REMOVE; - }, mockClock.get()); - - wait_msec (1000); - - EXPECT_TRUE (skewed); - GDateTime * expected = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - GDateTime * actual = mockClock->localtime(); - EXPECT_EQ (0, g_date_time_compare (expected, actual)); - g_date_time_unref (actual); - g_date_time_unref (expected); -} - -/** - * Confirm that a "PrepareForSleep" event wil trigger a skew event - */ -TEST_F (SkewFixture, PrepareForSleep) -{ - SkewDetector skew (std::dynamic_pointer_cast(mockClock)); - wait_msec (500); // wait for the bus to set up - - bool skewed = false; - skew.skewDetected.connect([&skewed, this](){ - skewed = true; - g_main_loop_quit(loop); - return G_SOURCE_REMOVE; - }); - - g_idle_add ([](gpointer gself){ - static_cast(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; - }, this); - - wait_msec (1000); - EXPECT_TRUE(skewed); -} - - -/** - * Confirm that normal time passing doesn't trigger a skew event. - * that idling changing the clock's time triggers a skew event - */ -TEST_F (SkewFixture, IdleDoesNotTriggerEvent) -{ - SkewDetector skew (std::dynamic_pointer_cast(mockClock)); - wait_msec (500); // wait for the bus to set up - - bool skewed = false; - skew.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - //abort(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 4; - skew.intervalSec.set(intervalSec); - wait_msec (intervalSec * 2.5 * 1000); - EXPECT_FALSE (skewed); -} diff --git a/tests/test-timezone-file.cc b/tests/test-timezone-file.cc index 510d12c..453b353 100644 --- a/tests/test-timezone-file.cc +++ b/tests/test-timezone-file.cc @@ -52,25 +52,25 @@ class TimezoneFixture: public GlibFixture protected: - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); } - virtual void TearDown () + virtual void TearDown() { - super::TearDown (); + super::TearDown(); } public: /* convenience func to set the timezone file */ - void set_file (const std::string& text) + void set_file(const std::string& text) { - FILE * fp = fopen (TIMEZONE_FILE, "w+"); - fprintf (fp, "%s\n", text.c_str()); - fclose (fp); - sync (); + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); } }; @@ -78,56 +78,56 @@ class TimezoneFixture: public GlibFixture /** * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist */ -TEST_F (TimezoneFixture, NoFile) +TEST_F(TimezoneFixture, NoFile) { - remove (TIMEZONE_FILE); - ASSERT_FALSE (g_file_test (TIMEZONE_FILE, G_FILE_TEST_EXISTS)); + remove(TIMEZONE_FILE); + ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); - FileTimezone tz (TIMEZONE_FILE); - testLogCount (G_LOG_LEVEL_WARNING, 1); + FileTimezone tz(TIMEZONE_FILE); + testLogCount(G_LOG_LEVEL_WARNING, 1); } /** * Test that timezone-file picks up the initial value */ -TEST_F (TimezoneFixture, InitialValue) +TEST_F(TimezoneFixture, InitialValue) { const std::string expected_timezone = "America/Chicago"; - set_file (expected_timezone); - FileTimezone tz (TIMEZONE_FILE); - ASSERT_EQ (expected_timezone, tz.timezone.get()); + set_file(expected_timezone); + FileTimezone tz(TIMEZONE_FILE); + ASSERT_EQ(expected_timezone, tz.timezone.get()); } /** * Test that clearing the timezone results in an empty string */ -TEST_F (TimezoneFixture, ChangedValue) +TEST_F(TimezoneFixture, ChangedValue) { const std::string initial_timezone = "America/Chicago"; const std::string changed_timezone = "America/New_York"; - set_file (initial_timezone); + set_file(initial_timezone); - FileTimezone tz (TIMEZONE_FILE); - ASSERT_EQ (initial_timezone, tz.timezone.get()); + FileTimezone tz(TIMEZONE_FILE); + ASSERT_EQ(initial_timezone, tz.timezone.get()); bool changed = false; auto connection = tz.timezone.changed().connect( [&changed, this](const std::string& s){ - g_message ("timezone changed to %s", s.c_str()); + g_message("timezone changed to %s", s.c_str()); changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - g_idle_add ([](gpointer gself){ - static_cast(gself)->set_file ("America/New_York"); - // static_cast(gtz)->timezone.set ("America/New_York"); + g_idle_add([](gpointer gself){ + static_cast(gself)->set_file("America/New_York"); + // static_cast(gtz)->timezone.set("America/New_York"); return G_SOURCE_REMOVE; }, this);//&tz); - g_main_loop_run (loop); + g_main_loop_run(loop); - ASSERT_TRUE (changed); - ASSERT_EQ (changed_timezone, tz.timezone.get()); + ASSERT_TRUE(changed); + ASSERT_EQ(changed_timezone, tz.timezone.get()); } diff --git a/tests/test-timezone-geoclue.cc b/tests/test-timezone-geoclue.cc index a577fbd..4bf08a7 100644 --- a/tests/test-timezone-geoclue.cc +++ b/tests/test-timezone-geoclue.cc @@ -46,21 +46,21 @@ namespace const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} }; - gboolean emit_address_changed_idle (gpointer gdata) + gboolean emit_address_changed_idle(gpointer gdata) { auto data = static_cast(gdata); GError * error = nullptr; - dbus_test_dbus_mock_object_emit_signal (data->mock, data->obj_client, + dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, "org.freedesktop.Geoclue.Address", "AddressChanged", G_VARIANT_TYPE("(ia{ss}(idd))"), - g_variant_new_parsed ("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), + g_variant_new_parsed("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), &error); if (error) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_error_free (error); + g_warning("%s: %s", G_STRFUNC, error->message); + g_error_free(error); } delete data; @@ -69,29 +69,29 @@ namespace } #endif -TEST_F (TimezoneGeoclueFixture, ChangeDetected) +TEST_F(TimezoneGeoclueFixture, ChangeDetected) { // const std::string timezone_1 = "America/Denver"; const std::string timezone_2 = "America/Chicago"; GeoclueTimezone tz; - wait_msec (500); // wait for the bus to get set up - EXPECT_EQ (timezone_1, tz.timezone.get()); + wait_msec(500); // wait for the bus to get set up + EXPECT_EQ(timezone_1, tz.timezone.get()); // start listening for a timezone change, then change the timezone bool changed = false; auto connection = tz.timezone.changed().connect( [&changed, this](const std::string& s){ - g_debug ("timezone changed to %s", s.c_str()); + g_debug("timezone changed to %s", s.c_str()); changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - setGeoclueTimezoneOnIdle (timezone_2); - //g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); - g_main_loop_run (loop); - EXPECT_EQ (timezone_2, tz.timezone.get()); + setGeoclueTimezoneOnIdle(timezone_2); + //g_timeout_add(50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); + g_main_loop_run(loop); + EXPECT_EQ(timezone_2, tz.timezone.get()); } diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc index cda53a6..d3c8e3a 100644 --- a/tests/test-timezones.cc +++ b/tests/test-timezones.cc @@ -33,33 +33,34 @@ typedef GeoclueFixture TimezonesFixture; namespace { /* convenience func to set the timezone file */ - void set_file (const std::string& text) + void set_file(const std::string& text) { - FILE * fp = fopen (TIMEZONE_FILE, "w+"); - fprintf (fp, "%s\n", text.c_str()); - fclose (fp); - sync (); + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); } } -TEST_F (TimezonesFixture, ManagerTest) +TEST_F(TimezonesFixture, ManagerTest) { std::string timezone_file = "America/New_York"; std::string timezone_geo = "America/Denver"; - set_file (timezone_file); - LiveTimezones z (TIMEZONE_FILE); - wait_msec (500); // wait for the bus to get set up - EXPECT_EQ (timezone_file, z.timezone.get()); - std::set zones = z.timezones.get(); - EXPECT_EQ (1, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); + set_file(timezone_file); + LiveTimezones z(TIMEZONE_FILE); + wait_msec(500); // wait for the bus to get set up + EXPECT_EQ(timezone_file, z.timezone.get()); + auto zones = z.timezones.get(); + //std::set zones = z.timezones.get(); + EXPECT_EQ(1, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); bool zone_changed = false; auto zone_connection = z.timezone.changed().connect([&zone_changed, this](const std::string&) { zone_changed = true; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); // start listening for a timezone change, then change the timezone @@ -67,24 +68,24 @@ TEST_F (TimezonesFixture, ManagerTest) auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { zones_changed = true; zones = timezones; - g_main_loop_quit (loop); + g_main_loop_quit(loop); }); - g_idle_add ([](gpointer gz) { + g_idle_add([](gpointer gz) { auto az = static_cast(gz); - g_message ("geolocation was %d", (int)az->geolocationEnabled.get()); - g_message ("turning geolocation on"); + g_message("geolocation was %d", (int)az->geolocationEnabled.get()); + g_message("turning geolocation on"); az->geolocationEnabled.set(true); return G_SOURCE_REMOVE; }, &z); // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property - g_main_loop_run (loop); - EXPECT_TRUE (zones_changed); - EXPECT_EQ (timezone_file, z.timezone.get()); - EXPECT_EQ (2, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); - EXPECT_EQ (1, zones.count(timezone_geo)); + g_main_loop_run(loop); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); zones_changed = false; // now tweak the geoclue value... the geoclue-detected timezone should change, @@ -92,28 +93,28 @@ TEST_F (TimezonesFixture, ManagerTest) zone_changed = false; zones_changed = false; timezone_geo = "America/Chicago"; - setGeoclueTimezoneOnIdle (timezone_geo); - g_main_loop_run (loop); - EXPECT_FALSE (zone_changed); - EXPECT_TRUE (zones_changed); - EXPECT_EQ (timezone_file, z.timezone.get()); - EXPECT_EQ (2, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); - EXPECT_EQ (1, zones.count(timezone_geo)); + setGeoclueTimezoneOnIdle(timezone_geo); + g_main_loop_run(loop); + EXPECT_FALSE(zone_changed); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); // now set the file value... this should change both the primary property and set property zone_changed = false; zones_changed = false; timezone_file = "America/Los_Angeles"; - EXPECT_EQ (0, zones.count(timezone_file)); - g_idle_add ([](gpointer str) {set_file(static_cast(str)); return G_SOURCE_REMOVE;}, const_cast(timezone_file.c_str())); - g_main_loop_run (loop); - EXPECT_TRUE (zone_changed); - EXPECT_TRUE (zones_changed); - EXPECT_EQ (timezone_file, z.timezone.get()); - EXPECT_EQ (2, zones.size()); - EXPECT_EQ (1, zones.count(timezone_file)); - EXPECT_EQ (1, zones.count(timezone_geo)); + EXPECT_EQ(0, zones.count(timezone_file)); + g_idle_add([](gpointer str) {set_file(static_cast(str)); return G_SOURCE_REMOVE;}, const_cast(timezone_file.c_str())); + g_main_loop_run(loop); + EXPECT_TRUE(zone_changed); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); diff --git a/tests/test-utils.cc b/tests/test-utils.cc index d0f277b..8246396 100644 --- a/tests/test-utils.cc +++ b/tests/test-utils.cc @@ -1,21 +1,21 @@ /* -Copyright 2012 Canonical Ltd. - -Authors: - Charles Kerr - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ #include @@ -27,36 +27,33 @@ with this program. If not, see . **** ***/ -TEST (UtilsTest, SplitSettingsLocation) +TEST(UtilsTest, SplitSettingsLocation) { - guint i; - guint n; - - struct { - const char * location; - const char * expected_zone; - const char * expected_name; - } test_cases[] = { - { "America/Chicago Chicago", "America/Chicago", "Chicago" }, - { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" }, - { "America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, - { "America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, - { " America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, - { " America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, - { "UTC UTC", "UTC", "UTC" } - }; - - for (i=0, n=G_N_ELEMENTS(test_cases); i Date: Thu, 16 Jan 2014 15:10:39 -0600 Subject: Implement Settings, a properties-cpp wrapper around GSettings --- include/datetime/settings-live.h | 19 +- include/datetime/settings.h | 16 +- src/settings-live.cpp | 410 ++++++++++++++++++--------------------- tests/CMakeLists.txt | 14 ++ tests/test-settings.cc | 197 +++++++++++++++++++ 5 files changed, 423 insertions(+), 233 deletions(-) create mode 100644 tests/test-settings.cc (limited to 'tests') diff --git a/include/datetime/settings-live.h b/include/datetime/settings-live.h index 44e27b0..202c998 100644 --- a/include/datetime/settings-live.h +++ b/include/datetime/settings-live.h @@ -38,10 +38,23 @@ public: virtual ~LiveSettings(); private: - void update_show_clock(); - - void update_key(const std::string& key); static void on_changed(GSettings*, gchar*, gpointer); + void update_key(const std::string& key); + + void update_custom_time_format(); + void update_locations(); + void update_show_calendar(); + void update_show_clock(); + void update_show_date(); + void update_show_day(); + void update_show_detected_locations(); + void update_show_events(); + void update_show_locations(); + void update_show_seconds(); + void update_show_week_numbers(); + void update_show_year(); + void update_time_format_mode(); + void update_timezone_name(); GSettings* m_settings; diff --git a/include/datetime/settings.h b/include/datetime/settings.h index 3d4bc33..418bc33 100644 --- a/include/datetime/settings.h +++ b/include/datetime/settings.h @@ -42,17 +42,19 @@ public: Settings() =default; virtual ~Settings() =default; - core::Property time_format_mode; - core::Property show_clock; - core::Property show_day; - core::Property show_year; - core::Property show_seconds; core::Property custom_time_format; + core::Property> locations; core::Property show_calendar; + core::Property show_clock; + core::Property show_date; + core::Property show_day; + core::Property show_detected_location; core::Property show_events; core::Property show_locations; - core::Property show_auto_detected_location; - core::Property> locations; + core::Property show_seconds; + core::Property show_week_numbers; + core::Property show_year; + core::Property time_format_mode; core::Property timezone_name; }; diff --git a/src/settings-live.cpp b/src/settings-live.cpp index 74085a9..2305c93 100644 --- a/src/settings-live.cpp +++ b/src/settings-live.cpp @@ -37,257 +37,221 @@ LiveSettings::LiveSettings(): { g_signal_connect (m_settings, "changed", G_CALLBACK(on_changed), this); + // init the Properties from the GSettings backend + update_custom_time_format(); + update_locations(); + update_show_calendar(); update_show_clock(); + update_show_date(); + update_show_day(); + update_show_detected_locations(); + update_show_events(); + update_show_locations(); + update_show_seconds(); + update_show_week_numbers(); + update_show_year(); + update_time_format_mode(); + update_timezone_name(); + + // now listen for clients to change the properties s.t. we can sync update GSettings + + custom_time_format.changed().connect([this](const std::string& value){ + g_settings_set_string(m_settings, SETTINGS_CUSTOM_TIME_FORMAT_S, value.c_str()); + }); + + locations.changed().connect([this](const std::vector& value){ + const int n = value.size(); + gchar** strv = g_new0(gchar*, n+1); + for(int i=0; i(value[i].c_str()); + g_settings_set_strv(m_settings, SETTINGS_LOCATIONS_S, strv); + g_free(strv); + }); + + show_calendar.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_CALENDAR_S, value); + }); + + show_clock.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_CLOCK_S, value); + }); + + show_date.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_DATE_S, value); + }); + + show_day.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_DAY_S, value); + }); + + show_detected_location.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_DETECTED_S, value); + }); + + show_events.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_EVENTS_S, value); + }); + + show_locations.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_LOCATIONS_S, value); + }); + + show_seconds.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_SECONDS_S, value); + }); + + show_week_numbers.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_WEEK_NUMBERS_S, value); + }); + + show_year.changed().connect([this](bool value){ + g_settings_set_boolean(m_settings, SETTINGS_SHOW_YEAR_S, value); + }); + + time_format_mode.changed().connect([this](TimeFormatMode value){ + g_settings_set_enum(m_settings, SETTINGS_TIME_FORMAT_S, gint(value)); + }); + + timezone_name.changed().connect([this](const std::string& value){ + g_settings_set_string(m_settings, SETTINGS_TIMEZONE_NAME_S, value.c_str()); + }); } -void LiveSettings::update_show_clock() +/*** +**** +***/ + +void LiveSettings::update_custom_time_format() { - auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S); - show_clock.set(val); + auto val = g_settings_get_string(m_settings, SETTINGS_CUSTOM_TIME_FORMAT_S); + custom_time_format.set(val); + g_free(val); } -void LiveSettings::on_changed(GSettings* /*settings*/, - gchar* key, - gpointer gself) +void LiveSettings::update_locations() { - static_cast(gself)->update_key(key); + auto strv = g_settings_get_strv(m_settings, SETTINGS_LOCATIONS_S); + std::vector l; + for(int i=0; strv && strv[i]; i++) + l.push_back(strv[i]); + g_strfreev(strv); + locations.set(l); } -void LiveSettings::update_key(const std::string& key) +void LiveSettings::update_show_calendar() { - if (key == SETTINGS_SHOW_CLOCK_S) - { - update_show_clock(); - } + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_CALENDAR_S); + show_calendar.set(val); } -/*** -**** -***/ - -} // namespace datetime -} // namespace indicator -} // namespace unity - -#if 0 - else if (!g_strcmp0(key, TIME_FORMAT_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_TIME_FORMAT_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_SECONDS_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_DAY_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_DATE_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_YEAR_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_CUSTOM_TIME_FORMAT_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_CALENDAR_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_WEEK_NUMBERS_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_EVENTS_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_LOCATIONS_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_TIMEZONE_NAME_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_SHOW_DETECTED_S)) - { - } - else if (!g_strcmp0(key, SETTINGS_LOCATIONS_S)) - { - } -#define SETTINGS_SHOW_DETECTED_S "show-auto-detected-location" -#define SETTINGS_LOCATIONS_S "locations" -#define SETTINGS_TIMEZONE_NAME_S "timezone-name" - zzz - "show-clock" - -void user_function (GSettings *settings, - gchar *key, - gpointer user_data) : Has Details - - for (i=0, n=G_N_ELEMENTS(header_settings); isettings, gstr->str, - G_CALLBACK(rebuild_header_soon), self); - } - - - const char * const calendar_settings[] = { - SETTINGS_SHOW_CALENDAR_S, - SETTINGS_SHOW_WEEK_NUMBERS_S - }; - const char * const appointment_settings[] = { - SETTINGS_SHOW_EVENTS_S, - SETTINGS_TIME_FORMAT_S, - SETTINGS_SHOW_SECONDS_S - }; - const char * const location_settings[] = { - SETTINGS_TIME_FORMAT_S, - SETTINGS_SHOW_SECONDS_S, - SETTINGS_CUSTOM_TIME_FORMAT_S, - SETTINGS_SHOW_LOCATIONS_S, - SETTINGS_LOCATIONS_S, - SETTINGS_SHOW_DETECTED_S, - SETTINGS_TIMEZONE_NAME_S - }; - const char * const time_format_string_settings[] = { - SETTINGS_TIME_FORMAT_S, - SETTINGS_SHOW_SECONDS_S, - SETTINGS_CUSTOM_TIME_FORMAT_S - }; - - - /*** - **** Listen for settings changes - ***/ - - for (i=0, n=G_N_ELEMENTS(header_settings); isettings, gstr->str, - G_CALLBACK(rebuild_header_soon), self); - } - +void LiveSettings::update_show_clock() +{ + show_clock.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_CLOCK_S)); } +void LiveSettings::update_show_date() +{ + show_date.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_DATE_S)); +} -#ifndef INDICATOR_DATETIME_SETTINGS_LIVE_H -#define INDICATOR_DATETIME_SETTINGS_LIVE_H - -#include // parent class - -#include // GSettings - -namespace unity { -namespace indicator { -namespace datetime { - -/** - * \brief #Settings implementation which uses GSettings. - */ -class LiveSettings +void LiveSettings::update_show_day() { -public: - LiveSettings(); - virtual ~LiveSettings(); + show_day.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_DAY_S)); +} -private: - GSettings* m_settings; +void LiveSettings::update_show_detected_locations() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_DETECTED_S); + show_detected_location.set(val); +} - // we've got a raw pointer here, so disable copying - LiveSettings(const LiveSettings&) =delete; - LiveSettings& operator=(const LiveSettings&) =delete; -}; +void LiveSettings::update_show_events() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_EVENTS_S); + show_events.set(val); +} +void LiveSettings::update_show_locations() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_LOCATIONS_S); + show_locations.set(val); +} -#endif // INDICATOR_DATETIME_SETTINGS_LIVE_H -/* - * Copyright 2010 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Ted Gould - * Charles Kerr - */ +void LiveSettings::update_show_seconds() +{ + show_seconds.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_SECONDS_S)); +} -#ifndef INDICATOR_DATETIME_SETTINGS_SHARED -#define INDICATOR_DATETIME_SETTINGS_SHARED +void LiveSettings::update_show_week_numbers() +{ + const auto val = g_settings_get_boolean(m_settings, SETTINGS_SHOW_WEEK_NUMBERS_S); + show_week_numbers.set(val); +} -typedef enum +void LiveSettings::update_show_year() { - TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM + show_year.set(g_settings_get_boolean(m_settings, SETTINGS_SHOW_YEAR_S)); } -TimeFormatMode; +void LiveSettings::update_time_format_mode() +{ + time_format_mode.set((TimeFormatMode)g_settings_get_enum(m_settings, SETTINGS_TIME_FORMAT_S)); +} -#endif // INDICATOR_DATETIME_SETTINGS_SHARED -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ +void LiveSettings::update_timezone_name() +{ + auto val = g_settings_get_string(m_settings, SETTINGS_TIMEZONE_NAME_S); + timezone_name.set(val); + g_free(val); +} -#ifndef INDICATOR_DATETIME_SETTINGS_H -#define INDICATOR_DATETIME_SETTINGS_H +/*** +**** +***/ -#include +void LiveSettings::on_changed(GSettings* /*settings*/, + gchar* key, + gpointer gself) +{ + static_cast(gself)->update_key(key); +} -#include +void LiveSettings::update_key(const std::string& key) +{ + if (key == SETTINGS_SHOW_CLOCK_S) + update_show_clock(); + else if (key == SETTINGS_LOCATIONS_S) + update_locations(); + else if (key == SETTINGS_TIME_FORMAT_S) + update_time_format_mode(); + else if (key == SETTINGS_SHOW_SECONDS_S) + update_show_seconds(); + else if (key == SETTINGS_SHOW_DAY_S) + update_show_day(); + else if (key == SETTINGS_SHOW_DATE_S) + update_show_date(); + else if (key == SETTINGS_SHOW_YEAR_S) + update_show_year(); + else if (key == SETTINGS_CUSTOM_TIME_FORMAT_S) + update_custom_time_format(); + else if (key == SETTINGS_SHOW_CALENDAR_S) + update_show_calendar(); + else if (key == SETTINGS_SHOW_WEEK_NUMBERS_S) + update_show_week_numbers(); + else if (key == SETTINGS_SHOW_EVENTS_S) + update_show_events(); + else if (key == SETTINGS_SHOW_LOCATIONS_S) + update_show_locations(); + else if (key == SETTINGS_SHOW_DETECTED_S) + update_show_detected_locations(); + else if (key == SETTINGS_TIMEZONE_NAME_S) + update_timezone_name(); +} -namespace unity { -namespace indicator { -namespace datetime { +/*** +**** +***/ -/** - * \brief Interface that represents user-configurable settings. - * - * See the descriptions in data/com.canonical.indicator.datetime.gschema.xml - * For more information. - */ -class Settings -{ -public: - Settings() =default; - virtual ~Settings =default; - - core::Property time_format_mode; - core::Property show_clock; - core::Property show_day; - core::Property show_year; - core::Property show_seconds; - core::Property custom_time_format; - core::Property show_calendar; - core::Property show_events; - core::Property show_locations; - core::Property show_auto_detected_location; - core::Property> locations; - core::Property timezone_name; -}; - -#endif // INDICATOR_DATETIME_SETTINGS_H -#endif +} // namespace datetime +} // namespace indicator +} // namespace unity diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb55f5a..a908801 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -91,6 +91,20 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-utils +set (TEST_NAME test-utils) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + +# test-utils +set (TEST_NAME test-settings) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + # test-desktop #set (TEST_NAME test-desktop) #add_executable (${TEST_NAME} ${TEST_NAME}.cc) diff --git a/tests/test-settings.cc b/tests/test-settings.cc new file mode 100644 index 0000000..9e52197 --- /dev/null +++ b/tests/test-settings.cc @@ -0,0 +1,197 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include +#include + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +class SettingsFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + + std::shared_ptr m_live; + std::shared_ptr m_settings; + GSettings * m_gsettings; + + virtual void SetUp() + { + super::SetUp(); + + m_gsettings = g_settings_new(SETTINGS_INTERFACE); + m_live.reset(new LiveSettings); + m_settings = std::dynamic_pointer_cast(m_live); + } + + virtual void TearDown() + { + g_clear_object(&m_gsettings); + m_settings.reset(); + m_live.reset(); + + super::TearDown(); + } + + void TestBoolProperty(core::Property& property, const gchar* key) + { + EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); + g_settings_set_boolean(m_gsettings, key, false); + EXPECT_EQ(false, property.get()); + g_settings_set_boolean(m_gsettings, key, true); + EXPECT_EQ(true, property.get()); + + property.set(false); + EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); + property.set(true); + EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); + } + + void TestStringProperty(core::Property& property, const gchar* key) + { + gchar* tmp; + std::string str; + + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(tmp, property.get()); + g_clear_pointer(&tmp, g_free); + + str = "a"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "b"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "a"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + + str = "b"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + } +}; + +/*** +**** +***/ + +TEST_F(SettingsFixture, HelloWorld) +{ + EXPECT_TRUE(true); +} + +TEST_F(SettingsFixture, BoolProperties) +{ + TestBoolProperty(m_settings->show_seconds, SETTINGS_SHOW_SECONDS_S); + TestBoolProperty(m_settings->show_calendar, SETTINGS_SHOW_CALENDAR_S); + TestBoolProperty(m_settings->show_clock, SETTINGS_SHOW_CLOCK_S); + TestBoolProperty(m_settings->show_date, SETTINGS_SHOW_DATE_S); + TestBoolProperty(m_settings->show_day, SETTINGS_SHOW_DAY_S); + TestBoolProperty(m_settings->show_detected_location, SETTINGS_SHOW_DETECTED_S); + TestBoolProperty(m_settings->show_events, SETTINGS_SHOW_EVENTS_S); + TestBoolProperty(m_settings->show_locations, SETTINGS_SHOW_LOCATIONS_S); + TestBoolProperty(m_settings->show_week_numbers, SETTINGS_SHOW_WEEK_NUMBERS_S); + TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); +} + +TEST_F(SettingsFixture, StringProperties) +{ + TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); + TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); +} + +TEST_F(SettingsFixture, TimeFormatMode) +{ + const auto key = SETTINGS_TIME_FORMAT_S; + const TimeFormatMode modes[] = { TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM }; + + for(const auto& mode : modes) + { + g_settings_set_enum(m_gsettings, key, mode); + EXPECT_EQ(mode, m_settings->time_format_mode.get()); + } + + for(const auto& mode : modes) + { + m_settings->time_format_mode.set(mode); + EXPECT_EQ(mode, g_settings_get_enum(m_gsettings, key)); + } +} + +namespace +{ + std::vector strv_to_vector(const gchar** strv) + { + std::vector v; + for(int i=0; strv && strv[i]; i++) + v.push_back(strv[i]); + return v; + } +}; + +TEST_F(SettingsFixture, Locations) +{ + const auto key = SETTINGS_LOCATIONS_S; + + const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; + const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; + const std::vector av = strv_to_vector(astrv); + const std::vector bv = strv_to_vector(bstrv); + + g_settings_set_strv(m_gsettings, key, astrv); + EXPECT_EQ(av, m_settings->locations.get()); + g_settings_set_strv(m_gsettings, key, bstrv); + EXPECT_EQ(bv, m_settings->locations.get()); + + m_settings->locations.set(av); + auto tmp = g_settings_get_strv(m_gsettings, key); + auto vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(av, vtmp); + + m_settings->locations.set(bv); + tmp = g_settings_get_strv(m_gsettings, key); + vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(bv, vtmp); +} + +#if 0 + core::Property> locations; + core::Property time_format_mode; + +#endif -- cgit v1.2.3 From 78d0a231c12c159d1130ec080efab472f59851af Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 16:42:36 -0600 Subject: update DesktopFormatter class to use the "Settings" class instead of using GSettings directly. --- include/datetime/formatter.h | 19 +++-- src/formatter-desktop.cpp | 186 +++++++++++++++++-------------------------- src/formatter.cpp | 49 +++++------- tests/test-formatter.cc | 177 +++++++++++++++++++--------------------- 4 files changed, 186 insertions(+), 245 deletions(-) (limited to 'tests') diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index 09ed035..d8736c7 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -23,8 +23,14 @@ #include #include +#include +#include + #include +#include +#include + namespace unity { namespace indicator { namespace datetime { @@ -109,13 +115,16 @@ private: class DesktopFormatter: public Formatter { public: - DesktopFormatter(const std::shared_ptr&); - ~DesktopFormatter(); + DesktopFormatter(const std::shared_ptr&, const std::shared_ptr&); private: - class Impl; - friend Impl; - std::unique_ptr p; + std::shared_ptr m_settings; + + void rebuildHeaderFormat(); + const gchar* getFullTimeFormatString() const; + std::string getHeaderLabelFormatString() const; + const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const; + }; diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 3f942f4..5efdf8b 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -18,10 +18,6 @@ */ #include -#include - -#include -#include namespace unity { namespace indicator { @@ -31,80 +27,108 @@ namespace datetime { **** ***/ -class DesktopFormatter::Impl +namespace { -public: -Impl(DesktopFormatter * owner, const std::shared_ptr& clock): - m_owner(owner), - m_clock(clock), - m_settings(g_settings_new(SETTINGS_INTERFACE)) +std::string joinDateAndTimeFormatStrings(const char* date_string, + const char* time_string) { - const gchar * const keys[] = { "changed::" SETTINGS_SHOW_SECONDS_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_TIME_FORMAT_S, - "changed::" SETTINGS_CUSTOM_TIME_FORMAT_S, - "changed::" SETTINGS_SHOW_DAY_S, - "changed::" SETTINGS_SHOW_DATE_S, - "changed::" SETTINGS_SHOW_YEAR_S }; - for(const auto& key : keys) - g_signal_connect(m_settings, key, G_CALLBACK(onSettingsChanged), this); + std::string str; - rebuildHeaderFormat(); -} + if (date_string && time_string) + { + /* TRANSLATORS: This is a format string passed to strftime to + * combine the date and the time. The value of "%s\u2003%s" + * will result in a string like this in US English 12-hour time: + * 'Fri Jul 16 11:50 AM'. The space in between date and time is + * a Unicode en space (E28082 in UTF-8 hex). */ + str = date_string; + str += "\u2003"; + str += time_string; + } + else if (date_string) + { + str = date_string; + } + else // time_string + { + str = time_string; + } -~Impl() -{ - g_signal_handlers_disconnect_by_data(m_settings, this); - g_object_unref(m_settings); + return str; } +} // unnamed namespace -private: +/*** +**** +***/ -static void onSettingsChanged(GSettings * /*changed*/, - const gchar * /*key*/, - gpointer gself) +DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock_in, + const std::shared_ptr& settings_in): + Formatter(clock_in), + m_settings(settings_in) { - static_cast(gself)->rebuildHeaderFormat(); + m_settings->show_day.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->show_date.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->show_year.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->show_seconds.changed().connect([this](bool){rebuildHeaderFormat();}); + m_settings->time_format_mode.changed().connect([this](TimeFormatMode){rebuildHeaderFormat();}); + m_settings->custom_time_format.changed().connect([this](const std::string&){rebuildHeaderFormat();}); + + rebuildHeaderFormat(); } -void rebuildHeaderFormat() +void DesktopFormatter::rebuildHeaderFormat() { - auto fmt = getHeaderLabelFormatString(m_settings); - m_owner->headerFormat.set(fmt); - g_free(fmt); + headerFormat.set(getHeaderLabelFormatString()); } -private: - -gchar* getHeaderLabelFormatString(GSettings* s) const +std::string DesktopFormatter::getHeaderLabelFormatString() const { - char * fmt; - const auto mode = g_settings_get_enum(s, SETTINGS_TIME_FORMAT_S); + std::string fmt; + const auto mode = m_settings->time_format_mode.get(); if (mode == TIME_FORMAT_MODE_CUSTOM) { - fmt = g_settings_get_string(s, SETTINGS_CUSTOM_TIME_FORMAT_S); + fmt = m_settings->custom_time_format.get(); } else { - const auto show_day = g_settings_get_boolean(s, SETTINGS_SHOW_DAY_S); - const auto show_date = g_settings_get_boolean(s, SETTINGS_SHOW_DATE_S); - const auto show_year = show_date && g_settings_get_boolean(s, SETTINGS_SHOW_YEAR_S); + const auto show_day = m_settings->show_day.get(); + const auto show_date = m_settings->show_date.get(); + const auto show_year = show_date && m_settings->show_year.get(); const auto date_fmt = getDateFormat(show_day, show_date, show_year); - const auto time_fmt = getFullTimeFormatString(s); + const auto time_fmt = getFullTimeFormatString(); fmt = joinDateAndTimeFormatStrings(date_fmt, time_fmt); } return fmt; } -const gchar* T_(const gchar* in) const +const gchar* DesktopFormatter::getFullTimeFormatString() const { - return m_owner->T_(in); + const auto show_seconds = m_settings->show_seconds.get(); + + bool twelvehour; + switch (m_settings->time_format_mode.get()) + { + case TIME_FORMAT_MODE_LOCALE_DEFAULT: + twelvehour = is_locale_12h(); + break; + + case TIME_FORMAT_MODE_24_HOUR: + twelvehour = false; + break; + + default: + twelvehour = true; + break; + } + + return getDefaultHeaderTimeFormat(twelvehour, show_seconds); } -const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const +const gchar* DesktopFormatter::getDateFormat(bool show_day, bool show_date, bool show_year) const { const char * fmt; @@ -135,74 +159,6 @@ const gchar* getDateFormat(bool show_day, bool show_date, bool show_year) const return fmt; } -const gchar* getFullTimeFormatString(GSettings* settings) const -{ - auto show_seconds = g_settings_get_boolean(settings, SETTINGS_SHOW_SECONDS_S); - - bool twelvehour; - switch (g_settings_get_enum(settings, SETTINGS_TIME_FORMAT_S)) - { - case TIME_FORMAT_MODE_LOCALE_DEFAULT: - twelvehour = is_locale_12h(); - break; - - case TIME_FORMAT_MODE_24_HOUR: - twelvehour = false; - break; - - default: - twelvehour = true; - break; - } - - return m_owner->getDefaultHeaderTimeFormat(twelvehour, show_seconds); -} - -gchar* joinDateAndTimeFormatStrings(const char* date_string, - const char* time_string) const -{ - gchar * str; - - if (date_string && time_string) - { - /* TRANSLATORS: This is a format string passed to strftime to - * combine the date and the time. The value of "%s\u2003%s" - * will result in a string like this in US English 12-hour time: - * 'Fri Jul 16 11:50 AM'. The space in between date and time is - * a Unicode en space (E28082 in UTF-8 hex). */ - str = g_strdup_printf("%s\u2003%s", date_string, time_string); - } - else if (date_string) - { - str = g_strdup(date_string); - } - else // time_string - { - str = g_strdup(time_string); - } - - return str; -} - -private: - -DesktopFormatter * const m_owner; -std::shared_ptr m_clock; -GSettings * m_settings; -}; - -/*** -**** -***/ - -DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock): - Formatter(clock), - p(new Impl(this, clock)) -{ -} - -DesktopFormatter::~DesktopFormatter() = default; - /*** **** ***/ diff --git a/src/formatter.cpp b/src/formatter.cpp index 1f26cc7..88a64df 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -124,7 +124,7 @@ guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) g_date_time_unref(next); return seconds; } -} // anonymous namespace +} // unnamed namespace @@ -167,7 +167,7 @@ private: { clearTimer(m_header_timer); - const std::string fmt = m_owner->headerFormat.get(); + const auto fmt = m_owner->headerFormat.get(); const bool header_shows_seconds = (fmt.find("%s") != std::string::npos) || (fmt.find("%S") != std::string::npos) || (fmt.find("%T") != std::string::npos) @@ -214,7 +214,7 @@ private: } private: - Formatter * const m_owner; + Formatter* const m_owner; guint m_header_timer = 0; guint m_relative_timer = 0; @@ -248,7 +248,7 @@ Formatter::is_locale_12h() return true; } -const char * +const char* Formatter::T_(const char *msg) { /* General strategy here is to make sure LANGUAGE is empty (since that @@ -264,17 +264,16 @@ Formatter::T_(const char *msg) LC_MESSAGES directory, so we won't find any translation there. */ - char *message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); - const char *time_locale = setlocale(LC_TIME, nullptr); - char *language = g_strdup(g_getenv("LANGUAGE")); - const char *rv; + auto message_locale = g_strdup(setlocale(LC_MESSAGES, nullptr)); + const auto time_locale = setlocale(LC_TIME, nullptr); + auto language = g_strdup(g_getenv("LANGUAGE")); if (language) g_unsetenv("LANGUAGE"); setlocale(LC_MESSAGES, time_locale); /* Get the LC_TIME version */ - rv = _(msg); + const auto rv = _(msg); /* Put everything back the way it was */ setlocale(LC_MESSAGES, message_locale); @@ -286,10 +285,10 @@ Formatter::T_(const char *msg) return rv; } -const char * +const char* Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) { - const char * fmt; + const char* fmt; if (twelvehour && show_seconds) /* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */ @@ -322,9 +321,9 @@ typedef enum } date_proximity_t; -date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) +date_proximity_t getDateProximity(GDateTime* now, GDateTime* time) { - date_proximity_t prox = DATE_PROXIMITY_FAR; + auto prox = DATE_PROXIMITY_FAR; gint now_year, now_month, now_day; gint time_year, time_month, time_day; @@ -337,10 +336,9 @@ date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) // does it happen tomorrow? if (prox == DATE_PROXIMITY_FAR) { - GDateTime * tomorrow; - gint tom_year, tom_month, tom_day; + auto tomorrow = g_date_time_add_days(now, 1); - tomorrow = g_date_time_add_days(now, 1); + gint tom_year, tom_month, tom_day; g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day); if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) prox = DATE_PROXIMITY_TOMORROW; @@ -351,14 +349,11 @@ date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) // does it happen this week? if (prox == DATE_PROXIMITY_FAR) { - GDateTime * week; - GDateTime * week_bound; - - week = g_date_time_add_days(now, 6); - week_bound = g_date_time_new_local(g_date_time_get_year(week), - g_date_time_get_month(week), - g_date_time_get_day_of_month(week), - 23, 59, 59.9); + auto week = g_date_time_add_days(now, 6); + auto week_bound = g_date_time_new_local(g_date_time_get_year(week), + g_date_time_get_month(week), + g_date_time_get_day_of_month(week), + 23, 59, 59.9); if (g_date_time_compare(time, week_bound) <= 0) prox = DATE_PROXIMITY_WEEK; @@ -369,7 +364,7 @@ date_proximity_t getDateProximity(GDateTime * now, GDateTime * time) return prox; } -} // anonymous namespace +} // unnamed namespace /** * _ a time today should be shown as just the time (e.g. “3:55 PM”) @@ -391,7 +386,7 @@ std::string Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const { std::string ret; - auto now = p->m_clock->localtime().get(); + const auto now = p->m_clock->localtime().get(); if (then != nullptr) { @@ -448,7 +443,5 @@ Formatter::getRelativeFormat(GDateTime* then, GDateTime* then_end) const ***/ } // namespace datetime - } // namespace indicator - } // namespace unity diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc index 42c828c..9950453 100644 --- a/tests/test-formatter.cc +++ b/tests/test-formatter.cc @@ -22,18 +22,14 @@ #include #include -#include +#include #include #include #include -using unity::indicator::datetime::Clock; -using unity::indicator::datetime::DateTime; -using unity::indicator::datetime::DesktopFormatter; -using unity::indicator::datetime::MockClock; -using unity::indicator::datetime::PhoneFormatter; +using namespace unity::indicator::datetime; /*** **** @@ -44,75 +40,73 @@ class FormatterFixture: public GlibFixture private: typedef GlibFixture super; - gchar * original_locale = nullptr; + gchar* m_original_locale = nullptr; protected: - GSettings * settings = nullptr; + std::shared_ptr m_settings; - virtual void SetUp () + virtual void SetUp() { - super::SetUp (); + super::SetUp(); - settings = g_settings_new (SETTINGS_INTERFACE); - - original_locale = g_strdup (setlocale (LC_TIME, NULL)); + m_settings.reset(new Settings); + m_original_locale = g_strdup(setlocale(LC_TIME, nullptr)); } - virtual void TearDown () + virtual void TearDown() { - g_clear_object (&settings); + m_settings.reset(); - setlocale (LC_TIME, original_locale); - g_clear_pointer (&original_locale, g_free); + setlocale(LC_TIME, m_original_locale); + g_clear_pointer(&m_original_locale, g_free); - super::TearDown (); + super::TearDown(); } - bool SetLocale (const char * expected_locale, const char * name) + bool SetLocale(const char* expected_locale, const char* name) { - setlocale (LC_TIME, expected_locale); - const char * actual_locale = setlocale (LC_TIME, NULL); - if (!g_strcmp0 (expected_locale, actual_locale)) + setlocale(LC_TIME, expected_locale); + const auto actual_locale = setlocale(LC_TIME, nullptr); + if (!g_strcmp0(expected_locale, actual_locale)) { return true; } else { - g_warning ("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); + g_warning("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); return false; } } - inline bool Set24hLocale () { return SetLocale ("C", "24h"); } - inline bool Set12hLocale () { return SetLocale ("en_US.utf8", "12h"); } + inline bool Set24hLocale() { return SetLocale("C", "24h"); } + inline bool Set12hLocale() { return SetLocale("en_US.utf8", "12h"); } }; /** * Test the phone header format */ -TEST_F (FormatterFixture, TestPhoneHeader) +TEST_F(FormatterFixture, TestPhoneHeader) { - GDateTime * now = g_date_time_new_local (2020, 10, 31, 18, 30, 59); - std::shared_ptr mock (new MockClock(DateTime(now))); + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); g_date_time_unref(now); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); // test the default value in a 24h locale - if (Set24hLocale ()) + if(Set24hLocale()) { - PhoneFormatter formatter (clock); - EXPECT_EQ (std::string("%H:%M"), formatter.headerFormat.get()); - EXPECT_EQ (std::string("18:30"), formatter.header.get()); + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("18:30"), formatter.header.get()); } // test the default value in a 12h locale - if (Set12hLocale ()) + if(Set12hLocale()) { - PhoneFormatter formatter (clock); - EXPECT_EQ (std::string("%l:%M %p"), formatter.headerFormat.get()); - EXPECT_EQ (std::string(" 6:30 PM"), formatter.header.get()); + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); } } @@ -121,14 +115,14 @@ TEST_F (FormatterFixture, TestPhoneHeader) /** * Test the default values of the desktop header format */ -TEST_F (FormatterFixture, TestDesktopHeader) +TEST_F(FormatterFixture, TestDesktopHeader) { struct { bool is_12h; bool show_day; bool show_date; bool show_year; - const char * expected_format_string; + const char* expected_format_string; } test_cases[] = { { false, false, false, false, "%H:%M" }, { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false @@ -148,26 +142,21 @@ TEST_F (FormatterFixture, TestDesktopHeader) { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } }; - GDateTime * now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - std::shared_ptr mock(new MockClock(DateTime(now))); + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); g_date_time_unref(now); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); - for (int i=0, n=G_N_ELEMENTS(test_cases); ishow_day.set(test_case.show_day); + m_settings->show_date.set(test_case.show_date); + m_settings->show_year.set(test_case.show_year); - ASSERT_STREQ (test_cases[i].expected_format_string, f.headerFormat.get().c_str()); - - g_settings_reset (settings, SETTINGS_SHOW_DAY_S); - g_settings_reset (settings, SETTINGS_SHOW_DATE_S); - g_settings_reset (settings, SETTINGS_SHOW_YEAR_S); + ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); } } } @@ -175,15 +164,15 @@ TEST_F (FormatterFixture, TestDesktopHeader) /** * Test the default values of the desktop header format */ -TEST_F (FormatterFixture, TestUpcomingTimes) +TEST_F(FormatterFixture, TestUpcomingTimes) { - auto a = g_date_time_new_local (2020, 10, 31, 18, 30, 59); + auto a = g_date_time_new_local(2020, 10, 31, 18, 30, 59); struct { gboolean is_12h; - GDateTime * now; - GDateTime * then; - const char * expected_format_string; + GDateTime* now; + GDateTime* then; + const char* expected_format_string; } test_cases[] = { { true, g_date_time_ref(a), g_date_time_ref(a), "%l:%M %p" }, // identical time { true, g_date_time_ref(a), g_date_time_add_hours(a,1), "%l:%M %p" }, // later today @@ -200,45 +189,42 @@ TEST_F (FormatterFixture, TestUpcomingTimes) { false, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%H:%M" } // over one week away }; - for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(tmp));//DateTime(test_cases[i].now))); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); - DesktopFormatter f (clock); + std::shared_ptr clock (new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); - std::string fmt = f.getRelativeFormat (test_cases[i].then); - ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); + const auto fmt = f.getRelativeFormat(test_case.then); + ASSERT_EQ(test_case.expected_format_string, fmt); - g_clear_pointer (&test_cases[i].now, g_date_time_unref); - g_clear_pointer (&test_cases[i].then, g_date_time_unref); + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); } } - g_date_time_unref (a); + g_date_time_unref(a); } /** * Test the default values of the desktop header format */ -TEST_F (FormatterFixture, TestEventTimes) +TEST_F(FormatterFixture, TestEventTimes) { - auto day = g_date_time_new_local (2013, 1, 1, 13, 0, 0); - auto day_begin = g_date_time_new_local (2013, 1, 1, 13, 0, 0); - auto day_end = g_date_time_add_days (day_begin, 1); - auto tomorrow_begin = g_date_time_add_days (day_begin, 1); - auto tomorrow_end = g_date_time_add_days (tomorrow_begin, 1); + auto day = g_date_time_new_local(2013, 1, 1, 13, 0, 0); + auto day_begin = g_date_time_new_local(2013, 1, 1, 13, 0, 0); + auto day_end = g_date_time_add_days(day_begin, 1); + auto tomorrow_begin = g_date_time_add_days(day_begin, 1); + auto tomorrow_end = g_date_time_add_days(tomorrow_begin, 1); struct { bool is_12h; - GDateTime * now; - GDateTime * then; - GDateTime * then_end; - const char * expected_format_string; + GDateTime* now; + GDateTime* then; + GDateTime* then_end; + const char* expected_format_string; } test_cases[] = { { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, @@ -246,28 +232,25 @@ TEST_F (FormatterFixture, TestEventTimes) { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } }; - for (int i=0, n=G_N_ELEMENTS(test_cases); i mock (new MockClock(DateTime(test_cases[i].now))); - std::shared_ptr clock = std::dynamic_pointer_cast(mock); - DesktopFormatter f (clock); + std::shared_ptr clock(new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); - std::string fmt = f.getRelativeFormat (test_cases[i].then, test_cases[i].then_end); - ASSERT_STREQ (test_cases[i].expected_format_string, fmt.c_str()); + const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); + ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); - g_clear_pointer (&test_cases[i].now, g_date_time_unref); - g_clear_pointer (&test_cases[i].then, g_date_time_unref); - g_clear_pointer (&test_cases[i].then_end, g_date_time_unref); + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); + g_clear_pointer(&test_case.then_end, g_date_time_unref); } } - g_date_time_unref (tomorrow_end); - g_date_time_unref (tomorrow_begin); - g_date_time_unref (day_end); - g_date_time_unref (day_begin); - g_date_time_unref (day); + g_date_time_unref(tomorrow_end); + g_date_time_unref(tomorrow_begin); + g_date_time_unref(day_end); + g_date_time_unref(day_begin); + g_date_time_unref(day); } - - -- cgit v1.2.3 From 9c81a4d60d06b1f33001602cd0cde9844c9233a6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 16:44:06 -0600 Subject: update SettingsLocations class to use the "Settings" class instead of using GSettings directly. --- include/datetime/locations-settings.h | 18 ++--- src/locations-settings.cpp | 50 +++++-------- tests/test-locations.cc | 133 +++++++++++++++++----------------- 3 files changed, 89 insertions(+), 112 deletions(-) (limited to 'tests') diff --git a/include/datetime/locations-settings.h b/include/datetime/locations-settings.h index eaabf73..4072736 100644 --- a/include/datetime/locations-settings.h +++ b/include/datetime/locations-settings.h @@ -22,18 +22,15 @@ #include // base class -#include -#include +#include +#include namespace unity { namespace indicator { namespace datetime { -class Timezones; - /** - * \brief Settings implentation which builds its list from the - * user's GSettings and from the Timezones passed in the ctor. + * \brief #Locations implementation which builds its list from the #Settings. */ class SettingsLocations: public Locations { @@ -42,15 +39,12 @@ public: * @param[in] schemaId the settings schema to load * @param[in] timezones the timezones to always show first in the list */ - SettingsLocations (const std::string& schemaId, + SettingsLocations (const std::shared_ptr& settings, const std::shared_ptr& timezones); -protected: - std::unique_ptr> m_settings; - std::shared_ptr m_timezones; - private: - static void onSettingsChanged (gpointer gself); + std::shared_ptr m_settings; + std::shared_ptr m_timezones; void reload(); }; diff --git a/src/locations-settings.cpp b/src/locations-settings.cpp index 646a360..9b90bc0 100644 --- a/src/locations-settings.cpp +++ b/src/locations-settings.cpp @@ -29,49 +29,39 @@ namespace unity { namespace indicator { namespace datetime { -SettingsLocations::SettingsLocations(const std::string& schemaId, +SettingsLocations::SettingsLocations(const std::shared_ptr& settings, const std::shared_ptr& timezones): + m_settings(settings), m_timezones(timezones) { - auto deleter = [](GSettings* s){g_object_unref(s);}; - m_settings = std::unique_ptr>(g_settings_new(schemaId.c_str()), deleter); - const char * keys[] = { "changed::" SETTINGS_LOCATIONS_S, - "changed::" SETTINGS_SHOW_LOCATIONS_S }; - for (const auto& key : keys) - g_signal_connect_swapped(m_settings.get(), key, G_CALLBACK(onSettingsChanged), this); - - timezones->timezone.changed().connect([this](const std::string&){reload();}); - timezones->timezones.changed().connect([this](const std::set&){reload();}); + m_settings->locations.changed().connect([this](const std::vector&){reload();}); + m_settings->show_locations.changed().connect([this](bool){reload();}); + m_timezones->timezone.changed().connect([this](const std::string&){reload();}); + m_timezones->timezones.changed().connect([this](const std::set&){reload();}); reload(); } -void -SettingsLocations::onSettingsChanged(gpointer gself) -{ - static_cast(gself)->reload(); -} - void SettingsLocations::reload() { std::vector v; - auto settings = m_settings.get(); + const std::string timezone_name = m_settings->timezone_name.get(); // add the primary timezone first auto zone = m_timezones->timezone.get(); if (!zone.empty()) { - gchar * name = get_current_zone_name(zone.c_str(), settings); + gchar * name = get_beautified_timezone_name(zone.c_str(), timezone_name.c_str()); Location l(zone, name); v.push_back(l); g_free(name); } // add the other detected timezones - for (const auto& zone : m_timezones->timezones.get()) + for(const auto& zone : m_timezones->timezones.get()) { - gchar * name = get_current_zone_name(zone.c_str(), settings); + gchar * name = get_beautified_timezone_name(zone.c_str(), timezone_name.c_str()); Location l(zone, name); if (std::find(v.begin(), v.end(), l) == v.end()) v.push_back(l); @@ -79,23 +69,19 @@ SettingsLocations::reload() } // maybe add the user-specified locations - if (g_settings_get_boolean(settings, SETTINGS_SHOW_LOCATIONS_S)) + if (m_settings->show_locations.get()) { - gchar ** user_locations = g_settings_get_strv(settings, SETTINGS_LOCATIONS_S); - - for (int i=0; user_locations[i]; i++) + for(const auto& locstr : m_settings->locations.get()) { - gchar * zone; - gchar * name; - split_settings_location(user_locations[i], &zone, &name); - Location l(zone, name); - if (std::find(v.begin(), v.end(), l) == v.end()) - v.push_back(l); + gchar* zone; + gchar* name; + split_settings_location(locstr.c_str(), &zone, &name); + Location loc(zone, name); + if (std::find(v.begin(), v.end(), loc) == v.end()) + v.push_back(loc); g_free(name); g_free(zone); } - - g_strfreev(user_locations); } locations.set(v); diff --git a/tests/test-locations.cc b/tests/test-locations.cc index edaac69..65adbc7 100644 --- a/tests/test-locations.cc +++ b/tests/test-locations.cc @@ -21,18 +21,11 @@ #include "glib-fixture.h" -#include -#include #include -#include - -#include - -#include -#include using unity::indicator::datetime::Location; using unity::indicator::datetime::Locations; +using unity::indicator::datetime::Settings; using unity::indicator::datetime::SettingsLocations; using unity::indicator::datetime::Timezones; @@ -48,8 +41,9 @@ class LocationsFixture: public GlibFixture protected: - GSettings * settings = nullptr; - std::shared_ptr timezones; + //GSettings * settings = nullptr; + std::shared_ptr m_settings; + std::shared_ptr m_timezones; const std::string nyc = "America/New_York"; const std::string chicago = "America/Chicago"; @@ -57,20 +51,24 @@ class LocationsFixture: public GlibFixture { super::SetUp(); - settings = g_settings_new(SETTINGS_INTERFACE); - const gchar * location_strv[] = { "America/Los_Angeles Oakland", "America/Chicago Chicago", "America/Chicago Oklahoma City", "America/Toronto Toronto", "Europe/London London", "Europe/Berlin Berlin", NULL }; - g_settings_set_strv(settings, SETTINGS_LOCATIONS_S, location_strv); - g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, true); - - timezones.reset(new Timezones); - timezones->timezone.set(chicago); - timezones->timezones.set(std::set({ nyc, chicago })); + m_settings.reset(new Settings); + m_settings->show_locations.set(true); + m_settings->locations.set({"America/Los_Angeles Oakland", + "America/Chicago Chicago", + "America/Chicago Oklahoma City", + "America/Toronto Toronto", + "Europe/London London", + "Europe/Berlin Berlin"}); + + m_timezones.reset(new Timezones); + m_timezones->timezone.set(chicago); + m_timezones->timezones.set(std::set({ nyc, chicago })); } virtual void TearDown() { - //timezones.reset(nullptr); - g_clear_object(&settings); + m_timezones.reset(); + m_settings.reset(); super::TearDown(); } @@ -78,42 +76,42 @@ class LocationsFixture: public GlibFixture TEST_F(LocationsFixture, Timezones) { - g_settings_set_boolean(settings, SETTINGS_SHOW_LOCATIONS_S, false); + m_settings->show_locations.set(false); - SettingsLocations locations(SETTINGS_INTERFACE, timezones); - std::vector l = locations.locations.get(); + SettingsLocations locations(m_settings, m_timezones); + const auto l = locations.locations.get(); EXPECT_EQ(2, l.size()); - EXPECT_EQ("Chicago", l[0].name); - EXPECT_EQ(chicago, l[0].zone); - EXPECT_EQ("New York", l[1].name); - EXPECT_EQ(nyc, l[1].zone); + EXPECT_STREQ("Chicago", l[0].name().c_str()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); } TEST_F(LocationsFixture, SettingsLocations) { - SettingsLocations locations(SETTINGS_INTERFACE, timezones); + SettingsLocations locations(m_settings, m_timezones); - std::vector l = locations.locations.get(); + const auto l = locations.locations.get(); EXPECT_EQ(7, l.size()); - EXPECT_EQ("Chicago", l[0].name); - EXPECT_EQ(chicago, l[0].zone); - EXPECT_EQ("New York", l[1].name); - EXPECT_EQ(nyc, l[1].zone); - EXPECT_EQ("Oakland", l[2].name); - EXPECT_EQ("America/Los_Angeles", l[2].zone); - EXPECT_EQ("Oklahoma City", l[3].name); - EXPECT_EQ("America/Chicago", l[3].zone); - EXPECT_EQ("Toronto", l[4].name); - EXPECT_EQ("America/Toronto", l[4].zone); - EXPECT_EQ("London", l[5].name); - EXPECT_EQ("Europe/London", l[5].zone); - EXPECT_EQ("Berlin", l[6].name); - EXPECT_EQ("Europe/Berlin", l[6].zone); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("Oklahoma City", l[3].name()); + EXPECT_EQ("America/Chicago", l[3].zone()); + EXPECT_EQ("Toronto", l[4].name()); + EXPECT_EQ("America/Toronto", l[4].zone()); + EXPECT_EQ("London", l[5].name()); + EXPECT_EQ("Europe/London", l[5].zone()); + EXPECT_EQ("Berlin", l[6].name()); + EXPECT_EQ("Europe/Berlin", l[6].zone()); } TEST_F(LocationsFixture, ChangeLocationStrings) { - SettingsLocations locations(SETTINGS_INTERFACE, timezones); + SettingsLocations locations(m_settings, m_timezones); bool locations_changed = false; locations.locations.changed().connect([&locations_changed, this](const std::vector&){ @@ -121,33 +119,32 @@ TEST_F(LocationsFixture, ChangeLocationStrings) g_main_loop_quit(loop); }); - g_idle_add([](gpointer gsettings){ - const gchar * strv[] = { "America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin", NULL }; - g_settings_set_strv(static_cast(gsettings), SETTINGS_LOCATIONS_S, strv); + g_idle_add([](gpointer settings){ + static_cast(settings)->locations.set({"America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin"}); return G_SOURCE_REMOVE; - }, settings); + }, m_settings.get()); g_main_loop_run(loop); EXPECT_TRUE(locations_changed); - std::vector l = locations.locations.get(); + const auto l = locations.locations.get(); EXPECT_EQ(5, l.size()); - EXPECT_EQ("Chicago", l[0].name); - EXPECT_EQ(chicago, l[0].zone); - EXPECT_EQ("New York", l[1].name); - EXPECT_EQ(nyc, l[1].zone); - EXPECT_EQ("Oakland", l[2].name); - EXPECT_EQ("America/Los_Angeles", l[2].zone); - EXPECT_EQ("London", l[3].name); - EXPECT_EQ("Europe/London", l[3].zone); - EXPECT_EQ("Berlin", l[4].name); - EXPECT_EQ("Europe/Berlin", l[4].zone); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("London", l[3].name()); + EXPECT_EQ("Europe/London", l[3].zone()); + EXPECT_EQ("Berlin", l[4].name()); + EXPECT_EQ("Europe/Berlin", l[4].zone()); locations_changed = false; } TEST_F(LocationsFixture, ChangeLocationVisibility) { - SettingsLocations locations(SETTINGS_INTERFACE, timezones); + SettingsLocations locations(m_settings, m_timezones); bool locations_changed = false; locations.locations.changed().connect([&locations_changed, this](const std::vector&){ @@ -155,18 +152,18 @@ TEST_F(LocationsFixture, ChangeLocationVisibility) g_main_loop_quit(loop); }); - g_idle_add([](gpointer gsettings){ - g_settings_set_boolean(static_cast(gsettings), SETTINGS_SHOW_LOCATIONS_S, false); + g_idle_add([](gpointer settings){ + static_cast(settings)->show_locations.set(false); return G_SOURCE_REMOVE; - }, settings); + }, m_settings.get()); g_main_loop_run(loop); EXPECT_TRUE(locations_changed); - std::vector l = locations.locations.get(); + const auto l = locations.locations.get(); EXPECT_EQ(2, l.size()); - EXPECT_EQ("Chicago", l[0].name); - EXPECT_EQ(chicago, l[0].zone); - EXPECT_EQ("New York", l[1].name); - EXPECT_EQ(nyc, l[1].zone); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); } -- cgit v1.2.3 From a2b5c79157fa8db36d94786de1b86b756308912d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 16:45:40 -0600 Subject: Plug the Settings object into the State container s.t. menus and actions can update themselves when the user's settings change. --- include/datetime/state.h | 14 +++++++------- src/actions.cpp | 4 ++-- src/menu.cpp | 20 +++++--------------- tests/test-menus.cc | 4 ++-- 4 files changed, 16 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/include/datetime/state.h b/include/datetime/state.h index a82bb4b..e735b6f 100644 --- a/include/datetime/state.h +++ b/include/datetime/state.h @@ -21,9 +21,10 @@ #define INDICATOR_DATETIME_STATE_H #include -#include -#include #include +#include +#include +#include #include @@ -47,18 +48,17 @@ namespace datetime { * @see Clock * @see Planner * @see Locations + * @see Settings */ struct State { - std::shared_ptr timezones; std::shared_ptr clock; - std::shared_ptr planner; std::shared_ptr locations; + std::shared_ptr planner; + std::shared_ptr settings; + std::shared_ptr timezones; - core::Property show_events; - core::Property show_clock; core::Property calendar_day; - core::Property show_week_numbers; }; } // namespace datetime diff --git a/src/actions.cpp b/src/actions.cpp index 4efe950..52ee1eb 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -154,12 +154,12 @@ GVariant* create_calendar_state(std::shared_ptr& state) g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "show-week-numbers"; - v = g_variant_new_boolean(state->show_week_numbers.get()); + v = g_variant_new_boolean(state->settings->show_week_numbers.get()); g_variant_builder_add(&dict_builder, "{sv}", key, v); return g_variant_builder_end(&dict_builder); } -} // anonymous namespace +} // unnamed namespace /*** **** diff --git a/src/menu.cpp b/src/menu.cpp index 76306cc..c81f185 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -69,11 +69,11 @@ protected: update_section(Appointments); // uses formatter.getRelativeFormat() update_section(Locations); // uses formatter.getRelativeFormat() }); - m_state->show_clock.changed().connect([this](bool){ + m_state->settings->show_clock.changed().connect([this](bool){ update_header(); // update header's label update_section(Locations); // locations' relative time may have changed }); - m_state->show_events.changed().connect([this](bool){ + m_state->settings->show_events.changed().connect([this](bool){ update_section(Appointments); // showing events got toggled }); m_state->planner->upcoming.changed().connect([this](const std::vector&){ @@ -261,7 +261,7 @@ private: { auto menu = g_menu_new(); - if (((profile==Phone) || (profile==Desktop)) && m_state->show_events.get()) + if (((profile==Phone) || (profile==Desktop)) && m_state->settings->show_events.get()) { add_appointments (menu, profile); @@ -363,14 +363,14 @@ protected: std::shared_ptr& state_, std::shared_ptr& actions_): MenuImpl(profile_, name_, state_, actions_, - std::shared_ptr(new DesktopFormatter(state_->clock))) + std::shared_ptr(new DesktopFormatter(state_->clock, state_->settings))) { update_header(); } GVariant* create_header_state() { - const auto visible = m_state->show_clock.get(); + const auto visible = m_state->settings->show_clock.get(); const auto title = _("Date and Time"); auto label = g_variant_new_string(m_formatter->header.get().c_str()); @@ -472,32 +472,22 @@ std::shared_ptr MenuFactory::buildMenu(Menu::Profile profile) { std::shared_ptr menu; - m_state->show_clock.set (true); // FIXME - - //std::shared_ptr state(new State); - //state->clock = clock; - //state->planner = planner; - //state->locations = locations; switch (profile) { case Menu::Desktop: - m_state->show_events.set(true); // FIXME menu.reset(new DesktopMenu(m_state, m_actions)); break; case Menu::DesktopGreeter: - m_state->show_events.set(true); // FIXME menu.reset(new DesktopGreeterMenu(m_state, m_actions)); break; case Menu::Phone: - m_state->show_events.set(true); // FIXME menu.reset(new PhoneMenu(m_state, m_actions)); break; case Menu::PhoneGreeter: - m_state->show_events.set(false); // FIXME menu.reset(new PhoneGreeterMenu(m_state, m_actions)); break; diff --git a/tests/test-menus.cc b/tests/test-menus.cc index 88e4706..0f86b0d 100644 --- a/tests/test-menus.cc +++ b/tests/test-menus.cc @@ -170,7 +170,7 @@ protected: auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); // there shouldn't be any menuitems when "show events" is false - m_state->show_events.set(false); + m_state->settings->show_events.set(false); wait_msec(); auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); EXPECT_EQ(0, g_menu_model_get_n_items(section)); @@ -179,7 +179,7 @@ protected: // when "show_events" is true, // there should be an "add event" button even if there aren't any appointments std::vector appointments; - m_state->show_events.set(true); + m_state->settings->show_events.set(true); m_state->planner->upcoming.set(appointments); wait_msec(); section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); -- cgit v1.2.3 From 4cc19729c540ffba163d5c9a53b9352fe61fe8af Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 16 Jan 2014 17:28:37 -0600 Subject: in utils.c, make a version of get_timezone_name() that doesn't require a GSettings argument. Update utils tests. --- include/datetime/utils.h | 5 ++- src/utils.cpp | 89 ++++++++++++++++++++++++++++-------------------- tests/state-fixture.h | 1 + tests/test-utils.cc | 79 +++++++++++++----------------------------- 4 files changed, 82 insertions(+), 92 deletions(-) (limited to 'tests') diff --git a/include/datetime/utils.h b/include/datetime/utils.h index fbc80d7..10e881f 100644 --- a/include/datetime/utils.h +++ b/include/datetime/utils.h @@ -34,9 +34,12 @@ void split_settings_location (const char * location, char ** zone, char ** name); -gchar * get_current_zone_name (const char * location, +gchar * get_timezone_name (const char * timezone, GSettings * settings); +gchar * get_beautified_timezone_name (const char * timezone, + const char * saved_location); + gchar * generate_full_format_string_at_time (GDateTime * now, GDateTime * time); diff --git a/src/utils.cpp b/src/utils.cpp index acd9796..bef8c2e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -25,10 +25,9 @@ with this program. If not, see . #include #include #include -#include +#include -#include -#include +#include #include #include @@ -53,16 +52,15 @@ void split_settings_location(const gchar* location, gchar** zone, gchar** name) { auto location_dup = g_strdup(location); - g_strstrip(location_dup); + if(location_dup != nullptr) + g_strstrip(location_dup); gchar* first; - if((first = strchr(location_dup, ' '))) + if(location_dup && (first = strchr(location_dup, ' '))) *first = '\0'; if(zone) - { *zone = location_dup; - } if(name != nullptr) { @@ -72,7 +70,7 @@ split_settings_location(const gchar* location, gchar** zone, gchar** name) { *name = g_strdup(after); } - else // make the name from zone + else if (location_dup) // make the name from zone { gchar * chr = strrchr(location_dup, '/'); after = g_strdup(chr ? chr + 1 : location_dup); @@ -84,54 +82,73 @@ split_settings_location(const gchar* location, gchar** zone, gchar** name) *name = after; } + else + { + *name = nullptr; + } } } +/** + * Our Locations come from two places: (1) direct user input and (2) ones + * guessed by the system, such as from geoclue or timedate1. + * + * Since the latter only have a timezone (eg, "America/Chicago") and the + * former have a descriptive name provided by the end user (eg, + * "America/Chicago Oklahoma City"), this function tries to make a + * more human-readable name by using the user-provided name if the guessed + * timezone matches the last one the user manually clicked on. + * + * In the example above, this allows the menuitem for the system-guessed + * timezone ("America/Chicago") to read "Oklahoma City" after the user clicks + * on the "Oklahoma City" menuitem. + */ gchar* -get_current_zone_name(const gchar* location, GSettings* settings) +get_beautified_timezone_name(const char* timezone, const char* saved_location) { - gchar* new_zone; - gchar* new_name; - split_settings_location(location, &new_zone, &new_name); - - auto tz_name = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); - gchar* old_zone; - gchar* old_name; - split_settings_location(tz_name, &old_zone, &old_name); - g_free(tz_name); + gchar* zone; + gchar* name; + split_settings_location(timezone, &zone, &name); - /* new_name is always just a sanitized version of a timezone. - old_name is potentially a saved "pretty" version of a timezone name from - geonames. So we prefer to use it if available and the zones match. */ + gchar* saved_zone; + gchar* saved_name; + split_settings_location(saved_location, &saved_zone, &saved_name); gchar* rv; - if (g_strcmp0(old_zone, new_zone) == 0) + if (g_strcmp0(zone, saved_zone) == 0) { - rv = old_name; - old_name = nullptr; + rv = saved_name; + saved_name = nullptr; } else { - rv = new_name; - new_name = nullptr; + rv = name; + name = nullptr; } - g_free(new_zone); - g_free(old_zone); - g_free(new_name); - g_free(old_name); + g_free(zone); + g_free(name); + g_free(saved_zone); + g_free(saved_name); return rv; } -gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) +gchar* +get_timezone_name(const gchar* timezone, GSettings* settings) { - using unity::indicator::datetime::Clock; - using unity::indicator::datetime::DateTime; - using unity::indicator::datetime::MockClock; - using unity::indicator::datetime::DesktopFormatter; + auto saved_location = g_settings_get_string(settings, SETTINGS_TIMEZONE_NAME_S); + auto rv = get_beautified_timezone_name(timezone, saved_location); + g_free(saved_location); + return rv; +} +using namespace unity::indicator::datetime; + +gchar* generate_full_format_string_at_time(GDateTime* now, GDateTime* then) +{ std::shared_ptr clock(new MockClock(DateTime(now))); - DesktopFormatter formatter(clock); + std::shared_ptr settings(new LiveSettings); + DesktopFormatter formatter(clock, settings); return g_strdup(formatter.getRelativeFormat(then).c_str()); } diff --git a/tests/state-fixture.h b/tests/state-fixture.h index 0286ea9..3c6ecd5 100644 --- a/tests/state-fixture.h +++ b/tests/state-fixture.h @@ -50,6 +50,7 @@ protected: const DateTime now = DateTime::NowLocal(); m_clock.reset(new MockClock(now)); m_state.reset(new State); + m_state->settings.reset(new Settings); m_state->timezones.reset(new Timezones); m_state->clock = std::dynamic_pointer_cast(m_clock); m_state->planner.reset(new MockPlanner); diff --git a/tests/test-utils.cc b/tests/test-utils.cc index 8246396..2fe6a2e 100644 --- a/tests/test-utils.cc +++ b/tests/test-utils.cc @@ -17,22 +17,16 @@ * Charles Kerr */ -#include - -#include - -#include "utils.h" +#include -/*** -**** -***/ +#include TEST(UtilsTest, SplitSettingsLocation) { struct { - const char * location; - const char * expected_zone; - const char * expected_name; + const char* location; + const char* expected_zone; + const char* expected_name; } test_cases[] = { { "America/Chicago Chicago", "America/Chicago", "Chicago" }, { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" }, @@ -43,63 +37,38 @@ TEST(UtilsTest, SplitSettingsLocation) { "UTC UTC", "UTC", "UTC" } }; - for(guint i=0, n=G_N_ELEMENTS(test_cases); i Date: Thu, 16 Jan 2014 21:23:57 -0600 Subject: get timezone, clock tests running again with Settings & State --- include/datetime/actions-live.h | 7 +- include/datetime/actions.h | 2 +- src/actions-live.cpp | 9 +-- src/actions.cpp | 6 +- tests/geoclue-fixture.h | 48 +++++++----- tests/test-clock.cc | 168 +++++++++------------------------------- tests/test-dbus-fixture.h | 14 ++-- tests/test-timezones.cc | 21 ++--- 8 files changed, 91 insertions(+), 184 deletions(-) (limited to 'tests') diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 2059a4d..3d04660 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -36,15 +36,14 @@ namespace datetime { class LiveActions: public Actions { public: - LiveActions(std::shared_ptr& state): Actions(state) {} + LiveActions(const std::shared_ptr& state): Actions(state) {} ~LiveActions() =default; void open_desktop_settings(); void open_phone_settings(); - void open_phone_clock(); - void open_phone_planner(); + void open_phone_clock_app(); + void open_planner(); void open_planner_at(const DateTime&); - void open_calendar_at(const DateTime&); void open_appointment(const std::string& uid); void set_location(const std::string& zone, const std::string& name); void set_calendar_date(const DateTime&); diff --git a/include/datetime/actions.h b/include/datetime/actions.h index ed45c3c..a4c6017 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -51,7 +51,7 @@ public: std::shared_ptr state() { return m_state; } protected: - Actions(std::shared_ptr& state); + Actions(const std::shared_ptr& state); virtual ~Actions(); private: diff --git a/src/actions-live.cpp b/src/actions-live.cpp index 08e1466..c699aff 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -39,12 +39,12 @@ void LiveActions::open_phone_settings() g_message("%s", G_STRFUNC); } -void LiveActions::open_phone_clock() +void LiveActions::open_phone_clock_app() { g_message("%s", G_STRFUNC); } -void LiveActions::open_phone_planner() +void LiveActions::open_planner() { g_message("%s", G_STRFUNC); } @@ -54,11 +54,6 @@ void LiveActions::open_planner_at(const DateTime&) g_message("%s", G_STRFUNC); } -void LiveActions::open_calendar_at(const DateTime&) -{ - g_message("%s", G_STRFUNC); -} - void LiveActions::open_appointment(const std::string& uid) { g_message("%s - %s", G_STRFUNC, uid.c_str()); diff --git a/src/actions.cpp b/src/actions.cpp index 52ee1eb..0df7d53 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -130,7 +130,7 @@ GVariant* create_default_header_state() return g_variant_builder_end(&b); } -GVariant* create_calendar_state(std::shared_ptr& state) +GVariant* create_calendar_state(const std::shared_ptr& state) { gboolean days[32] = { 0 }; for(const auto& appt : state->planner->thisMonth.get()) @@ -138,7 +138,7 @@ GVariant* create_calendar_state(std::shared_ptr& state) GVariantBuilder day_builder; g_variant_builder_init(&day_builder, G_VARIANT_TYPE("ai")); - for (int i=0; i& state) **** ***/ -Actions::Actions(std::shared_ptr& state): +Actions::Actions(const std::shared_ptr& state): m_state(state), m_actions(g_simple_action_group_new()) { diff --git a/tests/geoclue-fixture.h b/tests/geoclue-fixture.h index 890204a..7e29018 100644 --- a/tests/geoclue-fixture.h +++ b/tests/geoclue-fixture.h @@ -33,8 +33,10 @@ class GeoclueFixture : public GlibFixture DbusTestService * service = nullptr; DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_master = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; + DbusTestDbusMockObject * obj_geo = nullptr; + DbusTestDbusMockObject * obj_geo_m = nullptr; + DbusTestDbusMockObject * obj_geo_mc = nullptr; + DbusTestDbusMockObject * obj_geo_addr = nullptr; const std::string timezone_1 = "America/Denver"; void SetUp () @@ -42,25 +44,31 @@ class GeoclueFixture : public GlibFixture super::SetUp(); GError * error = nullptr; - const gchar * const client_path = "/org/freedesktop/Geoclue/Master/client0"; + const auto master_path = "/org/freedesktop/Geoclue/Master"; + const auto client_path = "/org/freedesktop/Geoclue/Master/client0"; GString * gstr = g_string_new (nullptr); service = dbus_test_service_new (nullptr); mock = dbus_test_dbus_mock_new ("org.freedesktop.Geoclue.Master"); - obj_master = dbus_test_dbus_mock_get_object (mock, - "/org/freedesktop/Geoclue/Master", - "org.freedesktop.Geoclue.Master", - nullptr); + auto interface = "org.freedesktop.Geoclue.Master"; + obj_geo_m = dbus_test_dbus_mock_get_object (mock, master_path, interface, nullptr); g_string_printf (gstr, "ret = '%s'", client_path); - dbus_test_dbus_mock_object_add_method (mock, obj_master, nullptr, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_m, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error); - obj_client = dbus_test_dbus_mock_get_object (mock, client_path, "org.freedesktop.Geoclue.MasterClient", nullptr); - dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error); - dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "AddressStart", nullptr, nullptr, "", &error); - dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue", "AddReference", nullptr, nullptr, "", &error); + interface = "org.freedesktop.Geoclue.MasterClient"; + obj_geo_mc = dbus_test_dbus_mock_get_object (mock, client_path, interface, nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_mc, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_mc, "AddressStart", nullptr, nullptr, "", &error); + + interface = "org.freedesktop.Geoclue"; + obj_geo = dbus_test_dbus_mock_get_object (mock, client_path, interface, nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_geo, "AddReference", nullptr, nullptr, "", &error); g_string_printf (gstr, "ret = (1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", timezone_1.c_str()); - dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error); + + interface = "org.freedesktop.Geoclue.Address"; + obj_geo_addr = dbus_test_dbus_mock_get_object (mock, client_path, interface, nullptr); + dbus_test_dbus_mock_object_add_method (mock, obj_geo_addr, "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error); dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); dbus_test_service_start_tasks(service); @@ -101,11 +109,11 @@ private: struct EmitAddressChangedData { DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; + DbusTestDbusMockObject * obj_geo_addr = nullptr; std::string timezone; - EmitAddressChangedData(DbusTestDbusMock * mock_, - DbusTestDbusMockObject * obj_client_, - const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + EmitAddressChangedData(DbusTestDbusMock* mock_, + DbusTestDbusMockObject* obj_geo_addr_, + const std::string& timezone_): mock(mock_), obj_geo_addr(obj_geo_addr_), timezone(timezone_) {} }; static gboolean emit_address_changed_idle (gpointer gdata) @@ -114,8 +122,8 @@ private: auto fmt = g_strdup_printf ("(1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", data->timezone.c_str()); GError * error = nullptr; - dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, - "org.freedesktop.Geoclue.Address", + dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_geo_addr, + //"org.freedesktop.Geoclue.Address", "AddressChanged", G_VARIANT_TYPE("(ia{ss}(idd))"), g_variant_new_parsed (fmt), @@ -135,7 +143,7 @@ public: void setGeoclueTimezoneOnIdle (const std::string& newZone) { - g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, newZone.c_str())); + g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_geo_addr, newZone.c_str())); } }; diff --git a/tests/test-clock.cc b/tests/test-clock.cc index 7d3a35e..142ccad 100644 --- a/tests/test-clock.cc +++ b/tests/test-clock.cc @@ -20,95 +20,56 @@ #include #include -#include "glib-fixture.h" +#include "test-dbus-fixture.h" /*** **** ***/ -using unity::indicator::datetime::Clock; -using unity::indicator::datetime::LiveClock; -using unity::indicator::datetime::Timezones; +using namespace unity::indicator::datetime; -class ClockFixture: public GlibFixture +class ClockFixture: public TestDBusFixture { private: - - typedef GlibFixture super; - - static void - on_bus_opened(GObject* /*object*/, GAsyncResult* res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - self->system_bus = g_bus_get_finish(res, &err); - g_assert_no_error(err); - - g_main_loop_quit(self->loop); - } - - static void - on_bus_closed(GObject* /*object*/, GAsyncResult* res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - g_dbus_connection_close_finish(self->system_bus, res, &err); - g_assert_no_error(err); - - g_main_loop_quit(self->loop); - } - - protected: - - GTestDBus * test_dbus; - GDBusConnection * system_bus; - - virtual void SetUp() - { - super::SetUp(); - - // pull up a test dbus - test_dbus = g_test_dbus_new(G_TEST_DBUS_NONE); - g_test_dbus_up(test_dbus); - const char * address = g_test_dbus_get_bus_address(test_dbus); - g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE); - g_debug("test_dbus's address is %s", address); - - // wait for the GDBusConnection before returning - g_bus_get(G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run(loop); - } - - virtual void TearDown() - { - // close the system bus - g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); - g_main_loop_run(loop); - g_clear_object(&system_bus); - - // tear down the test dbus - g_test_dbus_down(test_dbus); - g_clear_object(&test_dbus); - - super::TearDown(); - } + typedef TestDBusFixture super; public: - void emitPrepareForSleep() { g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), - NULL, - "/org/freedesktop/login1", // object path - "org.freedesktop.login1.Manager", // interface - "PrepareForSleep", // signal name - g_variant_new("(b)", FALSE), - NULL); + NULL, + "/org/freedesktop/login1", // object path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + g_variant_new("(b)", FALSE), + NULL); } }; +/** + * Confirm that normal time passing doesn't trigger a skew event. + * that idling changing the clock's time triggers a skew event + */ +TEST_F(ClockFixture, IdleDoesNotTriggerSkew) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 3; + clock.skewTestIntervalSec.set(intervalSec); + wait_msec(intervalSec * 2.5 * 1000); + EXPECT_FALSE(skewed); +} + /*** **** ***/ @@ -120,37 +81,6 @@ TEST_F(ClockFixture, HelloFixture) std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); LiveClock clock(zones); - -#if 0 - GTimeZone * tz_nyc = g_time_zone_new(file_timezone.c_str()); - GDateTime * now_nyc = g_date_time_new_now(tz_nyc); - GDateTime * now = clock.localtime(); - EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now)); - EXPECT_LE(abs(g_date_time_difference(now_nyc,now)), G_USEC_PER_SEC); - g_date_time_unref(now); - g_date_time_unref(now_nyc); - g_time_zone_unref(tz_nyc); - - /// change the timezones! - clock.skewDetected.connect([this](){ - g_main_loop_quit(loop); - }); - file_timezone = "America/Los_Angeles"; - g_idle_add([](gpointer str){ - set_file(static_cast(str)); - return G_SOURCE_REMOVE; - }, const_cast(file_timezone.c_str())); - g_main_loop_run(loop); - - GTimeZone * tz_la = g_time_zone_new(file_timezone.c_str()); - GDateTime * now_la = g_date_time_new_now(tz_la); - now = clock.localtime(); - EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now)); - EXPECT_LE(abs(g_date_time_difference(now_la,now)), G_USEC_PER_SEC); - g_date_time_unref(now); - g_date_time_unref(now_la); - g_time_zone_unref(tz_la); -#endif } @@ -159,10 +89,6 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); LiveClock clock(zones); - //std::string file_timezone = "America/New_York"; - //set_file(file_timezone); - //std::shared_ptr detector(new TimezoneDetector(TIMEZONE_FILE)); - //LiveClock clock(detector); auto tz_nyc = g_time_zone_new("America/New_York"); auto now_nyc = g_date_time_new_now(tz_nyc); @@ -213,30 +139,6 @@ TEST_F(ClockFixture, SleepTriggersSkew) return G_SOURCE_REMOVE; }, this); - wait_msec(1000); + g_main_loop_run(loop); EXPECT_TRUE(skewed); } - -/** - * Confirm that normal time passing doesn't trigger a skew event. - * that idling changing the clock's time triggers a skew event - */ -TEST_F(ClockFixture, IdleDoesNotTriggerSkew) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up - - bool skewed = false; - clock.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 4; - clock.skewTestIntervalSec.set(intervalSec); - wait_msec(intervalSec * 2.5 * 1000); - EXPECT_FALSE(skewed); -} diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h index fc7ab5a..db06be2 100644 --- a/tests/test-dbus-fixture.h +++ b/tests/test-dbus-fixture.h @@ -86,15 +86,17 @@ class TestDBusFixture: public GlibFixture virtual void TearDown () { + wait_msec(); + // close the system bus - g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this); - g_main_loop_run (loop); - g_clear_object (&system_bus); + g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&system_bus); // tear down the test dbus - g_test_dbus_down (test_dbus); - g_clear_object (&test_dbus); + g_test_dbus_down(test_dbus); + g_clear_object(&test_dbus); - super::TearDown (); + super::TearDown(); } }; diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc index d3c8e3a..3f02761 100644 --- a/tests/test-timezones.cc +++ b/tests/test-timezones.cc @@ -19,12 +19,15 @@ #include "geoclue-fixture.h" +#include #include +#include // std::shared_ptr + #include // fopen() #include // sync() -using unity::indicator::datetime::LiveTimezones; +using namespace unity::indicator::datetime; typedef GeoclueFixture TimezonesFixture; @@ -49,7 +52,8 @@ TEST_F(TimezonesFixture, ManagerTest) std::string timezone_geo = "America/Denver"; set_file(timezone_file); - LiveTimezones z(TIMEZONE_FILE); + std::shared_ptr settings(new Settings); + LiveTimezones z(settings, TIMEZONE_FILE); wait_msec(500); // wait for the bus to get set up EXPECT_EQ(timezone_file, z.timezone.get()); auto zones = z.timezones.get(); @@ -71,13 +75,13 @@ TEST_F(TimezonesFixture, ManagerTest) g_main_loop_quit(loop); }); - g_idle_add([](gpointer gz) { - auto az = static_cast(gz); - g_message("geolocation was %d", (int)az->geolocationEnabled.get()); + g_idle_add([](gpointer s_in) { + auto s = static_cast(s_in); + g_message("geolocation was %d", (int)s->show_detected_location.get()); g_message("turning geolocation on"); - az->geolocationEnabled.set(true); + s->show_detected_location.set(true); return G_SOURCE_REMOVE; - }, &z); + }, settings.get()); // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property g_main_loop_run(loop); @@ -115,9 +119,6 @@ TEST_F(TimezonesFixture, ManagerTest) EXPECT_EQ(2, zones.size()); EXPECT_EQ(1, zones.count(timezone_file)); EXPECT_EQ(1, zones.count(timezone_geo)); - - - } -- cgit v1.2.3 From 8910c3c25d9c3c77aaa987fdc366cd77f63d376c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 21 Jan 2014 23:27:45 -0600 Subject: Extract the MockState logic from state-fixture.h so that it can be reused for testing LiveActions --- tests/state-fixture.h | 30 +++++++----------------------- tests/state-mock.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 tests/state-mock.h (limited to 'tests') diff --git a/tests/state-fixture.h b/tests/state-fixture.h index 3c6ecd5..7d8358e 100644 --- a/tests/state-fixture.h +++ b/tests/state-fixture.h @@ -18,16 +18,9 @@ */ #include "glib-fixture.h" -#include "actions-mock.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "actions-mock.h" +#include "state-mock.h" using namespace unity::indicator::datetime; @@ -37,7 +30,7 @@ private: typedef GlibFixture super; protected: - std::shared_ptr m_clock; + std::shared_ptr m_mock_state; std::shared_ptr m_state; std::shared_ptr m_mock_actions; std::shared_ptr m_actions; @@ -46,19 +39,9 @@ protected: { super::SetUp(); - // first, build a mock backend state - const DateTime now = DateTime::NowLocal(); - m_clock.reset(new MockClock(now)); - m_state.reset(new State); - m_state->settings.reset(new Settings); - m_state->timezones.reset(new Timezones); - m_state->clock = std::dynamic_pointer_cast(m_clock); - m_state->planner.reset(new MockPlanner); - m_state->planner->time = now; - m_state->locations.reset(new Locations); - m_state->calendar_day = now; + m_mock_state.reset(new MockState); + m_state = std::dynamic_pointer_cast(m_mock_state); - // build the actions on top of the state m_mock_actions.reset(new MockActions(m_state)); m_actions = std::dynamic_pointer_cast(m_mock_actions); } @@ -67,8 +50,9 @@ protected: { m_actions.reset(); m_mock_actions.reset(); + m_state.reset(); - m_clock.reset(); + m_mock_state.reset(); super::TearDown(); } diff --git a/tests/state-mock.h b/tests/state-mock.h new file mode 100644 index 0000000..ae1217b --- /dev/null +++ b/tests/state-mock.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace unity::indicator::datetime; + +class MockState: public State +{ +public: + std::shared_ptr mock_clock; + + MockState() + { + const DateTime now = DateTime::NowLocal(); + mock_clock.reset(new MockClock(now)); + settings.reset(new Settings); + timezones.reset(new Timezones); + clock = std::dynamic_pointer_cast(mock_clock); + planner.reset(new MockPlanner); + planner->time = now; + locations.reset(new Locations); + calendar_day = now; + } +}; + -- cgit v1.2.3 From 74f8897902c99180e721d616614a9962c819d90b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 21 Jan 2014 23:29:19 -0600 Subject: add LiveActions implementation and unit tests --- include/datetime/actions-live.h | 6 +- src/actions-live.cpp | 153 ++++++++++++++++++++++++++++++++++++---- tests/CMakeLists.txt | 9 ++- tests/test-menus.cc | 2 +- 4 files changed, 154 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/include/datetime/actions-live.h b/include/datetime/actions-live.h index 3d04660..1947e6e 100644 --- a/include/datetime/actions-live.h +++ b/include/datetime/actions-live.h @@ -36,7 +36,7 @@ namespace datetime { class LiveActions: public Actions { public: - LiveActions(const std::shared_ptr& state): Actions(state) {} + LiveActions(const std::shared_ptr& state_in): Actions(state_in) {} ~LiveActions() =default; void open_desktop_settings(); @@ -47,6 +47,10 @@ public: void open_appointment(const std::string& uid); void set_location(const std::string& zone, const std::string& name); void set_calendar_date(const DateTime&); + +protected: + virtual void execute_command(const std::string& command); + virtual void dispatch_url(const std::string& url); }; } // namespace datetime diff --git a/src/actions-live.cpp b/src/actions-live.cpp index c699aff..e4f5e9f 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -19,6 +19,8 @@ #include +#include + #include namespace unity { @@ -29,46 +31,171 @@ namespace datetime { **** ***/ +void LiveActions::execute_command(const std::string& cmdstr) +{ + const auto cmd = cmdstr.c_str(); + g_debug("Issuing command '%s'", cmd); + + GError* error = nullptr; + if (!g_spawn_command_line_async(cmd, &error)) + { + g_warning("Unable to start \"%s\": %s", cmd, error->message); + g_error_free(error); + } +} + +void LiveActions::dispatch_url(const std::string& url) +{ + url_dispatch_send(url.c_str(), nullptr, nullptr); +} + +/*** +**** +***/ + void LiveActions::open_desktop_settings() { - g_message ("%s", G_STRFUNC); +#ifdef HAVE_CCPANEL + execute_command("gnome-control-center indicator-datetime"); +#else + execute_command("gnome-control-center datetime"); +#endif +} + +void LiveActions::open_planner() +{ + execute_command("evolution -c calendar"); } void LiveActions::open_phone_settings() { - g_message("%s", G_STRFUNC); + dispatch_url("settings:///system/time-date"); } void LiveActions::open_phone_clock_app() { - g_message("%s", G_STRFUNC); + dispatch_url("appid://com.ubuntu.clock/clock/current-user-version"); } -void LiveActions::open_planner() +void LiveActions::open_planner_at(const DateTime& dt) { - g_message("%s", G_STRFUNC); + auto cmd = dt.format("evolution \"calendar:///?startdate=%Y%m%d\""); + execute_command(cmd.c_str()); } -void LiveActions::open_planner_at(const DateTime&) +void LiveActions::open_appointment(const std::string& uid) { - g_message("%s", G_STRFUNC); + for(const auto& appt : state()->planner->upcoming.get()) + { + if(appt.uid != uid) + continue; + + if (!appt.url.empty()) + dispatch_url(appt.url); + break; + } } -void LiveActions::open_appointment(const std::string& uid) +void LiveActions::set_calendar_date(const DateTime& dt) { - g_message("%s - %s", G_STRFUNC, uid.c_str()); + state()->calendar_day.set(dt); } -void LiveActions::set_location(const std::string& zone, const std::string& name) +/*** +**** +***/ + +namespace +{ + +struct setlocation_data { - g_message("%s - %s %s", G_STRFUNC, zone.c_str(), name.c_str()); + std::string tzid; + std::string name; + std::shared_ptr settings; +}; + +static void +on_datetime1_set_timezone_response(GObject * object, + GAsyncResult * res, + gpointer gdata) +{ + GError* err = nullptr; + auto response = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), res, &err); + auto data = static_cast(gdata); + + if (err != nullptr) + { + if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Could not set new timezone: %s", err->message); + + g_error_free(err); + } + else + { + data->settings->timezone_name.set(data->tzid + " " + data->name); + g_variant_unref(response); + } + + delete data; } -void LiveActions::set_calendar_date(const DateTime&) +static void +on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) { - g_message("%s", G_STRFUNC); + auto data = static_cast(gdata); + + GError * err = nullptr; + auto proxy = g_dbus_proxy_new_for_bus_finish(res, &err); + if (err != NULL) + { + if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("Could not grab DBus proxy for timedated: %s", err->message); + + g_error_free(err); + + delete data; + } + else + { + g_dbus_proxy_call(proxy, + "SetTimezone", + g_variant_new ("(sb)", data->tzid.c_str(), TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + on_datetime1_set_timezone_response, + data); + + g_object_unref (proxy); + } } +} // unnamed namespace + + +void LiveActions::set_location(const std::string& tzid, const std::string& name) +{ + g_return_if_fail(!tzid.empty()); + g_return_if_fail(!name.empty()); + + auto data = new struct setlocation_data; + data->tzid = tzid; + data->name = name; + data->settings = state()->settings; + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + nullptr, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + nullptr, + on_datetime1_proxy_ready, + data); +} /*** **** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a908801..9bc1d75 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -84,6 +84,13 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-live-actions +set (TEST_NAME test-live-actions) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + # test-menus set (TEST_NAME test-menus) add_executable (${TEST_NAME} ${TEST_NAME}.cc) @@ -98,7 +105,7 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) -# test-utils +# test-settings set (TEST_NAME test-settings) add_executable (${TEST_NAME} ${TEST_NAME}.cc) add_test (${TEST_NAME} ${TEST_NAME}) diff --git a/tests/test-menus.cc b/tests/test-menus.cc index 0f86b0d..95ac5c2 100644 --- a/tests/test-menus.cc +++ b/tests/test-menus.cc @@ -148,7 +148,7 @@ protected: auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); auto tomorrow = DateTime(gdt_tomorrow); g_date_time_unref(gdt_tomorrow); - m_clock->set_localtime(tomorrow); + m_mock_state->mock_clock->set_localtime(tomorrow); wait_msec(); section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); -- cgit v1.2.3 From 931e448b916453139e2c043161088851a1b47b42 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 00:33:22 -0600 Subject: remove unnecessary #includes --- tests/state-mock.h | 3 --- tests/test-menus.cc | 4 ---- 2 files changed, 7 deletions(-) (limited to 'tests') diff --git a/tests/state-mock.h b/tests/state-mock.h index ae1217b..2104aa0 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -18,11 +18,8 @@ */ #include -#include #include -#include #include -#include #include #include diff --git a/tests/test-menus.cc b/tests/test-menus.cc index 95ac5c2..fe1e86e 100644 --- a/tests/test-menus.cc +++ b/tests/test-menus.cc @@ -22,13 +22,9 @@ #include "state-fixture.h" #include -#include #include #include -#include -#include #include -#include #include -- cgit v1.2.3 From 6d515551ef24c92fa9a56e9533c4175e2b4f7bbd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 01:23:38 -0600 Subject: add unit tests for the menu/action exporter --- tests/CMakeLists.txt | 7 +++ tests/test-exporter.cc | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/test-exporter.cc (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9bc1d75..ff4a8a5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -112,6 +112,13 @@ add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +# test-exporter +set (TEST_NAME test-exporter) +add_executable (${TEST_NAME} ${TEST_NAME}.cc) +add_test (${TEST_NAME} ${TEST_NAME}) +add_dependencies (${TEST_NAME} libindicatordatetimeservice) +target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) + # test-desktop #set (TEST_NAME test-desktop) #add_executable (${TEST_NAME} ${TEST_NAME}.cc) diff --git a/tests/test-exporter.cc b/tests/test-exporter.cc new file mode 100644 index 0000000..ea62cd3 --- /dev/null +++ b/tests/test-exporter.cc @@ -0,0 +1,128 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include "actions-mock.h" +#include "state-mock.h" +#include "glib-fixture.h" + +#include +#include +#include + +#include +#include + +using namespace unity::indicator::datetime; + +class ExporterFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static void on_bus_closed(GObject * object, + GAsyncResult * res, + gpointer gself) + { + auto self = static_cast(gself); + GError* err = nullptr; + g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); + g_assert_no_error(err); + g_main_loop_quit(self->loop); + } + +protected: + + GTestDBus* bus = nullptr; + + void SetUp() + { + super::SetUp(); + + // bring up the test bus + bus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(bus); + const auto address = g_test_dbus_get_bus_address(bus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); + } + + void TearDown() + { + GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_close(connection, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&connection); + g_test_dbus_down(bus); + g_clear_object(&bus); + + super::TearDown(); + } +}; + +TEST_F(ExporterFixture, HelloWorld) +{ + // confirms that the Test DBus SetUp() and TearDown() works +} + +TEST_F(ExporterFixture, Publish) +{ + std::shared_ptr state(new MockState); + std::shared_ptr actions(new MockActions(state)); + std::vector> menus; + + Exporter exporter; + exporter.publish(actions, menus); + wait_msec(); + + auto connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + auto exported = g_dbus_action_group_get (connection, BUS_NAME, BUS_PATH); + auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + + // wait for the exported ActionGroup to be populated + if (g_strv_length(names_strv) == 0) + { + g_strfreev(names_strv); + wait_for_signal(exported, "action-added"); + names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + } + + // convert it to a std::set for easy prodding + std::set names; + for(int i=0; names_strv && names_strv[i]; i++) + names.insert(names_strv[i]); + + // confirm the actions that we expect + EXPECT_EQ(1, names.count("activate-appointment")); + EXPECT_EQ(1, names.count("activate-desktop-settings")); + EXPECT_EQ(1, names.count("activate-phone-clock-app")); + EXPECT_EQ(1, names.count("activate-phone-settings")); + EXPECT_EQ(1, names.count("activate-planner")); + EXPECT_EQ(1, names.count("calendar")); + EXPECT_EQ(1, names.count("desktop-greeter-header")); + EXPECT_EQ(1, names.count("desktop-header")); + EXPECT_EQ(1, names.count("phone-greeter-header")); + EXPECT_EQ(1, names.count("phone-header")); + EXPECT_EQ(1, names.count("set-location")); + + // cleanup + g_strfreev(names_strv); + g_clear_object(&exported); + g_clear_object(&connection); +} -- cgit v1.2.3 From f1027f7cd2f846211a46b072965331fb0eb69fec Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 01:34:04 -0600 Subject: fold repetitious unit test rules into CMake functions --- tests/CMakeLists.txt | 122 ++++++++++++--------------------------------------- 1 file changed, 29 insertions(+), 93 deletions(-) (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ff4a8a5..c8dd6aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,97 +32,33 @@ include_directories (${DBUSTEST_INCLUDE_DIRS}) add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") -# test-timezone-file -set (TEST_NAME test-timezone-file) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-timezone-geoclue -set (TEST_NAME test-timezone-geoclue) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) - -# test-timezones -set (TEST_NAME test-timezones) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) - -# test-clock -set (TEST_NAME test-clock) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-formatter -set (TEST_NAME test-formatter) -add_executable (${TEST_NAME} test-formatter.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-planner -set (TEST_NAME test-planner) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-locations -set (TEST_NAME test-locations) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-actions -set (TEST_NAME test-actions) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-live-actions -set (TEST_NAME test-live-actions) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-menus -set (TEST_NAME test-menus) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-utils -set (TEST_NAME test-utils) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-settings -set (TEST_NAME test-settings) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-exporter -set (TEST_NAME test-exporter) -add_executable (${TEST_NAME} ${TEST_NAME}.cc) -add_test (${TEST_NAME} ${TEST_NAME}) -add_dependencies (${TEST_NAME} libindicatordatetimeservice) -target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) - -# test-desktop -#set (TEST_NAME test-desktop) -#add_executable (${TEST_NAME} ${TEST_NAME}.cc) -#add_test (${TEST_NAME} ${TEST_NAME}) -#add_dependencies (${TEST_NAME} libindicatordatetimeservice) -#target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +function(add_test_by_name name) + set (TEST_NAME ${name}) + add_executable (${TEST_NAME} ${TEST_NAME}.cc) + add_test (${TEST_NAME} ${TEST_NAME}) + add_dependencies (${TEST_NAME} libindicatordatetimeservice) + target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) +endfunction() +add_test_by_name(test-actions) +add_test_by_name(test-clock) +add_test_by_name(test-exporter) +add_test_by_name(test-formatter) +add_test_by_name(test-live-actions) +add_test_by_name(test-locations) +add_test_by_name(test-menus) +add_test_by_name(test-planner) +add_test_by_name(test-settings) +add_test_by_name(test-timezone-file) +add_test_by_name(test-utils) + + +function(add_dbusmock_test_by_name name) + set (TEST_NAME ${name}) + add_executable (${TEST_NAME} ${TEST_NAME}.cc) + add_test (${TEST_NAME} ${TEST_NAME}) + add_dependencies (${TEST_NAME} libindicatordatetimeservice) + target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) +endfunction() +add_dbusmock_test_by_name(test-timezone-geoclue) +add_dbusmock_test_by_name(test-timezones) -- cgit v1.2.3 From dd41db685c518acab1c8e3676f4292f66e4d0476 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:06:01 -0600 Subject: copyediting: rename the test/ directory's .cc files to .cpp for consistency with src/ --- tests/CMakeLists.txt | 4 +- tests/test-actions.cc | 173 ----------------- tests/test-actions.cpp | 173 +++++++++++++++++ tests/test-clock.cc | 144 -------------- tests/test-clock.cpp | 144 ++++++++++++++ tests/test-exporter.cc | 128 ------------- tests/test-exporter.cpp | 128 +++++++++++++ tests/test-formatter.cc | 256 ------------------------- tests/test-formatter.cpp | 256 +++++++++++++++++++++++++ tests/test-live-actions.cpp | 289 +++++++++++++++++++++++++++++ tests/test-locations.cc | 169 ----------------- tests/test-locations.cpp | 169 +++++++++++++++++ tests/test-menus.cc | 402 ---------------------------------------- tests/test-menus.cpp | 402 ++++++++++++++++++++++++++++++++++++++++ tests/test-planner.cc | 85 --------- tests/test-planner.cpp | 85 +++++++++ tests/test-settings.cc | 197 -------------------- tests/test-settings.cpp | 197 ++++++++++++++++++++ tests/test-timezone-file.cc | 133 ------------- tests/test-timezone-file.cpp | 133 +++++++++++++ tests/test-timezone-geoclue.cc | 97 ---------- tests/test-timezone-geoclue.cpp | 97 ++++++++++ tests/test-timezones.cc | 124 ------------- tests/test-timezones.cpp | 124 +++++++++++++ tests/test-utils.cc | 74 -------- tests/test-utils.cpp | 74 ++++++++ 26 files changed, 2273 insertions(+), 1984 deletions(-) delete mode 100644 tests/test-actions.cc create mode 100644 tests/test-actions.cpp delete mode 100644 tests/test-clock.cc create mode 100644 tests/test-clock.cpp delete mode 100644 tests/test-exporter.cc create mode 100644 tests/test-exporter.cpp delete mode 100644 tests/test-formatter.cc create mode 100644 tests/test-formatter.cpp create mode 100644 tests/test-live-actions.cpp delete mode 100644 tests/test-locations.cc create mode 100644 tests/test-locations.cpp delete mode 100644 tests/test-menus.cc create mode 100644 tests/test-menus.cpp delete mode 100644 tests/test-planner.cc create mode 100644 tests/test-planner.cpp delete mode 100644 tests/test-settings.cc create mode 100644 tests/test-settings.cpp delete mode 100644 tests/test-timezone-file.cc create mode 100644 tests/test-timezone-file.cpp delete mode 100644 tests/test-timezone-geoclue.cc create mode 100644 tests/test-timezone-geoclue.cpp delete mode 100644 tests/test-timezones.cc create mode 100644 tests/test-timezones.cpp delete mode 100644 tests/test-utils.cc create mode 100644 tests/test-utils.cpp (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c8dd6aa..c909e0e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,7 +35,7 @@ add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") function(add_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cc) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) @@ -55,7 +55,7 @@ add_test_by_name(test-utils) function(add_dbusmock_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cc) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) diff --git a/tests/test-actions.cc b/tests/test-actions.cc deleted file mode 100644 index 4329608..0000000 --- a/tests/test-actions.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include - -#include "state-fixture.h" - -using namespace unity::indicator::datetime; - -typedef StateFixture ActionsFixture; - -TEST_F(ActionsFixture, ActionsExist) -{ - EXPECT_TRUE(m_actions != nullptr); - - const char* names[] = { "desktop-header", - "calendar", - "set-location", - "activate-planner", - "activate-appointment", - "activate-phone-settings", - "activate-phone-clock-app", - "activate-desktop-settings" }; - for(const auto& name: names) - { - EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), name)); - } -} - -TEST_F(ActionsFixture, ActivateDesktopSettings) -{ - const auto action_name = "activate-desktop-settings"; - const auto expected_action = MockActions::OpenDesktopSettings; - - auto action_group = m_actions->action_group(); - auto history = m_mock_actions->history(); - EXPECT_EQ(0, history.size()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - g_action_group_activate_action(action_group, action_name, nullptr); - history = m_mock_actions->history(); - EXPECT_EQ(1, history.size()); - EXPECT_EQ(expected_action, history[0]); -} - -TEST_F(ActionsFixture, ActivatePhoneSettings) -{ - const auto action_name = "activate-phone-settings"; - const auto expected_action = MockActions::OpenPhoneSettings; - - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - g_action_group_activate_action(action_group, action_name, nullptr); - auto history = m_mock_actions->history(); - EXPECT_EQ(1, history.size()); - EXPECT_EQ(expected_action, history[0]); -} - -TEST_F(ActionsFixture, ActivatePhoneClockApp) -{ - const auto action_name = "activate-phone-clock-app"; - const auto expected_action = MockActions::OpenPhoneClockApp; - - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - g_action_group_activate_action(action_group, action_name, nullptr); - auto history = m_mock_actions->history(); - EXPECT_EQ(1, history.size()); - EXPECT_EQ(expected_action, history[0]); -} - -TEST_F(ActionsFixture, ActivatePlanner) -{ - const auto action_name = "activate-planner"; - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - const auto expected_action = MockActions::OpenPlanner; - auto v = g_variant_new_int64(0); - g_action_group_activate_action(action_group, action_name, v); - auto history = m_mock_actions->history(); - EXPECT_EQ(1, history.size()); - EXPECT_EQ(expected_action, history[0]); -} - -TEST_F(ActionsFixture, ActivatePlannerAt) -{ - const auto action_name = "activate-planner"; - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - const auto now = DateTime::NowLocal(); - auto v = g_variant_new_int64(now.to_unix()); - g_action_group_activate_action(action_group, action_name, v); - const auto a = MockActions::OpenPlannerAt; - EXPECT_EQ(std::vector({a}), m_mock_actions->history()); - EXPECT_EQ(now.to_unix(), m_mock_actions->date_time().to_unix()); -} - -TEST_F(ActionsFixture, SetLocation) -{ - const auto action_name = "set-location"; - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - auto v = g_variant_new_string("America/Chicago Oklahoma City"); - g_action_group_activate_action(action_group, action_name, v); - const auto expected_action = MockActions::SetLocation; - ASSERT_EQ(1, m_mock_actions->history().size()); - EXPECT_EQ(expected_action, m_mock_actions->history()[0]); - EXPECT_EQ("America/Chicago", m_mock_actions->zone()); - EXPECT_EQ("Oklahoma City", m_mock_actions->name()); -} - -TEST_F(ActionsFixture, SetCalendarDate) -{ - const auto action_name = "calendar"; - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - auto unix = m_state->clock->localtime().to_unix(); - auto v = g_variant_new_int64(unix); - g_action_group_activate_action(action_group, action_name, v); - const auto expected_action = MockActions::SetCalendarDate; - ASSERT_EQ(1, m_mock_actions->history().size()); - EXPECT_EQ(expected_action, m_mock_actions->history()[0]); - EXPECT_EQ(unix, m_mock_actions->date_time().to_unix()); -} - -TEST_F(ActionsFixture, OpenAppointment) -{ - Appointment appt; - appt.uid = "some arbitrary uid"; - appt.url = "http://www.canonical.com/"; - m_state->planner->upcoming.set(std::vector({appt})); - - const auto action_name = "activate-appointment"; - auto action_group = m_actions->action_group(); - EXPECT_TRUE(m_mock_actions->history().empty()); - EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - - auto v = g_variant_new_string(appt.uid.c_str()); - g_action_group_activate_action(action_group, action_name, v); - const auto a = MockActions::OpenAppointment; - ASSERT_EQ(1, m_mock_actions->history().size()); - ASSERT_EQ(a, m_mock_actions->history()[0]); - EXPECT_EQ(appt.url, m_mock_actions->url()); -} - diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp new file mode 100644 index 0000000..4329608 --- /dev/null +++ b/tests/test-actions.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include "state-fixture.h" + +using namespace unity::indicator::datetime; + +typedef StateFixture ActionsFixture; + +TEST_F(ActionsFixture, ActionsExist) +{ + EXPECT_TRUE(m_actions != nullptr); + + const char* names[] = { "desktop-header", + "calendar", + "set-location", + "activate-planner", + "activate-appointment", + "activate-phone-settings", + "activate-phone-clock-app", + "activate-desktop-settings" }; + for(const auto& name: names) + { + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), name)); + } +} + +TEST_F(ActionsFixture, ActivateDesktopSettings) +{ + const auto action_name = "activate-desktop-settings"; + const auto expected_action = MockActions::OpenDesktopSettings; + + auto action_group = m_actions->action_group(); + auto history = m_mock_actions->history(); + EXPECT_EQ(0, history.size()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePhoneSettings) +{ + const auto action_name = "activate-phone-settings"; + const auto expected_action = MockActions::OpenPhoneSettings; + + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePhoneClockApp) +{ + const auto action_name = "activate-phone-clock-app"; + const auto expected_action = MockActions::OpenPhoneClockApp; + + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + g_action_group_activate_action(action_group, action_name, nullptr); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePlanner) +{ + const auto action_name = "activate-planner"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + const auto expected_action = MockActions::OpenPlanner; + auto v = g_variant_new_int64(0); + g_action_group_activate_action(action_group, action_name, v); + auto history = m_mock_actions->history(); + EXPECT_EQ(1, history.size()); + EXPECT_EQ(expected_action, history[0]); +} + +TEST_F(ActionsFixture, ActivatePlannerAt) +{ + const auto action_name = "activate-planner"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + const auto now = DateTime::NowLocal(); + auto v = g_variant_new_int64(now.to_unix()); + g_action_group_activate_action(action_group, action_name, v); + const auto a = MockActions::OpenPlannerAt; + EXPECT_EQ(std::vector({a}), m_mock_actions->history()); + EXPECT_EQ(now.to_unix(), m_mock_actions->date_time().to_unix()); +} + +TEST_F(ActionsFixture, SetLocation) +{ + const auto action_name = "set-location"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto v = g_variant_new_string("America/Chicago Oklahoma City"); + g_action_group_activate_action(action_group, action_name, v); + const auto expected_action = MockActions::SetLocation; + ASSERT_EQ(1, m_mock_actions->history().size()); + EXPECT_EQ(expected_action, m_mock_actions->history()[0]); + EXPECT_EQ("America/Chicago", m_mock_actions->zone()); + EXPECT_EQ("Oklahoma City", m_mock_actions->name()); +} + +TEST_F(ActionsFixture, SetCalendarDate) +{ + const auto action_name = "calendar"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto unix = m_state->clock->localtime().to_unix(); + auto v = g_variant_new_int64(unix); + g_action_group_activate_action(action_group, action_name, v); + const auto expected_action = MockActions::SetCalendarDate; + ASSERT_EQ(1, m_mock_actions->history().size()); + EXPECT_EQ(expected_action, m_mock_actions->history()[0]); + EXPECT_EQ(unix, m_mock_actions->date_time().to_unix()); +} + +TEST_F(ActionsFixture, OpenAppointment) +{ + Appointment appt; + appt.uid = "some arbitrary uid"; + appt.url = "http://www.canonical.com/"; + m_state->planner->upcoming.set(std::vector({appt})); + + const auto action_name = "activate-appointment"; + auto action_group = m_actions->action_group(); + EXPECT_TRUE(m_mock_actions->history().empty()); + EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); + + auto v = g_variant_new_string(appt.uid.c_str()); + g_action_group_activate_action(action_group, action_name, v); + const auto a = MockActions::OpenAppointment; + ASSERT_EQ(1, m_mock_actions->history().size()); + ASSERT_EQ(a, m_mock_actions->history()[0]); + EXPECT_EQ(appt.url, m_mock_actions->url()); +} + diff --git a/tests/test-clock.cc b/tests/test-clock.cc deleted file mode 100644 index 142ccad..0000000 --- a/tests/test-clock.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include -#include - -#include "test-dbus-fixture.h" - -/*** -**** -***/ - -using namespace unity::indicator::datetime; - -class ClockFixture: public TestDBusFixture -{ - private: - typedef TestDBusFixture super; - - public: - void emitPrepareForSleep() - { - g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), - NULL, - "/org/freedesktop/login1", // object path - "org.freedesktop.login1.Manager", // interface - "PrepareForSleep", // signal name - g_variant_new("(b)", FALSE), - NULL); - } -}; - -/** - * Confirm that normal time passing doesn't trigger a skew event. - * that idling changing the clock's time triggers a skew event - */ -TEST_F(ClockFixture, IdleDoesNotTriggerSkew) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up - - bool skewed = false; - clock.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 3; - clock.skewTestIntervalSec.set(intervalSec); - wait_msec(intervalSec * 2.5 * 1000); - EXPECT_FALSE(skewed); -} - -/*** -**** -***/ - -#define TIMEZONE_FILE (SANDBOX"/timezone") - -TEST_F(ClockFixture, HelloFixture) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); -} - - -TEST_F(ClockFixture, TimezoneChangeTriggersSkew) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - - auto tz_nyc = g_time_zone_new("America/New_York"); - auto now_nyc = g_date_time_new_now(tz_nyc); - auto now = clock.localtime(); - EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now.get())); - EXPECT_LE(abs(g_date_time_difference(now_nyc,now.get())), G_USEC_PER_SEC); - g_date_time_unref(now_nyc); - g_time_zone_unref(tz_nyc); - - /// change the timezones! - clock.skewDetected.connect([this](){ - g_main_loop_quit(loop); - }); - g_idle_add([](gpointer gs){ - static_cast(gs)->timezone.set("America/Los_Angeles"); - return G_SOURCE_REMOVE; - }, zones.get()); - g_main_loop_run(loop); - - auto tz_la = g_time_zone_new("America/Los_Angeles"); - auto now_la = g_date_time_new_now(tz_la); - now = clock.localtime(); - EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now.get())); - EXPECT_LE(abs(g_date_time_difference(now_la,now.get())), G_USEC_PER_SEC); - g_date_time_unref(now_la); - g_time_zone_unref(tz_la); -} - -/** - * Confirm that a "PrepareForSleep" event wil trigger a skew event - */ -TEST_F(ClockFixture, SleepTriggersSkew) -{ - std::shared_ptr zones(new Timezones); - zones->timezone.set("America/New_York"); - LiveClock clock(zones); - wait_msec(500); // wait for the bus to set up - - bool skewed = false; - clock.skewDetected.connect([&skewed, this](){ - skewed = true; - g_main_loop_quit(loop); - return G_SOURCE_REMOVE; - }); - - g_idle_add([](gpointer gself){ - static_cast(gself)->emitPrepareForSleep(); - return G_SOURCE_REMOVE; - }, this); - - g_main_loop_run(loop); - EXPECT_TRUE(skewed); -} diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp new file mode 100644 index 0000000..142ccad --- /dev/null +++ b/tests/test-clock.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include "test-dbus-fixture.h" + +/*** +**** +***/ + +using namespace unity::indicator::datetime; + +class ClockFixture: public TestDBusFixture +{ + private: + typedef TestDBusFixture super; + + public: + void emitPrepareForSleep() + { + g_dbus_connection_emit_signal(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), + NULL, + "/org/freedesktop/login1", // object path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + g_variant_new("(b)", FALSE), + NULL); + } +}; + +/** + * Confirm that normal time passing doesn't trigger a skew event. + * that idling changing the clock's time triggers a skew event + */ +TEST_F(ClockFixture, IdleDoesNotTriggerSkew) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed](){ + skewed = true; + g_warn_if_reached(); + return G_SOURCE_REMOVE; + }); + + const unsigned int intervalSec = 3; + clock.skewTestIntervalSec.set(intervalSec); + wait_msec(intervalSec * 2.5 * 1000); + EXPECT_FALSE(skewed); +} + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +TEST_F(ClockFixture, HelloFixture) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); +} + + +TEST_F(ClockFixture, TimezoneChangeTriggersSkew) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + + auto tz_nyc = g_time_zone_new("America/New_York"); + auto now_nyc = g_date_time_new_now(tz_nyc); + auto now = clock.localtime(); + EXPECT_EQ(g_date_time_get_utc_offset(now_nyc), g_date_time_get_utc_offset(now.get())); + EXPECT_LE(abs(g_date_time_difference(now_nyc,now.get())), G_USEC_PER_SEC); + g_date_time_unref(now_nyc); + g_time_zone_unref(tz_nyc); + + /// change the timezones! + clock.skewDetected.connect([this](){ + g_main_loop_quit(loop); + }); + g_idle_add([](gpointer gs){ + static_cast(gs)->timezone.set("America/Los_Angeles"); + return G_SOURCE_REMOVE; + }, zones.get()); + g_main_loop_run(loop); + + auto tz_la = g_time_zone_new("America/Los_Angeles"); + auto now_la = g_date_time_new_now(tz_la); + now = clock.localtime(); + EXPECT_EQ(g_date_time_get_utc_offset(now_la), g_date_time_get_utc_offset(now.get())); + EXPECT_LE(abs(g_date_time_difference(now_la,now.get())), G_USEC_PER_SEC); + g_date_time_unref(now_la); + g_time_zone_unref(tz_la); +} + +/** + * Confirm that a "PrepareForSleep" event wil trigger a skew event + */ +TEST_F(ClockFixture, SleepTriggersSkew) +{ + std::shared_ptr zones(new Timezones); + zones->timezone.set("America/New_York"); + LiveClock clock(zones); + wait_msec(500); // wait for the bus to set up + + bool skewed = false; + clock.skewDetected.connect([&skewed, this](){ + skewed = true; + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; + }); + + g_idle_add([](gpointer gself){ + static_cast(gself)->emitPrepareForSleep(); + return G_SOURCE_REMOVE; + }, this); + + g_main_loop_run(loop); + EXPECT_TRUE(skewed); +} diff --git a/tests/test-exporter.cc b/tests/test-exporter.cc deleted file mode 100644 index ea62cd3..0000000 --- a/tests/test-exporter.cc +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include "actions-mock.h" -#include "state-mock.h" -#include "glib-fixture.h" - -#include -#include -#include - -#include -#include - -using namespace unity::indicator::datetime; - -class ExporterFixture: public GlibFixture -{ -private: - - typedef GlibFixture super; - - static void on_bus_closed(GObject * object, - GAsyncResult * res, - gpointer gself) - { - auto self = static_cast(gself); - GError* err = nullptr; - g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); - g_assert_no_error(err); - g_main_loop_quit(self->loop); - } - -protected: - - GTestDBus* bus = nullptr; - - void SetUp() - { - super::SetUp(); - - // bring up the test bus - bus = g_test_dbus_new(G_TEST_DBUS_NONE); - g_test_dbus_up(bus); - const auto address = g_test_dbus_get_bus_address(bus); - g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); - g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); - } - - void TearDown() - { - GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - g_dbus_connection_close(connection, nullptr, on_bus_closed, this); - g_main_loop_run(loop); - g_clear_object(&connection); - g_test_dbus_down(bus); - g_clear_object(&bus); - - super::TearDown(); - } -}; - -TEST_F(ExporterFixture, HelloWorld) -{ - // confirms that the Test DBus SetUp() and TearDown() works -} - -TEST_F(ExporterFixture, Publish) -{ - std::shared_ptr state(new MockState); - std::shared_ptr actions(new MockActions(state)); - std::vector> menus; - - Exporter exporter; - exporter.publish(actions, menus); - wait_msec(); - - auto connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - auto exported = g_dbus_action_group_get (connection, BUS_NAME, BUS_PATH); - auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); - - // wait for the exported ActionGroup to be populated - if (g_strv_length(names_strv) == 0) - { - g_strfreev(names_strv); - wait_for_signal(exported, "action-added"); - names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); - } - - // convert it to a std::set for easy prodding - std::set names; - for(int i=0; names_strv && names_strv[i]; i++) - names.insert(names_strv[i]); - - // confirm the actions that we expect - EXPECT_EQ(1, names.count("activate-appointment")); - EXPECT_EQ(1, names.count("activate-desktop-settings")); - EXPECT_EQ(1, names.count("activate-phone-clock-app")); - EXPECT_EQ(1, names.count("activate-phone-settings")); - EXPECT_EQ(1, names.count("activate-planner")); - EXPECT_EQ(1, names.count("calendar")); - EXPECT_EQ(1, names.count("desktop-greeter-header")); - EXPECT_EQ(1, names.count("desktop-header")); - EXPECT_EQ(1, names.count("phone-greeter-header")); - EXPECT_EQ(1, names.count("phone-header")); - EXPECT_EQ(1, names.count("set-location")); - - // cleanup - g_strfreev(names_strv); - g_clear_object(&exported); - g_clear_object(&connection); -} diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp new file mode 100644 index 0000000..ea62cd3 --- /dev/null +++ b/tests/test-exporter.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include "actions-mock.h" +#include "state-mock.h" +#include "glib-fixture.h" + +#include +#include +#include + +#include +#include + +using namespace unity::indicator::datetime; + +class ExporterFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static void on_bus_closed(GObject * object, + GAsyncResult * res, + gpointer gself) + { + auto self = static_cast(gself); + GError* err = nullptr; + g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); + g_assert_no_error(err); + g_main_loop_quit(self->loop); + } + +protected: + + GTestDBus* bus = nullptr; + + void SetUp() + { + super::SetUp(); + + // bring up the test bus + bus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(bus); + const auto address = g_test_dbus_get_bus_address(bus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); + } + + void TearDown() + { + GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_close(connection, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&connection); + g_test_dbus_down(bus); + g_clear_object(&bus); + + super::TearDown(); + } +}; + +TEST_F(ExporterFixture, HelloWorld) +{ + // confirms that the Test DBus SetUp() and TearDown() works +} + +TEST_F(ExporterFixture, Publish) +{ + std::shared_ptr state(new MockState); + std::shared_ptr actions(new MockActions(state)); + std::vector> menus; + + Exporter exporter; + exporter.publish(actions, menus); + wait_msec(); + + auto connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); + auto exported = g_dbus_action_group_get (connection, BUS_NAME, BUS_PATH); + auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + + // wait for the exported ActionGroup to be populated + if (g_strv_length(names_strv) == 0) + { + g_strfreev(names_strv); + wait_for_signal(exported, "action-added"); + names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); + } + + // convert it to a std::set for easy prodding + std::set names; + for(int i=0; names_strv && names_strv[i]; i++) + names.insert(names_strv[i]); + + // confirm the actions that we expect + EXPECT_EQ(1, names.count("activate-appointment")); + EXPECT_EQ(1, names.count("activate-desktop-settings")); + EXPECT_EQ(1, names.count("activate-phone-clock-app")); + EXPECT_EQ(1, names.count("activate-phone-settings")); + EXPECT_EQ(1, names.count("activate-planner")); + EXPECT_EQ(1, names.count("calendar")); + EXPECT_EQ(1, names.count("desktop-greeter-header")); + EXPECT_EQ(1, names.count("desktop-header")); + EXPECT_EQ(1, names.count("phone-greeter-header")); + EXPECT_EQ(1, names.count("phone-header")); + EXPECT_EQ(1, names.count("set-location")); + + // cleanup + g_strfreev(names_strv); + g_clear_object(&exported); + g_clear_object(&connection); +} diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc deleted file mode 100644 index 9950453..0000000 --- a/tests/test-formatter.cc +++ /dev/null @@ -1,256 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "glib-fixture.h" - -#include -#include -#include - -#include - -#include -#include - -using namespace unity::indicator::datetime; - -/*** -**** -***/ - -class FormatterFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - gchar* m_original_locale = nullptr; - - protected: - - std::shared_ptr m_settings; - - virtual void SetUp() - { - super::SetUp(); - - m_settings.reset(new Settings); - m_original_locale = g_strdup(setlocale(LC_TIME, nullptr)); - } - - virtual void TearDown() - { - m_settings.reset(); - - setlocale(LC_TIME, m_original_locale); - g_clear_pointer(&m_original_locale, g_free); - - super::TearDown(); - } - - bool SetLocale(const char* expected_locale, const char* name) - { - setlocale(LC_TIME, expected_locale); - const auto actual_locale = setlocale(LC_TIME, nullptr); - if (!g_strcmp0(expected_locale, actual_locale)) - { - return true; - } - else - { - g_warning("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); - return false; - } - } - - inline bool Set24hLocale() { return SetLocale("C", "24h"); } - inline bool Set12hLocale() { return SetLocale("en_US.utf8", "12h"); } -}; - - -/** - * Test the phone header format - */ -TEST_F(FormatterFixture, TestPhoneHeader) -{ - auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - std::shared_ptr clock(new MockClock(DateTime(now))); - g_date_time_unref(now); - - // test the default value in a 24h locale - if(Set24hLocale()) - { - PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); - EXPECT_EQ(std::string("18:30"), formatter.header.get()); - } - - // test the default value in a 12h locale - if(Set12hLocale()) - { - PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); - EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); - } -} - -#define EM_SPACE "\u2003" - -/** - * Test the default values of the desktop header format - */ -TEST_F(FormatterFixture, TestDesktopHeader) -{ - struct { - bool is_12h; - bool show_day; - bool show_date; - bool show_year; - const char* expected_format_string; - } test_cases[] = { - { false, false, false, false, "%H:%M" }, - { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false - { false, false, true, false, "%b %e" EM_SPACE "%H:%M" }, - { false, false, true, true, "%b %e %Y" EM_SPACE "%H:%M" }, - { false, true, false, false, "%a" EM_SPACE "%H:%M" }, - { false, true, false, true, "%a" EM_SPACE "%H:%M" }, // show_year is ignored iff show_date is false - { false, true, true, false, "%a %b %e" EM_SPACE "%H:%M" }, - { false, true, true, true, "%a %b %e %Y" EM_SPACE "%H:%M" }, - { true, false, false, false, "%l:%M %p" }, - { true, false, false, true, "%l:%M %p" }, // show_year is ignored iff show_date is false - { true, false, true, false, "%b %e" EM_SPACE "%l:%M %p" }, - { true, false, true, true, "%b %e %Y" EM_SPACE "%l:%M %p" }, - { true, true, false, false, "%a" EM_SPACE "%l:%M %p" }, - { true, true, false, true, "%a" EM_SPACE "%l:%M %p" }, // show_year is ignored iff show_date is false - { true, true, true, false, "%a %b %e" EM_SPACE "%l:%M %p" }, - { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } - }; - - auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - std::shared_ptr clock(new MockClock(DateTime(now))); - g_date_time_unref(now); - - for(const auto& test_case : test_cases) - { - if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) - { - DesktopFormatter f(clock, m_settings); - - m_settings->show_day.set(test_case.show_day); - m_settings->show_date.set(test_case.show_date); - m_settings->show_year.set(test_case.show_year); - - ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); - } - } -} - -/** - * Test the default values of the desktop header format - */ -TEST_F(FormatterFixture, TestUpcomingTimes) -{ - auto a = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - - struct { - gboolean is_12h; - GDateTime* now; - GDateTime* then; - const char* expected_format_string; - } test_cases[] = { - { true, g_date_time_ref(a), g_date_time_ref(a), "%l:%M %p" }, // identical time - { true, g_date_time_ref(a), g_date_time_add_hours(a,1), "%l:%M %p" }, // later today - { true, g_date_time_ref(a), g_date_time_add_days(a,1), "Tomorrow" EM_SPACE "%l:%M %p" }, // tomorrow - { true, g_date_time_ref(a), g_date_time_add_days(a,2), "%a" EM_SPACE "%l:%M %p" }, - { true, g_date_time_ref(a), g_date_time_add_days(a,6), "%a" EM_SPACE "%l:%M %p" }, - { true, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%l:%M %p" }, // over one week away - - { false, g_date_time_ref(a), g_date_time_ref(a), "%H:%M" }, // identical time - { false, g_date_time_ref(a), g_date_time_add_hours(a,1), "%H:%M" }, // later today - { false, g_date_time_ref(a), g_date_time_add_days(a,1), "Tomorrow" EM_SPACE "%H:%M" }, // tomorrow - { false, g_date_time_ref(a), g_date_time_add_days(a,2), "%a" EM_SPACE "%H:%M" }, - { false, g_date_time_ref(a), g_date_time_add_days(a,6), "%a" EM_SPACE "%H:%M" }, - { false, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%H:%M" } // over one week away - }; - - for(const auto& test_case : test_cases) - { - if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) - { - std::shared_ptr clock (new MockClock(DateTime(test_case.now))); - DesktopFormatter f(clock, m_settings); - - const auto fmt = f.getRelativeFormat(test_case.then); - ASSERT_EQ(test_case.expected_format_string, fmt); - - g_clear_pointer(&test_case.now, g_date_time_unref); - g_clear_pointer(&test_case.then, g_date_time_unref); - } - } - - g_date_time_unref(a); -} - - -/** - * Test the default values of the desktop header format - */ -TEST_F(FormatterFixture, TestEventTimes) -{ - auto day = g_date_time_new_local(2013, 1, 1, 13, 0, 0); - auto day_begin = g_date_time_new_local(2013, 1, 1, 13, 0, 0); - auto day_end = g_date_time_add_days(day_begin, 1); - auto tomorrow_begin = g_date_time_add_days(day_begin, 1); - auto tomorrow_end = g_date_time_add_days(tomorrow_begin, 1); - - struct { - bool is_12h; - GDateTime* now; - GDateTime* then; - GDateTime* then_end; - const char* expected_format_string; - } test_cases[] = { - { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, - { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, - { false, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") }, - { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } - }; - - for(const auto& test_case : test_cases) - { - if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) - { - std::shared_ptr clock(new MockClock(DateTime(test_case.now))); - DesktopFormatter f(clock, m_settings); - - const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); - ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); - - g_clear_pointer(&test_case.now, g_date_time_unref); - g_clear_pointer(&test_case.then, g_date_time_unref); - g_clear_pointer(&test_case.then_end, g_date_time_unref); - } - } - - g_date_time_unref(tomorrow_end); - g_date_time_unref(tomorrow_begin); - g_date_time_unref(day_end); - g_date_time_unref(day_begin); - g_date_time_unref(day); -} diff --git a/tests/test-formatter.cpp b/tests/test-formatter.cpp new file mode 100644 index 0000000..9950453 --- /dev/null +++ b/tests/test-formatter.cpp @@ -0,0 +1,256 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include +#include +#include + +#include + +#include +#include + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +class FormatterFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + gchar* m_original_locale = nullptr; + + protected: + + std::shared_ptr m_settings; + + virtual void SetUp() + { + super::SetUp(); + + m_settings.reset(new Settings); + m_original_locale = g_strdup(setlocale(LC_TIME, nullptr)); + } + + virtual void TearDown() + { + m_settings.reset(); + + setlocale(LC_TIME, m_original_locale); + g_clear_pointer(&m_original_locale, g_free); + + super::TearDown(); + } + + bool SetLocale(const char* expected_locale, const char* name) + { + setlocale(LC_TIME, expected_locale); + const auto actual_locale = setlocale(LC_TIME, nullptr); + if (!g_strcmp0(expected_locale, actual_locale)) + { + return true; + } + else + { + g_warning("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); + return false; + } + } + + inline bool Set24hLocale() { return SetLocale("C", "24h"); } + inline bool Set12hLocale() { return SetLocale("en_US.utf8", "12h"); } +}; + + +/** + * Test the phone header format + */ +TEST_F(FormatterFixture, TestPhoneHeader) +{ + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); + g_date_time_unref(now); + + // test the default value in a 24h locale + if(Set24hLocale()) + { + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("18:30"), formatter.header.get()); + } + + // test the default value in a 12h locale + if(Set12hLocale()) + { + PhoneFormatter formatter(clock); + EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); + } +} + +#define EM_SPACE "\u2003" + +/** + * Test the default values of the desktop header format + */ +TEST_F(FormatterFixture, TestDesktopHeader) +{ + struct { + bool is_12h; + bool show_day; + bool show_date; + bool show_year; + const char* expected_format_string; + } test_cases[] = { + { false, false, false, false, "%H:%M" }, + { false, false, false, true, "%H:%M" }, // show_year is ignored iff show_date is false + { false, false, true, false, "%b %e" EM_SPACE "%H:%M" }, + { false, false, true, true, "%b %e %Y" EM_SPACE "%H:%M" }, + { false, true, false, false, "%a" EM_SPACE "%H:%M" }, + { false, true, false, true, "%a" EM_SPACE "%H:%M" }, // show_year is ignored iff show_date is false + { false, true, true, false, "%a %b %e" EM_SPACE "%H:%M" }, + { false, true, true, true, "%a %b %e %Y" EM_SPACE "%H:%M" }, + { true, false, false, false, "%l:%M %p" }, + { true, false, false, true, "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, false, true, false, "%b %e" EM_SPACE "%l:%M %p" }, + { true, false, true, true, "%b %e %Y" EM_SPACE "%l:%M %p" }, + { true, true, false, false, "%a" EM_SPACE "%l:%M %p" }, + { true, true, false, true, "%a" EM_SPACE "%l:%M %p" }, // show_year is ignored iff show_date is false + { true, true, true, false, "%a %b %e" EM_SPACE "%l:%M %p" }, + { true, true, true, true, "%a %b %e %Y" EM_SPACE "%l:%M %p" } + }; + + auto now = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + std::shared_ptr clock(new MockClock(DateTime(now))); + g_date_time_unref(now); + + for(const auto& test_case : test_cases) + { + if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) + { + DesktopFormatter f(clock, m_settings); + + m_settings->show_day.set(test_case.show_day); + m_settings->show_date.set(test_case.show_date); + m_settings->show_year.set(test_case.show_year); + + ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); + } + } +} + +/** + * Test the default values of the desktop header format + */ +TEST_F(FormatterFixture, TestUpcomingTimes) +{ + auto a = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + + struct { + gboolean is_12h; + GDateTime* now; + GDateTime* then; + const char* expected_format_string; + } test_cases[] = { + { true, g_date_time_ref(a), g_date_time_ref(a), "%l:%M %p" }, // identical time + { true, g_date_time_ref(a), g_date_time_add_hours(a,1), "%l:%M %p" }, // later today + { true, g_date_time_ref(a), g_date_time_add_days(a,1), "Tomorrow" EM_SPACE "%l:%M %p" }, // tomorrow + { true, g_date_time_ref(a), g_date_time_add_days(a,2), "%a" EM_SPACE "%l:%M %p" }, + { true, g_date_time_ref(a), g_date_time_add_days(a,6), "%a" EM_SPACE "%l:%M %p" }, + { true, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%l:%M %p" }, // over one week away + + { false, g_date_time_ref(a), g_date_time_ref(a), "%H:%M" }, // identical time + { false, g_date_time_ref(a), g_date_time_add_hours(a,1), "%H:%M" }, // later today + { false, g_date_time_ref(a), g_date_time_add_days(a,1), "Tomorrow" EM_SPACE "%H:%M" }, // tomorrow + { false, g_date_time_ref(a), g_date_time_add_days(a,2), "%a" EM_SPACE "%H:%M" }, + { false, g_date_time_ref(a), g_date_time_add_days(a,6), "%a" EM_SPACE "%H:%M" }, + { false, g_date_time_ref(a), g_date_time_add_days(a,7), "%a %d %b" EM_SPACE "%H:%M" } // over one week away + }; + + for(const auto& test_case : test_cases) + { + if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) + { + std::shared_ptr clock (new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); + + const auto fmt = f.getRelativeFormat(test_case.then); + ASSERT_EQ(test_case.expected_format_string, fmt); + + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); + } + } + + g_date_time_unref(a); +} + + +/** + * Test the default values of the desktop header format + */ +TEST_F(FormatterFixture, TestEventTimes) +{ + auto day = g_date_time_new_local(2013, 1, 1, 13, 0, 0); + auto day_begin = g_date_time_new_local(2013, 1, 1, 13, 0, 0); + auto day_end = g_date_time_add_days(day_begin, 1); + auto tomorrow_begin = g_date_time_add_days(day_begin, 1); + auto tomorrow_end = g_date_time_add_days(tomorrow_begin, 1); + + struct { + bool is_12h; + GDateTime* now; + GDateTime* then; + GDateTime* then_end; + const char* expected_format_string; + } test_cases[] = { + { false, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { true, g_date_time_ref(day), g_date_time_ref(day_begin), g_date_time_ref(day_end), _("Today") }, + { false, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") }, + { true, g_date_time_ref(day), g_date_time_ref(tomorrow_begin), g_date_time_ref(tomorrow_end), _("Tomorrow") } + }; + + for(const auto& test_case : test_cases) + { + if (test_case.is_12h ? Set12hLocale() : Set24hLocale()) + { + std::shared_ptr clock(new MockClock(DateTime(test_case.now))); + DesktopFormatter f(clock, m_settings); + + const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); + ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); + + g_clear_pointer(&test_case.now, g_date_time_unref); + g_clear_pointer(&test_case.then, g_date_time_unref); + g_clear_pointer(&test_case.then_end, g_date_time_unref); + } + } + + g_date_time_unref(tomorrow_end); + g_date_time_unref(tomorrow_begin); + g_date_time_unref(day_end); + g_date_time_unref(day_begin); + g_date_time_unref(day); +} diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp new file mode 100644 index 0000000..d3d7720 --- /dev/null +++ b/tests/test-live-actions.cpp @@ -0,0 +1,289 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include "state-mock.h" +#include "glib-fixture.h" + +/*** +**** +***/ + +class MockLiveActions: public LiveActions +{ +public: + std::string last_cmd; + std::string last_url; + MockLiveActions(const std::shared_ptr& state_in): LiveActions(state_in) {} + virtual ~MockLiveActions() {} + +protected: + void dispatch_url(const std::string& url) { last_url = url; } + void execute_command(const std::string& cmd) { last_cmd = cmd; } +}; + +/*** +**** +***/ + +using namespace unity::indicator::datetime; + +class LiveActionsFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static void on_bus_acquired(GDBusConnection* conn, + const gchar* name, + gpointer gself) + { + auto self = static_cast(gself); + g_debug("bus acquired: %s, connection is %p", name, conn); + + // Set up a mock GSD. + // All it really does is wait for calls to GetDevice and + // returns the get_devices_retval variant + static const GDBusInterfaceVTable vtable = { + timedate1_handle_method_call, + nullptr, /* GetProperty */ + nullptr, /* SetProperty */ + }; + + self->connection = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(conn))); + + GError* error = nullptr; + self->object_register_id = g_dbus_connection_register_object( + conn, + "/org/freedesktop/timedate1", + self->node_info->interfaces[0], + &vtable, + self, + nullptr, + &error); + g_assert_no_error(error); + } + + static void on_name_acquired(GDBusConnection* /*conn*/, + const gchar* /*name*/, + gpointer gself) + { + auto self = static_cast(gself); + self->name_acquired = true; + g_main_loop_quit(self->loop); + } + + static void on_name_lost(GDBusConnection* /*conn*/, + const gchar* /*name*/, + gpointer gself) + { + auto self = static_cast(gself); + self->name_acquired = false; + } + + static void on_bus_closed(GObject* /*object*/, + GAsyncResult* res, + gpointer gself) + { + auto self = static_cast(gself); + GError* err = nullptr; + g_dbus_connection_close_finish(self->connection, res, &err); + g_assert_no_error(err); + g_main_loop_quit(self->loop); + } + + static void + timedate1_handle_method_call(GDBusConnection * /*connection*/, + const gchar * /*sender*/, + const gchar * /*object_path*/, + const gchar * /*interface_name*/, + const gchar * method_name, + GVariant * parameters, + GDBusMethodInvocation * invocation, + gpointer gself) + { + g_assert(!g_strcmp0(method_name, "SetTimezone")); + g_assert(g_variant_is_of_type(parameters, G_VARIANT_TYPE_TUPLE)); + g_assert(2 == g_variant_n_children(parameters)); + + auto child = g_variant_get_child_value(parameters, 0); + g_assert(g_variant_is_of_type(child, G_VARIANT_TYPE_STRING)); + auto self = static_cast(gself); + self->attempted_tzid = g_variant_get_string(child, nullptr); + g_variant_unref(child); + + g_dbus_method_invocation_return_value(invocation, nullptr); + g_main_loop_quit(self->loop); + } + +protected: + + std::shared_ptr m_mock_state; + std::shared_ptr m_state; + std::shared_ptr m_live_actions; + std::shared_ptr m_actions; + + bool name_acquired; + std::string attempted_tzid; + + GTestDBus* bus; + guint own_name; + GDBusConnection* connection; + GDBusNodeInfo* node_info; + int object_register_id; + + void SetUp() + { + super::SetUp(); + + name_acquired = false; + attempted_tzid.clear(); + connection = nullptr; + node_info = nullptr; + object_register_id = 0; + own_name = 0; + + // bring up the test bus + bus = g_test_dbus_new(G_TEST_DBUS_NONE); + g_test_dbus_up(bus); + const auto address = g_test_dbus_get_bus_address(bus); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv("DBUS_SESSION_BUS_ADDRESS", address, true); + g_debug("test_dbus's address is %s", address); + + // parse the org.freedesktop.timedate1 interface + const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + ""; + node_info = g_dbus_node_info_new_for_xml(introspection_xml, nullptr); + ASSERT_TRUE(node_info != nullptr); + ASSERT_TRUE(node_info->interfaces != nullptr); + ASSERT_TRUE(node_info->interfaces[0] != nullptr); + ASSERT_TRUE(node_info->interfaces[1] == nullptr); + ASSERT_STREQ("org.freedesktop.timedate1", node_info->interfaces[0]->name); + + // own the bus + own_name = g_bus_own_name(G_BUS_TYPE_SYSTEM, + "org.freedesktop.timedate1", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, on_name_acquired, on_name_lost, + this, nullptr); + ASSERT_TRUE(object_register_id == 0); + ASSERT_FALSE(name_acquired); + ASSERT_TRUE(connection == nullptr); + g_main_loop_run(loop); + ASSERT_TRUE(object_register_id != 0); + ASSERT_TRUE(name_acquired); + ASSERT_TRUE(G_IS_DBUS_CONNECTION(connection)); + + // create the State and Actions + m_mock_state.reset(new MockState); + m_mock_state->settings.reset(new Settings); + m_state = std::dynamic_pointer_cast(m_mock_state); + m_live_actions.reset(new MockLiveActions(m_state)); + m_actions = std::dynamic_pointer_cast(m_live_actions); + } + + void TearDown() + { + m_actions.reset(); + m_live_actions.reset(); + m_state.reset(); + m_mock_state.reset(); + + g_dbus_connection_unregister_object(connection, object_register_id); + g_dbus_node_info_unref(node_info); + g_bus_unown_name(own_name); + g_dbus_connection_close(connection, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&connection); + g_test_dbus_down(bus); + g_clear_object(&bus); + + super::TearDown(); + } +}; + +/*** +**** +***/ + +TEST_F(LiveActionsFixture, HelloWorld) +{ + EXPECT_TRUE(true); +} + +TEST_F(LiveActionsFixture, SetLocation) +{ + const std::string tzid = "America/Chicago"; + const std::string name = "Oklahoma City"; + const std::string expected = tzid + " " + name; + + EXPECT_NE(expected, m_state->settings->timezone_name.get()); + + m_actions->set_location(tzid, name); + g_main_loop_run(loop); + EXPECT_EQ(attempted_tzid, tzid); + wait_msec(); + + EXPECT_EQ(expected, m_state->settings->timezone_name.get()); +} + +TEST_F(LiveActionsFixture, OpenDesktopSettings) +{ + m_actions->open_desktop_settings(); + const std::string expected_substr = "control-center"; + EXPECT_NE(m_live_actions->last_cmd.find(expected_substr), std::string::npos); +} + +TEST_F(LiveActionsFixture, OpenPlanner) +{ + m_actions->open_planner(); + const std::string expected = "evolution -c calendar"; + EXPECT_EQ(expected, m_live_actions->last_cmd); +} + +TEST_F(LiveActionsFixture, OpenPhoneSettings) +{ + m_actions->open_phone_settings(); + const std::string expected = "settings:///system/time-date"; + EXPECT_EQ(expected, m_live_actions->last_url); +} + +TEST_F(LiveActionsFixture, OpenPhoneClockApp) +{ + m_actions->open_phone_clock_app(); + const std::string expected = "appid://com.ubuntu.clock/clock/current-user-version"; + EXPECT_EQ(expected, m_live_actions->last_url); +} + +TEST_F(LiveActionsFixture, OpenPlannerAt) +{ + const auto now = DateTime::NowLocal(); + m_actions->open_planner_at(now); + const std::string expected = now.format("evolution \"calendar:///?startdate=%Y%m%d\""); + EXPECT_EQ(expected, m_live_actions->last_cmd); +} diff --git a/tests/test-locations.cc b/tests/test-locations.cc deleted file mode 100644 index 65adbc7..0000000 --- a/tests/test-locations.cc +++ /dev/null @@ -1,169 +0,0 @@ - - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "glib-fixture.h" - -#include - -using unity::indicator::datetime::Location; -using unity::indicator::datetime::Locations; -using unity::indicator::datetime::Settings; -using unity::indicator::datetime::SettingsLocations; -using unity::indicator::datetime::Timezones; - -/*** -**** -***/ - -class LocationsFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - //GSettings * settings = nullptr; - std::shared_ptr m_settings; - std::shared_ptr m_timezones; - const std::string nyc = "America/New_York"; - const std::string chicago = "America/Chicago"; - - virtual void SetUp() - { - super::SetUp(); - - m_settings.reset(new Settings); - m_settings->show_locations.set(true); - m_settings->locations.set({"America/Los_Angeles Oakland", - "America/Chicago Chicago", - "America/Chicago Oklahoma City", - "America/Toronto Toronto", - "Europe/London London", - "Europe/Berlin Berlin"}); - - m_timezones.reset(new Timezones); - m_timezones->timezone.set(chicago); - m_timezones->timezones.set(std::set({ nyc, chicago })); - } - - virtual void TearDown() - { - m_timezones.reset(); - m_settings.reset(); - - super::TearDown(); - } -}; - -TEST_F(LocationsFixture, Timezones) -{ - m_settings->show_locations.set(false); - - SettingsLocations locations(m_settings, m_timezones); - const auto l = locations.locations.get(); - EXPECT_EQ(2, l.size()); - EXPECT_STREQ("Chicago", l[0].name().c_str()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); -} - -TEST_F(LocationsFixture, SettingsLocations) -{ - SettingsLocations locations(m_settings, m_timezones); - - const auto l = locations.locations.get(); - EXPECT_EQ(7, l.size()); - EXPECT_EQ("Chicago", l[0].name()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); - EXPECT_EQ("Oakland", l[2].name()); - EXPECT_EQ("America/Los_Angeles", l[2].zone()); - EXPECT_EQ("Oklahoma City", l[3].name()); - EXPECT_EQ("America/Chicago", l[3].zone()); - EXPECT_EQ("Toronto", l[4].name()); - EXPECT_EQ("America/Toronto", l[4].zone()); - EXPECT_EQ("London", l[5].name()); - EXPECT_EQ("Europe/London", l[5].zone()); - EXPECT_EQ("Berlin", l[6].name()); - EXPECT_EQ("Europe/Berlin", l[6].zone()); -} - -TEST_F(LocationsFixture, ChangeLocationStrings) -{ - SettingsLocations locations(m_settings, m_timezones); - - bool locations_changed = false; - locations.locations.changed().connect([&locations_changed, this](const std::vector&){ - locations_changed = true; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer settings){ - static_cast(settings)->locations.set({"America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin"}); - return G_SOURCE_REMOVE; - }, m_settings.get()); - - g_main_loop_run(loop); - - EXPECT_TRUE(locations_changed); - const auto l = locations.locations.get(); - EXPECT_EQ(5, l.size()); - EXPECT_EQ("Chicago", l[0].name()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); - EXPECT_EQ("Oakland", l[2].name()); - EXPECT_EQ("America/Los_Angeles", l[2].zone()); - EXPECT_EQ("London", l[3].name()); - EXPECT_EQ("Europe/London", l[3].zone()); - EXPECT_EQ("Berlin", l[4].name()); - EXPECT_EQ("Europe/Berlin", l[4].zone()); - locations_changed = false; -} - -TEST_F(LocationsFixture, ChangeLocationVisibility) -{ - SettingsLocations locations(m_settings, m_timezones); - - bool locations_changed = false; - locations.locations.changed().connect([&locations_changed, this](const std::vector&){ - locations_changed = true; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer settings){ - static_cast(settings)->show_locations.set(false); - return G_SOURCE_REMOVE; - }, m_settings.get()); - - g_main_loop_run(loop); - - EXPECT_TRUE(locations_changed); - const auto l = locations.locations.get(); - EXPECT_EQ(2, l.size()); - EXPECT_EQ("Chicago", l[0].name()); - EXPECT_EQ(chicago, l[0].zone()); - EXPECT_EQ("New York", l[1].name()); - EXPECT_EQ(nyc, l[1].zone()); -} diff --git a/tests/test-locations.cpp b/tests/test-locations.cpp new file mode 100644 index 0000000..65adbc7 --- /dev/null +++ b/tests/test-locations.cpp @@ -0,0 +1,169 @@ + + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include + +using unity::indicator::datetime::Location; +using unity::indicator::datetime::Locations; +using unity::indicator::datetime::Settings; +using unity::indicator::datetime::SettingsLocations; +using unity::indicator::datetime::Timezones; + +/*** +**** +***/ + +class LocationsFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + //GSettings * settings = nullptr; + std::shared_ptr m_settings; + std::shared_ptr m_timezones; + const std::string nyc = "America/New_York"; + const std::string chicago = "America/Chicago"; + + virtual void SetUp() + { + super::SetUp(); + + m_settings.reset(new Settings); + m_settings->show_locations.set(true); + m_settings->locations.set({"America/Los_Angeles Oakland", + "America/Chicago Chicago", + "America/Chicago Oklahoma City", + "America/Toronto Toronto", + "Europe/London London", + "Europe/Berlin Berlin"}); + + m_timezones.reset(new Timezones); + m_timezones->timezone.set(chicago); + m_timezones->timezones.set(std::set({ nyc, chicago })); + } + + virtual void TearDown() + { + m_timezones.reset(); + m_settings.reset(); + + super::TearDown(); + } +}; + +TEST_F(LocationsFixture, Timezones) +{ + m_settings->show_locations.set(false); + + SettingsLocations locations(m_settings, m_timezones); + const auto l = locations.locations.get(); + EXPECT_EQ(2, l.size()); + EXPECT_STREQ("Chicago", l[0].name().c_str()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); +} + +TEST_F(LocationsFixture, SettingsLocations) +{ + SettingsLocations locations(m_settings, m_timezones); + + const auto l = locations.locations.get(); + EXPECT_EQ(7, l.size()); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("Oklahoma City", l[3].name()); + EXPECT_EQ("America/Chicago", l[3].zone()); + EXPECT_EQ("Toronto", l[4].name()); + EXPECT_EQ("America/Toronto", l[4].zone()); + EXPECT_EQ("London", l[5].name()); + EXPECT_EQ("Europe/London", l[5].zone()); + EXPECT_EQ("Berlin", l[6].name()); + EXPECT_EQ("Europe/Berlin", l[6].zone()); +} + +TEST_F(LocationsFixture, ChangeLocationStrings) +{ + SettingsLocations locations(m_settings, m_timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer settings){ + static_cast(settings)->locations.set({"America/Los_Angeles Oakland", "Europe/London London", "Europe/Berlin Berlin"}); + return G_SOURCE_REMOVE; + }, m_settings.get()); + + g_main_loop_run(loop); + + EXPECT_TRUE(locations_changed); + const auto l = locations.locations.get(); + EXPECT_EQ(5, l.size()); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); + EXPECT_EQ("Oakland", l[2].name()); + EXPECT_EQ("America/Los_Angeles", l[2].zone()); + EXPECT_EQ("London", l[3].name()); + EXPECT_EQ("Europe/London", l[3].zone()); + EXPECT_EQ("Berlin", l[4].name()); + EXPECT_EQ("Europe/Berlin", l[4].zone()); + locations_changed = false; +} + +TEST_F(LocationsFixture, ChangeLocationVisibility) +{ + SettingsLocations locations(m_settings, m_timezones); + + bool locations_changed = false; + locations.locations.changed().connect([&locations_changed, this](const std::vector&){ + locations_changed = true; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer settings){ + static_cast(settings)->show_locations.set(false); + return G_SOURCE_REMOVE; + }, m_settings.get()); + + g_main_loop_run(loop); + + EXPECT_TRUE(locations_changed); + const auto l = locations.locations.get(); + EXPECT_EQ(2, l.size()); + EXPECT_EQ("Chicago", l[0].name()); + EXPECT_EQ(chicago, l[0].zone()); + EXPECT_EQ("New York", l[1].name()); + EXPECT_EQ(nyc, l[1].zone()); +} diff --git a/tests/test-menus.cc b/tests/test-menus.cc deleted file mode 100644 index fe1e86e..0000000 --- a/tests/test-menus.cc +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - - -#include "actions-mock.h" -#include "state-fixture.h" - -#include -#include -#include -#include - -#include - -using namespace unity::indicator::datetime; - -class MenuFixture: public StateFixture -{ -private: - typedef StateFixture super; - -protected: - std::shared_ptr m_menu_factory; - std::vector> m_menus; - - virtual void SetUp() - { - super::SetUp(); - - // build the menus on top of the actions and state - m_menu_factory.reset(new MenuFactory(m_actions, m_state)); - for(int i=0; ibuildMenu(Menu::Profile(i))); - } - - virtual void TearDown() - { - m_menus.clear(); - m_menu_factory.reset(); - - super::TearDown(); - } - - void InspectHeader(GMenuModel* menu_model, const std::string& name) - { - // check that there's a header menuitem - EXPECT_EQ(1,g_menu_model_get_n_items(menu_model)); - gchar* str = nullptr; - g_menu_model_get_item_attribute(menu_model, 0, "x-canonical-type", "s", &str); - EXPECT_STREQ("com.canonical.indicator.root", str); - g_clear_pointer(&str, g_free); - g_menu_model_get_item_attribute(menu_model, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); - const auto action_name = name + "-header"; - EXPECT_EQ(std::string("indicator.")+action_name, str); - g_clear_pointer(&str, g_free); - - // check the header - auto dict = g_action_group_get_action_state(m_actions->action_group(), action_name.c_str()); - EXPECT_TRUE(dict != nullptr); - EXPECT_TRUE(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); - auto v = g_variant_lookup_value(dict, "accessible-desc", G_VARIANT_TYPE_STRING); - EXPECT_TRUE(v != nullptr); - g_variant_unref(v); - v = g_variant_lookup_value(dict, "label", G_VARIANT_TYPE_STRING); - EXPECT_TRUE(v != nullptr); - g_variant_unref(v); - v = g_variant_lookup_value(dict, "title", G_VARIANT_TYPE_STRING); - EXPECT_TRUE(v != nullptr); - g_variant_unref(v); - v = g_variant_lookup_value(dict, "visible", G_VARIANT_TYPE_BOOLEAN); - EXPECT_TRUE(v != nullptr); - g_variant_unref(v); - g_variant_unref(dict); - } - - void InspectCalendar(GMenuModel* menu_model, Menu::Profile profile) - { - gchar* str = nullptr; - const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); - const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); - - // get the calendar section - auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); - auto section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); - - // should be one or two items: a date label and maybe a calendar - ASSERT_TRUE(section != nullptr); - auto n_expected = calendar_expected ? 2 : 1; - EXPECT_EQ(n_expected, g_menu_model_get_n_items(section)); - - // look at the date menuitem - g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); - const auto now = m_state->clock->localtime(); - EXPECT_EQ(now.format("%A, %e %B %Y"), str); - - g_clear_pointer(&str, g_free); - - g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); - if (actions_expected) - EXPECT_STREQ("indicator.activate-planner", str); - else - EXPECT_TRUE(str == nullptr); - g_clear_pointer(&str, g_free); - - // look at the calendar menuitem - if (calendar_expected) - { - g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); - EXPECT_STREQ("com.canonical.indicator.calendar", str); - g_clear_pointer(&str, g_free); - - g_menu_model_get_item_attribute(section, 1, G_MENU_ATTRIBUTE_ACTION, "s", &str); - EXPECT_STREQ("indicator.calendar", str); - g_clear_pointer(&str, g_free); - - g_menu_model_get_item_attribute(section, 1, "activation-action", "s", &str); - if (actions_expected) - EXPECT_STREQ("indicator.activate-planner", str); - else - EXPECT_TRUE(str == nullptr); - g_clear_pointer(&str, g_free); - } - - g_clear_object(§ion); - - // now change the clock and see if the date label changes appropriately - - auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); - auto tomorrow = DateTime(gdt_tomorrow); - g_date_time_unref(gdt_tomorrow); - m_mock_state->mock_clock->set_localtime(tomorrow); - wait_msec(); - - section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); - g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); - EXPECT_EQ(tomorrow.format("%A, %e %B %Y"), str); - g_clear_pointer(&str, g_free); - g_clear_object(§ion); - - // cleanup - g_object_unref(submenu); - } - - void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) - { - const bool appointments_expected = (profile == Menu::Desktop) - || (profile == Menu::Phone); - - // get the Appointments section - auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); - - // there shouldn't be any menuitems when "show events" is false - m_state->settings->show_events.set(false); - wait_msec(); - auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - EXPECT_EQ(0, g_menu_model_get_n_items(section)); - g_clear_object(§ion); - - // when "show_events" is true, - // there should be an "add event" button even if there aren't any appointments - std::vector appointments; - m_state->settings->show_events.set(true); - m_state->planner->upcoming.set(appointments); - wait_msec(); - section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - int expected_n = appointments_expected ? 1 : 0; - EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); - g_clear_object(§ion); - - // try adding a few appointments and see if the menu updates itself - - const auto now = m_state->clock->localtime(); - auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); - const auto tomorrow = DateTime(gdt_tomorrow); - g_date_time_unref(gdt_tomorrow); - - Appointment a1; // an alarm clock appointment - a1.color = "red"; - a1.summary = "Alarm"; - a1.summary = "http://www.example.com/"; - a1.uid = "example"; - a1.has_alarms = true; - a1.begin = a1.end = tomorrow; - appointments.push_back(a1); - - Appointment a2; // a non-alarm appointment - a2.color = "green"; - a2.summary = "Other Text"; - a2.summary = "http://www.monkey.com/"; - a2.uid = "monkey"; - a2.has_alarms = false; - a2.begin = a2.end = tomorrow; - appointments.push_back(a2); - - m_state->planner->upcoming.set(appointments); - wait_msec(); // wait a moment for the menu to update - - section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - expected_n = appointments_expected ? 3 : 0; - EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); - if (appointments_expected) - { - gchar * str = nullptr; - - // test the alarm - // - confirm it has an x-canonical-type of "alarm" - g_menu_model_get_item_attribute(section, 0, "x-canonical-type", "s", &str); - EXPECT_STREQ("com.canonical.indicator.alarm", str); - g_clear_pointer(&str, g_free); - // - confirm it has a nonempty x-canonical-time-format - g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); - EXPECT_TRUE(str && *str); - g_clear_pointer(&str, g_free); - // - confirm it has a serialized icon attribute - auto v = g_menu_model_get_item_attribute_value(section, 0, G_MENU_ATTRIBUTE_ICON, nullptr); - EXPECT_TRUE(v != nullptr); - auto icon = g_icon_deserialize(v); - EXPECT_TRUE(icon != nullptr); - g_clear_object(&icon); - g_clear_pointer(&v, g_variant_unref); - - // test the appointment - // - confirm it has an x-canonical-type of "appointment" - g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); - EXPECT_STREQ("com.canonical.indicator.appointment", str); - g_clear_pointer(&str, g_free); - // - confirm it has a nonempty x-canonical-time-format - g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); - EXPECT_TRUE(str && *str); - g_clear_pointer(&str, g_free); - // - confirm its color matches the one we fed the appointments vector - g_menu_model_get_item_attribute(section, 1, "x-canonical-color", "s", &str); - EXPECT_EQ(a2.color, str); - g_clear_pointer(&str, g_free); - } - g_clear_object(§ion); - - g_object_unref(submenu); - } - - void CompareLocationsTo(GMenuModel* menu_model, const std::vector& locations) - { - // get the Locations section - auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); - auto section = g_menu_model_get_item_link(submenu, Menu::Locations, G_MENU_LINK_SECTION); - - // confirm that section's menuitems mirror the "locations" vector - const auto n = locations.size(); - ASSERT_EQ(n, g_menu_model_get_n_items(section)); - for (guint i=0; i empty; - m_state->locations->locations.set(empty); - wait_msec(); - CompareLocationsTo(menu_model, empty); - - // add some locations and confirm the menu picked up our changes - Location l1 ("America/Chicago", "Dallas"); - Location l2 ("America/Arizona", "Phoenix"); - std::vector locations({l1, l2}); - m_state->locations->locations.set(locations); - wait_msec(); - CompareLocationsTo(menu_model, locations_expected ? locations : empty); - - // now remove one of the locations... - locations.pop_back(); - m_state->locations->locations.set(locations); - wait_msec(); - CompareLocationsTo(menu_model, locations_expected ? locations : empty); - } - - void InspectSettings(GMenuModel* menu_model, Menu::Profile profile) - { - std::string expected_action; - - if (profile == Menu::Desktop) - expected_action = "indicator.activate-desktop-settings"; - else if (profile == Menu::Phone) - expected_action = "indicator.activate-phone-settings"; - - // get the Settings section - auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); - auto section = g_menu_model_get_item_link(submenu, Menu::Settings, G_MENU_LINK_SECTION); - - if (expected_action.empty()) - { - EXPECT_EQ(0, g_menu_model_get_n_items(section)); - } - else - { - EXPECT_EQ(1, g_menu_model_get_n_items(section)); - gchar* str = nullptr; - g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); - EXPECT_EQ(expected_action, str); - g_clear_pointer(&str, g_free); - } - - g_clear_object(§ion); - g_object_unref(submenu); - } -}; - - -TEST_F(MenuFixture, HelloWorld) -{ - EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size()); - for (int i=0; imenu_model() != nullptr); - EXPECT_EQ(i, m_menus[i]->profile()); - } - EXPECT_EQ(m_menus[Menu::Desktop]->name(), "desktop"); -} - -TEST_F(MenuFixture, Header) -{ - for(auto& menu : m_menus) - InspectHeader(menu->menu_model(), menu->name()); -} - -TEST_F(MenuFixture, Sections) -{ - for(auto& menu : m_menus) - { - // check that the header has a submenu - auto menu_model = menu->menu_model(); - auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); - EXPECT_TRUE(submenu != nullptr); - EXPECT_EQ(Menu::NUM_SECTIONS, g_menu_model_get_n_items(submenu)); - g_object_unref(submenu); - } -} - -TEST_F(MenuFixture, Calendar) -{ - for(auto& menu : m_menus) - InspectCalendar(menu->menu_model(), menu->profile()); -} - -TEST_F(MenuFixture, Appointments) -{ - for(auto& menu : m_menus) - InspectAppointments(menu->menu_model(), menu->profile()); -} - -TEST_F(MenuFixture, Locations) -{ - for(auto& menu : m_menus) - InspectLocations(menu->menu_model(), menu->profile()); -} - -TEST_F(MenuFixture, Settings) -{ - for(auto& menu : m_menus) - InspectSettings(menu->menu_model(), menu->profile()); -} - - diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp new file mode 100644 index 0000000..fe1e86e --- /dev/null +++ b/tests/test-menus.cpp @@ -0,0 +1,402 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + + +#include "actions-mock.h" +#include "state-fixture.h" + +#include +#include +#include +#include + +#include + +using namespace unity::indicator::datetime; + +class MenuFixture: public StateFixture +{ +private: + typedef StateFixture super; + +protected: + std::shared_ptr m_menu_factory; + std::vector> m_menus; + + virtual void SetUp() + { + super::SetUp(); + + // build the menus on top of the actions and state + m_menu_factory.reset(new MenuFactory(m_actions, m_state)); + for(int i=0; ibuildMenu(Menu::Profile(i))); + } + + virtual void TearDown() + { + m_menus.clear(); + m_menu_factory.reset(); + + super::TearDown(); + } + + void InspectHeader(GMenuModel* menu_model, const std::string& name) + { + // check that there's a header menuitem + EXPECT_EQ(1,g_menu_model_get_n_items(menu_model)); + gchar* str = nullptr; + g_menu_model_get_item_attribute(menu_model, 0, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.root", str); + g_clear_pointer(&str, g_free); + g_menu_model_get_item_attribute(menu_model, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + const auto action_name = name + "-header"; + EXPECT_EQ(std::string("indicator.")+action_name, str); + g_clear_pointer(&str, g_free); + + // check the header + auto dict = g_action_group_get_action_state(m_actions->action_group(), action_name.c_str()); + EXPECT_TRUE(dict != nullptr); + EXPECT_TRUE(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT)); + auto v = g_variant_lookup_value(dict, "accessible-desc", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "label", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "title", G_VARIANT_TYPE_STRING); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + v = g_variant_lookup_value(dict, "visible", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE(v != nullptr); + g_variant_unref(v); + g_variant_unref(dict); + } + + void InspectCalendar(GMenuModel* menu_model, Menu::Profile profile) + { + gchar* str = nullptr; + const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); + const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); + + // get the calendar section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); + + // should be one or two items: a date label and maybe a calendar + ASSERT_TRUE(section != nullptr); + auto n_expected = calendar_expected ? 2 : 1; + EXPECT_EQ(n_expected, g_menu_model_get_n_items(section)); + + // look at the date menuitem + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); + const auto now = m_state->clock->localtime(); + EXPECT_EQ(now.format("%A, %e %B %Y"), str); + + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + if (actions_expected) + EXPECT_STREQ("indicator.activate-planner", str); + else + EXPECT_TRUE(str == nullptr); + g_clear_pointer(&str, g_free); + + // look at the calendar menuitem + if (calendar_expected) + { + g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.calendar", str); + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 1, G_MENU_ATTRIBUTE_ACTION, "s", &str); + EXPECT_STREQ("indicator.calendar", str); + g_clear_pointer(&str, g_free); + + g_menu_model_get_item_attribute(section, 1, "activation-action", "s", &str); + if (actions_expected) + EXPECT_STREQ("indicator.activate-planner", str); + else + EXPECT_TRUE(str == nullptr); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + + // now change the clock and see if the date label changes appropriately + + auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); + auto tomorrow = DateTime(gdt_tomorrow); + g_date_time_unref(gdt_tomorrow); + m_mock_state->mock_clock->set_localtime(tomorrow); + wait_msec(); + + section = g_menu_model_get_item_link(submenu, Menu::Calendar, G_MENU_LINK_SECTION); + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_LABEL, "s", &str); + EXPECT_EQ(tomorrow.format("%A, %e %B %Y"), str); + g_clear_pointer(&str, g_free); + g_clear_object(§ion); + + // cleanup + g_object_unref(submenu); + } + + void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) + { + const bool appointments_expected = (profile == Menu::Desktop) + || (profile == Menu::Phone); + + // get the Appointments section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + + // there shouldn't be any menuitems when "show events" is false + m_state->settings->show_events.set(false); + wait_msec(); + auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(0, g_menu_model_get_n_items(section)); + g_clear_object(§ion); + + // when "show_events" is true, + // there should be an "add event" button even if there aren't any appointments + std::vector appointments; + m_state->settings->show_events.set(true); + m_state->planner->upcoming.set(appointments); + wait_msec(); + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + int expected_n = appointments_expected ? 1 : 0; + EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + g_clear_object(§ion); + + // try adding a few appointments and see if the menu updates itself + + const auto now = m_state->clock->localtime(); + auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); + const auto tomorrow = DateTime(gdt_tomorrow); + g_date_time_unref(gdt_tomorrow); + + Appointment a1; // an alarm clock appointment + a1.color = "red"; + a1.summary = "Alarm"; + a1.summary = "http://www.example.com/"; + a1.uid = "example"; + a1.has_alarms = true; + a1.begin = a1.end = tomorrow; + appointments.push_back(a1); + + Appointment a2; // a non-alarm appointment + a2.color = "green"; + a2.summary = "Other Text"; + a2.summary = "http://www.monkey.com/"; + a2.uid = "monkey"; + a2.has_alarms = false; + a2.begin = a2.end = tomorrow; + appointments.push_back(a2); + + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + expected_n = appointments_expected ? 3 : 0; + EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + if (appointments_expected) + { + gchar * str = nullptr; + + // test the alarm + // - confirm it has an x-canonical-type of "alarm" + g_menu_model_get_item_attribute(section, 0, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.alarm", str); + g_clear_pointer(&str, g_free); + // - confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + // - confirm it has a serialized icon attribute + auto v = g_menu_model_get_item_attribute_value(section, 0, G_MENU_ATTRIBUTE_ICON, nullptr); + EXPECT_TRUE(v != nullptr); + auto icon = g_icon_deserialize(v); + EXPECT_TRUE(icon != nullptr); + g_clear_object(&icon); + g_clear_pointer(&v, g_variant_unref); + + // test the appointment + // - confirm it has an x-canonical-type of "appointment" + g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); + EXPECT_STREQ("com.canonical.indicator.appointment", str); + g_clear_pointer(&str, g_free); + // - confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + // - confirm its color matches the one we fed the appointments vector + g_menu_model_get_item_attribute(section, 1, "x-canonical-color", "s", &str); + EXPECT_EQ(a2.color, str); + g_clear_pointer(&str, g_free); + } + g_clear_object(§ion); + + g_object_unref(submenu); + } + + void CompareLocationsTo(GMenuModel* menu_model, const std::vector& locations) + { + // get the Locations section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Locations, G_MENU_LINK_SECTION); + + // confirm that section's menuitems mirror the "locations" vector + const auto n = locations.size(); + ASSERT_EQ(n, g_menu_model_get_n_items(section)); + for (guint i=0; i empty; + m_state->locations->locations.set(empty); + wait_msec(); + CompareLocationsTo(menu_model, empty); + + // add some locations and confirm the menu picked up our changes + Location l1 ("America/Chicago", "Dallas"); + Location l2 ("America/Arizona", "Phoenix"); + std::vector locations({l1, l2}); + m_state->locations->locations.set(locations); + wait_msec(); + CompareLocationsTo(menu_model, locations_expected ? locations : empty); + + // now remove one of the locations... + locations.pop_back(); + m_state->locations->locations.set(locations); + wait_msec(); + CompareLocationsTo(menu_model, locations_expected ? locations : empty); + } + + void InspectSettings(GMenuModel* menu_model, Menu::Profile profile) + { + std::string expected_action; + + if (profile == Menu::Desktop) + expected_action = "indicator.activate-desktop-settings"; + else if (profile == Menu::Phone) + expected_action = "indicator.activate-phone-settings"; + + // get the Settings section + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto section = g_menu_model_get_item_link(submenu, Menu::Settings, G_MENU_LINK_SECTION); + + if (expected_action.empty()) + { + EXPECT_EQ(0, g_menu_model_get_n_items(section)); + } + else + { + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* str = nullptr; + g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &str); + EXPECT_EQ(expected_action, str); + g_clear_pointer(&str, g_free); + } + + g_clear_object(§ion); + g_object_unref(submenu); + } +}; + + +TEST_F(MenuFixture, HelloWorld) +{ + EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size()); + for (int i=0; imenu_model() != nullptr); + EXPECT_EQ(i, m_menus[i]->profile()); + } + EXPECT_EQ(m_menus[Menu::Desktop]->name(), "desktop"); +} + +TEST_F(MenuFixture, Header) +{ + for(auto& menu : m_menus) + InspectHeader(menu->menu_model(), menu->name()); +} + +TEST_F(MenuFixture, Sections) +{ + for(auto& menu : m_menus) + { + // check that the header has a submenu + auto menu_model = menu->menu_model(); + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + EXPECT_TRUE(submenu != nullptr); + EXPECT_EQ(Menu::NUM_SECTIONS, g_menu_model_get_n_items(submenu)); + g_object_unref(submenu); + } +} + +TEST_F(MenuFixture, Calendar) +{ + for(auto& menu : m_menus) + InspectCalendar(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Appointments) +{ + for(auto& menu : m_menus) + InspectAppointments(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Locations) +{ + for(auto& menu : m_menus) + InspectLocations(menu->menu_model(), menu->profile()); +} + +TEST_F(MenuFixture, Settings) +{ + for(auto& menu : m_menus) + InspectSettings(menu->menu_model(), menu->profile()); +} + + diff --git a/tests/test-planner.cc b/tests/test-planner.cc deleted file mode 100644 index 3072aea..0000000 --- a/tests/test-planner.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "glib-fixture.h" - -#include -#include -#include -#include -#include - -#include -#include - -using unity::indicator::datetime::Appointment; -using unity::indicator::datetime::DateTime; -using unity::indicator::datetime::PlannerEds; - -/*** -**** -***/ - -typedef GlibFixture PlannerFixture; - -TEST_F(PlannerFixture, EDS) -{ - PlannerEds planner; - wait_msec(100); - - auto now = g_date_time_new_now_local(); - planner.time.set(DateTime(now)); - wait_msec(2500); - - std::vector thisMonth = planner.thisMonth.get(); - std::cerr << thisMonth.size() << " appointments this month" << std::endl; - for(const auto& a : thisMonth) - std::cerr << a.summary << std::endl; -} - - -TEST_F(PlannerFixture, HelloWorld) -{ - auto halloween = g_date_time_new_local(2020, 10, 31, 18, 30, 59); - auto christmas = g_date_time_new_local(2020, 12, 25, 0, 0, 0); - - Appointment a; - a.summary = "Test"; - a.begin = halloween; - a.end = g_date_time_add_hours(halloween, 1); - const Appointment b = a; - a.summary = "Foo"; - - EXPECT_EQ(a.summary, "Foo"); - EXPECT_EQ(b.summary, "Test"); - EXPECT_EQ(0, g_date_time_compare(a.begin(), b.begin())); - EXPECT_EQ(0, g_date_time_compare(a.end(), b.end())); - - Appointment c; - c.begin = christmas; - c.end = g_date_time_add_hours(christmas, 1); - Appointment d; - d = c; - EXPECT_EQ(0, g_date_time_compare(c.begin(), d.begin())); - EXPECT_EQ(0, g_date_time_compare(c.end(), d.end())); - a = d; - EXPECT_EQ(0, g_date_time_compare(d.begin(), a.begin())); - EXPECT_EQ(0, g_date_time_compare(d.end(), a.end())); -} - diff --git a/tests/test-planner.cpp b/tests/test-planner.cpp new file mode 100644 index 0000000..3072aea --- /dev/null +++ b/tests/test-planner.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include +#include +#include +#include +#include + +#include +#include + +using unity::indicator::datetime::Appointment; +using unity::indicator::datetime::DateTime; +using unity::indicator::datetime::PlannerEds; + +/*** +**** +***/ + +typedef GlibFixture PlannerFixture; + +TEST_F(PlannerFixture, EDS) +{ + PlannerEds planner; + wait_msec(100); + + auto now = g_date_time_new_now_local(); + planner.time.set(DateTime(now)); + wait_msec(2500); + + std::vector thisMonth = planner.thisMonth.get(); + std::cerr << thisMonth.size() << " appointments this month" << std::endl; + for(const auto& a : thisMonth) + std::cerr << a.summary << std::endl; +} + + +TEST_F(PlannerFixture, HelloWorld) +{ + auto halloween = g_date_time_new_local(2020, 10, 31, 18, 30, 59); + auto christmas = g_date_time_new_local(2020, 12, 25, 0, 0, 0); + + Appointment a; + a.summary = "Test"; + a.begin = halloween; + a.end = g_date_time_add_hours(halloween, 1); + const Appointment b = a; + a.summary = "Foo"; + + EXPECT_EQ(a.summary, "Foo"); + EXPECT_EQ(b.summary, "Test"); + EXPECT_EQ(0, g_date_time_compare(a.begin(), b.begin())); + EXPECT_EQ(0, g_date_time_compare(a.end(), b.end())); + + Appointment c; + c.begin = christmas; + c.end = g_date_time_add_hours(christmas, 1); + Appointment d; + d = c; + EXPECT_EQ(0, g_date_time_compare(c.begin(), d.begin())); + EXPECT_EQ(0, g_date_time_compare(c.end(), d.end())); + a = d; + EXPECT_EQ(0, g_date_time_compare(d.begin(), a.begin())); + EXPECT_EQ(0, g_date_time_compare(d.end(), a.end())); +} + diff --git a/tests/test-settings.cc b/tests/test-settings.cc deleted file mode 100644 index 9e52197..0000000 --- a/tests/test-settings.cc +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "glib-fixture.h" - -#include -#include - -using namespace unity::indicator::datetime; - -/*** -**** -***/ - -class SettingsFixture: public GlibFixture -{ -private: - typedef GlibFixture super; - -protected: - - std::shared_ptr m_live; - std::shared_ptr m_settings; - GSettings * m_gsettings; - - virtual void SetUp() - { - super::SetUp(); - - m_gsettings = g_settings_new(SETTINGS_INTERFACE); - m_live.reset(new LiveSettings); - m_settings = std::dynamic_pointer_cast(m_live); - } - - virtual void TearDown() - { - g_clear_object(&m_gsettings); - m_settings.reset(); - m_live.reset(); - - super::TearDown(); - } - - void TestBoolProperty(core::Property& property, const gchar* key) - { - EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); - g_settings_set_boolean(m_gsettings, key, false); - EXPECT_EQ(false, property.get()); - g_settings_set_boolean(m_gsettings, key, true); - EXPECT_EQ(true, property.get()); - - property.set(false); - EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); - property.set(true); - EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); - } - - void TestStringProperty(core::Property& property, const gchar* key) - { - gchar* tmp; - std::string str; - - tmp = g_settings_get_string(m_gsettings, key); - EXPECT_EQ(tmp, property.get()); - g_clear_pointer(&tmp, g_free); - - str = "a"; - g_settings_set_string(m_gsettings, key, str.c_str()); - EXPECT_EQ(str, property.get()); - - str = "b"; - g_settings_set_string(m_gsettings, key, str.c_str()); - EXPECT_EQ(str, property.get()); - - str = "a"; - property.set(str); - tmp = g_settings_get_string(m_gsettings, key); - EXPECT_EQ(str, tmp); - g_clear_pointer(&tmp, g_free); - - str = "b"; - property.set(str); - tmp = g_settings_get_string(m_gsettings, key); - EXPECT_EQ(str, tmp); - g_clear_pointer(&tmp, g_free); - } -}; - -/*** -**** -***/ - -TEST_F(SettingsFixture, HelloWorld) -{ - EXPECT_TRUE(true); -} - -TEST_F(SettingsFixture, BoolProperties) -{ - TestBoolProperty(m_settings->show_seconds, SETTINGS_SHOW_SECONDS_S); - TestBoolProperty(m_settings->show_calendar, SETTINGS_SHOW_CALENDAR_S); - TestBoolProperty(m_settings->show_clock, SETTINGS_SHOW_CLOCK_S); - TestBoolProperty(m_settings->show_date, SETTINGS_SHOW_DATE_S); - TestBoolProperty(m_settings->show_day, SETTINGS_SHOW_DAY_S); - TestBoolProperty(m_settings->show_detected_location, SETTINGS_SHOW_DETECTED_S); - TestBoolProperty(m_settings->show_events, SETTINGS_SHOW_EVENTS_S); - TestBoolProperty(m_settings->show_locations, SETTINGS_SHOW_LOCATIONS_S); - TestBoolProperty(m_settings->show_week_numbers, SETTINGS_SHOW_WEEK_NUMBERS_S); - TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); -} - -TEST_F(SettingsFixture, StringProperties) -{ - TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); - TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); -} - -TEST_F(SettingsFixture, TimeFormatMode) -{ - const auto key = SETTINGS_TIME_FORMAT_S; - const TimeFormatMode modes[] = { TIME_FORMAT_MODE_LOCALE_DEFAULT, - TIME_FORMAT_MODE_12_HOUR, - TIME_FORMAT_MODE_24_HOUR, - TIME_FORMAT_MODE_CUSTOM }; - - for(const auto& mode : modes) - { - g_settings_set_enum(m_gsettings, key, mode); - EXPECT_EQ(mode, m_settings->time_format_mode.get()); - } - - for(const auto& mode : modes) - { - m_settings->time_format_mode.set(mode); - EXPECT_EQ(mode, g_settings_get_enum(m_gsettings, key)); - } -} - -namespace -{ - std::vector strv_to_vector(const gchar** strv) - { - std::vector v; - for(int i=0; strv && strv[i]; i++) - v.push_back(strv[i]); - return v; - } -}; - -TEST_F(SettingsFixture, Locations) -{ - const auto key = SETTINGS_LOCATIONS_S; - - const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; - const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; - const std::vector av = strv_to_vector(astrv); - const std::vector bv = strv_to_vector(bstrv); - - g_settings_set_strv(m_gsettings, key, astrv); - EXPECT_EQ(av, m_settings->locations.get()); - g_settings_set_strv(m_gsettings, key, bstrv); - EXPECT_EQ(bv, m_settings->locations.get()); - - m_settings->locations.set(av); - auto tmp = g_settings_get_strv(m_gsettings, key); - auto vtmp = strv_to_vector((const gchar**)tmp); - g_strfreev(tmp); - EXPECT_EQ(av, vtmp); - - m_settings->locations.set(bv); - tmp = g_settings_get_strv(m_gsettings, key); - vtmp = strv_to_vector((const gchar**)tmp); - g_strfreev(tmp); - EXPECT_EQ(bv, vtmp); -} - -#if 0 - core::Property> locations; - core::Property time_format_mode; - -#endif diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp new file mode 100644 index 0000000..9e52197 --- /dev/null +++ b/tests/test-settings.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include +#include + +using namespace unity::indicator::datetime; + +/*** +**** +***/ + +class SettingsFixture: public GlibFixture +{ +private: + typedef GlibFixture super; + +protected: + + std::shared_ptr m_live; + std::shared_ptr m_settings; + GSettings * m_gsettings; + + virtual void SetUp() + { + super::SetUp(); + + m_gsettings = g_settings_new(SETTINGS_INTERFACE); + m_live.reset(new LiveSettings); + m_settings = std::dynamic_pointer_cast(m_live); + } + + virtual void TearDown() + { + g_clear_object(&m_gsettings); + m_settings.reset(); + m_live.reset(); + + super::TearDown(); + } + + void TestBoolProperty(core::Property& property, const gchar* key) + { + EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); + g_settings_set_boolean(m_gsettings, key, false); + EXPECT_EQ(false, property.get()); + g_settings_set_boolean(m_gsettings, key, true); + EXPECT_EQ(true, property.get()); + + property.set(false); + EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); + property.set(true); + EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); + } + + void TestStringProperty(core::Property& property, const gchar* key) + { + gchar* tmp; + std::string str; + + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(tmp, property.get()); + g_clear_pointer(&tmp, g_free); + + str = "a"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "b"; + g_settings_set_string(m_gsettings, key, str.c_str()); + EXPECT_EQ(str, property.get()); + + str = "a"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + + str = "b"; + property.set(str); + tmp = g_settings_get_string(m_gsettings, key); + EXPECT_EQ(str, tmp); + g_clear_pointer(&tmp, g_free); + } +}; + +/*** +**** +***/ + +TEST_F(SettingsFixture, HelloWorld) +{ + EXPECT_TRUE(true); +} + +TEST_F(SettingsFixture, BoolProperties) +{ + TestBoolProperty(m_settings->show_seconds, SETTINGS_SHOW_SECONDS_S); + TestBoolProperty(m_settings->show_calendar, SETTINGS_SHOW_CALENDAR_S); + TestBoolProperty(m_settings->show_clock, SETTINGS_SHOW_CLOCK_S); + TestBoolProperty(m_settings->show_date, SETTINGS_SHOW_DATE_S); + TestBoolProperty(m_settings->show_day, SETTINGS_SHOW_DAY_S); + TestBoolProperty(m_settings->show_detected_location, SETTINGS_SHOW_DETECTED_S); + TestBoolProperty(m_settings->show_events, SETTINGS_SHOW_EVENTS_S); + TestBoolProperty(m_settings->show_locations, SETTINGS_SHOW_LOCATIONS_S); + TestBoolProperty(m_settings->show_week_numbers, SETTINGS_SHOW_WEEK_NUMBERS_S); + TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); +} + +TEST_F(SettingsFixture, StringProperties) +{ + TestStringProperty(m_settings->custom_time_format, SETTINGS_CUSTOM_TIME_FORMAT_S); + TestStringProperty(m_settings->timezone_name, SETTINGS_TIMEZONE_NAME_S); +} + +TEST_F(SettingsFixture, TimeFormatMode) +{ + const auto key = SETTINGS_TIME_FORMAT_S; + const TimeFormatMode modes[] = { TIME_FORMAT_MODE_LOCALE_DEFAULT, + TIME_FORMAT_MODE_12_HOUR, + TIME_FORMAT_MODE_24_HOUR, + TIME_FORMAT_MODE_CUSTOM }; + + for(const auto& mode : modes) + { + g_settings_set_enum(m_gsettings, key, mode); + EXPECT_EQ(mode, m_settings->time_format_mode.get()); + } + + for(const auto& mode : modes) + { + m_settings->time_format_mode.set(mode); + EXPECT_EQ(mode, g_settings_get_enum(m_gsettings, key)); + } +} + +namespace +{ + std::vector strv_to_vector(const gchar** strv) + { + std::vector v; + for(int i=0; strv && strv[i]; i++) + v.push_back(strv[i]); + return v; + } +}; + +TEST_F(SettingsFixture, Locations) +{ + const auto key = SETTINGS_LOCATIONS_S; + + const gchar* astrv[] = {"America/Los_Angeles Oakland", "America/Chicago Oklahoma City", "Europe/London London", NULL}; + const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", NULL}; + const std::vector av = strv_to_vector(astrv); + const std::vector bv = strv_to_vector(bstrv); + + g_settings_set_strv(m_gsettings, key, astrv); + EXPECT_EQ(av, m_settings->locations.get()); + g_settings_set_strv(m_gsettings, key, bstrv); + EXPECT_EQ(bv, m_settings->locations.get()); + + m_settings->locations.set(av); + auto tmp = g_settings_get_strv(m_gsettings, key); + auto vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(av, vtmp); + + m_settings->locations.set(bv); + tmp = g_settings_get_strv(m_gsettings, key); + vtmp = strv_to_vector((const gchar**)tmp); + g_strfreev(tmp); + EXPECT_EQ(bv, vtmp); +} + +#if 0 + core::Property> locations; + core::Property time_format_mode; + +#endif diff --git a/tests/test-timezone-file.cc b/tests/test-timezone-file.cc deleted file mode 100644 index 453b353..0000000 --- a/tests/test-timezone-file.cc +++ /dev/null @@ -1,133 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "glib-fixture.h" - -#include - -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include - -#include // fopen() -//#include // chmod() -#include // sync() - -using unity::indicator::datetime::FileTimezone; - - -/*** -**** -***/ - -#define TIMEZONE_FILE (SANDBOX"/timezone") - -class TimezoneFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - - protected: - - virtual void SetUp() - { - super::SetUp(); - } - - virtual void TearDown() - { - super::TearDown(); - } - - public: - - /* convenience func to set the timezone file */ - void set_file(const std::string& text) - { - auto fp = fopen(TIMEZONE_FILE, "w+"); - fprintf(fp, "%s\n", text.c_str()); - fclose(fp); - sync(); - } -}; - - -/** - * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist - */ -TEST_F(TimezoneFixture, NoFile) -{ - remove(TIMEZONE_FILE); - ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); - - FileTimezone tz(TIMEZONE_FILE); - testLogCount(G_LOG_LEVEL_WARNING, 1); -} - - -/** - * Test that timezone-file picks up the initial value - */ -TEST_F(TimezoneFixture, InitialValue) -{ - const std::string expected_timezone = "America/Chicago"; - set_file(expected_timezone); - FileTimezone tz(TIMEZONE_FILE); - ASSERT_EQ(expected_timezone, tz.timezone.get()); -} - - -/** - * Test that clearing the timezone results in an empty string - */ -TEST_F(TimezoneFixture, ChangedValue) -{ - const std::string initial_timezone = "America/Chicago"; - const std::string changed_timezone = "America/New_York"; - set_file(initial_timezone); - - FileTimezone tz(TIMEZONE_FILE); - ASSERT_EQ(initial_timezone, tz.timezone.get()); - - bool changed = false; - auto connection = tz.timezone.changed().connect( - [&changed, this](const std::string& s){ - g_message("timezone changed to %s", s.c_str()); - changed = true; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer gself){ - static_cast(gself)->set_file("America/New_York"); - // static_cast(gtz)->timezone.set("America/New_York"); - return G_SOURCE_REMOVE; - }, this);//&tz); - - g_main_loop_run(loop); - - ASSERT_TRUE(changed); - ASSERT_EQ(changed_timezone, tz.timezone.get()); -} diff --git a/tests/test-timezone-file.cpp b/tests/test-timezone-file.cpp new file mode 100644 index 0000000..453b353 --- /dev/null +++ b/tests/test-timezone-file.cpp @@ -0,0 +1,133 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "glib-fixture.h" + +#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include // fopen() +//#include // chmod() +#include // sync() + +using unity::indicator::datetime::FileTimezone; + + +/*** +**** +***/ + +#define TIMEZONE_FILE (SANDBOX"/timezone") + +class TimezoneFixture: public GlibFixture +{ + private: + + typedef GlibFixture super; + + protected: + + virtual void SetUp() + { + super::SetUp(); + } + + virtual void TearDown() + { + super::TearDown(); + } + + public: + + /* convenience func to set the timezone file */ + void set_file(const std::string& text) + { + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); + } +}; + + +/** + * Test that timezone-file warns, but doesn't crash, if the timezone file doesn't exist + */ +TEST_F(TimezoneFixture, NoFile) +{ + remove(TIMEZONE_FILE); + ASSERT_FALSE(g_file_test(TIMEZONE_FILE, G_FILE_TEST_EXISTS)); + + FileTimezone tz(TIMEZONE_FILE); + testLogCount(G_LOG_LEVEL_WARNING, 1); +} + + +/** + * Test that timezone-file picks up the initial value + */ +TEST_F(TimezoneFixture, InitialValue) +{ + const std::string expected_timezone = "America/Chicago"; + set_file(expected_timezone); + FileTimezone tz(TIMEZONE_FILE); + ASSERT_EQ(expected_timezone, tz.timezone.get()); +} + + +/** + * Test that clearing the timezone results in an empty string + */ +TEST_F(TimezoneFixture, ChangedValue) +{ + const std::string initial_timezone = "America/Chicago"; + const std::string changed_timezone = "America/New_York"; + set_file(initial_timezone); + + FileTimezone tz(TIMEZONE_FILE); + ASSERT_EQ(initial_timezone, tz.timezone.get()); + + bool changed = false; + auto connection = tz.timezone.changed().connect( + [&changed, this](const std::string& s){ + g_message("timezone changed to %s", s.c_str()); + changed = true; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer gself){ + static_cast(gself)->set_file("America/New_York"); + // static_cast(gtz)->timezone.set("America/New_York"); + return G_SOURCE_REMOVE; + }, this);//&tz); + + g_main_loop_run(loop); + + ASSERT_TRUE(changed); + ASSERT_EQ(changed_timezone, tz.timezone.get()); +} diff --git a/tests/test-timezone-geoclue.cc b/tests/test-timezone-geoclue.cc deleted file mode 100644 index 4bf08a7..0000000 --- a/tests/test-timezone-geoclue.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "geoclue-fixture.h" - -#include - -//#include - -using unity::indicator::datetime::GeoclueTimezone; - -/*** -**** -***/ - -class TimezoneGeoclueFixture : public GeoclueFixture -{ -}; - -#if 0 -namespace -{ - struct EmitAddressChangedData - { - DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; - std::string timezone; - EmitAddressChangedData(DbusTestDbusMock * mock_, - DbusTestDbusMockObject * obj_client_, - const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} - }; - - gboolean emit_address_changed_idle(gpointer gdata) - { - auto data = static_cast(gdata); - - GError * error = nullptr; - dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, - "org.freedesktop.Geoclue.Address", - "AddressChanged", - G_VARIANT_TYPE("(ia{ss}(idd))"), - g_variant_new_parsed("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), - &error); - if (error) - { - g_warning("%s: %s", G_STRFUNC, error->message); - g_error_free(error); - } - - delete data; - return G_SOURCE_REMOVE; - } -} -#endif - -TEST_F(TimezoneGeoclueFixture, ChangeDetected) -{ -// const std::string timezone_1 = "America/Denver"; - const std::string timezone_2 = "America/Chicago"; - - GeoclueTimezone tz; - wait_msec(500); // wait for the bus to get set up - EXPECT_EQ(timezone_1, tz.timezone.get()); - - // start listening for a timezone change, then change the timezone - - bool changed = false; - auto connection = tz.timezone.changed().connect( - [&changed, this](const std::string& s){ - g_debug("timezone changed to %s", s.c_str()); - changed = true; - g_main_loop_quit(loop); - }); - - setGeoclueTimezoneOnIdle(timezone_2); - //g_timeout_add(50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); - g_main_loop_run(loop); - EXPECT_EQ(timezone_2, tz.timezone.get()); -} - - diff --git a/tests/test-timezone-geoclue.cpp b/tests/test-timezone-geoclue.cpp new file mode 100644 index 0000000..4bf08a7 --- /dev/null +++ b/tests/test-timezone-geoclue.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "geoclue-fixture.h" + +#include + +//#include + +using unity::indicator::datetime::GeoclueTimezone; + +/*** +**** +***/ + +class TimezoneGeoclueFixture : public GeoclueFixture +{ +}; + +#if 0 +namespace +{ + struct EmitAddressChangedData + { + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj_client = nullptr; + std::string timezone; + EmitAddressChangedData(DbusTestDbusMock * mock_, + DbusTestDbusMockObject * obj_client_, + const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} + }; + + gboolean emit_address_changed_idle(gpointer gdata) + { + auto data = static_cast(gdata); + + GError * error = nullptr; + dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, + "org.freedesktop.Geoclue.Address", + "AddressChanged", + G_VARIANT_TYPE("(ia{ss}(idd))"), + g_variant_new_parsed("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), + &error); + if (error) + { + g_warning("%s: %s", G_STRFUNC, error->message); + g_error_free(error); + } + + delete data; + return G_SOURCE_REMOVE; + } +} +#endif + +TEST_F(TimezoneGeoclueFixture, ChangeDetected) +{ +// const std::string timezone_1 = "America/Denver"; + const std::string timezone_2 = "America/Chicago"; + + GeoclueTimezone tz; + wait_msec(500); // wait for the bus to get set up + EXPECT_EQ(timezone_1, tz.timezone.get()); + + // start listening for a timezone change, then change the timezone + + bool changed = false; + auto connection = tz.timezone.changed().connect( + [&changed, this](const std::string& s){ + g_debug("timezone changed to %s", s.c_str()); + changed = true; + g_main_loop_quit(loop); + }); + + setGeoclueTimezoneOnIdle(timezone_2); + //g_timeout_add(50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); + g_main_loop_run(loop); + EXPECT_EQ(timezone_2, tz.timezone.get()); +} + + diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc deleted file mode 100644 index 3f02761..0000000 --- a/tests/test-timezones.cc +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include "geoclue-fixture.h" - -#include -#include - -#include // std::shared_ptr - -#include // fopen() -#include // sync() - -using namespace unity::indicator::datetime; - -typedef GeoclueFixture TimezonesFixture; - -#define TIMEZONE_FILE (SANDBOX "/timezone") - -namespace -{ - /* convenience func to set the timezone file */ - void set_file(const std::string& text) - { - auto fp = fopen(TIMEZONE_FILE, "w+"); - fprintf(fp, "%s\n", text.c_str()); - fclose(fp); - sync(); - } -} - - -TEST_F(TimezonesFixture, ManagerTest) -{ - std::string timezone_file = "America/New_York"; - std::string timezone_geo = "America/Denver"; - - set_file(timezone_file); - std::shared_ptr settings(new Settings); - LiveTimezones z(settings, TIMEZONE_FILE); - wait_msec(500); // wait for the bus to get set up - EXPECT_EQ(timezone_file, z.timezone.get()); - auto zones = z.timezones.get(); - //std::set zones = z.timezones.get(); - EXPECT_EQ(1, zones.size()); - EXPECT_EQ(1, zones.count(timezone_file)); - - bool zone_changed = false; - auto zone_connection = z.timezone.changed().connect([&zone_changed, this](const std::string&) { - zone_changed = true; - g_main_loop_quit(loop); - }); - - // start listening for a timezone change, then change the timezone - bool zones_changed = false; - auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { - zones_changed = true; - zones = timezones; - g_main_loop_quit(loop); - }); - - g_idle_add([](gpointer s_in) { - auto s = static_cast(s_in); - g_message("geolocation was %d", (int)s->show_detected_location.get()); - g_message("turning geolocation on"); - s->show_detected_location.set(true); - return G_SOURCE_REMOVE; - }, settings.get()); - - // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property - g_main_loop_run(loop); - EXPECT_TRUE(zones_changed); - EXPECT_EQ(timezone_file, z.timezone.get()); - EXPECT_EQ(2, zones.size()); - EXPECT_EQ(1, zones.count(timezone_file)); - EXPECT_EQ(1, zones.count(timezone_geo)); - zones_changed = false; - - // now tweak the geoclue value... the geoclue-detected timezone should change, - // causing the 'timezones' property to change - zone_changed = false; - zones_changed = false; - timezone_geo = "America/Chicago"; - setGeoclueTimezoneOnIdle(timezone_geo); - g_main_loop_run(loop); - EXPECT_FALSE(zone_changed); - EXPECT_TRUE(zones_changed); - EXPECT_EQ(timezone_file, z.timezone.get()); - EXPECT_EQ(2, zones.size()); - EXPECT_EQ(1, zones.count(timezone_file)); - EXPECT_EQ(1, zones.count(timezone_geo)); - - // now set the file value... this should change both the primary property and set property - zone_changed = false; - zones_changed = false; - timezone_file = "America/Los_Angeles"; - EXPECT_EQ(0, zones.count(timezone_file)); - g_idle_add([](gpointer str) {set_file(static_cast(str)); return G_SOURCE_REMOVE;}, const_cast(timezone_file.c_str())); - g_main_loop_run(loop); - EXPECT_TRUE(zone_changed); - EXPECT_TRUE(zones_changed); - EXPECT_EQ(timezone_file, z.timezone.get()); - EXPECT_EQ(2, zones.size()); - EXPECT_EQ(1, zones.count(timezone_file)); - EXPECT_EQ(1, zones.count(timezone_geo)); -} - - diff --git a/tests/test-timezones.cpp b/tests/test-timezones.cpp new file mode 100644 index 0000000..3f02761 --- /dev/null +++ b/tests/test-timezones.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include "geoclue-fixture.h" + +#include +#include + +#include // std::shared_ptr + +#include // fopen() +#include // sync() + +using namespace unity::indicator::datetime; + +typedef GeoclueFixture TimezonesFixture; + +#define TIMEZONE_FILE (SANDBOX "/timezone") + +namespace +{ + /* convenience func to set the timezone file */ + void set_file(const std::string& text) + { + auto fp = fopen(TIMEZONE_FILE, "w+"); + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); + sync(); + } +} + + +TEST_F(TimezonesFixture, ManagerTest) +{ + std::string timezone_file = "America/New_York"; + std::string timezone_geo = "America/Denver"; + + set_file(timezone_file); + std::shared_ptr settings(new Settings); + LiveTimezones z(settings, TIMEZONE_FILE); + wait_msec(500); // wait for the bus to get set up + EXPECT_EQ(timezone_file, z.timezone.get()); + auto zones = z.timezones.get(); + //std::set zones = z.timezones.get(); + EXPECT_EQ(1, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + + bool zone_changed = false; + auto zone_connection = z.timezone.changed().connect([&zone_changed, this](const std::string&) { + zone_changed = true; + g_main_loop_quit(loop); + }); + + // start listening for a timezone change, then change the timezone + bool zones_changed = false; + auto zones_connection = z.timezones.changed().connect([&zones_changed, &zones, this](const std::set& timezones) { + zones_changed = true; + zones = timezones; + g_main_loop_quit(loop); + }); + + g_idle_add([](gpointer s_in) { + auto s = static_cast(s_in); + g_message("geolocation was %d", (int)s->show_detected_location.get()); + g_message("turning geolocation on"); + s->show_detected_location.set(true); + return G_SOURCE_REMOVE; + }, settings.get()); + + // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property + g_main_loop_run(loop); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); + zones_changed = false; + + // now tweak the geoclue value... the geoclue-detected timezone should change, + // causing the 'timezones' property to change + zone_changed = false; + zones_changed = false; + timezone_geo = "America/Chicago"; + setGeoclueTimezoneOnIdle(timezone_geo); + g_main_loop_run(loop); + EXPECT_FALSE(zone_changed); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); + + // now set the file value... this should change both the primary property and set property + zone_changed = false; + zones_changed = false; + timezone_file = "America/Los_Angeles"; + EXPECT_EQ(0, zones.count(timezone_file)); + g_idle_add([](gpointer str) {set_file(static_cast(str)); return G_SOURCE_REMOVE;}, const_cast(timezone_file.c_str())); + g_main_loop_run(loop); + EXPECT_TRUE(zone_changed); + EXPECT_TRUE(zones_changed); + EXPECT_EQ(timezone_file, z.timezone.get()); + EXPECT_EQ(2, zones.size()); + EXPECT_EQ(1, zones.count(timezone_file)); + EXPECT_EQ(1, zones.count(timezone_geo)); +} + + diff --git a/tests/test-utils.cc b/tests/test-utils.cc deleted file mode 100644 index 2fe6a2e..0000000 --- a/tests/test-utils.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#include - -#include - -TEST(UtilsTest, SplitSettingsLocation) -{ - struct { - const char* location; - const char* expected_zone; - const char* expected_name; - } test_cases[] = { - { "America/Chicago Chicago", "America/Chicago", "Chicago" }, - { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" }, - { "America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, - { "America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, - { " America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, - { " America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, - { "UTC UTC", "UTC", "UTC" } - }; - - for(const auto& test_case : test_cases) - { - char * zone = nullptr; - char * name = nullptr; - - split_settings_location(test_case.location, &zone, &name); - ASSERT_STREQ(test_case.expected_zone, zone); - ASSERT_STREQ(test_case.expected_name, name); - - g_free(zone); - g_free(name); - } -} - -TEST(UtilsTest, BeautifulTimezoneName) -{ - struct { - const char* timezone; - const char* location; - const char* expected_name; - } test_cases[] = { - { "America/Chicago", NULL, "Chicago" }, - { "America/Chicago", "America/Chicago", "Chicago" }, - { "America/Chicago", "America/Chigago Chicago", "Chicago" }, - { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, - { "America/Chicago", "Europe/London London", "Chicago" } - }; - - for(const auto& test_case : test_cases) - { - auto name = get_beautified_timezone_name(test_case.timezone, test_case.location); - EXPECT_STREQ(test_case.expected_name, name); - g_free(name); - } -} diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp new file mode 100644 index 0000000..2fe6a2e --- /dev/null +++ b/tests/test-utils.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +TEST(UtilsTest, SplitSettingsLocation) +{ + struct { + const char* location; + const char* expected_zone; + const char* expected_name; + } test_cases[] = { + { "America/Chicago Chicago", "America/Chicago", "Chicago" }, + { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" }, + { "America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, + { "America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, + { " America/Los_Angeles", "America/Los_Angeles", "Los Angeles" }, + { " America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" }, + { "UTC UTC", "UTC", "UTC" } + }; + + for(const auto& test_case : test_cases) + { + char * zone = nullptr; + char * name = nullptr; + + split_settings_location(test_case.location, &zone, &name); + ASSERT_STREQ(test_case.expected_zone, zone); + ASSERT_STREQ(test_case.expected_name, name); + + g_free(zone); + g_free(name); + } +} + +TEST(UtilsTest, BeautifulTimezoneName) +{ + struct { + const char* timezone; + const char* location; + const char* expected_name; + } test_cases[] = { + { "America/Chicago", NULL, "Chicago" }, + { "America/Chicago", "America/Chicago", "Chicago" }, + { "America/Chicago", "America/Chigago Chicago", "Chicago" }, + { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, + { "America/Chicago", "Europe/London London", "Chicago" } + }; + + for(const auto& test_case : test_cases) + { + auto name = get_beautified_timezone_name(test_case.timezone, test_case.location); + EXPECT_STREQ(test_case.expected_name, name); + g_free(name); + } +} -- cgit v1.2.3 From 572377478193fab7189e817f08487a17543ee32b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 09:16:32 -0600 Subject: silence a minor compiler warning in test-settings --- tests/test-settings.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp index 9e52197..df01cd7 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -61,14 +61,14 @@ protected: { EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get()); g_settings_set_boolean(m_gsettings, key, false); - EXPECT_EQ(false, property.get()); + EXPECT_FALSE(property.get()); g_settings_set_boolean(m_gsettings, key, true); - EXPECT_EQ(true, property.get()); + EXPECT_TRUE(property.get()); property.set(false); - EXPECT_EQ(false, g_settings_get_boolean(m_gsettings, key)); + EXPECT_FALSE(g_settings_get_boolean(m_gsettings, key)); property.set(true); - EXPECT_EQ(true, g_settings_get_boolean(m_gsettings, key)); + EXPECT_TRUE(g_settings_get_boolean(m_gsettings, key)); } void TestStringProperty(core::Property& property, const gchar* key) -- cgit v1.2.3 From 2db8f6e7c4b7148377800400c72bb2e59b793d3a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:01:20 -0600 Subject: remove the State.timezones property. Timezones is a helper class for LiveClock and doesn't need to be public in State. --- include/datetime/state.h | 11 ++++++++++- src/state-live.cpp | 1 - tests/state-mock.h | 3 --- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/include/datetime/state.h b/include/datetime/state.h index e735b6f..b14908e 100644 --- a/include/datetime/state.h +++ b/include/datetime/state.h @@ -52,11 +52,20 @@ namespace datetime { */ struct State { + /** \brief The current time. Used by the header, by the date menuitem, + and by the locations for relative timestamp */ std::shared_ptr clock; + + /** \brief The locations to be displayed in the Locations + section of the #Menu */ std::shared_ptr locations; + + /** \brief The appointments to be displayed in the Calendar and + Appointments sections of the #Menu */ std::shared_ptr planner; + + /** \brief Configuration options that modify the view */ std::shared_ptr settings; - std::shared_ptr timezones; core::Property calendar_day; }; diff --git a/src/state-live.cpp b/src/state-live.cpp index f4690b3..8ee663b 100644 --- a/src/state-live.cpp +++ b/src/state-live.cpp @@ -41,7 +41,6 @@ LiveState::LiveState() std::shared_ptr live_clock(new LiveClock(live_timezones)); settings = live_settings; - timezones = live_timezones; clock = live_clock; locations.reset(new SettingsLocations(live_settings, live_timezones)); planner.reset(new PlannerEds); diff --git a/tests/state-mock.h b/tests/state-mock.h index 2104aa0..f2e58e6 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -18,10 +18,8 @@ */ #include -#include #include #include -#include using namespace unity::indicator::datetime; @@ -35,7 +33,6 @@ public: const DateTime now = DateTime::NowLocal(); mock_clock.reset(new MockClock(now)); settings.reset(new Settings); - timezones.reset(new Timezones); clock = std::dynamic_pointer_cast(mock_clock); planner.reset(new MockPlanner); planner->time = now; -- cgit v1.2.3 From 063ff0ef0a7761e713b7b36e220e0e2bfa442326 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:14:56 -0600 Subject: sync test-exporter with the greeter profile's header names that changed in r325 --- tests/test-exporter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index ea62cd3..aa9e89d 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -115,9 +115,9 @@ TEST_F(ExporterFixture, Publish) EXPECT_EQ(1, names.count("activate-phone-settings")); EXPECT_EQ(1, names.count("activate-planner")); EXPECT_EQ(1, names.count("calendar")); - EXPECT_EQ(1, names.count("desktop-greeter-header")); + EXPECT_EQ(1, names.count("desktop_greeter-header")); EXPECT_EQ(1, names.count("desktop-header")); - EXPECT_EQ(1, names.count("phone-greeter-header")); + EXPECT_EQ(1, names.count("phone_greeter-header")); EXPECT_EQ(1, names.count("phone-header")); EXPECT_EQ(1, names.count("set-location")); -- cgit v1.2.3 From 08b1cfd6bc97cae18b916d97a03781986e7421fe Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 10:15:56 -0600 Subject: move planner-mock.h from include/datetime/ to tests/ because it's only needed by the tests. --- include/datetime/planner-mock.h | 44 ---------------------------------- tests/planner-mock.h | 52 +++++++++++++++-------------------------- tests/state-mock.h | 3 ++- 3 files changed, 21 insertions(+), 78 deletions(-) delete mode 100644 include/datetime/planner-mock.h (limited to 'tests') diff --git a/include/datetime/planner-mock.h b/include/datetime/planner-mock.h deleted file mode 100644 index 44d30c7..0000000 --- a/include/datetime/planner-mock.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_DATETIME_PLANNER_MOCK_H -#define INDICATOR_DATETIME_PLANNER_MOCK_H - -#include - -namespace unity { -namespace indicator { -namespace datetime { - -/** - * \brief Planner which does nothing on its own. - * It requires its client must set its appointments property. - */ -class MockPlanner: public Planner -{ -public: - MockPlanner() =default; - virtual ~MockPlanner() =default; -}; - -} // namespace datetime -} // namespace indicator -} // namespace unity - -#endif // INDICATOR_DATETIME_PLANNER_MOCK_H diff --git a/tests/planner-mock.h b/tests/planner-mock.h index 8d7d7c2..44d30c7 100644 --- a/tests/planner-mock.h +++ b/tests/planner-mock.h @@ -1,9 +1,6 @@ /* * Copyright 2013 Canonical Ltd. * - * Authors: - * Charles Kerr - * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. @@ -15,44 +12,33 @@ * * You should have received a copy of the GNU General Public License along * with this program. If not, see . + * + * Authors: + * Charles Kerr */ -#ifndef __INDICATOR_DATETIME_PLANNER_MOCK__H__ -#define __INDICATOR_DATETIME_PLANNER_MOCK__H__ - -#include "planner.h" /* parent class */ +#ifndef INDICATOR_DATETIME_PLANNER_MOCK_H +#define INDICATOR_DATETIME_PLANNER_MOCK_H -G_BEGIN_DECLS +#include -#define INDICATOR_TYPE_DATETIME_PLANNER_MOCK (indicator_datetime_planner_mock_get_type()) -#define INDICATOR_DATETIME_PLANNER_MOCK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK, IndicatorDatetimePlannerMock)) -#define INDICATOR_DATETIME_PLANNER_MOCK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK, IndicatorDatetimePlannerMockClass)) -#define INDICATOR_IS_DATETIME_PLANNER_MOCK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK)) - -typedef struct _IndicatorDatetimePlannerMock IndicatorDatetimePlannerMock; -typedef struct _IndicatorDatetimePlannerMockPriv IndicatorDatetimePlannerMockPriv; -typedef struct _IndicatorDatetimePlannerMockClass IndicatorDatetimePlannerMockClass; - -GType indicator_datetime_planner_mock_get_type (void); +namespace unity { +namespace indicator { +namespace datetime { /** - * An IndicatorDatetimePlanner which uses Evolution Data Server - * to get its list of appointments. + * \brief Planner which does nothing on its own. + * It requires its client must set its appointments property. */ -struct _IndicatorDatetimePlannerMock -{ - /*< private >*/ - IndicatorDatetimePlanner parent; - IndicatorDatetimePlannerMockPriv * priv; -}; - -struct _IndicatorDatetimePlannerMockClass +class MockPlanner: public Planner { - IndicatorDatetimePlannerClass parent_class; +public: + MockPlanner() =default; + virtual ~MockPlanner() =default; }; -IndicatorDatetimePlanner * indicator_datetime_planner_mock_new (void); - -G_END_DECLS +} // namespace datetime +} // namespace indicator +} // namespace unity -#endif /* __INDICATOR_DATETIME_PLANNER_MOCK__H__ */ +#endif // INDICATOR_DATETIME_PLANNER_MOCK_H diff --git a/tests/state-mock.h b/tests/state-mock.h index f2e58e6..64ab08b 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -17,8 +17,9 @@ * Charles Kerr */ +#include "planner-mock.h" + #include -#include #include using namespace unity::indicator::datetime; -- cgit v1.2.3 From aad7e86a109aeec75b3772cda20478363f966745 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 22 Jan 2014 14:28:20 -0600 Subject: Alarms is going to need to know when the clock's minute changes. We already have a timer for that in Formatter, so move it from there to Clock and add a corresponding public signal Clock.minuteChanged that both Formatter and Alarms can use. Sync unit tests. --- include/datetime/clock-mock.h | 8 +-- include/datetime/clock.h | 20 ++++---- include/datetime/date-time.h | 5 +- src/clock-live.cpp | 113 ++++++++++++++++++++++++------------------ src/clock.cpp | 2 +- src/date-time.cpp | 65 ++++++++++-------------- src/formatter.cpp | 106 ++++++++++++++++++--------------------- tests/test-clock.cpp | 32 ++++++------ 8 files changed, 169 insertions(+), 182 deletions(-) (limited to 'tests') diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index 19a859b..27926ff 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -42,11 +42,11 @@ public: DateTime localtime() const { return m_localtime; } void set_localtime(const DateTime& dt) { - const auto old_day = m_localtime.day_of_year(); + const auto old = m_localtime; m_localtime = dt; - skewDetected(); - const auto new_day = m_localtime.day_of_year(); - if (old_day != new_day) + if (!DateTime::is_same_minute(old, m_localtime)) + minuteChanged(); + if (!DateTime::is_same_day(old, m_localtime)) dateChanged(); } diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 9e87082..b3e3538 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -35,21 +35,25 @@ namespace datetime { /** * \brief A clock. - * - * Provides a signal to notify when clock skew is detected, such as - * when the timezone changes or when the system resumes from sleep. */ class Clock { public: virtual ~Clock(); virtual DateTime localtime() const =0; - core::Signal<> skewDetected; + + /** \brief A signal which fires when the clock's minute changes */ + core::Signal<> minuteChanged; + + /** \brief A signal which fires when the clock's date changes */ core::Signal<> dateChanged; protected: Clock(); + /** \brief Compares old and new times, emits minuteChanged() or dateChanged() signals if appropriate */ + void maybe_emit (const DateTime& a, const DateTime& b); + private: static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); @@ -70,12 +74,7 @@ private: class Timezones; /** - * \brief A live clock that provides the actual system time. - * - * This subclass also adds another clock skew detection test: - * it wakes up every skewTestIntervalSec seconds to see how - * much time has passed since the last wakeup. If the answer - * isn't what it expected, the skewDetected signal is triggered. + * \brief A live #Clock that provides the actual system time. */ class LiveClock: public Clock { @@ -83,7 +82,6 @@ public: LiveClock (const std::shared_ptr& zones); virtual ~LiveClock(); virtual DateTime localtime() const; - core::Property skewTestIntervalSec; private: class Impl; diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index 145e34e..c2429eb 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -49,13 +49,14 @@ public: std::string format(const std::string& fmt) const; int day_of_month() const; int64_t to_unix() const; - int day_of_year() const; - gint64 difference(const DateTime& that) const; bool operator<(const DateTime& that) const; bool operator!=(const DateTime& that) const; bool operator==(const DateTime& that) const; + static bool is_same_day(const DateTime& a, const DateTime& b); + static bool is_same_minute(const DateTime& a, const DateTime& b); + private: std::shared_ptr m_dt; }; diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 25623ae..69ebda7 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -24,6 +24,37 @@ namespace unity { namespace indicator { namespace datetime { +/*** +**** +***/ + +namespace +{ + +void clearTimer(guint& tag) +{ + if (tag) + { + g_source_remove(tag); + tag = 0; + } +} + +guint calculate_milliseconds_until_next_minute(const DateTime& now) +{ + auto next = g_date_time_add_minutes(now.get(), 1); + auto start_of_next = g_date_time_add_seconds (next, -g_date_time_get_seconds(next)); + const auto interval_usec = g_date_time_difference(start_of_next, now.get()); + const guint interval_msec = (interval_usec + 999) / 1000; + g_date_time_unref(start_of_next); + g_date_time_unref(next); + g_assert (interval_msec <= 60000); + return interval_msec; +} + +} // unnamed namespace + + class LiveClock::Impl { public: @@ -38,13 +69,12 @@ public: setTimezone(m_timezones->timezone.get()); } - m_owner.skewTestIntervalSec.changed().connect([this](unsigned int intervalSec) {setInterval(intervalSec);}); - setInterval(m_owner.skewTestIntervalSec.get()); + restart_minute_timer(); } ~Impl() { - clearTimer(); + clearTimer(m_timer); g_clear_pointer(&m_timezone, g_time_zone_unref); } @@ -64,70 +94,55 @@ private: void setTimezone(const std::string& str) { g_clear_pointer(&m_timezone, g_time_zone_unref); - m_timezone= g_time_zone_new(str.c_str()); - m_owner.skewDetected(); - } - -private: - - void clearTimer() - { - if (m_skew_timeout_id) - { - g_source_remove(m_skew_timeout_id); - m_skew_timeout_id = 0; - } - - m_prev_datetime.reset(); + m_timezone = g_time_zone_new(str.c_str()); + m_owner.minuteChanged(); } - void setInterval(unsigned int seconds) - { - clearTimer(); - - if (seconds > 0) - { - m_prev_datetime = localtime(); - m_skew_timeout_id = g_timeout_add_seconds(seconds, onTimerPulse, this); - } - } + /*** + **** + ***/ - static gboolean onTimerPulse(gpointer gself) + void restart_minute_timer() { - static_cast(gself)->onTimerPulse(); - return G_SOURCE_CONTINUE; - } + clearTimer(m_timer); - void onTimerPulse() - { - // check to see if too much time passed since the last check */ + // maybe emit change signals const auto now = localtime(); - const auto diff = now.difference (m_prev_datetime); - const GTimeSpan fuzz = 5; - const GTimeSpan max = (m_owner.skewTestIntervalSec.get() + fuzz) * G_USEC_PER_SEC; - if (abs(diff) > max) - m_owner.skewDetected(); - - // check to see if the day has changed - if (now.day_of_year() != m_prev_datetime.day_of_year()) + if (!DateTime::is_same_minute(m_prev_datetime, now)) + m_owner.minuteChanged(); + if (!DateTime::is_same_day(m_prev_datetime, now)) m_owner.dateChanged(); - // update m_prev_datetime + // queue up a timer to fire at the next minute m_prev_datetime = now; + auto interval_msec = calculate_milliseconds_until_next_minute(now); + interval_msec += 50; // add a small margin to ensure the callback + // fires /after/ next is reached + m_timer = g_timeout_add_full(G_PRIORITY_HIGH, + interval_msec, + on_minute_timer_reached, + this, + nullptr); + } + + static gboolean on_minute_timer_reached(gpointer gself) + { + static_cast(gself)->restart_minute_timer(); + return G_SOURCE_REMOVE; } protected: LiveClock& m_owner; - GTimeZone * m_timezone = nullptr; + GTimeZone* m_timezone = nullptr; std::shared_ptr m_timezones; DateTime m_prev_datetime; - unsigned int m_skew_timeout_id = 0; + unsigned int m_timer = 0; }; LiveClock::LiveClock(const std::shared_ptr& tzd): - p(new Impl(*this, tzd)) + p(new Impl(*this, tzd)) { } @@ -138,6 +153,10 @@ DateTime LiveClock::localtime() const return p->localtime(); } +/*** +**** +***/ + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/clock.cpp b/src/clock.cpp index 7c3b8c7..d5293cc 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -81,7 +81,7 @@ Clock::onPrepareForSleep(GDBusConnection* /*connection*/, GVariant* /*parameters*/, gpointer gself) { - static_cast(gself)->skewDetected(); + static_cast(gself)->minuteChanged(); } /*** diff --git a/src/date-time.cpp b/src/date-time.cpp index 3842ac0..40c638f 100644 --- a/src/date-time.cpp +++ b/src/date-time.cpp @@ -76,11 +76,6 @@ int64_t DateTime::to_unix() const return g_date_time_to_unix(get()); } -int DateTime::day_of_year() const -{ - return m_dt ? g_date_time_get_day_of_year(get()) : -1; -} - void DateTime::reset(GDateTime* in) { if (in) @@ -95,39 +90,6 @@ void DateTime::reset(GDateTime* in) } } -#if 0 -DateTime& DateTime::operator=(GDateTime* in) -{ - reset(in); - return *this; -} - -DateTime& DateTime::operator=(const DateTime& in) -{ - m_dt = in.m_dt; - return *this; -} -#endif - -gint64 DateTime::difference(const DateTime& that) const -{ - const auto dt = get(); - const auto tdt = that.get(); - - gint64 ret; - - if (dt && tdt) - ret = g_date_time_difference(dt, tdt); - else if (dt) - ret = to_unix(); - else if (tdt) - ret = that.to_unix(); - else - ret = 0; - - return ret; -} - bool DateTime::operator<(const DateTime& that) const { return g_date_time_compare(get(), that.get()) < 0; @@ -141,13 +103,36 @@ bool DateTime::operator!=(const DateTime& that) const bool DateTime::operator==(const DateTime& that) const { - GDateTime * dt = get(); - GDateTime * tdt = that.get(); + auto dt = get(); + auto tdt = that.get(); if (!dt && !tdt) return true; if (!dt || !tdt) return false; return g_date_time_compare(get(), that.get()) == 0; } +bool DateTime::is_same_day(const DateTime& a, const DateTime& b) +{ + // it's meaningless to compare uninitialized dates + if (!a.m_dt || !b.m_dt) + return false; + + const auto adt = a.get(); + const auto bdt = b.get(); + return (g_date_time_get_year(adt) == g_date_time_get_year(bdt)) + && (g_date_time_get_day_of_year(adt) == g_date_time_get_day_of_year(bdt)); +} + +bool DateTime::is_same_minute(const DateTime& a, const DateTime& b) +{ + if (!is_same_day(a,b)) + return false; + + const auto adt = a.get(); + const auto bdt = b.get(); + return (g_date_time_get_hour(adt) == g_date_time_get_hour(bdt)) + && (g_date_time_get_minute(adt) == g_date_time_get_minute(bdt)); +} + /*** **** ***/ diff --git a/src/formatter.cpp b/src/formatter.cpp index 88a64df..a271ba0 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -28,9 +28,18 @@ #include // nl_langinfo() #include // strstr() +namespace unity { +namespace indicator { +namespace datetime { + +/*** +**** +***/ + namespace { -void clearTimer(guint& tag) + +void clear_timer(guint& tag) { if (tag) { @@ -39,35 +48,12 @@ void clearTimer(guint& tag) } } -guint calculate_milliseconds_until_next_minute(GDateTime * now) -{ - GDateTime * next; - GDateTime * start_of_next; - GTimeSpan interval_usec; - guint interval_msec; - - next = g_date_time_add_minutes(now, 1); - start_of_next = g_date_time_new_local(g_date_time_get_year(next), - g_date_time_get_month(next), - g_date_time_get_day_of_month(next), - g_date_time_get_hour(next), - g_date_time_get_minute(next), - 0.1); - - interval_usec = g_date_time_difference(start_of_next, now); - interval_msec = (interval_usec + 999) / 1000; - - g_date_time_unref(start_of_next); - g_date_time_unref(next); - return interval_msec; -} - -gint calculate_milliseconds_until_next_second(GDateTime * now) +gint calculate_milliseconds_until_next_second(const DateTime& now) { gint interval_usec; guint interval_msec; - interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond(now); + interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond(now.get()); interval_msec = (interval_usec + 999) / 1000; return interval_msec; } @@ -127,11 +113,6 @@ guint calculate_seconds_until_next_fifteen_minutes(GDateTime * now) } // unnamed namespace - -namespace unity { -namespace indicator { -namespace datetime { - class Formatter::Impl { public: @@ -140,57 +121,64 @@ public: m_owner(owner), m_clock(clock) { - m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){updateHeader();}); - m_clock->skewDetected.connect([this](){updateHeader();}); - updateHeader(); + m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){update_header();}); + m_clock->minuteChanged.connect([this](){update_header();}); + update_header(); restartRelativeTimer(); } ~Impl() { - clearTimer(m_header_timer); + clear_timer(m_header_seconds_timer); + clear_timer(m_relative_timer); } private: - void updateHeader() + static bool format_shows_seconds(const std::string& fmt) { + return (fmt.find("%s") != std::string::npos) + || (fmt.find("%S") != std::string::npos) + || (fmt.find("%T") != std::string::npos) + || (fmt.find("%X") != std::string::npos) + || (fmt.find("%c") != std::string::npos); + } + + void update_header() + { + // update the header property const auto fmt = m_owner->headerFormat.get(); const auto str = m_clock->localtime().format(fmt); m_owner->header.set(str); - restartHeaderTimer(); + // if the header needs to show seconds, set a timer. + if (format_shows_seconds(fmt)) + start_header_timer(); + else + clear_timer(m_header_seconds_timer); } - void restartHeaderTimer() + // we've got a header format that shows seconds, + // so we need to update it every second + void start_header_timer() { - clearTimer(m_header_timer); + clear_timer(m_header_seconds_timer); - const auto fmt = m_owner->headerFormat.get(); - const bool header_shows_seconds = (fmt.find("%s") != std::string::npos) - || (fmt.find("%S") != std::string::npos) - || (fmt.find("%T") != std::string::npos) - || (fmt.find("%X") != std::string::npos) - || (fmt.find("%c") != std::string::npos); - - guint interval_msec; const auto now = m_clock->localtime(); - auto str = now.format("%F %T"); - if (header_shows_seconds) - interval_msec = calculate_milliseconds_until_next_second(now.get()); - else - interval_msec = calculate_milliseconds_until_next_minute(now.get()); - + auto interval_msec = calculate_milliseconds_until_next_second(now); interval_msec += 50; // add a small margin to ensure the callback // fires /after/ next is reached - - m_header_timer = g_timeout_add_full(G_PRIORITY_HIGH, interval_msec, onHeaderTimer, this, nullptr); + m_header_seconds_timer = g_timeout_add_full(G_PRIORITY_HIGH, + interval_msec, + on_header_timer, + this, + nullptr); } - static gboolean onHeaderTimer(gpointer gself) + static gboolean on_header_timer(gpointer gself) { - static_cast(gself)->updateHeader(); + static_cast(gself)->update_header(); return G_SOURCE_REMOVE; } @@ -198,7 +186,7 @@ private: void restartRelativeTimer() { - clearTimer(m_relative_timer); + clear_timer(m_relative_timer); const auto now = m_clock->localtime(); const auto seconds = calculate_seconds_until_next_fifteen_minutes(now.get()); @@ -215,7 +203,7 @@ private: private: Formatter* const m_owner; - guint m_header_timer = 0; + guint m_header_seconds_timer = 0; guint m_relative_timer = 0; public: diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp index 142ccad..4271374 100644 --- a/tests/test-clock.cpp +++ b/tests/test-clock.cpp @@ -46,28 +46,24 @@ class ClockFixture: public TestDBusFixture } }; -/** - * Confirm that normal time passing doesn't trigger a skew event. - * that idling changing the clock's time triggers a skew event - */ -TEST_F(ClockFixture, IdleDoesNotTriggerSkew) +TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute) { + // start up a live clock std::shared_ptr zones(new Timezones); zones->timezone.set("America/New_York"); LiveClock clock(zones); wait_msec(500); // wait for the bus to set up - bool skewed = false; - clock.skewDetected.connect([&skewed](){ - skewed = true; - g_warn_if_reached(); - return G_SOURCE_REMOVE; - }); - - const unsigned int intervalSec = 3; - clock.skewTestIntervalSec.set(intervalSec); - wait_msec(intervalSec * 2.5 * 1000); - EXPECT_FALSE(skewed); + // count how many times clock.minuteChanged() is emitted over the next minute + const DateTime now = clock.localtime(); + const auto gnow = now.get(); + auto gthen = g_date_time_add_minutes(gnow, 1); + int count = 0; + clock.minuteChanged.connect([&count](){count++;}); + const auto msec = g_date_time_difference(gthen,gnow) / 1000; + wait_msec(msec); + EXPECT_EQ(1, count); + g_date_time_unref(gthen); } /*** @@ -99,7 +95,7 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) g_time_zone_unref(tz_nyc); /// change the timezones! - clock.skewDetected.connect([this](){ + clock.minuteChanged.connect([this](){ g_main_loop_quit(loop); }); g_idle_add([](gpointer gs){ @@ -128,7 +124,7 @@ TEST_F(ClockFixture, SleepTriggersSkew) wait_msec(500); // wait for the bus to set up bool skewed = false; - clock.skewDetected.connect([&skewed, this](){ + clock.minuteChanged.connect([&skewed, this](){ skewed = true; g_main_loop_quit(loop); return G_SOURCE_REMOVE; -- cgit v1.2.3 From 7b09a0ff5652bdca7c8d8e046d2af6a696f94147 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 25 Jan 2014 16:54:41 -0600 Subject: sync the exported calendar state with the #State backend --- include/datetime/actions.h | 4 +- include/datetime/state.h | 2 - src/actions-live.cpp | 5 -- src/actions.cpp | 30 +++++++++++- src/state-live.cpp | 1 - tests/actions-mock.h | 8 +--- tests/state-mock.h | 1 - tests/test-actions.cpp | 19 +++++--- tests/test-live-actions.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 157 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/include/datetime/actions.h b/include/datetime/actions.h index 18286dc..3686b95 100644 --- a/include/datetime/actions.h +++ b/include/datetime/actions.h @@ -49,8 +49,7 @@ public: virtual void open_planner_at(const DateTime&) =0; virtual void open_appointment(const std::string& uid) =0; virtual void set_location(const std::string& zone, const std::string& name)=0; - virtual void set_calendar_date(const DateTime&) =0; - + void set_calendar_date(const DateTime&); GActionGroup* action_group() { return G_ACTION_GROUP(m_actions); } std::shared_ptr state() { return m_state; } @@ -61,6 +60,7 @@ protected: private: std::shared_ptr m_state; GSimpleActionGroup* m_actions = nullptr; + void update_calendar_state(); // we've got raw pointers in here, so disable copying Actions(const Actions&) =delete; diff --git a/include/datetime/state.h b/include/datetime/state.h index b14908e..414be32 100644 --- a/include/datetime/state.h +++ b/include/datetime/state.h @@ -66,8 +66,6 @@ struct State /** \brief Configuration options that modify the view */ std::shared_ptr settings; - - core::Property calendar_day; }; } // namespace datetime diff --git a/src/actions-live.cpp b/src/actions-live.cpp index e4f5e9f..d5f7180 100644 --- a/src/actions-live.cpp +++ b/src/actions-live.cpp @@ -96,11 +96,6 @@ void LiveActions::open_appointment(const std::string& uid) } } -void LiveActions::set_calendar_date(const DateTime& dt) -{ - state()->calendar_day.set(dt); -} - /*** **** ***/ diff --git a/src/actions.cpp b/src/actions.cpp index 1d80c99..a6a7c0b 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -150,7 +150,7 @@ GVariant* create_calendar_state(const std::shared_ptr& state) g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "calendar-day"; - v = g_variant_new_int64(state->calendar_day.get().to_unix()); + v = g_variant_new_int64(state->planner->time.get().to_unix()); g_variant_builder_add(&dict_builder, "{sv}", key, v); key = "show-week-numbers"; @@ -200,7 +200,20 @@ Actions::Actions(const std::shared_ptr& state): a = g_simple_action_new_stateful("calendar", G_VARIANT_TYPE_INT64, v); g_action_map_add_action(gam, G_ACTION(a)); g_signal_connect(a, "activate", G_CALLBACK(on_calendar_activated), this); - //m_calendar_action = a; + + /// + /// Keep our GActionGroup's action's states in sync with m_state + /// + + m_state->planner->time.changed().connect([this](const DateTime&){ + update_calendar_state(); + }); + m_state->planner->thisMonth.changed().connect([this](const std::vector&){ + update_calendar_state(); + }); + m_state->settings->show_week_numbers.changed().connect([this](bool){ + update_calendar_state(); + }); // FIXME: rebuild the calendar state when show-week-number changes } @@ -210,6 +223,19 @@ Actions::~Actions() g_clear_object(&m_actions); } +void Actions::update_calendar_state() +{ + g_action_group_change_action_state(action_group(), + "calendar", + create_calendar_state(m_state)); +} + +void Actions::set_calendar_date(const DateTime& date) +{ + m_state->planner->time.set(date); +} + + } // namespace datetime } // namespace indicator } // namespace unity diff --git a/src/state-live.cpp b/src/state-live.cpp index 8ee663b..fe1e6cd 100644 --- a/src/state-live.cpp +++ b/src/state-live.cpp @@ -45,7 +45,6 @@ LiveState::LiveState() locations.reset(new SettingsLocations(live_settings, live_timezones)); planner.reset(new PlannerEds); planner->time = clock->localtime(); - calendar_day = clock->localtime(); } /*** diff --git a/tests/actions-mock.h b/tests/actions-mock.h index 112900b..da93cb9 100644 --- a/tests/actions-mock.h +++ b/tests/actions-mock.h @@ -35,8 +35,7 @@ public: ~MockActions() =default; enum Action { OpenDesktopSettings, OpenPhoneSettings, OpenPhoneClockApp, - OpenPlanner, OpenPlannerAt, OpenAppointment, - SetLocation, SetCalendarDate }; + OpenPlanner, OpenPlannerAt, OpenAppointment, SetLocation }; const std::vector& history() const { return m_history; } const DateTime& date_time() const { return m_date_time; } const std::string& zone() const { return m_zone; } @@ -68,11 +67,6 @@ public: m_url = url_; } - void set_calendar_date(const DateTime& date_time_) { - m_history.push_back(SetCalendarDate); - m_date_time = date_time_; - } - private: std::string m_url; std::string m_zone; diff --git a/tests/state-mock.h b/tests/state-mock.h index 64ab08b..721b82f 100644 --- a/tests/state-mock.h +++ b/tests/state-mock.h @@ -38,7 +38,6 @@ public: planner.reset(new MockPlanner); planner->time = now; locations.reset(new Locations); - calendar_day = now; } }; diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp index 4329608..c30d1fb 100644 --- a/tests/test-actions.cpp +++ b/tests/test-actions.cpp @@ -137,18 +137,23 @@ TEST_F(ActionsFixture, SetLocation) TEST_F(ActionsFixture, SetCalendarDate) { + // confirm that such an action exists const auto action_name = "calendar"; auto action_group = m_actions->action_group(); EXPECT_TRUE(m_mock_actions->history().empty()); EXPECT_TRUE(g_action_group_has_action(action_group, action_name)); - auto unix = m_state->clock->localtime().to_unix(); - auto v = g_variant_new_int64(unix); - g_action_group_activate_action(action_group, action_name, v); - const auto expected_action = MockActions::SetCalendarDate; - ASSERT_EQ(1, m_mock_actions->history().size()); - EXPECT_EQ(expected_action, m_mock_actions->history()[0]); - EXPECT_EQ(unix, m_mock_actions->date_time().to_unix()); + // pick an arbitrary DateTime... + auto tmp = g_date_time_new_local(2010, 1, 2, 3, 4, 5); + const auto now = DateTime(tmp); + g_date_time_unref(tmp); + + // confirm that Planner.time gets changed to that date when we + // activate the 'calendar' action with that date's time_t as the arg + EXPECT_NE (now, m_state->planner->time.get()); + auto v = g_variant_new_int64(now.to_unix()); + g_action_group_activate_action (action_group, action_name, v); + EXPECT_EQ (now, m_state->planner->time.get()); } TEST_F(ActionsFixture, OpenAppointment) diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp index d3d7720..562b358 100644 --- a/tests/test-live-actions.cpp +++ b/tests/test-live-actions.cpp @@ -287,3 +287,117 @@ TEST_F(LiveActionsFixture, OpenPlannerAt) const std::string expected = now.format("evolution \"calendar:///?startdate=%Y%m%d\""); EXPECT_EQ(expected, m_live_actions->last_cmd); } + +TEST_F(LiveActionsFixture, CalendarState) +{ + // init the clock + auto tmp = g_date_time_new_local (2014, 1, 1, 0, 0, 0); + const DateTime now (tmp); + g_date_time_unref (tmp); + m_mock_state->mock_clock->set_localtime (now); + m_state->planner->time.set(now); + + /// + /// Test the default calendar state. + /// + + auto action_group = m_actions->action_group(); + auto calendar_state = g_action_group_get_action_state (action_group, "calendar"); + EXPECT_TRUE (calendar_state != nullptr); + EXPECT_TRUE (g_variant_is_of_type (calendar_state, G_VARIANT_TYPE_DICTIONARY)); + + // there's nothing in the planner yet, so appointment-days should be an empty array + auto v = g_variant_lookup_value (calendar_state, "appointment-days", G_VARIANT_TYPE_ARRAY); + EXPECT_TRUE (v != nullptr); + EXPECT_EQ (0, g_variant_n_children (v)); + g_clear_pointer (&v, g_variant_unref); + + // calendar-day should be in sync with m_state->calendar_day + v = g_variant_lookup_value (calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); + EXPECT_TRUE (v != nullptr); + EXPECT_EQ (m_state->planner->time.get().to_unix(), g_variant_get_int64(v)); + g_clear_pointer (&v, g_variant_unref); + + // show-week-numbers should be false because MockSettings defaults everything to 0 + v = g_variant_lookup_value (calendar_state, "show-week-numbers", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE (v != nullptr); + EXPECT_FALSE (g_variant_get_boolean (v)); + g_clear_pointer (&v, g_variant_unref); + + // cleanup this step + g_clear_pointer (&calendar_state, g_variant_unref); + + + /// + /// Now add appointments to the planner and confirm that the state keeps in sync + /// + + auto tomorrow = g_date_time_add_days (now.get(), 1); + auto tomorrow_begin = g_date_time_add_full (tomorrow, 0, 0, 0, + -g_date_time_get_hour(tomorrow), + -g_date_time_get_minute(tomorrow), + -g_date_time_get_seconds(tomorrow)); + auto tomorrow_end = g_date_time_add_full (tomorrow_begin, 0, 0, 1, 0, 0, -1); + Appointment a1; + a1.color = "green"; + a1.summary = "write unit tests"; + a1.url = "http://www.ubuntu.com/"; + a1.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; + a1.begin = tomorrow_begin; + a1.end = tomorrow_end; + + auto next_begin = g_date_time_add_days (tomorrow_begin, 1); + auto next_end = g_date_time_add_full (next_begin, 0, 0, 1, 0, 0, -1); + Appointment a2; + a2.color = "orange"; + a2.summary = "code review"; + a2.url = "http://www.ubuntu.com/"; + a2.uid = "2756ff7de3745bbffd65d2e4779c37c7ca60d843"; + a2.begin = next_begin; + a2.end = next_end; + + m_state->planner->thisMonth.set(std::vector({a1, a2})); + + /// + /// Now test the calendar state again. + /// The thisMonth field should now contain the appointments we just added. + /// + + calendar_state = g_action_group_get_action_state (action_group, "calendar"); + v = g_variant_lookup_value (calendar_state, "appointment-days", G_VARIANT_TYPE_ARRAY); + EXPECT_TRUE (v != nullptr); + int i; + g_variant_get_child (v, 0, "i", &i); + EXPECT_EQ (g_date_time_get_day_of_month(a1.begin.get()), i); + g_variant_get_child (v, 1, "i", &i); + EXPECT_EQ (g_date_time_get_day_of_month(a2.begin.get()), i); + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + + // cleanup this step + g_date_time_unref (next_end); + g_date_time_unref (next_begin); + g_date_time_unref (tomorrow_end); + g_date_time_unref (tomorrow_begin); + g_date_time_unref (tomorrow); + + /// + /// Confirm that the action state's dictionary + /// keeps in sync with settings.show_week_numbers + /// + + auto b = m_state->settings->show_week_numbers.get(); + for (i=0; i<2; i++) + { + b = !b; + m_state->settings->show_week_numbers.set(b); + + calendar_state = g_action_group_get_action_state (action_group, "calendar"); + v = g_variant_lookup_value (calendar_state, "show-week-numbers", G_VARIANT_TYPE_BOOLEAN); + EXPECT_TRUE(v != nullptr); + EXPECT_EQ(b, g_variant_get_boolean(v)); + + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + } +} -- cgit v1.2.3 From 1c547710eb14d7805e47205f7c62fb7b0d0f3756 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 27 Jan 2014 01:26:02 -0600 Subject: add unit tests for utils.cpp's get_timezone_name() --- tests/test-utils.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp index 2fe6a2e..036c13f 100644 --- a/tests/test-utils.cpp +++ b/tests/test-utils.cpp @@ -17,6 +17,7 @@ * Charles Kerr */ +#include #include #include @@ -51,24 +52,47 @@ TEST(UtilsTest, SplitSettingsLocation) } } -TEST(UtilsTest, BeautifulTimezoneName) +namespace { struct { const char* timezone; const char* location; const char* expected_name; - } test_cases[] = { + } beautify_timezone_test_cases[] = { { "America/Chicago", NULL, "Chicago" }, { "America/Chicago", "America/Chicago", "Chicago" }, { "America/Chicago", "America/Chigago Chicago", "Chicago" }, { "America/Chicago", "America/Chicago Oklahoma City", "Oklahoma City" }, { "America/Chicago", "Europe/London London", "Chicago" } }; - - for(const auto& test_case : test_cases) +} + +TEST(UtilsTest, BeautifulTimezoneName) +{ + for(const auto& test_case : beautify_timezone_test_cases) { auto name = get_beautified_timezone_name(test_case.timezone, test_case.location); EXPECT_STREQ(test_case.expected_name, name); g_free(name); } } + + +TEST(UtilsTest, GetTimezonename) +{ + // set up a local GSettings + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); + auto settings = g_settings_new(SETTINGS_INTERFACE); + + for(const auto& test_case : beautify_timezone_test_cases) + { + g_settings_set_string(settings, SETTINGS_TIMEZONE_NAME_S, test_case.location); + auto name = get_timezone_name (test_case.timezone, settings); + EXPECT_STREQ(test_case.expected_name, name); + g_free(name); + } + + g_clear_object(&settings); +} -- cgit v1.2.3 From f320760f20b282e9b2695477ab602d4041a3ada8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 27 Jan 2014 01:26:52 -0600 Subject: add test coverage for Exporter's name_lost signal --- tests/test-exporter.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index aa9e89d..104fb4b 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -36,17 +36,6 @@ private: typedef GlibFixture super; - static void on_bus_closed(GObject * object, - GAsyncResult * res, - gpointer gself) - { - auto self = static_cast(gself); - GError* err = nullptr; - g_dbus_connection_close_finish(G_DBUS_CONNECTION(object), res, &err); - g_assert_no_error(err); - g_main_loop_quit(self->loop); - } - protected: GTestDBus* bus = nullptr; @@ -65,9 +54,11 @@ protected: void TearDown() { - GDBusConnection* connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr); - g_dbus_connection_close(connection, nullptr, on_bus_closed, this); - g_main_loop_run(loop); + GError * error = nullptr; + GDBusConnection* connection = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error); + if(!g_dbus_connection_is_closed(connection)) + g_dbus_connection_close_sync(connection, nullptr, &error); + g_assert_no_error(error); g_clear_object(&connection); g_test_dbus_down(bus); g_clear_object(&bus); @@ -87,6 +78,10 @@ TEST_F(ExporterFixture, Publish) std::shared_ptr actions(new MockActions(state)); std::vector> menus; + MenuFactory menu_factory (actions, state); + for(int i=0; i Date: Tue, 28 Jan 2014 17:02:34 -0600 Subject: fix bootstrap error in unit tests by ensuring we always use the local gschema instead of the one preinstalled on the system --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c909e0e..c86b4bb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,7 +35,7 @@ add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") function(add_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) @@ -55,7 +55,7 @@ add_test_by_name(test-utils) function(add_dbusmock_test_by_name name) set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) add_test (${TEST_NAME} ${TEST_NAME}) add_dependencies (${TEST_NAME} libindicatordatetimeservice) target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) -- cgit v1.2.3 From c3ee494eefcabd48eb0d2cb8ea8cf8cb1600331a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 21:15:39 -0600 Subject: ensure the unit tests' default locale supports utf-8 --- tests/glib-fixture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 3f517d4..1914b8c 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -61,7 +61,7 @@ class GlibFixture : public ::testing::Test virtual void SetUp() { - setlocale(LC_ALL, ""); + setlocale(LC_ALL, "C.UTF-8"); loop = g_main_loop_new(nullptr, false); -- cgit v1.2.3 From 9045a274a93bb96be458e563bebbceca1426da96 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 28 Jan 2014 21:42:05 -0600 Subject: comment out unit tests that require a version of dbus-test-runner that hasn't landed yet. --- tests/CMakeLists.txt | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c86b4bb..3dcd151 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,12 +53,15 @@ add_test_by_name(test-timezone-file) add_test_by_name(test-utils) -function(add_dbusmock_test_by_name name) - set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) - add_test (${TEST_NAME} ${TEST_NAME}) - add_dependencies (${TEST_NAME} libindicatordatetimeservice) - target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) -endfunction() -add_dbusmock_test_by_name(test-timezone-geoclue) -add_dbusmock_test_by_name(test-timezones) +# disabling the timezone unit tests because they require +# https://code.launchpad.net/~ted/dbus-test-runner/multi-interface-test/+merge/199724 +# which hasn't landed yet. These can be re-enabled as soon as that lands. +#function(add_dbusmock_test_by_name name) +# set (TEST_NAME ${name}) +# add_executable (${TEST_NAME} ${TEST_NAME}.cpp gschemas.compiled) +# add_test (${TEST_NAME} ${TEST_NAME}) +# add_dependencies (${TEST_NAME} libindicatordatetimeservice) +# target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS}) +#endfunction() +#add_dbusmock_test_by_name(test-timezone-geoclue) +#add_dbusmock_test_by_name(test-timezones) -- cgit v1.2.3 From f07f97ef53522abdce52cf3c7b583c0d6d47aa40 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 12:50:59 -0600 Subject: sync with lp:~larsu/indicator-datetime/reset-date and add corresponding unit tests. --- src/actions.cpp | 20 ++++++++++++++++--- src/menu.cpp | 2 ++ tests/test-actions.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/src/actions.cpp b/src/actions.cpp index a6a7c0b..acf8fd4 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -108,9 +108,22 @@ void on_set_location(GSimpleAction * /*action*/, g_free(zone); } -static void on_calendar_activated(GSimpleAction * /*action*/, - GVariant * state, - gpointer gself) +void on_calendar_active_changed(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) +{ + // reset the date when the menu is shown + if (g_variant_get_boolean(state)) + { + auto self = static_cast(gself); + + self->set_calendar_date(self->state()->clock->localtime()); + } +} + +void on_calendar_activated(GSimpleAction * /*action*/, + GVariant * state, + gpointer gself) { const time_t t = g_variant_get_int64(state); @@ -175,6 +188,7 @@ Actions::Actions(const std::shared_ptr& state): { "activate-phone-clock-app", on_phone_clock_activated }, { "activate-appointment", on_activate_appointment, "s", nullptr }, { "activate-planner", on_activate_planner, "x", nullptr }, + { "calendar-active", nullptr, nullptr, "false", on_calendar_active_changed }, { "set-location", on_set_location, "s" } }; diff --git a/src/menu.cpp b/src/menu.cpp index 390a06f..b0ba79d 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -204,6 +204,8 @@ private: auto header = g_menu_item_new(nullptr, detailed_action.c_str()); g_menu_item_set_attribute(header, "x-canonical-type", "s", "com.canonical.indicator.root"); + g_menu_item_set_attribute(header, "submenu-action", "s", + "indicator.calendar-active"); g_menu_item_set_submenu(header, G_MENU_MODEL(m_submenu)); g_object_unref(m_submenu); diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp index c30d1fb..1865cfd 100644 --- a/tests/test-actions.cpp +++ b/tests/test-actions.cpp @@ -156,6 +156,60 @@ TEST_F(ActionsFixture, SetCalendarDate) EXPECT_EQ (now, m_state->planner->time.get()); } +TEST_F(ActionsFixture, ActivatingTheCalendarResetsItsDate) +{ + // Confirm that the GActions exist + auto action_group = m_actions->action_group(); + EXPECT_TRUE(g_action_group_has_action(action_group, "calendar")); + EXPECT_TRUE(g_action_group_has_action(action_group, "calendar-active")); + + /// + /// Prerequisite for the test: move calendar-date away from today + /// + + // move calendar-date a week into the future... + const auto now = m_state->clock->localtime(); + auto next_week = g_date_time_add_weeks(now.get(), 1); + const auto next_week_unix = g_date_time_to_unix(next_week); + g_action_group_activate_action (action_group, "calendar", g_variant_new_int64(next_week_unix)); + + // confirm the planner and calendar action state moved a week into the future + // but that m_state->clock is unchanged + EXPECT_EQ(next_week_unix, m_state->planner->time.get().to_unix()); + EXPECT_EQ(now, m_state->clock->localtime()); + auto calendar_state = g_action_group_get_action_state(action_group, "calendar"); + EXPECT_TRUE(calendar_state != nullptr); + EXPECT_TRUE(g_variant_is_of_type(calendar_state, G_VARIANT_TYPE_DICTIONARY)); + auto v = g_variant_lookup_value(calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); + EXPECT_TRUE(v != nullptr); + EXPECT_EQ(next_week_unix, g_variant_get_int64(v)); + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + + /// + /// Now the actual test. + /// We set the state of 'calendar-active' to true, which should reset the calendar date. + /// This is so the calendar always starts on today's date when the indicator's menu is pulled down. + /// + + // change the state... + g_action_group_change_action_state(action_group, "calendar-active", g_variant_new_boolean(true)); + + // confirm the planner and calendar action state were reset back to m_state->clock's time + EXPECT_EQ(now.to_unix(), m_state->planner->time.get().to_unix()); + EXPECT_EQ(now, m_state->clock->localtime()); + calendar_state = g_action_group_get_action_state(action_group, "calendar"); + EXPECT_TRUE(calendar_state != nullptr); + EXPECT_TRUE(g_variant_is_of_type(calendar_state, G_VARIANT_TYPE_DICTIONARY)); + v = g_variant_lookup_value(calendar_state, "calendar-day", G_VARIANT_TYPE_INT64); + EXPECT_TRUE(v != nullptr); + EXPECT_EQ(now.to_unix(), g_variant_get_int64(v)); + g_clear_pointer(&v, g_variant_unref); + g_clear_pointer(&calendar_state, g_variant_unref); + +} + + TEST_F(ActionsFixture, OpenAppointment) { Appointment appt; -- cgit v1.2.3 From 6b2c01ed6063bd7fd12e192668b738a075dc3a24 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 16:10:50 -0600 Subject: fix instant calendar toggle issue reported in testing by seb128; add a test for this regression --- src/menu.cpp | 7 +++++-- tests/test-menus.cpp | 8 +++++++- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/src/menu.cpp b/src/menu.cpp index b0ba79d..40a94fa 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -74,6 +74,9 @@ protected: update_header(); // update header's label update_section(Locations); // locations' relative time may have changed }); + m_state->settings->show_calendar.changed().connect([this](bool){ + update_section(Calendar); + }); m_state->settings->show_events.changed().connect([this](bool){ update_section(Appointments); // showing events got toggled }); @@ -219,8 +222,8 @@ private: { const bool allow_activation = (profile == Desktop) || (profile == Phone); - const bool show_calendar = (profile == Desktop) - || (profile == DesktopGreeter); + const bool show_calendar = m_state->settings->show_calendar.get() && + ((profile == Desktop) || (profile == DesktopGreeter)); auto menu = g_menu_new(); // add a menuitem that shows the current date diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index fe1e86e..27248ad 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -93,7 +93,8 @@ protected: { gchar* str = nullptr; const auto actions_expected = (profile == Menu::Desktop) || (profile == Menu::Phone); - const auto calendar_expected = (profile == Menu::Desktop) || (profile == Menu::DesktopGreeter); + const auto calendar_expected = ((profile == Menu::Desktop) || (profile == Menu::DesktopGreeter)) + && (m_state->settings->show_calendar.get()); // get the calendar section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); @@ -377,6 +378,11 @@ TEST_F(MenuFixture, Sections) TEST_F(MenuFixture, Calendar) { + m_state->settings->show_calendar.set(true); + for(auto& menu : m_menus) + InspectCalendar(menu->menu_model(), menu->profile()); + + m_state->settings->show_calendar.set(false); for(auto& menu : m_menus) InspectCalendar(menu->menu_model(), menu->profile()); } -- cgit v1.2.3 From 131f7512975e3e0e1363cb787834abee5b79b4e5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jan 2014 16:26:25 -0600 Subject: fix disabled 'Add Event…' menuitem issue reported in testing by seb128; add a test for this regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/menu.cpp | 2 +- tests/test-menus.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/menu.cpp b/src/menu.cpp index 40a94fa..44da7b7 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -321,7 +321,7 @@ private: // add the 'Add Event…' menuitem auto menu_item = g_menu_item_new(_("Add Event…"), nullptr); - const gchar* action_name = "indicator.activate_planner"; + const gchar* action_name = "indicator.activate-planner"; auto v = g_variant_new_int64(0); g_menu_item_set_action_and_target_value(menu_item, action_name, v); g_menu_append_item(menu, menu_item); diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index 27248ad..e9dd7df 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -182,6 +182,14 @@ protected: section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); int expected_n = appointments_expected ? 1 : 0; EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); + if (appointments_expected) + { + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + EXPECT_STREQ("indicator.activate-planner", action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), "activate-planner")); + g_free(action); + } g_clear_object(§ion); // try adding a few appointments and see if the menu updates itself -- cgit v1.2.3 From 9752ca1cd3e75e1245ebb8dcb4719503e332a352 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 10:57:19 -0600 Subject: fix 'clock app' menuitem on phone profile reported by seb128 and update the corresponding unit tests --- src/menu.cpp | 17 +++- tests/test-menus.cpp | 242 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 188 insertions(+), 71 deletions(-) (limited to 'tests') diff --git a/src/menu.cpp b/src/menu.cpp index 44da7b7..d0756cc 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -293,10 +293,10 @@ private: else { g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.appointment"); - - if (!appt.color.empty()) - g_menu_item_set_attribute (menu_item, "x-canonical-color", "s", appt.color.c_str()); } + + if (!appt.color.empty()) + g_menu_item_set_attribute (menu_item, "x-canonical-color", "s", appt.color.c_str()); if (profile == Phone) g_menu_item_set_action_and_target_value (menu_item, @@ -315,7 +315,7 @@ private: { auto menu = g_menu_new(); - if (((profile==Phone) || (profile==Desktop)) && m_state->settings->show_events.get()) + if ((profile==Desktop) && m_state->settings->show_events.get()) { add_appointments (menu, profile); @@ -327,6 +327,15 @@ private: g_menu_append_item(menu, menu_item); g_object_unref(menu_item); } + else if (profile==Phone) + { + auto menu_item = g_menu_item_new (_("Clock"), "indicator.activate-phone-clock-app"); + g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, get_serialized_alarm_icon()); + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); + + add_appointments (menu, profile); + } return G_MENU_MODEL(menu); } diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index e9dd7df..73d6036 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -158,42 +158,20 @@ protected: g_object_unref(submenu); } - void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) - { - const bool appointments_expected = (profile == Menu::Desktop) - || (profile == Menu::Phone); +private: + void InspectEmptySection(GMenuModel* menu_model, Menu::Section section) + { // get the Appointments section auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + auto menu_section = g_menu_model_get_item_link(submenu, section, G_MENU_LINK_SECTION); + EXPECT_EQ(0, g_menu_model_get_n_items(menu_section)); + g_clear_object(&menu_section); + g_clear_object(&submenu); + } - // there shouldn't be any menuitems when "show events" is false - m_state->settings->show_events.set(false); - wait_msec(); - auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - EXPECT_EQ(0, g_menu_model_get_n_items(section)); - g_clear_object(§ion); - - // when "show_events" is true, - // there should be an "add event" button even if there aren't any appointments - std::vector appointments; - m_state->settings->show_events.set(true); - m_state->planner->upcoming.set(appointments); - wait_msec(); - section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - int expected_n = appointments_expected ? 1 : 0; - EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); - if (appointments_expected) - { - gchar* action = nullptr; - EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); - EXPECT_STREQ("indicator.activate-planner", action); - EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), "activate-planner")); - g_free(action); - } - g_clear_object(§ion); - - // try adding a few appointments and see if the menu updates itself - + std::vector build_some_appointments() + { const auto now = m_state->clock->localtime(); auto gdt_tomorrow = g_date_time_add_days(now.get(), 1); const auto tomorrow = DateTime(gdt_tomorrow); @@ -206,7 +184,6 @@ protected: a1.uid = "example"; a1.has_alarms = true; a1.begin = a1.end = tomorrow; - appointments.push_back(a1); Appointment a2; // a non-alarm appointment a2.color = "green"; @@ -215,52 +192,183 @@ protected: a2.uid = "monkey"; a2.has_alarms = false; a2.begin = a2.end = tomorrow; - appointments.push_back(a2); - m_state->planner->upcoming.set(appointments); - wait_msec(); // wait a moment for the menu to update + return std::vector({a1, a2}); + } - section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); - expected_n = appointments_expected ? 3 : 0; - EXPECT_EQ(expected_n, g_menu_model_get_n_items(section)); - if (appointments_expected) + void InspectAppointmentMenuItem(GMenuModel* section, + int index, + const Appointment& appt) + { + // confirm it has the right x-canonical-type + gchar * str = nullptr; + g_menu_model_get_item_attribute(section, index, "x-canonical-type", "s", &str); + if (appt.has_alarms) + EXPECT_STREQ("com.canonical.indicator.alarm", str); + else + EXPECT_STREQ("com.canonical.indicator.appointment", str); + g_clear_pointer(&str, g_free); + + // confirm it has a nonempty x-canonical-time-format + g_menu_model_get_item_attribute(section, index, "x-canonical-time-format", "s", &str); + EXPECT_TRUE(str && *str); + g_clear_pointer(&str, g_free); + + // confirm the color hint, if it exists, + // is in the x-canonical-color attribute + if (appt.color.empty()) { - gchar * str = nullptr; + EXPECT_FALSE(g_menu_model_get_item_attribute(section, + index, + "x-canonical-color", + "s", + &str)); + } + else + { + EXPECT_TRUE(g_menu_model_get_item_attribute(section, + index, + "x-canonical-color", + "s", + &str)); + EXPECT_EQ(appt.color, str); + } + g_clear_pointer(&str, g_free); - // test the alarm - // - confirm it has an x-canonical-type of "alarm" - g_menu_model_get_item_attribute(section, 0, "x-canonical-type", "s", &str); - EXPECT_STREQ("com.canonical.indicator.alarm", str); - g_clear_pointer(&str, g_free); - // - confirm it has a nonempty x-canonical-time-format - g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); - EXPECT_TRUE(str && *str); - g_clear_pointer(&str, g_free); - // - confirm it has a serialized icon attribute - auto v = g_menu_model_get_item_attribute_value(section, 0, G_MENU_ATTRIBUTE_ICON, nullptr); + // confirm that alarms have an icon + if (appt.has_alarms) + { + auto v = g_menu_model_get_item_attribute_value(section, + index, + G_MENU_ATTRIBUTE_ICON, + nullptr); EXPECT_TRUE(v != nullptr); auto icon = g_icon_deserialize(v); EXPECT_TRUE(icon != nullptr); g_clear_object(&icon); g_clear_pointer(&v, g_variant_unref); - - // test the appointment - // - confirm it has an x-canonical-type of "appointment" - g_menu_model_get_item_attribute(section, 1, "x-canonical-type", "s", &str); - EXPECT_STREQ("com.canonical.indicator.appointment", str); - g_clear_pointer(&str, g_free); - // - confirm it has a nonempty x-canonical-time-format - g_menu_model_get_item_attribute(section, 0, "x-canonical-time-format", "s", &str); - EXPECT_TRUE(str && *str); - g_clear_pointer(&str, g_free); - // - confirm its color matches the one we fed the appointments vector - g_menu_model_get_item_attribute(section, 1, "x-canonical-color", "s", &str); - EXPECT_EQ(a2.color, str); - g_clear_pointer(&str, g_free); } + } + + void InspectAppointmentMenuItems(GMenuModel* section, + int first_appt_index, + const std::vector& appointments) + { + // try adding a few appointments and see if the menu updates itself + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + //auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + //auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(appointments.size()+1, g_menu_model_get_n_items(section)); + + for (int i=0, n=appointments.size(); isettings->show_events.set(false); + wait_msec(); + auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(0, g_menu_model_get_n_items(section)); g_clear_object(§ion); - g_object_unref(submenu); + // when "show_events" is true, + // there should be an "add event" button even if there aren't any appointments + std::vector appointments; + m_state->settings->show_events.set(true); + m_state->planner->upcoming.set(appointments); + wait_msec(); + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + const char* expected_action = "activate-planner"; + EXPECT_EQ(std::string("indicator.")+expected_action, action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), expected_action)); + g_free(action); + g_clear_object(§ion); + + // try adding a few appointments and see if the menu updates itself + appointments = build_some_appointments(); + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(3, g_menu_model_get_n_items(section)); + InspectAppointmentMenuItems(section, 0, appointments); + g_clear_object(§ion); + + // cleanup + g_clear_object(&submenu); + } + + void InspectPhoneAppointments(GMenuModel* menu_model) + { + auto submenu = g_menu_model_get_item_link(menu_model, 0, G_MENU_LINK_SUBMENU); + + // clear all the appointments + std::vector appointments; + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + + // check that there's a "clock app" menuitem even when there are no appointments + auto section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + const char* expected_action = "activate-phone-clock-app"; + EXPECT_EQ(1, g_menu_model_get_n_items(section)); + gchar* action = nullptr; + EXPECT_TRUE(g_menu_model_get_item_attribute(section, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)); + EXPECT_EQ(std::string("indicator.")+expected_action, action); + EXPECT_TRUE(g_action_group_has_action(m_actions->action_group(), expected_action)); + g_free(action); + g_clear_object(§ion); + + // add some appointments and test them + appointments = build_some_appointments(); + m_state->planner->upcoming.set(appointments); + wait_msec(); // wait a moment for the menu to update + section = g_menu_model_get_item_link(submenu, Menu::Appointments, G_MENU_LINK_SECTION); + EXPECT_EQ(3, g_menu_model_get_n_items(section)); + InspectAppointmentMenuItems(section, 1, appointments); + g_clear_object(§ion); + + // cleanup + g_clear_object(&submenu); + } + +protected: + + void InspectAppointments(GMenuModel* menu_model, Menu::Profile profile) + { + switch (profile) + { + case Menu::Desktop: + InspectDesktopAppointments(menu_model); + break; + + case Menu::DesktopGreeter: + InspectEmptySection(menu_model, Menu::Appointments); + break; + + case Menu::Phone: + InspectPhoneAppointments(menu_model); + break; + + case Menu::PhoneGreeter: + InspectEmptySection(menu_model, Menu::Appointments); + break; + + default: + g_warn_if_reached(); + break; + } } void CompareLocationsTo(GMenuModel* menu_model, const std::vector& locations) -- cgit v1.2.3 From 8564861a5026561d94310cd60ed77e3986f64246 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 14:24:17 -0600 Subject: remove dead files --- tests/planner-mock.c | 178 ------------------------------------------------ tests/test-formatter.cc | 98 -------------------------- 2 files changed, 276 deletions(-) delete mode 100644 tests/planner-mock.c delete mode 100644 tests/test-formatter.cc (limited to 'tests') diff --git a/tests/planner-mock.c b/tests/planner-mock.c deleted file mode 100644 index df5413e..0000000 --- a/tests/planner-mock.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "config.h" - -#include "planner-mock.h" - -struct _IndicatorDatetimePlannerMockPriv -{ - gboolean is_configured; -}; - -typedef IndicatorDatetimePlannerMockPriv priv_t; - -G_DEFINE_TYPE (IndicatorDatetimePlannerMock, - indicator_datetime_planner_mock, - INDICATOR_TYPE_DATETIME_PLANNER) - -/*** -**** IndicatorDatetimePlanner virtual funcs -***/ - -static void -my_get_appointments (IndicatorDatetimePlanner * planner, - GDateTime * begin_datetime, - GDateTime * /*end_datetime*/, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask * task; - GSList * appointments; - struct IndicatorDatetimeAppt * appt; - struct IndicatorDatetimeAppt * prev; - - task = g_task_new (planner, NULL, callback, user_data); - - /** - *** Build the appointments list - **/ - - appointments = NULL; - - /* add a daily appointment that occurs at the beginning of the next minute */ - appt = g_slice_new0 (struct IndicatorDatetimeAppt); - appt->is_daily = TRUE; - appt->begin = g_date_time_add_seconds (begin_datetime, 60-g_date_time_get_seconds(begin_datetime)); - appt->end = g_date_time_add_minutes (appt->begin, 1); - appt->color = g_strdup ("#00FF00"); - appt->is_event = TRUE; - appt->summary = g_strdup ("Daily alarm"); - appt->uid = g_strdup ("this uid isn't very random."); - appt->has_alarms = TRUE; - appt->url = g_strdup ("alarm:///some-alarm-info-goes-here"); - appointments = g_slist_prepend (appointments, appt); - prev = appt; - - /* and add one for a minute later that has an alarm uri */ - appt = g_slice_new0 (struct IndicatorDatetimeAppt); - appt->is_daily = TRUE; - appt->begin = g_date_time_add_minutes (prev->end, 1); - appt->end = g_date_time_add_minutes (appt->begin, 1); - appt->color = g_strdup ("#0000FF"); - appt->is_event = TRUE; - appt->summary = g_strdup ("Second Daily alarm"); - appt->uid = g_strdup ("this uid isn't very random either."); - appt->has_alarms = FALSE; - appointments = g_slist_prepend (appointments, appt); - - /* done */ - g_task_return_pointer (task, appointments, NULL); - g_object_unref (task); -} - -static GSList * -my_get_appointments_finish (IndicatorDatetimePlanner* /*self*/, - GAsyncResult* res, - GError** error) -{ - return g_task_propagate_pointer(G_TASK(res), error); -} - -static gboolean -my_is_configured(IndicatorDatetimePlanner* planner) -{ - IndicatorDatetimePlannerMock * self; - self = INDICATOR_DATETIME_PLANNER_MOCK(planner); - return self->priv->is_configured; -} - -static void -my_activate(IndicatorDatetimePlanner* /*self*/) -{ - g_message("%s %s", G_STRLOC, G_STRFUNC); -} - -static void -my_activate_time(IndicatorDatetimePlanner* /*self*/, - GDateTime* activate_time) -{ - gchar * str = g_date_time_format(activate_time, "%F %T"); - g_message("%s %s: %s", G_STRLOC, G_STRFUNC, str); - g_free(str); -} - -/*** -**** GObject virtual funcs -***/ - -static void -my_dispose(GObject * o) -{ - G_OBJECT_CLASS(indicator_datetime_planner_mock_parent_class)->dispose(o); -} - -/*** -**** Instantiation -***/ - -static void -indicator_datetime_planner_mock_class_init(IndicatorDatetimePlannerMockClass* klass) -{ - GObjectClass * object_class; - IndicatorDatetimePlannerClass * planner_class; - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = my_dispose; - - planner_class = INDICATOR_DATETIME_PLANNER_CLASS (klass); - planner_class->is_configured = my_is_configured; - planner_class->activate = my_activate; - planner_class->activate_time = my_activate_time; - planner_class->get_appointments = my_get_appointments; - planner_class->get_appointments_finish = my_get_appointments_finish; - - g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerMockPriv)); -} - -static void -indicator_datetime_planner_mock_init (IndicatorDatetimePlannerMock * self) -{ - priv_t * p; - - p = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_TYPE_DATETIME_PLANNER_MOCK, - IndicatorDatetimePlannerMockPriv); - - p->is_configured = TRUE; - - self->priv = p; -} - -/*** -**** Public -***/ - -IndicatorDatetimePlanner * -indicator_datetime_planner_mock_new (void) -{ - gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_MOCK, NULL); - - return INDICATOR_DATETIME_PLANNER (o); -} diff --git a/tests/test-formatter.cc b/tests/test-formatter.cc deleted file mode 100644 index 6a408ab..0000000 --- a/tests/test-formatter.cc +++ /dev/null @@ -1,98 +0,0 @@ - -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include -#include - -#include - -#include "utils.h" - -#include "glib-fixture.h" - -/*** -**** -***/ - -class FormatterFixture: public GlibFixture -{ - private: - - typedef GlibFixture super; - gchar * original_locale = nullptr; - - protected: - - virtual void SetUp () - { - super::SetUp (); - - original_locale = g_strdup (setlocale (LC_TIME, NULL)); - } - - virtual void TearDown () - { - setlocale (LC_TIME, original_locale); - g_clear_pointer (&original_locale, g_free); - - super::TearDown (); - } - - bool SetLocale (const char * expected_locale, const char * name) - { - setlocale (LC_TIME, expected_locale); - const char * actual_locale = setlocale (LC_TIME, NULL); - if (!g_strcmp0 (expected_locale, actual_locale)) - { - return true; - } - else - { - g_warning ("Unable to set locale to %s; skipping %s locale tests.", expected_locale, name); - return false; - } - } - - inline bool Set24hLocale () { return SetLocale ("C", "24h"); } - inline bool Set12hLocale () { return SetLocale ("en_US.utf8", "12h"); } -}; - - -/** - * Test the phone header format - */ -TEST_F (FormatterFixture, TestPhoneHeader) -{ - // test the default value in a 24h locale - if (Set24hLocale ()) - { - const gchar * format = get_terse_header_time_format_string (); - ASSERT_NE (nullptr, format); - ASSERT_STREQ ("%H:%M", format); - } - - // test the default value in a 12h locale - if (Set12hLocale ()) - { - const gchar * format = get_terse_header_time_format_string (); - ASSERT_NE (nullptr, format); - ASSERT_STREQ ("%l:%M %p", format); - } -} -- cgit v1.2.3 From a7a09a5ca5012fb1c48f259d2587542316e7349b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 18:33:14 -0600 Subject: copyediting: as per review, use name_of_thing() instead of get_name_of_thing() or getNameOfThing() --- include/datetime/clock-mock.h | 4 ++-- include/datetime/clock.h | 10 +++++----- include/datetime/formatter.h | 12 ++++++------ include/datetime/planner.h | 6 +++--- src/actions.cpp | 4 ++-- src/clock-live.cpp | 6 +++--- src/clock.cpp | 22 +++++++++++----------- src/formatter-desktop.cpp | 4 ++-- src/formatter.cpp | 12 ++++++------ src/menu.cpp | 14 +++++++------- src/planner-eds.cpp | 2 +- tests/test-clock.cpp | 8 ++++---- tests/test-formatter.cpp | 10 +++++----- tests/test-live-actions.cpp | 4 ++-- tests/test-planner.cpp | 6 +++--- 15 files changed, 62 insertions(+), 62 deletions(-) (limited to 'tests') diff --git a/include/datetime/clock-mock.h b/include/datetime/clock-mock.h index 27926ff..fb9b52f 100644 --- a/include/datetime/clock-mock.h +++ b/include/datetime/clock-mock.h @@ -45,9 +45,9 @@ public: const auto old = m_localtime; m_localtime = dt; if (!DateTime::is_same_minute(old, m_localtime)) - minuteChanged(); + minute_changed(); if (!DateTime::is_same_day(old, m_localtime)) - dateChanged(); + date_changed(); } private: diff --git a/include/datetime/clock.h b/include/datetime/clock.h index 4a9db8f..1d488d1 100644 --- a/include/datetime/clock.h +++ b/include/datetime/clock.h @@ -43,20 +43,20 @@ public: virtual DateTime localtime() const =0; /** \brief A signal which fires when the clock's minute changes */ - core::Signal<> minuteChanged; + core::Signal<> minute_changed; /** \brief A signal which fires when the clock's date changes */ - core::Signal<> dateChanged; + core::Signal<> date_changed; protected: Clock(); - /** \brief Compares old and new times, emits minuteChanged() or dateChanged() signals if appropriate */ + /** \brief Compares old and new times, emits minute_changed() or date_changed() signals if appropriate */ void maybe_emit (const DateTime& a, const DateTime& b); private: - static void onSystemBusReady(GObject*, GAsyncResult*, gpointer); - static void onPrepareForSleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); + static void on_system_bus_ready(GObject*, GAsyncResult*, gpointer); + static void on_prepare_for_sleep(GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant*, gpointer); GCancellable * m_cancellable = nullptr; GDBusConnection * m_system_bus = nullptr; diff --git a/include/datetime/formatter.h b/include/datetime/formatter.h index f323858..0d695e2 100644 --- a/include/datetime/formatter.h +++ b/include/datetime/formatter.h @@ -69,27 +69,27 @@ class Formatter public: /** \brief The time format string for the menu header */ - core::Property headerFormat; + core::Property header_format; - /** \brief The time string for the menu header. (eg, the headerFormat + the clock's time */ + /** \brief The time string for the menu header. (eg, the header_format + the clock's time */ core::Property header; /** \brief Signal to denote when the relativeFormat has changed. When this is emitted, clients will want to rebuild their menuitems that contain relative time strings (ie, the Appointments and Locations menuitems) */ - core::Signal<> relativeFormatChanged; + core::Signal<> relative_format_changed; /** \brief Generate a relative time format for some time (or time range) from the current clock's value. For example, a full-day interval starting at the end of the current clock's day yields "Tomorrow" */ - std::string getRelativeFormat(GDateTime* then, GDateTime* then_end=nullptr) const; + std::string relative_format(GDateTime* then, GDateTime* then_end=nullptr) const; protected: Formatter(const std::shared_ptr&); virtual ~Formatter(); - static const char* getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds); + static const char* default_header_time_format(bool twelvehour, bool show_seconds); private: @@ -127,7 +127,7 @@ class PhoneFormatter: public Formatter { public: PhoneFormatter(const std::shared_ptr& clock): Formatter(clock) { - headerFormat.set(getDefaultHeaderTimeFormat(is_locale_12h(), false)); + header_format.set(default_header_time_format(is_locale_12h(), false)); } }; diff --git a/include/datetime/planner.h b/include/datetime/planner.h index a8f9941..376a31f 100644 --- a/include/datetime/planner.h +++ b/include/datetime/planner.h @@ -43,9 +43,9 @@ public: virtual ~Planner() =default; /** - * \brief Timestamp used to determine the appointments in the `upcoming' and `thisMonth' properties. + * \brief Timestamp used to determine the appointments in the `upcoming' and `this_month' properties. * Setting this value will cause the planner to re-query its backend and - * update the `upcoming' and `thisMonth' properties. + * update the `upcoming' and `this_month' properties. */ core::Property time; @@ -57,7 +57,7 @@ public: /** * \brief The appointments that occur in the same month as the time property */ - core::Property> thisMonth; + core::Property> this_month; protected: Planner() =default; diff --git a/src/actions.cpp b/src/actions.cpp index cdeb77f..d6fa698 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -146,7 +146,7 @@ GVariant* create_default_header_state() GVariant* create_calendar_state(const std::shared_ptr& state) { gboolean days[32] = { 0 }; - for (const auto& appt : state->planner->thisMonth.get()) + for (const auto& appt : state->planner->this_month.get()) days[appt.begin.day_of_month()] = true; GVariantBuilder day_builder; @@ -222,7 +222,7 @@ Actions::Actions(const std::shared_ptr& state): m_state->planner->time.changed().connect([this](const DateTime&){ update_calendar_state(); }); - m_state->planner->thisMonth.changed().connect([this](const std::vector&){ + m_state->planner->this_month.changed().connect([this](const std::vector&){ update_calendar_state(); }); m_state->settings->show_week_numbers.changed().connect([this](bool){ diff --git a/src/clock-live.cpp b/src/clock-live.cpp index 7c9db40..21a18a3 100644 --- a/src/clock-live.cpp +++ b/src/clock-live.cpp @@ -95,7 +95,7 @@ private: { g_clear_pointer(&m_timezone, g_time_zone_unref); m_timezone = g_time_zone_new(str.c_str()); - m_owner.minuteChanged(); + m_owner.minute_changed(); } /*** @@ -109,9 +109,9 @@ private: // maybe emit change signals const auto now = localtime(); if (!DateTime::is_same_minute(m_prev_datetime, now)) - m_owner.minuteChanged(); + m_owner.minute_changed(); if (!DateTime::is_same_day(m_prev_datetime, now)) - m_owner.dateChanged(); + m_owner.date_changed(); // queue up a timer to fire at the next minute m_prev_datetime = now; diff --git a/src/clock.cpp b/src/clock.cpp index d5293cc..f41a0cc 100644 --- a/src/clock.cpp +++ b/src/clock.cpp @@ -33,7 +33,7 @@ namespace datetime { Clock::Clock(): m_cancellable(g_cancellable_new()) { - g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, onSystemBusReady, this); + g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_system_bus_ready, this); } Clock::~Clock() @@ -48,7 +48,7 @@ Clock::~Clock() } void -Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) +Clock::on_system_bus_ready(GObject*, GAsyncResult * res, gpointer gself) { GDBusConnection * system_bus; @@ -66,22 +66,22 @@ Clock::onSystemBusReady(GObject*, GAsyncResult * res, gpointer gself) "/org/freedesktop/login1", // object path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, - onPrepareForSleep, + on_prepare_for_sleep, self, nullptr); } } void -Clock::onPrepareForSleep(GDBusConnection* /*connection*/, - const gchar* /*sender_name*/, - const gchar* /*object_path*/, - const gchar* /*interface_name*/, - const gchar* /*signal_name*/, - GVariant* /*parameters*/, - gpointer gself) +Clock::on_prepare_for_sleep(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* /*object_path*/, + const gchar* /*interface_name*/, + const gchar* /*signal_name*/, + GVariant* /*parameters*/, + gpointer gself) { - static_cast(gself)->minuteChanged(); + static_cast(gself)->minute_changed(); } /*** diff --git a/src/formatter-desktop.cpp b/src/formatter-desktop.cpp index 9a098c6..336d2d3 100644 --- a/src/formatter-desktop.cpp +++ b/src/formatter-desktop.cpp @@ -81,7 +81,7 @@ DesktopFormatter::DesktopFormatter(const std::shared_ptr& clock_ void DesktopFormatter::rebuildHeaderFormat() { - headerFormat.set(getHeaderLabelFormatString()); + header_format.set(getHeaderLabelFormatString()); } std::string DesktopFormatter::getHeaderLabelFormatString() const @@ -126,7 +126,7 @@ const gchar* DesktopFormatter::getFullTimeFormatString() const break; } - return getDefaultHeaderTimeFormat(twelvehour, show_seconds); + return default_header_time_format(twelvehour, show_seconds); } const gchar* DesktopFormatter::getDateFormat(bool show_day, bool show_date, bool show_year) const diff --git a/src/formatter.cpp b/src/formatter.cpp index 638eac4..9aa9bbb 100644 --- a/src/formatter.cpp +++ b/src/formatter.cpp @@ -122,8 +122,8 @@ public: m_owner(owner), m_clock(clock) { - m_owner->headerFormat.changed().connect([this](const std::string& /*fmt*/){update_header();}); - m_clock->minuteChanged.connect([this](){update_header();}); + m_owner->header_format.changed().connect([this](const std::string& /*fmt*/){update_header();}); + m_clock->minute_changed.connect([this](){update_header();}); update_header(); restartRelativeTimer(); @@ -149,7 +149,7 @@ private: void update_header() { // update the header property - const auto fmt = m_owner->headerFormat.get(); + const auto fmt = m_owner->header_format.get(); const auto str = m_clock->localtime().format(fmt); m_owner->header.set(str); @@ -197,7 +197,7 @@ private: static gboolean onRelativeTimer(gpointer gself) { auto self = static_cast(gself); - self->m_owner->relativeFormatChanged(); + self->m_owner->relative_format_changed(); self->restartRelativeTimer(); return G_SOURCE_REMOVE; } @@ -225,7 +225,7 @@ Formatter::~Formatter() } const char* -Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) +Formatter::default_header_time_format(bool twelvehour, bool show_seconds) { const char* fmt; @@ -250,7 +250,7 @@ Formatter::getDefaultHeaderTimeFormat(bool twelvehour, bool show_seconds) ***/ std::string -Formatter::getRelativeFormat(GDateTime* then_begin, GDateTime* then_end) const +Formatter::relative_format(GDateTime* then_begin, GDateTime* then_end) const { auto cstr = generate_full_format_string_at_time (p->m_clock->localtime().get(), then_begin, then_end); const std::string ret = cstr; diff --git a/src/menu.cpp b/src/menu.cpp index e92d398..91f7dd2 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -90,12 +90,12 @@ protected: m_formatter->header.changed().connect([this](const std::string&){ update_header(); }); - m_formatter->headerFormat.changed().connect([this](const std::string&){ + m_formatter->header_format.changed().connect([this](const std::string&){ update_section(Locations); // need to update x-canonical-time-format }); - m_formatter->relativeFormatChanged.connect([this](){ - update_section(Appointments); // uses formatter.getRelativeFormat() - update_section(Locations); // uses formatter.getRelativeFormat() + m_formatter->relative_format_changed.connect([this](){ + update_section(Appointments); // uses formatter.relative_format() + update_section(Locations); // uses formatter.relative_format() }); m_state->settings->show_clock.changed().connect([this](bool){ update_header(); // update header's label @@ -110,7 +110,7 @@ protected: m_state->planner->upcoming.changed().connect([this](const std::vector&){ update_section(Appointments); // "upcoming" is the list of Appointments we show }); - m_state->clock->dateChanged.connect([this](){ + m_state->clock->date_changed.connect([this](){ update_section(Calendar); // need to update the Date menuitem update_section(Locations); // locations' relative time may have changed }); @@ -305,7 +305,7 @@ private: GDateTime* begin = appt.begin(); GDateTime* end = appt.end(); - auto fmt = m_formatter->getRelativeFormat(begin, end); + auto fmt = m_formatter->relative_format(begin, end); auto unix_time = g_date_time_to_unix(begin); auto menu_item = g_menu_item_new (appt.summary.c_str(), nullptr); @@ -380,7 +380,7 @@ private: const auto& zone = location.zone(); const auto& name = location.name(); const auto zone_now = now.to_timezone(zone); - const auto fmt = m_formatter->getRelativeFormat(zone_now.get()); + const auto fmt = m_formatter->relative_format(zone_now.get()); auto detailed_action = g_strdup_printf("indicator.set-location::%s %s", zone.c_str(), name.c_str()); auto i = g_menu_item_new (name.c_str(), detailed_action); g_menu_item_set_attribute(i, "x-canonical-type", "s", "com.canonical.indicator.location"); diff --git a/src/planner-eds.cpp b/src/planner-eds.cpp index 98cfe0a..cb42d6e 100644 --- a/src/planner-eds.cpp +++ b/src/planner-eds.cpp @@ -227,7 +227,7 @@ private: { getAppointments(begin, end, [this](const std::vector& appointments) { g_debug("got %d appointments in this calendar month", (int)appointments.size()); - m_owner.thisMonth.set(appointments); + m_owner.this_month.set(appointments); }); } g_clear_pointer(&begin, g_date_time_unref); diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp index 4271374..4287e1c 100644 --- a/tests/test-clock.cpp +++ b/tests/test-clock.cpp @@ -54,12 +54,12 @@ TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute) LiveClock clock(zones); wait_msec(500); // wait for the bus to set up - // count how many times clock.minuteChanged() is emitted over the next minute + // count how many times clock.minute_changed() is emitted over the next minute const DateTime now = clock.localtime(); const auto gnow = now.get(); auto gthen = g_date_time_add_minutes(gnow, 1); int count = 0; - clock.minuteChanged.connect([&count](){count++;}); + clock.minute_changed.connect([&count](){count++;}); const auto msec = g_date_time_difference(gthen,gnow) / 1000; wait_msec(msec); EXPECT_EQ(1, count); @@ -95,7 +95,7 @@ TEST_F(ClockFixture, TimezoneChangeTriggersSkew) g_time_zone_unref(tz_nyc); /// change the timezones! - clock.minuteChanged.connect([this](){ + clock.minute_changed.connect([this](){ g_main_loop_quit(loop); }); g_idle_add([](gpointer gs){ @@ -124,7 +124,7 @@ TEST_F(ClockFixture, SleepTriggersSkew) wait_msec(500); // wait for the bus to set up bool skewed = false; - clock.minuteChanged.connect([&skewed, this](){ + clock.minute_changed.connect([&skewed, this](){ skewed = true; g_main_loop_quit(loop); return G_SOURCE_REMOVE; diff --git a/tests/test-formatter.cpp b/tests/test-formatter.cpp index 9950453..01df4f2 100644 --- a/tests/test-formatter.cpp +++ b/tests/test-formatter.cpp @@ -97,7 +97,7 @@ TEST_F(FormatterFixture, TestPhoneHeader) if(Set24hLocale()) { PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%H:%M"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("%H:%M"), formatter.header_format.get()); EXPECT_EQ(std::string("18:30"), formatter.header.get()); } @@ -105,7 +105,7 @@ TEST_F(FormatterFixture, TestPhoneHeader) if(Set12hLocale()) { PhoneFormatter formatter(clock); - EXPECT_EQ(std::string("%l:%M %p"), formatter.headerFormat.get()); + EXPECT_EQ(std::string("%l:%M %p"), formatter.header_format.get()); EXPECT_EQ(std::string(" 6:30 PM"), formatter.header.get()); } } @@ -156,7 +156,7 @@ TEST_F(FormatterFixture, TestDesktopHeader) m_settings->show_date.set(test_case.show_date); m_settings->show_year.set(test_case.show_year); - ASSERT_STREQ(test_case.expected_format_string, f.headerFormat.get().c_str()); + ASSERT_STREQ(test_case.expected_format_string, f.header_format.get().c_str()); } } } @@ -196,7 +196,7 @@ TEST_F(FormatterFixture, TestUpcomingTimes) std::shared_ptr clock (new MockClock(DateTime(test_case.now))); DesktopFormatter f(clock, m_settings); - const auto fmt = f.getRelativeFormat(test_case.then); + const auto fmt = f.relative_format(test_case.then); ASSERT_EQ(test_case.expected_format_string, fmt); g_clear_pointer(&test_case.now, g_date_time_unref); @@ -239,7 +239,7 @@ TEST_F(FormatterFixture, TestEventTimes) std::shared_ptr clock(new MockClock(DateTime(test_case.now))); DesktopFormatter f(clock, m_settings); - const auto fmt = f.getRelativeFormat(test_case.then, test_case.then_end); + const auto fmt = f.relative_format(test_case.then, test_case.then_end); ASSERT_STREQ(test_case.expected_format_string, fmt.c_str()); g_clear_pointer(&test_case.now, g_date_time_unref); diff --git a/tests/test-live-actions.cpp b/tests/test-live-actions.cpp index 562b358..eab8596 100644 --- a/tests/test-live-actions.cpp +++ b/tests/test-live-actions.cpp @@ -356,11 +356,11 @@ TEST_F(LiveActionsFixture, CalendarState) a2.begin = next_begin; a2.end = next_end; - m_state->planner->thisMonth.set(std::vector({a1, a2})); + m_state->planner->this_month.set(std::vector({a1, a2})); /// /// Now test the calendar state again. - /// The thisMonth field should now contain the appointments we just added. + /// The this_month field should now contain the appointments we just added. /// calendar_state = g_action_group_get_action_state (action_group, "calendar"); diff --git a/tests/test-planner.cpp b/tests/test-planner.cpp index 3072aea..b476ee8 100644 --- a/tests/test-planner.cpp +++ b/tests/test-planner.cpp @@ -47,9 +47,9 @@ TEST_F(PlannerFixture, EDS) planner.time.set(DateTime(now)); wait_msec(2500); - std::vector thisMonth = planner.thisMonth.get(); - std::cerr << thisMonth.size() << " appointments this month" << std::endl; - for(const auto& a : thisMonth) + std::vector this_month = planner.this_month.get(); + std::cerr << this_month.size() << " appointments this month" << std::endl; + for(const auto& a : this_month) std::cerr << a.summary << std::endl; } -- cgit v1.2.3 From 271b0fbf8b14a4f7a8f47de0e3a8751bd50676c3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 30 Jan 2014 18:40:13 -0600 Subject: copyediting: remove dead '#if 0''ed code --- src/locations.cpp | 23 ---------------- tests/test-settings.cpp | 6 ----- tests/test-timezone-geoclue.cpp | 59 ++++------------------------------------- 3 files changed, 5 insertions(+), 83 deletions(-) (limited to 'tests') diff --git a/src/locations.cpp b/src/locations.cpp index c59f988..0690acd 100644 --- a/src/locations.cpp +++ b/src/locations.cpp @@ -54,29 +54,6 @@ Location::Location(const std::string& zone_, const std::string& name_): g_time_zone_unref (gzone); } -#if 0 -DateTime Location::localtime(const DateTime& reference_point) const -{ -GDateTime * g_date_time_to_timezone (GDateTime *datetime, - GTimeZone *tz); - auto gzone = g_time_zone_new(zone().c_str()); - const auto gtime = reference_point.get(); - auto glocal = g_date_time_new (gzone, - g_date_time_get_year(gtime), - g_date_time_get_month(gtime), - g_date_time_get_day_of_month(gtime), - g_date_time_get_hour(gtime), - g_date_time_get_minute(gtime), - g_date_time_get_seconds(gtime)); - DateTime local(glocal); - g_date_time_unref(glocal); - g_message("reference: %zu", (size_t)reference_point.to_unix(), (size_t)local.to_unix()); - //g_date_time_unref(gtime); - g_time_zone_unref(gzone); - return local; -} -#endif - } // namespace datetime } // namespace indicator } // namespace unity diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp index df01cd7..980e7fa 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -189,9 +189,3 @@ TEST_F(SettingsFixture, Locations) g_strfreev(tmp); EXPECT_EQ(bv, vtmp); } - -#if 0 - core::Property> locations; - core::Property time_format_mode; - -#endif diff --git a/tests/test-timezone-geoclue.cpp b/tests/test-timezone-geoclue.cpp index 4bf08a7..3cc1393 100644 --- a/tests/test-timezone-geoclue.cpp +++ b/tests/test-timezone-geoclue.cpp @@ -21,64 +21,17 @@ #include -//#include - using unity::indicator::datetime::GeoclueTimezone; -/*** -**** -***/ - -class TimezoneGeoclueFixture : public GeoclueFixture -{ -}; - -#if 0 -namespace +// This test looks small because the interesting +// work is all happening in GeoclueFixture... +TEST_F(GeoclueFixture, ChangeDetected) { - struct EmitAddressChangedData - { - DbusTestDbusMock * mock = nullptr; - DbusTestDbusMockObject * obj_client = nullptr; - std::string timezone; - EmitAddressChangedData(DbusTestDbusMock * mock_, - DbusTestDbusMockObject * obj_client_, - const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {} - }; - - gboolean emit_address_changed_idle(gpointer gdata) - { - auto data = static_cast(gdata); - - GError * error = nullptr; - dbus_test_dbus_mock_object_emit_signal(data->mock, data->obj_client, - "org.freedesktop.Geoclue.Address", - "AddressChanged", - G_VARIANT_TYPE("(ia{ss}(idd))"), - g_variant_new_parsed("(1385238033, {'timezone': 'America/Chicago'}, (3, 0.0, 0.0))"), - &error); - if (error) - { - g_warning("%s: %s", G_STRFUNC, error->message); - g_error_free(error); - } - - delete data; - return G_SOURCE_REMOVE; - } -} -#endif - -TEST_F(TimezoneGeoclueFixture, ChangeDetected) -{ -// const std::string timezone_1 = "America/Denver"; - const std::string timezone_2 = "America/Chicago"; - GeoclueTimezone tz; wait_msec(500); // wait for the bus to get set up EXPECT_EQ(timezone_1, tz.timezone.get()); - // start listening for a timezone change, then change the timezone + // Start listening for a timezone change, then change the timezone. bool changed = false; auto connection = tz.timezone.changed().connect( @@ -88,10 +41,8 @@ TEST_F(TimezoneGeoclueFixture, ChangeDetected) g_main_loop_quit(loop); }); + const std::string timezone_2 = "America/Chicago"; setGeoclueTimezoneOnIdle(timezone_2); - //g_timeout_add(50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, timezone_2.c_str())); g_main_loop_run(loop); EXPECT_EQ(timezone_2, tz.timezone.get()); } - - -- cgit v1.2.3