From 2206a15709b43f27e553ded0c29e3dd369d1377d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 17:59:22 -0500 Subject: when an alarm is being shown, inhibit sleep and force the screen on. --- src/snap.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) diff --git a/src/snap.cpp b/src/snap.cpp index 45eb14e..a601cae 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -45,6 +45,8 @@ namespace datetime { namespace { +static constexpr char const * APP_NAME = {"indicator-datetime-service"}; + /** * Plays a sound, possibly looping. */ @@ -214,13 +216,29 @@ public: Popup(const Appointment& appointment, const SoundBuilder& sound_builder): m_appointment(appointment), m_interactive(get_interactive()), - m_sound_builder(sound_builder) + m_sound_builder(sound_builder), + m_cancellable(g_cancellable_new()) { + g_bus_get (G_BUS_TYPE_SYSTEM, m_cancellable, on_system_bus_ready, this); + show(); } ~Popup() { + if (m_cancellable != nullptr) + { + g_cancellable_cancel (m_cancellable); + g_clear_object (&m_cancellable); + } + + if (m_system_bus != nullptr) + { + unforce_awake (); + unforce_screen (); + g_clear_object (&m_system_bus); + } + if (m_nn != nullptr) { notify_notification_clear_actions(m_nn); @@ -361,6 +379,169 @@ private: **** ***/ + static void on_system_bus_ready (GObject *, GAsyncResult *res, gpointer gself) + { + GError * error; + GDBusConnection * system_bus; + + error = nullptr; + system_bus = g_bus_get_finish (res, &error); + if (error != nullptr) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Unable to get bus: %s", error->message); + + g_error_free (error); + } + else if (system_bus != nullptr) + { + auto self = static_cast(gself); + + self->m_system_bus = G_DBUS_CONNECTION (g_object_ref (system_bus)); + + // ask powerd to keep the system awake + static constexpr int32_t POWERD_SYS_STATE_ACTIVE = 1; + g_dbus_connection_call (system_bus, + "com.canonical.powerd", + "/com/canonical/powerd", + "com.canonical.powerd", + "requestSysState", + g_variant_new("(si)", APP_NAME, POWERD_SYS_STATE_ACTIVE), + G_VARIANT_TYPE("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->m_cancellable, + on_force_awake_response, + self); + + // ask unity-system-compositor to turn on the screen + g_dbus_connection_call (system_bus, + "com.canonical.Unity.Screen", + "/com/canonical/Unity/Screen", + "com.canonical.Unity.Screen", + "keepDisplayOn", + nullptr, + G_VARIANT_TYPE("(i)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->m_cancellable, + on_force_screen_response, + self); + + g_object_unref (system_bus); + } + } + + static void on_force_awake_response (GObject * connection, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GVariant * args; + + error = nullptr; + args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + if (error != nullptr) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Unable to inhibit sleep: %s", error->message); + + g_error_free (error); + } + else + { + auto self = static_cast(gself); + + g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); + + g_clear_pointer (&self->m_awake_cookie, g_free); + g_variant_get (args, "(s)", &self->m_awake_cookie); + g_message ("m_awake_cookie is now '%s'", self->m_awake_cookie); + + g_variant_unref (args); + } + } + + static void on_force_screen_response (GObject * connection, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GVariant * args; + + error = nullptr; + args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + if (error != nullptr) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Unable to turn on the screen: %s", error->message); + + g_error_free (error); + } + else + { + auto self = static_cast(gself); + + g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); + + g_clear_pointer (&self->m_screen_cookie, g_free); + g_variant_get (args, "(i)", &self->m_screen_cookie); + g_message ("m_screen_cookie is now '%d'", self->m_screen_cookie); + + g_variant_unref (args); + } + } + + void unforce_awake () + { + g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); + + if (m_awake_cookie != nullptr) + { + g_dbus_connection_call (m_system_bus, + "com.canonical.powerd", + "/com/canonical/powerd", + "com.canonical.powerd", + "clearSysState", + g_variant_new("(s)", m_awake_cookie), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + nullptr, + nullptr); + + g_clear_pointer (&m_awake_cookie, g_free); + } + } + + void unforce_screen () + { + g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); + + if (m_screen_cookie != 0) + { + g_dbus_connection_call (m_system_bus, + "com.canonical.Unity.Screen", + "/com/canonical/Unity/Screen", + "com.canonical.Unity.Screen", + "removeDisplayOnRequest", + g_variant_new("(i)", m_screen_cookie), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + nullptr, + nullptr); + + m_screen_cookie = 0; + } + } + + /*** + **** + ***/ + typedef Popup Self; const Appointment m_appointment; @@ -370,6 +551,10 @@ private: core::Signal m_response; Response m_response_value = RESPONSE_CLOSE; NotifyNotification* m_nn = nullptr; + GCancellable * m_cancellable = nullptr; + GDBusConnection * m_system_bus = nullptr; + char * m_awake_cookie = nullptr; + int32_t m_screen_cookie = 0; static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; @@ -426,7 +611,7 @@ Snap::Snap(const std::shared_ptr& clock, m_clock(clock), m_settings(settings) { - if (!n_existing_snaps++ && !notify_init("indicator-datetime-service")) + if (!n_existing_snaps++ && !notify_init(APP_NAME)) g_critical("libnotify initialization failed"); } -- cgit v1.2.3 From 5235e6046a6a1c924cd2d39edbef0fd8de2a21f8 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 18:58:42 -0500 Subject: handle Screen.keepDisplayOn() returning a cookie whose value is '0' --- src/snap.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/snap.cpp b/src/snap.cpp index a601cae..cf9cbd8 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -30,6 +30,7 @@ #include #include +#include #include // std::call_once() #include #include @@ -484,7 +485,7 @@ private: g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); - g_clear_pointer (&self->m_screen_cookie, g_free); + self->m_screen_cookie = NO_SCREEN_COOKIE; g_variant_get (args, "(i)", &self->m_screen_cookie); g_message ("m_screen_cookie is now '%d'", self->m_screen_cookie); @@ -498,6 +499,7 @@ private: if (m_awake_cookie != nullptr) { +g_message ("%s calling clearSysState %s", G_STRFUNC, m_awake_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.powerd", "/com/canonical/powerd", @@ -519,8 +521,9 @@ private: { g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); - if (m_screen_cookie != 0) + if (m_screen_cookie != NO_SCREEN_COOKIE) { +g_message ("%s calling removeDisplayOnRequest %d", G_STRFUNC, (int)m_screen_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.Unity.Screen", "/com/canonical/Unity/Screen", @@ -534,7 +537,7 @@ private: nullptr, nullptr); - m_screen_cookie = 0; + m_screen_cookie = NO_SCREEN_COOKIE; } } @@ -554,7 +557,9 @@ private: GCancellable * m_cancellable = nullptr; GDBusConnection * m_system_bus = nullptr; char * m_awake_cookie = nullptr; - int32_t m_screen_cookie = 0; + int32_t m_screen_cookie = NO_SCREEN_COOKIE; + + static constexpr int32_t NO_SCREEN_COOKIE { std::numeric_limits::min() }; static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; -- cgit v1.2.3 From e94fa94e3d8d3d7a9630d3e6fa94b78525ae4345 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 19:12:14 -0500 Subject: remove tracer g_messages() that were used during development --- src/snap.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/snap.cpp b/src/snap.cpp index cf9cbd8..5971c68 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -453,11 +453,9 @@ private: { auto self = static_cast(gself); - g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); - g_clear_pointer (&self->m_awake_cookie, g_free); g_variant_get (args, "(s)", &self->m_awake_cookie); - g_message ("m_awake_cookie is now '%s'", self->m_awake_cookie); + g_debug ("m_awake_cookie is now '%s'", self->m_awake_cookie); g_variant_unref (args); } @@ -483,11 +481,9 @@ private: { auto self = static_cast(gself); - g_message ("%s response is '%s'", G_STRFUNC, g_variant_print (args, true)); - self->m_screen_cookie = NO_SCREEN_COOKIE; g_variant_get (args, "(i)", &self->m_screen_cookie); - g_message ("m_screen_cookie is now '%d'", self->m_screen_cookie); + g_debug ("m_screen_cookie is now '%d'", self->m_screen_cookie); g_variant_unref (args); } @@ -499,7 +495,6 @@ private: if (m_awake_cookie != nullptr) { -g_message ("%s calling clearSysState %s", G_STRFUNC, m_awake_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.powerd", "/com/canonical/powerd", @@ -523,7 +518,6 @@ g_message ("%s calling clearSysState %s", G_STRFUNC, m_awake_cookie); if (m_screen_cookie != NO_SCREEN_COOKIE) { -g_message ("%s calling removeDisplayOnRequest %d", G_STRFUNC, (int)m_screen_cookie); g_dbus_connection_call (m_system_bus, "com.canonical.Unity.Screen", "/com/canonical/Unity/Screen", @@ -650,8 +644,8 @@ void Snap::operator()(const Appointment& appointment, dismiss, popup](Popup::Response response){ - // we can't delete the Popup inside its response() signal handler, - // so push that to an idle func + // we can't delete the Popup inside its response() signal handler + // because core::signal deadlocks, so push that to an idle func g_idle_add([](gpointer gdata){ delete static_cast(gdata); return G_SOURCE_REMOVE; -- cgit v1.2.3 From 909ccbc93d9fa21075cf5001887e69159e621f5d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Jul 2014 22:56:36 -0500 Subject: move the powerd and screen bus name, path, and interface strings into dbus-shared --- include/datetime/dbus-shared.h | 14 ++++++++++++-- src/exporter.cpp | 8 ++++---- src/snap.cpp | 25 +++++++++++++------------ tests/test-exporter.cpp | 6 +++--- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/include/datetime/dbus-shared.h b/include/datetime/dbus-shared.h index c5ff6ab..4b71ce5 100644 --- a/include/datetime/dbus-shared.h +++ b/include/datetime/dbus-shared.h @@ -18,8 +18,18 @@ * Charles Kerr */ +#ifndef _DBUS_SHARED_H_ +#define _DBUS_SHARED_H_ -#define BUS_NAME "com.canonical.indicator.datetime" +#define BUS_DATETIME_NAME "com.canonical.indicator.datetime" +#define BUS_DATETIME_PATH "/com/canonical/indicator/datetime" -#define BUS_PATH "/com/canonical/indicator/datetime" +#define BUS_SCREEN_NAME "com.canonical.Unity.Screen" +#define BUS_SCREEN_PATH "/com/canonical/Unity/Screen" +#define BUS_SCREEN_INTERFACE "com.canonical.Unity.Screen" +#define BUS_POWERD_NAME "com.canonical.powerd" +#define BUS_POWERD_PATH "/com/canonical/powerd" +#define BUS_POWERD_INTERFACE "com.canonical.powerd" + +#endif /* _DBUS_SHARED_H_ */ diff --git a/src/exporter.cpp b/src/exporter.cpp index e2b60f2..88aee2f 100644 --- a/src/exporter.cpp +++ b/src/exporter.cpp @@ -72,7 +72,7 @@ public: m_actions = actions; m_menus = menus; m_own_id = g_bus_own_name(G_BUS_TYPE_SESSION, - BUS_NAME, + BUS_DATETIME_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, on_bus_acquired, nullptr, @@ -166,12 +166,12 @@ private: GError * error = nullptr; g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(m_alarm_props), m_bus, - BUS_PATH"/AlarmProperties", + BUS_DATETIME_PATH"/AlarmProperties", &error); // export the actions const auto id = g_dbus_connection_export_action_group(m_bus, - BUS_PATH, + BUS_DATETIME_PATH, m_actions->action_group(), &error); if (id) @@ -187,7 +187,7 @@ private: // export the menus for(auto& menu : m_menus) { - const auto path = std::string(BUS_PATH) + "/" + menu->name(); + const auto path = std::string(BUS_DATETIME_PATH) + "/" + menu->name(); const auto id = g_dbus_connection_export_menu_model(m_bus, path.c_str(), menu->menu_model(), &error); if (id) { diff --git a/src/snap.cpp b/src/snap.cpp index 5971c68..566dfae 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -403,9 +404,9 @@ private: // ask powerd to keep the system awake static constexpr int32_t POWERD_SYS_STATE_ACTIVE = 1; g_dbus_connection_call (system_bus, - "com.canonical.powerd", - "/com/canonical/powerd", - "com.canonical.powerd", + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, "requestSysState", g_variant_new("(si)", APP_NAME, POWERD_SYS_STATE_ACTIVE), G_VARIANT_TYPE("(s)"), @@ -417,9 +418,9 @@ private: // ask unity-system-compositor to turn on the screen g_dbus_connection_call (system_bus, - "com.canonical.Unity.Screen", - "/com/canonical/Unity/Screen", - "com.canonical.Unity.Screen", + BUS_SCREEN_NAME, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, "keepDisplayOn", nullptr, G_VARIANT_TYPE("(i)"), @@ -496,9 +497,9 @@ private: if (m_awake_cookie != nullptr) { g_dbus_connection_call (m_system_bus, - "com.canonical.powerd", - "/com/canonical/powerd", - "com.canonical.powerd", + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, "clearSysState", g_variant_new("(s)", m_awake_cookie), nullptr, @@ -519,9 +520,9 @@ private: if (m_screen_cookie != NO_SCREEN_COOKIE) { g_dbus_connection_call (m_system_bus, - "com.canonical.Unity.Screen", - "/com/canonical/Unity/Screen", - "com.canonical.Unity.Screen", + BUS_SCREEN_NAME, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, "removeDisplayOnRequest", g_variant_new("(i)", m_screen_cookie), nullptr, diff --git a/tests/test-exporter.cpp b/tests/test-exporter.cpp index e947740..2e3411a 100644 --- a/tests/test-exporter.cpp +++ b/tests/test-exporter.cpp @@ -90,7 +90,7 @@ TEST_F(ExporterFixture, Publish) 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 exported = g_dbus_action_group_get (connection, BUS_DATETIME_NAME, BUS_DATETIME_PATH); auto names_strv = g_action_group_list_actions(G_ACTION_GROUP(exported)); // wait for the exported ActionGroup to be populated @@ -171,8 +171,8 @@ TEST_F(ExporterFixture, AlarmProperties) DatetimeAlarmProperties* proxy = nullptr; datetime_alarm_properties_proxy_new_for_bus(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, - BUS_NAME, - BUS_PATH"/AlarmProperties", + BUS_DATETIME_NAME, + BUS_DATETIME_PATH"/AlarmProperties", nullptr, on_proxy_ready, &proxy); -- cgit v1.2.3 From b9b3cfea551b5c7e12e6bb9904cffe2873b328c7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 01:18:10 -0500 Subject: when a Snap object is destructed, delete any active Popups that it owns. This cleaner shutdown doesn't have any effect in production, but is needed to shut down the bus cleanly in the tests. --- include/datetime/snap.h | 7 +++- src/snap.cpp | 94 +++++++++++++++++++++++++++---------------------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/include/datetime/snap.h b/include/datetime/snap.h index 9b45b3f..1c90496 100644 --- a/include/datetime/snap.h +++ b/include/datetime/snap.h @@ -24,8 +24,9 @@ #include #include -#include #include +#include +#include namespace unity { namespace indicator { @@ -49,6 +50,10 @@ public: private: const std::shared_ptr m_clock; const std::shared_ptr m_settings; + + class Popup; + friend class Popup; + std::set m_pending; }; } // namespace datetime diff --git a/src/snap.cpp b/src/snap.cpp index 566dfae..c21a398 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -207,11 +207,52 @@ private: bool m_looping = true; }; +/** +*** libnotify -- snap decisions +**/ + +std::string get_alarm_uri(const Appointment& appointment, + const std::shared_ptr& settings) +{ + const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; + + const std::string candidates[] = { appointment.audio_url, + settings->alarm_sound.get(), + FALLBACK }; + + std::string uri; + + for(const auto& candidate : candidates) + { + if (gst_uri_is_valid (candidate.c_str())) + { + uri = candidate; + break; + } + else if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) + { + gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); + if (tmp != nullptr) + { + uri = tmp; + g_free (tmp); + break; + } + } + } + + return uri; +} + +int32_t n_existing_snaps = 0; + +} // unnamed namespace + /** * A popup notification (with optional sound) * that emits a Response signal when done. */ -class Popup +class Snap::Popup { public: @@ -561,47 +602,6 @@ private: static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; }; -/** -*** libnotify -- snap decisions -**/ - -std::string get_alarm_uri(const Appointment& appointment, - const std::shared_ptr& settings) -{ - const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; - - const std::string candidates[] = { appointment.audio_url, - settings->alarm_sound.get(), - FALLBACK }; - - std::string uri; - - for(const auto& candidate : candidates) - { - if (gst_uri_is_valid (candidate.c_str())) - { - uri = candidate; - break; - } - else if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) - { - gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); - if (tmp != nullptr) - { - uri = tmp; - g_free (tmp); - break; - } - } - } - - return uri; -} - -int32_t n_existing_snaps = 0; - -} // unnamed namespace - /*** **** ***/ @@ -617,6 +617,9 @@ Snap::Snap(const std::shared_ptr& clock, Snap::~Snap() { + for (auto popup : m_pending) + delete popup; + if (!--n_existing_snaps) notify_uninit(); } @@ -638,12 +641,17 @@ void Snap::operator()(const Appointment& appointment, sound_builder.set_clock(m_clock); sound_builder.set_duration_minutes(m_settings->alarm_duration.get()); auto popup = new Popup(appointment, sound_builder); + + m_pending.insert(popup); // listen for it to finish... - popup->response().connect([appointment, + popup->response().connect([this, + appointment, show, dismiss, popup](Popup::Response response){ + + m_pending.erase(popup); // we can't delete the Popup inside its response() signal handler // because core::signal deadlocks, so push that to an idle func -- cgit v1.2.3 From d0e5dd99c4b886e9e5f14f2726a76cf29b1d9b41 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 01:26:51 -0500 Subject: add tests to confirm that sleep gets inhibited, and the screen gets forced on, while a notification is shown. --- tests/test-snap.cpp | 290 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 257 insertions(+), 33 deletions(-) diff --git a/tests/test-snap.cpp b/tests/test-snap.cpp index efe30f5..f9e7a66 100644 --- a/tests/test-snap.cpp +++ b/tests/test-snap.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -50,6 +51,15 @@ private: protected: + static constexpr int SCREEN_COOKIE {8675309}; + static constexpr char const * SCREEN_METHOD_KEEP_DISPLAY_ON {"keepDisplayOn"}; + static constexpr char const * SCREEN_METHOD_REMOVE_DISPLAY_ON_REQUEST {"removeDisplayOnRequest"}; + + static constexpr int POWERD_SYS_STATE_ACTIVE = 1; + static constexpr char const * POWERD_COOKIE {"567-48-8307"}; + static constexpr char const * POWERD_METHOD_REQUEST_SYS_STATE {"requestSysState"}; + static constexpr char const * POWERD_METHOD_CLEAR_SYS_STATE {"clearSysState"}; + static constexpr int NOTIFY_ID {1234}; static constexpr int NOTIFICATION_CLOSED_EXPIRED {1}; @@ -65,14 +75,22 @@ protected: 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; + GDBusConnection * system_bus = nullptr; + GDBusConnection * session_bus = nullptr; + DbusTestService * service = nullptr; + DbusTestDbusMock * notify_mock = nullptr; + DbusTestDbusMock * powerd_mock = nullptr; + DbusTestDbusMock * screen_mock = nullptr; + DbusTestDbusMockObject * notify_obj = nullptr; + DbusTestDbusMockObject * powerd_obj = nullptr; + DbusTestDbusMockObject * screen_obj = nullptr; void SetUp() { + GError * error = nullptr; + char * str = nullptr; + super::SetUp(); // init the Appointment @@ -88,39 +106,126 @@ protected: 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); + /// + /// Add the Notifications mock + /// + + notify_mock = dbus_test_dbus_mock_new(NOTIFY_BUSNAME); + notify_obj = dbus_test_dbus_mock_get_object(notify_mock, + NOTIFY_PATH, + NOTIFY_INTERFACE, + &error); + g_assert_no_error(error); - dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO, + str = g_strdup("ret = ('mock-notify', 'test vendor', '1.0', '1.1')"); + dbus_test_dbus_mock_object_add_method(notify_mock, + notify_obj, + METHOD_GET_INFO, nullptr, G_VARIANT_TYPE("(ssss)"), - "ret = ('mock-notify', 'test vendor', '1.0', '1.1')", // python + str, &error); g_assert_no_error (error); + g_free (str); - auto python_str = g_strdup_printf ("ret = %d", NOTIFY_ID); - dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_NOTIFY, + str = g_strdup_printf ("ret = %d", NOTIFY_ID); + dbus_test_dbus_mock_object_add_method(notify_mock, + notify_obj, + METHOD_NOTIFY, G_VARIANT_TYPE("(susssasa{sv}i)"), G_VARIANT_TYPE_UINT32, - python_str, + str, + &error); + g_assert_no_error (error); + g_free (str); + + dbus_test_service_add_task(service, DBUS_TEST_TASK(notify_mock)); + + /// + /// Add the powerd mock + /// + + powerd_mock = dbus_test_dbus_mock_new(BUS_POWERD_NAME); + powerd_obj = dbus_test_dbus_mock_get_object(powerd_mock, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, + &error); + g_assert_no_error(error); + + str = g_strdup_printf ("ret = '%s'", POWERD_COOKIE); + dbus_test_dbus_mock_object_add_method(powerd_mock, + powerd_obj, + POWERD_METHOD_REQUEST_SYS_STATE, + G_VARIANT_TYPE("(si)"), + G_VARIANT_TYPE("(s)"), + str, + &error); + g_assert_no_error (error); + g_free (str); + + dbus_test_dbus_mock_object_add_method(powerd_mock, + powerd_obj, + POWERD_METHOD_CLEAR_SYS_STATE, + G_VARIANT_TYPE("(s)"), + nullptr, + "", + &error); + g_assert_no_error (error); + + dbus_test_service_add_task(service, DBUS_TEST_TASK(powerd_mock)); + + /// + /// Add the Screen mock + /// + + screen_mock = dbus_test_dbus_mock_new(BUS_SCREEN_NAME); + screen_obj = dbus_test_dbus_mock_get_object(screen_mock, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, + &error); + g_assert_no_error(error); + + str = g_strdup_printf ("ret = %d", SCREEN_COOKIE); + dbus_test_dbus_mock_object_add_method(screen_mock, + screen_obj, + SCREEN_METHOD_KEEP_DISPLAY_ON, + nullptr, + G_VARIANT_TYPE("(i)"), + str, + &error); + g_assert_no_error (error); + g_free (str); + + dbus_test_dbus_mock_object_add_method(screen_mock, + screen_obj, + SCREEN_METHOD_REMOVE_DISPLAY_ON_REQUEST, + G_VARIANT_TYPE("(i)"), + nullptr, + "", &error); - g_free (python_str); g_assert_no_error (error); - dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); + dbus_test_service_add_task(service, DBUS_TEST_TASK(screen_mock)); + + + // start 'em up. + // make the system bus work off the mock bus too, since that's + // where the upower and screen are on the system bus... + dbus_test_service_start_tasks(service); + g_setenv("DBUS_SYSTEM_BUS_ADDRESS", g_getenv("DBUS_SESSION_BUS_ADDRESS"), TRUE); - 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); + session_bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); + ASSERT_NE(nullptr, session_bus); + g_dbus_connection_set_exit_on_close(session_bus, false); + g_object_add_weak_pointer(G_OBJECT(session_bus), (gpointer *)&session_bus); + + system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); + ASSERT_NE(nullptr, system_bus); + g_dbus_connection_set_exit_on_close(system_bus, FALSE); + g_object_add_weak_pointer(G_OBJECT(system_bus), (gpointer *)&system_bus); notify_init(APP_NAME); } @@ -129,14 +234,17 @@ protected: { notify_uninit(); - g_clear_object(&mock); + g_clear_object(&screen_mock); + g_clear_object(&powerd_mock); + g_clear_object(¬ify_mock); g_clear_object(&service); - g_object_unref(bus); + g_object_unref(session_bus); + g_object_unref(system_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)) + while (((system_bus != nullptr) || (session_bus != nullptr)) && (cleartry < 50)) { g_usleep(100000); while (g_main_pending()) @@ -146,6 +254,23 @@ protected: super::TearDown(); } + + void make_interactive() + { + // 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(notify_mock, + notify_obj, + METHOD_GET_CAPS, + nullptr, + G_VARIANT_TYPE_STRING_ARRAY, + "ret = ['actions', 'body']", + &error); + g_assert_no_error (error); + } }; /*** @@ -168,15 +293,10 @@ TEST_F(SnapFixture, InteractiveDuration) settings->alarm_duration.set(duration_minutes); auto timezones = std::make_shared(); auto clock = std::make_shared(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); + make_interactive(); // call the Snap Decision auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; @@ -184,7 +304,12 @@ TEST_F(SnapFixture, InteractiveDuration) // 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); + GError * error = nullptr; + const auto calls = dbus_test_dbus_mock_object_get_method_calls (notify_mock, + notify_obj, + METHOD_NOTIFY, + &len, + &error); g_assert_no_error(error); ASSERT_EQ(1, len); @@ -210,3 +335,102 @@ TEST_F(SnapFixture, InteractiveDuration) g_variant_unref(hints); } +/*** +**** +***/ + +TEST_F(SnapFixture, InhibitSleep) +{ + //static constexpr int duration_minutes = 120; + auto settings = std::make_shared(); + //settings->alarm_duration.set(duration_minutes); + auto timezones = std::make_shared(); + auto clock = std::make_shared(timezones); + auto snap = new Snap (clock, settings); + + make_interactive(); + + // invoke the notification + auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; + (*snap)(appt, func, func); + + wait_msec(1000); + + // confirm that sleep got inhibited + GError * error = nullptr; + EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (powerd_mock, + powerd_obj, + POWERD_METHOD_REQUEST_SYS_STATE, + g_variant_new("(si)", APP_NAME, POWERD_SYS_STATE_ACTIVE), + &error)); + + // confirm that the screen got forced on + EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (screen_mock, + screen_obj, + SCREEN_METHOD_KEEP_DISPLAY_ON, + nullptr, + &error)); + + // force-close the snap + delete snap; + wait_msec(100); + + // confirm that sleep got uninhibted + EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (powerd_mock, + powerd_obj, + POWERD_METHOD_CLEAR_SYS_STATE, + g_variant_new("(s)", POWERD_COOKIE), + &error)); + + // confirm that the screen's no longer forced on + EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (screen_mock, + screen_obj, + SCREEN_METHOD_REMOVE_DISPLAY_ON_REQUEST, + g_variant_new("(i)", SCREEN_COOKIE), + &error)); + + g_assert_no_error (error); +} + +/*** +**** +***/ + +TEST_F(SnapFixture, ForceScreen) +{ + //static constexpr int duration_minutes = 120; + auto settings = std::make_shared(); + //settings->alarm_duration.set(duration_minutes); + auto timezones = std::make_shared(); + auto clock = std::make_shared(timezones); + auto snap = new Snap (clock, settings); + + make_interactive(); + + // invoke the notification + auto func = [this](const Appointment&){g_idle_add(quit_idle, loop);}; + (*snap)(appt, func, func); + + wait_msec(1000); + + // confirm that sleep got inhibited + GError * error = nullptr; + EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (powerd_mock, + powerd_obj, + POWERD_METHOD_REQUEST_SYS_STATE, + g_variant_new("(si)", APP_NAME, POWERD_SYS_STATE_ACTIVE), + &error)); + g_assert_no_error(error); + + // force-close the snap + delete snap; + wait_msec(100); + + // confirm that sleep got uninhibted + EXPECT_TRUE (dbus_test_dbus_mock_object_check_method_call (powerd_mock, + powerd_obj, + POWERD_METHOD_CLEAR_SYS_STATE, + g_variant_new("(s)", POWERD_COOKIE), + &error)); + g_assert_no_error(error); +} -- cgit v1.2.3 From a7e8b219e8a9301249301cff6fbe989670d4e312 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Jul 2014 01:36:50 -0500 Subject: add 'confirm that the screen comes on' to relevant manual tests --- tests/manual | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/manual b/tests/manual index c5c52bc..be64863 100644 --- a/tests/manual +++ b/tests/manual @@ -29,6 +29,7 @@ Test-case indicator-datetime/new-alarm-wakeup
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)
+
Confirm that the screen comes on when the alarm is triggered.
Test-case indicator-datetime/edited-alarm-wakeup @@ -38,6 +39,7 @@ Test-case indicator-datetime/edited-alarm-wakeup
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)
+
Confirm that the screen comes on when the alarm is triggered.
Test-case indicator-datetime/tell-snap-decision-to-dismiss -- cgit v1.2.3