Merge "Add min_client_version decorator for CLI tests"
This commit is contained in:
commit
fceab11864
@ -13,14 +13,18 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
import testtools
|
||||||
|
|
||||||
import tempest.cli.output_parser
|
import tempest.cli.output_parser
|
||||||
from tempest import config
|
from tempest import config
|
||||||
from tempest import exceptions
|
from tempest import exceptions
|
||||||
from tempest.openstack.common import log as logging
|
from tempest.openstack.common import log as logging
|
||||||
|
from tempest.openstack.common import versionutils
|
||||||
import tempest.test
|
import tempest.test
|
||||||
|
|
||||||
|
|
||||||
@ -29,6 +33,65 @@ LOG = logging.getLogger(__name__)
|
|||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
def execute(cmd, action, flags='', params='', fail_ok=False,
|
||||||
|
merge_stderr=False):
|
||||||
|
"""Executes specified command for the given action."""
|
||||||
|
cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
|
||||||
|
flags, action, params])
|
||||||
|
LOG.info("running: '%s'" % cmd)
|
||||||
|
cmd = shlex.split(cmd.encode('utf-8'))
|
||||||
|
result = ''
|
||||||
|
result_err = ''
|
||||||
|
stdout = subprocess.PIPE
|
||||||
|
stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
|
||||||
|
proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
|
||||||
|
result, result_err = proc.communicate()
|
||||||
|
if not fail_ok and proc.returncode != 0:
|
||||||
|
raise exceptions.CommandFailed(proc.returncode,
|
||||||
|
cmd,
|
||||||
|
result,
|
||||||
|
result_err)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def check_client_version(client, version):
|
||||||
|
"""Checks if the client's version is compatible with the given version
|
||||||
|
|
||||||
|
@param client: The client to check.
|
||||||
|
@param version: The version to compare against.
|
||||||
|
@return: True if the client version is compatible with the given version
|
||||||
|
parameter, False otherwise.
|
||||||
|
"""
|
||||||
|
current_version = execute(client, '', params='--version',
|
||||||
|
merge_stderr=True)
|
||||||
|
|
||||||
|
if not current_version.strip():
|
||||||
|
raise exceptions.TempestException('"%s --version" output was empty' %
|
||||||
|
client)
|
||||||
|
|
||||||
|
return versionutils.is_compatible(version, current_version,
|
||||||
|
same_major=False)
|
||||||
|
|
||||||
|
|
||||||
|
def min_client_version(*args, **kwargs):
|
||||||
|
"""A decorator to skip tests if the client used isn't of the right version.
|
||||||
|
|
||||||
|
@param client: The client command to run. For python-novaclient, this is
|
||||||
|
'nova', for python-cinderclient this is 'cinder', etc.
|
||||||
|
@param version: The minimum version required to run the CLI test.
|
||||||
|
"""
|
||||||
|
def decorator(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*func_args, **func_kwargs):
|
||||||
|
if not check_client_version(kwargs['client'], kwargs['version']):
|
||||||
|
msg = "requires %s client version >= %s" % (kwargs['client'],
|
||||||
|
kwargs['version'])
|
||||||
|
raise testtools.TestCase.skipException(msg)
|
||||||
|
return func(*func_args, **func_kwargs)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class ClientTestBase(tempest.test.BaseTestCase):
|
class ClientTestBase(tempest.test.BaseTestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@ -50,7 +113,7 @@ class ClientTestBase(tempest.test.BaseTestCase):
|
|||||||
def nova_manage(self, action, flags='', params='', fail_ok=False,
|
def nova_manage(self, action, flags='', params='', fail_ok=False,
|
||||||
merge_stderr=False):
|
merge_stderr=False):
|
||||||
"""Executes nova-manage command for the given action."""
|
"""Executes nova-manage command for the given action."""
|
||||||
return self.cmd(
|
return execute(
|
||||||
'nova-manage', action, flags, params, fail_ok, merge_stderr)
|
'nova-manage', action, flags, params, fail_ok, merge_stderr)
|
||||||
|
|
||||||
def keystone(self, action, flags='', params='', admin=True, fail_ok=False):
|
def keystone(self, action, flags='', params='', admin=True, fail_ok=False):
|
||||||
@ -114,28 +177,7 @@ class ClientTestBase(tempest.test.BaseTestCase):
|
|||||||
CONF.identity.admin_password,
|
CONF.identity.admin_password,
|
||||||
CONF.identity.uri))
|
CONF.identity.uri))
|
||||||
flags = creds + ' ' + flags
|
flags = creds + ' ' + flags
|
||||||
return self.cmd(cmd, action, flags, params, fail_ok, merge_stderr)
|
return execute(cmd, action, flags, params, fail_ok, merge_stderr)
|
||||||
|
|
||||||
def cmd(self, cmd, action, flags='', params='', fail_ok=False,
|
|
||||||
merge_stderr=False):
|
|
||||||
"""Executes specified command for the given action."""
|
|
||||||
cmd = ' '.join([os.path.join(CONF.cli.cli_dir, cmd),
|
|
||||||
flags, action, params])
|
|
||||||
LOG.info("running: '%s'" % cmd)
|
|
||||||
cmd = shlex.split(cmd.encode('utf-8'))
|
|
||||||
result = ''
|
|
||||||
result_err = ''
|
|
||||||
stdout = subprocess.PIPE
|
|
||||||
stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
cmd, stdout=stdout, stderr=stderr)
|
|
||||||
result, result_err = proc.communicate()
|
|
||||||
if not fail_ok and proc.returncode != 0:
|
|
||||||
raise exceptions.CommandFailed(proc.returncode,
|
|
||||||
cmd,
|
|
||||||
result,
|
|
||||||
result_err)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def assertTableStruct(self, items, field_names):
|
def assertTableStruct(self, items, field_names):
|
||||||
"""Verify that all items has keys listed in field_names."""
|
"""Verify that all items has keys listed in field_names."""
|
||||||
|
@ -85,6 +85,7 @@ class SimpleReadOnlyHeatClientTest(tempest.cli.ClientTestBase):
|
|||||||
def test_heat_help(self):
|
def test_heat_help(self):
|
||||||
self.heat('help')
|
self.heat('help')
|
||||||
|
|
||||||
|
@tempest.cli.min_client_version(client='heat', version='0.2.7')
|
||||||
def test_heat_bash_completion(self):
|
def test_heat_bash_completion(self):
|
||||||
self.heat('bash-completion')
|
self.heat('bash-completion')
|
||||||
|
|
||||||
|
@ -144,6 +144,7 @@ class SimpleReadOnlyNovaClientTest(cli.ClientTestBase):
|
|||||||
def test_admin_secgroup_list_rules(self):
|
def test_admin_secgroup_list_rules(self):
|
||||||
self.nova('secgroup-list-rules')
|
self.nova('secgroup-list-rules')
|
||||||
|
|
||||||
|
@tempest.cli.min_client_version(client='nova', version='2.18')
|
||||||
def test_admin_server_group_list(self):
|
def test_admin_server_group_list(self):
|
||||||
self.nova('server-group-list')
|
self.nova('server-group-list')
|
||||||
|
|
||||||
|
59
tempest/tests/cli/test_cli.py
Normal file
59
tempest/tests/cli/test_cli.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# 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 import cli
|
||||||
|
from tempest import exceptions
|
||||||
|
from tempest.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')
|
Loading…
Reference in New Issue
Block a user