aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCédric Ollivier <cedric.ollivier@orange.com>2018-08-10 12:55:10 +0200
committerCédric Ollivier <cedric.ollivier@orange.com>2018-08-12 18:32:59 +0200
commit95092b511dbacac412dbe5b8ff3a28abb3d3f664 (patch)
tree24a547cf1a687af0d4db184375abcc03aef998ba
parenta9491ef0948f8fe3eb3772b6b6ae44d86bde7e9b (diff)
Leverage on abc and stevedore
Change-Id: I7b3c4c0c5dd0c9e6fb3e52c3fff5221d4b831b02 Signed-off-by: Cédric Ollivier <cedric.ollivier@orange.com>
-rw-r--r--requirements.txt1
-rw-r--r--setup.cfg6
-rw-r--r--tox.ini1
-rw-r--r--xtesting/ci/run_tests.py10
-rw-r--r--xtesting/ci/testcases.yaml12
-rw-r--r--xtesting/core/feature.py9
-rw-r--r--xtesting/core/testcase.py13
-rw-r--r--xtesting/tests/unit/ci/test_run_tests.py21
-rw-r--r--xtesting/tests/unit/core/test_feature.py15
-rw-r--r--xtesting/tests/unit/core/test_testcase.py35
-rw-r--r--xtesting/tests/unit/utils/test_decorators.py13
11 files changed, 89 insertions, 47 deletions
diff --git a/requirements.txt b/requirements.txt
index dc05d71d..02b36559 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,6 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr!=2.1.0,>=2.0.0 # Apache-2.0
+stevedore>=1.20.0 # Apache-2.0
PyYAML>=3.12 # MIT
enum34>=1.0.4;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
requests>=2.14.2 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index 4c1a1edc..279e46a3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -23,6 +23,12 @@ packages = xtesting
[entry_points]
console_scripts =
run_tests = xtesting.ci.run_tests:main
+xtesting.testcase =
+ bashfeature = xtesting.core.feature:BashFeature
+ robotframework = xtesting.core.robotframework:RobotFramework
+ unit = xtesting.core.unit:Suite
+ first = xtesting.samples.first:Test
+ second = xtesting.samples.second:Test
[build_sphinx]
all_files = 1
diff --git a/tox.ini b/tox.ini
index 6c6de708..98df801f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,6 @@
[tox]
envlist = docs,pep8,pylint,yamllint,bashate,py35,py27,perm,cover
+skipsdist = True
[testenv]
usedevelop = True
diff --git a/xtesting/ci/run_tests.py b/xtesting/ci/run_tests.py
index 59ed5617..3a3e6858 100644
--- a/xtesting/ci/run_tests.py
+++ b/xtesting/ci/run_tests.py
@@ -15,7 +15,6 @@
import argparse
import errno
-import importlib
import logging
import logging.config
import os
@@ -27,6 +26,7 @@ import enum
import pkg_resources
import prettytable
import six
+from stevedore import driver
import yaml
from xtesting.ci import tier_builder
@@ -151,10 +151,12 @@ class Runner(object):
if run_dict:
try:
LOGGER.info("Loading test case '%s'...", test.get_name())
- module = importlib.import_module(run_dict['module'])
- cls = getattr(module, run_dict['class'])
test_dict = Runner.get_dict_by_test(test.get_name())
- test_case = cls(**test_dict)
+ test_case = driver.DriverManager(
+ namespace='xtesting.testcase',
+ name=run_dict['name'],
+ invoke_on_load=True,
+ invoke_kwds=test_dict).driver
self.executed_test_cases[test.get_name()] = test_case
test_case.check_requirements()
if test_case.is_skipped:
diff --git a/xtesting/ci/testcases.yaml b/xtesting/ci/testcases.yaml
index 6ab5927f..7c621fd0 100644
--- a/xtesting/ci/testcases.yaml
+++ b/xtesting/ci/testcases.yaml
@@ -13,8 +13,7 @@ tiers:
clean_flag: false
description: ''
run:
- module: 'xtesting.samples.first'
- class: 'Test'
+ name: 'first'
-
case_name: second
@@ -24,8 +23,7 @@ tiers:
clean_flag: false
description: ''
run:
- module: 'xtesting.samples.second'
- class: 'Test'
+ name: 'second'
-
case_name: third
@@ -35,8 +33,7 @@ tiers:
clean_flag: false
description: ''
run:
- module: 'xtesting.core.feature'
- class: 'BashFeature'
+ name: 'bashfeature'
args:
cmd: 'echo -n Hello World; exit 0'
@@ -48,8 +45,7 @@ tiers:
clean_flag: false
description: ''
run:
- module: 'xtesting.core.unit'
- class: 'Suite'
+ name: 'unit'
args:
name: 'xtesting.samples.fourth'
diff --git a/xtesting/core/feature.py b/xtesting/core/feature.py
index 932e0230..86215515 100644
--- a/xtesting/core/feature.py
+++ b/xtesting/core/feature.py
@@ -13,21 +13,25 @@ Feature is considered as TestCase offered by Third-party. It offers
helpers to run any python method or any bash command.
"""
+import abc
import logging
import subprocess
import time
+import six
from xtesting.core import testcase
__author__ = ("Serena Feng <feng.xiaowei@zte.com.cn>, "
"Cedric Ollivier <cedric.ollivier@orange.com>")
+@six.add_metaclass(abc.ABCMeta)
class Feature(testcase.TestCase):
"""Base model for single feature."""
__logger = logging.getLogger(__name__)
+ @abc.abstractmethod
def execute(self, **kwargs):
"""Execute the Python method.
@@ -39,12 +43,7 @@ class Feature(testcase.TestCase):
Args:
kwargs: Arbitrary keyword arguments.
-
- Returns:
- -1.
"""
- # pylint: disable=unused-argument,no-self-use
- return -1
def run(self, **kwargs):
"""Run the feature.
diff --git a/xtesting/core/testcase.py b/xtesting/core/testcase.py
index 61a2e8c8..c3a1c38c 100644
--- a/xtesting/core/testcase.py
+++ b/xtesting/core/testcase.py
@@ -9,6 +9,7 @@
"""Define the parent class of all Xtesting TestCases."""
+import abc
from datetime import datetime
import json
import logging
@@ -17,6 +18,7 @@ import re
import requests
import prettytable
+import six
from xtesting.utils import decorators
from xtesting.utils import env
@@ -24,7 +26,9 @@ from xtesting.utils import env
__author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
-class TestCase(object): # pylint: disable=too-many-instance-attributes
+@six.add_metaclass(abc.ABCMeta)
+class TestCase(object):
+ # pylint: disable=too-many-instance-attributes
"""Base model for single test case."""
EX_OK = os.EX_OK
@@ -138,6 +142,7 @@ class TestCase(object): # pylint: disable=too-many-instance-attributes
"""
self.is_skipped = False
+ @abc.abstractmethod
def run(self, **kwargs):
"""Run the test case.
@@ -156,13 +161,7 @@ class TestCase(object): # pylint: disable=too-many-instance-attributes
Args:
kwargs: Arbitrary keyword arguments.
-
- Returns:
- TestCase.EX_RUN_ERROR.
"""
- # pylint: disable=unused-argument
- self.__logger.error("Run must be implemented")
- return TestCase.EX_RUN_ERROR
@decorators.can_dump_request_to_file
def push_to_db(self):
diff --git a/xtesting/tests/unit/ci/test_run_tests.py b/xtesting/tests/unit/ci/test_run_tests.py
index 392612bd..423bf486 100644
--- a/xtesting/tests/unit/ci/test_run_tests.py
+++ b/xtesting/tests/unit/ci/test_run_tests.py
@@ -20,6 +20,10 @@ from xtesting.core.testcase import TestCase
class FakeModule(TestCase):
def run(self, **kwargs):
+ self.start_time = 0
+ self.stop_time = 1
+ self.criteria = 100
+ self.result = 100
return TestCase.EX_OK
@@ -140,9 +144,8 @@ class RunTestsTesting(unittest.TestCase):
msg = "Cannot import the class for the test case."
self.assertTrue(msg in str(context.exception))
- @mock.patch('importlib.import_module', name="module",
- return_value=mock.Mock(test_class=mock.Mock(
- side_effect=FakeModule)))
+ @mock.patch('stevedore.driver.DriverManager',
+ return_value=mock.Mock(driver=FakeModule()))
@mock.patch('xtesting.ci.run_tests.Runner.get_dict_by_test')
def test_run_tests_default(self, *args):
mock_test = mock.Mock()
@@ -151,14 +154,15 @@ class RunTestsTesting(unittest.TestCase):
'is_enabled.return_value': True,
'needs_clean.return_value': True}
mock_test.configure_mock(**kwargs)
- test_run_dict = {'module': 'test_module',
- 'class': 'test_class'}
+ test_run_dict = {'name': 'test_module'}
with mock.patch('xtesting.ci.run_tests.Runner.get_run_dict',
return_value=test_run_dict):
self.runner.clean_flag = True
- self.runner.run_test(mock_test)
+ self.assertEqual(self.runner.run_test(mock_test), TestCase.EX_OK)
args[0].assert_called_with('test_name')
- args[1].assert_called_with('test_module')
+ args[1].assert_called_with(
+ invoke_kwds=mock.ANY, invoke_on_load=True, name='test_module',
+ namespace='xtesting.testcase')
self.assertEqual(self.runner.overall_result,
run_tests.Result.EX_OK)
@@ -170,8 +174,7 @@ class RunTestsTesting(unittest.TestCase):
'is_enabled.return_value': False,
'needs_clean.return_value': True}
mock_test.configure_mock(**kwargs)
- test_run_dict = {'module': 'test_module',
- 'class': 'test_class'}
+ test_run_dict = {'name': 'test_name'}
with mock.patch('xtesting.ci.run_tests.Runner.get_run_dict',
return_value=test_run_dict):
self.runner.clean_flag = True
diff --git a/xtesting/tests/unit/core/test_feature.py b/xtesting/tests/unit/core/test_feature.py
index 72bc488f..a4ac5af7 100644
--- a/xtesting/tests/unit/core/test_feature.py
+++ b/xtesting/tests/unit/core/test_feature.py
@@ -18,6 +18,19 @@ from xtesting.core import feature
from xtesting.core import testcase
+class FakeTestCase(feature.Feature):
+
+ def execute(self, **kwargs):
+ pass
+
+
+class AbstractFeatureTesting(unittest.TestCase):
+
+ def test_run_unimplemented(self):
+ with self.assertRaises(TypeError):
+ feature.Feature(case_name="feature", project_name="xtesting")
+
+
class FeatureTestingBase(unittest.TestCase):
_case_name = "foo"
@@ -46,7 +59,7 @@ class FeatureTesting(FeatureTestingBase):
# what will break these unit tests.
logging.disable(logging.CRITICAL)
with mock.patch('six.moves.builtins.open'):
- self.feature = feature.Feature(
+ self.feature = FakeTestCase(
project_name=self._project_name, case_name=self._case_name)
def test_run_exc(self):
diff --git a/xtesting/tests/unit/core/test_testcase.py b/xtesting/tests/unit/core/test_testcase.py
index 6b83b97c..51ea6f35 100644
--- a/xtesting/tests/unit/core/test_testcase.py
+++ b/xtesting/tests/unit/core/test_testcase.py
@@ -7,6 +7,8 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
+# pylint: disable=missing-docstring
+
"""Define the class required to fully cover testcase."""
from datetime import datetime
@@ -23,10 +25,22 @@ from xtesting.core import testcase
__author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
-class TestCaseTesting(unittest.TestCase):
- """The class testing TestCase."""
+class FakeTestCase(testcase.TestCase):
+ # pylint: disable=too-many-instance-attributes
+
+ def run(self, **kwargs):
+ return testcase.TestCase.EX_OK
+
+
+class AbstractTestCaseTesting(unittest.TestCase):
+
+ def test_run_unimplemented(self):
+ with self.assertRaises(TypeError):
+ testcase.TestCase(case_name="base", project_name="xtesting")
- # pylint: disable=missing-docstring,too-many-public-methods
+
+class TestCaseTesting(unittest.TestCase):
+ # pylint: disable=too-many-instance-attributes,too-many-public-methods
_case_name = "base"
_project_name = "xtesting"
@@ -35,8 +49,8 @@ class TestCaseTesting(unittest.TestCase):
_headers = {'Content-Type': 'application/json'}
def setUp(self):
- self.test = testcase.TestCase(case_name=self._case_name,
- project_name=self._project_name)
+ self.test = FakeTestCase(
+ case_name=self._case_name, project_name=self._project_name)
self.test.start_time = 1
self.test.stop_time = 2
self.test.result = 100
@@ -47,9 +61,8 @@ class TestCaseTesting(unittest.TestCase):
os.environ['NODE_NAME'] = "node_name"
os.environ['BUILD_TAG'] = "foo-daily-master-bar"
- def test_run_unimplemented(self):
- self.assertEqual(self.test.run(),
- testcase.TestCase.EX_RUN_ERROR)
+ def test_run_fake(self):
+ self.assertEqual(self.test.run(), testcase.TestCase.EX_OK)
def _test_pushdb_missing_attribute(self):
self.assertEqual(self.test.push_to_db(),
@@ -255,13 +268,11 @@ class TestCaseTesting(unittest.TestCase):
def test_str_project_name_ko(self):
self.test.project_name = None
- self.assertIn("<xtesting.core.testcase.TestCase object at",
- str(self.test))
+ self.assertIn("FakeTestCase object at", str(self.test))
def test_str_case_name_ko(self):
self.test.case_name = None
- self.assertIn("<xtesting.core.testcase.TestCase object at",
- str(self.test))
+ self.assertIn("FakeTestCase object at", str(self.test))
def test_str_pass(self):
duration = '01:01'
diff --git a/xtesting/tests/unit/utils/test_decorators.py b/xtesting/tests/unit/utils/test_decorators.py
index 83b182a8..c08a7ea3 100644
--- a/xtesting/tests/unit/utils/test_decorators.py
+++ b/xtesting/tests/unit/utils/test_decorators.py
@@ -28,6 +28,13 @@ FILE = '{}/null'.format(DIR)
URL = 'file://{}'.format(FILE)
+class FakeTestCase(testcase.TestCase):
+ # pylint: disable=missing-docstring
+
+ def run(self, **kwargs):
+ return testcase.TestCase.EX_OK
+
+
class DecoratorsTesting(unittest.TestCase):
# pylint: disable=missing-docstring
@@ -66,7 +73,7 @@ class DecoratorsTesting(unittest.TestCase):
return json.dumps(data, sort_keys=True)
def _get_testcase(self):
- test = testcase.TestCase(
+ test = FakeTestCase(
project_name=self._project_name, case_name=self._case_name)
test.start_time = self._start_time
test.stop_time = self._stop_time
@@ -74,6 +81,10 @@ class DecoratorsTesting(unittest.TestCase):
test.details = {}
return test
+ def test_run_fake(self):
+ test = self._get_testcase()
+ self.assertEqual(test.run(), testcase.TestCase.EX_OK)
+
@mock.patch('requests.post')
def test_http_shema(self, *args):
os.environ['TEST_DB_URL'] = 'http://127.0.0.1'