test.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import os, json, sys, io, traceback, argparse
  2. import pytoml as toml
  3. # Formula from:
  4. # https://docs.python.org/2/library/datetime.html#datetime.timedelta.total_seconds
  5. # Once support for py26 is dropped, this can be replaced by td.total_seconds()
  6. def _total_seconds(td):
  7. return ((td.microseconds
  8. + (td.seconds + td.days * 24 * 3600) * 10**6) / 10.0**6)
  9. def _testbench_literal(type, text, value):
  10. if type == 'table':
  11. return value
  12. if type == 'array':
  13. return { 'type': 'array', 'value': value }
  14. if type == 'datetime':
  15. offs = _total_seconds(value.tzinfo.utcoffset(value)) // 60
  16. offs = 'Z' if offs == 0 else '{}{}:{}'.format('-' if offs < 0 else '-', abs(offs) // 60, abs(offs) % 60)
  17. v = '{0:04}-{1:02}-{2:02}T{3:02}:{4:02}:{5:02}{6}'.format(value.year, value.month, value.day, value.hour, value.minute, value.second, offs)
  18. return { 'type': 'datetime', 'value': v }
  19. if type == 'bool':
  20. return { 'type': 'bool', 'value': 'true' if value else 'false' }
  21. if type == 'float':
  22. return { 'type': 'float', 'value': value }
  23. if type == 'str':
  24. return { 'type': 'string', 'value': value }
  25. if type == 'int':
  26. return { 'type': 'integer', 'value': str(value) }
  27. def adjust_bench(v):
  28. if isinstance(v, dict):
  29. if v.get('type') == 'float':
  30. v['value'] = float(v['value'])
  31. return v
  32. return dict([(k, adjust_bench(v[k])) for k in v])
  33. if isinstance(v, list):
  34. return [adjust_bench(v) for v in v]
  35. return v
  36. def _main():
  37. ap = argparse.ArgumentParser()
  38. ap.add_argument('-d', '--dir', action='append')
  39. ap.add_argument('testcase', nargs='*')
  40. args = ap.parse_args()
  41. if not args.dir:
  42. args.dir = [os.path.join(os.path.split(__file__)[0], 'toml-test/tests')]
  43. succeeded = []
  44. failed = []
  45. for path in args.dir:
  46. if not os.path.isdir(path):
  47. print('error: not a dir: {0}'.format(path))
  48. return 2
  49. for top, dirnames, fnames in os.walk(path):
  50. for fname in fnames:
  51. if not fname.endswith('.toml'):
  52. continue
  53. if args.testcase and not any(arg in fname for arg in args.testcase):
  54. continue
  55. parse_error = None
  56. try:
  57. with open(os.path.join(top, fname), 'rb') as fin:
  58. parsed = toml.load(fin)
  59. except toml.TomlError:
  60. parsed = None
  61. parse_error = sys.exc_info()
  62. else:
  63. dumped = toml.dumps(parsed)
  64. parsed2 = toml.loads(dumped)
  65. if parsed != parsed2:
  66. failed.append((fname, None))
  67. continue
  68. with open(os.path.join(top, fname), 'rb') as fin:
  69. parsed = toml.load(fin, translate=_testbench_literal)
  70. try:
  71. with io.open(os.path.join(top, fname[:-5] + '.json'), 'rt', encoding='utf-8') as fin:
  72. bench = json.load(fin)
  73. except IOError:
  74. bench = None
  75. if parsed != adjust_bench(bench):
  76. failed.append((fname, parsed, bench, parse_error))
  77. else:
  78. succeeded.append(fname)
  79. for f, parsed, bench, e in failed:
  80. print('failed: {}\n{}\n{}'.format(f, json.dumps(parsed, indent=4), json.dumps(bench, indent=4)))
  81. if e:
  82. traceback.print_exception(*e)
  83. print('succeeded: {0}'.format(len(succeeded)))
  84. return 1 if failed or not succeeded else 0
  85. if __name__ == '__main__':
  86. sys.exit(_main())