checking.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. '''
  2. A simplified version of the unittest module, adapted to run unit tests on data sets instead of code.
  3. @author: olivier.massot, 2018
  4. '''
  5. import inspect
  6. import re
  7. import sys
  8. import traceback
  9. from PyQt5.QtCore import QObject
  10. UNKNOWN = 0
  11. SUCCESS = 1
  12. FAILURE = 2
  13. ERROR = 3
  14. _result_to_str = {UNKNOWN: 'Inconnu',
  15. SUCCESS: 'Succès',
  16. FAILURE: 'Echec',
  17. ERROR: 'Erreur'}
  18. def _linenumber(m):
  19. try:
  20. _, line_no = inspect.findsource(m)
  21. return line_no
  22. except AttributeError:
  23. return -1
  24. class TestError():
  25. def __init__(self, message, info = {}, critical=False):
  26. self.message = message
  27. self.info = info
  28. self.critical = critical
  29. def __repr__(self):
  30. return f"TestError[message='{self.message}'; info={self.info}; critical={self.critical}]"
  31. class TestResult():
  32. def __init__(self, test):
  33. self._test = test
  34. self._name = ""
  35. self._status = SUCCESS
  36. self.errors = []
  37. self._exc_info = None
  38. @property
  39. def name(self):
  40. return self._name or self._test.__name__[5:]
  41. @property
  42. def title(self):
  43. try:
  44. return self._test.__doc__.split("\n")[0].strip()
  45. except AttributeError:
  46. return self.name
  47. @property
  48. def description(self):
  49. try:
  50. return re.sub(" +", " ", self._test.__doc__.strip(), flags=re.MULTILINE) #@UndefinedVariable
  51. except AttributeError:
  52. return ""
  53. @property
  54. def status(self):
  55. return self._status
  56. @property
  57. def status_str(self):
  58. return _result_to_str[self._status]
  59. def __repr__(self):
  60. return f"TestResult[title='{self.title}'; status={self.status}; name={self.name}; method={self._test.__name__}; errors_count={len(self.errors)}]"
  61. def log_error(self, message, info={}, critical=False):
  62. self._status = FAILURE
  63. error = TestError(message, info, critical)
  64. self.errors.append(error)
  65. def handle_exception(self, exc_info):
  66. self._status = ERROR
  67. typ, value, trace = exc_info
  68. error = TestError("Une erreur inconnue s'est produite, veuillez consulter les fichiers de journalisation.",
  69. {"exc_info": "{}\n{}\n{}".format(typ.__name__, value, ''.join(traceback.format_tb(trace)))})
  70. self.errors.append(error)
  71. class Comlink():
  72. def _started_test(self, test):
  73. pass
  74. def _ended_test(self, test):
  75. pass
  76. class BaseChecker():
  77. def __init__(self):
  78. self._test_running = None
  79. self.comlink = Comlink()
  80. self.tests = sorted([m for _, m in inspect.getmembers(self, predicate=inspect.ismethod) if m.__name__[:5] == 'test_'], key=_linenumber)
  81. def setUp(self):
  82. pass
  83. def tearDown(self):
  84. pass
  85. def log_error(self, message, **info):
  86. self._test_running.log_error(message, info)
  87. def log_critical(self, message, **info):
  88. self._test_running.log_error(f"[CRITIQUE] {message}", info, critical=True)
  89. def run(self):
  90. tests_results = []
  91. for test in self.tests:
  92. result = TestResult(test)
  93. self._test_running = result
  94. self.comlink._started_test(result)
  95. self.setUp()
  96. try:
  97. test()
  98. except:
  99. result.handle_exception(sys.exc_info())
  100. self.tearDown()
  101. tests_results.append(result)
  102. self.comlink._ended_test(result)
  103. if any(err.critical for err in result.errors):
  104. break
  105. return tests_results
  106. if __name__ == '__main__':
  107. class ExampleChecker(BaseChecker):
  108. def test_c(self):
  109. """ Test 1 """
  110. for i in range(10):
  111. self.log_error(f"error-{i}", i=i)
  112. def test_b(self):
  113. """ Test 2
  114. some longer description """
  115. raise Exception("bla bla")
  116. ch = ExampleChecker()
  117. results = ch.run()
  118. for r in results:
  119. print(r)
  120. for e in r.errors:
  121. print(e)