#!/usr/bin/env python # ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- # # patchtest: execute all unittest test cases discovered for a single patch # # Copyright (C) 2016 Intel Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Author: Leo Sandoval # import sys import os import unittest import fileinput import logging import traceback import re import json # Include current path so test cases can see it sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) # Include patchtest library sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')) from patchtestdata import PatchTestInput as pti import utils logger = utils.logger_create('patchtest') info = logger.info error = logger.error from patchtestrepo import Repo def getResult(patch, mergepatch): class PatchTestResult(unittest.TextTestResult): """ Patchtest TextTestResult """ shouldStop = True longMessage = False success = 'PASS' fail = 'FAIL' skip = 'SKIP' class JSON(object): """ Formats results in JSON format""" status = 'status' results = 'results' def __init__(self, status, obj): self._status = status self._results = str(obj) self._out = dict([(self.status,status),(self.results,'')]) def __str__(self): d = self._results # results may be in json format, so try decoding it # before inserting it into self._out try: self._out[self.results] = json.loads(self._results) except ValueError: pass return json.dumps(self._out) def startTestRun(self): # let's create the repo already, it can be used later on repoargs = { 'repodir': pti.repodir, 'commit' : pti.basecommit, 'branch' : pti.basebranch, 'patch' : patch, } self.repo_error = False self.test_error = False self.test_failure = False try: self.repo = pti.repo = Repo(**repoargs) except: logger.error(traceback.print_exc()) self.repo_error = True self.stop() return if mergepatch: self.repo.merge() def addError(self, test, err): self.test_error = True (ty, va, trace) = err logger.error(traceback.print_exc()) def addFailure(self, test, err): self.test_failure = True if pti.json: print(self.JSON(self.fail, err[1])) else: print('{} {}'.format(self.fail, test.id())) def addSuccess(self, test): if pti.json: print(self.JSON(self.success, test)) else: print('{} {}'.format(self.success, test.id())) def addSkip(self, test, reason): if pti.json: print(self.JSON(self.skip, test)) else: print('{} {}'.format(self.skip, test.id())) def stopTestRun(self): # in case there was an error on repo object creation, just return if self.repo_error: return self.repo.clean() return PatchTestResult def _runner(resultklass, prefix=None): # load test with the corresponding prefix loader = unittest.TestLoader() if prefix: loader.testMethodPrefix = prefix # create the suite with discovered tests and the corresponding runner suite = loader.discover(start_dir=pti.startdir, pattern=pti.pattern, top_level_dir=pti.topdir) ntc = suite.countTestCases() # if there are no test cases, just quit if not ntc: return 2 runner = unittest.TextTestRunner(resultclass=resultklass, verbosity=0) try: result = runner.run(suite) except: logger.error(traceback.print_exc()) logger.error('patchtest failed, removing patchtest branch') return 1 return 0 def run(patch): """ Load, setup and run pre and post-merge tests """ # Get the result class and install the control-c handler unittest.installHandler() # run pre-merge tests, meaning those methods with 'pretest' as prefix premerge_resultklass = getResult(patch, False) premerge_result = _runner(premerge_resultklass, 'pretest') # run post-merge tests, meaning those methods with 'test' as prefix postmerge_resultklass = getResult(patch, True) postmerge_result = _runner(postmerge_resultklass, 'test') if premerge_result == 2 and postmerge_result == 2: logger.error('No tests to run - did you specify the correct suite directory?') return premerge_result or postmerge_result def main(): patch = pti.patch if not sys.stdin.isatty(): patch = pti.namespace_stdin(fileinput.input('-')) if os.path.getsize(patch) == 0: logger.error('Patch is empty') return 1 return run(patch) if __name__ == '__main__': ret = 1 # Parse the command line arguments and store it on the PatchTestInput namespace pti.set_namespace() # set debugging level if pti.debug: logger.setLevel(logging.DEBUG) # if topdir not define, default it to startdir if not pti.topdir: pti.topdir = pti.startdir try: ret = main() except Exception: import traceback traceback.print_exc(5) sys.exit(ret)