/* * Copyright 2014-2016 Canonical Ltd. * Copyright 2023 Robert Tari * * 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 . * * Authors: * Charles Kerr * Robert Tari */ #include #include #include #include "notification-fixture.h" using namespace ayatana::indicator::datetime; namespace uin = ayatana::indicator::notifications; /*** **** ***/ namespace { static constexpr char const * APP_NAME {"indicator-datetime-service"}; } namespace { gboolean quit_idle (gpointer gloop) { g_main_loop_quit(static_cast(gloop)); return G_SOURCE_REMOVE; }; class SoundNotificationFixture : public NotificationFixture { }; } /*** **** ***/ TEST_F(SoundNotificationFixture, InteractiveDuration) { static constexpr int duration_minutes = 120; auto settings = std::make_shared(); settings->alarm_duration.set(duration_minutes); auto ne = std::make_shared(APP_NAME); auto sb = std::make_shared(); auto snap = create_snap(ne, sb, settings); settings->cal_notification_enabled.set(true); settings->cal_notification_sounds.set(true); settings->cal_notification_vibrations.set(true); settings->cal_notification_bubbles.set(true); settings->cal_notification_list.set(true); #ifdef LOMIRI_FEATURES_ENABLED /* Here both values true|false should succeed. */ mock_capabilities(true); #else mock_capabilities(false); #endif // call the Snap Decision auto func = [this](const Appointment&, const Alarm&, const Snap::Response&){g_idle_add(quit_idle, loop);}; (*snap)(appt, appt.alarms.front(), func); // confirm that Notify got called once guint len = 0; 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); // 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 "calendar-app" g_variant_get_child (params, 2, "&s", &str); ASSERT_STREQ("calendar-app", str); // confirm that the timeout passed to Notify matches duration_minutes int32_t i32; g_variant_get_child (params, 7, "i", &i32); const auto duration = std::chrono::minutes(duration_minutes); EXPECT_EQ(std::chrono::duration_cast(duration).count(), i32); #ifdef LOMIRI_FEATURES_ENABLED /* If setting mock_capabilities to false, set the below to false, as well. */ if (true) { // Due to custom logic in Lomiri, also make sure custom timeout hint is set. bool b; auto hints = g_variant_get_child_value (params, 6); i32 = 0; b = g_variant_lookup (hints, HINT_LOMIRI_TIMEOUT, "i", &i32); EXPECT_TRUE(b); EXPECT_EQ(std::chrono::duration_cast(duration).count(), i32); g_variant_unref(hints); } #endif ne.reset(); } /*** **** ***/ /** * A DefaultSoundBuilder wrapper which remembers the parameters of the last sound created. */ class TestSoundBuilder: public uin::SoundBuilder { public: TestSoundBuilder() =default; ~TestSoundBuilder() =default; virtual std::shared_ptr create(const std::string& role, const std::string& uri, unsigned int volume, bool loop) override { m_role = role; m_uri = uri; m_volume = volume; m_loop = loop; return m_impl.create(role, uri, volume, loop); } const std::string& role() { return m_role; } const std::string& uri() { return m_uri; } unsigned int volume() { return m_volume; } bool loop() { return m_loop; } private: std::string m_role; std::string m_uri; unsigned int m_volume; bool m_loop; uin::DefaultSoundBuilder m_impl; }; std::string path_to_uri_if_exists(const std::string& path) { std::string uri; auto file = g_file_new_for_path(path.c_str()); if (g_file_query_exists(file, /* cancellable */ nullptr)) { auto uri_cstr = g_file_get_uri(file); uri = std::string(uri_cstr); g_free(uri_cstr); } g_clear_pointer(&file, g_object_unref); return uri; } TEST_F(NotificationFixture,DefaultSounds) { auto settings = std::make_shared(); auto ne = std::make_shared(APP_NAME); auto sb = std::make_shared(); auto func = [this](const Appointment&, const Alarm&, const Snap::Response&){g_idle_add(quit_idle, loop);}; settings->cal_notification_enabled.set(true); settings->cal_notification_sounds.set(true); settings->cal_notification_vibrations.set(true); settings->cal_notification_bubbles.set(true); settings->cal_notification_list.set(true); const struct { Appointment appointment; std::string expected_role; std::string expected_uri; } test_cases[] = { { ualarm, "alarm", path_to_uri_if_exists(ALARM_DEFAULT_SOUND) } // No sound for appointments // { appt, "alert", path_to_uri_if_exists(CALENDAR_DEFAULT_SOUND) } }; auto snap = create_snap(ne, sb, settings); for(const auto& test_case : test_cases) { (*snap)(test_case.appointment, test_case.appointment.alarms.front(), func); EXPECT_EQ(test_case.expected_uri, sb->uri()); EXPECT_EQ(test_case.expected_role, sb->role()); } }