Merge "Add workaround to handle the testtool skip exception in CLI test"
diff --git a/releasenotes/notes/fix-1847749-2670b1d4f6097a1a.yaml b/releasenotes/notes/fix-1847749-2670b1d4f6097a1a.yaml
new file mode 100644
index 0000000..d9ef626
--- /dev/null
+++ b/releasenotes/notes/fix-1847749-2670b1d4f6097a1a.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Bug#1847749. This privides the workaround of Skip Exception raised instead of skipping
+    the CLI tests. If you are running Tempest with stestr > 2.5.0 then use this fix.
+    Ref- https://github.com/testing-cabal/testtools/issues/272
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
index 3be55c0..74ae77c 100644
--- a/tempest/lib/base.py
+++ b/tempest/lib/base.py
@@ -14,11 +14,29 @@
 #    under the License.
 
 import os
+import sys
 
 import fixtures
+import pkg_resources
 import testtools
 
 
+def _handle_skip_exception():
+    try:
+        stestr_version = pkg_resources.parse_version(
+            pkg_resources.get_distribution("stestr").version)
+        stestr_min = pkg_resources.parse_version('2.5.0')
+        new_stestr = (stestr_version >= stestr_min)
+        import unittest
+        import unittest2
+        if sys.version_info >= (3, 5) and new_stestr:
+            testtools.TestCase.skipException = unittest.case.SkipTest
+        else:
+            testtools.TestCase.skipException = unittest2.case.SkipTest
+    except Exception:
+        pass
+
+
 class BaseTestCase(testtools.testcase.WithAttributes, testtools.TestCase):
     setUpClassCalled = False
 
@@ -33,6 +51,18 @@
         if hasattr(super(BaseTestCase, cls), 'setUpClass'):
             super(BaseTestCase, cls).setUpClass()
         cls.setUpClassCalled = True
+        # TODO(gmann): cls.handle_skip_exception is really workaround for
+        # testtools bug- https://github.com/testing-cabal/testtools/issues/272
+        # stestr which is used by Tempest internally to run the test switch
+        # the customize test runner(which use stdlib unittest) for >=py3.5
+        # else testtools.run.- https://github.com/mtreinish/stestr/pull/265
+        # These two test runner are not compatible due to skip exception
+        # handling(due to unittest2). testtools.run treat unittestt.SkipTest
+        # as error and stdlib unittest treat unittest2.case.SkipTest raised
+        # by testtools.TestCase.skipException.
+        # The below workaround can be removed once testtools fix issue# 272.
+        cls.orig_skip_exception = testtools.TestCase.skipException
+        _handle_skip_exception()
 
     @classmethod
     def tearDownClass(cls):
@@ -40,6 +70,7 @@
             super(BaseTestCase, cls).tearDownClass()
 
     def setUp(self):
+        testtools.TestCase.skipException = self.orig_skip_exception
         super(BaseTestCase, self).setUp()
         if not self.setUpClassCalled:
             raise RuntimeError("setUpClass does not calls the super's "
diff --git a/tempest/test.py b/tempest/test.py
index 1e5cd19..f383bc1 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -20,7 +20,6 @@
 import debtcollector.moves
 import fixtures
 from oslo_log import log as logging
-import pkg_resources
 import six
 import testtools
 
@@ -28,6 +27,7 @@
 from tempest.common import credentials_factory as credentials
 from tempest.common import utils
 from tempest import config
+from tempest.lib import base as lib_base
 from tempest.lib.common import fixed_network
 from tempest.lib.common import profiler
 from tempest.lib.common import validation_resources as vr
@@ -78,10 +78,6 @@
 atexit.register(validate_tearDownClass)
 
 
-class DummyException(Exception):
-    pass
-
-
 class BaseTestCase(testtools.testcase.WithAttributes,
                    testtools.TestCase):
     """The test base class defines Tempest framework for class level fixtures.
@@ -145,26 +141,6 @@
         cls._teardowns = []
 
     @classmethod
-    def handle_skip_exception(cls):
-        try:
-            stestr_version = pkg_resources.parse_version(
-                pkg_resources.get_distribution("stestr").version)
-            stestr_min = pkg_resources.parse_version('2.5.0')
-            new_stestr = (stestr_version >= stestr_min)
-            import unittest
-            import unittest2
-            if sys.version_info >= (3, 5) and new_stestr:
-                exc = unittest2.case.SkipTest
-                exc_to_raise = unittest.case.SkipTest
-            else:
-                exc = unittest.case.SkipTest
-                exc_to_raise = unittest2.case.SkipTest
-        except Exception:
-            exc = DummyException
-            exc_to_raise = DummyException
-        return exc, exc_to_raise
-
-    @classmethod
     def setUpClass(cls):
         cls.__setupclass_called = True
         # Reset state
@@ -183,8 +159,9 @@
         # as error and stdlib unittest treat unittest2.case.SkipTest raised
         # by testtools.TestCase.skipException.
         # The below workaround can be removed once testtools fix issue# 272.
+        orig_skip_exception = testtools.TestCase.skipException
+        lib_base._handle_skip_exception()
         try:
-            exc, exc_to_raise = cls.handle_skip_exception()
             cls.skip_checks()
 
             if not cls.__skip_checks_called:
@@ -202,12 +179,6 @@
             # Additional class-wide test resources
             cls._teardowns.append(('resources', cls.resource_cleanup))
             cls.resource_setup()
-        except exc as e:
-            # NOTE(dviroel): the exception may be raised after setting up the
-            # user credentials, so we must call tearDownClass to release all
-            # allocated resources.
-            cls.tearDownClass()
-            raise exc_to_raise(e.args)
         except Exception:
             etype, value, trace = sys.exc_info()
             LOG.info("%s raised in %s.setUpClass. Invoking tearDownClass.",
@@ -217,6 +188,8 @@
                 six.reraise(etype, value, trace)
             finally:
                 del trace  # to avoid circular refs
+        finally:
+            testtools.TestCase.skipException = orig_skip_exception
 
     @classmethod
     def tearDownClass(cls):
diff --git a/tempest/tests/lib/test_base.py b/tempest/tests/lib/test_base.py
index 27cda1a..2c16e1c 100644
--- a/tempest/tests/lib/test_base.py
+++ b/tempest/tests/lib/test_base.py
@@ -48,6 +48,7 @@
     @classmethod
     def setUpClass(cls):  # noqa
         """Simulate absence of super() call."""
+        cls.orig_skip_exception = cls.skipException
 
     def setUp(self):
         try:
diff --git a/tempest/tests/test_test.py b/tempest/tests/test_test.py
index a2e0efd..49fd010 100644
--- a/tempest/tests/test_test.py
+++ b/tempest/tests/test_test.py
@@ -531,8 +531,8 @@
     def test_skip_only(self):
         # If a skip condition is hit in the test, no credentials or resource
         # is provisioned / cleaned-up
-        exc, _ = test.BaseTestCase.handle_skip_exception()
-        self.mocks['skip_checks'].side_effect = (exc)
+        self.mocks['skip_checks'].side_effect = (
+            testtools.TestCase.skipException())
         suite = unittest.TestSuite((self.test,))
         log = []
         result = LoggingTestResult(log)