1*44844408SAndroid Build Coastguard Worker# Copyright 2007 Baptiste Lepilleur and The JsonCpp Authors 2*44844408SAndroid Build Coastguard Worker# Distributed under MIT license, or public domain if desired and 3*44844408SAndroid Build Coastguard Worker# recognized in your jurisdiction. 4*44844408SAndroid Build Coastguard Worker# See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE 5*44844408SAndroid Build Coastguard Worker 6*44844408SAndroid Build Coastguard Workerfrom __future__ import print_function 7*44844408SAndroid Build Coastguard Workerfrom __future__ import unicode_literals 8*44844408SAndroid Build Coastguard Workerfrom io import open 9*44844408SAndroid Build Coastguard Workerfrom glob import glob 10*44844408SAndroid Build Coastguard Workerimport sys 11*44844408SAndroid Build Coastguard Workerimport os 12*44844408SAndroid Build Coastguard Workerimport os.path 13*44844408SAndroid Build Coastguard Workerimport optparse 14*44844408SAndroid Build Coastguard Worker 15*44844408SAndroid Build Coastguard WorkerVALGRIND_CMD = 'valgrind --tool=memcheck --leak-check=yes --undef-value-errors=yes ' 16*44844408SAndroid Build Coastguard Worker 17*44844408SAndroid Build Coastguard Workerdef getStatusOutput(cmd): 18*44844408SAndroid Build Coastguard Worker """ 19*44844408SAndroid Build Coastguard Worker Return int, unicode (for both Python 2 and 3). 20*44844408SAndroid Build Coastguard Worker Note: os.popen().close() would return None for 0. 21*44844408SAndroid Build Coastguard Worker """ 22*44844408SAndroid Build Coastguard Worker print(cmd, file=sys.stderr) 23*44844408SAndroid Build Coastguard Worker pipe = os.popen(cmd) 24*44844408SAndroid Build Coastguard Worker process_output = pipe.read() 25*44844408SAndroid Build Coastguard Worker try: 26*44844408SAndroid Build Coastguard Worker # We have been using os.popen(). When we read() the result 27*44844408SAndroid Build Coastguard Worker # we get 'str' (bytes) in py2, and 'str' (unicode) in py3. 28*44844408SAndroid Build Coastguard Worker # Ugh! There must be a better way to handle this. 29*44844408SAndroid Build Coastguard Worker process_output = process_output.decode('utf-8') 30*44844408SAndroid Build Coastguard Worker except AttributeError: 31*44844408SAndroid Build Coastguard Worker pass # python3 32*44844408SAndroid Build Coastguard Worker status = pipe.close() 33*44844408SAndroid Build Coastguard Worker return status, process_output 34*44844408SAndroid Build Coastguard Workerdef compareOutputs(expected, actual, message): 35*44844408SAndroid Build Coastguard Worker expected = expected.strip().replace('\r','').split('\n') 36*44844408SAndroid Build Coastguard Worker actual = actual.strip().replace('\r','').split('\n') 37*44844408SAndroid Build Coastguard Worker diff_line = 0 38*44844408SAndroid Build Coastguard Worker max_line_to_compare = min(len(expected), len(actual)) 39*44844408SAndroid Build Coastguard Worker for index in range(0,max_line_to_compare): 40*44844408SAndroid Build Coastguard Worker if expected[index].strip() != actual[index].strip(): 41*44844408SAndroid Build Coastguard Worker diff_line = index + 1 42*44844408SAndroid Build Coastguard Worker break 43*44844408SAndroid Build Coastguard Worker if diff_line == 0 and len(expected) != len(actual): 44*44844408SAndroid Build Coastguard Worker diff_line = max_line_to_compare+1 45*44844408SAndroid Build Coastguard Worker if diff_line == 0: 46*44844408SAndroid Build Coastguard Worker return None 47*44844408SAndroid Build Coastguard Worker def safeGetLine(lines, index): 48*44844408SAndroid Build Coastguard Worker index += -1 49*44844408SAndroid Build Coastguard Worker if index >= len(lines): 50*44844408SAndroid Build Coastguard Worker return '' 51*44844408SAndroid Build Coastguard Worker return lines[index].strip() 52*44844408SAndroid Build Coastguard Worker return """ Difference in %s at line %d: 53*44844408SAndroid Build Coastguard Worker Expected: '%s' 54*44844408SAndroid Build Coastguard Worker Actual: '%s' 55*44844408SAndroid Build Coastguard Worker""" % (message, diff_line, 56*44844408SAndroid Build Coastguard Worker safeGetLine(expected,diff_line), 57*44844408SAndroid Build Coastguard Worker safeGetLine(actual,diff_line)) 58*44844408SAndroid Build Coastguard Worker 59*44844408SAndroid Build Coastguard Workerdef safeReadFile(path): 60*44844408SAndroid Build Coastguard Worker try: 61*44844408SAndroid Build Coastguard Worker return open(path, 'rt', encoding = 'utf-8').read() 62*44844408SAndroid Build Coastguard Worker except IOError as e: 63*44844408SAndroid Build Coastguard Worker return '<File "%s" is missing: %s>' % (path,e) 64*44844408SAndroid Build Coastguard Worker 65*44844408SAndroid Build Coastguard Workerclass FailError(Exception): 66*44844408SAndroid Build Coastguard Worker def __init__(self, msg): 67*44844408SAndroid Build Coastguard Worker super(Exception, self).__init__(msg) 68*44844408SAndroid Build Coastguard Worker 69*44844408SAndroid Build Coastguard Workerdef runAllTests(jsontest_executable_path, input_dir = None, 70*44844408SAndroid Build Coastguard Worker use_valgrind=False, with_json_checker=False, 71*44844408SAndroid Build Coastguard Worker writerClass='StyledWriter'): 72*44844408SAndroid Build Coastguard Worker if not input_dir: 73*44844408SAndroid Build Coastguard Worker input_dir = os.path.join(os.getcwd(), 'data') 74*44844408SAndroid Build Coastguard Worker tests = glob(os.path.join(input_dir, '*.json')) 75*44844408SAndroid Build Coastguard Worker if with_json_checker: 76*44844408SAndroid Build Coastguard Worker all_tests = glob(os.path.join(input_dir, '../jsonchecker', '*.json')) 77*44844408SAndroid Build Coastguard Worker # These tests fail with strict json support, but pass with JsonCPP's 78*44844408SAndroid Build Coastguard Worker # extra leniency features. When adding a new exclusion to this list, 79*44844408SAndroid Build Coastguard Worker # remember to add the test's number and reasoning here: 80*44844408SAndroid Build Coastguard Worker known = ["fail{}.json".format(n) for n in [ 81*44844408SAndroid Build Coastguard Worker 4, 9, # fail because we allow trailing commas 82*44844408SAndroid Build Coastguard Worker 7, # fails because we allow commas after close 83*44844408SAndroid Build Coastguard Worker 8, # fails because we allow extra close 84*44844408SAndroid Build Coastguard Worker 10, # fails because we allow extra values after close 85*44844408SAndroid Build Coastguard Worker 13, # fails because we allow leading zeroes in numbers 86*44844408SAndroid Build Coastguard Worker 18, # fails because we allow deeply nested values 87*44844408SAndroid Build Coastguard Worker 25, # fails because we allow tab characters in strings 88*44844408SAndroid Build Coastguard Worker 27, # fails because we allow string line breaks 89*44844408SAndroid Build Coastguard Worker ]] 90*44844408SAndroid Build Coastguard Worker test_jsonchecker = [ test for test in all_tests 91*44844408SAndroid Build Coastguard Worker if os.path.basename(test) not in known] 92*44844408SAndroid Build Coastguard Worker 93*44844408SAndroid Build Coastguard Worker else: 94*44844408SAndroid Build Coastguard Worker test_jsonchecker = [] 95*44844408SAndroid Build Coastguard Worker 96*44844408SAndroid Build Coastguard Worker failed_tests = [] 97*44844408SAndroid Build Coastguard Worker valgrind_path = use_valgrind and VALGRIND_CMD or '' 98*44844408SAndroid Build Coastguard Worker for input_path in tests + test_jsonchecker: 99*44844408SAndroid Build Coastguard Worker expect_failure = os.path.basename(input_path).startswith('fail') 100*44844408SAndroid Build Coastguard Worker is_json_checker_test = (input_path in test_jsonchecker) or expect_failure 101*44844408SAndroid Build Coastguard Worker print('TESTING:', input_path, end=' ') 102*44844408SAndroid Build Coastguard Worker options = is_json_checker_test and '--json-checker' or '' 103*44844408SAndroid Build Coastguard Worker options += ' --json-writer %s'%writerClass 104*44844408SAndroid Build Coastguard Worker cmd = '%s%s %s "%s"' % ( valgrind_path, jsontest_executable_path, options, 105*44844408SAndroid Build Coastguard Worker input_path) 106*44844408SAndroid Build Coastguard Worker status, process_output = getStatusOutput(cmd) 107*44844408SAndroid Build Coastguard Worker if is_json_checker_test: 108*44844408SAndroid Build Coastguard Worker if expect_failure: 109*44844408SAndroid Build Coastguard Worker if not status: 110*44844408SAndroid Build Coastguard Worker print('FAILED') 111*44844408SAndroid Build Coastguard Worker failed_tests.append((input_path, 'Parsing should have failed:\n%s' % 112*44844408SAndroid Build Coastguard Worker safeReadFile(input_path))) 113*44844408SAndroid Build Coastguard Worker else: 114*44844408SAndroid Build Coastguard Worker print('OK') 115*44844408SAndroid Build Coastguard Worker else: 116*44844408SAndroid Build Coastguard Worker if status: 117*44844408SAndroid Build Coastguard Worker print('FAILED') 118*44844408SAndroid Build Coastguard Worker failed_tests.append((input_path, 'Parsing failed:\n' + process_output)) 119*44844408SAndroid Build Coastguard Worker else: 120*44844408SAndroid Build Coastguard Worker print('OK') 121*44844408SAndroid Build Coastguard Worker else: 122*44844408SAndroid Build Coastguard Worker base_path = os.path.splitext(input_path)[0] 123*44844408SAndroid Build Coastguard Worker actual_output = safeReadFile(base_path + '.actual') 124*44844408SAndroid Build Coastguard Worker actual_rewrite_output = safeReadFile(base_path + '.actual-rewrite') 125*44844408SAndroid Build Coastguard Worker open(base_path + '.process-output', 'wt', encoding = 'utf-8').write(process_output) 126*44844408SAndroid Build Coastguard Worker if status: 127*44844408SAndroid Build Coastguard Worker print('parsing failed') 128*44844408SAndroid Build Coastguard Worker failed_tests.append((input_path, 'Parsing failed:\n' + process_output)) 129*44844408SAndroid Build Coastguard Worker else: 130*44844408SAndroid Build Coastguard Worker expected_output_path = os.path.splitext(input_path)[0] + '.expected' 131*44844408SAndroid Build Coastguard Worker expected_output = open(expected_output_path, 'rt', encoding = 'utf-8').read() 132*44844408SAndroid Build Coastguard Worker detail = (compareOutputs(expected_output, actual_output, 'input') 133*44844408SAndroid Build Coastguard Worker or compareOutputs(expected_output, actual_rewrite_output, 'rewrite')) 134*44844408SAndroid Build Coastguard Worker if detail: 135*44844408SAndroid Build Coastguard Worker print('FAILED') 136*44844408SAndroid Build Coastguard Worker failed_tests.append((input_path, detail)) 137*44844408SAndroid Build Coastguard Worker else: 138*44844408SAndroid Build Coastguard Worker print('OK') 139*44844408SAndroid Build Coastguard Worker 140*44844408SAndroid Build Coastguard Worker if failed_tests: 141*44844408SAndroid Build Coastguard Worker print() 142*44844408SAndroid Build Coastguard Worker print('Failure details:') 143*44844408SAndroid Build Coastguard Worker for failed_test in failed_tests: 144*44844408SAndroid Build Coastguard Worker print('* Test', failed_test[0]) 145*44844408SAndroid Build Coastguard Worker print(failed_test[1]) 146*44844408SAndroid Build Coastguard Worker print() 147*44844408SAndroid Build Coastguard Worker print('Test results: %d passed, %d failed.' % (len(tests)-len(failed_tests), 148*44844408SAndroid Build Coastguard Worker len(failed_tests))) 149*44844408SAndroid Build Coastguard Worker raise FailError(repr(failed_tests)) 150*44844408SAndroid Build Coastguard Worker else: 151*44844408SAndroid Build Coastguard Worker print('All %d tests passed.' % len(tests)) 152*44844408SAndroid Build Coastguard Worker 153*44844408SAndroid Build Coastguard Workerdef main(): 154*44844408SAndroid Build Coastguard Worker from optparse import OptionParser 155*44844408SAndroid Build Coastguard Worker parser = OptionParser(usage="%prog [options] <path to jsontestrunner.exe> [test case directory]") 156*44844408SAndroid Build Coastguard Worker parser.add_option("--valgrind", 157*44844408SAndroid Build Coastguard Worker action="store_true", dest="valgrind", default=False, 158*44844408SAndroid Build Coastguard Worker help="run all the tests using valgrind to detect memory leaks") 159*44844408SAndroid Build Coastguard Worker parser.add_option("-c", "--with-json-checker", 160*44844408SAndroid Build Coastguard Worker action="store_true", dest="with_json_checker", default=False, 161*44844408SAndroid Build Coastguard Worker help="run all the tests from the official JSONChecker test suite of json.org") 162*44844408SAndroid Build Coastguard Worker parser.enable_interspersed_args() 163*44844408SAndroid Build Coastguard Worker options, args = parser.parse_args() 164*44844408SAndroid Build Coastguard Worker 165*44844408SAndroid Build Coastguard Worker if len(args) < 1 or len(args) > 2: 166*44844408SAndroid Build Coastguard Worker parser.error('Must provides at least path to jsontestrunner executable.') 167*44844408SAndroid Build Coastguard Worker sys.exit(1) 168*44844408SAndroid Build Coastguard Worker 169*44844408SAndroid Build Coastguard Worker jsontest_executable_path = os.path.normpath(os.path.abspath(args[0])) 170*44844408SAndroid Build Coastguard Worker if len(args) > 1: 171*44844408SAndroid Build Coastguard Worker input_path = os.path.normpath(os.path.abspath(args[1])) 172*44844408SAndroid Build Coastguard Worker else: 173*44844408SAndroid Build Coastguard Worker input_path = None 174*44844408SAndroid Build Coastguard Worker runAllTests(jsontest_executable_path, input_path, 175*44844408SAndroid Build Coastguard Worker use_valgrind=options.valgrind, 176*44844408SAndroid Build Coastguard Worker with_json_checker=options.with_json_checker, 177*44844408SAndroid Build Coastguard Worker writerClass='StyledWriter') 178*44844408SAndroid Build Coastguard Worker runAllTests(jsontest_executable_path, input_path, 179*44844408SAndroid Build Coastguard Worker use_valgrind=options.valgrind, 180*44844408SAndroid Build Coastguard Worker with_json_checker=options.with_json_checker, 181*44844408SAndroid Build Coastguard Worker writerClass='StyledStreamWriter') 182*44844408SAndroid Build Coastguard Worker runAllTests(jsontest_executable_path, input_path, 183*44844408SAndroid Build Coastguard Worker use_valgrind=options.valgrind, 184*44844408SAndroid Build Coastguard Worker with_json_checker=options.with_json_checker, 185*44844408SAndroid Build Coastguard Worker writerClass='BuiltStyledStreamWriter') 186*44844408SAndroid Build Coastguard Worker 187*44844408SAndroid Build Coastguard Workerif __name__ == '__main__': 188*44844408SAndroid Build Coastguard Worker try: 189*44844408SAndroid Build Coastguard Worker main() 190*44844408SAndroid Build Coastguard Worker except FailError: 191*44844408SAndroid Build Coastguard Worker sys.exit(1) 192