pbs_benchpress 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. # Copyright (C) 1994-2018 Altair Engineering, Inc.
  4. # For more information, contact Altair at www.altair.com.
  5. #
  6. # This file is part of the PBS Professional ("PBS Pro") software.
  7. #
  8. # Open Source License Information:
  9. #
  10. # PBS Pro is free software. You can redistribute it and/or modify it under the
  11. # terms of the GNU Affero General Public License as published by the Free
  12. # Software Foundation, either version 3 of the License, or (at your option) any
  13. # later version.
  14. #
  15. # PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
  16. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  17. # FOR A PARTICULAR PURPOSE.
  18. # See the GNU Affero General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU Affero General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. #
  23. # Commercial License Information:
  24. #
  25. # For a copy of the commercial license terms and conditions,
  26. # go to: (http://www.pbspro.com/UserArea/agreement.html)
  27. # or contact the Altair Legal Department.
  28. #
  29. # Altair’s dual-license business model allows companies, individuals, and
  30. # organizations to create proprietary derivative works of PBS Pro and
  31. # distribute them - whether embedded or bundled with other software -
  32. # under a commercial license agreement.
  33. #
  34. # Use of Altair’s trademarks, including but not limited to "PBS™",
  35. # "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
  36. # trademark licensing policies.
  37. import sys
  38. import os
  39. import getopt
  40. import logging
  41. import logging.config
  42. import platform
  43. import errno
  44. import signal
  45. import importlib
  46. import ptl
  47. import nose
  48. from nose.plugins.base import Plugin
  49. from ptl.lib.pbs_testlib import PtlConfig
  50. from distutils.version import LooseVersion
  51. from ptl.utils.pbs_cliutils import CliUtils
  52. from ptl.utils.plugins.ptl_test_loader import PTLTestLoader
  53. from ptl.utils.plugins.ptl_test_runner import PTLTestRunner
  54. from ptl.utils.plugins.ptl_test_db import PTLTestDb
  55. from ptl.utils.plugins.ptl_test_info import PTLTestInfo
  56. from ptl.utils.plugins.ptl_test_tags import PTLTestTags
  57. from ptl.utils.plugins.ptl_test_data import PTLTestData
  58. # trap SIGINT and SIGPIPE
  59. def trap_exceptions(etype, value, tb):
  60. sys.excepthook = sys.__excepthook__
  61. if issubclass(etype, IOError) and value.errno == errno.EPIPE:
  62. pass
  63. else:
  64. sys.__excepthook__(etype, value, tb)
  65. sys.excepthook = trap_exceptions
  66. def sighandler(signum, frames):
  67. signal.alarm(0)
  68. raise KeyboardInterrupt('Signal %d received' % (signum))
  69. # join process group of caller makes it possible to programmatically interrupt
  70. # when run in a subshell
  71. if os.getpgrp() != os.getpid():
  72. os.setpgrp()
  73. signal.signal(signal.SIGINT, sighandler)
  74. signal.signal(signal.SIGTERM, sighandler)
  75. def usage():
  76. msg = []
  77. msg += ['Usage: ' + os.path.basename(sys.argv[0]) + ' [OPTION]\n\n']
  78. msg += [' Test harness used to run or list test suites and test ' +
  79. 'cases\n\n']
  80. msg += ['-f <file names>: comma-separated list of file names to run\n']
  81. msg += ['-F: set logging format to include timestamp and level\n']
  82. msg += ['-g <testgroup file>: path to file containing comma-separated']
  83. msg += [' list of testsuites\n']
  84. msg += ['-h: display usage information\n']
  85. msg += ['-i: show test info\n']
  86. msg += ['-l <level>: log level\n']
  87. msg += ['-L: display list of tests\n']
  88. msg += ['-o <logfile>: log file name\n']
  89. msg += ['-p <param>: test parameter. Comma-separated list of key=val']
  90. msg += [' pairs. Note that the comma can not be used in val\n']
  91. msg += ['-t <test suites>: comma-separated list of test suites to run\n']
  92. msg += ['--exclude=<names>: comma-separated string of tests to exclude\n']
  93. msg += ['--user-plugins=<names>: comma-separated list of key=val of']
  94. msg += [' user plugins to load, where key is module and val is']
  95. msg += [' classname of plugin which is subclass of nose.plugins.base']
  96. msg += ['.Plugin\n']
  97. msg += ['--db-type=<type>: Type of database to use.']
  98. msg += [' can be one of "html", "file", "sqlite", "pgsql", "json".']
  99. msg += [' Default to "file"\n']
  100. msg += ['--db-name=<name>: database name. Default to']
  101. msg += [' ptl_test_results.db\n']
  102. msg += ['--db-access=<path>: Path to a file that defines db options '
  103. '(PostreSQL only)\n']
  104. msg += ['--lcov-bin=<bin>: path to lcov binary. Defaults to lcov\n']
  105. msg += ['--genhtml-bin=<bin>: path to genhtml binary. '
  106. 'Defaults to genhtml\n']
  107. msg += ['--lcov-data=<dir>: path to directory containig .gcno files\n']
  108. msg += ['--lcov-out=<dir>: path to output directory\n']
  109. msg += ['--lcov-baseurl=<url>: use <url> as baseurl in html report\n']
  110. msg += ['--lcov-nosrc: don\'t include PBS source in coverage analysis.']
  111. msg += [' Default PBS source will be included in coverage analysis\n']
  112. msg += ['--log-conf=<file>: logging config file\n']
  113. msg += ['--min-pyver=<version>: minimum Python version\n']
  114. msg += ['--max-pyver=<version>: maximum Python version\n']
  115. msg += ['--param-file=<file>: get params from file. Overrides -p\n']
  116. msg += ['--post-analysis-data=<dir>: path to post analysis data' +
  117. ' directory\n']
  118. msg += ['--max-postdata-threshold=<count>: max post analysis data' +
  119. ' threshold per testsuite. Defaults to 10. <count>=0 will' +
  120. ' disable this threshold\n']
  121. msg += ['--tc-failure-threshold=<count>: test case failure threshold' +
  122. ' per testsuite. Defaults to 10. <count>=0 will disable this' +
  123. ' threshold\n']
  124. msg += ['--cumulative-tc-failure-threshold=<count>: cumulative test' +
  125. ' case failure threshold. Defaults to 100. <count>=0 will' +
  126. ' disable this threshold.\n ' +
  127. ' Must be greater or equal to' +
  128. ' \'tc-failure-threshold\'\n']
  129. msg += ['--stop-on-failure: if set, stop when one of multiple tests ' +
  130. 'fails\n']
  131. msg += ['--timeout=<seconds>: duration after which no test suites are '
  132. 'run\n']
  133. msg += ['--follow-child: if set, walk the test hierarchy and run ' +
  134. 'each test\n']
  135. msg += ['--tags=<tag>: Select only tests that have <tag> tag.']
  136. msg += [' can be applied multiple times\n']
  137. msg += [' Format: [!]tag[,tag]\n']
  138. msg += [' Example:\n']
  139. msg += [' smoke - This will select all tests which has']
  140. msg += [' "smoke" tag\n']
  141. msg += [' !smoke - This will select all tests which']
  142. msg += [' doesn\'t have "smoke" tag\n']
  143. msg += [' smoke,regression - This will select all tests']
  144. msg += [' which has both "smoke" and "regression" tag\n']
  145. msg += ['--eval-tags=\'<Python expression>\': Select only tests for whose']
  146. msg += [' tags evaluates <Python expression> to True.']
  147. msg += [' can be applied multiple times\n']
  148. msg += [' Example:\n']
  149. msg += [' \'smoke and (not regression)\' - This will select']
  150. msg += [' all tests which has "smoke" tag and does\'t have "regression"']
  151. msg += [' tag\n']
  152. msg += [' \'priority>4\' - This will select all tests which']
  153. msg += [' has "priority" tag and its value is >4\n']
  154. msg += ['--tags-info: List all selected test suite (or test cases if ']
  155. msg += ['--verbose applied)\n']
  156. msg += [
  157. ' used with --tags or --eval-tags (also -t or --exclude']
  158. msg += [' can be applied to limit selection)\n']
  159. msg += ['--list-tags: List all currenlty used tags\n']
  160. msg += [
  161. '--verbose: show verbose output (used with -i, -L or --tag-info)\n']
  162. msg += ['--version: show version number and exit\n']
  163. print ''.join(msg)
  164. if __name__ == '__main__':
  165. if len(sys.argv) < 2:
  166. usage()
  167. sys.exit(1)
  168. level = 'INFOCLI2'
  169. fmt = '%(asctime)-15s %(levelname)-8s %(message)s'
  170. outfile = None
  171. dbtype = None
  172. dbname = None
  173. dbaccess = None
  174. testfiles = None
  175. testsuites = None
  176. testparam = None
  177. testgroup = None
  178. list_test = False
  179. showinfo = False
  180. follow = False
  181. excludes = None
  182. stoponfail = False
  183. paramfile = None
  184. logconf = None
  185. minpyver = None
  186. maxpyver = None
  187. verbose = False
  188. lcov_data = None
  189. lcov_bin = None
  190. lcov_out = None
  191. lcov_nosrc = False
  192. lcov_baseurl = None
  193. genhtml_bin = None
  194. timeout = None
  195. nosedebug = False
  196. only_info = False
  197. tags = []
  198. eval_tags = []
  199. tags_info = False
  200. list_tags = False
  201. post_data_dir = None
  202. gen_ts_tree = False
  203. tc_failure_threshold = 10
  204. cumulative_tc_failure_threshold = 100
  205. max_postdata_threshold = 10
  206. user_plugins = None
  207. PtlConfig()
  208. largs = ['exclude=', 'log-conf=', 'timeout=']
  209. largs += ['param-file=', 'min-pyver=', 'max-pyver=']
  210. largs += ['db-name=', 'db-access=', 'db-type=', 'genhtml-bin=']
  211. largs += ['lcov-bin=', 'lcov-data=', 'lcov-out=', 'lcov-nosrc']
  212. largs += ['lcov-baseurl=', 'tags=', 'eval-tags=', 'tags-info', 'list-tags']
  213. largs += ['version', 'verbose', 'follow-child']
  214. largs += ['stop-on-failure', 'enable-nose-debug']
  215. largs += ['post-analysis-data=', 'gen-ts-tree']
  216. largs += ['tc-failure-threshold=', 'cumulative-tc-failure-threshold=']
  217. largs += ['max-postdata-threshold=', 'user-plugins=']
  218. try:
  219. opts, args = getopt.getopt(
  220. sys.argv[1:], 'f:il:t:o:p:g:hLF', largs)
  221. except:
  222. sys.stderr.write('Unrecognized option. Exiting\n')
  223. usage()
  224. sys.exit(1)
  225. if args:
  226. sys.stderr.write('Invalid usage. Exiting\n')
  227. usage()
  228. sys.exit(1)
  229. for o, val in opts:
  230. if o == '-i':
  231. showinfo = True
  232. list_test = False
  233. gen_ts_tree = False
  234. only_info = True
  235. elif o == '-L':
  236. showinfo = False
  237. list_test = True
  238. gen_ts_tree = False
  239. only_info = True
  240. elif o == '--gen-ts-tree':
  241. showinfo = False
  242. list_test = False
  243. gen_ts_tree = True
  244. only_info = True
  245. elif o == '-l':
  246. level = val
  247. elif o == '-o':
  248. outfile = CliUtils.expand_abs_path(val)
  249. elif o == '-f':
  250. testfiles = CliUtils.expand_abs_path(val)
  251. elif o == '-t':
  252. testsuites = val
  253. elif o == '--user-plugins':
  254. user_plugins = val
  255. elif o == '--tags':
  256. tags.append(val.strip())
  257. elif o == '--eval-tags':
  258. eval_tags.append(val.strip())
  259. elif o == '--tags-info':
  260. tags_info = True
  261. elif o == '--list-tags':
  262. list_tags = True
  263. elif o == '--exclude':
  264. excludes = val
  265. elif o == '-F':
  266. fmt = '%(asctime)-15s %(levelname)-8s %(message)s'
  267. elif o == '-p':
  268. testparam = val
  269. elif o == '-g':
  270. testgroup = val
  271. elif o == '--timeout':
  272. timeout = int(val)
  273. elif o == '--db-type':
  274. dbtype = val
  275. elif o == '--db-name':
  276. dbname = CliUtils.expand_abs_path(val)
  277. elif o == '--db-access':
  278. dbaccess = CliUtils.expand_abs_path(val)
  279. elif o == '--genhtml-bin':
  280. genhtml_bin = CliUtils.expand_abs_path(val)
  281. elif o == '--lcov-bin':
  282. lcov_bin = CliUtils.expand_abs_path(val)
  283. elif o == '--lcov-data':
  284. lcov_data = CliUtils.expand_abs_path(val)
  285. elif o == '--lcov-out':
  286. lcov_out = CliUtils.expand_abs_path(val)
  287. elif o == '--lcov-nosrc':
  288. lcov_nosrc = True
  289. elif o == '--lcov-baseurl':
  290. lcov_baseurl = val
  291. elif o == '--param-file':
  292. paramfile = CliUtils.expand_abs_path(val)
  293. elif o == '--stop-on-failure':
  294. stoponfail = True
  295. elif o == '--follow-child':
  296. follow = True
  297. elif o == '--log-conf':
  298. logconf = val
  299. elif o == '--min-pyver':
  300. minpyver = val
  301. elif o == '--max-pyver':
  302. maxpyver = val
  303. elif o == '--enable-nose-debug':
  304. nosedebug = True
  305. elif o == '--verbose':
  306. verbose = True
  307. elif o == '--post-analysis-data':
  308. post_data_dir = CliUtils.expand_abs_path(val)
  309. elif o == '--tc-failure-threshold':
  310. tc_failure_threshold = val
  311. elif o == '--cumulative-tc-failure-threshold':
  312. cumulative_tc_failure_threshold = val
  313. elif o == '--max-postdata-threshold':
  314. max_postdata_threshold = val
  315. elif o == '-h':
  316. usage()
  317. sys.exit(0)
  318. elif o == '--version':
  319. print ptl.__version__
  320. sys.exit(0)
  321. else:
  322. sys.stderr.write('Unreocgnized option %s\n' % o)
  323. usage()
  324. sys.exit(1)
  325. if nosedebug:
  326. level = 'DEBUG'
  327. l = CliUtils.get_logging_level(level)
  328. if logconf:
  329. logging.config.fileConfig(logconf)
  330. else:
  331. logging.basicConfig(filename=outfile, filemode='w+', level=l,
  332. format=fmt)
  333. if outfile is not None:
  334. stream_hdlr = logging.StreamHandler()
  335. stream_hdlr.setLevel(l)
  336. stream_hdlr.setFormatter(logging.Formatter(fmt))
  337. ptl_logger = logging.getLogger('ptl')
  338. ptl_logger.addHandler(stream_hdlr)
  339. ptl_logger.setLevel(l)
  340. pyver = platform.python_version()
  341. if minpyver is not None and LooseVersion(pyver) < LooseVersion(minpyver):
  342. logging.error('Python version ' + str(pyver) + ' does not meet ' +
  343. 'required minimum version of ' + minpyver)
  344. sys.exit(1)
  345. if maxpyver is not None and LooseVersion(pyver) > LooseVersion(maxpyver):
  346. logging.error('Python version ' + str(pyver) + ' does not meet ' +
  347. 'required max version of ' + maxpyver)
  348. sys.exit(1)
  349. if showinfo and testsuites is None:
  350. logging.error(
  351. 'Testsuites names require (see -t) along with -i option!')
  352. sys.exit(1)
  353. try:
  354. tc_failure_threshold = int(tc_failure_threshold)
  355. if tc_failure_threshold < 0:
  356. raise ValueError
  357. except:
  358. _msg = 'Invalid value provided for testcase failure threshold, '
  359. _msg += 'please provide integer'
  360. logging.error(_msg)
  361. sys.exit(1)
  362. try:
  363. cumulative_tc_failure_threshold = int(cumulative_tc_failure_threshold)
  364. if cumulative_tc_failure_threshold < 0:
  365. raise ValueError
  366. except:
  367. _msg = 'Invalid value provided for cumulative-tc-failure-threshold, '
  368. _msg += 'please provide integer'
  369. logging.error(_msg)
  370. sys.exit(1)
  371. if cumulative_tc_failure_threshold < tc_failure_threshold:
  372. _msg = 'Value for cumulative-tc-failure-threshould should'
  373. _msg += ' be greater or equal to \'tc-failure-threshold\''
  374. logging.error(_msg)
  375. sys.exit(1)
  376. try:
  377. max_postdata_threshold = int(max_postdata_threshold)
  378. if max_postdata_threshold < 0:
  379. raise ValueError
  380. except:
  381. _msg = 'Invalid value provided for max-postdata-threshold, '
  382. _msg += 'please provide integer'
  383. logging.error(_msg)
  384. sys.exit(1)
  385. if outfile is not None and not os.path.isdir(os.path.dirname(outfile)):
  386. os.mkdir(os.path.dirname(outfile))
  387. if timeout is not None:
  388. signal.signal(signal.SIGALRM, sighandler)
  389. signal.alarm(timeout)
  390. if list_test:
  391. excludes = None
  392. testgroup = None
  393. follow = True
  394. if testfiles is not None:
  395. tests = testfiles.split(',')
  396. else:
  397. tests = os.getcwd()
  398. if testsuites is None:
  399. testsuites = 'PBSTestSuite'
  400. follow = True
  401. if only_info:
  402. testinfo = PTLTestInfo()
  403. testinfo.set_data(testsuites, list_test,
  404. showinfo, verbose, gen_ts_tree)
  405. plugins = (testinfo,)
  406. elif (tags_info or list_tags):
  407. loader = PTLTestLoader()
  408. testtags = PTLTestTags()
  409. loader.set_data(testgroup, testsuites, excludes, True)
  410. testtags.set_data(tags, eval_tags, tags_info, list_tags, verbose)
  411. plugins = (loader, testtags)
  412. else:
  413. loader = PTLTestLoader()
  414. testtags = PTLTestTags()
  415. runner = PTLTestRunner()
  416. db = PTLTestDb()
  417. data = PTLTestData()
  418. loader.set_data(testgroup, testsuites, excludes, follow)
  419. testtags.set_data(tags, eval_tags)
  420. runner.set_data(paramfile, testparam, lcov_bin, lcov_data, lcov_out,
  421. genhtml_bin, lcov_nosrc, lcov_baseurl,
  422. tc_failure_threshold, cumulative_tc_failure_threshold)
  423. db.set_data(dbtype, dbname, dbaccess)
  424. data.set_data(post_data_dir, max_postdata_threshold)
  425. plugins = (loader, testtags, runner, db, data)
  426. if user_plugins:
  427. for plugin in user_plugins.split(','):
  428. if '=' not in plugin:
  429. _msg = 'Invalid value (%s)' % (plugin)
  430. _msg += ' provided in user-plugins, it should be key value'
  431. _msg += ' pair where key is module name and value is class'
  432. _msg += ' name of plugin'
  433. logging.error(_msg)
  434. sys.exit(1)
  435. mod, clsname = plugin.split('=', 1)
  436. try:
  437. loaded_mod = importlib.import_module(mod)
  438. except ImportError:
  439. _msg = 'Failed to load module (%s)' % mod
  440. _msg += ' for plugin (%s)' % plugin
  441. logging.error(_msg)
  442. sys.exit(1)
  443. _plugin = getattr(loaded_mod, clsname, None)
  444. if not _plugin:
  445. _msg = 'Could not find class named "%s"' % clsname
  446. _msg += ' in module (%s)' % mod
  447. logging.error(_msg)
  448. sys.exit(1)
  449. if not issubclass(_plugin, Plugin):
  450. _msg = 'Plugin class (%s) should be subclass of ' % (clsname)
  451. _msg += 'nose.plugins.base.Plugin'
  452. logging.error(_msg)
  453. sys.exit(1)
  454. plugins += (_plugin(),)
  455. test_regex = r'(^(?:[\w]+|^)Test|pbs_|^test_[\(]*)'
  456. os.environ['NOSE_TESTMATCH'] = test_regex
  457. if nosedebug:
  458. os.environ['NOSE_VERBOSE'] = '7'
  459. if outfile:
  460. os.environ['NOSE_DEBUG_LOG'] = outfile
  461. else:
  462. os.environ['NOSE_VERBOSE'] = '2'
  463. if stoponfail:
  464. os.environ['NOSE_STOP'] = '1'
  465. nose.main(defaultTest=tests, argv=[sys.argv[0]], plugins=plugins)