aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/glib-fixture.h138
-rw-r--r--tests/test-notify.cc192
2 files changed, 316 insertions, 14 deletions
diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h
new file mode 100644
index 0000000..46b9640
--- /dev/null
+++ b/tests/glib-fixture.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2013 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 <map>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include <gtest/gtest.h>
+
+#include <locale.h> // setlocale()
+
+class GlibFixture : public ::testing::Test
+{
+ private:
+
+ //GLogFunc realLogHandler;
+
+ protected:
+
+ std::map<GLogLevelFlags,int> logCounts;
+
+ void testLogCount(GLogLevelFlags log_level, int /*expected*/)
+ {
+#if 0
+ EXPECT_EQ(expected, logCounts[log_level]);
+#endif
+
+ logCounts.erase(log_level);
+ }
+
+ private:
+
+ static void default_log_handler(const gchar * log_domain,
+ GLogLevelFlags log_level,
+ const gchar * message,
+ gpointer self)
+ {
+ g_print("%s - %d - %s\n", log_domain, (int)log_level, message);
+ static_cast<GlibFixture*>(self)->logCounts[log_level]++;
+ }
+
+ protected:
+
+ virtual void SetUp()
+ {
+ setlocale(LC_ALL, "C.UTF-8");
+
+ loop = g_main_loop_new(nullptr, false);
+
+ //g_log_set_default_handler(default_log_handler, this);
+
+ // 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);
+
+ g_unsetenv("DISPLAY");
+
+ }
+
+ virtual void TearDown()
+ {
+#if 0
+ // confirm there aren't any unexpected log messages
+ EXPECT_EQ(0, logCounts[G_LOG_LEVEL_ERROR]);
+ EXPECT_EQ(0, logCounts[G_LOG_LEVEL_CRITICAL]);
+ EXPECT_EQ(0, logCounts[G_LOG_LEVEL_WARNING]);
+ EXPECT_EQ(0, logCounts[G_LOG_LEVEL_MESSAGE]);
+ EXPECT_EQ(0, logCounts[G_LOG_LEVEL_INFO]);
+#endif
+
+ // revert to glib's log handler
+ //g_log_set_default_handler(realLogHandler, this);
+
+ g_clear_pointer(&loop, g_main_loop_unref);
+ }
+
+ private:
+
+ static gboolean
+ wait_for_signal__timeout(gpointer name)
+ {
+ g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name);
+ return G_SOURCE_REMOVE;
+ }
+
+ static gboolean
+ wait_msec__timeout(gpointer loop)
+ {
+ g_main_loop_quit(static_cast<GMainLoop*>(loop));
+ return G_SOURCE_CONTINUE;
+ }
+
+ protected:
+
+ /* convenience func to loop while waiting for a GObject's signal */
+ void wait_for_signal(gpointer o, const gchar * signal, unsigned int timeout_seconds=5u)
+ {
+ // wait for the signal or for timeout, whichever comes first
+ const auto handler_id = g_signal_connect_swapped(o, signal,
+ G_CALLBACK(g_main_loop_quit),
+ loop);
+ const auto timeout_id = g_timeout_add_seconds(timeout_seconds,
+ wait_for_signal__timeout,
+ loop);
+ g_main_loop_run(loop);
+ g_source_remove(timeout_id);
+ g_signal_handler_disconnect(o, handler_id);
+ }
+
+ /* convenience func to loop for N msec */
+ void wait_msec(unsigned int msec=50u)
+ {
+ const auto id = g_timeout_add(msec, wait_msec__timeout, loop);
+ g_main_loop_run(loop);
+ g_source_remove(id);
+ }
+
+ GMainLoop * loop;
+};
diff --git a/tests/test-notify.cc b/tests/test-notify.cc
index 0b75177..6ac3d82 100644
--- a/tests/test-notify.cc
+++ b/tests/test-notify.cc
@@ -17,30 +17,119 @@
* Charles Kerr <charles.kerr@canonical.com>
*/
+
+#include "glib-fixture.h"
+
#include "device.h"
-#include "service.h"
+#include "notifier.h"
#include <gtest/gtest.h>
+#include <libdbustest/dbus-test.h>
+
+#include <libnotify/notify.h>
+
+#include <glib.h>
#include <gio/gio.h>
-class NotifyTest : public ::testing::Test
+/***
+****
+***/
+
+class NotifyFixture: public GlibFixture
{
- private:
+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"};
+
+ DbusTestService * service = nullptr;
+ DbusTestDbusMock * mock = nullptr;
+ DbusTestDbusMockObject * obj = nullptr;
+ GDBusConnection * bus = nullptr;
+
+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-power-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"};
+
+protected:
+
+ void SetUp()
+ {
+ super::SetUp();
+
+ // init DBusMock / dbus-test-runner
+
+ service = dbus_test_service_new(NULL);
+
+ GError * error = NULL;
+ 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,
+ NULL,
+ 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, NULL, NULL);
+ g_dbus_connection_set_exit_on_close(bus, FALSE);
+ g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus);
- typedef ::testing::Test super;
+ notify_init(APP_NAME);
+ }
- protected:
+ virtual void TearDown()
+ {
+ notify_uninit();
- virtual void SetUp()
- {
- super::SetUp();
- }
+ g_clear_object(&mock);
+ g_clear_object(&service);
+ g_object_unref(bus);
- virtual void TearDown()
- {
- super::TearDown();
- }
+ // wait a little while for the scaffolding to shut down,
+ // but don't block on it forever...
+ unsigned int cleartry = 0;
+ while ((bus != NULL) && (cleartry < 50))
+ {
+ g_usleep(100000);
+ while (g_main_pending())
+ g_main_iteration(true);
+ cleartry++;
+ }
+
+ super::TearDown();
+ }
};
/***
@@ -53,7 +142,82 @@ class NotifyTest : public ::testing::Test
// popup should appear exactly twice: once at 10%, once at 5%
-TEST_F(NotifyTest, HelloWorld)
+TEST_F(NotifyFixture, HelloWorld)
{
}
+#if 0
+using namespace unity::indicator::datetime;
+
+/***
+****
+***/
+
+using namespace unity::indicator::datetime;
+
+class SnapFixture: public GlibFixture
+{
+
+/***
+****
+***/
+
+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);
+}
+#endif
+