diff --git a/lib/mu-container.c b/lib/mu-container.c index ec7b3795..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; @@ -263,9 +264,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; @@ -293,17 +309,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; } @@ -317,47 +344,54 @@ struct _SortFuncData { }; typedef struct _SortFuncData SortFuncData; -static MuContainer* -get_top_msg (MuContainer *c, MuMsgFieldId mfid) +static int +container_cmp (MuContainer *a, MuContainer *b, 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 (a == b) + return 0; + else if (!a->msg) + return -1; + else if (!b->msg) + return 1; - if (sub->msg != NULL && mu_msg_cmp (sub->msg, extreme->msg, mfid) > 0) - extreme = sub; - } - } - return extreme; + 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); - - a1 = get_top_msg (a1, data->mfid); - b1 = get_top_msg (b1, data->mfid); - - 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 (b->leader, a->leader, data->mfid); else - return mu_msg_cmp (a1->msg, b1->msg, data->mfid); + return container_cmp (a->leader, b->leader, data->mfid); } static MuContainer* @@ -369,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); @@ -384,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 ec9d7f72..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; @@ -122,6 +127,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 +149,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; 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/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/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/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/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 c8d75276..e11046fd 100644 --- a/mu/tests/test-mu-threads.c +++ b/mu/tests/test-mu-threads.c @@ -34,6 +34,65 @@ #include "mu-query.h" #include "mu-str.h" +struct _tinfo { + const char *threadpath; + const char *msgid; + const char *subject; +}; +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 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) { @@ -58,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; @@ -71,27 +131,28 @@ 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) { gchar *xpath; 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"}, @@ -115,54 +176,17 @@ 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)) { - MuMsg *msg; - const MuMsgIterThreadInfo *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)); - - 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); - - ++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); } - -struct _tinfo { - const char* threadpath; - const char *msgid; - const char* subject; -}; -typedef struct _tinfo tinfo; - static void test_mu_threads_rogue (void) { gchar *xpath; MuMsgIter *iter; - unsigned u; tinfo *items; tinfo items1 [] = { @@ -194,40 +218,214 @@ test_mu_threads_rogue (void) else items = items2; - u = 0; - while (!mu_msg_iter_is_done (iter) && u < G_N_ELEMENTS(items1)) { - MuMsg *msg; - const MuMsgIterThreadInfo *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) */ - /* ); */ - - 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); - - ++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); } +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)); +} + +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)); +} + +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)); +} + +/* 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)); +} + +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)); +} + +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[]) { @@ -237,6 +435,18 @@ 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_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_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_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,