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 - [email protected]
+
+- 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 ([email protected])`
- :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 ([email protected])`
+ :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 ([email protected])`
- :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 ([email protected])`
+ :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: [email protected]
For additional commands, e-mail: [email protected]