From 28f30d182ba1cc275626ded8694b63af2b8fa350 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Fri, 23 Nov 2018 13:50:46 +0100 Subject: [PATCH 01/23] Rename queues teardown function Make it consistent with the queues_init function --- src/dunst.c | 2 +- src/queues.c | 4 ++-- src/queues.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index 88031e8..337c2e2 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -101,7 +101,7 @@ static void teardown(void) { regex_teardown(); - teardown_queues(); + queues_teardown(); draw_deinit(); } diff --git a/src/queues.c b/src/queues.c index 9f292ff..8ee0243 100644 --- a/src/queues.c +++ b/src/queues.c @@ -518,7 +518,7 @@ bool queues_pause_status(void) } /** - * Helper function for teardown_queues() to free a single notification + * Helper function for queues_teardown() to free a single notification * * @param data The notification to free */ @@ -529,7 +529,7 @@ static void teardown_notification(gpointer data) } /* see queues.h */ -void teardown_queues(void) +void queues_teardown(void) { g_queue_free_full(history, teardown_notification); g_queue_free_full(displayed, teardown_notification); diff --git a/src/queues.h b/src/queues.h index d093582..f0feaed 100644 --- a/src/queues.h +++ b/src/queues.h @@ -181,7 +181,7 @@ bool queues_pause_status(void); * * @pre At least one time queues_init() called */ -void teardown_queues(void); +void queues_teardown(void); #endif /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 5f3960b171fd9dcea75022e670c7ac48f44e7928 Mon Sep 17 00:00:00 2001 From: Nikos Tsipinakis Date: Thu, 15 Nov 2018 13:42:05 +0200 Subject: [PATCH 02/23] Fix id replacement not assigning id if no notification has it In previous releases when a replace request came in with an id that doesn't exist we created a new notification with it anyway. This is used by some to imitate the behaviour of `stack_tag` and while not recommended (as it will break if another notification gets assigned that id) we want to avoid such subtle breakages without consideration. This bug was introduced in d879d70da060ea78fe735d62249a0afdf3e61bc8. --- src/queues.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/queues.c b/src/queues.c index 8ee0243..6597247 100644 --- a/src/queues.c +++ b/src/queues.c @@ -124,8 +124,6 @@ static bool queues_notification_is_ready(const struct notification *n, bool full /* see queues.h */ int queues_notification_insert(struct notification *n) { - bool inserted = false; - /* do not display the message, if the message is empty */ if (STR_EMPTY(n->msg)) { if (settings.always_run_script) { @@ -148,15 +146,23 @@ int queues_notification_insert(struct notification *n) return 0; } - if (!inserted && n->id != 0 && queues_notification_replace_id(n)) + bool inserted = false; + if (n->id != 0) { + if (!queues_notification_replace_id(n)) { + // Requested id was not valid, but play nice and assign it anyway + g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); + } inserted = true; - else + } else { n->id = ++next_notification_id; + } if (!inserted && STR_FULL(n->stack_tag) && queues_stack_by_tag(n)) inserted = true; + if (!inserted && settings.stack_duplicates && queues_stack_duplicate(n)) inserted = true; + if (!inserted) g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); From cb16fe9d9652918610d2b86d955f0d09014c3f23 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 17:08:18 +0100 Subject: [PATCH 03/23] Fix DoS in notification_format_message When using a format with a trailing % character, dunst ends in an endless loop, searching for a % char, while pointing exactly with the haystack on the % character. Increasing the substring pointer will shift the pointer forwards onto the actual NULL character and stop the loop. --- src/notification.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/notification.c b/src/notification.c index 0155093..fabda53 100644 --- a/src/notification.c +++ b/src/notification.c @@ -376,7 +376,7 @@ static void notification_format_message(struct notification *n) /* replace all formatter */ for(char *substr = strchr(n->msg, '%'); - substr; + substr && *substr; substr = strchr(substr, '%')) { char pg[16]; @@ -450,6 +450,7 @@ static void notification_format_message(struct notification *n) case '\0': LOG_W("format_string has trailing %% character. " "To escape it use %%%%."); + substr++; break; default: LOG_W("format_string %%%c is unknown.", substr[1]); From e04003e29182cb74ce24d83711a85fb875b8f642 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Mon, 1 Oct 2018 22:02:10 +0200 Subject: [PATCH 04/23] Add Framework for queues tests --- src/log.c | 3 ++- src/queues.c | 2 +- src/queues.h | 2 +- test/queues.c | 20 ++++++++++++++++++++ test/queues.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ test/test.c | 2 ++ 6 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 test/queues.c create mode 100644 test/queues.h diff --git a/src/log.c b/src/log.c index 3e51fb0..a5e7de7 100644 --- a/src/log.c +++ b/src/log.c @@ -1,6 +1,7 @@ /* copyright 2012 - 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */ -/** @file log.c +/** + * @file src/log.c * @brief logging wrapper to use GLib's logging capabilities */ diff --git a/src/queues.c b/src/queues.c index 6597247..427e6b9 100644 --- a/src/queues.c +++ b/src/queues.c @@ -1,7 +1,7 @@ /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */ /** - * @file queues.c + * @file src/queues.c * @brief All important functions to handle the notification queues for * history, entrance and currently displayed ones. * diff --git a/src/queues.h b/src/queues.h index f0feaed..0b55a38 100644 --- a/src/queues.h +++ b/src/queues.h @@ -1,7 +1,7 @@ /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */ /** - * @file queues.c + * @file src/queues.h */ #ifndef DUNST_QUEUE_H diff --git a/test/queues.c b/test/queues.c new file mode 100644 index 0000000..93f8c25 --- /dev/null +++ b/test/queues.c @@ -0,0 +1,20 @@ +#include "../src/queues.c" +#include "greatest.h" +#include "queues.h" + +TEST test_queue_init(void) +{ + queues_init(); + + QUEUE_LEN_ALL(0, 0, 0); + + queues_teardown(); + PASS(); +} + +SUITE(suite_queues) +{ + RUN_TEST(test_queue_init); +} + +/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/test/queues.h b/test/queues.h new file mode 100644 index 0000000..72bf29f --- /dev/null +++ b/test/queues.h @@ -0,0 +1,48 @@ +#include "greatest.h" + +#ifndef DUNST_TEST_QUEUES_H +#define DUNST_TEST_QUEUES_H + +#include +#include + +#include "../src/notification.h" +#include "../src/queues.h" + +#define QUEUE_WAIT waiting +#define QUEUE_DISP displayed +#define QUEUE_HIST history +#define QUEUE(q) QUEUE_##q + +#define QUEUE_LEN_ALL(wait, disp, hist) do { \ + if (wait >= 0) ASSERTm("Waiting is not " #wait, wait == g_queue_get_length(QUEUE(WAIT))); \ + if (disp >= 0) ASSERTm("Displayed is not " #disp, disp == g_queue_get_length(QUEUE(DISP))); \ + if (disp >= 0) ASSERTm("History is not " #hist, hist == g_queue_get_length(QUEUE(HIST))); \ + } while (0) + +#define QUEUE_CONTAINS(q, n) QUEUE_CONTAINSm("QUEUE_CONTAINS(" #q "," #n ")", q, n) +#define QUEUE_CONTAINSm(msg, q, n) ASSERTm(msg, g_queue_find(QUEUE(q), n)) + +#define NOT_LAST(n) do {ASSERT_EQm("Notification " #n " should have been deleted.", 1, notification_refcount_get(n)); g_clear_pointer(&n, notification_unref); } while(0) + +static inline struct notification *test_notification(const char *name, gint64 timeout) +{ + struct notification *n = notification_create(); + + if (timeout != -1) + n->timeout = S2US(timeout); + + n->dbus_client = g_strconcat(":", name, NULL); + n->appname = g_strconcat("app of ", name, NULL); + n->summary = g_strconcat(name, NULL); + n->body = g_strconcat("See, ", name, ", I've got a body for you!", NULL); + + n->format = "%s\n%b"; + + notification_init(n); + + return n; +} + +#endif +/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/test/test.c b/test/test.c index 432911d..7d2f6f4 100644 --- a/test/test.c +++ b/test/test.c @@ -15,6 +15,7 @@ SUITE_EXTERN(suite_notification); SUITE_EXTERN(suite_markup); SUITE_EXTERN(suite_misc); SUITE_EXTERN(suite_icon); +SUITE_EXTERN(suite_queues); GREATEST_MAIN_DEFS(); @@ -36,6 +37,7 @@ int main(int argc, char *argv[]) { RUN_SUITE(suite_markup); RUN_SUITE(suite_misc); RUN_SUITE(suite_icon); + RUN_SUITE(suite_queues); GREATEST_MAIN_END(); base = NULL; From fb5926b6fa2c895b5d268db7bde6f2dd3dc816de Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Mon, 1 Oct 2018 22:03:38 +0200 Subject: [PATCH 05/23] Add tests for queue teardown --- src/queues.c | 3 +++ test/queues.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/queues.c b/src/queues.c index 427e6b9..24a8b9f 100644 --- a/src/queues.c +++ b/src/queues.c @@ -538,7 +538,10 @@ static void teardown_notification(gpointer data) void queues_teardown(void) { g_queue_free_full(history, teardown_notification); + history = NULL; g_queue_free_full(displayed, teardown_notification); + displayed = NULL; g_queue_free_full(waiting, teardown_notification); + waiting = NULL; } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/test/queues.c b/test/queues.c index 93f8c25..55a59df 100644 --- a/test/queues.c +++ b/test/queues.c @@ -12,9 +12,27 @@ TEST test_queue_init(void) PASS(); } +TEST test_queue_teardown(void) +{ + queues_init(); + QUEUE_LEN_ALL(0, 0, 0); + + struct notification *n = test_notification("n", -1); + queues_notification_insert(n); + + queues_teardown(); + + ASSERT(waiting == NULL); + ASSERT(displayed == NULL); + ASSERT(history == NULL); + + PASS(); +} + SUITE(suite_queues) { RUN_TEST(test_queue_init); + RUN_TEST(test_queue_teardown); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 294cfdfa47cd4ab2c3745e5c9c544ccc586ad6dc Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Mon, 1 Oct 2018 22:39:00 +0200 Subject: [PATCH 06/23] Test insertion mechanisms --- test/queues.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test/queues.c b/test/queues.c index 55a59df..15a5045 100644 --- a/test/queues.c +++ b/test/queues.c @@ -2,6 +2,79 @@ #include "greatest.h" #include "queues.h" +TEST test_queue_insert_id_valid_newid(void) +{ + struct notification *n; + queues_init(); + + n = test_notification("n", -1); + n->id = 0; + + queues_notification_insert(n); + + QUEUE_LEN_ALL(1, 0, 0); + ASSERT(n->id > 0); + + queues_teardown(); + PASS(); +} + +TEST test_queue_insert_id_invalid(void) +{ + struct notification *n; + queues_init(); + + n = test_notification("n", -1); + n->id = 1000; + + queues_notification_insert(n); + QUEUE_LEN_ALL(1, 0, 0); + ASSERTm("The given ID shouldn't be 0 anymore.", + n->id > 0); + ASSERT_EQm("This is a relict from times before stack_tag: " + "Even if next_notification_id is lower than the requested id, " + "it should use the requested id.", + 1000, n->id); + + queues_teardown(); + PASS(); +} + +TEST test_queue_insert_id_replacement(void) +{ + struct notification *a, *b, *c; + queues_init(); + + a = test_notification("a", -1); + notification_ref(a); + + queues_notification_insert(a); + QUEUE_LEN_ALL(1, 0, 0); + + b = test_notification("b", -1); + notification_ref(b); + b->id = a->id; + + queues_notification_insert(b); + QUEUE_LEN_ALL(1, 0, 0); + + ASSERT_EQ(a->id, b->id); + NOT_LAST(a); + + queues_update(false); + c = test_notification("c", -1); + c->id = b->id; + + queues_notification_insert(c); + + QUEUE_LEN_ALL(0, 1, 0); + ASSERT_EQ(b->id, c->id); + NOT_LAST(b); + + queues_teardown(); + PASS(); +} + TEST test_queue_init(void) { queues_init(); @@ -32,6 +105,9 @@ TEST test_queue_teardown(void) SUITE(suite_queues) { RUN_TEST(test_queue_init); + RUN_TEST(test_queue_insert_id_invalid); + RUN_TEST(test_queue_insert_id_replacement); + RUN_TEST(test_queue_insert_id_valid_newid); RUN_TEST(test_queue_teardown); } From ef08f6aa6690e41005793452e545832b8c57c985 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Tue, 2 Oct 2018 15:36:09 +0200 Subject: [PATCH 07/23] Test notification closing in queues --- test/queues.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/queues.c b/test/queues.c index 15a5045..e70d48e 100644 --- a/test/queues.c +++ b/test/queues.c @@ -75,6 +75,70 @@ TEST test_queue_insert_id_replacement(void) PASS(); } +TEST test_queue_notification_close(void) +{ + struct notification *n; + + // Test closing from waiting queue + n = test_notification("n", -1); + + queues_init(); + queues_notification_insert(n); + QUEUE_LEN_ALL(1, 0, 0); + queues_notification_close(n, REASON_UNDEF); + queues_update(NULL); + QUEUE_LEN_ALL(0, 0, 1); + queues_teardown(); + + // Test closing from displayed queue + n = test_notification("n", -1); + + queues_init(); + queues_notification_insert(n); + QUEUE_LEN_ALL(1, 0, 0); + queues_update(NULL); + QUEUE_LEN_ALL(0, 1, 0); + queues_notification_close(n, REASON_UNDEF); + queues_update(NULL); + QUEUE_LEN_ALL(0, 0, 1); + queues_teardown(); + + PASS(); +} + +TEST test_queue_notification_close_histignore(void) +{ + struct notification *n; + + // Test closing from waiting queue + n = test_notification("n", -1); + n->history_ignore = true; + + queues_init(); + queues_notification_insert(n); + QUEUE_LEN_ALL(1, 0, 0); + queues_notification_close(n, REASON_UNDEF); + queues_update(NULL); + QUEUE_LEN_ALL(0, 0, 0); + queues_teardown(); + + // Test closing from displayed queue + n = test_notification("n", -1); + n->history_ignore = true; + + queues_init(); + queues_notification_insert(n); + QUEUE_LEN_ALL(1, 0, 0); + queues_update(NULL); + QUEUE_LEN_ALL(0, 1, 0); + queues_notification_close(n, REASON_UNDEF); + queues_update(NULL); + QUEUE_LEN_ALL(0, 0, 0); + queues_teardown(); + + PASS(); +} + TEST test_queue_init(void) { queues_init(); @@ -108,6 +172,8 @@ SUITE(suite_queues) RUN_TEST(test_queue_insert_id_invalid); RUN_TEST(test_queue_insert_id_replacement); RUN_TEST(test_queue_insert_id_valid_newid); + RUN_TEST(test_queue_notification_close); + RUN_TEST(test_queue_notification_close_histignore); RUN_TEST(test_queue_teardown); } From 140278a36f5ab3ebce80b2fa20be8e15ec00a128 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 3 Oct 2018 15:02:46 +0200 Subject: [PATCH 08/23] Introduce a status structure --- src/dunst.c | 43 +++++++++++++++++++++++++++++++++++++----- src/dunst.h | 24 ++++++++++++++++++++++++ src/queues.c | 52 +++++++++++++++++---------------------------------- src/queues.h | 35 +++++----------------------------- test/dunst.c | 23 +++++++++++++++++++++++ test/queues.c | 14 +++++++------- test/queues.h | 6 ++++++ test/test.c | 2 ++ 8 files changed, 122 insertions(+), 77 deletions(-) create mode 100644 test/dunst.c diff --git a/src/dunst.c b/src/dunst.c index 337c2e2..6f901c0 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -2,6 +2,7 @@ #include "dunst.h" +#include #include #include #include @@ -24,6 +25,34 @@ GMainLoop *mainloop = NULL; +static struct dunst_status status; + +/* see dunst.h */ +void dunst_status(const enum dunst_status_field field, + bool value) +{ + switch (field) { + case S_FULLSCREEN: + status.fullscreen = value; + break; + case S_IDLE: + status.idle = value; + break; + case S_RUNNING: + status.running = value; + break; + default: + LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__); + break; + } +} + +/* see dunst.h */ +struct dunst_status dunst_status_get(void) +{ + return status; +} + /* misc functions */ static gboolean run(void *data); @@ -36,10 +65,11 @@ static gboolean run(void *data) { LOG_D("RUN"); - bool fullscreen = have_fullscreen_window(); + dunst_status(S_FULLSCREEN, have_fullscreen_window()); + dunst_status(S_IDLE, x_is_idle()); - queues_check_timeouts(x_is_idle(), fullscreen); - queues_update(fullscreen); + queues_check_timeouts(status); + queues_update(status); static gint64 next_timeout = 0; @@ -76,7 +106,7 @@ static gboolean run(void *data) gboolean pause_signal(gpointer data) { - queues_pause_on(); + dunst_status(S_RUNNING, false); wake_up(); return G_SOURCE_CONTINUE; @@ -84,7 +114,7 @@ gboolean pause_signal(gpointer data) gboolean unpause_signal(gpointer data) { - queues_pause_off(); + dunst_status(S_RUNNING, true); wake_up(); return G_SOURCE_CONTINUE; @@ -109,6 +139,9 @@ static void teardown(void) int dunst_main(int argc, char *argv[]) { + dunst_status(S_RUNNING, true); + dunst_status(S_IDLE, false); + queues_init(); cmdline_load(argc, argv); diff --git a/src/dunst.h b/src/dunst.h index a0d0519..47a2b35 100644 --- a/src/dunst.h +++ b/src/dunst.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "notification.h" @@ -14,6 +15,29 @@ #define ColFG 1 #define ColBG 0 +//!< A structure to describe dunst's global window status +struct dunst_status { + bool fullscreen; //!< a fullscreen window is currently focused + bool running; //!< set true if dunst is currently running + bool idle; //!< set true if the user is idle +}; + +enum dunst_status_field { + S_FULLSCREEN, + S_IDLE, + S_RUNNING, +}; + +/** + * Modify the current status of dunst + * @param field The field to change in the global status structure + * @param value Anything boolean or DO_TOGGLE to toggle the current value + */ +void dunst_status(const enum dunst_status_field field, + bool value); + +struct dunst_status dunst_status_get(void); + extern const char *colors[3][3]; void wake_up(void); diff --git a/src/queues.c b/src/queues.c index 24a8b9f..4bff9b1 100644 --- a/src/queues.c +++ b/src/queues.c @@ -20,6 +20,7 @@ #include #include +#include "dunst.h" #include "log.h" #include "notification.h" #include "settings.h" @@ -31,7 +32,6 @@ static GQueue *displayed = NULL; /**< currently displayed notifications */ static GQueue *history = NULL; /**< history of displayed notifications */ int next_notification_id = 1; -bool pause_displayed = false; static bool queues_stack_duplicate(struct notification *n); static bool queues_stack_by_tag(struct notification *n); @@ -107,15 +107,15 @@ static void queues_swap_notifications(GQueue *queueA, /** * Check if a notification is eligible to get shown. * - * @param n The notification to check - * @param fullscreen True if a fullscreen window is currently active - * @param visible True if the notification is currently displayed + * @param n The notification to check + * @param status The current status of dunst + * @param shown True if the notification is currently displayed */ -static bool queues_notification_is_ready(const struct notification *n, bool fullscreen, bool visible) +static bool queues_notification_is_ready(const struct notification *n, struct dunst_status status, bool shown) { - if (fullscreen && visible) + if (status.fullscreen && shown) return n && n->fullscreen != FS_PUSHBACK; - else if (fullscreen && !visible) + else if (status.fullscreen && !shown) return n && n->fullscreen == FS_SHOW; else return true; @@ -134,15 +134,15 @@ int queues_notification_insert(struct notification *n) } /* Do not insert the message if it's a command */ if (STR_EQ("DUNST_COMMAND_PAUSE", n->summary)) { - pause_displayed = true; + dunst_status(S_RUNNING, false); return 0; } if (STR_EQ("DUNST_COMMAND_RESUME", n->summary)) { - pause_displayed = false; + dunst_status(S_RUNNING, true); return 0; } if (STR_EQ("DUNST_COMMAND_TOGGLE", n->summary)) { - pause_displayed = !pause_displayed; + dunst_status(S_RUNNING, !dunst_status_get().running); return 0; } @@ -343,13 +343,13 @@ void queues_history_push_all(void) } /* see queues.h */ -void queues_check_timeouts(bool idle, bool fullscreen) +void queues_check_timeouts(struct dunst_status status) { /* nothing to do */ if (displayed->length == 0) return; - bool is_idle = fullscreen ? false : idle; + bool is_idle = status.fullscreen ? false : status.idle; GList *iter = g_queue_peek_head_link(displayed); while (iter) { @@ -381,9 +381,9 @@ void queues_check_timeouts(bool idle, bool fullscreen) } /* see queues.h */ -void queues_update(bool fullscreen) +void queues_update(struct dunst_status status) { - if (pause_displayed) { + if (!status.running) { while (displayed->length > 0) { g_queue_insert_sorted( waiting, g_queue_pop_head(displayed), notification_cmp_data, NULL); @@ -392,7 +392,7 @@ void queues_update(bool fullscreen) } /* move notifications back to queue, which are set to pushback */ - if (fullscreen) { + if (status.fullscreen) { GList *iter = g_queue_peek_head_link(displayed); while (iter) { struct notification *n = iter->data; @@ -426,7 +426,7 @@ void queues_update(bool fullscreen) if (!n) return; - if (!queues_notification_is_ready(n, fullscreen, false)) { + if (!queues_notification_is_ready(n, status, false)) { iter = nextiter; continue; } @@ -455,7 +455,7 @@ void queues_update(bool fullscreen) while ( (i_waiting = g_queue_peek_head_link(waiting)) && (i_displayed = g_queue_peek_tail_link(displayed))) { - while (i_waiting && ! queues_notification_is_ready(i_waiting->data, fullscreen, true)) { + while (i_waiting && ! queues_notification_is_ready(i_waiting->data, status, true)) { i_waiting = i_waiting->prev; } @@ -505,24 +505,6 @@ gint64 queues_get_next_datachange(gint64 time) return sleep != G_MAXINT64 ? sleep : -1; } -/* see queues.h */ -void queues_pause_on(void) -{ - pause_displayed = true; -} - -/* see queues.h */ -void queues_pause_off(void) -{ - pause_displayed = false; -} - -/* see queues.h */ -bool queues_pause_status(void) -{ - return pause_displayed; -} - /** * Helper function for queues_teardown() to free a single notification * diff --git a/src/queues.h b/src/queues.h index 0b55a38..a74e30b 100644 --- a/src/queues.h +++ b/src/queues.h @@ -8,6 +8,7 @@ #define DUNST_QUEUE_H #include "dbus.h" +#include "dunst.h" #include "notification.h" /** @@ -120,12 +121,9 @@ void queues_history_push_all(void); /** * Check timeout of each notification and close it, if necessary * - * @param idle the program's idle status. Important to calculate the - * timeout for transient notifications - * @param fullscreen the desktop's fullscreen status. Important to - * calculate the timeout for transient notifications + * @param status the current status of dunst */ -void queues_check_timeouts(bool idle, bool fullscreen); +void queues_check_timeouts(struct dunst_status status); /** * Move inserted notifications from waiting queue to displayed queue @@ -135,10 +133,9 @@ void queues_check_timeouts(bool idle, bool fullscreen); * @post Call wake_up() to synchronize the queues with the UI * (which closes old and shows new notifications on screen) * - * @param fullscreen the desktop's fullscreen status. Important to - * move notifications to the right queue + * @param status the current status of dunst */ -void queues_update(bool fullscreen); +void queues_update(struct dunst_status status); /** * Calculate the distance to the next event, when an element in the @@ -154,28 +151,6 @@ void queues_update(bool fullscreen); */ gint64 queues_get_next_datachange(gint64 time); -/** - * Pause queue-management of dunst - * - * @post Calling update_lists() is necessary - */ -void queues_pause_on(void); - -/** - * Unpause (run) queue-management of dunst - * - * @post Calling update_lists() is necessary - */ -void queues_pause_off(void); - -/** - * Get the current status - * - * @return true if paused - * @return false if running - */ -bool queues_pause_status(void); - /** * Remove all notifications from all list and free the notifications * diff --git a/test/dunst.c b/test/dunst.c new file mode 100644 index 0000000..827f98d --- /dev/null +++ b/test/dunst.c @@ -0,0 +1,23 @@ +#include "../src/dunst.c" +#include "greatest.h" + +TEST test_dunst_status(void) +{ + status = (struct dunst_status) {false, false, false}; + + dunst_status(S_FULLSCREEN, true); + ASSERT(status.fullscreen); + dunst_status(S_IDLE, true); + ASSERT(status.idle); + dunst_status(S_RUNNING, true); + ASSERT(status.running); + + PASS(); +} + +SUITE(suite_dunst) +{ + RUN_TEST(test_dunst_status); +} + +/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/test/queues.c b/test/queues.c index e70d48e..6afda5d 100644 --- a/test/queues.c +++ b/test/queues.c @@ -61,7 +61,7 @@ TEST test_queue_insert_id_replacement(void) ASSERT_EQ(a->id, b->id); NOT_LAST(a); - queues_update(false); + queues_update(STATUS_NORMAL); c = test_notification("c", -1); c->id = b->id; @@ -86,7 +86,7 @@ TEST test_queue_notification_close(void) queues_notification_insert(n); QUEUE_LEN_ALL(1, 0, 0); queues_notification_close(n, REASON_UNDEF); - queues_update(NULL); + queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0, 0, 1); queues_teardown(); @@ -96,10 +96,10 @@ TEST test_queue_notification_close(void) queues_init(); queues_notification_insert(n); QUEUE_LEN_ALL(1, 0, 0); - queues_update(NULL); + queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0, 1, 0); queues_notification_close(n, REASON_UNDEF); - queues_update(NULL); + queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0, 0, 1); queues_teardown(); @@ -118,7 +118,7 @@ TEST test_queue_notification_close_histignore(void) queues_notification_insert(n); QUEUE_LEN_ALL(1, 0, 0); queues_notification_close(n, REASON_UNDEF); - queues_update(NULL); + queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0, 0, 0); queues_teardown(); @@ -129,10 +129,10 @@ TEST test_queue_notification_close_histignore(void) queues_init(); queues_notification_insert(n); QUEUE_LEN_ALL(1, 0, 0); - queues_update(NULL); + queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0, 1, 0); queues_notification_close(n, REASON_UNDEF); - queues_update(NULL); + queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0, 0, 0); queues_teardown(); diff --git a/test/queues.h b/test/queues.h index 72bf29f..67b68a7 100644 --- a/test/queues.h +++ b/test/queues.h @@ -9,6 +9,12 @@ #include "../src/notification.h" #include "../src/queues.h" +#define STATUS_NORMAL ((struct dunst_status) {.fullscreen=false, .running=true, .idle=false}) +#define STATUS_IDLE ((struct dunst_status) {.fullscreen=false, .running=true, .idle=true}) +#define STATUS_FSIDLE ((struct dunst_status) {.fullscreen=true, .running=true, .idle=true}) +#define STATUS_FS ((struct dunst_status) {.fullscreen=true, .running=true, .idle=false}) +#define STATUS_PAUSE ((struct dunst_status) {.fullscreen=false, .running=false, .idle=false}) + #define QUEUE_WAIT waiting #define QUEUE_DISP displayed #define QUEUE_HIST history diff --git a/test/test.c b/test/test.c index 7d2f6f4..a816ee5 100644 --- a/test/test.c +++ b/test/test.c @@ -16,6 +16,7 @@ SUITE_EXTERN(suite_markup); SUITE_EXTERN(suite_misc); SUITE_EXTERN(suite_icon); SUITE_EXTERN(suite_queues); +SUITE_EXTERN(suite_dunst); GREATEST_MAIN_DEFS(); @@ -38,6 +39,7 @@ int main(int argc, char *argv[]) { RUN_SUITE(suite_misc); RUN_SUITE(suite_icon); RUN_SUITE(suite_queues); + RUN_SUITE(suite_dunst); GREATEST_MAIN_END(); base = NULL; From 662b22e5de4aa07f445f5e086556bcd6003f5d24 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 14:07:43 +0100 Subject: [PATCH 09/23] Test sleep times for get_next_datachange --- src/queues.c | 8 ++-- test/queues.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/queues.c b/src/queues.c index 4bff9b1..d852da2 100644 --- a/src/queues.c +++ b/src/queues.c @@ -494,11 +494,11 @@ gint64 queues_get_next_datachange(gint64 time) if (settings.show_age_threshold >= 0) { gint64 age = time - n->timestamp; - if (age > settings.show_age_threshold) - // sleep exactly until the next shift of the second happens + // sleep exactly until the next shift of the second happens + if (age > settings.show_age_threshold - S2US(1)) sleep = MIN(sleep, (S2US(1) - (age % S2US(1)))); - else if (n->timeout == 0 || ttl > settings.show_age_threshold) - sleep = MIN(sleep, settings.show_age_threshold); + else + sleep = MIN(sleep, settings.show_age_threshold - age); } } diff --git a/test/queues.c b/test/queues.c index 6afda5d..4ac042b 100644 --- a/test/queues.c +++ b/test/queues.c @@ -1,4 +1,8 @@ #include "../src/queues.c" + +#define GREATEST_FLOAT gint64 +#define GREATEST_FLOAT_FMT "%ld" + #include "greatest.h" #include "queues.h" @@ -166,8 +170,116 @@ TEST test_queue_teardown(void) PASS(); } +TEST test_datachange_beginning_empty(void) +{ + queues_init(); + + ASSERTm("There are no notifications at all, the timeout has to be less than 0.", + queues_get_next_datachange(time_monotonic_now()) < 0); + + queues_teardown(); + PASS(); +} + +TEST test_datachange_endless(void) +{ + queues_init(); + + settings.show_age_threshold = -1; + + struct notification *n = test_notification("n", 0); + + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + + ASSERTm("Age threshold is deactivated and the notification is infinite, there is no wakeup necessary.", + queues_get_next_datachange(time_monotonic_now()) < 0); + + queues_teardown(); + PASS(); +} + +TEST test_datachange_endless_agethreshold(void) +{ + settings.show_age_threshold = S2US(5); + + queues_init(); + + struct notification *n = test_notification("n", 0); + + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + + ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than a second away", + S2US(1)/2, queues_get_next_datachange(time_monotonic_now() + S2US(4)), S2US(1)/2); + + ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than the age threshold", + settings.show_age_threshold/2, queues_get_next_datachange(time_monotonic_now()), settings.show_age_threshold/2); + + settings.show_age_threshold = S2US(0); + ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than a second away", + S2US(1)/2, queues_get_next_datachange(time_monotonic_now()), S2US(1)/2); + + queues_teardown(); + PASS(); +} + +TEST test_datachange_queues(void) +{ + queues_init(); + + struct notification *n = test_notification("n", 10); + + queues_notification_insert(n); + ASSERTm("The inserted notification is inside the waiting queue, so it should get ignored.", + queues_get_next_datachange(time_monotonic_now()) < S2US(0)); + + queues_update(STATUS_NORMAL); + ASSERT_IN_RANGEm("The notification has to get closed in less than its timeout", + S2US(10)/2, queues_get_next_datachange(time_monotonic_now()), S2US(10)/2); + + queues_notification_close(n, REASON_UNDEF); + ASSERTm("The inserted notification is inside the history queue, so it should get ignored", + queues_get_next_datachange(time_monotonic_now()) < S2US(0)); + + queues_teardown(); + PASS(); +} + +TEST test_datachange_ttl(void) +{ + struct notification *n; + queues_init(); + + n = test_notification("n1", 15); + + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + ASSERT_IN_RANGEm("The notification has to get closed in less than its timeout.", + n->timeout/2, queues_get_next_datachange(time_monotonic_now()), n->timeout/2); + + n = test_notification("n2", 10); + + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + ASSERT_IN_RANGEm("The timeout of the second notification has to get used as sleep time now.", + n->timeout/2, queues_get_next_datachange(time_monotonic_now()), n->timeout/2); + + ASSERT_EQm("The notification already timed out. You have to answer with 0.", + S2US(0), queues_get_next_datachange(time_monotonic_now() + S2US(10))); + + queues_teardown(); + PASS(); +} + + SUITE(suite_queues) { + RUN_TEST(test_datachange_beginning_empty); + RUN_TEST(test_datachange_endless); + RUN_TEST(test_datachange_endless_agethreshold); + RUN_TEST(test_datachange_queues); + RUN_TEST(test_datachange_ttl); RUN_TEST(test_queue_init); RUN_TEST(test_queue_insert_id_invalid); RUN_TEST(test_queue_insert_id_replacement); From 5cf9e51ada29e217ffbf3d265b3972e44c27dc89 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 14:25:02 +0100 Subject: [PATCH 10/23] Test queue length reporting functions --- test/queues.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/queues.c b/test/queues.c index 4ac042b..84b4aa9 100644 --- a/test/queues.c +++ b/test/queues.c @@ -6,6 +6,36 @@ #include "greatest.h" #include "queues.h" +TEST test_queue_length(void) +{ + queues_init(); + + struct notification *n; + + n = test_notification("n1", 0); + queues_notification_insert(n); + queues_notification_close(n, REASON_UNDEF); + + n = test_notification("n2", 0); + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + + n = test_notification("n3", 0); + queues_notification_insert(n); + + QUEUE_LEN_ALL(1,1,1); + + ASSERT_EQm("Queue waiting has to contain an element", + 1, queues_length_waiting()); + ASSERT_EQm("Queue displayed has to contain an element", + 1, queues_length_displayed()); + ASSERT_EQm("Queue history has to contain an element", + 1, queues_length_history()); + + queues_teardown(); + PASS(); +} + TEST test_queue_insert_id_valid_newid(void) { struct notification *n; @@ -284,6 +314,7 @@ SUITE(suite_queues) RUN_TEST(test_queue_insert_id_invalid); RUN_TEST(test_queue_insert_id_replacement); RUN_TEST(test_queue_insert_id_valid_newid); + RUN_TEST(test_queue_length); RUN_TEST(test_queue_notification_close); RUN_TEST(test_queue_notification_close_histignore); RUN_TEST(test_queue_teardown); From f1e391673a885b01995a6986d0b470ee6d527496 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 17:10:38 +0100 Subject: [PATCH 11/23] Add queue history tests --- test/queues.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/test/queues.c b/test/queues.c index 84b4aa9..50ae155 100644 --- a/test/queues.c +++ b/test/queues.c @@ -173,6 +173,68 @@ TEST test_queue_notification_close_histignore(void) PASS(); } +TEST test_queue_history_overfull(void) +{ + settings.history_length = 10; + queues_init(); + + struct notification *n; + + for (int i = 0; i < 10; i++) { + char name[] = { 'n', '0'+i, '\0' }; // n + n = test_notification(name, -1); + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + queues_notification_close(n, REASON_UNDEF); + } + + QUEUE_LEN_ALL(0, 0, 10); + + n = test_notification("n", -1); + queues_notification_insert(n); + queues_notification_close(n, REASON_UNDEF); + + QUEUE_CONTAINS(HIST, n); + QUEUE_LEN_ALL(0, 0, 10); + + queues_teardown(); + PASS(); +} + +TEST test_queue_history_pushall(void) +{ + settings.history_length = 5; + settings.indicate_hidden = false; + settings.geometry.h = 0; + + queues_init(); + + struct notification *n; + + for (int i = 0; i < 10; i++) { + char name[] = { 'n', '0'+i, '\0' }; // n + n = test_notification(name, -1); + queues_notification_insert(n); + } + queues_update(STATUS_NORMAL); + + for (int i = 0; i < 10; i++) { + char name[] = { '2', 'n', '0'+i, '\0' }; // 2n + n = test_notification(name, -1); + queues_notification_insert(n); + } + + QUEUE_LEN_ALL(10, 10, 0); + + queues_history_push_all(); + + QUEUE_CONTAINS(HIST, n); + QUEUE_LEN_ALL(0, 0, 5); + + queues_teardown(); + PASS(); +} + TEST test_queue_init(void) { queues_init(); @@ -310,6 +372,8 @@ SUITE(suite_queues) RUN_TEST(test_datachange_endless_agethreshold); RUN_TEST(test_datachange_queues); RUN_TEST(test_datachange_ttl); + RUN_TEST(test_queue_history_overfull); + RUN_TEST(test_queue_history_pushall); RUN_TEST(test_queue_init); RUN_TEST(test_queue_insert_id_invalid); RUN_TEST(test_queue_insert_id_replacement); From e6e0d881ebe3460cbd04f09069fc3b170e7e4ca8 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 18:48:49 +0100 Subject: [PATCH 12/23] Test stacking --- test/queues.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/queues.c b/test/queues.c index 50ae155..ed8dab4 100644 --- a/test/queues.c +++ b/test/queues.c @@ -364,6 +364,65 @@ TEST test_datachange_ttl(void) PASS(); } +TEST test_queue_stacking(void) +{ + settings.stack_duplicates = true; + struct notification *n1, *n2, *n3; + + queues_init(); + + n1 = test_notification("n1", -1); + n2 = test_notification("n1", -1); + n3 = test_notification("n1", -1); + + queues_notification_insert(n1); + QUEUE_LEN_ALL(1, 0, 0); + + notification_ref(n1); + queues_notification_insert(n2); + NOT_LAST(n1); + + notification_ref(n2); + queues_update(STATUS_NORMAL); + queues_notification_insert(n3); + QUEUE_LEN_ALL(0, 1, 0); + NOT_LAST(n2); + + queues_teardown(); + PASS(); +} + +TEST test_queue_stacktag(void) +{ + const char *stacktag = "THIS IS A SUPER WIERD STACK TAG"; + struct notification *n1, *n2, *n3; + + queues_init(); + + n1 = test_notification("n1", 1); + n2 = test_notification("n2", 1); + n3 = test_notification("n3", 1); + n1->stack_tag = g_strdup(stacktag); + n2->stack_tag = g_strdup(stacktag); + n3->stack_tag = g_strdup(stacktag); + + queues_notification_insert(n1); + QUEUE_LEN_ALL(1, 0, 0); + + notification_ref(n1); + queues_notification_insert(n2); + NOT_LAST(n1); + + notification_ref(n2); + queues_update(STATUS_NORMAL); + queues_notification_insert(n3); + QUEUE_LEN_ALL(0, 1, 0); + NOT_LAST(n2); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { @@ -381,6 +440,8 @@ SUITE(suite_queues) RUN_TEST(test_queue_length); RUN_TEST(test_queue_notification_close); RUN_TEST(test_queue_notification_close_histignore); + RUN_TEST(test_queue_stacking); + RUN_TEST(test_queue_stacktag); RUN_TEST(test_queue_teardown); } From 2c1de2dc220dca86e293f639dafe80d53e728a20 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 19:22:27 +0100 Subject: [PATCH 13/23] Add tests for checking notifications timing out --- test/queues.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/queues.c b/test/queues.c index ed8dab4..ed39a92 100644 --- a/test/queues.c +++ b/test/queues.c @@ -423,6 +423,52 @@ TEST test_queue_stacktag(void) PASS(); } +TEST test_queue_timeout(void) +{ + settings.geometry.h = 5; + struct notification *n1, *n2, *n3; + + queues_init(); + + // Usually this shouldn't do anything, but we may abort ;-) + queues_check_timeouts(STATUS_NORMAL); + + n1 = test_notification("n1", 0); + n2 = test_notification("n2", 10); + n3 = test_notification("n3", 10); + n3->transient = true; + + queues_notification_insert(n1); + queues_notification_insert(n2); + queues_notification_insert(n3); + + queues_update(STATUS_NORMAL); + + // hacky way to shift time + n1->start -= S2US(11); + n2->start -= S2US(11); + n3->start -= S2US(11); + queues_check_timeouts(STATUS_IDLE); + queues_update(STATUS_IDLE); + + QUEUE_LEN_ALL(0,2,1); + QUEUE_CONTAINS(HIST, n3); + + // hacky way to shift time + n1->start -= S2US(11); + n2->start -= S2US(11); + queues_check_timeouts(STATUS_NORMAL); + queues_update(STATUS_NORMAL); + + QUEUE_LEN_ALL(0,1,2); + QUEUE_CONTAINS(DISP, n1); + QUEUE_CONTAINS(HIST, n2); + QUEUE_CONTAINS(HIST, n3); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { @@ -443,6 +489,7 @@ SUITE(suite_queues) RUN_TEST(test_queue_stacking); RUN_TEST(test_queue_stacktag); RUN_TEST(test_queue_teardown); + RUN_TEST(test_queue_timeout); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 3fbd1fc58926773b7a867788492ed12575fb5a46 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 23:44:22 +0100 Subject: [PATCH 14/23] Add tests for fullscreen behavior --- test/queues.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/queues.c b/test/queues.c index ed39a92..9fd3d81 100644 --- a/test/queues.c +++ b/test/queues.c @@ -469,6 +469,43 @@ TEST test_queue_timeout(void) PASS(); } +TEST test_queues_update_fullscreen(void) +{ + settings.geometry.h = 5; + struct notification *n_show, *n_dela, *n_push; + + queues_init(); + + n_show = test_notification("show", 10); + n_dela = test_notification("dela", 10); + n_push = test_notification("push", 10); + n_show->fullscreen = FS_SHOW; + n_dela->fullscreen = FS_DELAY; + n_push->fullscreen = FS_PUSHBACK; + + queues_notification_insert(n_show); + queues_notification_insert(n_dela); + queues_notification_insert(n_push); + + queues_update(STATUS_FS); + QUEUE_CONTAINS(DISP, n_show); + QUEUE_CONTAINS(WAIT, n_dela); + QUEUE_CONTAINS(WAIT, n_push); + + queues_update(STATUS_NORMAL); + QUEUE_CONTAINS(DISP, n_show); + QUEUE_CONTAINS(DISP, n_dela); + QUEUE_CONTAINS(DISP, n_push); + + queues_update(STATUS_FS); + QUEUE_CONTAINS(DISP, n_show); + QUEUE_CONTAINS(DISP, n_dela); + QUEUE_CONTAINS(WAIT, n_push); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { @@ -490,6 +527,7 @@ SUITE(suite_queues) RUN_TEST(test_queue_stacktag); RUN_TEST(test_queue_teardown); RUN_TEST(test_queue_timeout); + RUN_TEST(test_queues_update_fullscreen); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From acee260624877455d5d364eccba2bf81800b1449 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Wed, 14 Nov 2018 23:58:20 +0100 Subject: [PATCH 15/23] Add test for pausing behavior --- test/queues.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/queues.c b/test/queues.c index 9fd3d81..6c89c5a 100644 --- a/test/queues.c +++ b/test/queues.c @@ -506,6 +506,35 @@ TEST test_queues_update_fullscreen(void) PASS(); } +TEST test_queues_update_paused(void) +{ + settings.geometry.h = 5; + struct notification *n1, *n2, *n3; + queues_init(); + + n1 = test_notification("n1", 0); + n2 = test_notification("n2", 0); + n3 = test_notification("n3", 0); + + queues_notification_insert(n1); + queues_notification_insert(n2); + queues_notification_insert(n3); + + QUEUE_LEN_ALL(3,0,0); + + queues_update(STATUS_PAUSE); + QUEUE_LEN_ALL(3,0,0); + + queues_update(STATUS_NORMAL); + QUEUE_LEN_ALL(0,3,0); + + queues_update(STATUS_PAUSE); + QUEUE_LEN_ALL(3,0,0); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { @@ -528,6 +557,7 @@ SUITE(suite_queues) RUN_TEST(test_queue_teardown); RUN_TEST(test_queue_timeout); RUN_TEST(test_queues_update_fullscreen); + RUN_TEST(test_queues_update_paused); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 24e0e25ec2dc19a517ded50d116688b28f932cf5 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Thu, 15 Nov 2018 00:19:58 +0100 Subject: [PATCH 16/23] Add tests for seeping important notifications --- test/queues.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/queues.c b/test/queues.c index 6c89c5a..6c1845e 100644 --- a/test/queues.c +++ b/test/queues.c @@ -535,6 +535,67 @@ TEST test_queues_update_paused(void) PASS(); } +TEST test_queues_update_seeping(void) +{ + settings.geometry.h = 5; + settings.sort = true; + settings.indicate_hidden = false; + struct notification *nl1, *nl2, *nl3, *nl4, *nl5; + struct notification *nc1, *nc2, *nc3, *nc4, *nc5; + queues_init(); + + nl1 = test_notification("nl1", 0); + nl2 = test_notification("nl2", 0); + nl3 = test_notification("nl3", 0); + nl4 = test_notification("nl4", 0); + nl5 = test_notification("nl5", 0); + + nc1 = test_notification("nc1", 0); + nc2 = test_notification("nc2", 0); + nc3 = test_notification("nc3", 0); + nc4 = test_notification("nc4", 0); + nc5 = test_notification("nc5", 0); + nc1->urgency = URG_CRIT; + nc2->urgency = URG_CRIT; + nc3->urgency = URG_CRIT; + nc4->urgency = URG_CRIT; + nc5->urgency = URG_CRIT; + + queues_notification_insert(nl1); + queues_notification_insert(nl2); + queues_notification_insert(nl3); + queues_notification_insert(nl4); + queues_notification_insert(nl5); + + QUEUE_LEN_ALL(5,0,0); + queues_update(STATUS_NORMAL); + QUEUE_LEN_ALL(0,5,0); + + queues_notification_insert(nc1); + queues_notification_insert(nc2); + queues_notification_insert(nc3); + queues_notification_insert(nc4); + queues_notification_insert(nc5); + + QUEUE_LEN_ALL(5,5,0); + queues_update(STATUS_NORMAL); + QUEUE_LEN_ALL(5,5,0); + + QUEUE_CONTAINS(DISP, nc1); + QUEUE_CONTAINS(DISP, nc2); + QUEUE_CONTAINS(DISP, nc3); + QUEUE_CONTAINS(DISP, nc4); + QUEUE_CONTAINS(DISP, nc5); + + QUEUE_CONTAINS(WAIT, nl1); + QUEUE_CONTAINS(WAIT, nl2); + QUEUE_CONTAINS(WAIT, nl3); + QUEUE_CONTAINS(WAIT, nl4); + QUEUE_CONTAINS(WAIT, nl5); + + queues_teardown(); + PASS(); +} SUITE(suite_queues) { @@ -558,6 +619,7 @@ SUITE(suite_queues) RUN_TEST(test_queue_timeout); RUN_TEST(test_queues_update_fullscreen); RUN_TEST(test_queues_update_paused); + RUN_TEST(test_queues_update_seeping); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 3e0daf40bf99c2c80065f585e70cec0f1adc7d0b Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Thu, 15 Nov 2018 00:21:45 +0100 Subject: [PATCH 17/23] Add tests for xmore layout behavior --- test/queues.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/queues.c b/test/queues.c index 6c1845e..63d212a 100644 --- a/test/queues.c +++ b/test/queues.c @@ -597,6 +597,38 @@ TEST test_queues_update_seeping(void) PASS(); } +TEST test_queues_update_xmore(void) +{ + settings.indicate_hidden = true; + settings.geometry.h = 4; + struct notification *n1, *n2, *n3, *n4, *n5; + queues_init(); + + n1 = test_notification("n1", 0); + n2 = test_notification("n2", 0); + n3 = test_notification("n3", 0); + n4 = test_notification("n4", 0); + n5 = test_notification("n5", 0); + + queues_notification_insert(n1); + queues_notification_insert(n2); + queues_notification_insert(n3); + + queues_update(STATUS_NORMAL); + QUEUE_LEN_ALL(0,3,0); + + queues_notification_insert(n4); + queues_update(STATUS_NORMAL); + QUEUE_LEN_ALL(0,4,0); + + queues_notification_insert(n5); + queues_update(STATUS_NORMAL); + QUEUE_LEN_ALL(2,3,0); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { RUN_TEST(test_datachange_beginning_empty); @@ -620,6 +652,7 @@ SUITE(suite_queues) RUN_TEST(test_queues_update_fullscreen); RUN_TEST(test_queues_update_paused); RUN_TEST(test_queues_update_seeping); + RUN_TEST(test_queues_update_xmore); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From 0b7a31b477646da3fcbf041e0e74343060f2eaa8 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 18 Nov 2018 03:07:13 +0100 Subject: [PATCH 18/23] Add faulty notification seep test --- test/queues.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/queues.c b/test/queues.c index 63d212a..6e5bfb4 100644 --- a/test/queues.c +++ b/test/queues.c @@ -629,6 +629,42 @@ TEST test_queues_update_xmore(void) PASS(); } +TEST test_queues_update_seep_showlowurg(void) +{ + // Test 3 notifications during fullscreen and only the one + // with the lowest priority is eligible to get shown + settings.geometry.h = 4; + struct notification *n1, *n2, *n3; + queues_init(); + + n1 = test_notification("n1", 0); + n2 = test_notification("n2", 0); + n3 = test_notification("n3", 0); + + n1->fullscreen = FS_DELAY; + n2->fullscreen = FS_DELAY; + n3->fullscreen = FS_SHOW; + + n3->urgency = URG_LOW; + + queues_notification_insert(n1); + queues_notification_insert(n2); + queues_update(STATUS_FS); + QUEUE_LEN_ALL(2,0,0); + + queues_notification_insert(n3); + + queues_update(STATUS_FS); + + QUEUE_LEN_ALL(2,1,0); + QUEUE_CONTAINS(WAIT, n1); + QUEUE_CONTAINS(WAIT, n2); + QUEUE_CONTAINS(DISP, n3); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { RUN_TEST(test_datachange_beginning_empty); @@ -651,6 +687,7 @@ SUITE(suite_queues) RUN_TEST(test_queue_timeout); RUN_TEST(test_queues_update_fullscreen); RUN_TEST(test_queues_update_paused); + RUN_TEST(test_queues_update_seep_showlowurg); RUN_TEST(test_queues_update_seeping); RUN_TEST(test_queues_update_xmore); } From 475d829733146a6fd1ea90fe093a964ecd1d52e8 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 18 Nov 2018 03:12:53 +0100 Subject: [PATCH 19/23] Fix notification seeping when displayed isn't full Only if the queue is full, dunst should swap the most important notifications from waiting with the least important notifications from displayed. --- src/queues.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/queues.c b/src/queues.c index d852da2..75e38aa 100644 --- a/src/queues.c +++ b/src/queues.c @@ -449,7 +449,7 @@ void queues_update(struct dunst_status status) /* If displayed is actually full, let the more important notifications * from waiting seep into displayed. */ - if (settings.sort) { + if (settings.sort && displayed->length == cur_displayed_limit) { GList *i_waiting, *i_displayed; while ( (i_waiting = g_queue_peek_head_link(waiting)) From 9878de312bba4fb89ba8019e67a0bb596a2641eb Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 18 Nov 2018 18:46:20 +0100 Subject: [PATCH 20/23] Do not mark the waiting notification as shown Notifications listed in the waiting queue are never shown :see_no_evil: --- src/queues.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/queues.c b/src/queues.c index 75e38aa..8e03b63 100644 --- a/src/queues.c +++ b/src/queues.c @@ -455,7 +455,7 @@ void queues_update(struct dunst_status status) while ( (i_waiting = g_queue_peek_head_link(waiting)) && (i_displayed = g_queue_peek_tail_link(displayed))) { - while (i_waiting && ! queues_notification_is_ready(i_waiting->data, status, true)) { + while (i_waiting && ! queues_notification_is_ready(i_waiting->data, status, false)) { i_waiting = i_waiting->prev; } From 711b11c92f3d304fb21c77b882adac45d90528e1 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Sun, 18 Nov 2018 21:36:27 +0100 Subject: [PATCH 21/23] Merge queue_update and queue_check_timeouts --- src/dunst.c | 1 - src/queues.c | 114 +++++++++++++++++++++++++------------------------- src/queues.h | 7 ---- test/queues.c | 25 ++++++++--- 4 files changed, 76 insertions(+), 71 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index 6f901c0..5dce077 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -68,7 +68,6 @@ static gboolean run(void *data) dunst_status(S_FULLSCREEN, have_fullscreen_window()); dunst_status(S_IDLE, x_is_idle()); - queues_check_timeouts(status); queues_update(status); static gint64 next_timeout = 0; diff --git a/src/queues.c b/src/queues.c index 8e03b63..09303cd 100644 --- a/src/queues.c +++ b/src/queues.c @@ -113,6 +113,8 @@ static void queues_swap_notifications(GQueue *queueA, */ static bool queues_notification_is_ready(const struct notification *n, struct dunst_status status, bool shown) { + if (!status.running) + return false; if (status.fullscreen && shown) return n && n->fullscreen != FS_PUSHBACK; else if (status.fullscreen && !shown) @@ -121,6 +123,38 @@ static bool queues_notification_is_ready(const struct notification *n, struct du return true; } +/** + * Check if a notification has timed out + * + * @param n the notification to check + * @param status the current status of dunst + * @returns true, if the notification is timed out, otherwise false + */ +static bool queues_notification_is_finished(struct notification *n, struct dunst_status status) +{ + assert(n); + + if (n->timeout == 0) // sticky + return false; + if (n->start == 0) // hidden // TODO: is this really the implication of hidden? + return false; + + bool is_idle = status.fullscreen ? false : status.idle; + + /* don't timeout when user is idle */ + if (is_idle && !n->transient) { + n->start = time_monotonic_now(); + return false; + } + + /* remove old message */ + if (time_monotonic_now() - n->start > n->timeout) { + return true; + } + + return false; +} + /* see queues.h */ int queues_notification_insert(struct notification *n) { @@ -342,69 +376,33 @@ void queues_history_push_all(void) } } -/* see queues.h */ -void queues_check_timeouts(struct dunst_status status) -{ - /* nothing to do */ - if (displayed->length == 0) - return; - - bool is_idle = status.fullscreen ? false : status.idle; - - GList *iter = g_queue_peek_head_link(displayed); - while (iter) { - struct notification *n = iter->data; - - /* - * Update iter to the next item before we either exit the - * current iteration of the loop or potentially delete the - * notification which would invalidate the pointer. - */ - iter = iter->next; - - /* don't timeout when user is idle */ - if (is_idle && !n->transient) { - n->start = time_monotonic_now(); - continue; - } - - /* skip hidden and sticky messages */ - if (n->start == 0 || n->timeout == 0) { - continue; - } - - /* remove old message */ - if (time_monotonic_now() - n->start > n->timeout) { - queues_notification_close(n, REASON_TIME); - } - } -} - /* see queues.h */ void queues_update(struct dunst_status status) { - if (!status.running) { - while (displayed->length > 0) { - g_queue_insert_sorted( - waiting, g_queue_pop_head(displayed), notification_cmp_data, NULL); - } - return; - } + GList *iter, *nextiter; - /* move notifications back to queue, which are set to pushback */ - if (status.fullscreen) { - GList *iter = g_queue_peek_head_link(displayed); - while (iter) { - struct notification *n = iter->data; - GList *nextiter = iter->next; - - if (n->fullscreen == FS_PUSHBACK){ - g_queue_delete_link(displayed, iter); - g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); - } + /* Move back all notifications, which aren't eligible to get shown anymore + * Will move the notifications back to waiting, if dunst isn't running or fullscreen + * and notifications is not eligible to get shown anymore */ + iter = g_queue_peek_head_link(displayed); + while (iter) { + struct notification *n = iter->data; + nextiter = iter->next; + if (queues_notification_is_finished(n, status)){ + queues_notification_close(n, REASON_TIME); iter = nextiter; + continue; } + + if (!queues_notification_is_ready(n, status, true)) { + g_queue_delete_link(displayed, iter); + g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); + iter = nextiter; + continue; + } + + iter = nextiter; } int cur_displayed_limit; @@ -418,10 +416,10 @@ void queues_update(struct dunst_status status) cur_displayed_limit = settings.geometry.h; /* move notifications from queue to displayed */ - GList *iter = g_queue_peek_head_link(waiting); + iter = g_queue_peek_head_link(waiting); while (displayed->length < cur_displayed_limit && iter) { struct notification *n = iter->data; - GList *nextiter = iter->next; + nextiter = iter->next; if (!n) return; diff --git a/src/queues.h b/src/queues.h index a74e30b..92d5156 100644 --- a/src/queues.h +++ b/src/queues.h @@ -118,13 +118,6 @@ void queues_history_push(struct notification *n); */ void queues_history_push_all(void); -/** - * Check timeout of each notification and close it, if necessary - * - * @param status the current status of dunst - */ -void queues_check_timeouts(struct dunst_status status); - /** * Move inserted notifications from waiting queue to displayed queue * and show them. In displayed queue, the amount of elements is limited diff --git a/test/queues.c b/test/queues.c index 6e5bfb4..de69578 100644 --- a/test/queues.c +++ b/test/queues.c @@ -430,9 +430,6 @@ TEST test_queue_timeout(void) queues_init(); - // Usually this shouldn't do anything, but we may abort ;-) - queues_check_timeouts(STATUS_NORMAL); - n1 = test_notification("n1", 0); n2 = test_notification("n2", 10); n3 = test_notification("n3", 10); @@ -448,7 +445,6 @@ TEST test_queue_timeout(void) n1->start -= S2US(11); n2->start -= S2US(11); n3->start -= S2US(11); - queues_check_timeouts(STATUS_IDLE); queues_update(STATUS_IDLE); QUEUE_LEN_ALL(0,2,1); @@ -457,7 +453,6 @@ TEST test_queue_timeout(void) // hacky way to shift time n1->start -= S2US(11); n2->start -= S2US(11); - queues_check_timeouts(STATUS_NORMAL); queues_update(STATUS_NORMAL); QUEUE_LEN_ALL(0,1,2); @@ -665,6 +660,25 @@ TEST test_queues_update_seep_showlowurg(void) PASS(); } +TEST test_queues_timeout_before_paused(void) +{ + struct notification *n; + queues_init(); + + n = test_notification("n", 10); + + queues_notification_insert(n); + queues_update(STATUS_NORMAL); + + n->start -= S2US(11); + queues_update(STATUS_PAUSE); + + QUEUE_LEN_ALL(0,0,1); + + queues_teardown(); + PASS(); +} + SUITE(suite_queues) { RUN_TEST(test_datachange_beginning_empty); @@ -690,6 +704,7 @@ SUITE(suite_queues) RUN_TEST(test_queues_update_seep_showlowurg); RUN_TEST(test_queues_update_seeping); RUN_TEST(test_queues_update_xmore); + RUN_TEST(test_queues_timeout_before_paused); } /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From d96a29fed5367aeee1a9cf17f1dae848e7491943 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Mon, 19 Nov 2018 17:15:38 +0100 Subject: [PATCH 22/23] Move field definition on top of function (readability) --- src/dunst.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dunst.c b/src/dunst.c index 5dce077..8ba2d15 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -63,6 +63,8 @@ void wake_up(void) static gboolean run(void *data) { + static gint64 next_timeout = 0; + LOG_D("RUN"); dunst_status(S_FULLSCREEN, have_fullscreen_window()); @@ -70,8 +72,6 @@ static gboolean run(void *data) queues_update(status); - static gint64 next_timeout = 0; - bool active = queues_length_displayed() > 0; if (active) { From 93f6eb58a3857a0ef4980f7fcea44563a22c6060 Mon Sep 17 00:00:00 2001 From: Benedikt Heine Date: Tue, 20 Nov 2018 17:06:23 +0100 Subject: [PATCH 23/23] Remove n->start tests These aren't used anymore. Any notification which gets moved from waiting to displayed, will have set the start field with the current monotonic time. So testing start == 0 won't ever succeed in queues_notification_is_finished, as the tested notification is contained in the displayed queue. So the start field never will be 0. Also there's no semantics for start being 0 in dunst actually implemented. --- src/queues.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/queues.c b/src/queues.c index 09303cd..2d47172 100644 --- a/src/queues.c +++ b/src/queues.c @@ -136,8 +136,6 @@ static bool queues_notification_is_finished(struct notification *n, struct dunst if (n->timeout == 0) // sticky return false; - if (n->start == 0) // hidden // TODO: is this really the implication of hidden? - return false; bool is_idle = status.fullscreen ? false : status.idle; @@ -344,7 +342,6 @@ void queues_history_pop(void) struct notification *n = g_queue_pop_tail(history); n->redisplayed = true; - n->start = 0; n->timeout = settings.sticky_history ? 0 : n->timeout; g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); }