reporter.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import threading
  2. from collections import OrderedDict, defaultdict
  3. from allure_commons.types import AttachmentType
  4. from allure_commons.model2 import ExecutableItem
  5. from allure_commons.model2 import TestResult
  6. from allure_commons.model2 import Attachment, ATTACHMENT_PATTERN
  7. from allure_commons.utils import now
  8. from allure_commons._core import plugin_manager
  9. class ThreadContextItems:
  10. _thread_context = defaultdict(OrderedDict)
  11. _init_thread: threading.Thread
  12. @property
  13. def thread_context(self):
  14. context = self._thread_context[threading.current_thread()]
  15. if not context and threading.current_thread() is not self._init_thread:
  16. uuid, last_item = next(reversed(self._thread_context[self._init_thread].items()))
  17. context[uuid] = last_item
  18. return context
  19. def __init__(self, *args, **kwargs):
  20. self._init_thread = threading.current_thread()
  21. super().__init__(*args, **kwargs)
  22. def __setitem__(self, key, value):
  23. self.thread_context.__setitem__(key, value)
  24. def __getitem__(self, item):
  25. return self.thread_context.__getitem__(item)
  26. def __iter__(self):
  27. return self.thread_context.__iter__()
  28. def __reversed__(self):
  29. return self.thread_context.__reversed__()
  30. def get(self, key):
  31. return self.thread_context.get(key)
  32. def pop(self, key):
  33. return self.thread_context.pop(key)
  34. def cleanup(self):
  35. stopped_threads = []
  36. for thread in self._thread_context.keys():
  37. if not thread.is_alive():
  38. stopped_threads.append(thread)
  39. for thread in stopped_threads:
  40. del self._thread_context[thread]
  41. class AllureReporter:
  42. def __init__(self):
  43. self._items = ThreadContextItems()
  44. self._orphan_items = []
  45. def _update_item(self, uuid, **kwargs):
  46. item = self._items[uuid] if uuid else self._items[next(reversed(self._items))]
  47. for name, value in kwargs.items():
  48. attr = getattr(item, name)
  49. if isinstance(attr, list):
  50. attr.append(value)
  51. else:
  52. setattr(item, name, value)
  53. def _last_executable(self):
  54. for _uuid in reversed(self._items):
  55. if isinstance(self._items[_uuid], ExecutableItem):
  56. return _uuid
  57. def get_item(self, uuid):
  58. return self._items.get(uuid)
  59. def get_last_item(self, item_type=None):
  60. for _uuid in reversed(self._items):
  61. if item_type is None:
  62. return self._items.get(_uuid)
  63. if isinstance(self._items[_uuid], item_type):
  64. return self._items.get(_uuid)
  65. def start_group(self, uuid, group):
  66. self._items[uuid] = group
  67. def stop_group(self, uuid, **kwargs):
  68. self._update_item(uuid, **kwargs)
  69. group = self._items.pop(uuid)
  70. plugin_manager.hook.report_container(container=group)
  71. def update_group(self, uuid, **kwargs):
  72. self._update_item(uuid, **kwargs)
  73. def start_before_fixture(self, parent_uuid, uuid, fixture):
  74. self._items.get(parent_uuid).befores.append(fixture)
  75. self._items[uuid] = fixture
  76. def stop_before_fixture(self, uuid, **kwargs):
  77. self._update_item(uuid, **kwargs)
  78. self._items.pop(uuid)
  79. def start_after_fixture(self, parent_uuid, uuid, fixture):
  80. self._items.get(parent_uuid).afters.append(fixture)
  81. self._items[uuid] = fixture
  82. def stop_after_fixture(self, uuid, **kwargs):
  83. self._update_item(uuid, **kwargs)
  84. fixture = self._items.pop(uuid)
  85. fixture.stop = now()
  86. def schedule_test(self, uuid, test_case):
  87. self._items[uuid] = test_case
  88. def get_test(self, uuid):
  89. return self.get_item(uuid) if uuid else self.get_last_item(TestResult)
  90. def close_test(self, uuid):
  91. test_case = self._items.pop(uuid)
  92. self._items.cleanup()
  93. plugin_manager.hook.report_result(result=test_case)
  94. def drop_test(self, uuid):
  95. self._items.pop(uuid)
  96. def start_step(self, parent_uuid, uuid, step):
  97. parent_uuid = parent_uuid if parent_uuid else self._last_executable()
  98. if parent_uuid is None:
  99. self._orphan_items.append(uuid)
  100. else:
  101. self._items[parent_uuid].steps.append(step)
  102. self._items[uuid] = step
  103. def stop_step(self, uuid, **kwargs):
  104. if uuid in self._orphan_items:
  105. self._orphan_items.remove(uuid)
  106. else:
  107. self._update_item(uuid, **kwargs)
  108. self._items.pop(uuid)
  109. def _attach(self, uuid, name=None, attachment_type=None, extension=None, parent_uuid=None):
  110. mime_type = attachment_type
  111. extension = extension if extension else 'attach'
  112. if type(attachment_type) is AttachmentType:
  113. extension = attachment_type.extension
  114. mime_type = attachment_type.mime_type
  115. file_name = ATTACHMENT_PATTERN.format(prefix=uuid, ext=extension)
  116. attachment = Attachment(source=file_name, name=name, type=mime_type)
  117. last_uuid = parent_uuid if parent_uuid else self._last_executable()
  118. self._items[last_uuid].attachments.append(attachment)
  119. return file_name
  120. def attach_file(self, uuid, source, name=None, attachment_type=None, extension=None, parent_uuid=None):
  121. file_name = self._attach(uuid, name=name, attachment_type=attachment_type,
  122. extension=extension, parent_uuid=parent_uuid)
  123. plugin_manager.hook.report_attached_file(source=source, file_name=file_name)
  124. def attach_data(self, uuid, body, name=None, attachment_type=None, extension=None, parent_uuid=None):
  125. file_name = self._attach(uuid, name=name, attachment_type=attachment_type,
  126. extension=extension, parent_uuid=parent_uuid)
  127. plugin_manager.hook.report_attached_data(body=body, file_name=file_name)