aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2013-12-17 22:10:18 -0600
committerCharles Kerr <charles.kerr@canonical.com>2013-12-17 22:10:18 -0600
commit3b8833efe6ab21387b6f73b4a4ef757445801623 (patch)
treeb518c7210850d1f2af1b88f52e391a6c6121381a /tests
parent81c3d4ca5ee8f43e3996bec3be02c566a5e33a4c (diff)
downloadayatana-indicator-datetime-3b8833efe6ab21387b6f73b4a4ef757445801623.tar.gz
ayatana-indicator-datetime-3b8833efe6ab21387b6f73b4a4ef757445801623.tar.bz2
ayatana-indicator-datetime-3b8833efe6ab21387b6f73b4a4ef757445801623.zip
add geoclue, glib test fixtures
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt76
-rw-r--r--tests/geoclue-fixture.h142
-rw-r--r--tests/glib-fixture.h131
-rw-r--r--tests/test-core.cc148
-rw-r--r--tests/test-skew.cc209
-rw-r--r--tests/test-timezones.cc122
6 files changed, 825 insertions, 3 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 682896b..a424858 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,3 +1,12 @@
+# build libgtest
+add_library (gtest STATIC
+ ${GTEST_SOURCE_DIR}/gtest-all.cc
+ ${GTEST_SOURCE_DIR}/gtest_main.cc)
+set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR})
+set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w)
+
+SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -g ${CC_WARNING_ARGS}")
+
# build the necessary schemas
set_directory_properties (PROPERTIES
ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled)
@@ -12,12 +21,73 @@ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compil
OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE
OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_command (OUTPUT gschemas.compiled
- DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.session.gschema.xml
+ DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.datetime.gschema.xml
COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR}
COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR})
-# look for hearder in our src dir, and also in the directories where we autogenerate files...
+# look for headers in our src dir, and also in the directories where we autogenerate files...
include_directories (${CMAKE_SOURCE_DIR}/src)
-include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS})
+include_directories (${CMAKE_CURRENT_BINARY_DIR})
+include_directories (${DBUSTEST_INCLUDE_DIRS})
+
+add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}")
+
+# test-core
+set (TEST_NAME test-core)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+
+# test-timezone-file
+set (TEST_NAME test-timezone-file)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+
+# test-timezone-geoclue
+set (TEST_NAME test-timezone-geoclue)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS})
+
+# test-timezones
+set (TEST_NAME test-timezones)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${DBUSTEST_LIBRARIES} ${GTEST_LIBS})
+
+# test-clock
+set (TEST_NAME test-clock)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+
+# test-formatter
+set (TEST_NAME test-formatter)
+add_executable (${TEST_NAME} test-formatter.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+add_dependencies (${TEST_NAME} libindicatordatetimeservice)
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+
+# test-planner
+set (TEST_NAME test-planner)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+add_dependencies (${TEST_NAME} libindicatordatetimeservice)
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+
+# test-planner-eds
+set (TEST_NAME test-planner-eds)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+add_dependencies (${TEST_NAME} libindicatordatetimeservice)
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+
+# test-locations
+set (TEST_NAME test-locations)
+add_executable (${TEST_NAME} ${TEST_NAME}.cc)
+add_test (${TEST_NAME} ${TEST_NAME})
+add_dependencies (${TEST_NAME} libindicatordatetimeservice)
+target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
diff --git a/tests/geoclue-fixture.h b/tests/geoclue-fixture.h
new file mode 100644
index 0000000..890204a
--- /dev/null
+++ b/tests/geoclue-fixture.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <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_master = nullptr;
+ DbusTestDbusMockObject * obj_client = nullptr;
+ const std::string timezone_1 = "America/Denver";
+
+ void SetUp ()
+ {
+ super::SetUp();
+
+ GError * error = nullptr;
+ const gchar * const client_path = "/org/freedesktop/Geoclue/Master/client0";
+ GString * gstr = g_string_new (nullptr);
+
+ service = dbus_test_service_new (nullptr);
+ mock = dbus_test_dbus_mock_new ("org.freedesktop.Geoclue.Master");
+
+ obj_master = dbus_test_dbus_mock_get_object (mock,
+ "/org/freedesktop/Geoclue/Master",
+ "org.freedesktop.Geoclue.Master",
+ nullptr);
+ g_string_printf (gstr, "ret = '%s'", client_path);
+ dbus_test_dbus_mock_object_add_method (mock, obj_master, nullptr, "Create", nullptr, G_VARIANT_TYPE_OBJECT_PATH, gstr->str, &error);
+
+ obj_client = dbus_test_dbus_mock_get_object (mock, client_path, "org.freedesktop.Geoclue.MasterClient", nullptr);
+ dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "SetRequirements", G_VARIANT_TYPE("(iibi)"), nullptr, "", &error);
+ dbus_test_dbus_mock_object_add_method (mock, obj_client, nullptr, "AddressStart", nullptr, nullptr, "", &error);
+ dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue", "AddReference", nullptr, nullptr, "", &error);
+ g_string_printf (gstr, "ret = (1385238033, {'timezone': '%s'}, (3, 0.0, 0.0))", timezone_1.c_str());
+ dbus_test_dbus_mock_object_add_method (mock, obj_client, "org.freedesktop.Geoclue.Address", "GetAddress", nullptr, G_VARIANT_TYPE("(ia{ss}(idd))"), gstr->str, &error);
+
+ dbus_test_service_add_task(service, DBUS_TEST_TASK(mock));
+ dbus_test_service_start_tasks(service);
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, nullptr);
+ g_dbus_connection_set_exit_on_close (bus, FALSE);
+ g_object_add_weak_pointer (G_OBJECT(bus), (gpointer*)&bus);
+
+ g_string_free (gstr, TRUE);
+ }
+
+ virtual void TearDown ()
+ {
+ g_clear_object (&mock);
+ g_clear_object (&service);
+ g_object_unref (bus);
+
+ unsigned int cleartry = 0;
+ while (bus != nullptr && cleartry < 10)
+ {
+ wait_msec (100);
+ cleartry++;
+ }
+
+ // I've looked and can't find where this extra ref is coming from.
+ // is there an unbalanced ref to the bus in the test harness?!
+ while (bus != NULL)
+ {
+ g_object_unref (bus);
+ wait_msec (1000);
+ }
+
+ super::TearDown ();
+ }
+
+private:
+
+ struct EmitAddressChangedData
+ {
+ DbusTestDbusMock * mock = nullptr;
+ DbusTestDbusMockObject * obj_client = nullptr;
+ std::string timezone;
+ EmitAddressChangedData(DbusTestDbusMock * mock_,
+ DbusTestDbusMockObject * obj_client_,
+ const std::string& timezone_): mock(mock_), obj_client(obj_client_), timezone(timezone_) {}
+ };
+
+ static gboolean emit_address_changed_idle (gpointer gdata)
+ {
+ auto data = static_cast<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_client,
+ "org.freedesktop.Geoclue.Address",
+ "AddressChanged",
+ G_VARIANT_TYPE("(ia{ss}(idd))"),
+ g_variant_new_parsed (fmt),
+ &error);
+ if (error)
+ {
+ g_warning("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+
+ g_free (fmt);
+ delete data;
+ return G_SOURCE_REMOVE;
+ }
+
+public:
+
+ void setGeoclueTimezoneOnIdle (const std::string& newZone)
+ {
+ g_timeout_add (50, emit_address_changed_idle, new EmitAddressChangedData(mock, obj_client, newZone.c_str()));
+ }
+
+};
+
diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h
new file mode 100644
index 0000000..043b7e3
--- /dev/null
+++ b/tests/glib-fixture.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <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 <map>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include <gtest/gtest.h>
+
+class GlibFixture : public ::testing::Test
+{
+ private:
+
+ //GLogFunc realLogHandler;
+
+ protected:
+
+ std::map<GLogLevelFlags,int> logCounts;
+
+ void testLogCount (GLogLevelFlags log_level, int expected G_GNUC_UNUSED)
+ {
+#if 0
+ EXPECT_EQ (expected, logCounts[log_level]);
+#endif
+
+ logCounts.erase (log_level);
+ }
+
+ private:
+
+ static void default_log_handler (const gchar * log_domain,
+ GLogLevelFlags log_level,
+ const gchar * message,
+ gpointer self)
+ {
+ g_print ("%s - %d - %s\n", log_domain, (int)log_level, message);
+ static_cast<GlibFixture*>(self)->logCounts[log_level]++;
+ }
+
+ protected:
+
+ virtual void SetUp ()
+ {
+ loop = g_main_loop_new (NULL, FALSE);
+
+ //g_log_set_default_handler (default_log_handler, this);
+
+ // only use local, temporary settings
+ g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+ g_debug ("SCHEMA_DIR is %s", SCHEMA_DIR);
+ }
+
+ virtual void TearDown()
+ {
+#if 0
+ // confirm there aren't any unexpected log messages
+ EXPECT_EQ (0, logCounts[G_LOG_LEVEL_ERROR]);
+ EXPECT_EQ (0, logCounts[G_LOG_LEVEL_CRITICAL]);
+ EXPECT_EQ (0, logCounts[G_LOG_LEVEL_WARNING]);
+ EXPECT_EQ (0, logCounts[G_LOG_LEVEL_MESSAGE]);
+ EXPECT_EQ (0, logCounts[G_LOG_LEVEL_INFO]);
+#endif
+
+ // revert to glib's log handler
+ //g_log_set_default_handler (realLogHandler, this);
+
+ g_clear_pointer (&loop, g_main_loop_unref);
+ }
+
+ private:
+
+ static gboolean
+ wait_for_signal__timeout (gpointer name)
+ {
+ g_error ("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name);
+ return G_SOURCE_REMOVE;
+ }
+
+ static gboolean
+ wait_msec__timeout (gpointer loop)
+ {
+ g_main_loop_quit (static_cast<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)
+ {
+ // wait for the signal or for timeout, whichever comes first
+ const auto handler_id = g_signal_connect_swapped (o, signal,
+ G_CALLBACK(g_main_loop_quit),
+ loop);
+ const auto timeout_id = g_timeout_add_seconds (timeout_seconds,
+ wait_for_signal__timeout,
+ loop);
+ g_main_loop_run (loop);
+ g_source_remove (timeout_id);
+ g_signal_handler_disconnect (o, handler_id);
+ }
+
+ /* convenience func to loop for N msec */
+ void wait_msec (int msec=50)
+ {
+ const auto id = g_timeout_add (msec, wait_msec__timeout, loop);
+ g_main_loop_run (loop);
+ g_source_remove (id);
+ }
+
+ GMainLoop * loop;
+};
diff --git a/tests/test-core.cc b/tests/test-core.cc
new file mode 100644
index 0000000..7ed38a9
--- /dev/null
+++ b/tests/test-core.cc
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <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 <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include <langinfo.h>
+#include <locale.h>
+
+#include <glib/gi18n.h>
+
+#include <core/connection.h>
+#include <core/signal.h>
+#include <core/property.h>
+
+#include "glib-fixture.h"
+
+/***
+****
+***/
+
+class CoreFixture: public GlibFixture
+{
+ private:
+
+ typedef GlibFixture super;
+
+ protected:
+
+ virtual void SetUp ()
+ {
+ super::SetUp ();
+ }
+
+ virtual void TearDown ()
+ {
+ super::TearDown ();
+ }
+};
+
+namespace
+{
+struct EventLoop
+{
+ typedef std::function<void()> Handler;
+
+ void stop()
+ {
+ stop_requested = true;
+ }
+
+ void run()
+ {
+ while (!stop_requested)
+ {
+ std::unique_lock<std::mutex> ul(guard);
+ wait_condition.wait_for(
+ ul,
+ std::chrono::milliseconds{500},
+ [this]() { return handlers.size() > 0; });
+
+ std::cerr << "handlers.size() is " << handlers.size() << std::endl;
+ while (handlers.size() > 0)
+ {
+ std::cerr << "gaba begin" << std::endl;
+ handlers.front()();
+ std::cerr << "gaba end" << std::endl;
+ handlers.pop();
+ }
+ }
+ }
+
+ void dispatch(const Handler& h)
+ {
+std::cerr << "in dispatch" << std::endl;
+ std::lock_guard<std::mutex> lg(guard);
+ handlers.push(h);
+ }
+
+ bool stop_requested = false;
+ std::queue<Handler> handlers;
+ std::mutex guard;
+ std::condition_variable wait_condition;
+};
+}
+
+
+TEST_F (CoreFixture, HelloWorld)
+{
+ // We instantiate an event loop and run it on a different thread than the main one.
+ EventLoop dispatcher;
+ std::thread dispatcher_thread{[&dispatcher]() { dispatcher.run(); }};
+ std::thread::id dispatcher_thread_id = dispatcher_thread.get_id();
+
+ // The signal that we want to dispatch via the event loop.
+ core::Signal<int, double> s;
+
+ static const int expected_invocation_count = 10000;
+
+ // Setup the connection. For each invocation we check that the id of the
+ // thread the handler is being called upon equals the thread that the
+ // event loop is running upon.
+ auto connection = s.connect(
+ [&dispatcher, dispatcher_thread_id](int value, double d)
+ {
+ std::cerr << "this is the lambda" << std::endl;
+ EXPECT_EQ(dispatcher_thread_id,
+ std::this_thread::get_id());
+
+ std::cout << d << std::endl;
+
+ if (value == expected_invocation_count)
+ dispatcher.stop();
+ });
+
+ // Route the connection via the dispatcher
+ connection.dispatch_via(
+ std::bind(
+ &EventLoop::dispatch,
+ std::ref(dispatcher),
+ std::placeholders::_1));
+
+ // Invoke the signal from the main thread.
+ for (unsigned int i = 1; i <= expected_invocation_count; i++)
+ s(i, 42.);
+
+ if (dispatcher_thread.joinable())
+ dispatcher_thread.join();
+}
diff --git a/tests/test-skew.cc b/tests/test-skew.cc
new file mode 100644
index 0000000..90c0164
--- /dev/null
+++ b/tests/test-skew.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <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 "Clock.h"
+#include "MockClock.h"
+
+/***
+****
+***/
+
+using unity::indicator::datetime::Clock;
+using unity::indicator::datetime::MockClock;
+using unity::indicator::datetime::SkewDetector;
+
+class SkewFixture: public GlibFixture
+{
+ private:
+
+ typedef GlibFixture super;
+
+ static void
+ on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+ {
+ auto self = static_cast<SkewFixture*>(gself);
+
+ GError * err = 0;
+ self->system_bus = g_bus_get_finish (res, &err);
+ g_assert_no_error (err);
+
+ g_main_loop_quit (self->loop);
+ }
+
+ static void
+ on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+ {
+ auto self = static_cast<SkewFixture*>(gself);
+
+ GError * err = 0;
+ g_dbus_connection_close_finish (self->system_bus, res, &err);
+ g_assert_no_error (err);
+
+ g_main_loop_quit (self->loop);
+ }
+
+ protected:
+
+ std::shared_ptr<Clock> mockClock;
+ GTestDBus * test_dbus;
+ GDBusConnection * system_bus;
+
+ virtual void SetUp ()
+ {
+ super::SetUp ();
+
+ // pull up a test dbus
+ test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_test_dbus_up (test_dbus);
+ const char * address = g_test_dbus_get_bus_address (test_dbus);
+ g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE);
+ g_debug ("test_dbus's address is %s", address);
+
+ // wait for the GDBusConnection before returning
+ g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this);
+ g_main_loop_run (loop);
+
+ // create a clock
+ GDateTime * now = g_date_time_new_now_local ();
+ mockClock.reset (new MockClock (now));
+ g_date_time_unref (now);
+ }
+
+ virtual void TearDown ()
+ {
+ mockClock.reset();
+
+ // close the system bus
+ g_dbus_connection_close (system_bus, nullptr, on_bus_closed, this);
+ g_main_loop_run (loop);
+ g_clear_object (&system_bus);
+
+ // tear down the test dbus
+ g_test_dbus_down (test_dbus);
+ g_clear_object (&test_dbus);
+
+ super::TearDown ();
+ }
+
+ public:
+
+ void emitPrepareForSleep ()
+ {
+ g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, nullptr, nullptr),
+ NULL,
+ "/org/freedesktop/login1", // object path
+ "org.freedesktop.login1.Manager", // interface
+ "PrepareForSleep", // signal name
+ g_variant_new("(b)", FALSE),
+ NULL);
+ }
+};
+
+
+/**
+ * A simple "hello world" style test.
+ */
+TEST_F (SkewFixture, CanInstantiate)
+{
+ SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock));
+ wait_msec (500); // wait for the bus to set up
+}
+
+
+/**
+ * Confirm that changing the clock's timezone triggers a skew event
+ */
+TEST_F (SkewFixture, ChangingTimezonesTriggersEvent)
+{
+ SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock));
+ wait_msec (500); // wait for the bus to set up
+
+ bool skewed = false;
+ skew.skewDetected.connect([&skewed, this](){
+ skewed = true;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ });
+
+ g_idle_add([](gpointer gclock){
+ GDateTime * arbitrary = g_date_time_new_local (2020, 10, 31, 18, 30, 59);
+ static_cast<MockClock*>(gclock)->setLocaltime (arbitrary);
+ g_date_time_unref (arbitrary);
+ return G_SOURCE_REMOVE;
+ }, mockClock.get());
+
+ wait_msec (1000);
+
+ EXPECT_TRUE (skewed);
+ GDateTime * expected = g_date_time_new_local (2020, 10, 31, 18, 30, 59);
+ GDateTime * actual = mockClock->localtime();
+ EXPECT_EQ (0, g_date_time_compare (expected, actual));
+ g_date_time_unref (actual);
+ g_date_time_unref (expected);
+}
+
+/**
+ * Confirm that a "PrepareForSleep" event wil trigger a skew event
+ */
+TEST_F (SkewFixture, PrepareForSleep)
+{
+ SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock));
+ wait_msec (500); // wait for the bus to set up
+
+ bool skewed = false;
+ skew.skewDetected.connect([&skewed, this](){
+ skewed = true;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ });
+
+ g_idle_add ([](gpointer gself){
+ static_cast<SkewFixture*>(gself)->emitPrepareForSleep();
+ return G_SOURCE_REMOVE;
+ }, this);
+
+ wait_msec (1000);
+ EXPECT_TRUE(skewed);
+}
+
+
+/**
+ * Confirm that normal time passing doesn't trigger a skew event.
+ * that idling changing the clock's time triggers a skew event
+ */
+TEST_F (SkewFixture, IdleDoesNotTriggerEvent)
+{
+ SkewDetector skew (std::dynamic_pointer_cast<Clock>(mockClock));
+ wait_msec (500); // wait for the bus to set up
+
+ bool skewed = false;
+ skew.skewDetected.connect([&skewed](){
+ skewed = true;
+ g_warn_if_reached();
+ //abort();
+ return G_SOURCE_REMOVE;
+ });
+
+ const unsigned int intervalSec = 4;
+ skew.intervalSec.set(intervalSec);
+ wait_msec (intervalSec * 2.5 * 1000);
+ EXPECT_FALSE (skewed);
+}
diff --git a/tests/test-timezones.cc b/tests/test-timezones.cc
new file mode 100644
index 0000000..cda53a6
--- /dev/null
+++ b/tests/test-timezones.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "geoclue-fixture.h"
+
+#include <datetime/timezones-live.h>
+
+#include <cstdio> // fopen()
+#include <unistd.h> // sync()
+
+using unity::indicator::datetime::LiveTimezones;
+
+typedef GeoclueFixture TimezonesFixture;
+
+#define TIMEZONE_FILE (SANDBOX "/timezone")
+
+namespace
+{
+ /* convenience func to set the timezone file */
+ void set_file (const std::string& text)
+ {
+ FILE * fp = fopen (TIMEZONE_FILE, "w+");
+ fprintf (fp, "%s\n", text.c_str());
+ fclose (fp);
+ sync ();
+ }
+}
+
+
+TEST_F (TimezonesFixture, ManagerTest)
+{
+ std::string timezone_file = "America/New_York";
+ std::string timezone_geo = "America/Denver";
+
+ set_file (timezone_file);
+ LiveTimezones z (TIMEZONE_FILE);
+ wait_msec (500); // wait for the bus to get set up
+ EXPECT_EQ (timezone_file, z.timezone.get());
+ std::set<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 gz) {
+ auto az = static_cast<LiveTimezones*>(gz);
+ g_message ("geolocation was %d", (int)az->geolocationEnabled.get());
+ g_message ("turning geolocation on");
+ az->geolocationEnabled.set(true);
+ return G_SOURCE_REMOVE;
+ }, &z);
+
+ // turn on geoclue during the idle... this should add timezone_1 to the 'timezones' property
+ g_main_loop_run (loop);
+ EXPECT_TRUE (zones_changed);
+ EXPECT_EQ (timezone_file, z.timezone.get());
+ EXPECT_EQ (2, zones.size());
+ EXPECT_EQ (1, zones.count(timezone_file));
+ EXPECT_EQ (1, zones.count(timezone_geo));
+ zones_changed = false;
+
+ // now tweak the geoclue value... the geoclue-detected timezone should change,
+ // causing the 'timezones' property to change
+ zone_changed = false;
+ zones_changed = false;
+ timezone_geo = "America/Chicago";
+ setGeoclueTimezoneOnIdle (timezone_geo);
+ g_main_loop_run (loop);
+ EXPECT_FALSE (zone_changed);
+ EXPECT_TRUE (zones_changed);
+ EXPECT_EQ (timezone_file, z.timezone.get());
+ EXPECT_EQ (2, zones.size());
+ EXPECT_EQ (1, zones.count(timezone_file));
+ EXPECT_EQ (1, zones.count(timezone_geo));
+
+ // now set the file value... this should change both the primary property and set property
+ zone_changed = false;
+ zones_changed = false;
+ timezone_file = "America/Los_Angeles";
+ EXPECT_EQ (0, zones.count(timezone_file));
+ g_idle_add ([](gpointer str) {set_file(static_cast<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));
+
+
+
+}
+
+