Re: [PATCH v2 01/13] test: Uniformly canonicalize actual and expected JSON
authorAustin Clements <amdragon@MIT.EDU>
Sat, 28 Jul 2012 19:21:58 +0000 (15:21 +2000)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:48:40 +0000 (09:48 -0800)
d2/780d3b90e010c7af6ba57435426068a4390327 [new file with mode: 0644]

diff --git a/d2/780d3b90e010c7af6ba57435426068a4390327 b/d2/780d3b90e010c7af6ba57435426068a4390327
new file mode 100644 (file)
index 0000000..adb5de9
--- /dev/null
@@ -0,0 +1,559 @@
+Return-Path: <amdragon@mit.edu>\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 5B632431FAF\r
+       for <notmuch@notmuchmail.org>; Sat, 28 Jul 2012 12:22:05 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_LOW=-0.7] 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 nla4Ewd7y8rW for <notmuch@notmuchmail.org>;\r
+       Sat, 28 Jul 2012 12:22:03 -0700 (PDT)\r
+Received: from dmz-mailsec-scanner-8.mit.edu (DMZ-MAILSEC-SCANNER-8.MIT.EDU\r
+       [18.7.68.37])\r
+       by olra.theworths.org (Postfix) with ESMTP id 0D33B431FAE\r
+       for <notmuch@notmuchmail.org>; Sat, 28 Jul 2012 12:22:02 -0700 (PDT)\r
+X-AuditID: 12074425-b7f9b6d0000008c4-73-50143bda9f72\r
+Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
+       by dmz-mailsec-scanner-8.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id D6.0E.02244.ADB34105; Sat, 28 Jul 2012 15:22:02 -0400 (EDT)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+       by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q6SJM1DY001316; \r
+       Sat, 28 Jul 2012 15:22:01 -0400\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+       (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q6SJLxtm000674\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Sat, 28 Jul 2012 15:21:59 -0400 (EDT)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1SvCaM-0004Ta-S1; Sat, 28 Jul 2012 15:21:58 -0400\r
+Date: Sat, 28 Jul 2012 15:21:58 -0400\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: Mark Walters <markwalters1009@gmail.com>\r
+Subject: Re: [PATCH v2 01/13] test: Uniformly canonicalize actual and\r
+       expected JSON\r
+Message-ID: <20120728192158.GE8502@mit.edu>\r
+References: <1343449754-9010-1-git-send-email-amdragon@mit.edu>\r
+       <1343449754-9010-2-git-send-email-amdragon@mit.edu>\r
+       <87lii4uir2.fsf@qmul.ac.uk>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=iso-8859-1\r
+Content-Disposition: inline\r
+Content-Transfer-Encoding: 8bit\r
+In-Reply-To: <87lii4uir2.fsf@qmul.ac.uk>\r
+User-Agent: Mutt/1.5.21 (2010-09-15)\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFjrEKsWRmVeSWpSXmKPExsUixG6nrnvLWiTAYMMXaYu9De2MFqvn8lhc\r
+       vzmT2eLNynmsDiweO2fdZfdYvGk/m8fhrwtZPJ6tusUcwBLFZZOSmpNZllqkb5fAlfHlwVfW\r
+       gq/nGSt6dl9ga2DcsZaxi5GTQ0LARGLt2x9MELaYxIV769lAbCGBfYwSE7e4dDFyAdkbGCXO\r
+       HX/OAuGcZJJ4tuYnE4SzhFHixNa5YO0sAqoSF3sngtlsAhoS2/YvB1shIqAjcfvQAnYQm1nA\r
+       XmLa2ROsILawQKhE787lQOs4OHgFtCWONEtCzJwMtHnfT7AaXgFBiZMzn7BA9OpI7Nx6B6ye\r
+       WUBaYvk/DoiwvETz1tnMIDYn0Npd5z+BrRIVUJGYcnIb2wRG4VlIJs1CMmkWwqRZSCYtYGRZ\r
+       xSibklulm5uYmVOcmqxbnJyYl5dapGuhl5tZopeaUrqJERwxLqo7GCccUjrEKMDBqMTDe+qK\r
+       UIAQa2JZcWXuIUZJDiYlUd7P5iIBQnxJ+SmVGYnFGfFFpTmpxYcYJTiYlUR4rV8JBwjxpiRW\r
+       VqUW5cOkpDlYlMR5b6Tc9BcSSE8sSc1OTS1ILYLJynBwKEnwcgATg5BgUWp6akVaZk4JQpqJ\r
+       gxNkOA/Q8F8WQDW8xQWJucWZ6RD5U4zGHPefP7nNyHH78IvbjEIsefl5qVLivBetgEoFQEoz\r
+       SvPgpsGS3itGcaDnhHllQZbyABMm3LxXQKuYgFZZRAuBrCpJREhJNTCeVUoOmKToeW/vpGBF\r
+       obi64JP2rGUX+G4/vpJ5cUe/7qErr/wCDDl5Dvq87ynfo335mkRg5af8xWL7+gorF9V7la1h\r
+       +r39YkYCw2Kf1RLNhwpsV7uHBvNwp/By1dYxBW9+sEmh/2Sh4uP8BO5PUypfc06+N8/k898l\r
+       Np51U9Vj0pKEhUye7FViKc5INNRiLipOBAAfJ1STVQMAAA==\r
+Cc: tomi.ollila@iki.fi, notmuch@notmuchmail.org\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 19:22:05 -0000\r
+\r
+Quoth Mark Walters on Jul 28 at  2:18 pm:\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 \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='[[[{"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='[[[{"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
+> >  \r
+> > @@ -85,8 +84,7 @@ expected='[[[{"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='[[[{"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
+> >  \r
+> > @@ -119,8 +117,7 @@ expected='[[[{"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='[[[{"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='[[[{"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='[[[{"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
+> >  \r
+> > @@ -221,7 +217,7 @@ output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted m\r
+> >  expected='{"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
+> >  \r
+> > @@ -248,8 +244,7 @@ expected='[[[{"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='[[[{"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='[[[{"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='[[[{"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
+> >  \r
+> > @@ -338,8 +332,7 @@ expected='[[[{"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='[[[{"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
+> >  \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="--format=json output"\r
+> >  test_begin_subtest "Show message: json"\r
+> >  add_message "[subject]=\"json-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-show-message\""\r
+> >  output=$(notmuch show --format=json "json-show-message")\r
+> > -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"\r
+> > +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"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
+Yes, that's a good idea.  I'd rather do that in a follow-up patch that\r
+cleans up all of the expected JSON output, since even the stuff that\r
+is wrapped is wrapped strangely and not indented.\r
+\r
+> >  # This should be the same output as above.\r
+> >  test_begin_subtest "Show message: json --body=true"\r
+> >  output=$(notmuch show --format=json --body=true "json-show-message")\r
+> > -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"\r
+> > +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"\r
+> >  \r
+> >  test_begin_subtest "Show message: json --body=false"\r
+> >  output=$(notmuch show --format=json --body=false "json-show-message")\r
+> > -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}}, []]]]"\r
+> > +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}}, []]]]"\r
+> >  \r
+> >  test_begin_subtest "Search message: json"\r
+> >  add_message "[subject]=\"json-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-search-message\""\r
+> >  output=$(notmuch search --format=json "json-search-message" | 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
+> > @@ -32,7 +32,7 @@ test_expect_equal "$output" "[{\"thread\": \"XXX\",\r
+> >  test_begin_subtest "Show message: json, utf-8"\r
+> >  add_message "[subject]=\"json-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""\r
+> >  output=$(notmuch show --format=json "jsön-show-méssage")\r
+> > -test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"\r
+> > +test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"\r
+> >  \r
+> >  test_begin_subtest "Show message: json, inline attachment filename"\r
+> >  subject='json-show-inline-attachment-filename'\r
+> > @@ -45,12 +45,12 @@ emacs_deliver_message \\r
+> >       (insert \"Message-ID: <$id>\n\")"\r
+> >  output=$(notmuch show --format=json "id:$id")\r
+> >  filename=$(notmuch search --output=files "id:$id")\r
+> > -test_expect_equal "$output" "[[[{\"id\": \"$id\", \"match\": true, \"excluded\": false, \"filename\": \"$filename\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"filename\": \"README\"}]}]}, []]]]"\r
+> > +test_expect_equal_json "$output" "[[[{\"id\": \"$id\", \"match\": true, \"excluded\": false, \"filename\": \"$filename\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"filename\": \"README\"}]}]}, []]]]"\r
+> >  \r
+> >  test_begin_subtest "Search message: json, utf-8"\r
+> >  add_message "[subject]=\"json-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""\r
+> >  output=$(notmuch search --format=json "jsön-search-méssage" | 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="maildir synchronization"\r
+> >  \r
+> >  . ./test-lib.sh\r
+> >  \r
+> > -# Much easier to examine differences if the "notmuch show\r
+> > -# --format=json" 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
+> >  \r
+> > @@ -44,7 +42,7 @@ test_expect_equal "$output" "adding-replied-tag:2,RS"\r
+> >  \r
+> >  test_begin_subtest "notmuch show works with renamed file (without notmuch new)"\r
+> >  output=$(notmuch show --format=json id:${gen_msg_id} | filter_show_json)\r
+> > -test_expect_equal "$output" '[[[{"id": "adding-replied-tag@notmuch-test-suite",\r
+> > +test_expect_equal_json "$output" '[[[{"id": "adding-replied-tag@notmuch-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-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 signed.\n\n-Carl\n"}]}, \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
+> >  \r
+> >  test_begin_subtest "--format=json --part=1, message body"\r
+> >  notmuch show --format=json --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.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 signed.\n\n-Carl\n"}]}, \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
+> >  \r
+> >  test_begin_subtest "--format=json --part=2, multipart/mixed"\r
+> >  notmuch show --format=json --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.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", "content": "This is a text attachment.\n"}, \r
+> >  {"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}\r
+> >  EOF\r
+> > -test_expect_equal_file OUTPUT EXPECTED\r
+> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+> >  \r
+> >  test_begin_subtest "--format=json --part=3, rfc822 part"\r
+> >  notmuch show --format=json --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -378,7 +378,7 @@ cat <<EOF >EXPECTED\r
+> >  {"id": 5, "content-type": "text/html"}, \r
+> >  {"id": 6, "content-type": "text/plain", "content": "This is an embedded 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
+> >  \r
+> >  test_begin_subtest "--format=json --part=4, rfc822's multipart/alternative"\r
+> >  notmuch show --format=json --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -389,7 +389,7 @@ cat <<EOF >EXPECTED\r
+> >  {"id": 5, "content-type": "text/html"}, \r
+> >  {"id": 6, "content-type": "text/plain", "content": "This is an embedded 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
+> >  \r
+> >  test_begin_subtest "--format=json --part=5, rfc822's html part"\r
+> >  notmuch show --format=json --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -398,7 +398,7 @@ cat <<EOF >EXPECTED\r
+> >  \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
+> >  \r
+> >  test_begin_subtest "--format=json --part=6, rfc822's text part"\r
+> >  notmuch show --format=json --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -407,7 +407,7 @@ cat <<EOF >EXPECTED\r
+> >  \r
+> >  {"id": 6, "content-type": "text/plain", "content": "This is an embedded 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
+> >  \r
+> >  test_begin_subtest "--format=json --part=7, inline attachment"\r
+> >  notmuch show --format=json --part=7 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -416,7 +416,7 @@ cat <<EOF >EXPECTED\r
+> >  \r
+> >  {"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "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
+> >  \r
+> >  test_begin_subtest "--format=json --part=8, plain text part"\r
+> >  notmuch show --format=json --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -425,7 +425,7 @@ cat <<EOF >EXPECTED\r
+> >  \r
+> >  {"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}\r
+> >  EOF\r
+> > -test_expect_equal_file OUTPUT EXPECTED\r
+> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"\r
+> >  \r
+> >  test_begin_subtest "--format=json --part=9, pgp signature (unverified)"\r
+> >  notmuch show --format=json --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT\r
+> > @@ -434,7 +434,7 @@ cat <<EOF >EXPECTED\r
+> >  \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
+> >  \r
+> >  test_expect_success \\r
+> >      "--format=json --part=10, no part, expect error" \\r
+> > @@ -617,8 +617,7 @@ notmuch reply --format=json 'id:87liy5ap00.fsf@yoom.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 part.\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
+> >  \r
+> >  test_begin_subtest "'notmuch show --part' does not corrupt a part with CRLF pair"\r
+> >  notmuch show --format=raw --part=3 id:base64-part-with-crlf > crlf.out\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
+> >  \r
+> >  test_begin_subtest "--output=messages"\r
+> >  notmuch search --output=messages '*' >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
+> >  \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=$(echo "$1" | python -mjson.tool || echo "$1")\r
+> > +    expected=$(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 "$#" = 2 && { prereq=$1; shift; } || prereq=\r
+> >    test "$#" = 1 ||\r
+> > @@ -565,10 +575,9 @@ notmuch_show_sanitize_all ()\r
+> >  \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
+> >  \r
+> >  # End of notmuch helper functions\r