unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH] test: Test thread linking in all possible delivery orders
@ 2014-03-23 21:00 Austin Clements
  2014-04-07 10:37 ` David Bremner
  0 siblings, 1 reply; 8+ messages in thread
From: Austin Clements @ 2014-03-23 21:00 UTC (permalink / raw)
  To: notmuch

This test delivers all possible (single-root) four-message threads in
all possible orders and checks that notmuch successfully links them
into threads in every case.

This is introduced as a new test (rather than just adding it to
T050-new) because it's much easier for this to start with an empty
database.
---

I thought I saw a bug in the thread linking code.  It turned out to be
okay, but I wrote this test to convince myself.

As far as I can tell, we don't have any tests specifically for thread
linking.  We certainly don't have any that are this systematic.

 test/T051-new-linking.sh | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100755 test/T051-new-linking.sh

diff --git a/test/T051-new-linking.sh b/test/T051-new-linking.sh
new file mode 100755
index 0000000..b6d778a
--- /dev/null
+++ b/test/T051-new-linking.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+test_description='"notmuch new" thread linking'
+
+. ./test-lib.sh
+
+test_begin_subtest "All four-message threads get linked in all delivery orders"
+# Generate all possible single-root four message thread structures.
+# Each line in THREADS is a thread structure, where the n'th field is
+# the parent of message n.
+THREADS=$(python -c '
+def mkTrees(free, tree={}):
+    if not free:
+        print(" ".join(map(str, [msg[1] for msg in sorted(tree.items())])))
+        return
+    # Attach each free message to each message in the tree (if there is
+    # no tree, make the free message the root)
+    for msg in sorted(free):
+        parents = sorted(tree.keys()) if tree else ["none"]
+        for parent in parents:
+            ntree = tree.copy()
+            ntree[msg] = parent
+            mkTrees(free - set([msg]), ntree)
+mkTrees(set(range(4)))')
+for ((n = 0; n < 4; n++)); do
+    # Deliver the n'th message of every thread
+    thread=0
+    while read -a parents; do
+        parent=${parents[$n]}
+        generate_message \
+            [id]=m$n@t$thread [in-reply-to]="\<m$parent@t$thread\>" \
+            [subject]=p$thread [from]=m$n
+        thread=$((thread + 1))
+    done <<< "$THREADS"
+    notmuch new > /dev/null
+done
+output=$(notmuch search '*' | notmuch_search_sanitize)
+nthreads=$(wc -l <<< "$THREADS")
+expected=$(for ((i = 0; i < $nthreads; i++)); do
+        echo "thread:XXX   2001-01-05 [4/4] m3, m2, m1, m0; p$i (inbox unread)"
+    done)
+test_expect_equal "$output" "$expected"
+
+test_done
-- 
1.8.4.rc3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] test: Test thread linking in all possible delivery orders
  2014-03-23 21:00 [PATCH] test: Test thread linking in all possible delivery orders Austin Clements
@ 2014-04-07 10:37 ` David Bremner
  2014-04-21 19:58   ` [PATCH v2] " Austin Clements
  0 siblings, 1 reply; 8+ messages in thread
From: David Bremner @ 2014-04-07 10:37 UTC (permalink / raw)
  To: Austin Clements, notmuch

Austin Clements <amdragon@MIT.EDU> writes:
> +THREADS=$(python -c '
> +def mkTrees(free, tree={}):
> +    if not free:

I'm not sure if using not for an empty set is idiomatic python or not,
but it did confuse me a moment.

> +        print(" ".join(map(str, [msg[1] for msg in sorted(tree.items())])))
> +        return
> +    # Attach each free message to each message in the tree (if there is
> +    # no tree, make the free message the root)
> +    for msg in sorted(free):
> +        parents = sorted(tree.keys()) if tree else ["none"]
> +        for parent in parents:
> +            ntree = tree.copy()
> +            ntree[msg] = parent
> +            mkTrees(free - set([msg]), ntree)
> +mkTrees(set(range(4)))')

FWIW, it took me a while to understand this.  I might have twigged
faster if the initial comment said something like "via backtracking".

> +output=$(notmuch search '*' | notmuch_search_sanitize)

> +expected=$(for ((i = 0; i < $nthreads; i++)); do
> +        echo "thread:XXX   2001-01-05 [4/4] m3, m2, m1, m0; p$i (inbox unread)"
> +    done)
> +test_expect_equal "$output" "$expected"

It seems to me this summary line depends on the default search order.
It might be worth specifying the search order in the "notmuch search"
command just to make it a bit more robust.

d

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2] test: Test thread linking in all possible delivery orders
  2014-04-07 10:37 ` David Bremner
@ 2014-04-21 19:58   ` Austin Clements
  2014-04-22 21:31     ` Mark Walters
  0 siblings, 1 reply; 8+ messages in thread
From: Austin Clements @ 2014-04-21 19:58 UTC (permalink / raw)
  To: notmuch

These tests deliver all possible (single-root) four-message threads in
all possible orders and check that notmuch successfully links them
into threads.

There are two variants of the test: one delivers messages that
reference only their immediate parent and the other delivers messages
that reference all of their parents.  The latter test is currently
known-broken.

This is introduced as a new test (rather than just adding it to
T050-new) because it's much easier for this to start with an empty
database.
---

This version hopefully addresses David's comments in
id:87y4zhfmrn.fsf@maritornes.cs.unb.ca and adds a second test that
demonstrates the bug Mark in figured out in
id:8738h7kv2q.fsf@qmul.ac.uk.

test/T051-new-linking.sh | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100755 test/T051-new-linking.sh

diff --git a/test/T051-new-linking.sh b/test/T051-new-linking.sh
new file mode 100755
index 0000000..9ccbc52
--- /dev/null
+++ b/test/T051-new-linking.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+test_description='"notmuch new" thread linking'
+
+. ./test-lib.sh
+
+# Generate all possible single-root four message thread structures.
+# Each line in THREADS is a thread structure, where the n'th field is
+# the parent of message n.  We'll use this for multiple tests below.
+THREADS=$(python -c '
+def mkTrees(free, tree={}):
+    if free == set():
+        print(" ".join(map(str, [msg[1] for msg in sorted(tree.items())])))
+        return
+    # Attach each free message to each message in the tree (if there is
+    # no tree, make the free message the root), backtracking after each
+    for msg in sorted(free):
+        parents = sorted(tree.keys()) if tree else ["none"]
+        for parent in parents:
+            ntree = tree.copy()
+            ntree[msg] = parent
+            mkTrees(free - set([msg]), ntree)
+mkTrees(set(range(4)))')
+nthreads=$(wc -l <<< "$THREADS")
+
+test_begin_subtest "All four-message threads get linked in all delivery orders (one parent)"
+# In the first variant, this delivers messages that reference only
+# their immediate parent.  Hence, we should only expect threads to be
+# fully joined at the end.
+for ((n = 0; n < 4; n++)); do
+    # Deliver the n'th message of every thread
+    thread=0
+    while read -a parents; do
+        parent=${parents[$n]}
+        generate_message \
+            [id]=m$n@t$thread [in-reply-to]="\<m$parent@t$thread\>" \
+            [subject]=p$thread [from]=m$n
+        thread=$((thread + 1))
+    done <<< "$THREADS"
+    notmuch new > /dev/null
+done
+output=$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)
+expected=$(for ((i = 0; i < $nthreads; i++)); do
+        echo "thread:XXX   2001-01-05 [4/4] m3, m2, m1, m0; p$i (inbox unread)"
+    done)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "The same (full parent linkage)"
+test_subtest_known_broken
+# Here we do the same thing as the previous test, but each message
+# references all of its parents.  Since every message references the
+# root of the thread, each thread should always be fully joined.  This
+# is currently broken because of the bug detailed in
+# id:8738h7kv2q.fsf@qmul.ac.uk.
+rm ${MAIL_DIR}/*
+notmuch new
+output=""
+expected=""
+for ((n = 0; n < 4; n++)); do
+    # Deliver the n'th message of every thread
+    thread=0
+    while read -a parents; do
+        references=""
+        parent=${parents[$n]}
+        while [[ $parent != none ]]; do
+            references="<m$parent@t$thread> $references"
+            parent=${parents[$parent]}
+        done
+
+        generate_message \
+            [id]=m$n@t$thread [references]="'$references'" \
+            [subject]=p$thread [from]=m$n
+        thread=$((thread + 1))
+    done <<< "$THREADS"
+    notmuch new > /dev/null
+
+    output="$output
+$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)"
+
+    # Construct expected output
+    template="thread:XXX   2001-01-05 [$((n+1))/$((n+1))]"
+    for ((m = n; m > 0; m--)); do
+        template="$template m$m,"
+    done
+    expected="$expected
+$(for ((i = 0; i < $nthreads; i++)); do
+        echo "$template m0; p$i (inbox unread)"
+    done)"
+done
+test_expect_equal "$output" "$expected"
+
+test_done
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] test: Test thread linking in all possible delivery orders
  2014-04-21 19:58   ` [PATCH v2] " Austin Clements
@ 2014-04-22 21:31     ` Mark Walters
  2014-04-22 22:11       ` Austin Clements
  2014-07-09 21:15       ` [PATCH v3] " Austin Clements
  0 siblings, 2 replies; 8+ messages in thread
From: Mark Walters @ 2014-04-22 21:31 UTC (permalink / raw)
  To: Austin Clements, notmuch


Hi

Broadly this looks good but I am somewhat confused by the python
part. Is it intended to produce the same tree multiple times? It does
seem to produce all the possible trees (*) so it doesn't matter but
might be worth a comment.

Best wishes

Mark

(*) I think there should be 64 rooted trees (16 trees and 4 possible
roots) and the python generates 144 lines. Piping the output to sort and
uniq gives 64 lines.


 
On Mon, 21 Apr 2014, Austin Clements <amdragon@MIT.EDU> wrote:
> These tests deliver all possible (single-root) four-message threads in
> all possible orders and check that notmuch successfully links them
> into threads.
>
> There are two variants of the test: one delivers messages that
> reference only their immediate parent and the other delivers messages
> that reference all of their parents.  The latter test is currently
> known-broken.
>
> This is introduced as a new test (rather than just adding it to
> T050-new) because it's much easier for this to start with an empty
> database.
> ---
>
> This version hopefully addresses David's comments in
> id:87y4zhfmrn.fsf@maritornes.cs.unb.ca and adds a second test that
> demonstrates the bug Mark in figured out in
> id:8738h7kv2q.fsf@qmul.ac.uk.
>
> test/T051-new-linking.sh | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
>  create mode 100755 test/T051-new-linking.sh
>
> diff --git a/test/T051-new-linking.sh b/test/T051-new-linking.sh
> new file mode 100755
> index 0000000..9ccbc52
> --- /dev/null
> +++ b/test/T051-new-linking.sh
> @@ -0,0 +1,91 @@
> +#!/usr/bin/env bash
> +test_description='"notmuch new" thread linking'
> +
> +. ./test-lib.sh
> +
> +# Generate all possible single-root four message thread structures.
> +# Each line in THREADS is a thread structure, where the n'th field is
> +# the parent of message n.  We'll use this for multiple tests below.
> +THREADS=$(python -c '
> +def mkTrees(free, tree={}):
> +    if free == set():
> +        print(" ".join(map(str, [msg[1] for msg in sorted(tree.items())])))
> +        return
> +    # Attach each free message to each message in the tree (if there is
> +    # no tree, make the free message the root), backtracking after each
> +    for msg in sorted(free):
> +        parents = sorted(tree.keys()) if tree else ["none"]
> +        for parent in parents:
> +            ntree = tree.copy()
> +            ntree[msg] = parent
> +            mkTrees(free - set([msg]), ntree)
> +mkTrees(set(range(4)))')
> +nthreads=$(wc -l <<< "$THREADS")
> +
> +test_begin_subtest "All four-message threads get linked in all delivery orders (one parent)"
> +# In the first variant, this delivers messages that reference only
> +# their immediate parent.  Hence, we should only expect threads to be
> +# fully joined at the end.
> +for ((n = 0; n < 4; n++)); do
> +    # Deliver the n'th message of every thread
> +    thread=0
> +    while read -a parents; do
> +        parent=${parents[$n]}
> +        generate_message \
> +            [id]=m$n@t$thread [in-reply-to]="\<m$parent@t$thread\>" \
> +            [subject]=p$thread [from]=m$n
> +        thread=$((thread + 1))
> +    done <<< "$THREADS"
> +    notmuch new > /dev/null
> +done
> +output=$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)
> +expected=$(for ((i = 0; i < $nthreads; i++)); do
> +        echo "thread:XXX   2001-01-05 [4/4] m3, m2, m1, m0; p$i (inbox unread)"
> +    done)
> +test_expect_equal "$output" "$expected"
> +
> +test_begin_subtest "The same (full parent linkage)"
> +test_subtest_known_broken
> +# Here we do the same thing as the previous test, but each message
> +# references all of its parents.  Since every message references the
> +# root of the thread, each thread should always be fully joined.  This
> +# is currently broken because of the bug detailed in
> +# id:8738h7kv2q.fsf@qmul.ac.uk.
> +rm ${MAIL_DIR}/*
> +notmuch new
> +output=""
> +expected=""
> +for ((n = 0; n < 4; n++)); do
> +    # Deliver the n'th message of every thread
> +    thread=0
> +    while read -a parents; do
> +        references=""
> +        parent=${parents[$n]}
> +        while [[ $parent != none ]]; do
> +            references="<m$parent@t$thread> $references"
> +            parent=${parents[$parent]}
> +        done
> +
> +        generate_message \
> +            [id]=m$n@t$thread [references]="'$references'" \
> +            [subject]=p$thread [from]=m$n
> +        thread=$((thread + 1))
> +    done <<< "$THREADS"
> +    notmuch new > /dev/null
> +
> +    output="$output
> +$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)"
> +
> +    # Construct expected output
> +    template="thread:XXX   2001-01-05 [$((n+1))/$((n+1))]"
> +    for ((m = n; m > 0; m--)); do
> +        template="$template m$m,"
> +    done
> +    expected="$expected
> +$(for ((i = 0; i < $nthreads; i++)); do
> +        echo "$template m0; p$i (inbox unread)"
> +    done)"
> +done
> +test_expect_equal "$output" "$expected"
> +
> +test_done
> -- 
> 1.9.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] test: Test thread linking in all possible delivery orders
  2014-04-22 21:31     ` Mark Walters
@ 2014-04-22 22:11       ` Austin Clements
  2014-07-09 21:15       ` [PATCH v3] " Austin Clements
  1 sibling, 0 replies; 8+ messages in thread
From: Austin Clements @ 2014-04-22 22:11 UTC (permalink / raw)
  To: Mark Walters; +Cc: notmuch

The intent was to produce distinct trees, but obviously combinatorics
is not my strong suit.  Any ideas how to fix/rewrite the algorithm,
other than just uniq'ing the output?

Quoth Mark Walters on Apr 22 at 10:31 pm:
> 
> Hi
> 
> Broadly this looks good but I am somewhat confused by the python
> part. Is it intended to produce the same tree multiple times? It does
> seem to produce all the possible trees (*) so it doesn't matter but
> might be worth a comment.
> 
> Best wishes
> 
> Mark
> 
> (*) I think there should be 64 rooted trees (16 trees and 4 possible
> roots) and the python generates 144 lines. Piping the output to sort and
> uniq gives 64 lines.
> 
> 
>  
> On Mon, 21 Apr 2014, Austin Clements <amdragon@MIT.EDU> wrote:
> > These tests deliver all possible (single-root) four-message threads in
> > all possible orders and check that notmuch successfully links them
> > into threads.
> >
> > There are two variants of the test: one delivers messages that
> > reference only their immediate parent and the other delivers messages
> > that reference all of their parents.  The latter test is currently
> > known-broken.
> >
> > This is introduced as a new test (rather than just adding it to
> > T050-new) because it's much easier for this to start with an empty
> > database.
> > ---
> >
> > This version hopefully addresses David's comments in
> > id:87y4zhfmrn.fsf@maritornes.cs.unb.ca and adds a second test that
> > demonstrates the bug Mark in figured out in
> > id:8738h7kv2q.fsf@qmul.ac.uk.
> >
> > test/T051-new-linking.sh | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 91 insertions(+)
> >  create mode 100755 test/T051-new-linking.sh
> >
> > diff --git a/test/T051-new-linking.sh b/test/T051-new-linking.sh
> > new file mode 100755
> > index 0000000..9ccbc52
> > --- /dev/null
> > +++ b/test/T051-new-linking.sh
> > @@ -0,0 +1,91 @@
> > +#!/usr/bin/env bash
> > +test_description='"notmuch new" thread linking'
> > +
> > +. ./test-lib.sh
> > +
> > +# Generate all possible single-root four message thread structures.
> > +# Each line in THREADS is a thread structure, where the n'th field is
> > +# the parent of message n.  We'll use this for multiple tests below.
> > +THREADS=$(python -c '
> > +def mkTrees(free, tree={}):
> > +    if free == set():
> > +        print(" ".join(map(str, [msg[1] for msg in sorted(tree.items())])))
> > +        return
> > +    # Attach each free message to each message in the tree (if there is
> > +    # no tree, make the free message the root), backtracking after each
> > +    for msg in sorted(free):
> > +        parents = sorted(tree.keys()) if tree else ["none"]
> > +        for parent in parents:
> > +            ntree = tree.copy()
> > +            ntree[msg] = parent
> > +            mkTrees(free - set([msg]), ntree)
> > +mkTrees(set(range(4)))')
> > +nthreads=$(wc -l <<< "$THREADS")
> > +
> > +test_begin_subtest "All four-message threads get linked in all delivery orders (one parent)"
> > +# In the first variant, this delivers messages that reference only
> > +# their immediate parent.  Hence, we should only expect threads to be
> > +# fully joined at the end.
> > +for ((n = 0; n < 4; n++)); do
> > +    # Deliver the n'th message of every thread
> > +    thread=0
> > +    while read -a parents; do
> > +        parent=${parents[$n]}
> > +        generate_message \
> > +            [id]=m$n@t$thread [in-reply-to]="\<m$parent@t$thread\>" \
> > +            [subject]=p$thread [from]=m$n
> > +        thread=$((thread + 1))
> > +    done <<< "$THREADS"
> > +    notmuch new > /dev/null
> > +done
> > +output=$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)
> > +expected=$(for ((i = 0; i < $nthreads; i++)); do
> > +        echo "thread:XXX   2001-01-05 [4/4] m3, m2, m1, m0; p$i (inbox unread)"
> > +    done)
> > +test_expect_equal "$output" "$expected"
> > +
> > +test_begin_subtest "The same (full parent linkage)"
> > +test_subtest_known_broken
> > +# Here we do the same thing as the previous test, but each message
> > +# references all of its parents.  Since every message references the
> > +# root of the thread, each thread should always be fully joined.  This
> > +# is currently broken because of the bug detailed in
> > +# id:8738h7kv2q.fsf@qmul.ac.uk.
> > +rm ${MAIL_DIR}/*
> > +notmuch new
> > +output=""
> > +expected=""
> > +for ((n = 0; n < 4; n++)); do
> > +    # Deliver the n'th message of every thread
> > +    thread=0
> > +    while read -a parents; do
> > +        references=""
> > +        parent=${parents[$n]}
> > +        while [[ $parent != none ]]; do
> > +            references="<m$parent@t$thread> $references"
> > +            parent=${parents[$parent]}
> > +        done
> > +
> > +        generate_message \
> > +            [id]=m$n@t$thread [references]="'$references'" \
> > +            [subject]=p$thread [from]=m$n
> > +        thread=$((thread + 1))
> > +    done <<< "$THREADS"
> > +    notmuch new > /dev/null
> > +
> > +    output="$output
> > +$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)"
> > +
> > +    # Construct expected output
> > +    template="thread:XXX   2001-01-05 [$((n+1))/$((n+1))]"
> > +    for ((m = n; m > 0; m--)); do
> > +        template="$template m$m,"
> > +    done
> > +    expected="$expected
> > +$(for ((i = 0; i < $nthreads; i++)); do
> > +        echo "$template m0; p$i (inbox unread)"
> > +    done)"
> > +done
> > +test_expect_equal "$output" "$expected"
> > +
> > +test_done

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3] test: Test thread linking in all possible delivery orders
  2014-04-22 21:31     ` Mark Walters
  2014-04-22 22:11       ` Austin Clements
@ 2014-07-09 21:15       ` Austin Clements
  2014-07-16 10:31         ` David Bremner
  2014-07-16 22:35         ` David Bremner
  1 sibling, 2 replies; 8+ messages in thread
From: Austin Clements @ 2014-07-09 21:15 UTC (permalink / raw)
  To: notmuch

These tests deliver all possible (single-root) four-message threads in
all possible orders and check that notmuch successfully links them
into threads.  These tests supersede and replace the previous and much
less thorough "T260-thread-order" tests.

There are two variants of the test: one delivers messages that
reference only their immediate parent and the other delivers messages
that reference all of their parents.  The latter test is currently
known-broken.
---
This version addresses Mark's review by using a completely different
thread tree generation algorithm that directly generates distinct
trees.  This algorithm is a little longer, so I pulled it out into its
own script.  Since v2, I also realized we already had an equivalent
hand-written, but far less thorough test in T260-thread-order, so this
version simply replaces that test.

Separately, I have an implementation of my ghost message proposal,
which fixes the known-broken test introduced in this patch (among
other things).  I'll send that once I've cleaned it up a bit.

 test/T260-thread-order.sh | 86 +++++++++++++++++++++++++++++++++++------------
 test/gen-threads.py       | 33 ++++++++++++++++++
 2 files changed, 98 insertions(+), 21 deletions(-)
 create mode 100644 test/gen-threads.py

diff --git a/test/T260-thread-order.sh b/test/T260-thread-order.sh
index 6c3a4b3..b435d79 100755
--- a/test/T260-thread-order.sh
+++ b/test/T260-thread-order.sh
@@ -2,31 +2,75 @@
 test_description="threading when messages received out of order"
 . ./test-lib.sh
 
-test_begin_subtest "Adding initial child message"
-generate_message [body]=foo "[in-reply-to]=\<parent-id\>" [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
+# Generate all single-root four message thread structures.  We'll use
+# this for multiple tests below.
+THREADS=$(python ${TEST_DIRECTORY}/gen-threads.py 4)
+nthreads=$(wc -l <<< "$THREADS")
 
-test_begin_subtest "Searching returns the message"
-output=$(notmuch search foo | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+test_begin_subtest "Messages with one parent get linked in all delivery orders"
+# In the first variant, this delivers messages that reference only
+# their immediate parent.  Hence, we should only expect threads to be
+# fully joined at the end.
+for ((n = 0; n < 4; n++)); do
+    # Deliver the n'th message of every thread
+    thread=0
+    while read -a parents; do
+        parent=${parents[$n]}
+        generate_message \
+            [id]=m$n@t$thread [in-reply-to]="\<m$parent@t$thread\>" \
+            [subject]=p$thread [from]=m$n
+        thread=$((thread + 1))
+    done <<< "$THREADS"
+    notmuch new > /dev/null
+done
+output=$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)
+expected=$(for ((i = 0; i < $nthreads; i++)); do
+        echo "thread:XXX   2001-01-05 [4/4] m3, m2, m1, m0; p$i (inbox unread)"
+    done)
+test_expect_equal "$output" "$expected"
 
-test_begin_subtest "Adding second child message"
-generate_message [body]=foo "[in-reply-to]=\<parent-id\>" [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
+test_begin_subtest "Messages with all parents get linked in all delivery orders"
+test_subtest_known_broken
+# Here we do the same thing as the previous test, but each message
+# references all of its parents.  Since every message references the
+# root of the thread, each thread should always be fully joined.  This
+# is currently broken because of the bug detailed in
+# id:8738h7kv2q.fsf@qmul.ac.uk.
+rm ${MAIL_DIR}/*
+notmuch new > /dev/null
+output=""
+expected=""
+for ((n = 0; n < 4; n++)); do
+    # Deliver the n'th message of every thread
+    thread=0
+    while read -a parents; do
+        references=""
+        parent=${parents[$n]}
+        while [[ $parent != None ]]; do
+            references="<m$parent@t$thread> $references"
+            parent=${parents[$parent]}
+        done
 
-test_begin_subtest "Searching returns both messages in one thread"
-output=$(notmuch search foo | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [2/2] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+        generate_message \
+            [id]=m$n@t$thread [references]="'$references'" \
+            [subject]=p$thread [from]=m$n
+        thread=$((thread + 1))
+    done <<< "$THREADS"
+    notmuch new > /dev/null
 
-test_begin_subtest "Adding parent message"
-generate_message [body]=foo [id]=parent-id [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
+    output="$output
+$(notmuch search --sort=newest-first '*' | notmuch_search_sanitize)"
 
-test_begin_subtest "Searching returns all three messages in one thread"
-output=$(notmuch search foo | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [3/3] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+    # Construct expected output
+    template="thread:XXX   2001-01-05 [$((n+1))/$((n+1))]"
+    for ((m = n; m > 0; m--)); do
+        template="$template m$m,"
+    done
+    expected="$expected
+$(for ((i = 0; i < $nthreads; i++)); do
+        echo "$template m0; p$i (inbox unread)"
+    done)"
+done
+test_expect_equal "$output" "$expected"
 
 test_done
diff --git a/test/gen-threads.py b/test/gen-threads.py
new file mode 100644
index 0000000..9fbb847
--- /dev/null
+++ b/test/gen-threads.py
@@ -0,0 +1,33 @@
+# Generate all possible single-root message thread structures of size
+# argv[1].  Each output line is a thread structure, where the n'th
+# field is either a number giving the parent of message n or "None"
+# for the root.
+
+import sys
+from itertools import chain, combinations
+
+def subsets(s):
+    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
+
+nodes = set(range(int(sys.argv[1])))
+
+# Queue of (tree, free, to_expand) where tree is a {node: parent}
+# dictionary, free is a set of unattached nodes, and to_expand is
+# itself a queue of nodes in the tree that need to be expanded.
+# The queue starts with all single-node trees.
+queue = [({root: None}, nodes - {root}, (root,)) for root in nodes]
+
+# Process queue
+while queue:
+    tree, free, to_expand = queue.pop()
+
+    if len(to_expand) == 0:
+        # Only print full-sized trees
+        if len(free) == 0:
+            print(" ".join(map(str, [msg[1] for msg in sorted(tree.items())])))
+    else:
+        # Expand node to_expand[0] with each possible set of children
+        for children in subsets(free):
+            ntree = dict(tree, **{child: to_expand[0] for child in children})
+            nfree = free.difference(children)
+            queue.append((ntree, nfree, to_expand[1:] + tuple(children)))
-- 
2.0.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] test: Test thread linking in all possible delivery orders
  2014-07-09 21:15       ` [PATCH v3] " Austin Clements
@ 2014-07-16 10:31         ` David Bremner
  2014-07-16 22:35         ` David Bremner
  1 sibling, 0 replies; 8+ messages in thread
From: David Bremner @ 2014-07-16 10:31 UTC (permalink / raw)
  To: Austin Clements, notmuch

Austin Clements <amdragon@MIT.EDU> writes:

> This version addresses Mark's review by using a completely different
> thread tree generation algorithm that directly generates distinct
> trees.  This algorithm is a little longer, so I pulled it out into its
> own script.  Since v2, I also realized we already had an equivalent
> hand-written, but far less thorough test in T260-thread-order, so this
> version simply replaces that test.

Well, it matches the first 8 terms of 

      http://oeis.org/A000169

So I guess that's good enough for me ;).

d

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3] test: Test thread linking in all possible delivery orders
  2014-07-09 21:15       ` [PATCH v3] " Austin Clements
  2014-07-16 10:31         ` David Bremner
@ 2014-07-16 22:35         ` David Bremner
  1 sibling, 0 replies; 8+ messages in thread
From: David Bremner @ 2014-07-16 22:35 UTC (permalink / raw)
  To: Austin Clements, notmuch

Austin Clements <amdragon@MIT.EDU> writes:

> These tests deliver all possible (single-root) four-message threads in
> all possible orders and check that notmuch successfully links them
> into threads.  These tests supersede and replace the previous and much
> less thorough "T260-thread-order" tests.

pushed.

d

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2014-07-16 22:35 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-23 21:00 [PATCH] test: Test thread linking in all possible delivery orders Austin Clements
2014-04-07 10:37 ` David Bremner
2014-04-21 19:58   ` [PATCH v2] " Austin Clements
2014-04-22 21:31     ` Mark Walters
2014-04-22 22:11       ` Austin Clements
2014-07-09 21:15       ` [PATCH v3] " Austin Clements
2014-07-16 10:31         ` David Bremner
2014-07-16 22:35         ` David Bremner

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).