aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt43
-rw-r--r--tests/Makefile.am.strings38
-rw-r--r--tests/actions-mock.h82
-rw-r--r--tests/geoclue-fixture.h150
-rw-r--r--tests/glib-fixture.h96
-rw-r--r--tests/manual-test-snap.cpp63
-rw-r--r--tests/planner-mock.c178
-rw-r--r--tests/planner-mock.h52
-rw-r--r--tests/state-fixture.h60
-rw-r--r--tests/state-mock.h43
-rw-r--r--tests/test-actions.cpp232
-rw-r--r--tests/test-clock-watcher.cpp166
-rw-r--r--tests/test-clock.cpp140
-rw-r--r--tests/test-dbus-fixture.h102
-rw-r--r--tests/test-exporter.cpp134
-rw-r--r--tests/test-formatter.cc98
-rw-r--r--tests/test-formatter.cpp256
-rw-r--r--tests/test-indicator.cc92
-rw-r--r--tests/test-live-actions.cpp403
-rw-r--r--tests/test-locations.cpp169
-rw-r--r--tests/test-menus.cpp524
-rw-r--r--tests/test-planner.cpp87
-rw-r--r--tests/test-settings.cpp191
-rw-r--r--tests/test-timezone-file.cpp133
-rw-r--r--tests/test-timezone-geoclue.cpp48
-rw-r--r--tests/test-timezones.cpp124
-rw-r--r--tests/test-utils.cc112
-rw-r--r--tests/test-utils.cpp98
28 files changed, 3319 insertions, 595 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6564a25..7d590c9 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -28,12 +28,45 @@ add_custom_command (OUTPUT gschemas.compiled
# 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})
-include_directories (${SERVICE_DEPS_INCLUDE_DIRS})
+include_directories (${DBUSTEST_INCLUDE_DIRS})
-# test-formatter
-set (TEST_NAME test-formatter)
-add_executable (${TEST_NAME} test-formatter.cc)
-add_test (${TEST_NAME} ${TEST_NAME})
+add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}")
+
+
+function(add_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} ${GTEST_LIBS})
+endfunction()
+add_test_by_name(test-actions)
+add_test_by_name(test-clock)
+add_test_by_name(test-clock-watcher)
+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)
+
+set (TEST_NAME manual-test-snap)
+add_executable (${TEST_NAME} ${TEST_NAME}.cpp)
add_dependencies (${TEST_NAME} libindicatordatetimeservice)
target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+# 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)
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..da93cb9
--- /dev/null
+++ b/tests/actions-mock.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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#ifndef INDICATOR_DATETIME_ACTIONS_MOCK_H
+#define INDICATOR_DATETIME_ACTIONS_MOCK_H
+
+#include <datetime/actions.h>
+
+#include <set>
+
+namespace unity {
+namespace indicator {
+namespace datetime {
+
+class MockActions: public Actions
+{
+public:
+ MockActions(std::shared_ptr<State>& state_in): Actions(state_in) {}
+ ~MockActions() =default;
+
+ enum Action { OpenDesktopSettings, OpenPhoneSettings, OpenPhoneClockApp,
+ OpenPlanner, OpenPlannerAt, OpenAppointment, SetLocation };
+ const std::vector<Action>& 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_;
+ }
+
+private:
+ std::string m_url;
+ std::string m_zone;
+ std::string m_name;
+ DateTime m_date_time;
+ std::vector<Action> m_history;
+};
+
+} // namespace datetime
+} // namespace indicator
+} // namespace unity
+
+#endif // INDICATOR_DATETIME_ACTIONS_MOCK_H
diff --git a/tests/geoclue-fixture.h b/tests/geoclue-fixture.h
new file mode 100644
index 0000000..0c597d3
--- /dev/null
+++ b/tests/geoclue-fixture.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glib-fixture.h"
+
+#include <libdbustest/dbus-test.h>
+
+class GeoclueFixture : public GlibFixture
+{
+ private:
+
+ typedef GlibFixture super;
+
+ GDBusConnection * bus = nullptr;
+
+ protected:
+
+ DbusTestService * service = nullptr;
+ DbusTestDbusMock * mock = 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 ()
+ {
+ super::SetUp();
+
+ GError * error = nullptr;
+ 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");
+
+ 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_geo_m, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &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());
+
+ 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);
+
+ 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 != nullptr)
+ {
+ g_object_unref (bus);
+ wait_msec (1000);
+ }
+
+ super::TearDown ();
+ }
+
+private:
+
+ struct EmitAddressChangedData
+ {
+ DbusTestDbusMock * mock = nullptr;
+ DbusTestDbusMockObject * obj_geo_addr = nullptr;
+ std::string 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)
+ {
+ auto data = static_cast<EmitAddressChangedData*>(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_geo_addr,
+ //"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_geo_addr, newZone.c_str()));
+ }
+
+};
+
diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h
index c6ecc68..1914b8c 100644
--- a/tests/glib-fixture.h
+++ b/tests/glib-fixture.h
@@ -25,96 +25,114 @@
#include <gtest/gtest.h>
+#include <locale.h> // setlocale()
+
class GlibFixture : public ::testing::Test
{
private:
- GLogFunc realLogHandler;
+ //GLogFunc realLogHandler;
protected:
std::map<GLogLevelFlags,int> logCounts;
- void testLogCount (GLogLevelFlags log_level, int expected G_GNUC_UNUSED)
+ void testLogCount(GLogLevelFlags log_level, int /*expected*/)
{
- ASSERT_EQ (expected, logCounts[log_level]);
+#if 0
+ 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", log_domain, (int)log_level, message);
+ g_print("%s - %d - %s\n", log_domain, (int)log_level, message);
static_cast<GlibFixture*>(self)->logCounts[log_level]++;
}
protected:
- virtual void SetUp ()
+ virtual void SetUp()
{
- loop = g_main_loop_new (NULL, FALSE);
+ setlocale(LC_ALL, "C.UTF-8");
+
+ 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
- ASSERT_EQ (0, logCounts[G_LOG_LEVEL_ERROR]);
- ASSERT_EQ (0, logCounts[G_LOG_LEVEL_CRITICAL]);
- ASSERT_EQ (0, logCounts[G_LOG_LEVEL_WARNING]);
- ASSERT_EQ (0, logCounts[G_LOG_LEVEL_MESSAGE]);
- ASSERT_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)
+ {
+ g_main_loop_quit(static_cast<GMainLoop*>(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
- guint handler_id = g_signal_connect_swapped (o, signal,
- G_CALLBACK(g_main_loop_quit),
- loop);
- gulong 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)
{
- guint id = g_timeout_add (msec, (GSourceFunc)g_main_loop_quit, 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;
+ GMainLoop * loop;
};
diff --git a/tests/manual-test-snap.cpp b/tests/manual-test-snap.cpp
new file mode 100644
index 0000000..51556cd
--- /dev/null
+++ b/tests/manual-test-snap.cpp
@@ -0,0 +1,63 @@
+
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <datetime/appointment.h>
+#include <datetime/snap.h>
+
+#include <glib.h>
+
+using namespace unity::indicator::datetime;
+
+/***
+****
+***/
+
+int main()
+{
+ Appointment a;
+ a.color = "green";
+ a.summary = "Alarm";
+ a.url = "alarm:///hello-world";
+ a.uid = "D4B57D50247291478ED31DED17FF0A9838DED402";
+ a.is_event = false;
+ a.is_daily = false;
+ a.has_alarms = true;
+ auto begin = g_date_time_new_local(2014,12,25,0,0,0);
+ auto end = g_date_time_add_full(begin,0,0,1,0,0,-1);
+ a.begin = begin;
+ a.end = end;
+ g_date_time_unref(end);
+ g_date_time_unref(begin);
+
+ auto loop = g_main_loop_new(nullptr, false);
+ auto show = [loop](const Appointment& appt){
+ g_message("You clicked 'show' for appt url '%s'", appt.url.c_str());
+ g_main_loop_quit(loop);
+ };
+ auto dismiss = [loop](const Appointment&){
+ g_message("You clicked 'dismiss'");
+ g_main_loop_quit(loop);
+ };
+
+ Snap snap;
+ snap(a, show, dismiss);
+ g_main_loop_run(loop);
+ return 0;
+}
diff --git a/tests/planner-mock.c b/tests/planner-mock.c
deleted file mode 100644
index e67ad7e..0000000
--- a/tests/planner-mock.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright 2013 Canonical Ltd.
- *
- * Authors:
- * Charles Kerr <charles.kerr@canonical.com>
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3, as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranties of
- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#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 G_GNUC_UNUSED,
- 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 G_GNUC_UNUSED,
- 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_GNUC_UNUSED)
-{
- g_message ("%s %s", G_STRLOC, G_STRFUNC);
-}
-
-static void
-my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
- 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/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 <charles.kerr@canonical.com>
- *
* 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
*/
-#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 <datetime/planner.h>
-#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-fixture.h b/tests/state-fixture.h
new file mode 100644
index 0000000..7d8358e
--- /dev/null
+++ b/tests/state-fixture.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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "glib-fixture.h"
+
+#include "actions-mock.h"
+#include "state-mock.h"
+
+using namespace unity::indicator::datetime;
+
+class StateFixture: public GlibFixture
+{
+private:
+ typedef GlibFixture super;
+
+protected:
+ std::shared_ptr<MockState> m_mock_state;
+ std::shared_ptr<State> m_state;
+ std::shared_ptr<MockActions> m_mock_actions;
+ std::shared_ptr<Actions> m_actions;
+
+ virtual void SetUp()
+ {
+ super::SetUp();
+
+ m_mock_state.reset(new MockState);
+ m_state = std::dynamic_pointer_cast<State>(m_mock_state);
+
+ m_mock_actions.reset(new MockActions(m_state));
+ m_actions = std::dynamic_pointer_cast<Actions>(m_mock_actions);
+ }
+
+ virtual void TearDown()
+ {
+ m_actions.reset();
+ m_mock_actions.reset();
+
+ m_state.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..721b82f
--- /dev/null
+++ b/tests/state-mock.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "planner-mock.h"
+
+#include <datetime/clock-mock.h>
+#include <datetime/state.h>
+
+using namespace unity::indicator::datetime;
+
+class MockState: public State
+{
+public:
+ std::shared_ptr<MockClock> mock_clock;
+
+ MockState()
+ {
+ const DateTime now = DateTime::NowLocal();
+ mock_clock.reset(new MockClock(now));
+ settings.reset(new Settings);
+ clock = std::dynamic_pointer_cast<Clock>(mock_clock);
+ planner.reset(new MockPlanner);
+ planner->time = now;
+ locations.reset(new Locations);
+ }
+};
+
diff --git a/tests/test-actions.cpp b/tests/test-actions.cpp
new file mode 100644
index 0000000..1865cfd
--- /dev/null
+++ b/tests/test-actions.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <datetime/actions.h>
+
+#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<MockActions::Action>({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)
+{
+ // 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));
+
+ // 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, 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;
+ appt.uid = "some arbitrary uid";
+ appt.url = "http://www.canonical.com/";
+ m_state->planner->upcoming.set(std::vector<Appointment>({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-watcher.cpp b/tests/test-clock-watcher.cpp
new file mode 100644
index 0000000..79b8485
--- /dev/null
+++ b/tests/test-clock-watcher.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2014 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <datetime/clock-watcher.h>
+
+#include <gtest/gtest.h>
+
+#include "state-fixture.h"
+
+using namespace unity::indicator::datetime;
+
+class ClockWatcherFixture: public StateFixture
+{
+private:
+
+ typedef StateFixture super;
+
+protected:
+
+ std::vector<std::string> m_triggered;
+ std::unique_ptr<ClockWatcher> m_watcher;
+
+ void SetUp()
+ {
+ super::SetUp();
+
+ m_watcher.reset(new ClockWatcherImpl(m_state));
+ m_watcher->alarm_reached().connect([this](const Appointment& appt){
+ m_triggered.push_back(appt.uid);
+ });
+
+ EXPECT_TRUE(m_triggered.empty());
+ }
+
+ void TearDown()
+ {
+ m_triggered.clear();
+ m_watcher.reset();
+
+ super::TearDown();
+ }
+
+ std::vector<Appointment> build_some_appointments()
+ {
+ const auto now = m_state->clock->localtime();
+ 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; // 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 = tomorrow_begin;
+ a1.end = tomorrow_end;
+
+ auto ubermorgen_begin = g_date_time_add_days (tomorrow, 1);
+ auto ubermorgen_end = g_date_time_add_full (tomorrow_begin, 0, 0, 1, 0, 0, -1);
+
+ 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 = ubermorgen_begin;
+ a2.end = ubermorgen_end;
+
+ // cleanup
+ g_date_time_unref(ubermorgen_end);
+ g_date_time_unref(ubermorgen_begin);
+ g_date_time_unref(tomorrow_end);
+ g_date_time_unref(tomorrow_begin);
+ g_date_time_unref(tomorrow);
+
+ return std::vector<Appointment>({a1, a2});
+ }
+};
+
+/***
+****
+***/
+
+TEST_F(ClockWatcherFixture, AppointmentsChanged)
+{
+ // Add some appointments to the planner.
+ // One of these matches our state's localtime, so that should get triggered.
+ std::vector<Appointment> a = build_some_appointments();
+ a[0].begin = m_state->clock->localtime();
+ m_state->planner->upcoming.set(a);
+
+ // Confirm that it got fired
+ EXPECT_EQ(1, m_triggered.size());
+ EXPECT_EQ(a[0].uid, m_triggered[0]);
+}
+
+
+TEST_F(ClockWatcherFixture, TimeChanged)
+{
+ // Add some appointments to the planner.
+ // Neither of these match the state's localtime, so nothing should be triggered.
+ std::vector<Appointment> a = build_some_appointments();
+ m_state->planner->upcoming.set(a);
+ EXPECT_TRUE(m_triggered.empty());
+
+ // Set the state's clock to a time that matches one of the appointments.
+ // That appointment should get triggered.
+ m_mock_state->mock_clock->set_localtime(a[1].begin);
+ EXPECT_EQ(1, m_triggered.size());
+ EXPECT_EQ(a[1].uid, m_triggered[0]);
+}
+
+
+TEST_F(ClockWatcherFixture, MoreThanOne)
+{
+ const auto now = m_state->clock->localtime();
+ std::vector<Appointment> a = build_some_appointments();
+ a[0].begin = a[1].begin = now;
+ m_state->planner->upcoming.set(a);
+
+ EXPECT_EQ(2, m_triggered.size());
+ EXPECT_EQ(a[0].uid, m_triggered[0]);
+ EXPECT_EQ(a[1].uid, m_triggered[1]);
+}
+
+
+TEST_F(ClockWatcherFixture, NoDuplicates)
+{
+ // Setup: add an appointment that gets triggered.
+ const auto now = m_state->clock->localtime();
+ const std::vector<Appointment> appointments = build_some_appointments();
+ std::vector<Appointment> a;
+ a.push_back(appointments[0]);
+ a[0].begin = now;
+ m_state->planner->upcoming.set(a);
+ EXPECT_EQ(1, m_triggered.size());
+ EXPECT_EQ(a[0].uid, m_triggered[0]);
+
+ // Now change the appointment vector by adding one to it.
+ // Confirm that the ClockWatcher doesn't re-trigger a[0]
+ a.push_back(appointments[1]);
+ m_state->planner->upcoming.set(a);
+ EXPECT_EQ(1, m_triggered.size());
+ EXPECT_EQ(a[0].uid, m_triggered[0]);
+}
diff --git a/tests/test-clock.cpp b/tests/test-clock.cpp
new file mode 100644
index 0000000..a4924b3
--- /dev/null
+++ b/tests/test-clock.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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <datetime/clock.h>
+#include <datetime/timezones.h>
+
+#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),
+ nullptr,
+ "/org/freedesktop/login1", // object path
+ "org.freedesktop.login1.Manager", // interface
+ "PrepareForSleep", // signal name
+ g_variant_new("(b)", FALSE),
+ nullptr);
+ }
+};
+
+TEST_F(ClockFixture, MinuteChangedSignalShouldTriggerOncePerMinute)
+{
+ // start up a live clock
+ std::shared_ptr<Timezones> zones(new Timezones);
+ zones->timezone.set("America/New_York");
+ LiveClock clock(zones);
+ wait_msec(500); // wait for the bus to set up
+
+ // 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.minute_changed.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);
+}
+
+/***
+****
+***/
+
+#define TIMEZONE_FILE (SANDBOX"/timezone")
+
+TEST_F(ClockFixture, HelloFixture)
+{
+ std::shared_ptr<Timezones> zones(new Timezones);
+ zones->timezone.set("America/New_York");
+ LiveClock clock(zones);
+}
+
+
+TEST_F(ClockFixture, TimezoneChangeTriggersSkew)
+{
+ std::shared_ptr<Timezones> 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.minute_changed.connect([this](){
+ g_main_loop_quit(loop);
+ });
+ g_idle_add([](gpointer gs){
+ static_cast<Timezones*>(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<Timezones> 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.minute_changed.connect([&skewed, this](){
+ skewed = true;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ });
+
+ g_idle_add([](gpointer gself){
+ static_cast<ClockFixture*>(gself)->emitPrepareForSleep();
+ return G_SOURCE_REMOVE;
+ }, this);
+
+ g_main_loop_run(loop);
+ EXPECT_TRUE(skewed);
+}
diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h
new file mode 100644
index 0000000..db06be2
--- /dev/null
+++ b/tests/test-dbus-fixture.h
@@ -0,0 +1,102 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "glib-fixture.h"
+
+/***
+****
+***/
+
+class TestDBusFixture: public GlibFixture
+{
+ public:
+
+ TestDBusFixture() {}
+
+ TestDBusFixture(const std::vector<std::string>& 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<TestDBusFixture*>(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<TestDBusFixture*>(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<std::string> 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 ()
+ {
+ 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);
+
+ // tear down the test dbus
+ g_test_dbus_down(test_dbus);
+ g_clear_object(&test_dbus);
+
+ super::TearDown();
+ }
+};
diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp
new file mode 100644
index 0000000..104fb4b
--- /dev/null
+++ b/tests/test-exporter.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "actions-mock.h"
+#include "state-mock.h"
+#include "glib-fixture.h"
+
+#include <datetime/actions.h>
+#include <datetime/dbus-shared.h>
+#include <datetime/exporter.h>
+
+#include <set>
+#include <string>
+
+using namespace unity::indicator::datetime;
+
+class ExporterFixture: public GlibFixture
+{
+private:
+
+ typedef GlibFixture super;
+
+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()
+ {
+ 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);
+
+ super::TearDown();
+ }
+};
+
+TEST_F(ExporterFixture, HelloWorld)
+{
+ // confirms that the Test DBus SetUp() and TearDown() works
+}
+
+TEST_F(ExporterFixture, Publish)
+{
+ std::shared_ptr<State> state(new MockState);
+ std::shared_ptr<Actions> actions(new MockActions(state));
+ std::vector<std::shared_ptr<Menu>> menus;
+
+ MenuFactory menu_factory (actions, state);
+ for(int i=0; i<Menu::NUM_PROFILES; i++)
+ menus.push_back(menu_factory.buildMenu(Menu::Profile(i)));
+
+ 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<std::string> 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"));
+
+ // try closing the connection prematurely
+ // to test Exporter's name-lost signal
+ bool name_lost = false;
+ exporter.name_lost.connect([this,&name_lost](){
+ name_lost = true;
+ g_main_loop_quit(loop);
+ });
+ g_dbus_connection_close_sync(connection, nullptr, nullptr);
+ g_main_loop_run(loop);
+ EXPECT_TRUE(name_lost);
+
+ // 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 6a408ab..0000000
--- a/tests/test-formatter.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-
-/*
- * Copyright 2013 Canonical Ltd.
- *
- * Authors:
- * Charles Kerr <charles.kerr@canonical.com>
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3, as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranties of
- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <langinfo.h>
-#include <locale.h>
-
-#include <glib/gi18n.h>
-
-#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);
- }
-}
diff --git a/tests/test-formatter.cpp b/tests/test-formatter.cpp
new file mode 100644
index 0000000..01df4f2
--- /dev/null
+++ b/tests/test-formatter.cpp
@@ -0,0 +1,256 @@
+
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glib-fixture.h"
+
+#include <datetime/clock-mock.h>
+#include <datetime/formatter.h>
+#include <datetime/settings.h>
+
+#include <glib/gi18n.h>
+
+#include <langinfo.h>
+#include <locale.h>
+
+using namespace unity::indicator::datetime;
+
+/***
+****
+***/
+
+class FormatterFixture: public GlibFixture
+{
+ private:
+
+ typedef GlibFixture super;
+ gchar* m_original_locale = nullptr;
+
+ protected:
+
+ std::shared_ptr<Settings> 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> 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.header_format.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.header_format.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> 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.header_format.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> clock (new MockClock(DateTime(test_case.now)));
+ DesktopFormatter f(clock, m_settings);
+
+ 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);
+ 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> clock(new MockClock(DateTime(test_case.now)));
+ DesktopFormatter f(clock, m_settings);
+
+ 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);
+ 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-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 <charles.kerr@canonical.com>
-
-This program is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License version 3, as published
-by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranties of
-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along
-with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <gtest/gtest.h>
-
-#include <glib-object.h>
-
-/***
-****
-***/
-
-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<IndicatorTest*>(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-live-actions.cpp b/tests/test-live-actions.cpp
new file mode 100644
index 0000000..eab8596
--- /dev/null
+++ b/tests/test-live-actions.cpp
@@ -0,0 +1,403 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <datetime/actions-live.h>
+
+#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>& 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<LiveActionsFixture*>(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<LiveActionsFixture*>(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<LiveActionsFixture*>(gself);
+ self->name_acquired = false;
+ }
+
+ static void on_bus_closed(GObject* /*object*/,
+ GAsyncResult* res,
+ gpointer gself)
+ {
+ auto self = static_cast<LiveActionsFixture*>(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<LiveActionsFixture*>(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<MockState> m_mock_state;
+ std::shared_ptr<State> m_state;
+ std::shared_ptr<MockLiveActions> m_live_actions;
+ std::shared_ptr<Actions> 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>"
+ " <interface name='org.freedesktop.timedate1'>"
+ " <method name='SetTimezone'>"
+ " <arg name='timezone' type='s' direction='in'/>"
+ " <arg name='user_interaction' type='b' direction='in'/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+ 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<State>(m_mock_state);
+ m_live_actions.reset(new MockLiveActions(m_state));
+ m_actions = std::dynamic_pointer_cast<Actions>(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);
+}
+
+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->this_month.set(std::vector<Appointment>({a1, a2}));
+
+ ///
+ /// Now test the calendar state again.
+ /// The this_month 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);
+ }
+}
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 <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glib-fixture.h"
+
+#include <datetime/locations-settings.h>
+
+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<Settings> m_settings;
+ std::shared_ptr<Timezones> 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<std::string>({ 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<Location>&){
+ locations_changed = true;
+ g_main_loop_quit(loop);
+ });
+
+ g_idle_add([](gpointer settings){
+ static_cast<Settings*>(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<Location>&){
+ locations_changed = true;
+ g_main_loop_quit(loop);
+ });
+
+ g_idle_add([](gpointer settings){
+ static_cast<Settings*>(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.cpp b/tests/test-menus.cpp
new file mode 100644
index 0000000..73d6036
--- /dev/null
+++ b/tests/test-menus.cpp
@@ -0,0 +1,524 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+
+#include "actions-mock.h"
+#include "state-fixture.h"
+
+#include <datetime/clock-mock.h>
+#include <datetime/locations.h>
+#include <datetime/menu.h>
+#include <datetime/state.h>
+
+#include <gio/gio.h>
+
+using namespace unity::indicator::datetime;
+
+class MenuFixture: public StateFixture
+{
+private:
+ typedef StateFixture super;
+
+protected:
+ std::shared_ptr<MenuFactory> m_menu_factory;
+ std::vector<std::shared_ptr<Menu>> 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; i<Menu::NUM_PROFILES; i++)
+ m_menus.push_back(m_menu_factory->buildMenu(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))
+ && (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);
+ 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(&section);
+
+ // 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(&section);
+
+ // cleanup
+ g_object_unref(submenu);
+ }
+
+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);
+ }
+
+ std::vector<Appointment> 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);
+ 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;
+
+ 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;
+
+ return std::vector<Appointment>({a1, a2});
+ }
+
+ 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())
+ {
+ 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);
+
+ // 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);
+ }
+ }
+
+ void InspectAppointmentMenuItems(GMenuModel* section,
+ int first_appt_index,
+ const std::vector<Appointment>& 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(); i<n; i++)
+ InspectAppointmentMenuItem(section, first_appt_index+i, appointments[i]);
+
+ //g_clear_object(&section);
+ //g_clear_object(&submenu);
+ }
+
+ void InspectDesktopAppointments(GMenuModel* menu_model)
+ {
+ // 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(&section);
+
+ // when "show_events" is true,
+ // there should be an "add event" button even if there aren't any appointments
+ std::vector<Appointment> 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(&section);
+
+ // 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(&section);
+
+ // 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<Appointment> 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(&section);
+
+ // 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(&section);
+
+ // 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<Location>& 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<n; i++)
+ {
+ gchar* str = nullptr;
+
+ // confirm that the x-canonical-type is right
+ g_menu_model_get_item_attribute(section, i, "x-canonical-type", "s", &str);
+ EXPECT_STREQ("com.canonical.indicator.location", str);
+ g_clear_pointer(&str, g_free);
+
+ // confirm that the timezones match the ones in the vector
+ g_menu_model_get_item_attribute(section, i, "x-canonical-timezone", "s", &str);
+ EXPECT_EQ(locations[i].zone(), str);
+ g_clear_pointer(&str, g_free);
+
+ // confirm that x-canonical-time-format has some kind of time format string
+ g_menu_model_get_item_attribute(section, i, "x-canonical-time-format", "s", &str);
+ EXPECT_TRUE(str && *str && (strchr(str,'%')!=nullptr));
+ g_clear_pointer(&str, g_free);
+ }
+
+ g_clear_object(&section);
+ g_clear_object(&submenu);
+ }
+
+ void InspectLocations(GMenuModel* menu_model, Menu::Profile profile)
+ {
+ const bool locations_expected = profile == Menu::Desktop;
+
+ // when there aren't any locations, confirm the menu is empty
+ const std::vector<Location> 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<Location> 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(&section);
+ g_object_unref(submenu);
+ }
+};
+
+
+TEST_F(MenuFixture, HelloWorld)
+{
+ EXPECT_EQ(Menu::NUM_PROFILES, m_menus.size());
+ for (int i=0; i<Menu::NUM_PROFILES; i++)
+ {
+ EXPECT_TRUE(m_menus[i] != false);
+ EXPECT_TRUE(m_menus[i]->menu_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)
+{
+ 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());
+}
+
+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.cpp b/tests/test-planner.cpp
new file mode 100644
index 0000000..1923ba1
--- /dev/null
+++ b/tests/test-planner.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glib-fixture.h"
+
+#include <datetime/appointment.h>
+#include <datetime/clock-mock.h>
+#include <datetime/date-time.h>
+#include <datetime/planner.h>
+#include <datetime/planner-eds.h>
+
+#include <langinfo.h>
+#include <locale.h>
+
+using namespace unity::indicator::datetime;
+
+/***
+****
+***/
+
+typedef GlibFixture PlannerFixture;
+
+TEST_F(PlannerFixture, EDS)
+{
+ auto tmp = g_date_time_new_now_local();
+ const auto now = DateTime(tmp);
+ g_date_time_unref(tmp);
+
+ std::shared_ptr<Clock> clock(new MockClock(now));
+ PlannerEds planner(clock);
+ wait_msec(100);
+
+ planner.time.set(now);
+ wait_msec(2500);
+
+ std::vector<Appointment> 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;
+}
+
+
+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.cpp b/tests/test-settings.cpp
new file mode 100644
index 0000000..707247d
--- /dev/null
+++ b/tests/test-settings.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glib-fixture.h"
+
+#include <datetime/settings-live.h>
+#include <datetime/settings-shared.h>
+
+using namespace unity::indicator::datetime;
+
+/***
+****
+***/
+
+class SettingsFixture: public GlibFixture
+{
+private:
+ typedef GlibFixture super;
+
+protected:
+
+ std::shared_ptr<LiveSettings> m_live;
+ std::shared_ptr<Settings> 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<Settings>(m_live);
+ }
+
+ virtual void TearDown()
+ {
+ g_clear_object(&m_gsettings);
+ m_settings.reset();
+ m_live.reset();
+
+ super::TearDown();
+ }
+
+ void TestBoolProperty(core::Property<bool>& property, const gchar* key)
+ {
+ EXPECT_EQ(g_settings_get_boolean(m_gsettings, key), property.get());
+ g_settings_set_boolean(m_gsettings, key, false);
+ EXPECT_FALSE(property.get());
+ g_settings_set_boolean(m_gsettings, key, true);
+ EXPECT_TRUE(property.get());
+
+ property.set(false);
+ EXPECT_FALSE(g_settings_get_boolean(m_gsettings, key));
+ property.set(true);
+ EXPECT_TRUE(g_settings_get_boolean(m_gsettings, key));
+ }
+
+ void TestStringProperty(core::Property<std::string>& 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<std::string> strv_to_vector(const gchar** strv)
+ {
+ std::vector<std::string> 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", nullptr};
+ const gchar* bstrv[] = {"America/Denver", "Europe/London London", "Europe/Berlin Berlin", nullptr};
+ const std::vector<std::string> av = strv_to_vector(astrv);
+ const std::vector<std::string> 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);
+}
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 <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "glib-fixture.h"
+
+#include <datetime/timezone-file.h>
+
+//#include <condition_variable>
+//#include <mutex>
+//#include <queue>
+//#include <string>
+//#include <thread>
+//#include <iostream>
+//#include <istream>
+//#include <fstream>
+
+#include <cstdio> // fopen()
+//#include <sys/stat.h> // chmod()
+#include <unistd.h> // 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<TimezoneFixture*>(gself)->set_file("America/New_York");
+ // static_cast<FileTimezone*>(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.cpp b/tests/test-timezone-geoclue.cpp
new file mode 100644
index 0000000..3cc1393
--- /dev/null
+++ b/tests/test-timezone-geoclue.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "geoclue-fixture.h"
+
+#include <datetime/timezone-geoclue.h>
+
+using unity::indicator::datetime::GeoclueTimezone;
+
+// This test looks small because the interesting
+// work is all happening in GeoclueFixture...
+TEST_F(GeoclueFixture, ChangeDetected)
+{
+ 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);
+ });
+
+ const std::string timezone_2 = "America/Chicago";
+ setGeoclueTimezoneOnIdle(timezone_2);
+ g_main_loop_run(loop);
+ EXPECT_EQ(timezone_2, tz.timezone.get());
+}
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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "geoclue-fixture.h"
+
+#include <datetime/settings.h>
+#include <datetime/timezones-live.h>
+
+#include <memory> // std::shared_ptr
+
+#include <cstdio> // fopen()
+#include <unistd.h> // 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> 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<std::string> 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<std::string>& timezones) {
+ zones_changed = true;
+ zones = timezones;
+ g_main_loop_quit(loop);
+ });
+
+ g_idle_add([](gpointer s_in) {
+ auto s = static_cast<Settings*>(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<const char*>(str)); return G_SOURCE_REMOVE;}, const_cast<char*>(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 d0f277b..0000000
--- a/tests/test-utils.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
-Copyright 2012 Canonical Ltd.
-
-Authors:
- Charles Kerr <charles.kerr@canonical.com>
-
-This program is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License version 3, as published
-by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranties of
-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
-PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along
-with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <gtest/gtest.h>
-
-#include <glib-object.h>
-
-#include "utils.h"
-
-/***
-****
-***/
-
-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<n; i++)
- {
- char * zone = NULL;
- char * name = NULL;
-
- split_settings_location (test_cases[i].location, &zone, &name);
- ASSERT_STREQ (test_cases[i].expected_zone, zone);
- ASSERT_STREQ (test_cases[i].expected_name, name);
-
- g_free (zone);
- g_free (name);
- }
-}
-
-/***
-****
-***/
-
-#define EM_SPACE "\xE2\x80\x82"
-
-TEST (UtilsTest, GenerateTerseFormatString)
-{
- guint i;
- guint n;
- GDateTime * arbitrary_day = g_date_time_new_local (2013, 6, 25, 12, 34, 56);
- GDateTime * on_the_hour = g_date_time_new_local (2013, 6, 25, 12, 0, 0);
-
- struct {
- GDateTime * now;
- GDateTime * time;
- const char * expected_format_string;
- } test_cases[] = {
- { g_date_time_ref(arbitrary_day), g_date_time_ref(arbitrary_day), "%l:%M %p" }, /* identical time */
- { g_date_time_ref(arbitrary_day), g_date_time_add_hours(arbitrary_day,1), "%l:%M %p" }, /* later today */
- { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,1), "Tomorrow" EM_SPACE "%l:%M %p" }, /* tomorrow */
- { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,2), "%a" EM_SPACE "%l:%M %p" },
- { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,6), "%a" EM_SPACE "%l:%M %p" },
- { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,7), "%d %b" EM_SPACE "%l:%M %p" }, /* over one week away */
-
- { g_date_time_ref(on_the_hour), g_date_time_ref(on_the_hour), "%l %p" }, /* identical time */
- { g_date_time_ref(on_the_hour), g_date_time_add_hours(on_the_hour,1), "%l %p" }, /* later today */
- { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,1), "Tomorrow" EM_SPACE "%l %p" }, /* tomorrow */
- { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,2), "%a" EM_SPACE "%l %p" },
- { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,6), "%a" EM_SPACE "%l %p" },
- { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,7), "%d %b" EM_SPACE "%l %p" }, /* over one week away */
- };
-
- for (i=0, n=G_N_ELEMENTS(test_cases); i<n; i++)
- {
- char * format_string;
-
- format_string = generate_terse_format_string_at_time (test_cases[i].now,
- test_cases[i].time);
-
- ASSERT_STREQ (test_cases[i].expected_format_string, format_string);
-
- g_free (format_string);
- g_date_time_unref (test_cases[i].now);
- g_date_time_unref (test_cases[i].time);
- }
-
- g_date_time_unref (arbitrary_day);
- g_date_time_unref (on_the_hour);
-}
diff --git a/tests/test-utils.cpp b/tests/test-utils.cpp
new file mode 100644
index 0000000..97f07ed
--- /dev/null
+++ b/tests/test-utils.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <datetime/settings-shared.h>
+#include <datetime/utils.h>
+
+#include <gtest/gtest.h>
+
+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);
+ }
+}
+
+namespace
+{
+ struct {
+ const char* timezone;
+ const char* location;
+ const char* expected_name;
+ } beautify_timezone_test_cases[] = {
+ { "America/Chicago", nullptr, "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" }
+ };
+}
+
+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);
+}