Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 5c / 9854f08b602f0a1d1cbe94b45cffa4d75358b9
1 Return-Path: <dkg@fifthhorseman.net>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5  by arlo.cworth.org (Postfix) with ESMTP id E0F3F6DE02DD\r
6  for <notmuch@notmuchmail.org>; Fri,  8 Apr 2016 18:02:46 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at cworth.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
12  autolearn=disabled\r
13 Received: from arlo.cworth.org ([127.0.0.1])\r
14  by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)\r
15  with ESMTP id 6tE6w4f95HQZ for <notmuch@notmuchmail.org>;\r
16  Fri,  8 Apr 2016 18:02:39 -0700 (PDT)\r
17 Received: from che.mayfirst.org (che.mayfirst.org [209.234.253.108])\r
18  by arlo.cworth.org (Postfix) with ESMTP id C349A6DE028C\r
19  for <notmuch@notmuchmail.org>; Fri,  8 Apr 2016 18:02:38 -0700 (PDT)\r
20 Received: from fifthhorseman.net (unknown [201.140.212.132])\r
21  by che.mayfirst.org (Postfix) with ESMTPSA id 969C110083\r
22  for <notmuch@notmuchmail.org>; Fri,  8 Apr 2016 21:02:36 -0400 (EDT)\r
23 Received: by fifthhorseman.net (Postfix, from userid 1000)\r
24  id F13151FE7C; Fri,  8 Apr 2016 22:02:34 -0300 (ART)\r
25 From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>\r
26 To: Notmuch Mail <notmuch@notmuchmail.org>\r
27 Subject: [PATCH v3 2/7] test thread breakage when messages are removed and\r
28  re-added\r
29 Date: Fri,  8 Apr 2016 22:02:29 -0300\r
30 Message-Id: <1460163754-22994-2-git-send-email-dkg@fifthhorseman.net>\r
31 X-Mailer: git-send-email 2.8.0.rc3\r
32 In-Reply-To: <1460163754-22994-1-git-send-email-dkg@fifthhorseman.net>\r
33 References: <1459445693-3900-1-git-send-email-dkg@fifthhorseman.net>\r
34  <1460163754-22994-1-git-send-email-dkg@fifthhorseman.net>\r
35 X-BeenThere: notmuch@notmuchmail.org\r
36 X-Mailman-Version: 2.1.20\r
37 Precedence: list\r
38 List-Id: "Use and development of the notmuch mail system."\r
39  <notmuch.notmuchmail.org>\r
40 List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,\r
41  <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
42 List-Archive: <http://notmuchmail.org/pipermail/notmuch/>\r
43 List-Post: <mailto:notmuch@notmuchmail.org>\r
44 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
45 List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,\r
46  <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
47 X-List-Received-Date: Sat, 09 Apr 2016 01:02:47 -0000\r
48 \r
49 This test (T590-thread-breakage.sh) has known-broken subtests.\r
50 \r
51 If you have a two-message thread where message "B" is in-reply-to "A",\r
52 notmuch rightly sees this as a single thread.\r
53 \r
54 But if you:\r
55 \r
56  * remove "A" from the message store\r
57  * run "notmuch new"\r
58  * add "A" back into the message store\r
59  * re-run "notmuch new"\r
60 \r
61 Then notmuch sees the messages as distinct threads.\r
62 \r
63 This happens because if you insert "B" initially (before anything is\r
64 known about "A"), then a "ghost message" gets added to the database in\r
65 reference to "A" that is in the same thread, which "A" takes over when\r
66 it appears.\r
67 \r
68 But if "A" is subsequently removed, no ghost message is retained, so\r
69 when "A" appears, it is treated as a new thread.\r
70 \r
71 I see a few options to fix this:\r
72 \r
73 ghost-on-removal\r
74 ----------------\r
75 \r
76 We could unilaterally add a ghost upon message removal.  This has a\r
77 few disadvantages: the message index would leak information about what\r
78 messages the user has ever been exposed to, and we also create a\r
79 perpetually-growing dataset -- the ghosts can never be removed.\r
80 \r
81 ghost-on-removal-when-shared-thread-exists\r
82 ------------------------------------------\r
83 \r
84 We could add a ghost upon message removal iff there are other\r
85 non-ghost messages with the same thread ID.\r
86 \r
87 We'd also need to remove all ghost messages that share a thread when\r
88 the last non-ghost message in that thread is removed.\r
89 \r
90 This still has a bit of information leakage, though: the message index\r
91 would reveal that i've seen a newer message in a thread, even if i had\r
92 deleted it from my message store\r
93 \r
94 track-dependencies\r
95 ------------------\r
96 \r
97 rather than a simple "ghost-message" we could store all the (A,B)\r
98 message-reference pairs internally, showing which messages A reference\r
99 which other messages B.\r
100 \r
101 Then removal of message X would require deleting all message-reference\r
102 pairs (X,B), and only deleting a ghost message if no (A,X) reference\r
103 pair exists.\r
104 \r
105 This requires modifying the database by adding a new and fairly weird\r
106 table that would need to be indexed by both columns.  I don't know\r
107 whether xapian has nice ways to do that.\r
108 \r
109 scan-dependencies\r
110 -----------------\r
111 \r
112 Without modifying the database, we could do something less efficient.\r
113 \r
114 Upon removal of message X, we could scan the headers of all non-ghost\r
115 messages that share a thread with X.  If any of those messages refers\r
116 to X, we would add a ghost message.  If none of them do, then we would\r
117 just drop X entirely from the table.\r
118 \r
119 ---------------------\r
120 \r
121 One risk of attempted fixes to this problem is that we could fail to\r
122 remove the search term indexes entirely.  This test contains\r
123 additional subtests to guard against that.\r
124 \r
125 This test also ensures that the right number of ghost messages exist\r
126 in each situation; this will help us ensure we don't accumulate ghosts\r
127 indefinitely or leak too much information about what messages we've\r
128 seen or not seen, while still making it easy to reassemble threads\r
129 when messages come in out-of-order.\r
130 ---\r
131  test/T590-thread-breakage.sh | 131 +++++++++++++++++++++++++++++++++++++++++++\r
132  1 file changed, 131 insertions(+)\r
133  create mode 100755 test/T590-thread-breakage.sh\r
134 \r
135 diff --git a/test/T590-thread-breakage.sh b/test/T590-thread-breakage.sh\r
136 new file mode 100755\r
137 index 0000000..24ffb42\r
138 --- /dev/null\r
139 +++ b/test/T590-thread-breakage.sh\r
140 @@ -0,0 +1,131 @@\r
141 +#!/usr/bin/env bash\r
142 +#\r
143 +# Copyright (c) 2016 Daniel Kahn Gillmor\r
144 +#\r
145 +\r
146 +test_description='thread breakage during reindexing\r
147 +\r
148 +notmuch uses ghost documents to track messages we have seen references\r
149 +to but have never seen.  Regardless of the order of delivery, message\r
150 +deletion, and reindexing, the list of ghost messages for a given\r
151 +stored corpus should not vary, so that threads can be reassmebled\r
152 +cleanly.\r
153 +\r
154 +In practice, we accept a small amount of variation (and therefore\r
155 +traffic pattern metadata leakage to be stored in the index) for the\r
156 +sake of efficiency.\r
157 +\r
158 +This test also embeds some subtests to ensure that indexing actually\r
159 +works properly and attempted fixes to threading issues do not break\r
160 +the expected contents of the index.'\r
161 +\r
162 +. ./test-lib.sh || exit 1\r
163 +\r
164 +message_a() {\r
165 +    mkdir -p ${MAIL_DIR}/cur\r
166 +    cat > ${MAIL_DIR}/cur/a <<EOF\r
167 +Subject: First message\r
168 +Message-ID: <a@example.net>\r
169 +From: Alice <alice@example.net>\r
170 +To: Bob <bob@example.net>\r
171 +Date: Thu, 31 Mar 2016 20:10:00 -0400\r
172 +\r
173 +This is the first message in the thread.\r
174 +Apple\r
175 +EOF\r
176 +}\r
177 +\r
178 +message_b() {\r
179 +    mkdir -p ${MAIL_DIR}/cur\r
180 +    cat > ${MAIL_DIR}/cur/b <<EOF\r
181 +Subject: Second message\r
182 +Message-ID: <b@example.net>\r
183 +In-Reply-To: <a@example.net>\r
184 +References: <a@example.net>\r
185 +From: Bob <bob@example.net>\r
186 +To: Alice <alice@example.net>\r
187 +Date: Thu, 31 Mar 2016 20:15:00 -0400\r
188 +\r
189 +This is the second message in the thread.\r
190 +Banana\r
191 +EOF\r
192 +}\r
193 +\r
194 +\r
195 +test_subject_count() {\r
196 +    test_begin_subtest "${3:-looking for $2 instance of '$1'}"\r
197 +    count=$(notmuch count --output=threads "$1")\r
198 +    test_expect_equal "$count" "$2"\r
199 +}\r
200 +\r
201 +test_thread_count() {\r
202 +    test_begin_subtest "${2:-Expecting $1 thread(s)}"\r
203 +    count=$(notmuch count --output=threads)\r
204 +    test_expect_equal "$count" "$1"\r
205 +}\r
206 +\r
207 +test_ghost_count() {\r
208 +    test_begin_subtest "${2:-Expecting $1 ghosts(s)}"\r
209 +    ghosts=$(../ghost-report ${MAIL_DIR}/.notmuch/xapian)\r
210 +    test_expect_equal "$ghosts" "$1"\r
211 +}\r
212 +\r
213 +notmuch new >/dev/null\r
214 +pwd\r
215 +ls -la ${MAIL_DIR}/.notmuch/xapian\r
216 +\r
217 +test_thread_count 0 'There should be no threads initially'\r
218 +test_ghost_count 0 'There should be no ghosts initially'\r
219 +\r
220 +message_a\r
221 +notmuch new >/dev/null\r
222 +test_thread_count 1 'One message in: one thread'\r
223 +test_subject_count apple 1\r
224 +test_subject_count banana 0\r
225 +test_ghost_count 0\r
226 +\r
227 +message_b\r
228 +notmuch new >/dev/null\r
229 +test_thread_count 1 'Second message in the same thread: one thread'\r
230 +test_subject_count apple 1\r
231 +test_subject_count banana 1\r
232 +test_ghost_count 0\r
233 +\r
234 +rm -f ${MAIL_DIR}/cur/a\r
235 +notmuch new >/dev/null\r
236 +test_thread_count 1 'First message removed: still only one thread'\r
237 +test_subject_count apple 0\r
238 +test_subject_count banana 1\r
239 +test_begin_subtest 'should be one ghost after first message removed'\r
240 +test_subtest_known_broken\r
241 +ghosts=$(../ghost-report ${MAIL_DIR}/.notmuch/xapian)\r
242 +test_expect_equal "$ghosts" "1"\r
243 +\r
244 +message_a\r
245 +notmuch new >/dev/null\r
246 +# this is known to fail (it shows 2 threads) because no "ghost\r
247 +# message" was created for message A when it was removed from the\r
248 +# index, despite message B still pointing to it.\r
249 +test_begin_subtest 'First message reappears: should return to the same thread'\r
250 +test_subtest_known_broken\r
251 +count=$(notmuch count --output=threads)\r
252 +test_expect_equal "$count" "1"\r
253 +test_subject_count apple 1\r
254 +test_subject_count banana 1\r
255 +test_ghost_count 0\r
256 +\r
257 +rm -f ${MAIL_DIR}/cur/b\r
258 +notmuch new >/dev/null\r
259 +test_thread_count 1 'Removing second message: still only one thread'\r
260 +test_subject_count apple 1\r
261 +test_subject_count banana 0\r
262 +test_ghost_count 0 'No ghosts should remain after deletion of second message'\r
263 +\r
264 +rm -f ${MAIL_DIR}/cur/a\r
265 +notmuch new >/dev/null\r
266 +test_thread_count 0 'All messages gone: no threads'\r
267 +test_subject_count apple 0\r
268 +test_subject_count banana 0\r
269 +test_ghost_count 0\r
270 +\r
271 +test_done\r
272 -- \r
273 2.8.0.rc3\r
274 \r