diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/CMakeLists.txt | 76 | ||||
-rw-r--r-- | tests/geoclue-fixture.h | 142 | ||||
-rw-r--r-- | tests/glib-fixture.h | 131 | ||||
-rw-r--r-- | tests/test-core.cc | 148 | ||||
-rw-r--r-- | tests/test-skew.cc | 209 | ||||
-rw-r--r-- | tests/test-timezones.cc | 122 |
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)); + + + +} + + |