diff options
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | setup.cfg | 6 | ||||
-rw-r--r-- | tox.ini | 1 | ||||
-rw-r--r-- | xtesting/ci/run_tests.py | 10 | ||||
-rw-r--r-- | xtesting/ci/testcases.yaml | 12 | ||||
-rw-r--r-- | xtesting/core/feature.py | 9 | ||||
-rw-r--r-- | xtesting/core/testcase.py | 13 | ||||
-rw-r--r-- | xtesting/tests/unit/ci/test_run_tests.py | 21 | ||||
-rw-r--r-- | xtesting/tests/unit/core/test_feature.py | 15 | ||||
-rw-r--r-- | xtesting/tests/unit/core/test_testcase.py | 35 | ||||
-rw-r--r-- | xtesting/tests/unit/utils/test_decorators.py | 13 |
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 @@ -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 @@ -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' |