ptl_test_runner.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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. import datetime
  40. import unittest
  41. import tempfile
  42. import platform
  43. import pwd
  44. import signal
  45. import ptl
  46. import re
  47. from logging import StreamHandler
  48. from traceback import format_exception
  49. from types import ModuleType
  50. from nose.core import TextTestRunner
  51. from nose.util import isclass
  52. from nose.plugins.base import Plugin
  53. from nose.plugins.skip import SkipTest
  54. from nose.suite import ContextSuite
  55. from ptl.utils.pbs_testsuite import PBSTestSuite
  56. from ptl.utils.pbs_testsuite import TIMEOUT_KEY
  57. from ptl.utils.pbs_dshutils import DshUtils
  58. from ptl.lib.pbs_testlib import PBSInitServices
  59. from ptl.utils.pbs_covutils import LcovUtils
  60. try:
  61. from cStringIO import StringIO
  62. except ImportError:
  63. from StringIO import StringIO
  64. log = logging.getLogger('nose.plugins.PTLTestRunner')
  65. class TimeOut(Exception):
  66. """
  67. Raise this exception to mark a test as timed out.
  68. """
  69. pass
  70. class TCThresholdReached(Exception):
  71. """
  72. Raise this exception to tell that tc-failure-threshold reached
  73. """
  74. class TestLogCaptureHandler(StreamHandler):
  75. """
  76. Log handler for capturing logs which test case print
  77. using logging module
  78. """
  79. def __init__(self):
  80. self.buffer = StringIO()
  81. StreamHandler.__init__(self, self.buffer)
  82. self.setLevel(logging.DEBUG2)
  83. fmt = '%(asctime)-15s %(levelname)-8s %(message)s'
  84. self.setFormatter(logging.Formatter(fmt))
  85. def get_logs(self):
  86. return self.buffer.getvalue()
  87. class _PtlTestResult(unittest.TestResult):
  88. """
  89. Ptl custom test result
  90. """
  91. separator1 = '=' * 70
  92. separator2 = '___m_oo_m___'
  93. logger = logging.getLogger(__name__)
  94. def __init__(self, stream, descriptions, verbosity, config=None):
  95. unittest.TestResult.__init__(self)
  96. self.stream = stream
  97. self.showAll = verbosity > 1
  98. self.dots = verbosity == 1
  99. self.descriptions = descriptions
  100. self.errorClasses = {}
  101. self.config = config
  102. self.success = []
  103. self.skipped = []
  104. self.timedout = []
  105. self.handler = TestLogCaptureHandler()
  106. self.start = datetime.datetime.now()
  107. self.stop = datetime.datetime.now()
  108. def getDescription(self, test):
  109. """
  110. Get the test result description
  111. """
  112. if hasattr(test, 'test'):
  113. return str(test.test)
  114. elif type(test.context) == ModuleType:
  115. tmn = getattr(test.context, '_testMethodName', 'unknown')
  116. return '%s (%s)' % (tmn, test.context.__name__)
  117. elif isinstance(test, ContextSuite):
  118. tmn = getattr(test.context, '_testMethodName', 'unknown')
  119. return '%s (%s.%s)' % (tmn,
  120. test.context.__module__,
  121. test.context.__name__)
  122. else:
  123. return str(test)
  124. def getTestDoc(self, test):
  125. """
  126. Get test document
  127. """
  128. if hasattr(test, 'test'):
  129. if hasattr(test.test, '_testMethodDoc'):
  130. return test.test._testMethodDoc
  131. else:
  132. return None
  133. else:
  134. if hasattr(test, '_testMethodDoc'):
  135. return test._testMethodDoc
  136. else:
  137. return None
  138. def clear_stop(self):
  139. self.shouldStop = False
  140. def startTest(self, test):
  141. """
  142. Start the test
  143. :param test: Test to start
  144. :type test: str
  145. """
  146. ptl_logger = logging.getLogger('ptl')
  147. if self.handler not in ptl_logger.handlers:
  148. ptl_logger.addHandler(self.handler)
  149. self.handler.buffer.truncate(0)
  150. unittest.TestResult.startTest(self, test)
  151. test.start_time = datetime.datetime.now()
  152. if self.showAll:
  153. self.logger.info('test name: ' + self.getDescription(test) + '...')
  154. self.logger.info('test start time: ' + test.start_time.ctime())
  155. tdoc = self.getTestDoc(test)
  156. if tdoc is not None:
  157. tdoc = '\n' + tdoc
  158. self.logger.info('test docstring: %s' % (tdoc))
  159. def addSuccess(self, test):
  160. """
  161. Add success to the test result
  162. """
  163. self.success.append(test)
  164. unittest.TestResult.addSuccess(self, test)
  165. if self.showAll:
  166. self.logger.info('ok\n')
  167. elif self.dots:
  168. self.logger.info('.')
  169. def _addError(self, test, err):
  170. unittest.TestResult.addError(self, test, err)
  171. if self.showAll:
  172. self.logger.info('ERROR\n')
  173. elif self.dots:
  174. self.logger.info('E')
  175. def addError(self, test, err):
  176. """
  177. Add error to the test result
  178. :param test: Test for which to add error
  179. :type test: str
  180. :param error: Error message to add
  181. :type error: str
  182. """
  183. if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
  184. return
  185. if isclass(err[0]) and issubclass(err[0], SkipTest):
  186. self.addSkip(test, err[1])
  187. return
  188. if isclass(err[0]) and issubclass(err[0], TimeOut):
  189. self.addTimedOut(test, err)
  190. return
  191. for cls, (storage, label, isfail) in self.errorClasses.items():
  192. if isclass(err[0]) and issubclass(err[0], cls):
  193. if isfail:
  194. test.passed = False
  195. storage.append((test, err))
  196. if self.showAll:
  197. self.logger.info(label + '\n')
  198. elif self.dots:
  199. self.logger.info(label[0])
  200. return
  201. test.passed = False
  202. self._addError(test, err)
  203. def addFailure(self, test, err):
  204. """
  205. Indicate failure
  206. """
  207. unittest.TestResult.addFailure(self, test, err)
  208. if self.showAll:
  209. self.logger.info('FAILED\n')
  210. elif self.dots:
  211. self.logger.info('F')
  212. def addSkip(self, test, reason):
  213. """
  214. Indicate skipping of test
  215. :param test: Test to skip
  216. :type test: str
  217. :param reason: Reason fot the skip
  218. :type reason: str
  219. """
  220. self.skipped.append((test, reason))
  221. if self.showAll:
  222. self.logger.info('SKIPPED')
  223. elif self.dots:
  224. self.logger.info('S')
  225. def addTimedOut(self, test, err):
  226. """
  227. Indicate timeout
  228. :param test: Test for which timeout happened
  229. :type test: str
  230. :param err: Error for timeout
  231. :type err: str
  232. """
  233. self.timedout.append((test, self._exc_info_to_string(err, test)))
  234. if self.showAll:
  235. self.logger.info('TIMEDOUT')
  236. elif self.dots:
  237. self.logger.info('T')
  238. def printErrors(self):
  239. """
  240. Print the errors
  241. """
  242. _blank_line = False
  243. if ((len(self.errors) > 0) or (len(self.failures) > 0) or
  244. (len(self.timedout) > 0)):
  245. if self.dots or self.showAll:
  246. self.logger.info('')
  247. _blank_line = True
  248. self.printErrorList('ERROR', self.errors)
  249. self.printErrorList('FAILED', self.failures)
  250. self.printErrorList('TIMEDOUT', self.timedout)
  251. for cls in self.errorClasses.keys():
  252. storage, label, isfail = self.errorClasses[cls]
  253. if isfail:
  254. if not _blank_line:
  255. self.logger.info('')
  256. _blank_line = True
  257. self.printErrorList(label, storage)
  258. self.config.plugins.report(self.stream)
  259. def printErrorList(self, flavour, errors):
  260. """
  261. Print the error list
  262. :param errors: Errors to print
  263. """
  264. for test, err in errors:
  265. self.logger.info(self.separator1)
  266. self.logger.info('%s: %s\n' % (flavour, self.getDescription(test)))
  267. self.logger.info(self.separator2)
  268. self.logger.info('%s\n' % err)
  269. def printLabel(self, label, err=None):
  270. """
  271. Print the label for the error
  272. :param label: Label to print
  273. :type label: str
  274. :param err: Error for which label to be printed
  275. :type err: str
  276. """
  277. if self.showAll:
  278. message = [label]
  279. if err:
  280. try:
  281. detail = str(err[1])
  282. except:
  283. detail = None
  284. if detail:
  285. message.append(detail)
  286. self.logger.info(': '.join(message))
  287. elif self.dots:
  288. self.logger.info(label[:1])
  289. def wasSuccessful(self):
  290. """
  291. Check whether the test successful or not
  292. :returns: True if no ``errors`` or no ``failures`` or no ``timeout``
  293. else return False
  294. """
  295. if self.errors or self.failures or self.timedout:
  296. return False
  297. for cls in self.errorClasses.keys():
  298. storage, _, isfail = self.errorClasses[cls]
  299. if not isfail:
  300. continue
  301. if storage:
  302. return False
  303. return True
  304. def printSummary(self):
  305. """
  306. Called by the test runner to print the final summary of test
  307. run results.
  308. :param start: Time at which test begins
  309. :param stop: Time at which test ends
  310. """
  311. self.printErrors()
  312. msg = ['=' * 80]
  313. ef = []
  314. error = 0
  315. fail = 0
  316. skip = 0
  317. timedout = 0
  318. success = len(self.success)
  319. if len(self.failures) > 0:
  320. for failedtest in self.failures:
  321. fail += 1
  322. msg += ['failed: ' + self.getDescription(failedtest[0])]
  323. ef.append(failedtest)
  324. if len(self.errors) > 0:
  325. for errtest in self.errors:
  326. error += 1
  327. msg += ['error: ' + self.getDescription(errtest[0])]
  328. ef.append(errtest)
  329. if len(self.skipped) > 0:
  330. for skiptest, reason in self.skipped:
  331. skip += 1
  332. _msg = 'skipped: ' + str(skiptest).strip()
  333. _msg += ' reason: ' + str(reason).strip()
  334. msg += [_msg]
  335. if len(self.timedout) > 0:
  336. for tdtest in self.timedout:
  337. timedout += 1
  338. msg += ['timedout: ' + self.getDescription(tdtest[0])]
  339. ef.append(tdtest)
  340. cases = []
  341. suites = []
  342. for _ef in ef:
  343. if hasattr(_ef[0], 'test'):
  344. cname = _ef[0].test.__class__.__name__
  345. tname = getattr(_ef[0].test, '_testMethodName', 'unknown')
  346. cases.append(cname + '.' + tname)
  347. suites.append(cname)
  348. cases = sorted(list(set(cases)))
  349. suites = sorted(list(set(suites)))
  350. if len(cases) > 0:
  351. _msg = 'Test cases with failures: '
  352. _msg += ','.join(cases)
  353. msg += [_msg]
  354. if len(suites) > 0:
  355. _msg = 'Test suites with failures: '
  356. _msg += ','.join(suites)
  357. msg += [_msg]
  358. runned = success + fail + error + skip + timedout
  359. _msg = 'run: ' + str(runned)
  360. _msg += ', succeeded: ' + str(success)
  361. _msg += ', failed: ' + str(fail)
  362. _msg += ', errors: ' + str(error)
  363. _msg += ', skipped: ' + str(skip)
  364. _msg += ', timedout: ' + str(timedout)
  365. msg += [_msg]
  366. msg += ['Tests run in ' + str(self.stop - self.start)]
  367. self.logger.info('\n'.join(msg))
  368. class PtlTestRunner(TextTestRunner):
  369. """
  370. Test runner that uses ``PtlTestResult`` to enable errorClasses,
  371. as well as providing hooks for plugins to override or replace the test
  372. output stream, results, and the test case itself.
  373. """
  374. def __init__(self, stream=sys.stdout, descriptions=True, verbosity=3,
  375. config=None):
  376. self.logger = logging.getLogger(__name__)
  377. self.result = None
  378. TextTestRunner.__init__(self, stream, descriptions, verbosity, config)
  379. def _makeResult(self):
  380. return _PtlTestResult(self.stream, self.descriptions, self.verbosity,
  381. self.config)
  382. def run(self, test):
  383. """
  384. Overrides to provide plugin hooks and defer all output to
  385. the test result class.
  386. """
  387. do_exit = False
  388. wrapper = self.config.plugins.prepareTest(test)
  389. if wrapper is not None:
  390. test = wrapper
  391. wrapped = self.config.plugins.setOutputStream(self.stream)
  392. if wrapped is not None:
  393. self.stream = wrapped
  394. self.result = result = self._makeResult()
  395. self.result.start = datetime.datetime.now()
  396. try:
  397. test(result)
  398. except KeyboardInterrupt:
  399. do_exit = True
  400. self.result.stop = datetime.datetime.now()
  401. result.printSummary()
  402. self.config.plugins.finalize(result)
  403. if do_exit:
  404. sys.exit(1)
  405. return result
  406. class PTLTestRunner(Plugin):
  407. """
  408. PTL Test Runner Plugin
  409. """
  410. name = 'PTLTestRunner'
  411. score = sys.maxint - 4
  412. logger = logging.getLogger(__name__)
  413. def __init__(self):
  414. Plugin.__init__(self)
  415. self.param = None
  416. self.lcov_bin = None
  417. self.lcov_data = None
  418. self.lcov_out = None
  419. self.lcov_utils = None
  420. self.lcov_nosrc = None
  421. self.lcov_baseurl = None
  422. self.genhtml_bin = None
  423. self.config = None
  424. self.result = None
  425. self.tc_failure_threshold = None
  426. self.cumulative_tc_failure_threshold = None
  427. self.__failed_tc_count = 0
  428. self.__tf_count = 0
  429. self.__failed_tc_count_msg = False
  430. def options(self, parser, env):
  431. """
  432. Register command line options
  433. """
  434. pass
  435. def set_data(self, paramfile, testparam,
  436. lcov_bin, lcov_data, lcov_out, genhtml_bin, lcov_nosrc,
  437. lcov_baseurl, tc_failure_threshold,
  438. cumulative_tc_failure_threshold):
  439. if paramfile is not None:
  440. _pf = open(paramfile, 'r')
  441. _params_from_file = _pf.readlines()
  442. _pf.close()
  443. _nparams = []
  444. for l in range(len(_params_from_file)):
  445. if _params_from_file[l].startswith('#'):
  446. continue
  447. else:
  448. _nparams.append(_params_from_file[l])
  449. _f = ','.join(map(lambda l: l.strip('\r\n'), _nparams))
  450. if testparam is not None:
  451. testparam += ',' + _f
  452. else:
  453. testparam = _f
  454. self.param = testparam
  455. self.lcov_bin = lcov_bin
  456. self.lcov_data = lcov_data
  457. self.lcov_out = lcov_out
  458. self.genhtml_bin = genhtml_bin
  459. self.lcov_nosrc = lcov_nosrc
  460. self.lcov_baseurl = lcov_baseurl
  461. self.tc_failure_threshold = tc_failure_threshold
  462. self.cumulative_tc_failure_threshold = cumulative_tc_failure_threshold
  463. def configure(self, options, config):
  464. """
  465. Configure the plugin and system, based on selected options
  466. """
  467. self.config = config
  468. self.enabled = True
  469. def prepareTestRunner(self, runner):
  470. """
  471. Prepare test runner
  472. """
  473. return PtlTestRunner(verbosity=3, config=self.config)
  474. def prepareTestResult(self, result):
  475. """
  476. Prepare test result
  477. """
  478. self.result = result
  479. def startContext(self, context):
  480. context.param = self.param
  481. context.start_time = datetime.datetime.now()
  482. if isclass(context) and issubclass(context, PBSTestSuite):
  483. self.result.logger.info(self.result.separator1)
  484. self.result.logger.info('suite name: ' + context.__name__)
  485. doc = context.__doc__
  486. if doc is not None:
  487. self.result.logger.info('suite docstring: \n' + doc + '\n')
  488. self.result.logger.info(self.result.separator1)
  489. self.__failed_tc_count = 0
  490. self.__failed_tc_count_msg = False
  491. def __get_timeout(self, test):
  492. try:
  493. method = getattr(test.test, getattr(test.test, '_testMethodName'))
  494. return getattr(method, TIMEOUT_KEY)
  495. except:
  496. if hasattr(test, 'test'):
  497. __conf = getattr(test.test, 'conf')
  498. else:
  499. __conf = getattr(test.context, 'conf')
  500. return __conf['default_testcase_timeout']
  501. def __set_test_end_data(self, test, err=None):
  502. if not hasattr(test, 'start_time'):
  503. test = test.context
  504. if err is not None:
  505. is_skip = issubclass(err[0], SkipTest)
  506. is_tctr = issubclass(err[0], TCThresholdReached)
  507. if not (is_skip or is_tctr):
  508. self.__failed_tc_count += 1
  509. self.__tf_count += 1
  510. try:
  511. test.err_in_string = self.result._exc_info_to_string(err,
  512. test)
  513. except:
  514. etype, value, tb = err
  515. test.err_in_string = ''.join(format_exception(etype, value,
  516. tb))
  517. else:
  518. test.err_in_string = 'None'
  519. test.end_time = datetime.datetime.now()
  520. test.duration = test.end_time - test.start_time
  521. test.captured_logs = self.result.handler.get_logs()
  522. def startTest(self, test):
  523. """
  524. Start the test
  525. """
  526. if ((self.cumulative_tc_failure_threshold != 0) and
  527. (self.__tf_count >= self.cumulative_tc_failure_threshold)):
  528. _msg = 'Total testcases failure count exceeded cumulative'
  529. _msg += ' testcase failure threshold '
  530. _msg += '(%d)' % self.cumulative_tc_failure_threshold
  531. self.logger.error(_msg)
  532. raise KeyboardInterrupt
  533. if ((self.tc_failure_threshold != 0) and
  534. (self.__failed_tc_count >= self.tc_failure_threshold)):
  535. if self.__failed_tc_count_msg:
  536. raise TCThresholdReached
  537. _msg = 'Testcases failure for this testsuite count exceeded'
  538. _msg += ' testcase failure threshold '
  539. _msg += '(%d)' % self.tc_failure_threshold
  540. self.logger.error(_msg)
  541. self.__failed_tc_count_msg = True
  542. raise TCThresholdReached
  543. timeout = self.__get_timeout(test)
  544. def timeout_handler(signum, frame):
  545. raise TimeOut('Timed out after %s second' % timeout)
  546. old_handler = signal.signal(signal.SIGALRM, timeout_handler)
  547. setattr(test, 'old_sigalrm_handler', old_handler)
  548. signal.alarm(timeout)
  549. def stopTest(self, test):
  550. """
  551. Stop the test
  552. """
  553. old_sigalrm_handler = getattr(test, 'old_sigalrm_handler', None)
  554. if old_sigalrm_handler is not None:
  555. signal.signal(signal.SIGALRM, old_sigalrm_handler)
  556. signal.alarm(0)
  557. def addError(self, test, err):
  558. """
  559. Add error
  560. """
  561. if isclass(err[0]) and issubclass(err[0], TCThresholdReached):
  562. return True
  563. self.__set_test_end_data(test, err)
  564. def addFailure(self, test, err):
  565. """
  566. Add failure
  567. """
  568. self.__set_test_end_data(test, err)
  569. def addSuccess(self, test):
  570. """
  571. Add success
  572. """
  573. self.__set_test_end_data(test)
  574. def _cleanup(self):
  575. self.logger.info('Cleaning up temporary files')
  576. du = DshUtils()
  577. root_dir = os.sep
  578. dirlist = set([os.path.join(root_dir, 'tmp'),
  579. os.path.join(root_dir, 'var', 'tmp')])
  580. # get tmp dir from the environment
  581. for envname in 'TMPDIR', 'TEMP', 'TMP':
  582. dirname = os.getenv(envname)
  583. if dirname:
  584. dirlist.add(dirname)
  585. p = re.compile(r'^pbs\.\d+')
  586. for tmpdir in dirlist:
  587. # list the contents of each tmp dir and
  588. # get the file list to be deleted
  589. self.logger.info('Cleaning up ' + tmpdir + ' dir')
  590. ftd = []
  591. files = du.listdir(path=tmpdir)
  592. bn = os.path.basename
  593. ftd.extend([f for f in files if bn(f).startswith('PtlPbs')])
  594. ftd.extend([f for f in files if bn(f).startswith('STDIN')])
  595. ftd.extend([f for f in files if bn(f).startswith('pbsscrpt')])
  596. ftd.extend([f for f in files if bn(f).startswith('pbs.conf.')])
  597. ftd.extend([f for f in files if p.match(bn(f))])
  598. for f in ftd:
  599. du.rm(path=f, sudo=True, recursive=True, force=True,
  600. level=logging.DEBUG)
  601. tmpdir = tempfile.gettempdir()
  602. os.chdir(tmpdir)
  603. tmppath = os.path.join(tmpdir, 'dejagnutemp%s' % os.getpid())
  604. if du.isdir(path=tmppath):
  605. du.rm(path=tmppath, recursive=True, sudo=True, force=True,
  606. level=logging.DEBUG)
  607. def begin(self):
  608. command = sys.argv
  609. command[0] = os.path.basename(command[0])
  610. self.logger.info('input command: ' + ' '.join(command))
  611. self.logger.info('param: ' + str(self.param))
  612. self.logger.info('ptl version: ' + str(ptl.__version__))
  613. _m = 'platform: ' + ' '.join(platform.uname()).strip()
  614. self.logger.info(_m)
  615. self.logger.info('python version: ' + str(platform.python_version()))
  616. self.logger.info('user: ' + pwd.getpwuid(os.getuid())[0])
  617. self.logger.info('-' * 80)
  618. if self.lcov_data is not None:
  619. self.lcov_utils = LcovUtils(cov_bin=self.lcov_bin,
  620. html_bin=self.genhtml_bin,
  621. cov_out=self.lcov_out,
  622. data_dir=self.lcov_data,
  623. html_nosrc=self.lcov_nosrc,
  624. html_baseurl=self.lcov_baseurl)
  625. # Initialize coverage analysis
  626. self.lcov_utils.zero_coverage()
  627. # The following 'dance' is done due to some oddities on lcov's
  628. # part, according to this the lcov readme file at
  629. # http://ltp.sourceforge.net/coverage/lcov/readme.php that reads:
  630. #
  631. # Note that this step only works after the application has
  632. # been started and stopped at least once. Otherwise lcov will
  633. # abort with an error mentioning that there are no data/.gcda
  634. # files.
  635. self.lcov_utils.initialize_coverage(name='PTLTestCov')
  636. PBSInitServices().restart()
  637. self._cleanup()
  638. def finalize(self, result):
  639. if self.lcov_data is not None:
  640. # See note above that briefly explains the 'dance' needed to get
  641. # reliable coverage data
  642. PBSInitServices().restart()
  643. self.lcov_utils.capture_coverage(name='PTLTestCov')
  644. exclude = ['"*work/gSOAP/*"', '"*/pbs/doc/*"', 'lex.yy.c',
  645. 'pbs_ifl_wrap.c', 'usr/include/*', 'unsupported/*']
  646. self.lcov_utils.merge_coverage_traces(name='PTLTestCov',
  647. exclude=exclude)
  648. self.lcov_utils.generate_html()
  649. self.lcov_utils.change_baseurl()
  650. self.logger.info('\n'.join(self.lcov_utils.summarize_coverage()))
  651. self._cleanup()