tipog.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. '''
  2. Tipog tests
  3. Usage:
  4. tipog [-v] <filename> [-o <output>]
  5. Options:
  6. -v --verbose Verbose result
  7. -o --output Register the results in <output> file (json)
  8. -h --help Show this screen.
  9. --version Show version.
  10. '''
  11. import cProfile
  12. import json
  13. import os
  14. import sys
  15. import timeit
  16. from docopt import docopt
  17. import yaml
  18. __version__ = "0.1"
  19. sysargs = docopt(__doc__, version=__version__)
  20. yaml_file = sysargs["<filename>"]
  21. try:
  22. with open(yaml_file, "r") as f:
  23. data = yaml.load(f)
  24. except FileNotFoundError:
  25. print("ERR: no file named '{}'".format(yaml_file))
  26. sys.exit(1)
  27. except yaml.scanner.ScannerError:
  28. print("ERR: unreadable yaml file '{}'".format(yaml_file))
  29. sys.exit(1)
  30. if not data:
  31. print("ERR: empty yaml file '{}'".format(yaml_file))
  32. sys.exit(1)
  33. for to_import in data["imports"]:
  34. try:
  35. # TODO: avoid the 'exec'
  36. exec(to_import)
  37. except (ModuleNotFoundError, ImportError):
  38. print("ERR: unable to import '{}'".format(to_import))
  39. sys.exit(1)
  40. try:
  41. jobs = data["jobs"]
  42. except KeyError:
  43. print("ERR: missing 'jobs' entry")
  44. sys.exit(1)
  45. def profile(_call, verbose=False):
  46. print(">> {}".format(_call))
  47. if verbose:
  48. cProfile.run(_call, sort='nfl')
  49. number = 1
  50. t = 0
  51. while 1:
  52. t = timeit.timeit(lambda: eval(_call), number=number)
  53. if t >= 0.1:
  54. break
  55. elif number > 10000000:
  56. print("ERR: unable to compute the execution time")
  57. number = "err"
  58. t = "?"
  59. break
  60. number *= 10
  61. return t / 1000
  62. def validate(fct, validator, args, verbose=False):
  63. result = fct(*args)
  64. attended = validator(*args)
  65. return result == attended
  66. to_register = []
  67. for function_name, job in jobs.items():
  68. try:
  69. function = eval(function_name)
  70. except NameError:
  71. print("ERR: unknown function ('{}')".format(function_name))
  72. continue
  73. if sysargs["--verbose"]:
  74. print("** Test function '{}'".format(function_name))
  75. try:
  76. args_lst = job["args"]
  77. except KeyError:
  78. args_lst = [[]]
  79. try:
  80. validator_str = job["validator"]
  81. try:
  82. validator = eval(validator_str)
  83. if sysargs["--verbose"]:
  84. print("> validator: '{}'".format(validator_str))
  85. except NameError:
  86. print("ERR: unknown function as validator ('{}')".format(validator_str))
  87. except (TypeError, KeyError):
  88. validator = None
  89. for args in args_lst:
  90. call_str = "{}(*{})".format(function_name, args)
  91. exectime = profile(call_str, sysargs["--verbose"])
  92. print("\t> Run in {} ms.".format(exectime))
  93. if validator:
  94. valid = validate(function, validator, args, sysargs["--verbose"])
  95. print("\t> Validated: {}".format(valid))
  96. if sysargs["--output"]:
  97. last_result = {"call": call_str, "result": function(*args), "exectime": exectime}
  98. try:
  99. last_result.update(job["infos"])
  100. except KeyError:
  101. pass
  102. to_register.append(last_result)
  103. if sysargs["--verbose"]:
  104. print("------------------------------")
  105. if sysargs["--output"]:
  106. output_name = sysargs["<output>"]
  107. if not output_name:
  108. output_name = "{}_result".format(os.path.splitext(sysargs["<filename>"])[0])
  109. reg_file_path = os.path.join(".", output_name)
  110. try:
  111. os.remove(reg_file_path)
  112. except FileNotFoundError:
  113. pass
  114. with open(reg_file_path, "w+") as f:
  115. json.dump(to_register, f)
  116. print("** End of the tests")