diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/CMakeLists.txt | 17 | ||||
-rw-r--r-- | tests/manual | 32 | ||||
-rw-r--r-- | tests/manual-test-snap.cpp | 53 | ||||
-rw-r--r-- | tests/test-exporter.cpp | 95 | ||||
-rw-r--r-- | tests/test-locations.cpp | 1 | ||||
-rw-r--r-- | tests/test-menus.cpp | 2 | ||||
-rw-r--r-- | tests/test-settings.cpp | 30 | ||||
-rw-r--r-- | tests/test-snap.cpp | 212 |
8 files changed, 429 insertions, 13 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fe6d7eb..20e744a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,12 @@ add_library (gtest STATIC 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}") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CC_WARNING_ARGS}") + +# dbustest +pkg_check_modules(DBUSTEST REQUIRED + dbustest-1>=14.04.0) +include_directories (SYSTEM ${DBUSTEST_INCLUDE_DIRS}) # build the necessary schemas set_directory_properties (PROPERTIES @@ -21,15 +26,17 @@ 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.datetime.gschema.xml - COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} + DEPENDS ${CMAKE_BINARY_DIR}/data/com.canonical.indicator.datetime.gschema.xml + COMMAND cp -f ${CMAKE_BINARY_DIR}/data/*gschema.xml ${SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) # 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_BINARY_DIR}/src) include_directories (${CMAKE_CURRENT_BINARY_DIR}) include_directories (${DBUSTEST_INCLUDE_DIRS}) + add_definitions (-DSANDBOX="${CMAKE_CURRENT_BINARY_DIR}") @@ -38,10 +45,12 @@ function(add_test_by_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}) + target_link_libraries (${TEST_NAME} indicatordatetimeservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS}) endfunction() +add_test_by_name(test-snap) add_test_by_name(test-actions) add_test_by_name(test-alarm-queue) +add_test(NAME dear-reader-the-next-test-takes-60-seconds COMMAND true) add_test_by_name(test-clock) add_test_by_name(test-exporter) add_test_by_name(test-formatter) diff --git a/tests/manual b/tests/manual index 17b4778..c5c52bc 100644 --- a/tests/manual +++ b/tests/manual @@ -22,3 +22,35 @@ Test-case indicator-datetime/unity8-items-check <dd>The menu is populated with items</dd> </dl> +Test-case indicator-datetime/new-alarm-wakeup +<dl> + <dt>Create and save an upcoming alarm in ubuntu-clock-app</dt> + <dt>Unplug the phone from any USB connection and put it to sleep</dt> + <dd>Confirm that the alarm sounds on time even if the phone is asleep. + (Note: if in doubt about sleep you can see in the syslog whether the + device actually suspended or whether the suspend was aborted)</dd> +</dl> + +Test-case indicator-datetime/edited-alarm-wakeup +<dl> + <dt>Edit an alarm that's already passed. (see previous test)</dt> + <dt>Unplug the phone from any USB connection and put it to sleep</dt> + <dd>Confirm that the alarm sounds on time even if the phone is asleep. + (Note: if in doubt about sleep you can see in the syslog whether the + device actually suspended or whether the suspend was aborted)</dd> +</dl> + +Test-case indicator-datetime/tell-snap-decision-to-dismiss +<dl> + <dt>Set an alarm and wait for it to arrive.</td> + <dd>Alarm should go off at the specified time</dd> + <dt>Press the 'Dismiss' button in the alarm's snap decision popup before the sound stops.</dt> + <dd>Popup should disappear</dd> + <dd>Sound should stop at the same time, rather than playing til the end of the file.</dd> +</dl> + + +<strong> + If all actions produce the expected results listed, please <a href="results#add_result">submit</a> a 'passed' result. + If an action fails, or produces an unexpected result, please <a href="results#add_result">submit</a> a 'failed' result and <a href="../../buginstructions">file a bug</a>. Please be sure to include the bug number when you <a href="results#add_result">submit</a> your result</strong>. + diff --git a/tests/manual-test-snap.cpp b/tests/manual-test-snap.cpp index 16e606a..cc24a67 100644 --- a/tests/manual-test-snap.cpp +++ b/tests/manual-test-snap.cpp @@ -19,18 +19,51 @@ */ #include <datetime/appointment.h> +#include <datetime/settings-live.h> #include <datetime/snap.h> +#include <datetime/timezones-live.h> #include <glib.h> using namespace unity::indicator::datetime; +#define TIMEZONE_FILE ("/etc/timezone") + + /*** **** ***/ -int main() +namespace +{ + gboolean quit_idle (gpointer gloop) + { + g_main_loop_quit(static_cast<GMainLoop*>(gloop)); + return G_SOURCE_REMOVE; + }; + + int volume = 50; + + GOptionEntry entries[] = + { + { "volume", 'v', 0, G_OPTION_ARG_INT, &volume, "Volume level [1..100]", "volume" }, + { NULL } + }; +} + +int main(int argc, const char* argv[]) { + GError* error = nullptr; + GOptionContext* context = g_option_context_new(nullptr); + g_option_context_add_main_entries(context, entries, nullptr); + if (!g_option_context_parse(context, &argc, (gchar***)&argv, &error)) + { + g_print("option parsing failed: %s\n", error->message); + exit(1); + } + g_option_context_free(context); + volume = CLAMP(volume, 1, 100); + Appointment a; a.color = "green"; a.summary = "Alarm"; @@ -47,15 +80,25 @@ int main() 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); + g_idle_add(quit_idle, loop); }; auto dismiss = [loop](const Appointment&){ g_message("You clicked 'dismiss'"); - g_main_loop_quit(loop); + g_idle_add(quit_idle, loop); }; - - Snap snap; + + // only use local, temporary settings + 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 = std::make_shared<LiveSettings>(); + settings->alarm_volume.set(volume); + auto timezones = std::make_shared<LiveTimezones>(settings, TIMEZONE_FILE); + auto clock = std::make_shared<LiveClock>(timezones); + Snap snap (clock, settings); snap(a, show, dismiss); g_main_loop_run(loop); + g_main_loop_unref(loop); return 0; } diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index a255ef9..e947740 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -21,6 +21,8 @@ #include "state-mock.h" #include "glib-fixture.h" +#include "dbus-alarm-properties.h" + #include <datetime/actions.h> #include <datetime/dbus-shared.h> #include <datetime/exporter.h> @@ -76,13 +78,14 @@ TEST_F(ExporterFixture, Publish) { std::shared_ptr<State> state(new MockState); std::shared_ptr<Actions> actions(new MockActions(state)); + std::shared_ptr<Settings> settings(new Settings); 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 exporter(settings); exporter.publish(actions, menus); wait_msec(); @@ -122,7 +125,7 @@ TEST_F(ExporterFixture, Publish) // try closing the connection prematurely // to test Exporter's name-lost signal bool name_lost = false; - exporter.name_lost.connect([this,&name_lost](){ + exporter.name_lost().connect([this,&name_lost](){ name_lost = true; g_main_loop_quit(loop); }); @@ -135,3 +138,91 @@ TEST_F(ExporterFixture, Publish) g_clear_object(&exported); g_clear_object(&connection); } + +TEST_F(ExporterFixture, AlarmProperties) +{ + /*** + **** Set up the exporter + ***/ + + std::shared_ptr<State> state(new MockState); + std::shared_ptr<Actions> actions(new MockActions(state)); + std::shared_ptr<Settings> settings(new Settings); + 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(settings); + exporter.publish(actions, menus); + wait_msec(); + + /*** + **** Set up the proxy + ***/ + + auto on_proxy_ready = [](GObject*, GAsyncResult* res, gpointer gproxy){ + GError* error = nullptr; + *reinterpret_cast<DatetimeAlarmProperties**>(gproxy) = datetime_alarm_properties_proxy_new_for_bus_finish(res, &error); + EXPECT_TRUE(error == nullptr); + }; + + DatetimeAlarmProperties* proxy = nullptr; + datetime_alarm_properties_proxy_new_for_bus(G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + BUS_NAME, + BUS_PATH"/AlarmProperties", + nullptr, + on_proxy_ready, + &proxy); + wait_msec(100); + ASSERT_TRUE(proxy != nullptr); + + /*** + **** Try changing the Settings -- do the DBus properties change to match it? + ***/ + + auto expected_volume = 1; + int expected_duration = 60; + const char * expected_sound = "/tmp/foo.wav"; + settings->alarm_volume.set(expected_volume); + settings->alarm_duration.set(expected_duration); + settings->alarm_sound.set(expected_sound); + wait_msec(); + + static constexpr const char* const SOUND_PROP {"default-sound"}; + static constexpr const char* const VOLUME_PROP {"default-volume"}; + static constexpr const char* const DURATION_PROP {"duration"}; + + char* sound = nullptr; + int volume = -1; + int duration = -1; + g_object_get(proxy, SOUND_PROP, &sound, + VOLUME_PROP, &volume, + DURATION_PROP, &duration, + nullptr); + EXPECT_STREQ(expected_sound, sound); + EXPECT_EQ(expected_volume, volume); + EXPECT_EQ(expected_duration, duration); + + /*** + **** Try chaning the DBus properties -- do the Settings change to match it? + ***/ + + expected_volume = 100; + expected_duration = 30; + expected_sound = "/tmp/bar.wav"; + g_object_set(proxy, SOUND_PROP, expected_sound, + VOLUME_PROP, expected_volume, + DURATION_PROP, expected_duration, + nullptr); + wait_msec(); + + EXPECT_STREQ(expected_sound, settings->alarm_sound.get().c_str()); + EXPECT_EQ(expected_volume, settings->alarm_volume.get()); + EXPECT_EQ(expected_duration, settings->alarm_duration.get()); + + // cleanup + g_clear_object(&proxy); +} diff --git a/tests/test-locations.cpp b/tests/test-locations.cpp index 65adbc7..48e845a 100644 --- a/tests/test-locations.cpp +++ b/tests/test-locations.cpp @@ -139,7 +139,6 @@ TEST_F(LocationsFixture, ChangeLocationStrings) 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) diff --git a/tests/test-menus.cpp b/tests/test-menus.cpp index b485037..f5f8df2 100644 --- a/tests/test-menus.cpp +++ b/tests/test-menus.cpp @@ -484,7 +484,7 @@ 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].get() != nullptr); EXPECT_TRUE(m_menus[i]->menu_model() != nullptr); EXPECT_EQ(i, m_menus[i]->profile()); } diff --git a/tests/test-settings.cpp b/tests/test-settings.cpp index 707247d..44a0252 100644 --- a/tests/test-settings.cpp +++ b/tests/test-settings.cpp @@ -100,6 +100,29 @@ protected: EXPECT_EQ(str, tmp); g_clear_pointer(&tmp, g_free); } + + void TestUIntProperty(core::Property<unsigned int>& property, const gchar* key) + { + EXPECT_EQ(g_settings_get_uint(m_gsettings, key), property.get()); + + unsigned int expected_values[] = { 1, 2, 3 }; + + // modify GSettings and confirm that the new value is propagated + for(const auto& expected_value : expected_values) + { + g_settings_set_uint(m_gsettings, key, expected_value); + EXPECT_EQ(expected_value, property.get()); + EXPECT_EQ(expected_value, g_settings_get_uint(m_gsettings, key)); + } + + // modify the property and confirm that the new value is propagated + for(const auto& expected_value : expected_values) + { + property.set(expected_value); + EXPECT_EQ(expected_value, property.get()); + EXPECT_EQ(expected_value, g_settings_get_uint(m_gsettings, key)); + } + } }; /*** @@ -125,10 +148,17 @@ TEST_F(SettingsFixture, BoolProperties) TestBoolProperty(m_settings->show_year, SETTINGS_SHOW_YEAR_S); } +TEST_F(SettingsFixture, UIntProperties) +{ + TestUIntProperty(m_settings->alarm_duration, SETTINGS_ALARM_DURATION_S); + TestUIntProperty(m_settings->alarm_volume, SETTINGS_ALARM_VOLUME_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); + TestStringProperty(m_settings->alarm_sound, SETTINGS_ALARM_SOUND_S); } TEST_F(SettingsFixture, TimeFormatMode) diff --git a/tests/test-snap.cpp b/tests/test-snap.cpp new file mode 100644 index 0000000..efe30f5 --- /dev/null +++ b/tests/test-snap.cpp @@ -0,0 +1,212 @@ +/* + * Copyright 2014 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/settings.h> +#include <datetime/snap.h> +#include <datetime/timezones.h> + +#include <libdbustest/dbus-test.h> + +#include <libnotify/notify.h> + +#include <glib.h> + +using namespace unity::indicator::datetime; + +#include "glib-fixture.h" + +/*** +**** +***/ + +using namespace unity::indicator::datetime; + +class SnapFixture: public GlibFixture +{ +private: + + typedef GlibFixture super; + + static constexpr char const * NOTIFY_BUSNAME {"org.freedesktop.Notifications"}; + static constexpr char const * NOTIFY_INTERFACE {"org.freedesktop.Notifications"}; + static constexpr char const * NOTIFY_PATH {"/org/freedesktop/Notifications"}; + +protected: + + static constexpr int NOTIFY_ID {1234}; + + static constexpr int NOTIFICATION_CLOSED_EXPIRED {1}; + static constexpr int NOTIFICATION_CLOSED_DISMISSED {2}; + static constexpr int NOTIFICATION_CLOSED_API {3}; + static constexpr int NOTIFICATION_CLOSED_UNDEFINED {4}; + + static constexpr char const * APP_NAME {"indicator-datetime-service"}; + + static constexpr char const * METHOD_NOTIFY {"Notify"}; + static constexpr char const * METHOD_GET_CAPS {"GetCapabilities"}; + static constexpr char const * METHOD_GET_INFO {"GetServerInformation"}; + + static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; + + DbusTestService * service = nullptr; + DbusTestDbusMock * mock = nullptr; + DbusTestDbusMockObject * obj = nullptr; + GDBusConnection * bus = nullptr; + Appointment appt; + + void SetUp() + { + super::SetUp(); + + // init the Appointment + appt.color = "green"; + appt.summary = "Alarm"; + appt.url = "alarm:///hello-world"; + appt.uid = "D4B57D50247291478ED31DED17FF0A9838DED402"; + appt.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); + appt.begin = begin; + appt.end = end; + g_date_time_unref(end); + g_date_time_unref(begin); + + // + // init DBusMock / dbus-test-runner + // + + service = dbus_test_service_new(nullptr); + + GError * error = nullptr; + mock = dbus_test_dbus_mock_new(NOTIFY_BUSNAME); + obj = dbus_test_dbus_mock_get_object(mock, NOTIFY_PATH, NOTIFY_INTERFACE, &error); + g_assert_no_error (error); + + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO, + nullptr, + G_VARIANT_TYPE("(ssss)"), + "ret = ('mock-notify', 'test vendor', '1.0', '1.1')", // python + &error); + g_assert_no_error (error); + + auto python_str = g_strdup_printf ("ret = %d", NOTIFY_ID); + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_NOTIFY, + G_VARIANT_TYPE("(susssasa{sv}i)"), + G_VARIANT_TYPE_UINT32, + python_str, + &error); + g_free (python_str); + g_assert_no_error (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); + + notify_init(APP_NAME); + } + + virtual void TearDown() + { + notify_uninit(); + + g_clear_object(&mock); + g_clear_object(&service); + g_object_unref(bus); + + // wait a little while for the scaffolding to shut down, + // but don't block on it forever... + unsigned int cleartry = 0; + while ((bus != nullptr) && (cleartry < 50)) + { + g_usleep(100000); + while (g_main_pending()) + g_main_iteration(true); + cleartry++; + } + + super::TearDown(); + } +}; + +/*** +**** +***/ + +namespace +{ + gboolean quit_idle (gpointer gloop) + { + g_main_loop_quit(static_cast<GMainLoop*>(gloop)); + return G_SOURCE_REMOVE; + }; +} + +TEST_F(SnapFixture, InteractiveDuration) +{ + static constexpr int duration_minutes = 120; + auto settings = std::make_shared<Settings>(); + settings->alarm_duration.set(duration_minutes); + auto timezones = std::make_shared<Timezones>(); + auto clock = std::make_shared<LiveClock>(timezones); + Snap snap (clock, settings); + + // GetCapabilities returns an array containing 'actions', + // so our snap decision will be interactive. + // For this test, it means we should get a timeout Notify Hint + // that matches duration_minutes + GError * error = nullptr; + dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_CAPS, nullptr, G_VARIANT_TYPE_STRING_ARRAY, "ret = ['actions', 'body']", &error); + g_assert_no_error (error); + + // call the Snap Decision + auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; + snap(appt, func, func); + + // confirm that Notify got called once + guint len = 0; + auto calls = dbus_test_dbus_mock_object_get_method_calls (mock, obj, METHOD_NOTIFY, &len, &error); + g_assert_no_error(error); + ASSERT_EQ(1, len); + + // confirm that the app_name passed to Notify was APP_NAME + const auto& params = calls[0].params; + ASSERT_EQ(8, g_variant_n_children(params)); + const char * str = nullptr; + g_variant_get_child (params, 0, "&s", &str); + ASSERT_STREQ(APP_NAME, str); + + // confirm that the icon passed to Notify was "alarm-clock" + g_variant_get_child (params, 2, "&s", &str); + ASSERT_STREQ("alarm-clock", str); + + // confirm that the hints passed to Notify included a timeout matching duration_minutes + int32_t i32; + bool b; + auto hints = g_variant_get_child_value (params, 6); + b = g_variant_lookup (hints, HINT_TIMEOUT, "i", &i32); + EXPECT_TRUE(b); + const auto duration = std::chrono::minutes(duration_minutes); + EXPECT_EQ(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(), i32); + g_variant_unref(hints); +} + |