runjsontests.py 8.3 KB

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