Re: [PATCH v2 01/13] test: Uniformly canonicalize actual and expected JSON
authorMark Walters <markwalters1009@gmail.com>
Sat, 28 Jul 2012 13:18:09 +0000 (14:18 +0100)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:48:39 +0000 (09:48 -0800)
50/681e10bd302d5f8db60f1e8f9206f6c6980342 [new file with mode: 0644]

diff --git a/50/681e10bd302d5f8db60f1e8f9206f6c6980342 b/50/681e10bd302d5f8db60f1e8f9206f6c6980342
new file mode 100644 (file)
index 0000000..a585330
--- /dev/null
@@ -0,0 +1,666 @@
+Return-Path: <m.walters@qmul.ac.uk>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 1872F431FAF\r
+       for <notmuch@notmuchmail.org>; Sat, 28 Jul 2012 06:18:19 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -1.098\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5\r
+       tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
+       NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id 88qZYImQnD27 for <notmuch@notmuchmail.org>;\r
+       Sat, 28 Jul 2012 06:18:17 -0700 (PDT)\r
+Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
+       (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 9E1D4431FAE\r
+       for <notmuch@notmuchmail.org>; Sat, 28 Jul 2012 06:18:16 -0700 (PDT)\r
+Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
+       by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
+       (envelope-from <m.walters@qmul.ac.uk>)\r
+       id 1Sv6uJ-0000ko-Gi; Sat, 28 Jul 2012 14:18:14 +0100\r
+Received: from 94-192-233-223.zone6.bethere.co.uk ([94.192.233.223]\r
+       helo=localhost)\r
+       by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69)\r
+       (envelope-from <m.walters@qmul.ac.uk>)\r
+       id 1Sv6uI-0001Qm-8U; Sat, 28 Jul 2012 14:18:11 +0100\r
+From: Mark Walters <markwalters1009@gmail.com>\r
+To: Austin Clements <amdragon@MIT.EDU>, notmuch@notmuchmail.org\r
+Subject: Re: [PATCH v2 01/13] test: Uniformly canonicalize actual and expected\r
+       JSON\r
+In-Reply-To: <1343449754-9010-2-git-send-email-amdragon@mit.edu>\r
+References: <1343449754-9010-1-git-send-email-amdragon@mit.edu>\r
+       <1343449754-9010-2-git-send-email-amdragon@mit.edu>\r
+User-Agent: Notmuch/0.13.2+93~gf33b188 (http://notmuchmail.org) Emacs/23.4.1\r
+       (x86_64-pc-linux-gnu)\r
+Date: Sat, 28 Jul 2012 14:18:09 +0100\r
+Message-ID: <87lii4uir2.fsf@qmul.ac.uk>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=utf-8\r
+Content-Transfer-Encoding: quoted-printable\r
+X-Sender-Host-Address: 94.192.233.223\r
+X-QM-SPAM-Info: Sender has good ham record.  :)\r
+X-QM-Body-MD5: bee0de2a259038a02d453a38c150ea76 (of first 20000 bytes)\r
+X-SpamAssassin-Score: -1.8\r
+X-SpamAssassin-SpamBar: -\r
+X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
+       determine if it is\r
+       spam. We require at least 5.0 points to mark a message as spam.\r
+       This message scored -1.8 points.\r
+       Summary of the scoring: \r
+       * -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/,\r
+       *      medium trust\r
+       *      [138.37.6.40 listed in list.dnswl.org]\r
+       * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
+       provider *      (markwalters1009[at]gmail.com)\r
+       * -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay\r
+       *      domain\r
+       *  0.5 AWL AWL: From: address is in the auto white-list\r
+X-QM-Scan-Virus: ClamAV says the message is clean\r
+Cc: tomi.ollila@iki.fi\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sat, 28 Jul 2012 13:18:19 -0000\r
+\r
+\r
+This looks good to me. I have read the test patches too now and they\r
+look fine. I just have one possible thought (see below) which is\r
+definitely not worth holding up this series for.\r
+\r
+Best wishes=20\r
+\r
+Mark\r
+\r
+On Sat, 28 Jul 2012, Austin Clements <amdragon@MIT.EDU> wrote:\r
+> Previously, we used a variety of ad-hoc canonicalizations for JSON\r
+> output in the test suite, but were ultimately very sensitive to JSON\r
+> irrelevancies such as whitespace.  This introduces a new test\r
+> comparison function, test_expect_equal_json, that first pretty-prints\r
+> *both* the actual and expected JSON and the compares the result.\r
+>\r
+> The current implementation of this simply uses Python's json.tool to\r
+> perform pretty-printing (with a fallback to the identity function if\r
+> parsing fails).  However, since the interface it introduces is\r
+> semantically high-level, we could swap in other mechanisms in the\r
+> future, such as another pretty-printer or something that does not\r
+> re-order object keys (if we decide that we care about that).\r
+>\r
+> In general, this patch does not remove the existing ad-hoc\r
+> canonicalization because it does no harm.  We do have to remove the\r
+> newline-after-comma rule from notmuch_json_show_sanitize and\r
+> filter_show_json because it results in invalid JSON that cannot be\r
+> pretty-printed.\r
+>\r
+> Most of this patch simply replaces test_expect_equal and\r
+> test_expect_equal_file with test_expect_equal_json.  It changes the\r
+> expected JSON in a few places where sanitizers had placed newlines\r
+> after commas inside strings.\r
+> ---\r
+>  test/crypto        |   37 +++++++++++++++----------------------\r
+>  test/json          |   14 +++++++-------\r
+>  test/maildir-sync  |   11 ++++-------\r
+>  test/multipart     |   34 +++++++++++++++-------------------\r
+>  test/search-output |    2 +-\r
+>  test/test-lib.sh   |   17 +++++++++++++----\r
+>  6 files changed, 55 insertions(+), 60 deletions(-)\r
+>\r
+> diff --git a/test/crypto b/test/crypto\r
+> index be752b1..5dd14c4 100755\r
+> --- a/test/crypto\r
+> +++ b/test/crypto\r
+> @@ -51,8 +51,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test signed message 001",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "sigstatus": [{"status": "good",\r
+>   "fingerprint": "'$FINGERPRINT'",\r
+> @@ -64,7 +63,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   {"id": 3,\r
+>   "content-type": "application/pgp-signature"}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>=20=20\r
+> @@ -85,8 +84,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test signed message 001",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "sigstatus": [{"status": "good",\r
+>   "fingerprint": "'$FINGERPRINT'",\r
+> @@ -99,7 +97,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   {"id": 3,\r
+>   "content-type": "application/pgp-signature"}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>=20=20\r
+> @@ -119,8 +117,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test signed message 001",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "sigstatus": [{"status": "error",\r
+>   "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",\r
+> @@ -132,7 +129,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   {"id": 3,\r
+>   "content-type": "application/pgp-signature"}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>  mv "${GNUPGHOME}"{.bak,}\r
+> @@ -193,8 +190,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test encrypted message 001",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "encstatus": [{"status": "good"}],\r
+>   "sigstatus": [],\r
+> @@ -210,7 +206,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "content-type": "application/octet-stream",\r
+>   "filename": "TESTATTACHMENT"}]}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>=20=20\r
+> @@ -221,7 +217,7 @@ output=3D$(notmuch show --format=3Djson --part=3D4 --=\r
+decrypt subject:"test encrypted m\r
+>  expected=3D'{"id": 4,\r
+>   "content-type": "text/plain",\r
+>   "content": "This is a test encrypted message.\n"}'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>=20=20\r
+> @@ -248,8 +244,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test encrypted message 001",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "encstatus": [{"status": "bad"}],\r
+>   "content-type": "multipart/encrypted",\r
+> @@ -258,7 +253,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   {"id": 3,\r
+>   "content-type": "application/octet-stream"}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>  mv "${GNUPGHOME}"{.bak,}\r
+> @@ -283,8 +278,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test encrypted message 002",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "encstatus": [{"status": "good"}],\r
+>   "sigstatus": [{"status": "good",\r
+> @@ -298,7 +292,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "content-type": "text/plain",\r
+>   "content": "This is another test encrypted message.\n"}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>=20=20\r
+> @@ -338,8 +332,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   "headers": {"Subject": "test signed message 001",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>   "To": "test_suite@notmuchmail.org",\r
+> - "Date": "Sat,\r
+> - 01 Jan 2000 12:00:00 +0000"},\r
+> + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "sigstatus": [{"status": "error",\r
+>   "keyid": "6D92612D94E46381",\r
+> @@ -351,7 +344,7 @@ expected=3D'[[[{"id": "XXXXX",\r
+>   {"id": 3,\r
+>   "content-type": "application/pgp-signature"}]}]},\r
+>   []]]]'\r
+> -test_expect_equal \\r
+> +test_expect_equal_json \\r
+>      "$output" \\r
+>      "$expected"\r
+>=20=20\r
+> diff --git a/test/json b/test/json\r
+> index 831e105..d86ee46 100755\r
+> --- a/test/json\r
+> +++ b/test/json\r
+> @@ -5,21 +5,21 @@ test_description=3D"--format=3Djson output"\r
+>  test_begin_subtest "Show message: json"\r
+>  add_message "[subject]=3D\"json-show-subject\"" "[date]=3D\"Sat, 01 Jan =\r
+2000 12:00:00 -0000\"" "[body]=3D\"json-show-message\""\r
+>  output=3D$(notmuch show --format=3Djson "json-show-message")\r
+> -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": t=\r
+rue, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestam=\r
+p\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"u=\r
+nread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Not=\r
+much Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suit=\r
+e <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +00=\r
+00\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\"=\r
+: \"json-show-message\n\"}]}, []]]]"\r
+> +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match=\r
+\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"tim=\r
+estamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\=\r
+",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": =\r
+\"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test=\r
+ Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:0=\r
+0 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"cont=\r
+ent\": \"json-show-message\n\"}]}, []]]]"\r
+\r
+Since test_expect_equal_json does not care about whitespace (outside of\r
+strings) would it be worth splitting the expected output for each of\r
+these tests into multiple lines?\r
+\r
+>  # This should be the same output as above.\r
+>  test_begin_subtest "Show message: json --body=3Dtrue"\r
+>  output=3D$(notmuch show --format=3Djson --body=3Dtrue "json-show-message=\r
+")\r
+> -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": t=\r
+rue, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestam=\r
+p\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"u=\r
+nread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Not=\r
+much Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suit=\r
+e <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +00=\r
+00\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\"=\r
+: \"json-show-message\n\"}]}, []]]]"\r
+> +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match=\r
+\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"tim=\r
+estamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\=\r
+",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": =\r
+\"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test=\r
+ Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:0=\r
+0 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"cont=\r
+ent\": \"json-show-message\n\"}]}, []]]]"\r
+>=20=20\r
+>  test_begin_subtest "Show message: json --body=3Dfalse"\r
+>  output=3D$(notmuch show --format=3Djson --body=3Dfalse "json-show-messag=\r
+e")\r
+> -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": t=\r
+rue, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestam=\r
+p\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"u=\r
+nread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Not=\r
+much Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suit=\r
+e <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +00=\r
+00\"}}, []]]]"\r
+> +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match=\r
+\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"tim=\r
+estamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\=\r
+",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": =\r
+\"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test=\r
+ Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:0=\r
+0 +0000\"}}, []]]]"\r
+>=20=20\r
+>  test_begin_subtest "Search message: json"\r
+>  add_message "[subject]=3D\"json-search-subject\"" "[date]=3D\"Sat, 01 Ja=\r
+n 2000 12:00:00 -0000\"" "[body]=3D\"json-search-message\""\r
+>  output=3D$(notmuch search --format=3Djson "json-search-message" | notmuc=\r
+h_json_show_sanitize | notmuch_search_sanitize)\r
+> -test_expect_equal "$output" "[{\"thread\": \"XXX\",\r
+> +test_expect_equal_json "$output" "[{\"thread\": \"XXX\",\r
+>   \"timestamp\": 946728000,\r
+>   \"date_relative\": \"2000-01-01\",\r
+>   \"matched\": 1,\r
+> @@ -32,7 +32,7 @@ test_expect_equal "$output" "[{\"thread\": \"XXX\",\r
+>  test_begin_subtest "Show message: json, utf-8"\r
+>  add_message "[subject]=3D\"json-show-utf8-body-s=C3=BCbj=C3=A9ct\"" "[da=\r
+te]=3D\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=3D\"js=C3=B6n-show-m=C3=\r
+=A9ssage\""\r
+>  output=3D$(notmuch show --format=3Djson "js=C3=B6n-show-m=C3=A9ssage")\r
+> -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": t=\r
+rue, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestam=\r
+p\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"u=\r
+nread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-s=C3=BCbj=C3=A9c=\r
+t\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\":=\r
+ \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 J=\r
+an 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/=\r
+plain\", \"content\": \"js=C3=B6n-show-m=C3=A9ssage\n\"}]}, []]]]"\r
+> +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match=\r
+\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"tim=\r
+estamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\=\r
+",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-s=C3=BCbj=\r
+=C3=A9ct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", =\r
+\"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sa=\r
+t, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": =\r
+\"text/plain\", \"content\": \"js=C3=B6n-show-m=C3=A9ssage\n\"}]}, []]]]"\r
+>=20=20\r
+>  test_begin_subtest "Show message: json, inline attachment filename"\r
+>  subject=3D'json-show-inline-attachment-filename'\r
+> @@ -45,12 +45,12 @@ emacs_deliver_message \\r
+>       (insert \"Message-ID: <$id>\n\")"\r
+>  output=3D$(notmuch show --format=3Djson "id:$id")\r
+>  filename=3D$(notmuch search --output=3Dfiles "id:$id")\r
+> -test_expect_equal "$output" "[[[{\"id\": \"$id\", \"match\": true, \"exc=\r
+luded\": false, \"filename\": \"$filename\", \"timestamp\": 946728000, \"da=\r
+te_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subje=\r
+ct\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.=\r
+org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 200=\r
+0 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/m=\r
+ixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"conte=\r
+nt\": \"This is a test message with inline attachment with a filename\"}, {=\r
+\"id\": 3, \"content-type\": \"application/octet-stream\", \"filename\": \"=\r
+README\"}]}]}, []]]]"\r
+> +test_expect_equal_json "$output" "[[[{\"id\": \"$id\", \"match\": true, =\r
+\"excluded\": false, \"filename\": \"$filename\", \"timestamp\": 946728000,=\r
+ \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"=\r
+Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuch=\r
+mail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Ja=\r
+n 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multip=\r
+art/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"=\r
+content\": \"This is a test message with inline attachment with a filename\=\r
+"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"filename\=\r
+": \"README\"}]}]}, []]]]"\r
+>=20=20\r
+>  test_begin_subtest "Search message: json, utf-8"\r
+>  add_message "[subject]=3D\"json-search-utf8-body-s=C3=BCbj=C3=A9ct\"" "[=\r
+date]=3D\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=3D\"js=C3=B6n-search-m=\r
+=C3=A9ssage\""\r
+>  output=3D$(notmuch search --format=3Djson "js=C3=B6n-search-m=C3=A9ssage=\r
+" | notmuch_json_show_sanitize | notmuch_search_sanitize)\r
+> -test_expect_equal "$output" "[{\"thread\": \"XXX\",\r
+> +test_expect_equal_json "$output" "[{\"thread\": \"XXX\",\r
+>   \"timestamp\": 946728000,\r
+>   \"date_relative\": \"2000-01-01\",\r
+>   \"matched\": 1,\r
+> diff --git a/test/maildir-sync b/test/maildir-sync\r
+> index 01348d3..b748d04 100755\r
+> --- a/test/maildir-sync\r
+> +++ b/test/maildir-sync\r
+> @@ -4,11 +4,9 @@ test_description=3D"maildir synchronization"\r
+>=20=20\r
+>  . ./test-lib.sh\r
+>=20=20\r
+> -# Much easier to examine differences if the "notmuch show\r
+> -# --format=3Djson" output includes some newlines. Also, need to avoid\r
+> -# including the local value of MAIL_DIR in the result.\r
+> +# Avoid including the local value of MAIL_DIR in the result.\r
+>  filter_show_json() {\r
+> -    sed -e 's/, /,\n/g'  | sed -e "s|${MAIL_DIR}/|MAIL_DIR/|"\r
+> +    sed -e "s|${MAIL_DIR}/|MAIL_DIR/|"\r
+>      echo\r
+>  }\r
+>=20=20\r
+> @@ -44,7 +42,7 @@ test_expect_equal "$output" "adding-replied-tag:2,RS"\r
+>=20=20\r
+>  test_begin_subtest "notmuch show works with renamed file (without notmuc=\r
+h new)"\r
+>  output=3D$(notmuch show --format=3Djson id:${gen_msg_id} | filter_show_j=\r
+son)\r
+> -test_expect_equal "$output" '[[[{"id": "adding-replied-tag@notmuch-test-=\r
+suite",\r
+> +test_expect_equal_json "$output" '[[[{"id": "adding-replied-tag@notmuch-=\r
+test-suite",\r
+>  "match": true,\r
+>  "excluded": false,\r
+>  "filename": "MAIL_DIR/cur/adding-replied-tag:2,RS",\r
+> @@ -54,8 +52,7 @@ test_expect_equal "$output" '[[[{"id": "adding-replied-=\r
+tag@notmuch-test-suite",\r
+>  "headers": {"Subject": "Adding replied tag",\r
+>  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+>  "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+> -"Date": "Fri,\r
+> -05 Jan 2001 15:43:57 +0000"},\r
+> +"Date": "Fri, 05 Jan 2001 15:43:57 +0000"},\r
+>  "body": [{"id": 1,\r
+>  "content-type": "text/plain",\r
+>  "content": "This is just a test message (#3)\n"}]},\r
+> diff --git a/test/multipart b/test/multipart\r
+> index 72d3927..3ccf27f 100755\r
+> --- a/test/multipart\r
+> +++ b/test/multipart\r
+> @@ -334,7 +334,7 @@ cat <<EOF >EXPECTED\r
+>  {"id": 8, "content-type": "text/plain", "content": "And this message is =\r
+signed.\n\n-Carl\n"}]},=20\r
+>  {"id": 9, "content-type": "application/pgp-signature"}]}]}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D1, message body"\r
+>  notmuch show --format=3Djson --part=3D1 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -351,7 +351,7 @@ cat <<EOF >EXPECTED\r
+>  {"id": 8, "content-type": "text/plain", "content": "And this message is =\r
+signed.\n\n-Carl\n"}]},=20\r
+>  {"id": 9, "content-type": "application/pgp-signature"}]}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D2, multipart/mixed"\r
+>  notmuch show --format=3Djson --part=3D2 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -366,7 +366,7 @@ cat <<EOF >EXPECTED\r
+>  {"id": 7, "content-type": "text/plain", "filename": "attachment", "conte=\r
+nt": "This is a text attachment.\n"},=20\r
+>  {"id": 8, "content-type": "text/plain", "content": "And this message is =\r
+signed.\n\n-Carl\n"}]}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D3, rfc822 part"\r
+>  notmuch show --format=3Djson --part=3D3 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -378,7 +378,7 @@ cat <<EOF >EXPECTED\r
+>  {"id": 5, "content-type": "text/html"},=20\r
+>  {"id": 6, "content-type": "text/plain", "content": "This is an embedded =\r
+message, with a multipart/alternative part.\n"}]}]}]}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D4, rfc822's multipart/alter=\r
+native"\r
+>  notmuch show --format=3Djson --part=3D4 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -389,7 +389,7 @@ cat <<EOF >EXPECTED\r
+>  {"id": 5, "content-type": "text/html"},=20\r
+>  {"id": 6, "content-type": "text/plain", "content": "This is an embedded =\r
+message, with a multipart/alternative part.\n"}]}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D5, rfc822's html part"\r
+>  notmuch show --format=3Djson --part=3D5 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -398,7 +398,7 @@ cat <<EOF >EXPECTED\r
+>=20=20\r
+>  {"id": 5, "content-type": "text/html"}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D6, rfc822's text part"\r
+>  notmuch show --format=3Djson --part=3D6 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -407,7 +407,7 @@ cat <<EOF >EXPECTED\r
+>=20=20\r
+>  {"id": 6, "content-type": "text/plain", "content": "This is an embedded =\r
+message, with a multipart/alternative part.\n"}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D7, inline attachment"\r
+>  notmuch show --format=3Djson --part=3D7 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -416,7 +416,7 @@ cat <<EOF >EXPECTED\r
+>=20=20\r
+>  {"id": 7, "content-type": "text/plain", "filename": "attachment", "conte=\r
+nt": "This is a text attachment.\n"}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D8, plain text part"\r
+>  notmuch show --format=3Djson --part=3D8 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -425,7 +425,7 @@ cat <<EOF >EXPECTED\r
+>=20=20\r
+>  {"id": 8, "content-type": "text/plain", "content": "And this message is =\r
+signed.\n\n-Carl\n"}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--format=3Djson --part=3D9, pgp signature (unverifie=\r
+d)"\r
+>  notmuch show --format=3Djson --part=3D9 'id:87liy5ap00.fsf@yoom.home.cwo=\r
+rth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> @@ -434,7 +434,7 @@ cat <<EOF >EXPECTED\r
+>=20=20\r
+>  {"id": 9, "content-type": "application/pgp-signature"}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_expect_success \\r
+>      "--format=3Djson --part=3D10, no part, expect error" \\r
+> @@ -617,8 +617,7 @@ notmuch reply --format=3Djson 'id:87liy5ap00.fsf@yoom=\r
+.home.cworth.org' | notmuch_j\r
+>  cat <<EOF >EXPECTED\r
+>  {"reply-headers": {"Subject": "Re: Multipart message",\r
+>   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",\r
+> - "To": "Carl Worth <cworth@cworth.org>,\r
+> - cworth@cworth.org",\r
+> + "To": "Carl Worth <cworth@cworth.org>, cworth@cworth.org",\r
+>   "In-reply-to": "<87liy5ap00.fsf@yoom.home.cworth.org>",\r
+>   "References": " <87liy5ap00.fsf@yoom.home.cworth.org>"},\r
+>   "original": {"id": "XXXXX",\r
+> @@ -631,8 +630,7 @@ cat <<EOF >EXPECTED\r
+>   "headers": {"Subject": "Multipart message",\r
+>   "From": "Carl Worth <cworth@cworth.org>",\r
+>   "To": "cworth@cworth.org",\r
+> - "Date": "Fri,\r
+> - 05 Jan 2001 15:43:57 +0000"},\r
+> + "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},\r
+>   "body": [{"id": 1,\r
+>   "content-type": "multipart/signed",\r
+>   "content": [{"id": 2,\r
+> @@ -642,16 +640,14 @@ cat <<EOF >EXPECTED\r
+>   "content": [{"headers": {"Subject": "html message",\r
+>   "From": "Carl Worth <cworth@cworth.org>",\r
+>   "To": "cworth@cworth.org",\r
+> - "Date": "Fri,\r
+> - 05 Jan 2001 15:42:57 +0000"},\r
+> + "Date": "Fri, 05 Jan 2001 15:42:57 +0000"},\r
+>   "body": [{"id": 4,\r
+>   "content-type": "multipart/alternative",\r
+>   "content": [{"id": 5,\r
+>   "content-type": "text/html"},\r
+>   {"id": 6,\r
+>   "content-type": "text/plain",\r
+> - "content": "This is an embedded message,\r
+> - with a multipart/alternative part.\n"}]}]}]},\r
+> + "content": "This is an embedded message, with a multipart/alternative p=\r
+art.\n"}]}]}]},\r
+>   {"id": 7,\r
+>   "content-type": "text/plain",\r
+>   "filename": "YYYYY",\r
+> @@ -662,7 +658,7 @@ cat <<EOF >EXPECTED\r
+>   {"id": 9,\r
+>   "content-type": "application/pgp-signature"}]}]}}\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "'notmuch show --part' does not corrupt a part with C=\r
+RLF pair"\r
+>  notmuch show --format=3Draw --part=3D3 id:base64-part-with-crlf > crlf.o=\r
+ut\r
+> diff --git a/test/search-output b/test/search-output\r
+> index 8b57a43..c2a87eb 100755\r
+> --- a/test/search-output\r
+> +++ b/test/search-output\r
+> @@ -62,7 +62,7 @@ cat <<EOF >EXPECTED\r
+>  "THREADID",\r
+>  "THREADID"]\r
+>  EOF\r
+> -test_expect_equal_file OUTPUT EXPECTED\r
+> +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+>=20=20\r
+>  test_begin_subtest "--output=3Dmessages"\r
+>  notmuch search --output=3Dmessages '*' >OUTPUT\r
+> diff --git a/test/test-lib.sh b/test/test-lib.sh\r
+> index 06aaea2..791d2dc 100644\r
+> --- a/test/test-lib.sh\r
+> +++ b/test/test-lib.sh\r
+> @@ -512,6 +512,16 @@ test_expect_equal_file ()\r
+>      fi\r
+>  }\r
+>=20=20\r
+> +# Like test_expect_equal, but arguments are JSON expressions to be\r
+> +# canonicalized before diff'ing.  If an argument cannot be parsed, it\r
+> +# is used unchanged so that there's something to diff against.\r
+> +test_expect_equal_json () {\r
+> +    output=3D$(echo "$1" | python -mjson.tool || echo "$1")\r
+> +    expected=3D$(echo "$2" | python -mjson.tool || echo "$2")\r
+> +    shift 2\r
+> +    test_expect_equal "$output" "$expected" "$@"\r
+> +}\r
+> +\r
+>  test_emacs_expect_t () {\r
+>      test "$#" =3D 2 && { prereq=3D$1; shift; } || prereq=3D\r
+>      test "$#" =3D 1 ||\r
+> @@ -565,10 +575,9 @@ notmuch_show_sanitize_all ()\r
+>=20=20\r
+>  notmuch_json_show_sanitize ()\r
+>  {\r
+> -    sed -e 's|, |,\n |g' | \\r
+> -    sed \\r
+> -    -e 's|"id": "[^"]*",|"id": "XXXXX",|' \\r
+> -    -e 's|"filename": "[^"]*",|"filename": "YYYYY",|'\r
+> +    sed \\r
+> +    -e 's|"id": "[^"]*",|"id": "XXXXX",|g' \\r
+> +    -e 's|"filename": "[^"]*",|"filename": "YYYYY",|g'\r
+>  }\r
+>=20=20\r
+>  # End of notmuch helper functions\r
+> --=20\r
+> 1.7.10\r