ptl_test_loader.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. # coding: utf-8
  2. # Copyright (C) 1994-2018 Altair Engineering, Inc.
  3. # For more information, contact Altair at www.altair.com.
  4. #
  5. # This file is part of the PBS Professional ("PBS Pro") software.
  6. #
  7. # Open Source License Information:
  8. #
  9. # PBS Pro is free software. You can redistribute it and/or modify it under the
  10. # terms of the GNU Affero General Public License as published by the Free
  11. # Software Foundation, either version 3 of the License, or (at your option) any
  12. # later version.
  13. #
  14. # PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
  15. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. # FOR A PARTICULAR PURPOSE.
  17. # See the GNU Affero General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Affero General Public License
  20. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. #
  22. # Commercial License Information:
  23. #
  24. # For a copy of the commercial license terms and conditions,
  25. # go to: (http://www.pbspro.com/UserArea/agreement.html)
  26. # or contact the Altair Legal Department.
  27. #
  28. # Altair’s dual-license business model allows companies, individuals, and
  29. # organizations to create proprietary derivative works of PBS Pro and
  30. # distribute them - whether embedded or bundled with other software -
  31. # under a commercial license agreement.
  32. #
  33. # Use of Altair’s trademarks, including but not limited to "PBS™",
  34. # "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
  35. # trademark licensing policies.
  36. import os
  37. import sys
  38. import logging
  39. from nose.plugins.base import Plugin
  40. from ptl.utils.pbs_testsuite import PBSTestSuite
  41. log = logging.getLogger('nose.plugins.PTLTestLoader')
  42. class PTLTestLoader(Plugin):
  43. """
  44. Load test cases from given parameter
  45. """
  46. name = 'PTLTestLoader'
  47. score = sys.maxint - 2
  48. logger = logging.getLogger(__name__)
  49. def __init__(self):
  50. Plugin.__init__(self)
  51. self.suites_list = []
  52. self.excludes = []
  53. self.follow = False
  54. self._only_ts = '__only__ts__'
  55. self._only_tc = '__only__tc__'
  56. self._test_marker = 'test_'
  57. self._tests_list = {self._only_ts: [], self._only_tc: []}
  58. self._excludes_list = {self._only_ts: [], self._only_tc: []}
  59. self.__tests_list_copy = {self._only_ts: [], self._only_tc: []}
  60. self.__allowed_cls = []
  61. self.__allowed_method = []
  62. def options(self, parser, env):
  63. """
  64. Register command line options
  65. """
  66. pass
  67. def set_data(self, testgroup, suites, excludes, follow):
  68. """
  69. Set the data required for loading test data
  70. :param testgroup: Test group
  71. :param suites: Test suites to load
  72. :param excludes: Tests to exclude while running
  73. """
  74. if os.access(str(testgroup), os.R_OK):
  75. f = open(testgroup, 'r')
  76. self.suites_list.extend(f.readline().strip().split(','))
  77. f.close()
  78. elif suites is not None:
  79. self.suites_list.extend(suites.split(','))
  80. if excludes is not None:
  81. self.excludes.extend(excludes.split(','))
  82. self.follow = follow
  83. def configure(self, options, config):
  84. """
  85. Configure the ``plugin`` and ``system``, based on selected options
  86. """
  87. tl = self._tests_list
  88. tlc = self.__tests_list_copy
  89. for _is in self.suites_list:
  90. if '.' in _is:
  91. suite, case = _is.split('.')
  92. if case in tl[self._only_tc]:
  93. tl[self._only_tc].remove(case)
  94. tlc[self._only_tc].remove(case)
  95. if suite in tl.keys():
  96. if case not in tl[suite]:
  97. tl[suite].append(case)
  98. tlc[suite].append(case)
  99. else:
  100. tl.setdefault(suite, [case])
  101. tlc.setdefault(suite, [case])
  102. elif _is.startswith(self._test_marker):
  103. if _is not in tl[self._only_tc]:
  104. tl[self._only_tc].append(_is)
  105. tlc[self._only_tc].append(_is)
  106. else:
  107. if _is not in tl[self._only_ts]:
  108. tl[self._only_ts].append(_is)
  109. tlc[self._only_ts].append(_is)
  110. for k, v in tl.items():
  111. if k in (self._only_ts, self._only_tc):
  112. continue
  113. if len(v) == 0:
  114. tl[self._only_ts].append(k)
  115. tlc[self._only_ts].append(k)
  116. for name in tl[self._only_ts]:
  117. if name in tl.keys():
  118. del tl[name]
  119. del tlc[name]
  120. extl = self._excludes_list
  121. for _is in self.excludes:
  122. if '.' in _is:
  123. suite, case = _is.split('.')
  124. if case in extl[self._only_tc]:
  125. extl[self._only_tc].remove(case)
  126. if suite in extl.keys():
  127. if case not in extl[suite]:
  128. extl[suite].append(case)
  129. else:
  130. extl.setdefault(suite, [case])
  131. elif _is.startswith(self._test_marker):
  132. if _is not in extl[self._only_tc]:
  133. extl[self._only_tc].append(_is)
  134. else:
  135. if _is not in extl[self._only_ts]:
  136. extl[self._only_ts].append(_is)
  137. for k, v in extl.items():
  138. if k in (self._only_ts, self._only_tc):
  139. continue
  140. if len(v) == 0:
  141. extl[self._only_ts].append(k)
  142. for name in extl[self._only_ts]:
  143. if name in extl.keys():
  144. del extl[name]
  145. log.debug('included_tests:%s' % (str(self._tests_list)))
  146. log.debug('included_tests(copy):%s' % (str(self.__tests_list_copy)))
  147. log.debug('excluded_tests:%s' % (str(self._excludes_list)))
  148. self.enabled = len(self.suites_list) > 0
  149. del self.suites_list
  150. del self.excludes
  151. def check_unknown(self):
  152. """
  153. Check for unknown test suite and test case
  154. """
  155. log.debug('check_unknown called')
  156. only_ts = self.__tests_list_copy.pop(self._only_ts)
  157. only_tc = self.__tests_list_copy.pop(self._only_tc)
  158. msg = []
  159. if len(self.__tests_list_copy) > 0:
  160. for k, v in self.__tests_list_copy.items():
  161. msg.extend(map(lambda x: k + '.' + x, v))
  162. if len(only_tc) > 0:
  163. msg.extend(only_tc)
  164. if len(msg) > 0:
  165. _msg = ['unknown testcase(s): %s' % (','.join(msg))]
  166. msg = _msg
  167. if len(only_ts) > 0:
  168. msg += ['unknown testsuite(s): %s' % (','.join(only_ts))]
  169. if len(msg) > 0:
  170. for l in msg:
  171. logging.error(l)
  172. sys.exit(1)
  173. def prepareTestLoader(self, loader):
  174. """
  175. Prepare test loader
  176. """
  177. old_loadTestsFromNames = loader.loadTestsFromNames
  178. def check_loadTestsFromNames(names, module=None):
  179. rv = old_loadTestsFromNames(names, module)
  180. self.check_unknown()
  181. return rv
  182. loader.loadTestsFromNames = check_loadTestsFromNames
  183. return loader
  184. def check_follow(self, cls, method=None):
  185. cname = cls.__name__
  186. if not issubclass(cls, PBSTestSuite):
  187. return False
  188. if cname == 'PBSTestSuite':
  189. if 'PBSTestSuite' not in self._tests_list[self._only_ts]:
  190. return False
  191. if cname in self._excludes_list[self._only_ts]:
  192. return False
  193. if cname in self._tests_list[self._only_ts]:
  194. if cname in self.__tests_list_copy[self._only_ts]:
  195. self.__tests_list_copy[self._only_ts].remove(cname)
  196. return True
  197. if ((cname in self._tests_list.keys()) and (method is None)):
  198. return True
  199. if method is not None:
  200. mname = method.__name__
  201. if not mname.startswith(self._test_marker):
  202. return False
  203. if mname in self._excludes_list[self._only_tc]:
  204. return False
  205. if ((cname in self._excludes_list.keys()) and
  206. (mname in self._excludes_list[cname])):
  207. return False
  208. if ((cname in self._tests_list.keys()) and
  209. (mname in self._tests_list[cname])):
  210. if cname in self.__tests_list_copy.keys():
  211. if mname in self.__tests_list_copy[cname]:
  212. self.__tests_list_copy[cname].remove(mname)
  213. if len(self.__tests_list_copy[cname]) == 0:
  214. del self.__tests_list_copy[cname]
  215. return True
  216. if mname in self._tests_list[self._only_tc]:
  217. if mname in self.__tests_list_copy[self._only_tc]:
  218. self.__tests_list_copy[self._only_tc].remove(mname)
  219. return True
  220. if self.follow:
  221. return self.check_follow(cls.__bases__[0], method)
  222. else:
  223. return False
  224. def is_already_allowed(self, cls, method=None):
  225. """
  226. :param method: Method to check
  227. :returns: True if method is already allowed else False
  228. """
  229. name = cls.__name__
  230. if method is not None:
  231. name += '.' + method.__name__
  232. if name in self.__allowed_method:
  233. return True
  234. else:
  235. self.__allowed_method.append(name)
  236. return False
  237. else:
  238. if name in self.__allowed_cls:
  239. return True
  240. else:
  241. self.__allowed_cls.append(name)
  242. return False
  243. def wantClass(self, cls):
  244. """
  245. Is the class wanted?
  246. """
  247. has_test = False
  248. for t in dir(cls):
  249. if t.startswith(self._test_marker):
  250. has_test = True
  251. break
  252. if not has_test:
  253. return False
  254. rv = self.check_follow(cls)
  255. if rv and not self.is_already_allowed(cls):
  256. log.debug('wantClass:%s' % (str(cls)))
  257. else:
  258. return False
  259. def wantFunction(self, function):
  260. """
  261. Is the function wanted?
  262. """
  263. return self.wantMethod(function)
  264. def wantMethod(self, method):
  265. """
  266. Is the method wanted?
  267. """
  268. try:
  269. cls = method.im_class
  270. except AttributeError:
  271. return False
  272. if not method.__name__.startswith(self._test_marker):
  273. return False
  274. rv = self.check_follow(cls, method)
  275. if rv and not self.is_already_allowed(cls, method):
  276. log.debug('wantMethod:%s' % (str(method)))
  277. else:
  278. return False