From 4391bbd783c9ca51eef7883b61292ff1b1beba7c Mon Sep 17 00:00:00 2001 From: Moshe Date: Mon, 26 Mar 2018 13:54:47 +0300 Subject: Add unit tests to improve coverage Issue-ID: VNFSDK-183 Change-Id: I26d2412d3fcfd25431722a3da7cb40b23e2a98b4 Signed-off-by: Moshe --- vnftest/common/utils.py | 27 + vnftest/onap/onap_api_call.py | 3 +- vnftest/onap/package_upload.py | 43 - vnftest/runners/base.py | 1 - vnftest/runners/dynamictp.py | 2 +- vnftest/tests/__init__.py | 77 -- vnftest/tests/unit/__init__.py | 0 vnftest/tests/unit/common/__init__.py | 0 vnftest/tests/unit/common/config_sample.yaml | 17 + vnftest/tests/unit/common/test_httpClient.py | 43 + vnftest/tests/unit/common/test_process.py | 152 +++ vnftest/tests/unit/common/test_template_format.py | 49 + vnftest/tests/unit/common/test_utils.py | 1125 +++++++++++++++++++++ vnftest/tests/unit/common/test_yaml_loader.py | 34 + vnftest/tests/unit/core/test_commands.py | 89 ++ 15 files changed, 1539 insertions(+), 123 deletions(-) delete mode 100644 vnftest/onap/package_upload.py create mode 100644 vnftest/tests/unit/__init__.py create mode 100644 vnftest/tests/unit/common/__init__.py create mode 100644 vnftest/tests/unit/common/config_sample.yaml create mode 100644 vnftest/tests/unit/common/test_httpClient.py create mode 100644 vnftest/tests/unit/common/test_process.py create mode 100644 vnftest/tests/unit/common/test_template_format.py create mode 100644 vnftest/tests/unit/common/test_utils.py create mode 100644 vnftest/tests/unit/common/test_yaml_loader.py create mode 100755 vnftest/tests/unit/core/test_commands.py (limited to 'vnftest') diff --git a/vnftest/common/utils.py b/vnftest/common/utils.py index e62b5db..fbc8391 100644 --- a/vnftest/common/utils.py +++ b/vnftest/common/utils.py @@ -397,3 +397,30 @@ class Timer(object): def __getattr__(self, item): return getattr(self.delta, item) + +def find_relative_file(path, task_path): + """ + Find file in one of places: in abs of path or relative to a directory path, + in this order. + + :param path: + :param task_path: + :return str: full path to file + """ + # fixme: create schema to validate all fields have been provided + for lookup in [os.path.abspath(path), os.path.join(task_path, path)]: + try: + with open(lookup): + return lookup + except IOError: + pass + raise IOError(errno.ENOENT, 'Unable to find {} file'.format(path)) + + +def open_relative_file(path, task_path): + try: + return open(path) + except IOError as e: + if e.errno == errno.ENOENT: + return open(os.path.join(task_path, path)) + raise diff --git a/vnftest/onap/onap_api_call.py b/vnftest/onap/onap_api_call.py index 8fbc98c..a19138b 100644 --- a/vnftest/onap/onap_api_call.py +++ b/vnftest/onap/onap_api_call.py @@ -31,7 +31,8 @@ LOG = logging.getLogger(__name__) class OnapApiCall(base.Step): - + """Call ONAP API + """ __step_type__ = "OnapApiCall" def __init__(self, step_cfg, context, input_params): diff --git a/vnftest/onap/package_upload.py b/vnftest/onap/package_upload.py deleted file mode 100644 index 084de78..0000000 --- a/vnftest/onap/package_upload.py +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################## -# Copyright 2018 EuropeanSoftwareMarketingLtd. -# =================================================================== -# Licensed under the ApacheLicense, Version2.0 (the"License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# -# software distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and limitations under -# the License -############################################################################## -from __future__ import absolute_import - -import copy -import logging -import time - -import os -import yaml - -from vnftest.common import constants as consts -from vnftest.common import rest_client -from vnftest.common.exceptions import MandatoryKeyException, InputParameterMissing -from vnftest.contexts.base import Context -from vnftest.crawlers.base import Crawler -from vnftest.onap.common.vnf_type_crawler import VnfTypeCrawler -from vnftest.onap.onap_api_call import OnapApiCall - -LOG = logging.getLogger(__name__) - - -class PackageUpload(OnapApiCall): - - __step_type__ = "PackageUpload" - - def __init__(self, step_cfg, context_cfg, input_params): - super(PackageUpload, self).__init__(step_cfg, context_cfg, input_params) - - def setup(self): - super(PackageUpload, self).setup() - self.input_cfg.append({'parameter_name': "package_file_path", 'value': Context.vnf_descriptor["csar_package_location"]}) diff --git a/vnftest/runners/base.py b/vnftest/runners/base.py index c00a4cd..f13d0d6 100755 --- a/vnftest/runners/base.py +++ b/vnftest/runners/base.py @@ -27,7 +27,6 @@ from Queue import Empty import vnftest.common.utils as utils from vnftest.steps import base as base_step from vnftest.onap.onap_api_call import OnapApiCall -from vnftest.onap.package_upload import PackageUpload log = logging.getLogger(__name__) diff --git a/vnftest/runners/dynamictp.py b/vnftest/runners/dynamictp.py index e394567..e26a2bb 100755 --- a/vnftest/runners/dynamictp.py +++ b/vnftest/runners/dynamictp.py @@ -153,7 +153,7 @@ def _worker_process(queue, cls, method_name, step_cfg, class IterationRunner(base.Runner): - """Run a step to find the max throughput + """Run a step several times in a loop If the step ends before the time has elapsed, it will be started again. diff --git a/vnftest/tests/__init__.py b/vnftest/tests/__init__.py index 061e263..e69de29 100644 --- a/vnftest/tests/__init__.py +++ b/vnftest/tests/__init__.py @@ -1,77 +0,0 @@ -############################################################################## -# Copyright 2018 EuropeanSoftwareMarketingLtd. -# =================================================================== -# Licensed under the ApacheLicense, Version2.0 (the"License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# -# software distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and limitations under -# the License -############################################################################## -# vnftest comment: this is a modified copy of -# yardstick/tests/__init__.py - -import mock - - -STL_MOCKS = { - 'trex_stl_lib': mock.MagicMock(), - 'trex_stl_lib.base64': mock.MagicMock(), - 'trex_stl_lib.binascii': mock.MagicMock(), - 'trex_stl_lib.collections': mock.MagicMock(), - 'trex_stl_lib.copy': mock.MagicMock(), - 'trex_stl_lib.datetime': mock.MagicMock(), - 'trex_stl_lib.functools': mock.MagicMock(), - 'trex_stl_lib.imp': mock.MagicMock(), - 'trex_stl_lib.inspect': mock.MagicMock(), - 'trex_stl_lib.json': mock.MagicMock(), - 'trex_stl_lib.linecache': mock.MagicMock(), - 'trex_stl_lib.math': mock.MagicMock(), - 'trex_stl_lib.os': mock.MagicMock(), - 'trex_stl_lib.platform': mock.MagicMock(), - 'trex_stl_lib.pprint': mock.MagicMock(), - 'trex_stl_lib.random': mock.MagicMock(), - 'trex_stl_lib.re': mock.MagicMock(), - 'trex_stl_lib.scapy': mock.MagicMock(), - 'trex_stl_lib.socket': mock.MagicMock(), - 'trex_stl_lib.string': mock.MagicMock(), - 'trex_stl_lib.struct': mock.MagicMock(), - 'trex_stl_lib.sys': mock.MagicMock(), - 'trex_stl_lib.threading': mock.MagicMock(), - 'trex_stl_lib.time': mock.MagicMock(), - 'trex_stl_lib.traceback': mock.MagicMock(), - 'trex_stl_lib.trex_stl_async_client': mock.MagicMock(), - 'trex_stl_lib.trex_stl_client': mock.MagicMock(), - 'trex_stl_lib.trex_stl_exceptions': mock.MagicMock(), - 'trex_stl_lib.trex_stl_ext': mock.MagicMock(), - 'trex_stl_lib.trex_stl_jsonrpc_client': mock.MagicMock(), - 'trex_stl_lib.trex_stl_packet_builder_interface': mock.MagicMock(), - 'trex_stl_lib.trex_stl_packet_builder_scapy': mock.MagicMock(), - 'trex_stl_lib.trex_stl_port': mock.MagicMock(), - 'trex_stl_lib.trex_stl_stats': mock.MagicMock(), - 'trex_stl_lib.trex_stl_streams': mock.MagicMock(), - 'trex_stl_lib.trex_stl_types': mock.MagicMock(), - 'trex_stl_lib.types': mock.MagicMock(), - 'trex_stl_lib.utils': mock.MagicMock(), - 'trex_stl_lib.utils.argparse': mock.MagicMock(), - 'trex_stl_lib.utils.collections': mock.MagicMock(), - 'trex_stl_lib.utils.common': mock.MagicMock(), - 'trex_stl_lib.utils.json': mock.MagicMock(), - 'trex_stl_lib.utils.os': mock.MagicMock(), - 'trex_stl_lib.utils.parsing_opts': mock.MagicMock(), - 'trex_stl_lib.utils.pwd': mock.MagicMock(), - 'trex_stl_lib.utils.random': mock.MagicMock(), - 'trex_stl_lib.utils.re': mock.MagicMock(), - 'trex_stl_lib.utils.string': mock.MagicMock(), - 'trex_stl_lib.utils.sys': mock.MagicMock(), - 'trex_stl_lib.utils.text_opts': mock.MagicMock(), - 'trex_stl_lib.utils.text_tables': mock.MagicMock(), - 'trex_stl_lib.utils.texttable': mock.MagicMock(), - 'trex_stl_lib.warnings': mock.MagicMock(), - 'trex_stl_lib.yaml': mock.MagicMock(), - 'trex_stl_lib.zlib': mock.MagicMock(), - 'trex_stl_lib.zmq': mock.MagicMock(), -} diff --git a/vnftest/tests/unit/__init__.py b/vnftest/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vnftest/tests/unit/common/__init__.py b/vnftest/tests/unit/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vnftest/tests/unit/common/config_sample.yaml b/vnftest/tests/unit/common/config_sample.yaml new file mode 100644 index 0000000..2a00dfe --- /dev/null +++ b/vnftest/tests/unit/common/config_sample.yaml @@ -0,0 +1,17 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/unit/common/config_sample.yaml +releng: + dir: /home/opnfv/repos/releng diff --git a/vnftest/tests/unit/common/test_httpClient.py b/vnftest/tests/unit/common/test_httpClient.py new file mode 100644 index 0000000..7bd90d3 --- /dev/null +++ b/vnftest/tests/unit/common/test_httpClient.py @@ -0,0 +1,43 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/unit/common/test_httpClient.py + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from vnftest.common import httpClient + + +class HttpClientTestCase(unittest.TestCase): + + @mock.patch('vnftest.common.httpClient.requests') + def test_post(self, mock_requests): + url = 'http://localhost:5000/hello' + data = {'hello': 'world'} + headers = {'Content-Type': 'application/json'} + httpClient.HttpClient().post(url, data) + mock_requests.post.assert_called_with( + url, data=jsonutils.dump_as_bytes(data), + headers=headers) + + @mock.patch('vnftest.common.httpClient.requests') + def test_get(self, mock_requests): + url = 'http://localhost:5000/hello' + httpClient.HttpClient().get(url) + mock_requests.get.assert_called_with(url) diff --git a/vnftest/tests/unit/common/test_process.py b/vnftest/tests/unit/common/test_process.py new file mode 100644 index 0000000..db253b1 --- /dev/null +++ b/vnftest/tests/unit/common/test_process.py @@ -0,0 +1,152 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/unit/common/test_process.py + +import mock +import unittest + +from oslo_utils import encodeutils + +from vnftest.common import exceptions +from vnftest.common import process + + +class ProcessTestcase(unittest.TestCase): + def test_check_if_procces_failed_None(self): + p = mock.MagicMock(**{"exitcode": None, "name": "debug"}) + process.check_if_process_failed(p) + + def test_check_if_procces_failed_0(self): + p = mock.MagicMock(**{"exitcode": 0, "name": "debug"}) + process.check_if_process_failed(p) + + def test_check_if_procces_failed_1(self): + p = mock.MagicMock(**{"exitcode": 1, "name": "debug"}) + with self.assertRaises(RuntimeError): + process.check_if_process_failed(p) + + +@mock.patch("vnftest.common.process.multiprocessing") +class TerminateChildrenTestcase(unittest.TestCase): + def test_some_children(self, mock_multiprocessing): + p1 = mock.MagicMock() + p2 = mock.MagicMock() + mock_multiprocessing.active_children.return_value = [p1, p2] + process.terminate_children() + + def test_no_children(self, mock_multiprocessing): + mock_multiprocessing.active_children.return_value = [] + process.terminate_children() + + +class ExecuteTestCase(unittest.TestCase): + + RET_CODE_OK = 0 + RET_CODE_WRONG = 1 + + def setUp(self): + self._mock_create_process = mock.patch.object(process, + 'create_process') + self.mock_create_process = self._mock_create_process.start() + self.obj = mock.Mock() + self.cmd = mock.Mock() + self.obj.communicate = mock.Mock() + self.stdout = 'std out' + self.stderr = 'std err' + self.obj.communicate.return_value = (self.stdout, self.stderr) + self.mock_create_process.return_value = (self.obj, self.cmd) + self.input_cmd = 'input cmd' + self.additional_env = mock.Mock() + + def test_execute_with_input(self): + process_input = 'process input' + self.obj.returncode = self.RET_CODE_OK + out = process.execute(self.input_cmd, process_input=process_input, + additional_env=self.additional_env) + self.obj.communicate.assert_called_once_with( + encodeutils.to_utf8(process_input)) + self.mock_create_process.assert_called_once_with( + self.input_cmd, run_as_root=False, + additional_env=self.additional_env) + self.assertEqual(self.stdout, out) + + def test_execute_no_input(self): + self.obj.returncode = self.RET_CODE_OK + out = process.execute(self.input_cmd, + additional_env=self.additional_env) + self.obj.communicate.assert_called_once_with(None) + self.mock_create_process.assert_called_once_with( + self.input_cmd, run_as_root=False, + additional_env=self.additional_env) + self.assertEqual(self.stdout, out) + + def test_execute_exception(self): + self.obj.returncode = self.RET_CODE_WRONG + self.assertRaises(exceptions.ProcessExecutionError, process.execute, + self.input_cmd, additional_env=self.additional_env) + self.obj.communicate.assert_called_once_with(None) + + def test_execute_with_extra_code(self): + self.obj.returncode = self.RET_CODE_WRONG + out = process.execute(self.input_cmd, + additional_env=self.additional_env, + extra_ok_codes=[self.RET_CODE_WRONG]) + self.obj.communicate.assert_called_once_with(None) + self.mock_create_process.assert_called_once_with( + self.input_cmd, run_as_root=False, + additional_env=self.additional_env) + self.assertEqual(self.stdout, out) + + def test_execute_exception_no_check(self): + self.obj.returncode = self.RET_CODE_WRONG + out = process.execute(self.input_cmd, + additional_env=self.additional_env, + check_exit_code=False) + self.obj.communicate.assert_called_once_with(None) + self.mock_create_process.assert_called_once_with( + self.input_cmd, run_as_root=False, + additional_env=self.additional_env) + self.assertEqual(self.stdout, out) + + +class CreateProcessTestCase(unittest.TestCase): + + @mock.patch.object(process, 'subprocess_popen') + def test_process_string_command(self, mock_subprocess_popen): + cmd = 'command' + obj = mock.Mock() + mock_subprocess_popen.return_value = obj + out1, out2 = process.create_process(cmd) + self.assertEqual(obj, out1) + self.assertEqual([cmd], out2) + + @mock.patch.object(process, 'subprocess_popen') + def test_process_list_command(self, mock_subprocess_popen): + cmd = ['command'] + obj = mock.Mock() + mock_subprocess_popen.return_value = obj + out1, out2 = process.create_process(cmd) + self.assertEqual(obj, out1) + self.assertEqual(cmd, out2) + + @mock.patch.object(process, 'subprocess_popen') + def test_process_with_env(self, mock_subprocess_popen): + cmd = ['command'] + obj = mock.Mock() + additional_env = {'var1': 'value1'} + mock_subprocess_popen.return_value = obj + out1, out2 = process.create_process(cmd, additional_env=additional_env) + self.assertEqual(obj, out1) + self.assertEqual(['env', 'var1=value1'] + cmd, out2) diff --git a/vnftest/tests/unit/common/test_template_format.py b/vnftest/tests/unit/common/test_template_format.py new file mode 100644 index 0000000..d091b9f --- /dev/null +++ b/vnftest/tests/unit/common/test_template_format.py @@ -0,0 +1,49 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/unit/common/test_template_format.py + +from __future__ import absolute_import +import mock +import unittest +import yaml + +from vnftest.common import template_format + + +class TemplateFormatTestCase(unittest.TestCase): + + def test_parse_to_value_exception(self): + + # TODO(elfoley): Don't hide the error that occurs in + # template_format.parse + # TODO(elfoley): Separate these tests; one per error type + with mock.patch.object(yaml, 'load') as yaml_loader: + yaml_loader.side_effect = yaml.scanner.ScannerError() + self.assertRaises(ValueError, template_format.parse, 'FOOBAR') + yaml_loader.side_effect = yaml.parser.ParserError() + self.assertRaises(ValueError, template_format.parse, 'FOOBAR') + yaml_loader.side_effect = \ + yaml.reader.ReaderError('', '', '', '', '') + self.assertRaises(ValueError, template_format.parse, 'FOOBAR') + + def test_parse_no_version_format(self): + + yaml = '' + self.assertRaises(ValueError, template_format.parse, yaml) + yaml2 = "Parameters: {}\n" \ + "Mappings: {}\n" \ + "Resources: {}\n" \ + "Outputs: {}" + self.assertRaises(ValueError, template_format.parse, yaml2) diff --git a/vnftest/tests/unit/common/test_utils.py b/vnftest/tests/unit/common/test_utils.py new file mode 100644 index 0000000..da64d4e --- /dev/null +++ b/vnftest/tests/unit/common/test_utils.py @@ -0,0 +1,1125 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/unit/common/test_utils.py + +from copy import deepcopy +import errno +import importlib +import ipaddress +from itertools import product, chain +import mock +import os +import six +from six.moves import configparser +import unittest + +import vnftest +from vnftest import ssh +from vnftest.common import utils +from vnftest.common import constants + + +class IterSubclassesTestCase(unittest.TestCase): + # Disclaimer: this class is a modified copy from + # rally/tests/unit/common/plugin/test_discover.py + # Copyright 2015: Mirantis Inc. + + def test_itersubclasses(self): + class A(object): + pass + + class B(A): + pass + + class C(A): + pass + + class D(C): + pass + + self.assertEqual([B, C, D], list(utils.itersubclasses(A))) + + +class ImportModulesFromPackageTestCase(unittest.TestCase): + + @mock.patch('vnftest.common.utils.os.walk') + def test_import_modules_from_package_no_mod(self, mock_walk): + vnftest_root = os.path.dirname(os.path.dirname(vnftest.__file__)) + mock_walk.return_value = ([ + (os.path.join(vnftest_root, 'foo'), ['bar'], ['__init__.py']), + (os.path.join(vnftest_root, 'foo', 'bar'), [], ['baz.txt', 'qux.rst']) + ]) + + utils.import_modules_from_package('foo.bar') + + @mock.patch('vnftest.common.utils.os.walk') + @mock.patch.object(importlib, 'import_module') + def test_import_modules_from_package(self, mock_import_module, mock_walk): + + vnftest_root = os.path.dirname(os.path.dirname(vnftest.__file__)) + mock_walk.return_value = ([ + (os.path.join(vnftest_root, 'foo', os.pardir, 'bar'), [], ['baz.py']) + ]) + + utils.import_modules_from_package('foo.bar') + mock_import_module.assert_called_once_with('bar.baz') + + +class GetParaFromYaml(unittest.TestCase): + + @mock.patch('vnftest.common.utils.os.environ.get') + def test_get_param_para_not_found(self, get_env): + file_path = 'config_sample.yaml' + get_env.return_value = self._get_file_abspath(file_path) + args = 'releng.file' + default = 'hello' + self.assertTrue(constants.get_param(args, default), default) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + +class CommonUtilTestCase(unittest.TestCase): + + def setUp(self): + self.data = { + "benchmark": { + "data": { + "mpstat": { + "cpu0": { + "%sys": "0.00", + "%idle": "99.00" + }, + "loadavg": [ + "1.09", + "0.29" + ] + }, + "rtt": "1.03" + } + } + } + + def test__dict_key_flatten(self): + line = 'mpstat.loadavg1=0.29,rtt=1.03,mpstat.loadavg0=1.09,' \ + 'mpstat.cpu0.%idle=99.00,mpstat.cpu0.%sys=0.00' + # need to sort for assert to work + line = ",".join(sorted(line.split(','))) + flattened_data = utils.flatten_dict_key( + self.data['benchmark']['data']) + result = ",".join( + ("=".join(item) for item in sorted(flattened_data.items()))) + self.assertEqual(result, line) + + @mock.patch('vnftest.common.utils.open', create=True) + def test_(self, mock_open): + mock_open.side_effect = IOError + + with self.assertRaises(IOError): + utils.find_relative_file('my/path', 'task/path') + + self.assertEqual(mock_open.call_count, 2) + + @mock.patch('vnftest.common.utils.open', create=True) + def test_open_relative_path(self, mock_open): + mock_open_result = mock_open() + mock_open_call_count = 1 # initial call to get result + + self.assertEqual(utils.open_relative_file('foo', 'bar'), mock_open_result) + + mock_open_call_count += 1 # one more call expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertNotIn('bar', mock_open.call_args_list[-1][0][0]) + + def open_effect(*args, **kwargs): + if kwargs.get('name', args[0]) == os.path.join('bar', 'foo'): + return mock_open_result + raise IOError(errno.ENOENT, 'not found') + + mock_open.side_effect = open_effect + self.assertEqual(utils.open_relative_file('foo', 'bar'), mock_open_result) + + mock_open_call_count += 2 # two more calls expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertIn('bar', mock_open.call_args_list[-1][0][0]) + + # test an IOError of type ENOENT + mock_open.side_effect = IOError(errno.ENOENT, 'not found') + with self.assertRaises(IOError): + # the second call still raises + utils.open_relative_file('foo', 'bar') + + mock_open_call_count += 2 # two more calls expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertIn('bar', mock_open.call_args_list[-1][0][0]) + + # test an IOError other than ENOENT + mock_open.side_effect = IOError(errno.EBUSY, 'busy') + with self.assertRaises(IOError): + utils.open_relative_file('foo', 'bar') + + mock_open_call_count += 1 # one more call expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + + +class TestMacAddressToHex(unittest.TestCase): + + def test_mac_address_to_hex_list(self): + self.assertEqual(utils.mac_address_to_hex_list("ea:3e:e1:9a:99:e8"), + ['0xea', '0x3e', '0xe1', '0x9a', '0x99', '0xe8']) + + +class TranslateToStrTestCase(unittest.TestCase): + + def test_translate_to_str_unicode(self): + input_str = u'hello' + output_str = utils.translate_to_str(input_str) + + result = 'hello' + self.assertEqual(result, output_str) + + def test_translate_to_str_dict_list_unicode(self): + input_str = { + u'hello': {u'hello': [u'world']} + } + output_str = utils.translate_to_str(input_str) + + result = { + 'hello': {'hello': ['world']} + } + self.assertEqual(result, output_str) + + def test_translate_to_str_non_string(self): + input_value = object() + result = utils.translate_to_str(input_value) + self.assertIs(input_value, result) + + +class TestParseCpuInfo(unittest.TestCase): + + def test_single_socket_no_hyperthread(self): + cpuinfo = """\ +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel Core Processor (Haswell, no TSX) +stepping : 1 +microcode : 0x1 +cpu MHz : 2294.684 +cache size : 4096 KB +physical id : 0 +siblings : 5 +core id : 2 +cpu cores : 5 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat +bugs : +bogomips : 4589.36 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 3 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel Core Processor (Haswell, no TSX) +stepping : 1 +microcode : 0x1 +cpu MHz : 2294.684 +cache size : 4096 KB +physical id : 0 +siblings : 5 +core id : 3 +cpu cores : 5 +apicid : 3 +initial apicid : 3 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat +bugs : +bogomips : 4589.36 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 4 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel Core Processor (Haswell, no TSX) +stepping : 1 +microcode : 0x1 +cpu MHz : 2294.684 +cache size : 4096 KB +physical id : 0 +siblings : 5 +core id : 4 +cpu cores : 5 +apicid : 4 +initial apicid : 4 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat +bugs : +bogomips : 4589.36 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +""" + socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) + assert sorted(socket_map.keys()) == [0] + assert sorted(socket_map[0].keys()) == [2, 3, 4] + + def test_single_socket_hyperthread(self): + cpuinfo = """\ +processor : 5 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel(R) Xeon(R) CPU E3-1275 v3 @ 3.50GHz +stepping : 3 +microcode : 0x1d +cpu MHz : 3501.708 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 1 +cpu cores : 4 +apicid : 3 +initial apicid : 3 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts +bugs : +bogomips : 6987.36 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 6 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel(R) Xeon(R) CPU E3-1275 v3 @ 3.50GHz +stepping : 3 +microcode : 0x1d +cpu MHz : 3531.829 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 2 +cpu cores : 4 +apicid : 5 +initial apicid : 5 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts +bugs : +bogomips : 6987.36 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 7 +vendor_id : GenuineIntel +cpu family : 6 +model : 60 +model name : Intel(R) Xeon(R) CPU E3-1275 v3 @ 3.50GHz +stepping : 3 +microcode : 0x1d +cpu MHz : 3500.213 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 3 +cpu cores : 4 +apicid : 7 +initial apicid : 7 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts +bugs : +bogomips : 6987.24 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +""" + socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) + assert sorted(socket_map.keys()) == [0] + assert sorted(socket_map[0].keys()) == [1, 2, 3] + assert sorted(socket_map[0][1]) == [5] + assert sorted(socket_map[0][2]) == [6] + assert sorted(socket_map[0][3]) == [7] + + def test_dual_socket_hyperthread(self): + cpuinfo = """\ +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.976 +cache size : 56320 KB +physical id : 0 +siblings : 44 +core id : 1 +cpu cores : 22 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4401.07 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1226.892 +cache size : 56320 KB +physical id : 0 +siblings : 44 +core id : 2 +cpu cores : 22 +apicid : 4 +initial apicid : 4 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4400.84 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 43 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.305 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 28 +cpu cores : 22 +apicid : 120 +initial apicid : 120 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4411.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 44 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.305 +cache size : 56320 KB +physical id : 0 +siblings : 44 +core id : 0 +cpu cores : 22 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4410.61 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 85 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.573 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 26 +cpu cores : 22 +apicid : 117 +initial apicid : 117 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4409.07 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 86 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.305 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 27 +cpu cores : 22 +apicid : 119 +initial apicid : 119 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4406.62 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 87 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.708 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 28 +cpu cores : 22 +apicid : 121 +initial apicid : 121 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4413.48 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +""" + socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) + assert sorted(socket_map.keys()) == [0, 1] + assert sorted(socket_map[0].keys()) == [0, 1, 2] + assert sorted(socket_map[1].keys()) == [26, 27, 28] + assert sorted(socket_map[0][0]) == [44] + assert sorted(socket_map[0][1]) == [1] + assert sorted(socket_map[0][2]) == [2] + assert sorted(socket_map[1][26]) == [85] + assert sorted(socket_map[1][27]) == [86] + assert sorted(socket_map[1][28]) == [43, 87] + + def test_dual_socket_no_hyperthread(self): + cpuinfo = """\ +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.976 +cache size : 56320 KB +physical id : 0 +siblings : 44 +core id : 1 +cpu cores : 22 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4401.07 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1226.892 +cache size : 56320 KB +physical id : 0 +siblings : 44 +core id : 2 +cpu cores : 22 +apicid : 4 +initial apicid : 4 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4400.84 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 43 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.305 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 28 +cpu cores : 22 +apicid : 120 +initial apicid : 120 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4411.31 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 44 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.305 +cache size : 56320 KB +physical id : 0 +siblings : 44 +core id : 0 +cpu cores : 22 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4410.61 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 85 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.573 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 26 +cpu cores : 22 +apicid : 117 +initial apicid : 117 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4409.07 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 86 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.305 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 27 +cpu cores : 22 +apicid : 119 +initial apicid : 119 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4406.62 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +processor : 87 +vendor_id : GenuineIntel +cpu family : 6 +model : 79 +model name : Intel(R) Xeon(R) CPU E5-2699 v4 @ 2.20GHz +stepping : 1 +microcode : 0xb00001f +cpu MHz : 1200.708 +cache size : 56320 KB +physical id : 1 +siblings : 44 +core id : 28 +cpu cores : 22 +apicid : 121 +initial apicid : 121 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch epb cat_l3 cdp_l3 intel_ppin intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt_a rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts +bugs : +bogomips : 4413.48 +clflush size : 64 +cache_alignment : 64 +address sizes : 46 bits physical, 48 bits virtual +power management: + +""" + socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) + processors = socket_map.processors() + assert processors == [1, 2, 43, 44, 85, 86, 87] + cores = socket_map.cores() + assert cores == [0, 1, 2, 26, 27, 28] + sockets = socket_map.sockets() + assert sockets == [0, 1] + + +class ChangeObjToDictTestCase(unittest.TestCase): + + def test_change_obj_to_dict(self): + class A(object): + def __init__(self): + self.name = 'vnftest' + + obj = A() + obj_r = utils.change_obj_to_dict(obj) + obj_s = {'name': 'vnftest'} + self.assertEqual(obj_r, obj_s) + + +class SetDictValueTestCase(unittest.TestCase): + + def test_set_dict_value(self): + input_dic = { + 'hello': 'world' + } + output_dic = utils.set_dict_value(input_dic, 'welcome.to', 'vnftest') + self.assertEqual(output_dic.get('welcome', {}).get('to'), 'vnftest') + + +class RemoveFileTestCase(unittest.TestCase): + + def test_remove_file(self): + try: + utils.remove_file('notexistfile.txt') + except Exception as e: # pylint: disable=broad-except + # NOTE(ralonsoh): to narrow the scope of this exception. + self.assertTrue(isinstance(e, OSError)) + + +class TestUtils(unittest.TestCase): + + @mock.patch('vnftest.common.utils.os.makedirs') + def test_makedirs(self, *_): + self.assertIsNone(utils.makedirs('a/b/c/d')) + + @mock.patch('vnftest.common.utils.os.makedirs') + def test_makedirs_exists(self, mock_os_makedirs): + mock_os_makedirs.side_effect = OSError(errno.EEXIST, 'exists') + self.assertIsNone(utils.makedirs('a/b/c/d')) + + @mock.patch('vnftest.common.utils.os.makedirs') + def test_makedirs_busy(self, mock_os_makedirs): + mock_os_makedirs.side_effect = OSError(errno.EBUSY, 'busy') + with self.assertRaises(OSError): + utils.makedirs('a/b/c/d') + + @mock.patch('vnftest.common.utils.jsonify') + def test_result_handler(self, mock_jsonify): + mock_jsonify.return_value = 432 + + self.assertEqual(utils.result_handler('x', 234), 432) + mock_jsonify.assert_called_once_with({'status': 'x', 'result': 234}) + + @mock.patch('random.randint') + @mock.patch('socket.socket') + def test_get_free_port(self, mock_socket, mock_randint): + mock_randint.return_value = 7777 + s = mock_socket('x', 'y') + s.connect_ex.side_effect = iter([0, 1]) + result = utils.get_free_port('10.20.30.40') + self.assertEqual(result, 7777) + self.assertEqual(s.connect_ex.call_count, 2) + + @mock.patch('subprocess.check_output') + def test_execute_command(self, mock_check_output): + expected = ['hello world', '1234'] + mock_check_output.return_value = os.linesep.join(expected) + result = utils.execute_command('my_command arg1 arg2') + self.assertEqual(result, expected) + + @mock.patch('subprocess.Popen') + def test_source_env(self, mock_popen): + base_env = deepcopy(os.environ) + mock_process = mock_popen() + output_list = [ + 'garbage line before', + 'NEW_ENV_VALUE=234', + 'garbage line after', + ] + mock_process.communicate.return_value = os.linesep.join(output_list), '', 0 + expected = {'NEW_ENV_VALUE': '234'} + result = utils.source_env('my_file') + self.assertDictEqual(result, expected) + os.environ.clear() + os.environ.update(base_env) + + @mock.patch('vnftest.common.utils.configparser.ConfigParser') + def test_parse_ini_file(self, mock_config_parser_type): + defaults = { + 'default1': 'value1', + 'default2': 'value2', + } + s1 = { + 'key1': 'value11', + 'key2': 'value22', + } + s2 = { + 'key1': 'value123', + 'key2': 'value234', + } + + mock_config_parser = mock_config_parser_type() + mock_config_parser.read.return_value = True + mock_config_parser.sections.return_value = ['s1', 's2'] + mock_config_parser.items.side_effect = iter([ + defaults.items(), + s1.items(), + s2.items(), + ]) + + expected = { + 'DEFAULT': defaults, + 's1': s1, + 's2': s2, + } + result = utils.parse_ini_file('my_path') + self.assertDictEqual(result, expected) + + @mock.patch('vnftest.common.utils.configparser.ConfigParser') + def test_parse_ini_file_missing_section_header(self, mock_config_parser_type): + mock_config_parser = mock_config_parser_type() + mock_config_parser.read.side_effect = \ + configparser.MissingSectionHeaderError(mock.Mock(), 321, mock.Mock()) + + with self.assertRaises(configparser.MissingSectionHeaderError): + utils.parse_ini_file('my_path') + + @mock.patch('vnftest.common.utils.configparser.ConfigParser') + def test_parse_ini_file_no_file(self, mock_config_parser_type): + mock_config_parser = mock_config_parser_type() + mock_config_parser.read.return_value = False + with self.assertRaises(RuntimeError): + utils.parse_ini_file('my_path') + + @mock.patch('vnftest.common.utils.configparser.ConfigParser') + def test_parse_ini_file_no_default_section_header(self, mock_config_parser_type): + s1 = { + 'key1': 'value11', + 'key2': 'value22', + } + s2 = { + 'key1': 'value123', + 'key2': 'value234', + } + + mock_config_parser = mock_config_parser_type() + mock_config_parser.read.return_value = True + mock_config_parser.sections.return_value = ['s1', 's2'] + mock_config_parser.items.side_effect = iter([ + configparser.NoSectionError(mock.Mock()), + s1.items(), + s2.items(), + ]) + + expected = { + 'DEFAULT': {}, + 's1': s1, + 's2': s2, + } + result = utils.parse_ini_file('my_path') + self.assertDictEqual(result, expected) + + def test_join_non_strings(self): + self.assertEqual(utils.join_non_strings(':'), '') + self.assertEqual(utils.join_non_strings(':', 'a'), 'a') + self.assertEqual(utils.join_non_strings(':', 'a', 2, 'c'), 'a:2:c') + self.assertEqual(utils.join_non_strings(':', ['a', 2, 'c']), 'a:2:c') + self.assertEqual(utils.join_non_strings(':', 'abc'), 'abc') + + def test_validate_non_string_sequence(self): + self.assertEqual(utils.validate_non_string_sequence([1, 2, 3]), [1, 2, 3]) + self.assertIsNone(utils.validate_non_string_sequence('123')) + self.assertIsNone(utils.validate_non_string_sequence(1)) + + self.assertEqual(utils.validate_non_string_sequence(1, 2), 2) + self.assertEqual(utils.validate_non_string_sequence(1, default=2), 2) + + with self.assertRaises(RuntimeError): + utils.validate_non_string_sequence(1, raise_exc=RuntimeError) + + +class TestUtilsIpAddrMethods(unittest.TestCase): + + GOOD_IP_V4_ADDRESS_STR_LIST = [ + u'0.0.0.0', + u'10.20.30.40', + u'127.0.0.1', + u'10.20.30.40', + u'172.29.50.75', + u'192.168.230.9', + u'255.255.255.255', + ] + + GOOD_IP_V4_MASK_STR_LIST = [ + u'/1', + u'/8', + u'/13', + u'/19', + u'/24', + u'/32', + ] + + GOOD_IP_V6_ADDRESS_STR_LIST = [ + u'::1', + u'fe80::250:56ff:fe89:91ff', + u'123:4567:89ab:cdef:123:4567:89ab:cdef', + ] + + GOOD_IP_V6_MASK_STR_LIST = [ + u'/1', + u'/16', + u'/29', + u'/64', + u'/99', + u'/128', + ] + + INVALID_IP_ADDRESS_STR_LIST = [ + 1, + u'w.x.y.z', + u'10.20.30.40/33', + u'123:4567:89ab:cdef:123:4567:89ab:cdef/129', + ] + + def test_safe_ip_address(self): + addr_list = self.GOOD_IP_V4_ADDRESS_STR_LIST + for addr in addr_list: + # test with no mask + expected = ipaddress.ip_address(addr) + self.assertEqual(utils.safe_ip_address(addr), expected, addr) + + def test_safe_ip_address_v6_ip(self): + addr_list = self.GOOD_IP_V6_ADDRESS_STR_LIST + for addr in addr_list: + # test with no mask + expected = ipaddress.ip_address(addr) + self.assertEqual(utils.safe_ip_address(addr), expected, addr) + + @mock.patch("vnftest.common.utils.logging") + def test_safe_ip_address_negative(self, *args): + # NOTE(ralonsoh): check the calls to mocked functions. + for value in self.INVALID_IP_ADDRESS_STR_LIST: + self.assertIsNone(utils.safe_ip_address(value), value) + + addr_list = self.GOOD_IP_V4_ADDRESS_STR_LIST + mask_list = self.GOOD_IP_V4_MASK_STR_LIST + for addr_mask_pair in product(addr_list, mask_list): + value = ''.join(addr_mask_pair) + self.assertIsNone(utils.safe_ip_address(value), value) + + addr_list = self.GOOD_IP_V6_ADDRESS_STR_LIST + mask_list = self.GOOD_IP_V6_MASK_STR_LIST + for addr_mask_pair in product(addr_list, mask_list): + value = ''.join(addr_mask_pair) + self.assertIsNone(utils.safe_ip_address(value), value) + + def test_get_ip_version(self): + addr_list = self.GOOD_IP_V4_ADDRESS_STR_LIST + for addr in addr_list: + # test with no mask + self.assertEqual(utils.get_ip_version(addr), 4, addr) + + def test_get_ip_version_v6_ip(self): + addr_list = self.GOOD_IP_V6_ADDRESS_STR_LIST + for addr in addr_list: + # test with no mask + self.assertEqual(utils.get_ip_version(addr), 6, addr) + + @mock.patch("vnftest.common.utils.logging") + def test_get_ip_version_negative(self, *args): + # NOTE(ralonsoh): check the calls to mocked functions. + for value in self.INVALID_IP_ADDRESS_STR_LIST: + self.assertIsNone(utils.get_ip_version(value), value) + + addr_list = self.GOOD_IP_V4_ADDRESS_STR_LIST + mask_list = self.GOOD_IP_V4_MASK_STR_LIST + for addr_mask_pair in product(addr_list, mask_list): + value = ''.join(addr_mask_pair) + self.assertIsNone(utils.get_ip_version(value), value) + + addr_list = self.GOOD_IP_V6_ADDRESS_STR_LIST + mask_list = self.GOOD_IP_V6_MASK_STR_LIST + for addr_mask_pair in product(addr_list, mask_list): + value = ''.join(addr_mask_pair) + self.assertIsNone(utils.get_ip_version(value), value) + + def test_ip_to_hex(self): + self.assertEqual(utils.ip_to_hex('0.0.0.0'), '00000000') + self.assertEqual(utils.ip_to_hex('10.20.30.40'), '0a141e28') + self.assertEqual(utils.ip_to_hex('127.0.0.1'), '7f000001') + self.assertEqual(utils.ip_to_hex('172.31.90.100'), 'ac1f5a64') + self.assertEqual(utils.ip_to_hex('192.168.254.253'), 'c0a8fefd') + self.assertEqual(utils.ip_to_hex('255.255.255.255'), 'ffffffff') + + def test_ip_to_hex_v6_ip(self): + for value in self.GOOD_IP_V6_ADDRESS_STR_LIST: + self.assertEqual(utils.ip_to_hex(value), value) + + @mock.patch("vnftest.common.utils.logging") + def test_ip_to_hex_negative(self, *args): + # NOTE(ralonsoh): check the calls to mocked functions. + addr_list = self.GOOD_IP_V4_ADDRESS_STR_LIST + mask_list = self.GOOD_IP_V4_MASK_STR_LIST + value_iter = (''.join(pair) for pair in product(addr_list, mask_list)) + for value in chain(value_iter, self.INVALID_IP_ADDRESS_STR_LIST): + self.assertEqual(utils.ip_to_hex(value), value) + + +class ReadMeminfoTestCase(unittest.TestCase): + + MEMINFO = (b'MemTotal: 65860500 kB\n' + b'MemFree: 28690900 kB\n' + b'MemAvailable: 52873764 kB\n' + b'Active(anon): 3015676 kB\n' + b'HugePages_Total: 8\n' + b'Hugepagesize: 1048576 kB') + MEMINFO_DICT = {'MemTotal': '65860500', + 'MemFree': '28690900', + 'MemAvailable': '52873764', + 'Active(anon)': '3015676', + 'HugePages_Total': '8', + 'Hugepagesize': '1048576'} diff --git a/vnftest/tests/unit/common/test_yaml_loader.py b/vnftest/tests/unit/common/test_yaml_loader.py new file mode 100644 index 0000000..361f1f7 --- /dev/null +++ b/vnftest/tests/unit/common/test_yaml_loader.py @@ -0,0 +1,34 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/unit/common/test_yaml_loader.py + +from __future__ import absolute_import +import unittest + +from vnftest.common import yaml_loader + + +class TemplateFormatTestCase(unittest.TestCase): + + def test_parse_to_value_exception(self): + + self.assertEquals(yaml_loader.yaml_load("string"), u"string") + + +def main(): + unittest.main() + +if __name__ == '__main__': + main() diff --git a/vnftest/tests/unit/core/test_commands.py b/vnftest/tests/unit/core/test_commands.py new file mode 100755 index 0000000..35660bd --- /dev/null +++ b/vnftest/tests/unit/core/test_commands.py @@ -0,0 +1,89 @@ +############################################################################## +# Copyright 2018 EuropeanSoftwareMarketingLtd. +# =================================================================== +# Licensed under the ApacheLicense, Version2.0 (the"License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and limitations under +# the License +############################################################################## +# vnftest comment: this is a modified copy of +# yardstick/tests/functional/test_cli_runner.py + +import unittest + +from vnftest.cmd.commands.runner import RunnerCommands +from vnftest.cmd.commands.step import StepCommands +from vnftest.core import Param +from vnftest.core.runner import Runners +from vnftest.core.step import Steps +from vnftest.runners.iteration import IterationRunner +from vnftest.runners.duration import DurationRunner +from vnftest.onap.onap_api_call import OnapApiCall +from cStringIO import StringIO +import sys + + +class Capture(list): + + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self._stringio = StringIO() + return self + + def __exit__(self, *args): + self.extend(self._stringio.getvalue().splitlines()) + del self._stringio + sys.stdout = self._stdout + + +class CommandsTestCase(unittest.TestCase): + + def setUp(self): + super(CommandsTestCase, self).setUp() + + def test_runner_list(self): + runner_cmd = RunnerCommands() + with Capture() as output: + runner_cmd.do_list(None) + self.assert_text_in_lines(output, ["Duration", "Iteration"]) + + def test_step_list(self): + step_cmd = StepCommands() + with Capture() as output: + step_cmd.do_list(None) + self.assert_text_in_lines(output, ["OnapApiCall"]) + + def test_runner_show_Duration(self): + param = Param({}) + setattr(param, 'type', ['Duration']) + with Capture() as output: + Runners().show(param) + self.assert_text_in_lines(output, ["duration - amount of time"]) + + def test_runner_show_Iteration(self): + param = Param({}) + setattr(param, 'type', ['Iteration']) + with Capture() as output: + Runners().show(param) + self.assert_text_in_lines(output, ["iterations - amount of times"]) + + def test_step_show_OnapApiCall(self): + param = Param({}) + setattr(param, 'type', ['OnapApiCall']) + with Capture() as output: + Steps().show(param) + self.assert_text_in_lines(output, ["Call ONAP API"]) + + def assert_text_in_lines(self, lines, texts): + for text in texts: + found = False + for line in lines: + if text in line: + found = True + break + self.assertTrue(found, "Not Found: " + text) -- cgit 1.2.3-korg