# Copyright 2007 Baptiste Lepilleur and The JsonCpp Authors # Distributed under MIT license, or public domain if desired and # recognized in your jurisdiction. # See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE from __future__ import print_function from __future__ import unicode_literals from io import open from glob import glob import sys import os import os.path import optparse VALGRIND_CMD = 'valgrind --tool=memcheck --leak-check=yes --undef-value-errors=yes ' def getStatusOutput(cmd): """ Return int, unicode (for both Python 2 and 3). Note: os.popen().close() would return None for 0. """ print(cmd, file=sys.stderr) pipe = os.popen(cmd) process_output = pipe.read() try: # We have been using os.popen(). When we read() the result # we get 'str' (bytes) in py2, and 'str' (unicode) in py3. # Ugh! There must be a better way to handle this. process_output = process_output.decode('utf-8') except AttributeError: pass # python3 status = pipe.close() return status, process_output def compareOutputs(expected, actual, message): expected = expected.strip().replace('\r','').split('\n') actual = actual.strip().replace('\r','').split('\n') diff_line = 0 max_line_to_compare = min(len(expected), len(actual)) for index in range(0,max_line_to_compare): if expected[index].strip() != actual[index].strip(): diff_line = index + 1 break if diff_line == 0 and len(expected) != len(actual): diff_line = max_line_to_compare+1 if diff_line == 0: return None def safeGetLine(lines, index): index += -1 if index >= len(lines): return '' return lines[index].strip() return """ Difference in %s at line %d: Expected: '%s' Actual: '%s' """ % (message, diff_line, safeGetLine(expected,diff_line), safeGetLine(actual,diff_line)) def safeReadFile(path): try: return open(path, 'rt', encoding = 'utf-8').read() except IOError as e: return '' % (path,e) class FailError(Exception): def __init__(self, msg): super(Exception, self).__init__(msg) def runAllTests(jsontest_executable_path, input_dir = None, use_valgrind=False, with_json_checker=False, writerClass='StyledWriter'): if not input_dir: input_dir = os.path.join(os.getcwd(), 'data') tests = glob(os.path.join(input_dir, '*.json')) if with_json_checker: all_tests = glob(os.path.join(input_dir, '../jsonchecker', '*.json')) # These tests fail with strict json support, but pass with JsonCPP's # extra leniency features. When adding a new exclusion to this list, # remember to add the test's number and reasoning here: known = ["fail{}.json".format(n) for n in [ 4, 9, # fail because we allow trailing commas 7, # fails because we allow commas after close 8, # fails because we allow extra close 10, # fails because we allow extra values after close 13, # fails because we allow leading zeroes in numbers 18, # fails because we allow deeply nested values 25, # fails because we allow tab characters in strings 27, # fails because we allow string line breaks ]] test_jsonchecker = [ test for test in all_tests if os.path.basename(test) not in known] else: test_jsonchecker = [] failed_tests = [] valgrind_path = use_valgrind and VALGRIND_CMD or '' for input_path in tests + test_jsonchecker: expect_failure = os.path.basename(input_path).startswith('fail') is_json_checker_test = (input_path in test_jsonchecker) or expect_failure print('TESTING:', input_path, end=' ') options = is_json_checker_test and '--json-checker' or '' options += ' --json-writer %s'%writerClass cmd = '%s%s %s "%s"' % ( valgrind_path, jsontest_executable_path, options, input_path) status, process_output = getStatusOutput(cmd) if is_json_checker_test: if expect_failure: if not status: print('FAILED') failed_tests.append((input_path, 'Parsing should have failed:\n%s' % safeReadFile(input_path))) else: print('OK') else: if status: print('FAILED') failed_tests.append((input_path, 'Parsing failed:\n' + process_output)) else: print('OK') else: base_path = os.path.splitext(input_path)[0] actual_output = safeReadFile(base_path + '.actual') actual_rewrite_output = safeReadFile(base_path + '.actual-rewrite') open(base_path + '.process-output', 'wt', encoding = 'utf-8').write(process_output) if status: print('parsing failed') failed_tests.append((input_path, 'Parsing failed:\n' + process_output)) else: expected_output_path = os.path.splitext(input_path)[0] + '.expected' expected_output = open(expected_output_path, 'rt', encoding = 'utf-8').read() detail = (compareOutputs(expected_output, actual_output, 'input') or compareOutputs(expected_output, actual_rewrite_output, 'rewrite')) if detail: print('FAILED') failed_tests.append((input_path, detail)) else: print('OK') if failed_tests: print() print('Failure details:') for failed_test in failed_tests: print('* Test', failed_test[0]) print(failed_test[1]) print() print('Test results: %d passed, %d failed.' % (len(tests)-len(failed_tests), len(failed_tests))) raise FailError(repr(failed_tests)) else: print('All %d tests passed.' % len(tests)) def main(): from optparse import OptionParser parser = OptionParser(usage="%prog [options] [test case directory]") parser.add_option("--valgrind", action="store_true", dest="valgrind", default=False, help="run all the tests using valgrind to detect memory leaks") parser.add_option("-c", "--with-json-checker", action="store_true", dest="with_json_checker", default=False, help="run all the tests from the official JSONChecker test suite of json.org") parser.enable_interspersed_args() options, args = parser.parse_args() if len(args) < 1 or len(args) > 2: parser.error('Must provides at least path to jsontestrunner executable.') sys.exit(1) jsontest_executable_path = os.path.normpath(os.path.abspath(args[0])) if len(args) > 1: input_path = os.path.normpath(os.path.abspath(args[1])) else: input_path = None runAllTests(jsontest_executable_path, input_path, use_valgrind=options.valgrind, with_json_checker=options.with_json_checker, writerClass='StyledWriter') runAllTests(jsontest_executable_path, input_path, use_valgrind=options.valgrind, with_json_checker=options.with_json_checker, writerClass='StyledStreamWriter') runAllTests(jsontest_executable_path, input_path, use_valgrind=options.valgrind, with_json_checker=options.with_json_checker, writerClass='BuiltStyledStreamWriter') if __name__ == '__main__': try: main() except FailError: sys.exit(1)