collection.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import os
  2. import sys
  3. from six import reraise
  4. import logging
  5. import py
  6. import pytest # noqa
  7. import _pytest.python
  8. import _pytest.doctest
  9. import json
  10. import library.python.testing.filter.filter as test_filter
  11. class LoadedModule(_pytest.python.Module):
  12. def __init__(self, parent, name, **kwargs):
  13. self.name = name + '.py'
  14. self.session = parent
  15. self.parent = parent
  16. self.config = parent.config
  17. self.keywords = {}
  18. self.own_markers = []
  19. self.extra_keyword_matches = set()
  20. self.fspath = py.path.local()
  21. @classmethod
  22. def from_parent(cls, **kwargs):
  23. namespace = kwargs.pop('namespace', True)
  24. kwargs.setdefault('fspath', py.path.local())
  25. loaded_module = getattr(super(LoadedModule, cls), 'from_parent', cls)(**kwargs)
  26. loaded_module.namespace = namespace
  27. return loaded_module
  28. @property
  29. def _nodeid(self):
  30. if os.getenv('CONFTEST_LOAD_POLICY') == 'LOCAL':
  31. return self._getobj().__file__
  32. else:
  33. return self.name
  34. @property
  35. def nodeid(self):
  36. return self._nodeid
  37. def _getobj(self):
  38. module_name = self.name[:-len('.py')]
  39. if self.namespace:
  40. module_name = '__tests__.' + module_name
  41. try:
  42. __import__(module_name)
  43. except Exception as e:
  44. msg = 'Failed to load module "{}" and obtain list of tests due to an error'.format(module_name)
  45. logging.exception('%s: %s', msg, e)
  46. etype, exc, tb = sys.exc_info()
  47. reraise(etype, type(exc)('{}\n{}'.format(exc, msg)), tb)
  48. return sys.modules[module_name]
  49. class DoctestModule(LoadedModule):
  50. def collect(self):
  51. import doctest
  52. module = self._getobj()
  53. # uses internal doctest module parsing mechanism
  54. finder = doctest.DocTestFinder()
  55. optionflags = _pytest.doctest.get_optionflags(self)
  56. runner = doctest.DebugRunner(verbose=0, optionflags=optionflags)
  57. try:
  58. for test in finder.find(module, self.name[:-len('.py')]):
  59. if test.examples: # skip empty doctests
  60. yield getattr(_pytest.doctest.DoctestItem, 'from_parent', _pytest.doctest.DoctestItem)(
  61. name=test.name,
  62. parent=self,
  63. runner=runner,
  64. dtest=test)
  65. except Exception:
  66. logging.exception('DoctestModule failed, probably you can add NO_DOCTESTS() macro to ya.make')
  67. etype, exc, tb = sys.exc_info()
  68. msg = 'DoctestModule failed, probably you can add NO_DOCTESTS() macro to ya.make'
  69. reraise(etype, type(exc)('{}\n{}'.format(exc, msg)), tb)
  70. # NOTE: Since we are overriding collect method of pytest session, pytest hooks are not invoked during collection.
  71. def pytest_ignore_collect(module, session, filenames_from_full_filters, accept_filename_predicate):
  72. if session.config.option.mode == 'list':
  73. return not accept_filename_predicate(module.name)
  74. if filenames_from_full_filters is not None and module.name not in filenames_from_full_filters:
  75. return True
  76. test_file_filter = getattr(session.config.option, 'test_file_filter', None)
  77. if test_file_filter is None:
  78. return False
  79. if module.name != test_file_filter.replace('/', '.'):
  80. return True
  81. return False
  82. class CollectionPlugin(object):
  83. def __init__(self, test_modules, doctest_modules):
  84. self._test_modules = test_modules
  85. self._doctest_modules = doctest_modules
  86. def pytest_sessionstart(self, session):
  87. def collect(*args, **kwargs):
  88. accept_filename_predicate = test_filter.make_py_file_filter(session.config.option.test_filter)
  89. full_test_names_file_path = session.config.option.test_list_path
  90. filenames_filter = None
  91. if full_test_names_file_path and os.path.exists(full_test_names_file_path):
  92. with open(full_test_names_file_path, 'r') as afile:
  93. # in afile stored 2 dimensional array such that array[modulo_index] contains tests which should be run in this test suite
  94. full_names_filter = set(json.load(afile)[int(session.config.option.modulo_index)])
  95. filenames_filter = set(map(lambda x: x.split('::')[0], full_names_filter))
  96. for test_module in self._test_modules:
  97. module = LoadedModule.from_parent(name=test_module, parent=session)
  98. if not pytest_ignore_collect(module, session, filenames_filter, accept_filename_predicate):
  99. yield module
  100. if os.environ.get('YA_PYTEST_DISABLE_DOCTEST', 'no') == 'no':
  101. module = DoctestModule.from_parent(name=test_module, parent=session)
  102. if not pytest_ignore_collect(module, session, filenames_filter, accept_filename_predicate):
  103. yield module
  104. if os.environ.get('YA_PYTEST_DISABLE_DOCTEST', 'no') == 'no':
  105. for doctest_module in self._doctest_modules:
  106. yield DoctestModule.from_parent(name=doctest_module, parent=session, namespace=False)
  107. session.collect = collect