Re: [PATCH v2 01/13] test: Uniformly canonicalize actual and expected JSON

Subject: Re: [PATCH v2 01/13] test: Uniformly canonicalize actual and expected JSON

Date: Sat, 28 Jul 2012 15:21:58 -0400

To: Mark Walters

Cc: tomi.ollila@iki.fi, notmuch@notmuchmail.org

From: Austin Clements


Quoth Mark Walters on Jul 28 at  2:18 pm:
> 
> This looks good to me. I have read the test patches too now and they
> look fine. I just have one possible thought (see below) which is
> definitely not worth holding up this series for.
> 
> Best wishes 
> 
> Mark
> 
> On Sat, 28 Jul 2012, Austin Clements <amdragon@MIT.EDU> wrote:
> > Previously, we used a variety of ad-hoc canonicalizations for JSON
> > output in the test suite, but were ultimately very sensitive to JSON
> > irrelevancies such as whitespace.  This introduces a new test
> > comparison function, test_expect_equal_json, that first pretty-prints
> > *both* the actual and expected JSON and the compares the result.
> >
> > The current implementation of this simply uses Python's json.tool to
> > perform pretty-printing (with a fallback to the identity function if
> > parsing fails).  However, since the interface it introduces is
> > semantically high-level, we could swap in other mechanisms in the
> > future, such as another pretty-printer or something that does not
> > re-order object keys (if we decide that we care about that).
> >
> > In general, this patch does not remove the existing ad-hoc
> > canonicalization because it does no harm.  We do have to remove the
> > newline-after-comma rule from notmuch_json_show_sanitize and
> > filter_show_json because it results in invalid JSON that cannot be
> > pretty-printed.
> >
> > Most of this patch simply replaces test_expect_equal and
> > test_expect_equal_file with test_expect_equal_json.  It changes the
> > expected JSON in a few places where sanitizers had placed newlines
> > after commas inside strings.
> > ---
> >  test/crypto        |   37 +++++++++++++++----------------------
> >  test/json          |   14 +++++++-------
> >  test/maildir-sync  |   11 ++++-------
> >  test/multipart     |   34 +++++++++++++++-------------------
> >  test/search-output |    2 +-
> >  test/test-lib.sh   |   17 +++++++++++++----
> >  6 files changed, 55 insertions(+), 60 deletions(-)
> >
> > diff --git a/test/crypto b/test/crypto
> > index be752b1..5dd14c4 100755
> > --- a/test/crypto
> > +++ b/test/crypto
> > @@ -51,8 +51,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test signed message 001",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "sigstatus": [{"status": "good",
> >   "fingerprint": "'$FINGERPRINT'",
> > @@ -64,7 +63,7 @@ expected='[[[{"id": "XXXXX",
> >   {"id": 3,
> >   "content-type": "application/pgp-signature"}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  
> > @@ -85,8 +84,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test signed message 001",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "sigstatus": [{"status": "good",
> >   "fingerprint": "'$FINGERPRINT'",
> > @@ -99,7 +97,7 @@ expected='[[[{"id": "XXXXX",
> >   {"id": 3,
> >   "content-type": "application/pgp-signature"}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  
> > @@ -119,8 +117,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test signed message 001",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "sigstatus": [{"status": "error",
> >   "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",
> > @@ -132,7 +129,7 @@ expected='[[[{"id": "XXXXX",
> >   {"id": 3,
> >   "content-type": "application/pgp-signature"}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  mv "${GNUPGHOME}"{.bak,}
> > @@ -193,8 +190,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test encrypted message 001",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "encstatus": [{"status": "good"}],
> >   "sigstatus": [],
> > @@ -210,7 +206,7 @@ expected='[[[{"id": "XXXXX",
> >   "content-type": "application/octet-stream",
> >   "filename": "TESTATTACHMENT"}]}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  
> > @@ -221,7 +217,7 @@ output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted m
> >  expected='{"id": 4,
> >   "content-type": "text/plain",
> >   "content": "This is a test encrypted message.\n"}'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  
> > @@ -248,8 +244,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test encrypted message 001",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "encstatus": [{"status": "bad"}],
> >   "content-type": "multipart/encrypted",
> > @@ -258,7 +253,7 @@ expected='[[[{"id": "XXXXX",
> >   {"id": 3,
> >   "content-type": "application/octet-stream"}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  mv "${GNUPGHOME}"{.bak,}
> > @@ -283,8 +278,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test encrypted message 002",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "encstatus": [{"status": "good"}],
> >   "sigstatus": [{"status": "good",
> > @@ -298,7 +292,7 @@ expected='[[[{"id": "XXXXX",
> >   "content-type": "text/plain",
> >   "content": "This is another test encrypted message.\n"}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  
> > @@ -338,8 +332,7 @@ expected='[[[{"id": "XXXXX",
> >   "headers": {"Subject": "test signed message 001",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >   "To": "test_suite@notmuchmail.org",
> > - "Date": "Sat,
> > - 01 Jan 2000 12:00:00 +0000"},
> > + "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
> >   "body": [{"id": 1,
> >   "sigstatus": [{"status": "error",
> >   "keyid": "6D92612D94E46381",
> > @@ -351,7 +344,7 @@ expected='[[[{"id": "XXXXX",
> >   {"id": 3,
> >   "content-type": "application/pgp-signature"}]}]},
> >   []]]]'
> > -test_expect_equal \
> > +test_expect_equal_json \
> >      "$output" \
> >      "$expected"
> >  
> > diff --git a/test/json b/test/json
> > index 831e105..d86ee46 100755
> > --- a/test/json
> > +++ b/test/json
> > @@ -5,21 +5,21 @@ test_description="--format=json output"
> >  test_begin_subtest "Show message: json"
> >  add_message "[subject]=\"json-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-show-message\""
> >  output=$(notmuch show --format=json "json-show-message")
> > -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\"}]}, []]]]"
> > +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\"}]}, []]]]"
> 
> Since test_expect_equal_json does not care about whitespace (outside of
> strings) would it be worth splitting the expected output for each of
> these tests into multiple lines?

Yes, that's a good idea.  I'd rather do that in a follow-up patch that
cleans up all of the expected JSON output, since even the stuff that
is wrapped is wrapped strangely and not indented.

> >  # This should be the same output as above.
> >  test_begin_subtest "Show message: json --body=true"
> >  output=$(notmuch show --format=json --body=true "json-show-message")
> > -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\"}]}, []]]]"
> > +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\"}]}, []]]]"
> >  
> >  test_begin_subtest "Show message: json --body=false"
> >  output=$(notmuch show --format=json --body=false "json-show-message")
> > -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\"}}, []]]]"
> > +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\"}}, []]]]"
> >  
> >  test_begin_subtest "Search message: json"
> >  add_message "[subject]=\"json-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-search-message\""
> >  output=$(notmuch search --format=json "json-search-message" | notmuch_json_show_sanitize | notmuch_search_sanitize)
> > -test_expect_equal "$output" "[{\"thread\": \"XXX\",
> > +test_expect_equal_json "$output" "[{\"thread\": \"XXX\",
> >   \"timestamp\": 946728000,
> >   \"date_relative\": \"2000-01-01\",
> >   \"matched\": 1,
> > @@ -32,7 +32,7 @@ test_expect_equal "$output" "[{\"thread\": \"XXX\",
> >  test_begin_subtest "Show message: json, utf-8"
> >  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\""
> >  output=$(notmuch show --format=json "jsön-show-méssage")
> > -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\"}]}, []]]]"
> > +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\"}]}, []]]]"
> >  
> >  test_begin_subtest "Show message: json, inline attachment filename"
> >  subject='json-show-inline-attachment-filename'
> > @@ -45,12 +45,12 @@ emacs_deliver_message \
> >       (insert \"Message-ID: <$id>\n\")"
> >  output=$(notmuch show --format=json "id:$id")
> >  filename=$(notmuch search --output=files "id:$id")
> > -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\"}]}]}, []]]]"
> > +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\"}]}]}, []]]]"
> >  
> >  test_begin_subtest "Search message: json, utf-8"
> >  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\""
> >  output=$(notmuch search --format=json "jsön-search-méssage" | notmuch_json_show_sanitize | notmuch_search_sanitize)
> > -test_expect_equal "$output" "[{\"thread\": \"XXX\",
> > +test_expect_equal_json "$output" "[{\"thread\": \"XXX\",
> >   \"timestamp\": 946728000,
> >   \"date_relative\": \"2000-01-01\",
> >   \"matched\": 1,
> > diff --git a/test/maildir-sync b/test/maildir-sync
> > index 01348d3..b748d04 100755
> > --- a/test/maildir-sync
> > +++ b/test/maildir-sync
> > @@ -4,11 +4,9 @@ test_description="maildir synchronization"
> >  
> >  . ./test-lib.sh
> >  
> > -# Much easier to examine differences if the "notmuch show
> > -# --format=json" output includes some newlines. Also, need to avoid
> > -# including the local value of MAIL_DIR in the result.
> > +# Avoid including the local value of MAIL_DIR in the result.
> >  filter_show_json() {
> > -    sed -e 's/, /,\n/g'  | sed -e "s|${MAIL_DIR}/|MAIL_DIR/|"
> > +    sed -e "s|${MAIL_DIR}/|MAIL_DIR/|"
> >      echo
> >  }
> >  
> > @@ -44,7 +42,7 @@ test_expect_equal "$output" "adding-replied-tag:2,RS"
> >  
> >  test_begin_subtest "notmuch show works with renamed file (without notmuch new)"
> >  output=$(notmuch show --format=json id:${gen_msg_id} | filter_show_json)
> > -test_expect_equal "$output" '[[[{"id": "adding-replied-tag@notmuch-test-suite",
> > +test_expect_equal_json "$output" '[[[{"id": "adding-replied-tag@notmuch-test-suite",
> >  "match": true,
> >  "excluded": false,
> >  "filename": "MAIL_DIR/cur/adding-replied-tag:2,RS",
> > @@ -54,8 +52,7 @@ test_expect_equal "$output" '[[[{"id": "adding-replied-tag@notmuch-test-suite",
> >  "headers": {"Subject": "Adding replied tag",
> >  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> >  "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> > -"Date": "Fri,
> > -05 Jan 2001 15:43:57 +0000"},
> > +"Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
> >  "body": [{"id": 1,
> >  "content-type": "text/plain",
> >  "content": "This is just a test message (#3)\n"}]},
> > diff --git a/test/multipart b/test/multipart
> > index 72d3927..3ccf27f 100755
> > --- a/test/multipart
> > +++ b/test/multipart
> > @@ -334,7 +334,7 @@ cat <<EOF >EXPECTED
> >  {"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}, 
> >  {"id": 9, "content-type": "application/pgp-signature"}]}]}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=1, message body"
> >  notmuch show --format=json --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -351,7 +351,7 @@ cat <<EOF >EXPECTED
> >  {"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}, 
> >  {"id": 9, "content-type": "application/pgp-signature"}]}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=2, multipart/mixed"
> >  notmuch show --format=json --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -366,7 +366,7 @@ cat <<EOF >EXPECTED
> >  {"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, 
> >  {"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=3, rfc822 part"
> >  notmuch show --format=json --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -378,7 +378,7 @@ cat <<EOF >EXPECTED
> >  {"id": 5, "content-type": "text/html"}, 
> >  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=4, rfc822's multipart/alternative"
> >  notmuch show --format=json --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -389,7 +389,7 @@ cat <<EOF >EXPECTED
> >  {"id": 5, "content-type": "text/html"}, 
> >  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=5, rfc822's html part"
> >  notmuch show --format=json --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -398,7 +398,7 @@ cat <<EOF >EXPECTED
> >  
> >  {"id": 5, "content-type": "text/html"}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=6, rfc822's text part"
> >  notmuch show --format=json --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -407,7 +407,7 @@ cat <<EOF >EXPECTED
> >  
> >  {"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=7, inline attachment"
> >  notmuch show --format=json --part=7 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -416,7 +416,7 @@ cat <<EOF >EXPECTED
> >  
> >  {"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=8, plain text part"
> >  notmuch show --format=json --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -425,7 +425,7 @@ cat <<EOF >EXPECTED
> >  
> >  {"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--format=json --part=9, pgp signature (unverified)"
> >  notmuch show --format=json --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' | sed 's|{"id":|\n{"id":|g' >OUTPUT
> > @@ -434,7 +434,7 @@ cat <<EOF >EXPECTED
> >  
> >  {"id": 9, "content-type": "application/pgp-signature"}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_expect_success \
> >      "--format=json --part=10, no part, expect error" \
> > @@ -617,8 +617,7 @@ notmuch reply --format=json 'id:87liy5ap00.fsf@yoom.home.cworth.org' | notmuch_j
> >  cat <<EOF >EXPECTED
> >  {"reply-headers": {"Subject": "Re: Multipart message",
> >   "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
> > - "To": "Carl Worth <cworth@cworth.org>,
> > - cworth@cworth.org",
> > + "To": "Carl Worth <cworth@cworth.org>, cworth@cworth.org",
> >   "In-reply-to": "<87liy5ap00.fsf@yoom.home.cworth.org>",
> >   "References": " <87liy5ap00.fsf@yoom.home.cworth.org>"},
> >   "original": {"id": "XXXXX",
> > @@ -631,8 +630,7 @@ cat <<EOF >EXPECTED
> >   "headers": {"Subject": "Multipart message",
> >   "From": "Carl Worth <cworth@cworth.org>",
> >   "To": "cworth@cworth.org",
> > - "Date": "Fri,
> > - 05 Jan 2001 15:43:57 +0000"},
> > + "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
> >   "body": [{"id": 1,
> >   "content-type": "multipart/signed",
> >   "content": [{"id": 2,
> > @@ -642,16 +640,14 @@ cat <<EOF >EXPECTED
> >   "content": [{"headers": {"Subject": "html message",
> >   "From": "Carl Worth <cworth@cworth.org>",
> >   "To": "cworth@cworth.org",
> > - "Date": "Fri,
> > - 05 Jan 2001 15:42:57 +0000"},
> > + "Date": "Fri, 05 Jan 2001 15:42:57 +0000"},
> >   "body": [{"id": 4,
> >   "content-type": "multipart/alternative",
> >   "content": [{"id": 5,
> >   "content-type": "text/html"},
> >   {"id": 6,
> >   "content-type": "text/plain",
> > - "content": "This is an embedded message,
> > - with a multipart/alternative part.\n"}]}]}]},
> > + "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]},
> >   {"id": 7,
> >   "content-type": "text/plain",
> >   "filename": "YYYYY",
> > @@ -662,7 +658,7 @@ cat <<EOF >EXPECTED
> >   {"id": 9,
> >   "content-type": "application/pgp-signature"}]}]}}
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "'notmuch show --part' does not corrupt a part with CRLF pair"
> >  notmuch show --format=raw --part=3 id:base64-part-with-crlf > crlf.out
> > diff --git a/test/search-output b/test/search-output
> > index 8b57a43..c2a87eb 100755
> > --- a/test/search-output
> > +++ b/test/search-output
> > @@ -62,7 +62,7 @@ cat <<EOF >EXPECTED
> >  "THREADID",
> >  "THREADID"]
> >  EOF
> > -test_expect_equal_file OUTPUT EXPECTED
> > +test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
> >  
> >  test_begin_subtest "--output=messages"
> >  notmuch search --output=messages '*' >OUTPUT
> > diff --git a/test/test-lib.sh b/test/test-lib.sh
> > index 06aaea2..791d2dc 100644
> > --- a/test/test-lib.sh
> > +++ b/test/test-lib.sh
> > @@ -512,6 +512,16 @@ test_expect_equal_file ()
> >      fi
> >  }
> >  
> > +# Like test_expect_equal, but arguments are JSON expressions to be
> > +# canonicalized before diff'ing.  If an argument cannot be parsed, it
> > +# is used unchanged so that there's something to diff against.
> > +test_expect_equal_json () {
> > +    output=$(echo "$1" | python -mjson.tool || echo "$1")
> > +    expected=$(echo "$2" | python -mjson.tool || echo "$2")
> > +    shift 2
> > +    test_expect_equal "$output" "$expected" "$@"
> > +}
> > +
> >  test_emacs_expect_t () {
> >  	test "$#" = 2 && { prereq=$1; shift; } || prereq=
> >  	test "$#" = 1 ||
> > @@ -565,10 +575,9 @@ notmuch_show_sanitize_all ()
> >  
> >  notmuch_json_show_sanitize ()
> >  {
> > -    sed -e 's|, |,\n |g' | \
> > -	sed \
> > -	-e 's|"id": "[^"]*",|"id": "XXXXX",|' \
> > -	-e 's|"filename": "[^"]*",|"filename": "YYYYY",|'
> > +    sed \
> > +	-e 's|"id": "[^"]*",|"id": "XXXXX",|g' \
> > +	-e 's|"filename": "[^"]*",|"filename": "YYYYY",|g'
> >  }
> >  
> >  # End of notmuch helper functions

Thread: