aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2014-07-30 10:54:56 +0000
committerCI bot <ps-jenkins@lists.canonical.com>2014-07-30 10:54:56 +0000
commita2f643c295b666f9913a01a43592ce61fbb023ee (patch)
tree5399ee6853f1fba884c3f303be473d58cf6ae7a9
parent18c9f881e5eb01b157048d083515e59ebdd08afa (diff)
parenta7e8b219e8a9301249301cff6fbe989670d4e312 (diff)
downloadayatana-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.h14
-rw-r--r--include/datetime/snap.h7
-rw-r--r--src/exporter.cpp8
-rw-r--r--src/snap.cpp285
-rw-r--r--tests/manual2
-rw-r--r--tests/test-exporter.cpp6
-rw-r--r--tests/test-snap.cpp290
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(&notify_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);
+}