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]

Reply via email to