mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-12-22 17:34:54 +01:00
test: new test framework to compare json parts
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 <dkg@fifthhorseman.net>
This commit is contained in:
parent
a6b0772b60
commit
03839a8110
2 changed files with 138 additions and 0 deletions
114
test/json_check_nodes.py
Executable file
114
test/json_check_nodes.py
Executable file
|
@ -0,0 +1,114 @@
|
|||
#!/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"] = 1
|
||||
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).__name__, 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)
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue