utils.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import pytest
  2. from itertools import chain, islice
  3. from allure_commons.utils import represent, SafeFormatter, md5
  4. from allure_commons.utils import format_exception, format_traceback
  5. from allure_commons.model2 import Status
  6. from allure_commons.model2 import StatusDetails
  7. from allure_commons.types import LabelType
  8. ALLURE_DESCRIPTION_MARK = 'allure_description'
  9. ALLURE_DESCRIPTION_HTML_MARK = 'allure_description_html'
  10. ALLURE_LABEL_MARK = 'allure_label'
  11. ALLURE_LINK_MARK = 'allure_link'
  12. ALLURE_UNIQUE_LABELS = [
  13. LabelType.SEVERITY,
  14. LabelType.FRAMEWORK,
  15. LabelType.HOST,
  16. LabelType.SUITE,
  17. LabelType.PARENT_SUITE,
  18. LabelType.SUB_SUITE
  19. ]
  20. def get_marker_value(item, keyword):
  21. marker = item.get_closest_marker(keyword)
  22. return marker.args[0] if marker and marker.args else None
  23. def allure_title(item):
  24. return getattr(
  25. getattr(item, "obj", None),
  26. "__allure_display_name__",
  27. None
  28. )
  29. def allure_description(item):
  30. description = get_marker_value(item, ALLURE_DESCRIPTION_MARK)
  31. if description:
  32. return description
  33. elif hasattr(item, 'function'):
  34. return item.function.__doc__
  35. def allure_description_html(item):
  36. return get_marker_value(item, ALLURE_DESCRIPTION_HTML_MARK)
  37. def allure_label(item, label):
  38. labels = []
  39. for mark in item.iter_markers(name=ALLURE_LABEL_MARK):
  40. if mark.kwargs.get("label_type") == label:
  41. labels.extend(mark.args)
  42. return labels
  43. def allure_labels(item):
  44. unique_labels = dict()
  45. labels = set()
  46. for mark in item.iter_markers(name=ALLURE_LABEL_MARK):
  47. label_type = mark.kwargs["label_type"]
  48. if label_type in ALLURE_UNIQUE_LABELS:
  49. if label_type not in unique_labels.keys():
  50. unique_labels[label_type] = mark.args[0]
  51. else:
  52. for arg in mark.args:
  53. labels.add((label_type, arg))
  54. for k, v in unique_labels.items():
  55. labels.add((k, v))
  56. return labels
  57. def allure_links(item):
  58. for mark in item.iter_markers(name=ALLURE_LINK_MARK):
  59. yield (mark.kwargs["link_type"], mark.args[0], mark.kwargs["name"])
  60. def format_allure_link(config, url, link_type):
  61. pattern = dict(config.option.allure_link_pattern).get(link_type, '{}')
  62. return pattern.format(url)
  63. def pytest_markers(item):
  64. for keyword in item.keywords.keys():
  65. if any([keyword.startswith('allure_'), keyword == 'parametrize']):
  66. continue
  67. marker = item.get_closest_marker(keyword)
  68. if marker is None:
  69. continue
  70. yield mark_to_str(marker)
  71. def mark_to_str(marker):
  72. args = [represent(arg) for arg in marker.args]
  73. kwargs = [f'{key}={represent(value)}' for key, value in marker.kwargs.items()]
  74. if marker.name in ('filterwarnings', 'skip', 'skipif', 'xfail', 'usefixtures', 'tryfirst', 'trylast'):
  75. markstr = f'@pytest.mark.{marker.name}'
  76. else:
  77. markstr = str(marker.name)
  78. if args or kwargs:
  79. parameters = ', '.join(args + kwargs)
  80. markstr = f'{markstr}({parameters})'
  81. return markstr
  82. def allure_package(item):
  83. parts = item.nodeid.split('::')
  84. path = parts[0].rsplit('.', 1)[0]
  85. return path.replace('/', '.')
  86. def allure_name(item, parameters, param_id=None):
  87. name = item.name
  88. title = allure_title(item)
  89. param_id_kwargs = {}
  90. if param_id:
  91. # if param_id is an ASCII string, it could have been encoded by pytest (_pytest.compat.ascii_escaped)
  92. if param_id.isascii():
  93. param_id = param_id.encode().decode("unicode-escape")
  94. param_id_kwargs["param_id"] = param_id
  95. return SafeFormatter().format(
  96. title,
  97. **{**param_id_kwargs, **parameters, **item.funcargs}
  98. ) if title else name
  99. def allure_full_name(item: pytest.Item):
  100. package = allure_package(item)
  101. class_name = f".{item.parent.name}" if isinstance(item.parent, pytest.Class) else ''
  102. test = item.originalname if isinstance(item, pytest.Function) else item.name.split("[")[0]
  103. full_name = f'{package}{class_name}#{test}'
  104. return full_name
  105. def allure_suite_labels(item):
  106. head, possibly_clazz, tail = islice(chain(item.nodeid.split('::'), [None], [None]), 3)
  107. clazz = possibly_clazz if tail else None
  108. file_name, path = islice(chain(reversed(head.rsplit('/', 1)), [None]), 2)
  109. module = file_name.split('.')[0]
  110. package = path.replace('/', '.') if path else None
  111. pairs = dict(zip([LabelType.PARENT_SUITE, LabelType.SUITE, LabelType.SUB_SUITE], [package, module, clazz]))
  112. labels = dict(allure_labels(item))
  113. default_suite_labels = []
  114. for label, value in pairs.items():
  115. if label not in labels.keys() and value:
  116. default_suite_labels.append((label, value))
  117. return default_suite_labels
  118. def get_outcome_status(outcome):
  119. _, exception, _ = outcome.excinfo or (None, None, None)
  120. return get_status(exception)
  121. def get_outcome_status_details(outcome):
  122. exception_type, exception, exception_traceback = outcome.excinfo or (None, None, None)
  123. return get_status_details(exception_type, exception, exception_traceback)
  124. def get_status(exception):
  125. if exception:
  126. if isinstance(exception, AssertionError) or isinstance(exception, pytest.fail.Exception):
  127. return Status.FAILED
  128. elif isinstance(exception, pytest.skip.Exception):
  129. return Status.SKIPPED
  130. return Status.BROKEN
  131. else:
  132. return Status.PASSED
  133. def get_status_details(exception_type, exception, exception_traceback):
  134. message = format_exception(exception_type, exception)
  135. trace = format_traceback(exception_traceback)
  136. return StatusDetails(message=message, trace=trace) if message or trace else None
  137. def get_pytest_report_status(pytest_report):
  138. pytest_statuses = ('failed', 'passed', 'skipped')
  139. statuses = (Status.FAILED, Status.PASSED, Status.SKIPPED)
  140. for pytest_status, status in zip(pytest_statuses, statuses):
  141. if getattr(pytest_report, pytest_status):
  142. return status
  143. def get_history_id(full_name, parameters, original_values):
  144. return md5(
  145. full_name,
  146. *(original_values.get(p.name, p.value) for p in sorted(
  147. filter(
  148. lambda p: not p.excluded,
  149. parameters
  150. ),
  151. key=lambda p: p.name
  152. ))
  153. )