aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt17
-rw-r--r--tests/manual32
-rw-r--r--tests/manual-test-snap.cpp53
-rw-r--r--tests/test-exporter.cpp95
-rw-r--r--tests/test-locations.cpp1
-rw-r--r--tests/test-menus.cpp2
-rw-r--r--tests/test-settings.cpp30
-rw-r--r--tests/test-snap.cpp212
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);
+}
+