aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/control2
-rw-r--r--src/snap.cpp37
-rw-r--r--tests/CMakeLists.txt11
-rw-r--r--tests/test-snap.cpp212
4 files changed, 244 insertions, 18 deletions
diff --git a/debian/control b/debian/control
index bd4988f..77e9241 100644
--- a/debian/control
+++ b/debian/control
@@ -9,7 +9,7 @@ Build-Depends: cmake,
python3-dbusmock,
debhelper (>= 9),
dh-translations,
- language-pack-en-base,
+ language-pack-touch-en | language-pack-en-base,
libgtest-dev,
libglib2.0-dev (>= 2.35.4),
libnotify-dev (>= 0.7.6),
diff --git a/src/snap.cpp b/src/snap.cpp
index 4495287..45eb14e 100644
--- a/src/snap.cpp
+++ b/src/snap.cpp
@@ -29,6 +29,7 @@
#include <glib/gi18n.h>
#include <glib.h>
+#include <chrono>
#include <mutex> // std::call_once()
#include <set>
#include <string>
@@ -182,7 +183,8 @@ public:
void set_clock(const std::shared_ptr<Clock>& c) {m_clock = c;}
void set_uri(const std::string& uri) {m_uri = uri;}
void set_volume(const unsigned int v) {m_volume = v;}
- void set_duration_minutes(int unsigned i) {m_duration_minutes=i;}
+ void set_duration_minutes(unsigned int i) {m_duration_minutes=i;}
+ unsigned int duration_minutes() const {return m_duration_minutes;}
void set_looping(bool b) {m_looping=b;}
Sound* operator()() {
@@ -214,14 +216,6 @@ public:
m_interactive(get_interactive()),
m_sound_builder(sound_builder)
{
- // ensure notify_init() is called once
- // before we start popping up dialogs
- static std::once_flag once;
- std::call_once(once, [](){
- if(!notify_init("indicator-datetime-service"))
- g_critical("libnotify initialization failed");
- });
-
show();
}
@@ -260,12 +254,15 @@ private:
m_nn = notify_notification_new(title, body.c_str(), icon_name);
if (m_interactive)
{
- notify_notification_set_hint_string(m_nn,
- "x-canonical-snap-decisions",
- "true");
- notify_notification_set_hint_string(m_nn,
- "x-canonical-private-button-tint",
- "true");
+ const auto duration = std::chrono::minutes(m_sound_builder.duration_minutes());
+
+ notify_notification_set_hint(m_nn, HINT_SNAP,
+ g_variant_new_boolean(true));
+ notify_notification_set_hint(m_nn, HINT_TINT,
+ g_variant_new_boolean(true));
+ notify_notification_set_hint(m_nn, HINT_TIMEOUT,
+ g_variant_new_int32(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()));
+
/// alarm popup dialog's button to show the active alarm
notify_notification_add_action(m_nn, "show", _("Show"),
on_snap_show, this, nullptr);
@@ -373,6 +370,10 @@ private:
core::Signal<Response> m_response;
Response m_response_value = RESPONSE_CLOSE;
NotifyNotification* m_nn = nullptr;
+
+ 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"};
};
/**
@@ -412,6 +413,8 @@ std::string get_alarm_uri(const Appointment& appointment,
return uri;
}
+int32_t n_existing_snaps = 0;
+
} // unnamed namespace
/***
@@ -423,10 +426,14 @@ Snap::Snap(const std::shared_ptr<Clock>& clock,
m_clock(clock),
m_settings(settings)
{
+ if (!n_existing_snaps++ && !notify_init("indicator-datetime-service"))
+ g_critical("libnotify initialization failed");
}
Snap::~Snap()
{
+ if (!--n_existing_snaps)
+ notify_uninit();
}
void Snap::operator()(const Appointment& appointment,
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 958e1cc..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
@@ -40,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/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);
+}
+