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
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
import testtools
|
||||
|
||||
import tempest.cli.output_parser
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
from tempest.openstack.common import log as logging
|
||||
from tempest.openstack.common import versionutils
|
||||
import tempest.test
|
||||
|
||||
|
||||
@ -29,6 +33,65 @@ LOG = logging.getLogger(__name__)
|
||||
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):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@ -50,7 +113,7 @@ class ClientTestBase(tempest.test.BaseTestCase):
|
||||
def nova_manage(self, action, flags='', params='', fail_ok=False,
|
||||
merge_stderr=False):
|
||||
"""Executes nova-manage command for the given action."""
|
||||
return self.cmd(
|
||||
return execute(
|
||||
'nova-manage', action, flags, params, fail_ok, merge_stderr)
|
||||
|
||||
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.uri))
|
||||
flags = creds + ' ' + flags
|
||||
return self.cmd(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
|
||||
return execute(cmd, action, flags, params, fail_ok, merge_stderr)
|
||||
|
||||
def assertTableStruct(self, items, 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):
|
||||
self.heat('help')
|
||||
|
||||
@tempest.cli.min_client_version(client='heat', version='0.2.7')
|
||||
def test_heat_bash_completion(self):
|
||||
self.heat('bash-completion')
|
||||
|
||||
|
@ -144,6 +144,7 @@ class SimpleReadOnlyNovaClientTest(cli.ClientTestBase):
|
||||
def test_admin_secgroup_list_rules(self):
|
||||
self.nova('secgroup-list-rules')
|
||||
|
||||
@tempest.cli.min_client_version(client='nova', version='2.18')
|
||||
def test_admin_server_group_list(self):
|
||||
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