From: Jameson Graef Rollins <jroll...@finestructure.net> This makes it easier to write fairly compact, readable tests of json output, without needing to sanitize away parts that we don't care about.
Signed-off-by: Daniel Kahn Gillmor <d...@fifthhorseman.net> --- test/json_check_nodes.py | 113 +++++++++++++++++++++++++++++++++++++++ test/test-lib.sh | 24 +++++++++ 2 files changed, 137 insertions(+) create mode 100755 test/json_check_nodes.py diff --git a/test/json_check_nodes.py b/test/json_check_nodes.py new file mode 100755 index 00000000..a622969a --- /dev/null +++ b/test/json_check_nodes.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +import re +import sys +import json + + +EXPR_RE = re.compile('(?P<label>[a-zA-Z0-9_-]+):(?P<address>[^=!]+)(?:(?P<type>[=!])(?P<val>.*))?', re.DOTALL|re.MULTILINE) + + +if len(sys.argv) < 2: + sys.exit('usage: '+ sys.argv[0] + """ EXPR [EXPR] + +Takes json data on stdin and evaluates test expressions specified in +arguments. Each test is evaluated, and output is printed only if the +test fails. If any test fails the return value of execution will be +non-zero. + +EXPR can be one of following types: + +Value test: test that object in json data found at address is equal to specified value: + + label:address=value + +Existence test: test that dict or list in json data found at address +does *not* contain the specified key: + + label:address!key + +Extract: extract object from json data found at address and print + + label:address + +Results are printed to stdout prefixed by expression label. In all +cases the test will fail if object does not exist in data. + +Example: + +0 $ echo '["a", "b", {"c": 1}]' | python3 json_check_nodes.py 'second_d:[1]="d"' 'no_c:[2]!"c"' +second_d: value not equal: data[1] = 'b' != 'd' +no_c: dict contains key: data[2]["c"] = "c" +1 $ + +""") + + +# parse expressions from arguments +exprs = [] +for expr in sys.argv[1:]: + m = re.match(EXPR_RE, expr) + if not m: + sys.exit("Invalid expression: {}".format(expr)) + exprs.append(m) + +data = json.load(sys.stdin) + +fail = False + +for expr in exprs: + # print(expr.groups(),fail) + + e = 'data{}'.format(expr.group('address')) + try: + val = eval(e) + except SyntaxError: + fail = True + print("{}: syntax error on evaluation of object: {}".format( + expr.group('label'), e)) + continue + except: + fail = True + print("{}: object not found: data{}".format( + expr.group('label'), expr.group('address'))) + continue + + if expr.group('type') == '=': + try: + obj_val = json.loads(expr.group('val')) + except: + fail = True + print("{}: error evaluating value: {}".format( + expr.group('label'), expr.group('address'))) + continue + if val != obj_val: + fail = True + print("{}: value not equal: data{} = {} != {}".format( + expr.group('label'), expr.group('address'), repr(val), repr(obj_val))) + + elif expr.group('type') == '!': + if not isinstance(val, (dict, list)): + fail = True + print("{}: not a dict or a list: data{}".format( + expr.group('label'), expr.group('address'))) + continue + try: + idx = json.loads(expr.group('val')) + if idx in val: + fail = True + print("{}: {} contains key: {}[{}] = {}".format( + expr.group('label'), type(val), e, expr.group('val'), val[idx])) + except SyntaxError: + fail = True + print("{}: syntax error on evaluation of value: {}".format( + expr.group('label'), expr.group('val'))) + continue + + + elif expr.group('type') is None: + print("{}: {}".format(expr.group('label'), val)) + + +if fail: + sys.exit(1) +sys.exit(0) diff --git a/test/test-lib.sh b/test/test-lib.sh index ff18fae6..616cb674 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -507,6 +507,30 @@ test_sort_json () { "import sys, json; json.dump(sorted(json.load(sys.stdin)),sys.stdout)" } +# test for json objects: +# read the source of test/json_check_nodes.py (or the output when +# invoking it without arguments) for an explanation of the syntax. +test_json_nodes () { + exec 1>&6 2>&7 # Restore stdout and stderr + if [ -z "$inside_subtest" ]; then + error "bug in the test script: test_json_eval without test_begin_subtest" + fi + inside_subtest= + test "$#" > 0 || + error "bug in the test script: test_json_nodes needs at least 1 parameter" + + if ! test_skip "$test_subtest_name" + then + output=$(PYTHONIOENCODING=utf-8 $NOTMUCH_PYTHON "$TEST_DIRECTORY"/json_check_nodes.py "$@") + if [ "$?" = 0 ] + then + test_ok_ + else + test_failure_ "$output" + fi + fi +} + test_emacs_expect_t () { test "$#" = 1 || error "bug in the test script: not 1 parameter to test_emacs_expect_t" -- 2.20.1 _______________________________________________ notmuch mailing list notmuch@notmuchmail.org https://notmuchmail.org/mailman/listinfo/notmuch