From b7efa1e3e338e61c161034e04503b5e344487e7f Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 7 Jul 2014 06:22:04 +0200 Subject: [PATCH 01/15] tests: threads: Use struct tinfo in all tests --- mu/tests/test-mu-threads.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index c8d75276..6779203f 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -34,6 +34,13 @@ #include "mu-query.h" #include "mu-str.h" +struct _tinfo { + const char *threadpath; + const char *msgid; + const char *subject; +}; +typedef struct _tinfo tinfo; + static gchar* fill_database (const char *testdir) { @@ -87,11 +94,7 @@ test_mu_threads_01 (void) MuMsgIter *iter; unsigned u; - struct { - const char* threadpath; - const char *msgid; - const char* subject; - } items [] = { + const tinfo items [] = { {"0", "root0@msg.id", "root0"}, {"0:0", "child0.0@msg.id", "Re: child 0.0"}, {"0:1", "child0.1@msg.id", "Re: child 0.1"}, @@ -149,14 +152,6 @@ test_mu_threads_01 (void) mu_msg_iter_destroy (iter); } - -struct _tinfo { - const char* threadpath; - const char *msgid; - const char* subject; -}; -typedef struct _tinfo tinfo; - static void test_mu_threads_rogue (void) { From 4c4b9af762a7c48ba2039f1a05a2127c6305549d Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 7 Jul 2014 06:22:31 +0200 Subject: [PATCH 02/15] tests: threads: Extract helpers for initializing and comparing thread info --- mu/tests/test-mu-threads.c | 74 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index 6779203f..809ed38f 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -41,6 +41,38 @@ struct _tinfo { }; typedef struct _tinfo tinfo; +static void +assert_tinfo_equal (const tinfo *expected, const tinfo *actual) +{ + g_assert_cmpstr (expected->threadpath,==,actual->threadpath); + g_assert_cmpstr (expected->subject,==,actual->subject); + g_assert_cmpstr (expected->msgid,==,actual->msgid); +} + +static void +tinfo_init_from_iter (tinfo *item, MuMsgIter *iter) +{ + MuMsg *msg; + const MuMsgIterThreadInfo *ti; + + msg = mu_msg_iter_get_msg_floating (iter); + g_assert (msg); + + ti = mu_msg_iter_get_thread_info (iter); + if (!ti) + g_print ("%s: thread info not found\n", mu_msg_get_msgid (msg)); + g_assert (ti); + + item->threadpath = ti->threadpath; + item->subject = mu_msg_get_subject (msg); + item->msgid = mu_msg_get_msgid (msg); + + if (g_test_verbose()) + g_print ("%s %s %s\n", + item->threadpath, item->subject, item->msgid); + +} + static gchar* fill_database (const char *testdir) { @@ -120,28 +152,12 @@ test_mu_threads_01 (void) u = 0; while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items)) { - MuMsg *msg; - const MuMsgIterThreadInfo *ti; + tinfo ti; - ti = mu_msg_iter_get_thread_info (iter); - if (!ti) - g_print ("%s: thread info not found for %u\n", - __FUNCTION__, (unsigned)mu_msg_iter_get_docid(iter)); - g_assert(ti); - - msg = mu_msg_iter_get_msg_floating (iter); - g_assert (msg); - - if (g_test_verbose()) - g_print ("%s %s %s\n", ti->threadpath, - mu_msg_get_msgid(msg), - mu_msg_get_path (msg)); + tinfo_init_from_iter (&ti, iter); g_assert (u < G_N_ELEMENTS(items)); - - g_assert_cmpstr (ti->threadpath,==,items[u].threadpath); - g_assert_cmpstr (mu_msg_get_subject(msg),==,items[u].subject); - g_assert_cmpstr (mu_msg_get_msgid(msg),==,items[u].msgid); + assert_tinfo_equal (&items[u], &ti); ++u; mu_msg_iter_next (iter); @@ -191,26 +207,12 @@ test_mu_threads_rogue (void) u = 0; while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items1)) { - MuMsg *msg; - const MuMsgIterThreadInfo *ti; + tinfo ti; - ti = mu_msg_iter_get_thread_info (iter); - if (!ti) - g_print ("%s: thread info not found\n", - mu_msg_get_msgid(mu_msg_iter_get_msg_floating (iter))); - g_assert(ti); - - msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */ - /* g_print ("%s %s %s\n", ti->threadpath, */ - /* mu_msg_get_msgid(msg), */ - /* mu_msg_get_path (msg) */ - /* ); */ + tinfo_init_from_iter (&ti, iter); g_assert (u < G_N_ELEMENTS(items1)); - - g_assert_cmpstr (ti->threadpath,==,(items)[u].threadpath); - g_assert_cmpstr (mu_msg_get_subject(msg),==,(items)[u].subject); - g_assert_cmpstr (mu_msg_get_msgid(msg),==,(items)[u].msgid); + assert_tinfo_equal (&items[u], &ti); ++u; mu_msg_iter_next (iter); From 80a484dc3f2ffd917beccad6f98fce53acf0c3ee Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:19:25 +0200 Subject: [PATCH 03/15] tests: threads: Extract helper for checking thread info for each message --- mu/tests/test-mu-threads.c | 51 ++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index 809ed38f..d56d56b7 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -73,6 +73,26 @@ tinfo_init_from_iter (tinfo *item, MuMsgIter *iter) } +static void +foreach_assert_tinfo_equal (MuMsgIter *iter, const tinfo items[], guint n_items) +{ + guint u; + + u = 0; + while (!mu_msg_iter_is_done (iter) && u < n_items) { + tinfo ti; + + tinfo_init_from_iter (&ti, iter); + + g_assert (u < n_items); + assert_tinfo_equal (&items[u], &ti); + + ++u; + mu_msg_iter_next (iter); + } + g_assert (u == n_items); +} + static gchar* fill_database (const char *testdir) { @@ -118,13 +138,11 @@ run_and_get_iter (const char *xpath, const char *query) return iter; } - static void test_mu_threads_01 (void) { gchar *xpath; MuMsgIter *iter; - unsigned u; const tinfo items [] = { {"0", "root0@msg.id", "root0"}, @@ -150,19 +168,7 @@ test_mu_threads_01 (void) g_assert (iter); g_assert (!mu_msg_iter_is_done(iter)); - u = 0; - while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items)) { - tinfo ti; - - tinfo_init_from_iter (&ti, iter); - - g_assert (u < G_N_ELEMENTS(items)); - assert_tinfo_equal (&items[u], &ti); - - ++u; - mu_msg_iter_next (iter); - } - g_assert (u == G_N_ELEMENTS(items)); + foreach_assert_tinfo_equal (iter, items, G_N_ELEMENTS (items)); g_free (xpath); mu_msg_iter_destroy (iter); @@ -173,7 +179,6 @@ test_mu_threads_rogue (void) { gchar *xpath; MuMsgIter *iter; - unsigned u; tinfo *items; tinfo items1 [] = { @@ -205,19 +210,7 @@ test_mu_threads_rogue (void) else items = items2; - u = 0; - while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items1)) { - tinfo ti; - - tinfo_init_from_iter (&ti, iter); - - g_assert (u < G_N_ELEMENTS(items1)); - assert_tinfo_equal (&items[u], &ti); - - ++u; - mu_msg_iter_next (iter); - } - g_assert (u == G_N_ELEMENTS(items1)); + foreach_assert_tinfo_equal (iter, items, G_N_ELEMENTS (items1)); g_free (xpath); mu_msg_iter_destroy (iter); From 22927a7dcff87ce8f7e4017a752cd208eed91f46 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:22:11 +0200 Subject: [PATCH 04/15] tests: threads: Test if 1st child message promotes its thread --- .../sort/1st-child-promotes-thread/cur/A | 7 ++ .../sort/1st-child-promotes-thread/cur/B | 7 ++ .../sort/1st-child-promotes-thread/cur/C | 7 ++ .../sort/1st-child-promotes-thread/cur/D | 9 ++ .../1st-child-promotes-thread/new/.noindex | 0 .../1st-child-promotes-thread/tmp/.noindex | 0 mu/tests/test-mu-threads.c | 86 ++++++++++++++++++- 7 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 lib/tests/testdir3/sort/1st-child-promotes-thread/cur/A create mode 100644 lib/tests/testdir3/sort/1st-child-promotes-thread/cur/B create mode 100644 lib/tests/testdir3/sort/1st-child-promotes-thread/cur/C create mode 100644 lib/tests/testdir3/sort/1st-child-promotes-thread/cur/D create mode 100644 lib/tests/testdir3/sort/1st-child-promotes-thread/new/.noindex create mode 100644 lib/tests/testdir3/sort/1st-child-promotes-thread/tmp/.noindex diff --git a/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/A b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/A new file mode 100644 index 00000000..85130e80 --- /dev/null +++ b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/A @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: A +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +A diff --git a/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/B b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/B new file mode 100644 index 00000000..254aeb72 --- /dev/null +++ b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/B @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: B +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +B diff --git a/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/C b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/C new file mode 100644 index 00000000..60b7db56 --- /dev/null +++ b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/C @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: C +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +C diff --git a/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/D b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/D new file mode 100644 index 00000000..1e4861df --- /dev/null +++ b/lib/tests/testdir3/sort/1st-child-promotes-thread/cur/D @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: D +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +D diff --git a/lib/tests/testdir3/sort/1st-child-promotes-thread/new/.noindex b/lib/tests/testdir3/sort/1st-child-promotes-thread/new/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/lib/tests/testdir3/sort/1st-child-promotes-thread/tmp/.noindex b/lib/tests/testdir3/sort/1st-child-promotes-thread/tmp/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index d56d56b7..8bfd3fe7 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -117,7 +117,8 @@ fill_database (const char *testdir) /* note: this also *moves the iter* */ static MuMsgIter* -run_and_get_iter (const char *xpath, const char *query) +run_and_get_iter_full (const char *xpath, const char *query, + MuMsgFieldId sort_field, MuQueryFlags flags) { MuQuery *mquery; MuStore *store; @@ -130,14 +131,21 @@ run_and_get_iter (const char *xpath, const char *query) mu_store_unref (store); g_assert (query); - iter = mu_query_run (mquery, query, MU_MSG_FIELD_ID_DATE, - -1, MU_QUERY_FLAG_THREADS, NULL); + flags |= MU_QUERY_FLAG_THREADS; + iter = mu_query_run (mquery, query, sort_field, -1, flags, NULL); mu_query_destroy (mquery); g_assert (iter); return iter; } +static MuMsgIter* +run_and_get_iter (const char *xpath, const char *query) +{ + return run_and_get_iter_full (xpath, query, MU_MSG_FIELD_ID_DATE, + MU_QUERY_FLAG_NONE); +} + static void test_mu_threads_01 (void) { @@ -216,6 +224,76 @@ test_mu_threads_rogue (void) mu_msg_iter_destroy (iter); } +static MuMsgIter* +query_testdir (const char *query, MuMsgFieldId sort_field, gboolean descending) +{ + MuMsgIter *iter; + gchar *xpath; + MuQueryFlags flags; + + flags = MU_QUERY_FLAG_NONE; + if (descending) + flags |= MU_QUERY_FLAG_DESCENDING; + + xpath = fill_database (MU_TESTMAILDIR3); + g_assert (xpath != NULL); + + iter = run_and_get_iter_full (xpath, query, sort_field, flags); + g_assert (iter != NULL); + g_assert (!mu_msg_iter_is_done (iter)); + + g_free (xpath); + return iter; +} + +static void +check_sort_by_subject (const char *query, const tinfo expected[], + guint n_expected, gboolean descending) +{ + MuMsgIter *iter; + + iter = query_testdir (query, MU_MSG_FIELD_ID_SUBJECT, descending); + foreach_assert_tinfo_equal (iter, expected, n_expected); + mu_msg_iter_destroy (iter); +} + +static void +check_sort_by_subject_asc (const char *query, const tinfo expected[], + guint n_expected) +{ + check_sort_by_subject (query, expected, n_expected, FALSE); +} + +static void +check_sort_by_subject_desc (const char *query, const tinfo expected[], + guint n_expected) +{ + check_sort_by_subject (query, expected, n_expected, TRUE); +} + +static void +test_mu_threads_sort_1st_child_promotes_thread (void) +{ + const char *query = "maildir:/sort/1st-child-promotes-thread"; + + const tinfo expected_asc [] = { + { "0", "A@msg.id", "A"}, + { "1", "C@msg.id", "C"}, + { "2", "B@msg.id", "B"}, + { "2:0", "D@msg.id", "D"}, + }; + const tinfo expected_desc [] = { + { "0", "B@msg.id", "B"}, + { "0:0", "D@msg.id", "D"}, + { "1", "C@msg.id", "C"}, + { "2", "A@msg.id", "A"}, + }; + + check_sort_by_subject_asc (query, expected_asc, + G_N_ELEMENTS (expected_asc)); + check_sort_by_subject_desc (query, expected_desc, + G_N_ELEMENTS (expected_desc)); +} int @@ -227,6 +305,8 @@ main (int argc, char *argv[]) g_test_add_func ("/mu-query/test-mu-threads-01", test_mu_threads_01); g_test_add_func ("/mu-query/test-mu-threads-rogue", test_mu_threads_rogue); + g_test_add_func ("/mu-query/test-mu-threads-sort-1st-child-promotes-thread", + test_mu_threads_sort_1st_child_promotes_thread); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From c5d4f7f33899d10cb24ad8f1d880778e749d276e Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:22:53 +0200 Subject: [PATCH 05/15] tests: threads: Test if 2nd child message promotes its subthread --- .../sort/2nd-child-promotes-thread/cur/A | 7 +++++ .../sort/2nd-child-promotes-thread/cur/B | 7 +++++ .../sort/2nd-child-promotes-thread/cur/C | 9 +++++++ .../sort/2nd-child-promotes-thread/cur/D | 7 +++++ .../sort/2nd-child-promotes-thread/cur/E | 9 +++++++ .../2nd-child-promotes-thread/new/.noindex | 0 .../2nd-child-promotes-thread/tmp/.noindex | 0 mu/tests/test-mu-threads.c | 27 +++++++++++++++++++ 8 files changed, 66 insertions(+) create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/A create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/B create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/C create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/D create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/E create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/new/.noindex create mode 100644 lib/tests/testdir3/sort/2nd-child-promotes-thread/tmp/.noindex diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/A b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/A new file mode 100644 index 00000000..85130e80 --- /dev/null +++ b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/A @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: A +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +A diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/B b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/B new file mode 100644 index 00000000..254aeb72 --- /dev/null +++ b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/B @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: B +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +B diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/C b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/C new file mode 100644 index 00000000..6d1e19af --- /dev/null +++ b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/C @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: C +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +C diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/D b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/D new file mode 100644 index 00000000..de61bc13 --- /dev/null +++ b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/D @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: D +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +D diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/E b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/E new file mode 100644 index 00000000..bb7f5f39 --- /dev/null +++ b/lib/tests/testdir3/sort/2nd-child-promotes-thread/cur/E @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: E +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +E diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/new/.noindex b/lib/tests/testdir3/sort/2nd-child-promotes-thread/new/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/lib/tests/testdir3/sort/2nd-child-promotes-thread/tmp/.noindex b/lib/tests/testdir3/sort/2nd-child-promotes-thread/tmp/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index 8bfd3fe7..a799b392 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -295,6 +295,31 @@ test_mu_threads_sort_1st_child_promotes_thread (void) G_N_ELEMENTS (expected_desc)); } +static void +test_mu_threads_sort_2nd_child_promotes_thread (void) +{ + const char *query = "maildir:/sort/2nd-child-promotes-thread"; + + const tinfo expected_asc [] = { + { "0", "A@msg.id", "A"}, + { "1", "D@msg.id", "D"}, + { "2", "B@msg.id", "B"}, + { "2:0", "C@msg.id", "C"}, + { "2:1", "E@msg.id", "E"}, + }; + const tinfo expected_desc [] = { + { "0", "B@msg.id", "B"}, + { "0:0", "E@msg.id", "E"}, + { "0:1", "C@msg.id", "C"}, + { "1", "D@msg.id", "D"}, + { "2", "A@msg.id", "A"}, + }; + + check_sort_by_subject_asc (query, expected_asc, + G_N_ELEMENTS (expected_asc)); + check_sort_by_subject_desc (query, expected_desc, + G_N_ELEMENTS (expected_desc)); +} int main (int argc, char *argv[]) @@ -307,6 +332,8 @@ main (int argc, char *argv[]) g_test_add_func ("/mu-query/test-mu-threads-rogue", test_mu_threads_rogue); g_test_add_func ("/mu-query/test-mu-threads-sort-1st-child-promotes-thread", test_mu_threads_sort_1st_child_promotes_thread); + g_test_add_func ("/mu-query/test-mu-threads-sort-2nd-child-promotes-thread", + test_mu_threads_sort_2nd_child_promotes_thread); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From f49296759ef8749e7a286fce08d948eba2f1624a Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:23:27 +0200 Subject: [PATCH 06/15] tests: threads: Test if orphan message promotes its thread --- mu/tests/test-mu-threads.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index a799b392..f059e9a5 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -321,6 +321,31 @@ test_mu_threads_sort_2nd_child_promotes_thread (void) G_N_ELEMENTS (expected_desc)); } +static void +test_mu_threads_sort_orphan_promotes_thread (void) +{ + const char *query = "maildir:/sort/2nd-child-promotes-thread NOT B"; + + /* B lost, C & E orphaned but not promoted */ + const tinfo expected_asc [] = { + { "0", "A@msg.id", "A"}, + { "1", "D@msg.id", "D"}, + { "2:0", "C@msg.id", "C"}, + { "2:1", "E@msg.id", "E"}, + }; + const tinfo expected_desc [] = { + { "0:0", "E@msg.id", "E"}, + { "0:1", "C@msg.id", "C"}, + { "1", "D@msg.id", "D"}, + { "2", "A@msg.id", "A"}, + }; + + check_sort_by_subject_asc (query, expected_asc, + G_N_ELEMENTS (expected_asc)); + check_sort_by_subject_desc (query, expected_desc, + G_N_ELEMENTS (expected_desc)); +} + int main (int argc, char *argv[]) { @@ -334,6 +359,8 @@ main (int argc, char *argv[]) test_mu_threads_sort_1st_child_promotes_thread); g_test_add_func ("/mu-query/test-mu-threads-sort-2nd-child-promotes-thread", test_mu_threads_sort_2nd_child_promotes_thread); + g_test_add_func ("/mu-query/test-mu-threads-orphan-promotes-thread", + test_mu_threads_sort_orphan_promotes_thread); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From b0357a2d7a1dd01d371b24dc6e1acc14828e1597 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:23:53 +0200 Subject: [PATCH 07/15] tests: threads: Test if child message doesn't promote its thread --- .../sort/child-does-not-promote-thread/cur/A | 9 +++++++ .../sort/child-does-not-promote-thread/cur/X | 7 +++++ .../sort/child-does-not-promote-thread/cur/Y | 7 +++++ .../sort/child-does-not-promote-thread/cur/Z | 7 +++++ .../new/.noindex | 0 .../tmp/.noindex | 0 mu/tests/test-mu-threads.c | 27 +++++++++++++++++++ 7 files changed, 57 insertions(+) create mode 100644 lib/tests/testdir3/sort/child-does-not-promote-thread/cur/A create mode 100644 lib/tests/testdir3/sort/child-does-not-promote-thread/cur/X create mode 100644 lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Y create mode 100644 lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Z create mode 100644 lib/tests/testdir3/sort/child-does-not-promote-thread/new/.noindex create mode 100644 lib/tests/testdir3/sort/child-does-not-promote-thread/tmp/.noindex diff --git a/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/A b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/A new file mode 100644 index 00000000..c59c42fd --- /dev/null +++ b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/A @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: A +Message-Id: +References: +In-reply-to: +Aate: Sat, 17 May 2014 10:00:00 +0000 + +A diff --git a/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/X b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/X new file mode 100644 index 00000000..c8ac3aa2 --- /dev/null +++ b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/X @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: X +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +X diff --git a/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Y b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Y new file mode 100644 index 00000000..ceadc203 --- /dev/null +++ b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Y @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: Y +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +Y diff --git a/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Z b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Z new file mode 100644 index 00000000..365775b8 --- /dev/null +++ b/lib/tests/testdir3/sort/child-does-not-promote-thread/cur/Z @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: Z +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +Z diff --git a/lib/tests/testdir3/sort/child-does-not-promote-thread/new/.noindex b/lib/tests/testdir3/sort/child-does-not-promote-thread/new/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/lib/tests/testdir3/sort/child-does-not-promote-thread/tmp/.noindex b/lib/tests/testdir3/sort/child-does-not-promote-thread/tmp/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index f059e9a5..edfa1602 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -346,6 +346,31 @@ test_mu_threads_sort_orphan_promotes_thread (void) G_N_ELEMENTS (expected_desc)); } +/* Won't normally happen when sorting by date. */ +static void +test_mu_threads_sort_child_does_not_promote_thread (void) +{ + const char *query = "maildir:/sort/child-does-not-promote-thread"; + + const tinfo expected_asc [] = { + { "0", "X@msg.id", "X"}, + { "1", "Y@msg.id", "Y"}, + { "1:0", "A@msg.id", "A"}, + { "2", "Z@msg.id", "Z"}, + }; + const tinfo expected_desc [] = { + { "0", "Z@msg.id", "Z"}, + { "1", "Y@msg.id", "Y"}, + { "1:0", "A@msg.id", "A"}, + { "2", "X@msg.id", "X"}, + }; + + check_sort_by_subject_asc (query, expected_asc, + G_N_ELEMENTS (expected_asc)); + check_sort_by_subject_desc (query, expected_desc, + G_N_ELEMENTS (expected_desc)); +} + int main (int argc, char *argv[]) { @@ -361,6 +386,8 @@ main (int argc, char *argv[]) test_mu_threads_sort_2nd_child_promotes_thread); g_test_add_func ("/mu-query/test-mu-threads-orphan-promotes-thread", test_mu_threads_sort_orphan_promotes_thread); + g_test_add_func ("/mu-query/test-mu-threads-sort-child-does-not-promote-thread", + test_mu_threads_sort_child_does_not_promote_thread); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From cbfe28b8852da65dc1b8db17962cd3816c403606 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:24:21 +0200 Subject: [PATCH 08/15] tests: threads: Test if grandchild message promotes its thread --- .../sort/grandchild-promotes-thread/cur/A | 7 +++++ .../sort/grandchild-promotes-thread/cur/B | 7 +++++ .../sort/grandchild-promotes-thread/cur/C | 9 ++++++ .../sort/grandchild-promotes-thread/cur/D | 7 +++++ .../sort/grandchild-promotes-thread/cur/E | 9 ++++++ .../grandchild-promotes-thread/new/.noindex | 0 .../grandchild-promotes-thread/tmp/.noindex | 0 mu/tests/test-mu-threads.c | 28 +++++++++++++++++++ 8 files changed, 67 insertions(+) create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/cur/A create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/cur/B create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/cur/C create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/cur/D create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/cur/E create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/new/.noindex create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-thread/tmp/.noindex diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/A b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/A new file mode 100644 index 00000000..85130e80 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/A @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: A +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +A diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/B b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/B new file mode 100644 index 00000000..254aeb72 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/B @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: B +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +B diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/C b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/C new file mode 100644 index 00000000..6d1e19af --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/C @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: C +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +C diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/D b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/D new file mode 100644 index 00000000..de61bc13 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/D @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: D +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +D diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/E b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/E new file mode 100644 index 00000000..d605b4b8 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-thread/cur/E @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: E +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +E diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/new/.noindex b/lib/tests/testdir3/sort/grandchild-promotes-thread/new/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/lib/tests/testdir3/sort/grandchild-promotes-thread/tmp/.noindex b/lib/tests/testdir3/sort/grandchild-promotes-thread/tmp/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index edfa1602..4e89d861 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -371,6 +371,32 @@ test_mu_threads_sort_child_does_not_promote_thread (void) G_N_ELEMENTS (expected_desc)); } +static void +test_mu_threads_sort_grandchild_promotes_thread (void) +{ + const char *query = "maildir:/sort/grandchild-promotes-thread"; + + const tinfo expected_asc [] = { + { "0", "A@msg.id", "A"}, + { "1", "D@msg.id", "D"}, + { "2", "B@msg.id", "B"}, + { "2:0", "C@msg.id", "C"}, + { "2:0:0", "E@msg.id", "E"}, + }; + const tinfo expected_desc [] = { + { "0", "B@msg.id", "B"}, + { "0:0", "C@msg.id", "C"}, + { "0:0:0", "E@msg.id", "E"}, + { "1", "D@msg.id", "D"}, + { "2", "A@msg.id", "A"}, + }; + + check_sort_by_subject_asc (query, expected_asc, + G_N_ELEMENTS (expected_asc)); + check_sort_by_subject_desc (query, expected_desc, + G_N_ELEMENTS (expected_desc)); +} + int main (int argc, char *argv[]) { @@ -388,6 +414,8 @@ main (int argc, char *argv[]) test_mu_threads_sort_orphan_promotes_thread); g_test_add_func ("/mu-query/test-mu-threads-sort-child-does-not-promote-thread", test_mu_threads_sort_child_does_not_promote_thread); + g_test_add_func ("/mu-query/test-mu-threads-sort-grandchild-promotes-thread", + test_mu_threads_sort_grandchild_promotes_thread); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From 856a651d38a520b614d6147f01afccb0cc84fa23 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 07:24:47 +0200 Subject: [PATCH 09/15] tests: threads: Test if grandchild message promotes only its subthread --- .../grandchild-promotes-only-subthread/cur/A | 7 +++++ .../grandchild-promotes-only-subthread/cur/B | 7 +++++ .../grandchild-promotes-only-subthread/cur/C | 9 ++++++ .../grandchild-promotes-only-subthread/cur/D | 9 ++++++ .../grandchild-promotes-only-subthread/cur/E | 9 ++++++ .../grandchild-promotes-only-subthread/cur/F | 9 ++++++ .../grandchild-promotes-only-subthread/cur/G | 7 +++++ .../new/.noindex | 0 .../tmp/.noindex | 0 mu/tests/test-mu-threads.c | 31 +++++++++++++++++++ 10 files changed, 88 insertions(+) create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/A create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/B create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/C create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/D create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/E create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/F create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/G create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/new/.noindex create mode 100644 lib/tests/testdir3/sort/grandchild-promotes-only-subthread/tmp/.noindex diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/A b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/A new file mode 100644 index 00000000..85130e80 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/A @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: A +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +A diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/B b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/B new file mode 100644 index 00000000..254aeb72 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/B @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: B +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +B diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/C b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/C new file mode 100644 index 00000000..6d1e19af --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/C @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: C +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +C diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/D b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/D new file mode 100644 index 00000000..1e4861df --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/D @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: D +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +D diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/E b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/E new file mode 100644 index 00000000..bb7f5f39 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/E @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: E +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +E diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/F b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/F new file mode 100644 index 00000000..7c4275d7 --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/F @@ -0,0 +1,9 @@ +From: testfrom@example.com +To: testto@example.com +Subject: F +Message-Id: +References: +In-reply-to: +Date: Sat, 17 May 2014 10:00:00 +0000 + +F diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/G b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/G new file mode 100644 index 00000000..4849455f --- /dev/null +++ b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/cur/G @@ -0,0 +1,7 @@ +From: testfrom@example.com +To: testto@example.com +Subject: G +Message-Id: +Date: Sat, 17 May 2014 10:00:00 +0000 + +G diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/new/.noindex b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/new/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/tmp/.noindex b/lib/tests/testdir3/sort/grandchild-promotes-only-subthread/tmp/.noindex new file mode 100644 index 00000000..e69de29b diff --git a/mu/tests/test-mu-threads.c b/mu/tests/test-mu-threads.c index 4e89d861..e11046fd 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -397,6 +397,35 @@ test_mu_threads_sort_grandchild_promotes_thread (void) G_N_ELEMENTS (expected_desc)); } +static void +test_mu_threads_sort_granchild_promotes_only_subthread (void) +{ + const char *query = "maildir:/sort/grandchild-promotes-only-subthread"; + + const tinfo expected_asc [] = { + { "0", "A@msg.id", "A"}, + { "1", "B@msg.id", "B"}, + { "1:0", "C@msg.id", "C"}, + { "1:1", "E@msg.id", "E"}, + { "1:2", "D@msg.id", "D"}, + { "1:2:0", "F@msg.id", "F"}, + { "2", "G@msg.id", "G"}, + }; + const tinfo expected_desc [] = { + { "0", "G@msg.id", "G"}, + { "1", "B@msg.id", "B"}, + { "1:0", "D@msg.id", "D"}, + { "1:0:0", "F@msg.id", "F"}, + { "1:1", "E@msg.id", "E"}, + { "1:2", "C@msg.id", "C"}, + { "2", "A@msg.id", "A"}, + }; + + check_sort_by_subject_asc (query, expected_asc, + G_N_ELEMENTS (expected_asc)); + check_sort_by_subject_desc (query, expected_desc, + G_N_ELEMENTS (expected_desc)); +} int main (int argc, char *argv[]) { @@ -416,6 +445,8 @@ main (int argc, char *argv[]) test_mu_threads_sort_child_does_not_promote_thread); g_test_add_func ("/mu-query/test-mu-threads-sort-grandchild-promotes-thread", test_mu_threads_sort_grandchild_promotes_thread); + g_test_add_func ("/mu-query/test-mu-threads-sort-grandchild-promotes-only-subthread", + test_mu_threads_sort_granchild_promotes_only_subthread); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From d93b8135a6df276fefd9c07ddab14aa12a53515b Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 7 Jul 2014 06:22:57 +0200 Subject: [PATCH 10/15] Revert "* bugfix for issue 164" This reverts commit c7b28419abd9d53ff36412e4fedaf308ce13c552. The reverted change fails to sort threads correctly when there is an empty container, serving as a parent to orphan messages, in the thread tree as demonstrated by the test in commit f49296759ef8 ("tests: threads: Test if orphan message promotes its thread"). Also, the reverted commit introduces a performance hit. The time it takes to sort threads has increased roughly by a factor of 4. Current state: $ perf stat --event=task-clock --repeat=10 -- \ mu find maildir:/INBOX -n 1 -t > /dev/null Performance counter stats for 'mu find maildir:/INBOX -n 1 -t' (10 runs): 4967.692519 task-clock (msec) # 1.000 CPUs utilized ( +- 0.14% ) 4.969247128 seconds time elapsed ( +- 0.14% ) With the reverted patch applied: $ perf stat --event=task-clock --repeat=10 -- \ mu find maildir:/INBOX -n 1 -t > /dev/null Performance counter stats for 'mu find maildir:/INBOX -n 1 -t' (10 runs): 1231.761588 task-clock (msec) # 0.996 CPUs utilized ( +- 1.02% ) 1.236209133 seconds time elapsed ( +- 1.08% ) The benchmark was ran on a maildir with ~16k messages: $ mu find maildir:/INBOX | wc -l 16503 --- lib/mu-container.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/mu-container.c b/lib/mu-container.c index ec7b3795..34db96a5 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -317,22 +317,6 @@ struct _SortFuncData { }; typedef struct _SortFuncData SortFuncData; -static MuContainer* -get_top_msg (MuContainer *c, MuMsgFieldId mfid) -{ - MuContainer *piv, *extreme = c; - for (piv = c; piv != NULL && piv->msg != NULL; piv = piv->child) { - if (mu_msg_cmp (piv->msg, extreme->msg, mfid) > 0) - extreme = piv; - if (piv != c && piv->next) { - MuContainer *sub = get_top_msg (piv->next, mfid); - - if (sub->msg != NULL && mu_msg_cmp (sub->msg, extreme->msg, mfid) > 0) - extreme = sub; - } - } - return extreme; -} static int sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) @@ -344,9 +328,6 @@ sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) for (a1 = a; a1->msg == NULL && a1->child != NULL; a1 = a1->child); for (b1 = b; b1->msg == NULL && b1->child != NULL; b1 = b1->child); - a1 = get_top_msg (a1, data->mfid); - b1 = get_top_msg (b1, data->mfid); - if (a1 == b1) return 0; else if (!a1->msg) From dc3be515afc528ac4f5f419fae2a582d483e84ae Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 15 Jul 2014 06:33:00 +0200 Subject: [PATCH 11/15] mu: Extract function for comparing two containers --- lib/mu-container.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/mu-container.c b/lib/mu-container.c index 34db96a5..fbac6bb2 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -317,6 +317,18 @@ struct _SortFuncData { }; typedef struct _SortFuncData SortFuncData; +static int +container_cmp (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) +{ + if (a == b) + return 0; + else if (!a->msg) + return 1; + else if (!b->msg) + return -1; + + return mu_msg_cmp (a->msg, b->msg, mfid); +} static int sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) @@ -328,17 +340,10 @@ sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) for (a1 = a; a1->msg == NULL && a1->child != NULL; a1 = a1->child); for (b1 = b; b1->msg == NULL && b1->child != NULL; b1 = b1->child); - if (a1 == b1) - return 0; - else if (!a1->msg) - return 1; - else if (!b1->msg) - return -1; - if (data->descending) - return mu_msg_cmp (b1->msg, a1->msg, data->mfid); + return container_cmp (b1, a1, data->mfid); else - return mu_msg_cmp (a1->msg, b1->msg, data->mfid); + return container_cmp (a1, b1, data->mfid); } static MuContainer* From f724f4a57d242171b68f327c572dbd5d61d70d58 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 1 Jul 2014 07:22:57 +0200 Subject: [PATCH 12/15] mu: Consider an empty container to be less than anything else Reasoning being that, arguably, it is the least surprising thing to do. --- lib/mu-container.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mu-container.c b/lib/mu-container.c index fbac6bb2..7011fec5 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -323,9 +323,9 @@ container_cmp (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) if (a == b) return 0; else if (!a->msg) - return 1; - else if (!b->msg) return -1; + else if (!b->msg) + return 1; return mu_msg_cmp (a->msg, b->msg, mfid); } From 97101f1f82ba55015484c124d2f2990c0773b75c Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 7 Jul 2014 06:23:22 +0200 Subject: [PATCH 13/15] mu: Prune empty container when an only child gets promoted to the root set --- lib/mu-container.c | 17 ++++++++++++++++- lib/mu-container.h | 17 +++++++++++++++-- lib/mu-threader.c | 13 +++---------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/mu-container.c b/lib/mu-container.c index 7011fec5..a201c30c 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -263,9 +263,24 @@ mu_container_foreach (MuContainer *c, MuContainerForeachFunc func, return func (c, user_data); } +MuContainer* +mu_container_splice_children (MuContainer *c, MuContainer *sibling) +{ + MuContainer *children; + + g_return_val_if_fail (c, NULL); + g_return_val_if_fail (sibling, NULL); + + children = sibling->child; + sibling->child = NULL; + + c = mu_container_remove_sibling (c, sibling); + + return mu_container_append_siblings (c, children); +} MuContainer* -mu_container_splice_children (MuContainer *parent, MuContainer *child) +mu_container_splice_grandchildren (MuContainer *parent, MuContainer *child) { MuContainer *newchild; diff --git a/lib/mu-container.h b/lib/mu-container.h index ec9d7f72..c80f0273 100644 --- a/lib/mu-container.h +++ b/lib/mu-container.h @@ -122,6 +122,19 @@ MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child); */ MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling); +/** + * promote sibling's children to be this container's siblings and + * remove the sibling + * + * @param c a container instance + * @param sibling a sibling of this container + * + * @return the container with the sibling's children promoted and the + * sibling itself removed + */ + +MuContainer* mu_container_splice_children (MuContainer *c, + MuContainer *sibling); /** * promote child's children to be parent's children and remove child @@ -131,8 +144,8 @@ MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling); * * @return the new container with it's children's children promoted */ -MuContainer* mu_container_splice_children (MuContainer *parent, - MuContainer *child); +MuContainer* mu_container_splice_grandchildren (MuContainer *parent, + MuContainer *child); typedef gboolean (*MuContainerForeachFunc) (MuContainer*, gpointer); diff --git a/lib/mu-threader.c b/lib/mu-threader.c index 719508d2..c42e166c 100644 --- a/lib/mu-threader.c +++ b/lib/mu-threader.c @@ -388,7 +388,7 @@ prune_maybe (MuContainer *c) if (cur->flags & MU_CONTAINER_FLAG_DELETE) c = mu_container_remove_child (c, cur); else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) - c = mu_container_splice_children (c, cur); + c = mu_container_splice_grandchildren (c, cur); } g_return_val_if_fail (c, FALSE); @@ -433,17 +433,10 @@ prune_empty_containers (MuContainer *root_set) /* and prune the root_set itself... */ for (cur = root_set; cur; cur = cur->next) { - if (cur->flags & MU_CONTAINER_FLAG_DELETE) root_set = mu_container_remove_sibling (root_set, cur); - - else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) { - MuContainer *newchild; - newchild = cur->child; - cur->child = NULL; - root_set = mu_container_append_siblings (root_set, - newchild); - } + else if (cur->flags & MU_CONTAINER_FLAG_SPLICE) + root_set = mu_container_splice_children (root_set, cur); } return root_set; From 619fb868853b2353720785cee4596c3a6191d202 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 7 Jul 2014 06:24:01 +0200 Subject: [PATCH 14/15] mu: Update container's pointer to last sibling when converting from a list --- lib/mu-container.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/mu-container.c b/lib/mu-container.c index a201c30c..1dd83829 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -308,17 +308,28 @@ mu_container_to_list (MuContainer *c) return lst; } +static gpointer +list_last_data (GSList *lst) +{ + GSList *tail; + + tail = g_slist_last (lst); + + return tail->data; +} static MuContainer* mu_container_from_list (GSList *lst) { - MuContainer *c, *cur; + MuContainer *c, *cur, *tail; if (!lst) return NULL; + tail = list_last_data (lst); for (c = cur = (MuContainer*)lst->data; cur; lst = g_slist_next(lst)) { cur->next = lst ? (MuContainer*)lst->data : NULL; + cur->last = tail; cur=cur->next; } From 32f5c8b1f6de16bd183a38ff37452956e28cde47 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 7 Jul 2014 06:23:11 +0200 Subject: [PATCH 15/15] mu: Sort containers by comparing their subtree leaders Traverse the container tree depth first and for each container find the node in the subtree rooted at this container which comes first in the descending sort order. Remember it as the subtree leader. Then, while sorting siblings, compare their subtree leaders instead of the sibling containers themselves. IOW, make threads containing the newest message float to the top when sorting by date in the descending order. There is no significant performance degradation when sorting a mailbox with ~16k messages: $ mu find maildir:/INBOX | wc -l 16503 Current state: $ perf stat --event=task-clock --repeat=10 -- \ mu find maildir:/INBOX -n 1 -t > /dev/null Performance counter stats for 'mu find maildir:/INBOX -n 1 -t' (10 runs): 1231.761588 task-clock (msec) # 0.996 CPUs utilized ( +- 1.02% ) 1.236209133 seconds time elapsed ( +- 1.08% ) With patch applied: $ perf stat --event=task-clock --repeat=10 -- \ mu find maildir:/INBOX -n 1 -t > /dev/null Performance counter stats for 'mu find maildir:/INBOX -n 1 -t' (10 runs): 1459.883316 task-clock (msec) # 0.998 CPUs utilized ( +- 0.72% ) 1.462540088 seconds time elapsed ( +- 0.77% ) This implements https://github.com/djcb/mu/issues/164. --- lib/mu-container.c | 45 ++++++++++++++++++++++++++++++++++----------- lib/mu-container.h | 5 +++++ 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/lib/mu-container.c b/lib/mu-container.c index 1dd83829..fbe612ff 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -52,6 +52,7 @@ mu_container_new (MuMsg *msg, guint docid, const char *msgid) if (msg) c->msg = mu_msg_ref (msg); + c->leader = c; c->docid = docid; c->msgid = msgid; @@ -356,20 +357,41 @@ container_cmp (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) return mu_msg_cmp (a->msg, b->msg, mfid); } +static gboolean +container_is_leaf (const MuContainer *c) +{ + return c->child == NULL; +} + +static MuContainer* +container_max (MuContainer *a, MuContainer *b, MuMsgFieldId mfid) +{ + return container_cmp (a, b, mfid) > 0 ? a : b; +} + +static MuContainer* +find_sorted_tree_leader (MuContainer *root, SortFuncData *order) +{ + MuContainer *last_child; + + if (container_is_leaf (root)) + return root; + + if (!order->descending) + last_child = root->child->last; + else /* reversed order, first is last */ + last_child = root->child; + + return container_max (root, last_child->leader, order->mfid); +} + static int sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) { - MuContainer *a1, *b1; - - /* use the first non-empty 'left child' message if this one - * is */ - for (a1 = a; a1->msg == NULL && a1->child != NULL; a1 = a1->child); - for (b1 = b; b1->msg == NULL && b1->child != NULL; b1 = b1->child); - if (data->descending) - return container_cmp (b1, a1, data->mfid); + return container_cmp (b->leader, a->leader, data->mfid); else - return container_cmp (a1, b1, data->mfid); + return container_cmp (a->leader, b->leader, data->mfid); } static MuContainer* @@ -381,9 +403,11 @@ container_sort_real (MuContainer *c, SortFuncData *sfdata) if (!c) return NULL; - for (cur = c; cur; cur = cur->next) + for (cur = c; cur; cur = cur->next) { if (cur->child) cur->child = container_sort_real (cur->child, sfdata); + cur->leader = find_sorted_tree_leader (cur, sfdata); + } /* sort siblings */ lst = mu_container_to_list (c); @@ -396,7 +420,6 @@ container_sort_real (MuContainer *c, SortFuncData *sfdata) return c; } - MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean descending, gpointer user_data) diff --git a/lib/mu-container.h b/lib/mu-container.h index c80f0273..9f2686a8 100644 --- a/lib/mu-container.h +++ b/lib/mu-container.h @@ -45,6 +45,11 @@ struct _MuContainer { * */ struct _MuContainer *last; + /* Node in the subtree rooted at this node which comes first + * in the descending sort order, e.g. the latest message if + * sorting by date. We compare the leaders when ordering + * subtrees. */ + struct _MuContainer *leader; MuMsg *msg; const char *msgid;