diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2014-07-30 10:54:56 +0000 |
---|---|---|
committer | CI bot <ps-jenkins@lists.canonical.com> | 2014-07-30 10:54:56 +0000 |
commit | a2f643c295b666f9913a01a43592ce61fbb023ee (patch) | |
tree | 5399ee6853f1fba884c3f303be473d58cf6ae7a9 | |
parent | 18c9f881e5eb01b157048d083515e59ebdd08afa (diff) | |
parent | a7e8b219e8a9301249301cff6fbe989670d4e312 (diff) | |
download | ayatana-indicator-datetime-a2f643c295b666f9913a01a43592ce61fbb023ee.tar.gz ayatana-indicator-datetime-a2f643c295b666f9913a01a43592ce61fbb023ee.tar.bz2 ayatana-indicator-datetime-a2f643c295b666f9913a01a43592ce61fbb023ee.zip |
Activate the phone's screen when an alarm is displayed. Fixes: 1340329
Approved by: Jussi Pakkanen, PS Jenkins bot
-rw-r--r-- | include/datetime/dbus-shared.h | 14 | ||||
-rw-r--r-- | include/datetime/snap.h | 7 | ||||
-rw-r--r-- | src/exporter.cpp | 8 | ||||
-rw-r--r-- | src/snap.cpp | 285 | ||||
-rw-r--r-- | tests/manual | 2 | ||||
-rw-r--r-- | tests/test-exporter.cpp | 6 | ||||
-rw-r--r-- | tests/test-snap.cpp | 290 |
7 files changed, 523 insertions, 89 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 <charles.kerr@canonical.com> */ +#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/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 <datetime/clock.h> #include <datetime/settings.h> -#include <memory> #include <functional> +#include <memory> +#include <set> namespace unity { namespace indicator { @@ -49,6 +50,10 @@ public: private: const std::shared_ptr<Clock> m_clock; const std::shared_ptr<const Settings> m_settings; + + class Popup; + friend class Popup; + std::set<Popup*> m_pending; }; } // namespace datetime 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 5480425..af39ec6 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -18,6 +18,7 @@ */ #include <datetime/appointment.h> +#include <datetime/dbus-shared.h> #include <datetime/formatter.h> #include <datetime/snap.h> @@ -30,6 +31,7 @@ #include <glib.h> #include <chrono> +#include <limits> #include <mutex> // std::call_once() #include <set> #include <string> @@ -45,6 +47,8 @@ namespace datetime { namespace { +static constexpr char const * APP_NAME = {"indicator-datetime-service"}; + /** * Plays a sound, possibly looping. */ @@ -203,24 +207,81 @@ private: bool m_looping = true; }; +/** +*** libnotify -- snap decisions +**/ + +std::string get_alarm_uri(const Appointment& appointment, + const std::shared_ptr<const Settings>& 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: 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); @@ -363,62 +424,186 @@ private: **** ***/ - typedef Popup Self; + static void on_system_bus_ready (GObject *, GAsyncResult *res, gpointer gself) + { + GError * error; + GDBusConnection * system_bus; - const Appointment m_appointment; - const bool m_interactive; - SoundBuilder m_sound_builder; - std::unique_ptr<Sound> m_sound; - core::Signal<Response> m_response; - Response m_response_value = RESPONSE_CLOSE; - NotifyNotification* m_nn = nullptr; + 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); - static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; - static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; - static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; - static constexpr char const * HINT_NONSHAPEDICON {"x-canonical-non-shaped-icon"}; -}; + g_error_free (error); + } + else if (system_bus != nullptr) + { + auto self = static_cast<Popup*>(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, + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, + "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, + BUS_SCREEN_NAME, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, + "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); + } + } -/** -*** libnotify -- snap decisions -**/ + static void on_force_awake_response (GObject * connection, + GAsyncResult * res, + gpointer gself) + { + GError * error; + GVariant * args; -std::string get_alarm_uri(const Appointment& appointment, - const std::shared_ptr<const Settings>& settings) -{ - const char* FALLBACK {"/usr/share/sounds/ubuntu/ringtones/Suru arpeggio.ogg"}; + 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); - const std::string candidates[] = { appointment.audio_url, - settings->alarm_sound.get(), - FALLBACK }; + g_error_free (error); + } + else + { + auto self = static_cast<Popup*>(gself); - std::string uri; + g_clear_pointer (&self->m_awake_cookie, g_free); + g_variant_get (args, "(s)", &self->m_awake_cookie); + g_debug ("m_awake_cookie is now '%s'", self->m_awake_cookie); + + g_variant_unref (args); + } + } - for(const auto& candidate : candidates) + static void on_force_screen_response (GObject * connection, + GAsyncResult * res, + gpointer gself) { - if (gst_uri_is_valid (candidate.c_str())) + GError * error; + GVariant * args; + + error = nullptr; + args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); + if (error != nullptr) { - uri = candidate; - break; + 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 if (g_file_test(candidate.c_str(), G_FILE_TEST_EXISTS)) + else { - gchar* tmp = gst_filename_to_uri(candidate.c_str(), nullptr); - if (tmp != nullptr) - { - uri = tmp; - g_free (tmp); - break; - } + auto self = static_cast<Popup*>(gself); + + self->m_screen_cookie = NO_SCREEN_COOKIE; + g_variant_get (args, "(i)", &self->m_screen_cookie); + g_debug ("m_screen_cookie is now '%d'", self->m_screen_cookie); + + g_variant_unref (args); } } - return uri; -} + void unforce_awake () + { + g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); -int32_t n_existing_snaps = 0; + if (m_awake_cookie != nullptr) + { + g_dbus_connection_call (m_system_bus, + BUS_POWERD_NAME, + BUS_POWERD_PATH, + BUS_POWERD_INTERFACE, + "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); + } + } -} // unnamed namespace + void unforce_screen () + { + g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); + + if (m_screen_cookie != NO_SCREEN_COOKIE) + { + g_dbus_connection_call (m_system_bus, + BUS_SCREEN_NAME, + BUS_SCREEN_PATH, + BUS_SCREEN_INTERFACE, + "removeDisplayOnRequest", + g_variant_new("(i)", m_screen_cookie), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + nullptr, + nullptr); + + m_screen_cookie = NO_SCREEN_COOKIE; + } + } + + /*** + **** + ***/ + + typedef Popup Self; + + const Appointment m_appointment; + const bool m_interactive; + SoundBuilder m_sound_builder; + std::unique_ptr<Sound> m_sound; + core::Signal<Response> 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 = NO_SCREEN_COOKIE; + + static constexpr int32_t NO_SCREEN_COOKIE { std::numeric_limits<int32_t>::min() }; + + static constexpr char const * HINT_SNAP {"x-canonical-snap-decisions"}; + static constexpr char const * HINT_TINT {"x-canonical-private-button-tint"}; + static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"}; + static constexpr char const * HINT_NONSHAPEDICON {"x-canonical-non-shaped-icon"}; +}; /*** **** @@ -429,12 +614,15 @@ Snap::Snap(const std::shared_ptr<Clock>& 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"); } Snap::~Snap() { + for (auto popup : m_pending) + delete popup; + if (!--n_existing_snaps) notify_uninit(); } @@ -456,15 +644,20 @@ 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, - // 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<Popup*>(gdata); return G_SOURCE_REMOVE; 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 <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> + <dd>Confirm that the screen comes on when the alarm is triggered.<dd> </dl> Test-case indicator-datetime/edited-alarm-wakeup @@ -38,6 +39,7 @@ Test-case indicator-datetime/edited-alarm-wakeup <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> + <dd>Confirm that the screen comes on when the alarm is triggered.<dd> </dl> Test-case indicator-datetime/tell-snap-decision-to-dismiss 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); 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 <datetime/appointment.h> +#include <datetime/dbus-shared.h> #include <datetime/settings.h> #include <datetime/snap.h> #include <datetime/timezones.h> @@ -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<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); + 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>(); + //settings->alarm_duration.set(duration_minutes); + auto timezones = std::make_shared<Timezones>(); + auto clock = std::make_shared<LiveClock>(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>(); + //settings->alarm_duration.set(duration_minutes); + auto timezones = std::make_shared<Timezones>(); + auto clock = std::make_shared<LiveClock>(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); +} |