diff --git a/tempest_lib/cli/output_parser.py b/tempest_lib/cli/output_parser.py index 58d88c5..d5e5039 100644 --- a/tempest_lib/cli/output_parser.py +++ b/tempest_lib/cli/output_parser.py @@ -17,8 +17,8 @@ import re -from tempest import exceptions -from tempest.openstack.common import log as logging +from tempest_lib import exceptions +from tempest_lib.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/tempest_lib/tests/cli/__init__.py b/tempest_lib/tests/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tempest_lib/tests/cli/test_cli.py b/tempest_lib/tests/cli/test_cli.py new file mode 100644 index 0000000..9d24b0e --- /dev/null +++ b/tempest_lib/tests/cli/test_cli.py @@ -0,0 +1,58 @@ +# Copyright 2014 IBM Corp. +# +# Licensed under the Apache License, Version 2.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 +# +# Unless required by applicable law or agreed to in writing, 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. + +import mock +import testtools + +from tempest_lib.cli import base as cli +from tempest_lib import exceptions +from tempest_lib.tests import base + + +class TestMinClientVersion(base.TestCase): + """Tests for the min_client_version decorator.""" + + def _test_min_version(self, required, installed, expect_skip): + + @cli.min_client_version(client='nova', version=required) + def fake(self, expect_skip): + if expect_skip: + # If we got here, the decorator didn't raise a skipException as + # expected so we need to fail. + self.fail('Should not have gotten past the decorator.') + + with mock.patch.object(cli, 'execute', + return_value=installed) as mock_cmd: + if expect_skip: + self.assertRaises(testtools.TestCase.skipException, fake, + self, expect_skip) + else: + fake(self, expect_skip) + mock_cmd.assert_called_once_with('nova', '', params='--version', + merge_stderr=True) + + def test_min_client_version(self): + # required, installed, expect_skip + cases = (('2.17.0', '2.17.0', False), + ('2.17.0', '2.18.0', False), + ('2.18.0', '2.17.0', True)) + + for case in cases: + self._test_min_version(*case) + + @mock.patch.object(cli, 'execute', return_value=' ') + def test_check_client_version_empty_output(self, mock_execute): + # Tests that an exception is raised if the command output is empty. + self.assertRaises(exceptions.TempestException, + cli.check_client_version, 'nova', '2.18.0') diff --git a/tempest_lib/tests/cli/test_command_failed.py b/tempest_lib/tests/cli/test_command_failed.py new file mode 100644 index 0000000..17543e9 --- /dev/null +++ b/tempest_lib/tests/cli/test_command_failed.py @@ -0,0 +1,30 @@ +# Licensed under the Apache License, Version 2.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 +# +# Unless required by applicable law or agreed to in writing, 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 tempest_lib import exceptions +from tempest_lib.tests import base + + +class TestOutputParser(base.TestCase): + + def test_command_failed_exception(self): + returncode = 1 + cmd = "foo" + stdout = "output" + stderr = "error" + try: + raise exceptions.CommandFailed(returncode, cmd, stdout, stderr) + except exceptions.CommandFailed as e: + self.assertIn(str(returncode), str(e)) + self.assertIn(cmd, str(e)) + self.assertIn(stdout, str(e)) + self.assertIn(stderr, str(e)) diff --git a/tempest_lib/tests/cli/test_output_parser.py b/tempest_lib/tests/cli/test_output_parser.py new file mode 100644 index 0000000..3aa7e91 --- /dev/null +++ b/tempest_lib/tests/cli/test_output_parser.py @@ -0,0 +1,177 @@ +# Copyright 2014 NEC Corporation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.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 +# +# Unless required by applicable law or agreed to in writing, 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 tempest_lib.cli import output_parser +from tempest_lib import exceptions +from tempest_lib.tests import base + + +class TestOutputParser(base.TestCase): + OUTPUT_LINES = """ ++----+------+---------+ +| ID | Name | Status | ++----+------+---------+ +| 11 | foo | BUILD | +| 21 | bar | ERROR | +| 31 | bee | None | ++----+------+---------+ +""" + OUTPUT_LINES2 = """ ++----+-------+---------+ +| ID | Name2 | Status2 | ++----+-------+---------+ +| 41 | aaa | SSSSS | +| 51 | bbb | TTTTT | +| 61 | ccc | AAAAA | ++----+-------+---------+ +""" + + EXPECTED_TABLE = {'headers': ['ID', 'Name', 'Status'], + 'values': [['11', 'foo', 'BUILD'], + ['21', 'bar', 'ERROR'], + ['31', 'bee', 'None']]} + EXPECTED_TABLE2 = {'headers': ['ID', 'Name2', 'Status2'], + 'values': [['41', 'aaa', 'SSSSS'], + ['51', 'bbb', 'TTTTT'], + ['61', 'ccc', 'AAAAA']]} + + def test_table_with_normal_values(self): + actual = output_parser.table(self.OUTPUT_LINES) + self.assertIsInstance(actual, dict) + self.assertEqual(self.EXPECTED_TABLE, actual) + + def test_table_with_list(self): + output_lines = self.OUTPUT_LINES.split('\n') + actual = output_parser.table(output_lines) + self.assertIsInstance(actual, dict) + self.assertEqual(self.EXPECTED_TABLE, actual) + + def test_table_with_invalid_line(self): + output_lines = self.OUTPUT_LINES + "aaaa" + actual = output_parser.table(output_lines) + self.assertIsInstance(actual, dict) + self.assertEqual(self.EXPECTED_TABLE, actual) + + def test_tables_with_normal_values(self): + output_lines = ('test' + self.OUTPUT_LINES + + 'test2' + self.OUTPUT_LINES2) + expected = [{'headers': self.EXPECTED_TABLE['headers'], + 'label': 'test', + 'values': self.EXPECTED_TABLE['values']}, + {'headers': self.EXPECTED_TABLE2['headers'], + 'label': 'test2', + 'values': self.EXPECTED_TABLE2['values']}] + actual = output_parser.tables(output_lines) + self.assertIsInstance(actual, list) + self.assertEqual(expected, actual) + + def test_tables_with_invalid_values(self): + output_lines = ('test' + self.OUTPUT_LINES + + 'test2' + self.OUTPUT_LINES2 + '\n') + expected = [{'headers': self.EXPECTED_TABLE['headers'], + 'label': 'test', + 'values': self.EXPECTED_TABLE['values']}, + {'headers': self.EXPECTED_TABLE2['headers'], + 'label': 'test2', + 'values': self.EXPECTED_TABLE2['values']}] + actual = output_parser.tables(output_lines) + self.assertIsInstance(actual, list) + self.assertEqual(expected, actual) + + def test_tables_with_invalid_line(self): + output_lines = ('test' + self.OUTPUT_LINES + + 'test2' + self.OUTPUT_LINES2 + + '+----+-------+---------+') + expected = [{'headers': self.EXPECTED_TABLE['headers'], + 'label': 'test', + 'values': self.EXPECTED_TABLE['values']}, + {'headers': self.EXPECTED_TABLE2['headers'], + 'label': 'test2', + 'values': self.EXPECTED_TABLE2['values']}] + + actual = output_parser.tables(output_lines) + self.assertIsInstance(actual, list) + self.assertEqual(expected, actual) + + LISTING_OUTPUT = """ ++----+ +| ID | ++----+ +| 11 | +| 21 | +| 31 | ++----+ +""" + + def test_listing(self): + expected = [{'ID': '11'}, {'ID': '21'}, {'ID': '31'}] + actual = output_parser.listing(self.LISTING_OUTPUT) + self.assertIsInstance(actual, list) + self.assertEqual(expected, actual) + + def test_details_multiple_with_invalid_line(self): + self.assertRaises(exceptions.InvalidStructure, + output_parser.details_multiple, + self.OUTPUT_LINES) + + DETAILS_LINES1 = """First Table ++----------+--------+ +| Property | Value | ++----------+--------+ +| foo | BUILD | +| bar | ERROR | +| bee | None | ++----------+--------+ +""" + DETAILS_LINES2 = """Second Table ++----------+--------+ +| Property | Value | ++----------+--------+ +| aaa | VVVVV | +| bbb | WWWWW | +| ccc | XXXXX | ++----------+--------+ +""" + + def test_details_with_normal_line_label_false(self): + expected = {'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'} + actual = output_parser.details(self.DETAILS_LINES1) + self.assertEqual(expected, actual) + + def test_details_with_normal_line_label_true(self): + expected = {'__label': 'First Table', + 'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'} + actual = output_parser.details(self.DETAILS_LINES1, with_label=True) + self.assertEqual(expected, actual) + + def test_details_multiple_with_normal_line_label_false(self): + expected = [{'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'}, + {'aaa': 'VVVVV', 'bbb': 'WWWWW', 'ccc': 'XXXXX'}] + actual = output_parser.details_multiple(self.DETAILS_LINES1 + + self.DETAILS_LINES2) + self.assertIsInstance(actual, list) + self.assertEqual(expected, actual) + + def test_details_multiple_with_normal_line_label_true(self): + expected = [{'__label': 'First Table', + 'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'}, + {'__label': 'Second Table', + 'aaa': 'VVVVV', 'bbb': 'WWWWW', 'ccc': 'XXXXX'}] + actual = output_parser.details_multiple(self.DETAILS_LINES1 + + self.DETAILS_LINES2, + with_label=True) + self.assertIsInstance(actual, list) + self.assertEqual(expected, actual)