commit python-salt-testing for openSUSE:Factory

Hello community, here is the log from the commit of package python-salt-testing for openSUSE:Factory checked in at 2014-05-02 14:03:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-salt-testing (Old) and /work/SRC/openSUSE:Factory/.python-salt-testing.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-salt-testing" Changes: -------- --- /work/SRC/openSUSE:Factory/python-salt-testing/python-salt-testing.changes 2014-01-03 14:50:28.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python-salt-testing.new/python-salt-testing.changes 2014-05-02 14:03:05.000000000 +0200 @@ -1,0 +2,10 @@ +Thu Apr 24 19:34:10 UTC 2014 - aboe76@gmail.com + +- Updated to version 2014.4.24 + - first date based release + - Add skip_if_binaries_missing helper method + - Global exception handler to catch weird and uncaught exceptions + - Docker runtests support enhancements + - Mark the start and stop of each tests on a log message + +------------------------------------------------------------------- Old: ---- SaltTesting-0.5.4.tar.gz New: ---- SaltTesting-2014.4.24.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-salt-testing.spec ++++++ --- /var/tmp/diff_new_pack.5xYrAx/_old 2014-05-02 14:03:06.000000000 +0200 +++ /var/tmp/diff_new_pack.5xYrAx/_new 2014-05-02 14:03:06.000000000 +0200 @@ -17,7 +17,7 @@ Name: python-salt-testing -Version: 0.5.4 +Version: 2014.4.24 Release: 0 Summary: Testing tools needed in the several Salt Stack projects License: Apache-2.0 ++++++ SaltTesting-0.5.4.tar.gz -> SaltTesting-2014.4.24.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/PKG-INFO new/SaltTesting-2014.4.24/PKG-INFO --- old/SaltTesting-0.5.4/PKG-INFO 2014-01-02 12:11:49.000000000 +0100 +++ new/SaltTesting-2014.4.24/PKG-INFO 2014-04-24 14:04:42.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: SaltTesting -Version: 0.5.4 +Version: 2014.4.24 Summary: Required testing tools needed in the several Salt Stack projects. Home-page: http://saltstack.org Author: Pedro Algarvio @@ -11,7 +11,7 @@ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 -Classifier: Development Status :: 3 - Alpha +Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/SaltTesting.egg-info/PKG-INFO new/SaltTesting-2014.4.24/SaltTesting.egg-info/PKG-INFO --- old/SaltTesting-0.5.4/SaltTesting.egg-info/PKG-INFO 2014-01-02 12:11:33.000000000 +0100 +++ new/SaltTesting-2014.4.24/SaltTesting.egg-info/PKG-INFO 2014-04-24 14:04:32.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: SaltTesting -Version: 0.5.4 +Version: 2014.4.24 Summary: Required testing tools needed in the several Salt Stack projects. Home-page: http://saltstack.org Author: Pedro Algarvio @@ -11,7 +11,7 @@ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 -Classifier: Development Status :: 3 - Alpha +Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/SaltTesting.egg-info/SOURCES.txt new/SaltTesting-2014.4.24/SaltTesting.egg-info/SOURCES.txt --- old/SaltTesting-0.5.4/SaltTesting.egg-info/SOURCES.txt 2014-01-02 12:11:33.000000000 +0100 +++ new/SaltTesting-2014.4.24/SaltTesting.egg-info/SOURCES.txt 2014-04-24 14:04:33.000000000 +0200 @@ -14,12 +14,14 @@ salttesting/mock.py salttesting/unit.py salttesting/version.py +salttesting/xmlunit.py salttesting/ext/__init__.py salttesting/ext/console.py salttesting/ext/os_data.py salttesting/parser/__init__.py salttesting/parser/cover.py salttesting/pylintplugins/__init__.py +salttesting/pylintplugins/flask_sqlalchemy_transform.py salttesting/pylintplugins/pep263.py salttesting/pylintplugins/pep8.py salttesting/pylintplugins/string_format.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/helpers.py new/SaltTesting-2014.4.24/salttesting/helpers.py --- old/SaltTesting-0.5.4/salttesting/helpers.py 2014-01-02 12:03:08.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/helpers.py 2014-02-08 02:08:52.000000000 +0100 @@ -10,6 +10,7 @@ :license: Apache 2.0, see LICENSE for more details. ''' +# Import Python libs import os import sys import types @@ -19,6 +20,9 @@ import __builtin__ from functools import wraps +# Import Salt Testing libs +from salttesting.unit import skip, _id + log = logging.getLogger(__name__) @@ -520,9 +524,16 @@ try: try: return func(cls, username) - except Exception as exc: - log.exception(exc) - failure = exc + except Exception as exc: # pylint: disable=W0703 + log.error( + 'Running {0!r} raised an exception: {1}'.format( + func, exc + ), + exc_info=True + ) + # Store the original exception details which will be raised + # a little further down the code + failure = sys.exc_info() finally: if delete: delete_account = cls.run_function( @@ -544,7 +555,7 @@ ) if failure is not None: # If an exception was thrown, raise it - raise failure + raise failure[0], failure[1], failure[2] return wrap return decorator @@ -626,3 +637,20 @@ return caller(cls) return wrapper return decorator + + +def skip_if_binaries_missing(binaries, check_all=False): + import salt.utils + if check_all: + for binary in binaries: + if salt.utils.which(binary) is None: + return skip( + 'The {0!r} binary was not found'.format(binary) + ) + elif salt.utils.which_bin(binaries) is None: + return skip( + 'None of the following binaries was found: {0}'.format( + ', '.join(binaries) + ) + ) + return _id diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/parser/__init__.py new/SaltTesting-2014.4.24/salttesting/parser/__init__.py --- old/SaltTesting-0.5.4/salttesting/parser/__init__.py 2014-01-02 12:03:08.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/parser/__init__.py 2014-04-18 02:21:34.000000000 +0200 @@ -6,7 +6,7 @@ Salt-Testing CLI access classes :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` - :copyright: © 2013 by the SaltStack Team, see AUTHORS for more details. + :copyright: © 2013-2014 by the SaltStack Team, see AUTHORS for more details :license: Apache 2.0, see LICENSE for more details. ''' @@ -18,10 +18,14 @@ import logging import optparse import tempfile +import traceback import subprocess import warnings +from functools import partial +from contextlib import closing from salttesting import TestLoader, TextTestRunner +from salttesting.xmlunit import HAS_XMLRUNNER, XMLTestRunner try: from salttesting.ext import console WIDTH, HEIGHT = console.getTerminalSize() @@ -29,20 +33,47 @@ except Exception: PNUM = 70 -try: - import xmlrunner -except ImportError: - xmlrunner = None +# This is a completely random and meaningful number intended to identify our +# own signal triggering. +WEIRD_SIGNAL_NUM = -45654 + + +# Let's setup a global exception hook handler which will log all exceptions +# Store a reference to the original handler +__GLOBAL_EXCEPTION_HANDLER = sys.excepthook + + +def __global_logging_exception_handler(exc_type, exc_value, exc_traceback): + ''' + This function will log all python exceptions. + ''' + # Log the exception + logging.getLogger(__name__).error( + 'An un-handled exception was caught by salt-testing\'s global ' + 'exception handler:\n{0}: {1}\n{2}'.format( + exc_type.__name__, + exc_value, + ''.join(traceback.format_exception( + exc_type, exc_value, exc_traceback + )).strip() + ) + ) + # Call the original sys.excepthook + __GLOBAL_EXCEPTION_HANDLER(exc_type, exc_value, exc_traceback) + + +# Set our own exception handler as the one to use +sys.excepthook = __global_logging_exception_handler def print_header(header, sep='~', top=True, bottom=True, inline=False, - centered=False): + centered=False, width=PNUM): ''' Allows some pretty printing of headers on the console, either with a "ruler" on bottom and/or top, inline, centered, etc. ''' if top and not inline: - print(sep * PNUM) + print(sep * width) if centered and not inline: fmt = u'{0:^{width}}' @@ -52,10 +83,10 @@ fmt = u'{0:{sep}^{width}}' else: fmt = u'{0}' - print(fmt.format(header, sep=sep, width=PNUM)) + print(fmt.format(header, sep=sep, width=width)) if bottom and not inline: - print(sep * PNUM) + print(sep * width) class SaltTestingParser(optparse.OptionParser): @@ -63,6 +94,18 @@ support_destructive_tests_selection = False source_code_basedir = None + _known_interpreters = { + 'salttest/arch': 'python2', + 'salttest/centos-5': 'python2.6', + 'salttest/centos-6': 'python2.6', + 'salttest/debian-7': 'python2.7', + 'salttest/opensuse-12.3': 'python2.7', + 'salttest/ubuntu-12.04': 'python2.7', + 'salttest/ubuntu-12.10': 'python2.7', + 'salttest/ubuntu-13.04': 'python2.7', + 'salttest/ubuntu-13.10': 'python2.7' + } + def __init__(self, testsuite_directory, *args, **kwargs): if kwargs.pop('html_output_from_env', None) is not None or \ kwargs.pop('html_output_dir', None) is not None: @@ -80,12 +123,14 @@ 'XML_TESTS_OUTPUT_DIR' ) xml_output_dir = kwargs.pop('xml_output_dir', None) - self.xml_output_dir = os.environ.get( - xml_output_dir_env_var, - xml_output_dir or os.path.join( + + if xml_output_dir_env_var in os.environ: + xml_output_dir = os.environ.get(xml_output_dir_env_var) + if not xml_output_dir: + xml_output_dir = os.path.join( tempfile.gettempdir(), 'xml-tests-output' ) - ) + self.xml_output_dir = xml_output_dir # Get the desired logfile to use while running tests self.tests_logfile = kwargs.pop('tests_logfile', None) @@ -109,13 +154,6 @@ 'Default: %default') ) - if self.support_docker_execution is True: - self.test_selection_group.add_option( - '--docked', - default=None, - help='Run the tests suite in the chosen Docker container' - ) - self.test_selection_group.add_option( '-n', '--name', @@ -127,6 +165,42 @@ ) self.add_option_group(self.test_selection_group) + if self.support_docker_execution is True: + self.docked_selection_group = optparse.OptionGroup( + self, + 'Docked Tests Execution', + 'Run the tests suite under a Docker container. This allows, ' + 'for example, to run destructive tests on your machine ' + 'without actually breaking it in any way.' + ) + self.docked_selection_group.add_option( + '--docked', + default=None, + metavar='CONTAINER', + help='Run the tests suite in the chosen Docker container' + ) + self.docked_selection_group.add_option( + '--docked-interpreter', + default=None, + metavar='PYTHON_INTERPRETER', + help='The python binary name to use when calling the tests ' + 'suite.' + ) + self.docked_selection_group.add_option( + '--docked-skip-delete', + default=False, + action='store_true', + help='Skip docker container deletion on exit. Default: False' + ) + self.docked_selection_group.add_option( + '--docked-skip-delete-on-errors', + default=False, + action='store_true', + help='Skip docker container deletion on exit if errors ' + 'occurred. Default: False' + ) + self.add_option_group(self.docked_selection_group) + self.output_options_group = optparse.OptionGroup( self, 'Output Options' ) @@ -138,6 +212,17 @@ action='count', help='Verbose test runner output' ) + self.output_options_group.add_option( + '--output-columns', + default=PNUM, + type=int, + help='Number of maximum columns to use on the output' + ) + self.output_options_group.add_option( + '--tests-logfile', + default=self.tests_logfile, + help='The path to the tests suite logging logfile' + ) if self.xml_output_dir is not None: self.output_options_group.add_option( '-x', @@ -182,9 +267,8 @@ self.options, self.args = optparse.OptionParser.parse_args( self, args, values ) - print_header(u'', inline=True) + print_header(u'', inline=True, width=self.options.output_columns) self.pre_execution_cleanup() - self._validate_options() if self.support_docker_execution and self.options.docked is not None: if self.source_code_basedir is None: @@ -192,10 +276,25 @@ 'You need to define the \'source_code_basedir\' attribute ' 'in {0!r}.'.format(self.__class__.__name__) ) + + if '/' not in self.options.docked: + self.options.docked = 'salttest/{0}'.format( + self.options.docked + ) + + if self.options.docked_interpreter is None: + self.options.docked_interpreter = self._known_interpreters.get( + self.options.docked, 'python' + ) + # No more processing should be done. We'll exit with the return # code we get from the docker container execution self.exit(self.run_suite_in_docker()) + # Validate options after checking that we're not goint to execute the + # tests suite under a docker container + self._validate_options() + print(' * Current Directory: {0}'.format(os.getcwd())) print(' * Test suite is running under PID {0}'.format(os.getpid())) @@ -203,7 +302,7 @@ try: return (self.options, self.args) finally: - print_header(u'', inline=True) + print_header(u'', inline=True, width=self.options.output_columns) def setup_additional_options(self): ''' @@ -215,16 +314,21 @@ Validate the default available options ''' if self.xml_output_dir is not None and self.options.xml_out and \ - xmlrunner is None: + HAS_XMLRUNNER is False: self.error( '\'--xml\' is not available. The xmlrunner library is not ' 'installed.' ) - elif self.xml_output_dir is not None and self.options.xml_out: + + if self.options.xml_out: + # Override any environment setting with the passed value + self.xml_output_dir = self.options.xml_out + + if self.xml_output_dir is not None and self.options.xml_out: if not os.path.isdir(self.xml_output_dir): os.makedirs(self.xml_output_dir) print( - 'Generated unit test XML reports will be stored ' + ' * Generated unit test XML reports will be stored ' 'at {0!r}'.format(self.xml_output_dir) ) @@ -251,17 +355,17 @@ '[%(levelname)-8s] %(message)s', datefmt='%H:%M:%S' ) - if self.tests_logfile: + if self.options.tests_logfile: filehandler = logging.FileHandler( mode='w', # Not preserved between re-runs - filename=self.tests_logfile + filename=self.options.tests_logfile ) filehandler.setLevel(logging.DEBUG) filehandler.setFormatter(formatter) logging.root.addHandler(filehandler) logging.root.setLevel(logging.DEBUG) - print(' * Logging tests on {0}'.format(self.tests_logfile)) + print(' * Logging tests on {0}'.format(self.options.tests_logfile)) # With greater verbosity we can also log to the console if self.options.verbosity > 2: @@ -314,10 +418,11 @@ tests = loader.discover(path, suffix, self.testsuite_directory) header = '{0} Tests'.format(display_name) - print_header('Starting {0}'.format(header)) + print_header('Starting {0}'.format(header), + width=self.options.output_columns) if self.options.xml_out: - runner = xmlrunner.XMLTestRunner( + runner = XMLTestRunner( stream=sys.stdout, output=self.xml_output_dir, verbosity=self.options.verbosity @@ -336,7 +441,8 @@ ''' print print_header( - u' Overall Tests Report ', sep=u'=', centered=True, inline=True + u' Overall Tests Report ', sep=u'=', centered=True, inline=True, + width=self.options.output_columns ) failures = errors = skipped = passed = 0 @@ -355,10 +461,14 @@ no_problems_found = False - print_header(u'*** {0} '.format(name), sep=u'*', inline=True) + print_header( + u'*** {0} '.format(name), sep=u'*', inline=True, + width=self.options.output_columns + ) if results.skipped: print_header( - u' -------- Skipped Tests ', sep='-', inline=True + u' -------- Skipped Tests ', sep='-', inline=True, + width=self.options.output_columns ) maxlen = len( max([testcase.id() for (testcase, reason) in @@ -367,43 +477,53 @@ fmt = u' -> {0: <{maxlen}} -> {1}' for testcase, reason in results.skipped: print(fmt.format(testcase.id(), reason, maxlen=maxlen)) - print_header(u' ', sep='-', inline=True) + print_header(u' ', sep='-', inline=True, + width=self.options.output_columns) if results.errors: print_header( - u' -------- Tests with Errors ', sep='-', inline=True + u' -------- Tests with Errors ', sep='-', inline=True, + width=self.options.output_columns ) for testcase, reason in results.errors: print_header( u' -> {0} '.format(testcase.id()), - sep=u'.', inline=True + sep=u'.', inline=True, + width=self.options.output_columns ) for line in reason.rstrip().splitlines(): print(' {0}'.format(line.rstrip())) - print_header(u' ', sep=u'.', inline=True) - print_header(u' ', sep='-', inline=True) + print_header(u' ', sep=u'.', inline=True, + width=self.options.output_columns) + print_header(u' ', sep='-', inline=True, + width=self.options.output_columns) if results.failures: print_header( - u' -------- Failed Tests ', sep='-', inline=True + u' -------- Failed Tests ', sep='-', inline=True, + width=self.options.output_columns ) for testcase, reason in results.failures: print_header( u' -> {0} '.format(testcase.id()), - sep=u'.', inline=True + sep=u'.', inline=True, + width=self.options.output_columns ) for line in reason.rstrip().splitlines(): print(' {0}'.format(line.rstrip())) - print_header(u' ', sep=u'.', inline=True) - print_header(u' ', sep='-', inline=True) + print_header(u' ', sep=u'.', inline=True, + width=self.options.output_columns) + print_header(u' ', sep='-', inline=True, + width=self.options.output_columns) if no_problems_found: print_header( u'*** No Problems Found While Running Tests ', - sep=u'*', inline=True + sep=u'*', inline=True, width=self.options.output_columns ) - print_header(u'', sep=u'=', inline=True) + print_header(u'', sep=u'=', inline=True, + width=self.options.output_columns) total = sum([passed, skipped, errors, failures]) print( '{0} (total={1}, skipped={2}, passed={3}, failures={4}, ' @@ -413,7 +533,8 @@ ) ) print_header( - ' Overall Tests Report ', sep='=', centered=True, inline=True + ' Overall Tests Report ', sep='=', centered=True, inline=True, + width=self.options.output_columns ) return @@ -442,20 +563,98 @@ ''' Run the tests suite in a Docker container ''' + def stop_running_docked_container(cid, signum=None, frame=None): + # Allow some time for the container to stop if it's going to be + # stopped by docker or any signals docker might have received + time.sleep(0.5) + + print_header('', inline=True, width=self.options.output_columns) + + # Let's check if, in fact, the container is stopped + scode_call = subprocess.Popen( + ['docker', 'inspect', '-format={{.State.Running}}', cid], + env=os.environ.copy(), + close_fds=True, + stdout=subprocess.PIPE + ) + scode_call.wait() + parsed_scode = scode_call.stdout.read().strip() + if parsed_scode != 'false': + # If the container is still running, let's make sure it + # properly stops + print(' * Making sure the container is stopped. CID:'), + sys.stdout.flush() + + stop_call = subprocess.Popen( + ['docker', 'stop', '--time=15', cid], + env=os.environ.copy(), + close_fds=True, + stdout=subprocess.PIPE + ) + stop_call.wait() + print(stop_call.stdout.read().strip()) + sys.stdout.flush() + time.sleep(0.5) + + # Let's get the container's exit code. We can't trust on Popen's + # returncode because it's not reporting the proper one? Still + # haven't narrowed it down why. + print(' * Container exit code:'), + sys.stdout.flush() + rcode_call = subprocess.Popen( + ['docker', 'inspect', '-format={{.State.ExitCode}}', cid], + env=os.environ.copy(), + close_fds=True, + stdout=subprocess.PIPE + ) + rcode_call.wait() + parsed_rcode = rcode_call.stdout.read().strip() + try: + returncode = int(parsed_rcode) + except ValueError: + returncode = -1 + print(parsed_rcode) + sys.stdout.flush() + + if self.options.docked_skip_delete is False and \ + (self.options.docked_skip_delete_on_errors is False or + (self.options.docked_skip_delete_on_error and + returncode == 0)): + print(' * Cleaning Up Temporary Docker Container. CID:'), + sys.stdout.flush() + cleanup_call = subprocess.Popen( + ['docker', 'rm', cid], + env=os.environ.copy(), + close_fds=True, + stdout=subprocess.PIPE + ) + cleanup_call.wait() + print(cleanup_call.stdout.read().strip()) + + if 'DOCKER_CIDFILE' not in os.environ: + # The CID file was not created "from the outside", so delete it + os.unlink(cidfile) + + print_header('', inline=True, width=self.options.output_columns) + # Finally, EXIT! + sys.exit(returncode) + # Let's start the Docker container and run the tests suite there if '/' not in self.options.docked: container = 'salttest/{0}'.format(self.options.docked) else: container = self.options.docked - calling_args = ['/salt-source/tests/runtests.py'] + calling_args = [self.options.docked_interpreter, + '/salt-source/tests/runtests.py'] for option in self._get_all_options(): if option.dest is None: # For example --version continue - if option.dest in ('docked', 'verbosity'): - # We don't need to pass the --docked argument inside the docker + if option.dest and (option.dest in ('verbosity',) or + option.dest.startswith('docked')): + # We don't need to pass any docker related arguments inside the # container, and verbose will be handled bellow continue @@ -493,16 +692,22 @@ '-{0}'.format('v' * (self.options.verbosity - 1)) ) - print_header( - 'Running the tests suite under the {0!r} docker container'.format( - container - ) - ) + print(' * Docker command: {0}'.format(' '.join(calling_args))) + print(' * Running the tests suite under the {0!r} docker ' + 'container. CID:'.format(container)), + sys.stdout.flush() - cidfile = tempfile.mktemp(prefix='docked-testsuite-', suffix='.cid') + cidfile = os.environ.get( + 'DOCKER_CIDFILE', + tempfile.mktemp(prefix='docked-testsuite-', suffix='.cid') + ) call = subprocess.Popen( ['docker', 'run', + #'--rm=true', Do not remove the container automatically, we need + # to get information back, even for stopped containers + '-t', # --tty but older versions don't support longopts + '-i', # --interactive=true, same as above '-v', '{0}:/salt-source'.format(self.source_code_basedir), '-w', @@ -515,16 +720,40 @@ 'LINES={0}'.format(HEIGHT), '-cidfile={0}'.format(cidfile), container, - ] + calling_args, + # We need to pass the runtests.py arguments as a single string so + # that the start-me-up.sh script can handle them properly + ' '.join(calling_args), + ], env=os.environ.copy(), close_fds=True, ) - signalled = terminating = exiting = False + cid = None + cid_printed = terminating = exiting = False + signal_handler_installed = signalled = False + + time.sleep(0.25) while True: try: time.sleep(0.15) + if cid_printed is False: + with closing(open(cidfile)) as cidfile_fd: + cid = cidfile_fd.read() + if cid: + print(cid) + sys.stdout.flush() + cid_printed = True + # Install our signal handler to properly shutdown + # the docker container + for sig in (signal.SIGTERM, signal.SIGINT, + signal.SIGHUP, signal.SIGQUIT): + signal.signal( + sig, + partial(stop_running_docked_container, cid) + ) + signal_handler_installed = True + if exiting: break elif terminating and not exiting: @@ -537,7 +766,7 @@ else: call.poll() if call.returncode is not None: - # Finshed + # Finished break except KeyboardInterrupt: print('Caught CTRL-C, exiting...') @@ -545,23 +774,16 @@ call.send_signal(signal.SIGINT) call.wait() - time.sleep(2) - - print_header('', inline=True) - print(' Cleaning Up Temporary Docker Container:'), - sys.stdout.flush() - cleanup_call = subprocess.Popen( - ['docker', 'rm', open(cidfile).read().strip()], - env=os.environ.copy(), - close_fds=True, - stdout=subprocess.PIPE - ) - os.unlink(cidfile) - cleanup_call.wait() - print(cleanup_call.stdout.read().strip()) - print_header('', inline=True) + time.sleep(0.25) - self.exit(call.returncode) + # Finish up + if signal_handler_installed: + stop_running_docked_container( + cid, + signum=(signal.SIGINT if signalled else WEIRD_SIGNAL_NUM) + ) + else: + sys.exit(call.returncode) class SaltTestcaseParser(SaltTestingParser): @@ -593,7 +815,8 @@ if not isinstance(testcase, list): header = '{0} Tests'.format(testcase.__name__) - print_header('Starting {0}'.format(header)) + print_header('Starting {0}'.format(header), + width=self.options.output_columns) runner = TextTestRunner( verbosity=self.options.verbosity).run(tests) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/pylintplugins/flask_sqlalchemy_transform.py new/SaltTesting-2014.4.24/salttesting/pylintplugins/flask_sqlalchemy_transform.py --- old/SaltTesting-0.5.4/salttesting/pylintplugins/flask_sqlalchemy_transform.py 1970-01-01 01:00:00.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/pylintplugins/flask_sqlalchemy_transform.py 2014-01-06 17:18:17.000000000 +0100 @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` + :copyright: © 2014 by the SaltStack Team, see AUTHORS for more details. + :license: Apache 2.0, see LICENSE for more details. + + + pylint_helpers + ~~~~~~~~~~~~~~ + + Help PyLint understand some of this projects parts +''' + +from astroid import MANAGER +from astroid import nodes +from astroid.builder import AstroidBuilder + + +def flask_sqlalchemy_transform(module): + if module.name != 'flask_sqlalchemy': + return + + import flask_sqlalchemy + flask_sqlalchemy._include_sqlalchemy(flask_sqlalchemy.SQLAlchemy) + + fake = AstroidBuilder(MANAGER).inspect_build(flask_sqlalchemy) + + for func_name, func in fake.locals.items(): + if func_name == 'SQLAlchemy': + func[0].Model = fake.locals['Model'][0] + module.locals[func_name] = func + + +def register(linter): + ''' + Allow this to be setup when loading the plugins + ''' + MANAGER.register_transform(nodes.Module, flask_sqlalchemy_transform) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/pylintplugins/pep8.py new/SaltTesting-2014.4.24/salttesting/pylintplugins/pep8.py --- old/SaltTesting-0.5.4/salttesting/pylintplugins/pep8.py 2014-01-02 12:03:08.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/pylintplugins/pep8.py 2014-04-15 10:37:43.000000000 +0200 @@ -99,6 +99,11 @@ # Log warning?? continue + if code == 'E113': + if _PROCESSED_NODES[node.path].lines[lineno-1].strip().startswith('#'): + # If E113 is triggered in comments, which I consider a bug, + # skip it. See https://github.com/jcrocholl/pep8/issues/274 + continue self.add_message(pylintcode, line=lineno, args=code) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/pylintplugins/strings.py new/SaltTesting-2014.4.24/salttesting/pylintplugins/strings.py --- old/SaltTesting-0.5.4/salttesting/pylintplugins/strings.py 2014-01-02 12:03:08.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/pylintplugins/strings.py 2014-01-12 22:17:08.000000000 +0100 @@ -12,7 +12,11 @@ ''' import sys -from logilab import astng +try: + # >= pylint 1.0 + import astroid +except ImportError: # pylint < 1.0 + from logilab import astng as astroid from pylint.checkers import utils from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages @@ -54,10 +58,10 @@ @check_messages(*(MSGS.keys())) def visit_callfunc(self, node): func = utils.safe_infer(node.func) - if isinstance(func, astng.BoundMethod) and func.name == 'format': + if isinstance(func, astroid.BoundMethod) and func.name == 'format': # If there's a .format() call, run the code below - if isinstance(node.func.expr, astng.Name): + if isinstance(node.func.expr, astroid.Name): # This is for: # foo = 'Foo {} bar' # print(foo.format(blah) @@ -80,7 +84,7 @@ # If it does not have an value attribute, it's not worth # checking return - elif isinstance(node.func.expr.value, astng.Name): + elif isinstance(node.func.expr.value, astroid.Name): # No need to check these either return elif '{}' in node.func.expr.value: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/unit.py new/SaltTesting-2014.4.24/salttesting/unit.py --- old/SaltTesting-0.5.4/salttesting/unit.py 2014-01-02 12:03:08.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/unit.py 2014-02-06 20:12:42.000000000 +0100 @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ''' :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` - :copyright: © 2013 by the SaltStack Team, see AUTHORS for more details. + :copyright: © 2013-2014 by the SaltStack Team, see AUTHORS for more details :license: Apache 2.0, see LICENSE for more details. @@ -11,20 +11,26 @@ Unit test related functions ''' +# Import python libs import sys +import logging + # support python < 2.7 via unittest2 if sys.version_info < (2, 7): try: from unittest2 import ( TestLoader as _TestLoader, - TextTestRunner as _TextTestRunner, + TextTestRunner as __TextTestRunner, TestCase as __TestCase, expectedFailure, TestSuite as _TestSuite, + skip, skipIf, TestResult as _TestResult, + TextTestResult as __TextTestResult ) + from unittest2.case import _id class NewStyleClassMixin(object): ''' @@ -38,7 +44,7 @@ class TestLoader(_TestLoader, NewStyleClassMixin): pass - class TextTestRunner(_TextTestRunner, NewStyleClassMixin): + class _TextTestRunner(__TextTestRunner, NewStyleClassMixin): pass class _TestCase(__TestCase, NewStyleClassMixin): @@ -50,18 +56,25 @@ class TestResult(_TestResult, NewStyleClassMixin): pass + class _TextTestResult(__TextTestResult, NewStyleClassMixin): + pass + + except ImportError: raise SystemExit('You need to install unittest2 to run the salt tests') else: from unittest import ( TestLoader, - TextTestRunner, + TextTestRunner as _TextTestRunner, TestCase as _TestCase, expectedFailure, TestSuite, + skip, skipIf, TestResult, + TextTestResult as _TextTestResult ) + from unittest.case import _id class TestCase(_TestCase): @@ -133,6 +146,31 @@ return _TestCase.failIfAlmostEqual(self, *args, **kwargs) +class TextTestResult(_TextTestResult): + ''' + Custom TestResult class whith logs the start and the end of a test + ''' + + def startTest(self, test): + logging.getLogger(__name__).debug( + '>>>>> START >>>>> {0}'.format(test.id()) + ) + return super(TextTestResult, self).startTest(test) + + def stopTest(self, test): + logging.getLogger(__name__).debug( + '<<<<< END <<<<<<< {0}'.format(test.id()) + ) + return super(TextTestResult, self).stopTest(test) + + +class TextTestRunner(_TextTestRunner): + ''' + Custom Text tests runner to log the start and the end of a test case + ''' + resultclass = TextTestResult + + __all__ = [ 'TestLoader', 'TextTestRunner', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/version.py new/SaltTesting-2014.4.24/salttesting/version.py --- old/SaltTesting-0.5.4/salttesting/version.py 2014-01-02 12:09:13.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/version.py 2014-04-24 14:02:39.000000000 +0200 @@ -8,5 +8,5 @@ :license: Apache 2.0, see LICENSE for more details. ''' -__version_info__ = (0, 5, 4) +__version_info__ = (2014, 4, 24) __version__ = '.'.join(map(str, __version_info__)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/salttesting/xmlunit.py new/SaltTesting-2014.4.24/salttesting/xmlunit.py --- old/SaltTesting-0.5.4/salttesting/xmlunit.py 1970-01-01 01:00:00.000000000 +0100 +++ new/SaltTesting-2014.4.24/salttesting/xmlunit.py 2014-02-09 03:01:05.000000000 +0100 @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` + :copyright: © 2014 by the SaltStack Team, see AUTHORS for more details. + :license: Apache 2.0, see LICENSE for more details. + + + salttesting.xmlunit + ~~~~~~~~~~~~~~~~~~~ + + XML Unit Tests +''' + +# Import python libs +import logging + +try: + import xmlrunner + HAS_XMLRUNNER = True + + class _XMLTestResult(xmlrunner._XMLTestResult): + def startTest(self, test): + logging.getLogger(__name__).debug( + '>>>>> START >>>>> {0}'.format(test.id()) + ) + # xmlrunner classes are NOT new-style classes + return xmlrunner._XMLTestResult.startTest(self, test) + + def stopTest(self, test): + logging.getLogger(__name__).debug( + '<<<<< END <<<<<<< {0}'.format(test.id()) + ) + # xmlrunner classes are NOT new-style classes + return xmlrunner._XMLTestResult.stopTest(self, test) + + class XMLTestRunner(xmlrunner.XMLTestRunner): + def _make_result(self): + return _XMLTestResult( + self.stream, + self.descriptions, + self.verbosity, + self.elapsed_times + ) + + def run(self, test): + result = xmlrunner.XMLTestRunner.run(self, test) + self.stream.writeln('Finished generating XML reports') + return result + +except ImportError: + HAS_XMLRUNNER = False + + class XMLTestRunner(object): + ''' + This is a dumb class just so we don't break projects at import time + ''' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SaltTesting-0.5.4/setup.py new/SaltTesting-2014.4.24/setup.py --- old/SaltTesting-0.5.4/setup.py 2013-11-12 06:14:36.000000000 +0100 +++ new/SaltTesting-2014.4.24/setup.py 2014-01-02 12:19:55.000000000 +0100 @@ -48,7 +48,7 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'Intended Audience :: Information Technology', -- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org
participants (1)
-
root@hilbert.suse.de