Merge branch '2018.3' into 'fluorine'

Conflicts:
  - tests/unit/states/test_virt.py
  - tests/unit/test_config.py
This commit is contained in:
rallytime 2018-10-29 12:36:28 -04:00
commit 3f2712fbc1
No known key found for this signature in database
GPG key ID: E8F1A4B90D0DEA19
19 changed files with 656 additions and 668 deletions

View file

@ -223,6 +223,7 @@ verifier:
sudo: true
run_destructive: true
transport: zeromq
enable_filenames: true
types:
- ssh
xml: /tmp/xml-unittests-output/

View file

@ -32,6 +32,46 @@ or the integration testing factions contain respective integration or unit
test files for Salt execution modules.
.. note::
Salt's test framework provides for the option to only run tests which
correspond to a given file (or set of files), via the ``--from-filenames``
argument to ``runtests.py``:
.. code-block:: bash
python /path/to/runtests.py --from-filenames=salt/modules/foo.py
Therefore, where possible, test files should be named to match the source
files they are testing. For example, when writing tests for
``salt/modules/foo.py``, unit tests should go into
``tests/unit/modules/test_foo.py``, and integration tests should go into
``tests/integration/modules/test_foo.py``.
However, integration tests are organized differently from unit tests, and
this may not always be plausible. In these cases, to ensure that the proper
tests are run for these files, they must be mapped in
`tests/filename_map.yml`__.
The filename map is used to supplement the test framework's filename
matching logic. This allows one to ensure that states correspnding to an
execution module are also tested when ``--from-filenames`` includes that
execution module. It can also be used for those cases where the path to a
test file doesn't correspond directly to the file which is being tested
(e.g. the ``shell``, ``spm``, and ``ssh`` integration tests, among others).
Both glob expressions and regular expressions are permitted in the filename
map.
.. important::
Test modules which don't map directly to the source file they are
testing (using the naming convention described above), **must** be
added to the ``ignore`` tuple in ``tests/unit/test_module_names.py``,
in the ``test_module_name_source_match`` function. This unit test
ensures that we maintain the naming convention for test files.
.. __: https://github.com/saltstack/salt/blob/develop/tests/filename_map.yml
Integration Tests
-----------------
@ -445,8 +485,8 @@ successfully. If a network connection is not detected, the test will not run.
order for the test to be executed. Otherwise, the test is skipped.
`@requires_system_grains` -- Loads and passes the grains on the system as an
keyword argument to the test function with the name `grains`.
keyword argument to the test function with the name `grains`.
`@skip_if_binaries_missing(['list', 'of', 'binaries'])` -- If called from inside a test,
the test will be skipped if the binaries are not all present on the system.

View file

@ -1335,6 +1335,7 @@ def id_():
'''
return {'id': __opts__.get('id', '')}
_REPLACE_LINUX_RE = re.compile(r'\W(?:gnu/)?linux', re.IGNORECASE)
# This maps (at most) the first ten characters (no spaces, lowercased) of

View file

@ -169,6 +169,12 @@ def get_repo(name, config_path=_DEFAULT_CONFIG_PATH, with_packages=False):
:return: A dictionary containing information about the repository.
:rtype: dict
CLI Example:
.. code-block:: bash
salt '*' aptly.get_repo name="test-repo"
'''
_validate_config(config_path)
with_packages = six.text_type(bool(with_packages)).lower()
@ -225,7 +231,7 @@ def new_repo(name, config_path=_DEFAULT_CONFIG_PATH, comment=None, component=Non
'''
_validate_config(config_path)
current_repo = __salt__['aptly.get_repo'](name=name)
current_repo = __salt__['aptly.get_repo'](name=name, config_path=config_path)
if current_repo:
log.debug('Repository already exists: %s', name)
@ -242,7 +248,7 @@ def new_repo(name, config_path=_DEFAULT_CONFIG_PATH, comment=None, component=Non
cmd.extend(['from', 'snapshot', from_snapshot])
_cmd_run(cmd)
repo = __salt__['aptly.get_repo'](name=name)
repo = __salt__['aptly.get_repo'](name=name, config_path=config_path)
if repo:
log.debug('Created repo: %s', name)
@ -286,7 +292,7 @@ def set_repo(name, config_path=_DEFAULT_CONFIG_PATH, comment=None, component=Non
if settings[setting] is None:
settings.pop(setting, None)
current_settings = __salt__['aptly.get_repo'](name=name)
current_settings = __salt__['aptly.get_repo'](name=name, config_path=config_path)
if not current_settings:
log.error('Unable to get repo: %s', name)
@ -312,7 +318,7 @@ def set_repo(name, config_path=_DEFAULT_CONFIG_PATH, comment=None, component=Non
cmd.append(name)
_cmd_run(cmd)
new_settings = __salt__['aptly.get_repo'](name=name)
new_settings = __salt__['aptly.get_repo'](name=name, config_path=config_path)
# Check the new repo settings to see if they have the desired values.
for setting in settings:
@ -347,7 +353,7 @@ def delete_repo(name, config_path=_DEFAULT_CONFIG_PATH, force=False):
_validate_config(config_path)
force = six.text_type(bool(force)).lower()
current_repo = __salt__['aptly.get_repo'](name=name)
current_repo = __salt__['aptly.get_repo'](name=name, config_path=config_path)
if not current_repo:
log.debug('Repository already absent: %s', name)
@ -357,7 +363,7 @@ def delete_repo(name, config_path=_DEFAULT_CONFIG_PATH, force=False):
'-force={}'.format(force), name]
_cmd_run(cmd)
repo = __salt__['aptly.get_repo'](name=name)
repo = __salt__['aptly.get_repo'](name=name, config_path=config_path)
if repo:
log.error('Unable to remove repo: %s', name)

227
tests/filename_map.yml Normal file
View file

@ -0,0 +1,227 @@
'*':
- unit.test_module_names
- unit.test_doc
- integration.modules.test_sysmod
salt/modules/(apk|aptpkg|ebuild|dpkg|freebsdpkg|mac_brew|mac_ports|openbsdpkg|opkg|pacman|pkgin|pkgng|pkg_resource|rpm|solarisips|solarispkg|win_pkg|xbpspkg|yumpkg|zypper)\.py:
- unit.states.test_pkg
- integration.modules.test_pkg
- integration.states.test_pkg
- integration.states.test_pkgrepo
salt/modules/(mac_user|useradd|pw_user|solaris_user|win_useradd)\.py:
- unit.states.test_user
- integration.states.test_user
salt/modules/(aix_group|groupadd|mac_group|pw_group|solaris_group|win_groupadd)\.py:
- unit.states.test_group
salt/modules/(debian_service|freebsdservice|gentoo_service|launchctl|mac_service|netbsdservice|openbsdrcctl|openbsdservice|rh_service|runit|service|smf|systemd|upstart|win_service)\.py:
- unit.states.test_service
- integration.states.test_service
salt/modules/*apache.py:
- unit.states.test_apache
- unit.states.test_apache_conf
- unit.states.test_apache_module
- unit.states.test_apache_site
salt/modules/augeas_cfg.py:
- unit.states.test_augeas
salt/modules/cp.py:
- unit.modules.test_file
- unit.states.test_file
- integration.modules.test_file
- integration.states.test_file
salt/modules/dockermod.py:
- unit.states.test_docker_image
- unit.states.test_docker_volume
- unit.utils.test_docker
- integration.states.test_docker_container
- integration.states.test_docker_network
salt/modules/influx08.py:
- unit.states.test_influxdb08_database
- unit.states.test_influxdb08_user
salt/modules/mysql.py:
- unit.states.test_mysql_user
- unit.states.test_mysql_query
- unit.states.test_mysql_grants
salt/modules/openvswitch.py:
- unit.states.test_openvswitch_port
salt/(states|modules)/.*postgres.py:
- unit.states.test_postgres
- unit.states.test_postgres_cluster
- unit.states.test_postgres_database
- unit.states.test_postgres_extension
- unit.states.test_postgres_group
- unit.states.test_postgres_initdb
- unit.states.test_postgres_language
- unit.states.test_postgres_privileges
- unit.states.test_postgres_schema
- unit.states.test_postgres_user
salt/modules/rabbitmq.py:
- unit.states.test_rabbitmq_cluster
- unit.states.test_rabbitmq_plugin
- unit.states.test_rabbitmq_policy
- unit.states.test_rabbitmq_vhost
- integration.states.test_rabbitmq_user
- integration.states.test_rabbitmq_vhost
salt/modules/ssh.py:
- unit.states.test_ssh_auth
- unit.states.test_ssh_known_hosts
salt/auth/*:
- integration.shell.test_auth
salt/cache/*:
- unit.cache.test_cache
salt/cli/*:
- integration.shell.test_arguments
salt/cli/call.py:
- integration.shell.test_call
salt/cli/cp.py:
- integration.shell.test_cp
salt/cli/key.py:
- integration.shell.test_key
salt/cli/salt.py:
- integration.cli.test_grains
- integration.shell.test_enabled
- integration.shell.test_matcher
- integration.shell.test_saltcli
salt/client/*:
- integration.client.test_kwarg
- integration.client.test_runner
- integration.client.test_standard
salt/cloud/*:
- integration.shell.test_cloud
salt/cloud/__init__.py:
- integration.cloud.test_cloud
salt/proxy/*:
- integration.proxy.test_shell
- integration.proxy.test_simple
salt/state.py:
- integration.modules.test_state_jinja_filters
- integration.states.test_compiler
- integration.states.test_handle_error
- integration.states.test_handle_iorder
- integration.states.test_match
- integration.states.test_renderers
salt/utils/decorators/*:
- integration.modules.test_decorators
salt/(utils|renderers)/jinja\.py:
- integration.modules.test_state_jinja_filters
- integration.states.test_renderers
salt/utils/minions.py:
- integration.shell.test_matcher
salt/utils/reactor.py:
- integration.reactor.test_reactor
salt/cli/daemons.py:
- integration.shell.test_master
- integration.shell.test_minion
- integration.shell.test_proxy
- integration.shell.test_syndic
salt/(client/ssh/.+|cli/ssh\.py):
- integration.cli.test_custom_module
- integration.ssh.test_deploy
- integration.ssh.test_grains
- integration.ssh.test_jinja_filters
- integration.ssh.test_master
- integration.ssh.test_mine
- integration.ssh.test_pillar
- integration.ssh.test_raw
- integration.ssh.test_state
salt/config/*:
- unit.test_config
salt/loader.py:
- integration.loader.test_ext_grains
- integration.loader.test_ext_modules
salt/minion.py:
- integration.client.test_syndic
- integration.minion.test_blackout
- integration.minion.test_pillar
- integration.minion.test_timeout
- integration.shell.test_matcher
salt/modules/*_sysctl.py:
- unit.states.test_sysctl
- integration.modules.test_sysctl
salt/netapi/rest_cherrypy/*:
- unit.netapi.test_rest_cherrypy
- integration.netapi.rest_cherrypy.test_app_pam
- integration.netapi.test_client
salt/netapi/rest_tornado/*:
- unit.netapi.test_rest_tornado
- integration.netapi.rest_tornado.test_app
- integration.netapi.test_client
salt/output/*:
- integration.output.test_output
salt/pillar/__init__.py:
- integration.minion.test_pillar
salt/(cli/run\.py|runner\.py):
- integration.shell.test_runner
- integration.runners.test_runner_returns
salt/runners/venafiapi.py:
- integration.externalapi.test_venafiapi
salt/serializers/*:
- unit.serializers.test_serializers
salt/(cli/spm\.py|spm/.+):
- unit.test_spm
- integration.shell.test_spm
- integration.spm.test_build
- integration.spm.test_files
- integration.spm.test_info
- integration.spm.test_install
- integration.spm.test_remove
- integration.spm.test_repo
salt/transport/*:
- unit.test_transport
salt/utils/docker/*:
- unit.utils.test_docker
salt/utils/schedule.py:
- integration.scheduler.test_eval
- integration.scheduler.test_postpone
- integration.scheduler.test_skip
salt/wheel/*:
- integration.wheel.test_client
tests/support/mock.py:
- unit.test_mock

View file

@ -26,7 +26,7 @@ try:
from libcloud.common.openstack_identity import OpenStackIdentityTokenScope
HAS_KEYSTONE = True
except ImportError:
HAS_KEYSTONE = True
HAS_KEYSTONE = False
# Import Third-Party Libs
try:

View file

@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: Nicole Thomas <nicole@saltstack.com>
'''
# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals
import os
# Import Salt Testing Libs
from tests.support.case import ShellCase
from tests.support.paths import FILES
from tests.support.unit import skipIf
from tests.support.helpers import expensiveTest, generate_random_name
# Import Salt Libs
from salt.config import cloud_providers_config
# Import Third-Party Libs
try:
import shade # pylint: disable=unused-import
HAS_SHADE = True
except ImportError:
HAS_SHADE = False
# Create the cloud instance name to be used throughout the tests
INSTANCE_NAME = generate_random_name('CLOUD-TEST-')
PROVIDER_NAME = 'openstack'
DRIVER_NAME = 'openstack'
@skipIf(HAS_SHADE is False, 'openstack driver requires `shade`')
class RackspaceTest(ShellCase):
'''
Integration tests for the Rackspace cloud provider using the Openstack driver
'''
@expensiveTest
def setUp(self):
'''
Sets up the test requirements
'''
super(RackspaceTest, self).setUp()
# check if appropriate cloud provider and profile files are present
profile_str = 'openstack-config'
providers = self.run_cloud('--list-providers')
if profile_str + ':' not in providers:
self.skipTest(
'Configuration file for {0} was not found. Check {0}.conf files '
'in tests/integration/files/conf/cloud.*.d/ to run these tests.'
.format(PROVIDER_NAME)
)
# check if personal access token, ssh_key_file, and ssh_key_names are present
config = cloud_providers_config(
os.path.join(
FILES,
'conf',
'cloud.providers.d',
PROVIDER_NAME + '.conf'
)
)
region_name = config[profile_str][DRIVER_NAME].get('region_name')
auth = config[profile_str][DRIVER_NAME].get('auth')
cloud = config[profile_str][DRIVER_NAME].get('cloud')
if not region_name or not (auth or cloud):
self.skipTest(
'A region_name and (auth or cloud) must be provided to run these '
'tests. Check tests/integration/files/conf/cloud.providers.d/{0}.conf'
.format(PROVIDER_NAME)
)
def test_instance(self):
'''
Test creating an instance on rackspace with the openstack driver
'''
# check if instance with salt installed returned
try:
self.assertIn(
INSTANCE_NAME,
[i.strip() for i in self.run_cloud('-p rackspace-test {0}'.format(INSTANCE_NAME), timeout=500)]
)
except AssertionError:
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
raise
# delete the instance
try:
self.assertIn(
INSTANCE_NAME + ':',
[i.strip() for i in self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)]
)
except AssertionError:
raise
def tearDown(self):
'''
Clean up after tests
'''
query = self.run_cloud('--query')
ret = ' {0}:'.format(INSTANCE_NAME)
# if test instance is still present, delete it
if ret in query:
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)

View file

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
def myfunction():
grains = {}
grains['a_custom'] = {'k1': 'v1'}
return grains

View file

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
def test(grains):
return {'custom_grain_test': 'itworked' if 'os' in grains else 'itdidntwork'}

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
def myfunction():
grains = {}
grains['match'] = 'maker'
return grains
grains = {}
grains['a_custom'] = {'k1': 'v1'}
return grains

View file

@ -404,7 +404,7 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin
self.run_script(
'salt-call',
'-c {0} --output-file={1} -l trace -g'.format(
self.get_config_dir(),
self.config_dir,
output_file
),
catch_stderr=True,

View file

@ -742,13 +742,16 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
continue
named_tests.append(test)
if (self.options.unit or self.options.kitchen or named_unit_test) and not named_tests and not \
self._check_enabled_suites(include_cloud_provider=True):
if (self.options.unit or self.options.kitchen or named_unit_test) \
and not named_tests \
and (self.options.from_filenames or
not self._check_enabled_suites(include_cloud_provider=True)):
# We're either not running any integration test suites, or we're
# only running unit tests by passing --unit or by passing only
# `unit.<whatever>` to --name. We don't need the tests daemon
# running
return [True]
if not salt.utils.platform.is_windows():
self.set_filehandle_limits('integration')
@ -807,9 +810,11 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
continue
named_unit_test.append(test)
if not self.options.unit and not named_unit_test:
if not named_unit_test \
and (self.options.from_filenames or not self.options.unit):
# We are not explicitly running the unit tests and none of the
# names passed to --name is a unit test.
# names passed to --name (or derived via --from-filenames) is a
# unit test.
return [True]
status = []

View file

@ -12,6 +12,7 @@
# pylint: disable=repr-flag-used-in-string
from __future__ import absolute_import, print_function
import fnmatch
import os
import sys
import time
@ -20,6 +21,7 @@ import shutil
import logging
import platform
import optparse
import re
import tempfile
import traceback
import subprocess
@ -27,13 +29,20 @@ import warnings
from functools import partial
from collections import namedtuple
import tests.support.paths
from tests.support import helpers
from tests.support.unit import TestLoader, TextTestRunner
from tests.support.xmlunit import HAS_XMLRUNNER, XMLTestRunner
# Import 3rd-party libs
from salt.ext import six
import salt.utils.data
import salt.utils.files
import salt.utils.path
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.yaml
try:
from tests.support.ext import console
WIDTH, HEIGHT = console.getTerminalSize()
@ -184,7 +193,7 @@ class SaltTestingParser(optparse.OptionParser):
'--name',
dest='name',
action='append',
default=None,
default=[],
help=('Specific test name to run. A named test is the module path '
'relative to the tests directory')
)
@ -195,6 +204,29 @@ class SaltTestingParser(optparse.OptionParser):
help=('The location of a newline delimited file of test names to '
'run')
)
self.test_selection_group.add_option(
'--from-filenames',
dest='from_filenames',
action='append',
default=None,
help=('Pass a comma-separated list of file paths, and any '
'unit/integration test module which corresponds to the '
'specified file(s) will be run. For example, a path of '
'salt/modules/git.py would result in unit.modules.test_git '
'and integration.modules.test_git being run. Absolute paths '
'are assumed to be files containing relative paths, one per '
'line. Providing the paths in a file can help get around '
'shell character limits when the list of files is long.')
)
self.test_selection_group.add_option(
'--filename-map',
dest='filename_map',
default=None,
help=('Path to a YAML file mapping paths/path globs to a list '
'of test names to run. See tests/filename_map.yml '
'for example usage (when --from-filenames is used, this '
'map file will be the default one used).')
)
self.add_option_group(self.test_selection_group)
if self.support_docker_execution is True:
@ -308,32 +340,165 @@ class SaltTestingParser(optparse.OptionParser):
self.add_option_group(self.fs_cleanup_options_group)
self.setup_additional_options()
@staticmethod
def _expand_paths(paths):
'''
Expand any comma-separated lists of paths, and return a set of all
paths to ensure there are no duplicates.
'''
ret = set()
for path in paths:
for item in [x.strip() for x in path.split(',')]:
if not item:
continue
elif os.path.isabs(item):
try:
with salt.utils.files.fopen(item, 'rb') as fp_:
for line in fp_:
line = salt.utils.stringutils.to_unicode(line.strip())
if os.path.isabs(line):
log.warning(
'Invalid absolute path %s in %s, '
'ignoring', line, item
)
else:
ret.add(line)
except (IOError, OSError) as exc:
log.error('Failed to read from %s: %s', item, exc)
else:
ret.add(item)
return ret
@property
def _test_mods(self):
'''
Use the test_mods generator to get all of the test module names, and
then store them in a set so that further references to this attribute
will not need to re-walk the test dir.
'''
try:
return self.__test_mods
except AttributeError:
self.__test_mods = set(tests.support.paths.test_mods())
return self.__test_mods
def _map_files(self, files):
'''
Map the passed paths to test modules, returning a set of the mapped
module names.
'''
ret = set()
if self.options.filename_map is not None:
try:
with salt.utils.files.fopen(self.options.filename_map) as fp_:
filename_map = salt.utils.yaml.safe_load(fp_)
except Exception as exc:
raise RuntimeError(
'Failed to load filename map: {0}'.format(exc)
)
else:
filename_map = {}
def _add(comps):
'''
Helper to add unit and integration tests matching a given mod path
'''
mod_relname = '.'.join(comps)
ret.update(
x for x in
['.'.join(('unit', mod_relname)),
'.'.join(('integration', mod_relname))]
if x in self._test_mods
)
# First, try a path match
for path in files:
match = re.match(r'^(salt/|tests/(integration|unit)/)(.+\.py)$', path)
if match:
comps = match.group(3).split('/')
# Find matches for a source file
if match.group(1) == 'salt/':
if comps[-1] == '__init__.py':
comps.pop(-1)
comps[-1] = 'test_' + comps[-1]
else:
comps[-1] = 'test_{0}'.format(comps[-1][:-3])
# Direct name matches
_add(comps)
# State matches for execution modules of the same name
# (e.g. unit.states.test_archive if
# unit.modules.test_archive is being run)
try:
if comps[-2] == 'modules':
comps[-2] = 'states'
_add(comps)
except IndexError:
# Not an execution module. This is either directly in
# the salt/ directory, or salt/something/__init__.py
pass
# Make sure to run a test module if it's been modified
elif match.group(1).startswith('tests/'):
comps.insert(0, match.group(2))
if fnmatch.fnmatch(comps[-1], 'test_*.py'):
comps[-1] = comps[-1][:-3]
test_name = '.'.join(comps)
if test_name in self._test_mods:
ret.add(test_name)
# Next, try the filename_map
for path_expr in filename_map:
for filename in files:
if salt.utils.stringutils.expr_match(filename, path_expr):
ret.update(filename_map[path_expr])
break
return ret
def parse_args(self, args=None, values=None):
self.options, self.args = optparse.OptionParser.parse_args(self, args, values)
file_names = []
if self.options.names_file:
with open(self.options.names_file, 'rb') as fp_: # pylint: disable=resource-leakage
lines = []
for line in fp_.readlines():
if six.PY2:
lines.append(line.strip())
file_names.append(line.strip())
else:
lines.append(
file_names.append(
line.decode(__salt_system_encoding__).strip())
if self.options.name:
self.options.name.extend(lines)
else:
self.options.name = lines
if self.args:
if not self.options.name:
self.options.name = []
for fpath in self.args:
if os.path.isfile(fpath) and \
fpath.endswith('.py') and \
os.path.basename(fpath).startswith('test_'):
self.options.name.append(fpath)
if fpath in file_names:
self.options.name.append(fpath)
continue
self.exit(status=1, msg='\'{}\' is not a valid test module\n'.format(fpath))
if self.options.from_filenames is not None:
self.options.from_filenames = self._expand_paths(self.options.from_filenames)
# Locate the default map file if one was not passed
if self.options.filename_map is None:
self.options.filename_map = salt.utils.path.join(
tests.support.paths.TESTS_DIR,
'filename_map.yml'
)
self.options.name.extend(self._map_files(self.options.from_filenames))
if self.options.name and file_names:
self.options.name = list(set(self.options.name).intersection(file_names))
elif file_names:
self.options.name = file_names
print_header(u'', inline=True, width=self.options.output_columns)
self.pre_execution_cleanup()
@ -341,7 +506,7 @@ class SaltTestingParser(optparse.OptionParser):
if self.source_code_basedir is None:
raise RuntimeError(
'You need to define the \'source_code_basedir\' attribute '
'in {0!r}.'.format(self.__class__.__name__)
'in \'{0}\'.'.format(self.__class__.__name__)
)
if '/' not in self.options.docked:

View file

@ -14,11 +14,14 @@
# Import python libs
from __future__ import absolute_import
import os
import re
import sys
import stat
import logging
import tempfile
import salt.utils.path
log = logging.getLogger(__name__)
TESTS_DIR = os.path.dirname(os.path.dirname(os.path.normpath(os.path.abspath(__file__))))
@ -30,6 +33,7 @@ if sys.platform.startswith('win'):
CODE_DIR = os.path.dirname(TESTS_DIR)
if sys.platform.startswith('win'):
CODE_DIR = CODE_DIR.replace('\\', '\\\\')
UNIT_TEST_DIR = os.path.join(TESTS_DIR, 'unit')
INTEGRATION_TEST_DIR = os.path.join(TESTS_DIR, 'integration')
# Let's inject CODE_DIR so salt is importable if not there already
@ -99,6 +103,24 @@ SCRIPT_TEMPLATES = {
}
def test_mods():
'''
A generator which returns all of the test files
'''
test_re = re.compile(r'^test_.+\.py$')
for dirname in (UNIT_TEST_DIR, INTEGRATION_TEST_DIR):
test_type = os.path.basename(dirname)
for root, _, files in salt.utils.path.os_walk(dirname):
parent_mod = root[len(dirname):].lstrip(os.sep).replace(os.sep, '.')
for filename in files:
if test_re.match(filename):
mod_name = test_type
if parent_mod:
mod_name += '.' + parent_mod
mod_name += '.' + filename[:-3]
yield mod_name
class ScriptPathMixin(object):
def get_script_path(self, script_name):

View file

@ -2,8 +2,6 @@
'''
:codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
'''
# pylint: disable=3rd-party-module-not-gated
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import tempfile
@ -23,27 +21,6 @@ from tests.support.mock import (
# Import Salt Libs
import salt.states.virt as virt
import salt.utils.files
from salt.exceptions import CommandExecutionError
# Import 3rd-party libs
from salt.ext import six
class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors
'''
libvirt library mockup
'''
class libvirtError(Exception): # pylint: disable=invalid-name
'''
libvirt error mockup
'''
def get_error_message(self):
'''
Fake function return error message
'''
return six.text_type(self)
@skipIf(NO_MOCK, NO_MOCK_REASON)
@ -52,12 +29,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
Test cases for salt.states.libvirt
'''
def setup_loader_modules(self):
self.mock_libvirt = LibvirtMock() # pylint: disable=attribute-defined-outside-init
self.addCleanup(delattr, self, 'mock_libvirt')
loader_globals = {
'libvirt': self.mock_libvirt
}
return {virt: loader_globals}
return {virt: {}}
@classmethod
def setUpClass(cls):
@ -84,17 +56,17 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
{'libvirt.servercert.pem': 'A'}])
with patch.dict(virt.__salt__, {'pillar.ext': mock}): # pylint: disable=no-member
with patch.dict(virt.__salt__, {'pillar.ext': mock}):
comt = ('All keys are correct')
ret.update({'comment': comt})
self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret)
with patch.dict(virt.__opts__, {'test': True}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': True}):
comt = ('Libvirt keys are set to be updated')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret)
with patch.dict(virt.__opts__, {'test': False}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': False}):
with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
comt = ('Updated libvirt certs and keys')
ret.update({'comment': comt, 'result': True,
@ -115,21 +87,21 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
{'libvirt.servercert.pem': 'A'}])
with patch.dict(virt.__salt__, {'pillar.ext': mock}): # pylint: disable=no-member
with patch.dict(virt.__salt__, {'pillar.ext': mock}):
comt = ('All keys are correct')
ret.update({'comment': comt})
self.assertDictEqual(virt.keys(name,
basepath=self.pki_dir,
expiration_days=700), ret)
with patch.dict(virt.__opts__, {'test': True}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': True}):
comt = ('Libvirt keys are set to be updated')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(virt.keys(name,
basepath=self.pki_dir,
expiration_days=700), ret)
with patch.dict(virt.__opts__, {'test': False}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': False}):
with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
comt = ('Updated libvirt certs and keys')
ret.update({'comment': comt, 'result': True,
@ -152,21 +124,21 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
{'libvirt.servercert.pem': 'A'}])
with patch.dict(virt.__salt__, {'pillar.ext': mock}): # pylint: disable=no-member
with patch.dict(virt.__salt__, {'pillar.ext': mock}):
comt = ('All keys are correct')
ret.update({'comment': comt})
self.assertDictEqual(virt.keys(name,
basepath=self.pki_dir,
st='California'), ret)
with patch.dict(virt.__opts__, {'test': True}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': True}):
comt = ('Libvirt keys are set to be updated')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(virt.keys(name,
basepath=self.pki_dir,
st='California'), ret)
with patch.dict(virt.__opts__, {'test': False}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': False}):
with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
comt = ('Updated libvirt certs and keys')
ret.update({'comment': comt, 'result': True,
@ -189,7 +161,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'],
{'libvirt.servercert.pem': 'A'}])
with patch.dict(virt.__salt__, {'pillar.ext': mock}): # pylint: disable=no-member
with patch.dict(virt.__salt__, {'pillar.ext': mock}):
comt = ('All keys are correct')
ret.update({'comment': comt})
self.assertDictEqual(virt.keys(name,
@ -200,7 +172,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
organization='SaltStack',
expiration_days=700), ret)
with patch.dict(virt.__opts__, {'test': True}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': True}):
comt = ('Libvirt keys are set to be updated')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(virt.keys(name,
@ -211,7 +183,7 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
organization='SaltStack',
expiration_days=700), ret)
with patch.dict(virt.__opts__, {'test': False}): # pylint: disable=no-member
with patch.dict(virt.__opts__, {'test': False}):
with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())):
comt = ('Updated libvirt certs and keys')
ret.update({'comment': comt, 'result': True,
@ -223,484 +195,3 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin):
locality='Los_Angeles',
organization='SaltStack',
expiration_days=700), ret)
def test_running(self):
'''
running state test cases.
'''
ret = {'name': 'myvm',
'changes': {},
'result': True,
'comment': 'myvm is running'}
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(return_value='stopped'),
'virt.start': MagicMock(return_value=0),
}):
ret.update({'changes': {'myvm': 'Domain started'},
'comment': 'Domain myvm started'})
self.assertDictEqual(virt.running('myvm'), ret)
init_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
'virt.init': init_mock,
'virt.start': MagicMock(return_value=0)
}):
ret.update({'changes': {'myvm': 'Domain defined and started'},
'comment': 'Domain myvm defined and started'})
self.assertDictEqual(virt.running('myvm',
cpu=2,
mem=2048,
image='/path/to/img.qcow2'), ret)
init_mock.assert_called_with('myvm', cpu=2, mem=2048, image='/path/to/img.qcow2',
os_type=None, arch=None,
disk=None, disks=None, nic=None, interfaces=None,
graphics=None, hypervisor=None,
seed=True, install=True, pub_key=None, priv_key=None,
connection=None, username=None, password=None)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')),
'virt.init': init_mock,
'virt.start': MagicMock(return_value=0)
}):
ret.update({'changes': {'myvm': 'Domain defined and started'},
'comment': 'Domain myvm defined and started'})
disks = [{
'name': 'system',
'size': 8192,
'overlay_image': True,
'pool': 'default',
'image': '/path/to/image.qcow2'
},
{
'name': 'data',
'size': 16834
}]
ifaces = [{
'name': 'eth0',
'mac': '01:23:45:67:89:AB'
},
{
'name': 'eth1',
'type': 'network',
'source': 'admin'
}]
graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}}
self.assertDictEqual(virt.running('myvm',
cpu=2,
mem=2048,
os_type='linux',
arch='i686',
vm_type='qemu',
disk_profile='prod',
disks=disks,
nic_profile='prod',
interfaces=ifaces,
graphics=graphics,
seed=False,
install=False,
pub_key='/path/to/key.pub',
priv_key='/path/to/key',
connection='someconnection',
username='libvirtuser',
password='supersecret'), ret)
init_mock.assert_called_with('myvm',
cpu=2,
mem=2048,
os_type='linux',
arch='i686',
image=None,
disk='prod',
disks=disks,
nic='prod',
interfaces=ifaces,
graphics=graphics,
hypervisor='qemu',
seed=False,
install=False,
pub_key='/path/to/key.pub',
priv_key='/path/to/key',
connection='someconnection',
username='libvirtuser',
password='supersecret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(return_value='stopped'),
'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')])
}):
ret.update({'changes': {}, 'result': False, 'comment': 'libvirt error msg'})
self.assertDictEqual(virt.running('myvm'), ret)
# Working update case when running
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(return_value='running'),
'virt.update': MagicMock(return_value={'definition': True, 'cpu': True})
}):
ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}},
'result': True,
'comment': 'Domain myvm updated, restart to fully apply the changes'})
self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
# Working update case when stopped
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(return_value='stopped'),
'virt.start': MagicMock(return_value=0),
'virt.update': MagicMock(return_value={'definition': True})
}):
ret.update({'changes': {'myvm': 'Domain updated and started'},
'result': True,
'comment': 'Domain myvm updated and started'})
self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
# Failed live update case
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(return_value='running'),
'virt.update': MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']})
}):
ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}},
'result': True,
'comment': 'Domain myvm updated, but some live update(s) failed'})
self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
# Failed definition update case
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.vm_state': MagicMock(return_value='running'),
'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')])
}):
ret.update({'changes': {},
'result': False,
'comment': 'error message'})
self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret)
def test_stopped(self):
'''
stopped state test cases.
'''
ret = {'name': 'myvm',
'changes': {},
'result': True}
shutdown_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.shutdown': shutdown_mock
}):
ret.update({'changes': {
'stopped': [{'domain': 'myvm', 'shutdown': True}]
},
'comment': 'Machine has been shut down'})
self.assertDictEqual(virt.stopped('myvm'), ret)
shutdown_mock.assert_called_with('myvm', connection=None, username=None, password=None)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.shutdown': shutdown_mock,
}):
self.assertDictEqual(virt.stopped('myvm',
connection='myconnection',
username='user',
password='secret'), ret)
shutdown_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.shutdown': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
}):
ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
'result': False,
'comment': 'No changes had happened'})
self.assertDictEqual(virt.stopped('myvm'), ret)
with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
self.assertDictEqual(virt.stopped('myvm'), ret)
def test_powered_off(self):
'''
powered_off state test cases.
'''
ret = {'name': 'myvm',
'changes': {},
'result': True}
stop_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.stop': stop_mock
}):
ret.update({'changes': {
'unpowered': [{'domain': 'myvm', 'stop': True}]
},
'comment': 'Machine has been powered off'})
self.assertDictEqual(virt.powered_off('myvm'), ret)
stop_mock.assert_called_with('myvm', connection=None, username=None, password=None)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.stop': stop_mock,
}):
self.assertDictEqual(virt.powered_off('myvm',
connection='myconnection',
username='user',
password='secret'), ret)
stop_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.stop': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
}):
ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
'result': False,
'comment': 'No changes had happened'})
self.assertDictEqual(virt.powered_off('myvm'), ret)
with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
self.assertDictEqual(virt.powered_off('myvm'), ret)
def test_snapshot(self):
'''
snapshot state test cases.
'''
ret = {'name': 'myvm',
'changes': {},
'result': True}
snapshot_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.snapshot': snapshot_mock
}):
ret.update({'changes': {
'saved': [{'domain': 'myvm', 'snapshot': True}]
},
'comment': 'Snapshot has been taken'})
self.assertDictEqual(virt.snapshot('myvm'), ret)
snapshot_mock.assert_called_with('myvm', suffix=None, connection=None, username=None, password=None)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.snapshot': snapshot_mock,
}):
self.assertDictEqual(virt.snapshot('myvm',
suffix='snap',
connection='myconnection',
username='user',
password='secret'), ret)
snapshot_mock.assert_called_with('myvm',
suffix='snap',
connection='myconnection',
username='user',
password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.snapshot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
}):
ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
'result': False,
'comment': 'No changes had happened'})
self.assertDictEqual(virt.snapshot('myvm'), ret)
with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
self.assertDictEqual(virt.snapshot('myvm'), ret)
def test_rebooted(self):
'''
rebooted state test cases.
'''
ret = {'name': 'myvm',
'changes': {},
'result': True}
reboot_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.reboot': reboot_mock
}):
ret.update({'changes': {
'rebooted': [{'domain': 'myvm', 'reboot': True}]
},
'comment': 'Machine has been rebooted'})
self.assertDictEqual(virt.rebooted('myvm'), ret)
reboot_mock.assert_called_with('myvm', connection=None, username=None, password=None)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.reboot': reboot_mock,
}):
self.assertDictEqual(virt.rebooted('myvm',
connection='myconnection',
username='user',
password='secret'), ret)
reboot_mock.assert_called_with('myvm',
connection='myconnection',
username='user',
password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']),
'virt.reboot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
}):
ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]},
'result': False,
'comment': 'No changes had happened'})
self.assertDictEqual(virt.rebooted('myvm'), ret)
with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member
ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'})
self.assertDictEqual(virt.rebooted('myvm'), ret)
def test_network_running(self):
'''
network_running state test cases.
'''
ret = {'name': 'mynet', 'changes': {}, 'result': True, 'comment': ''}
define_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.network_info': MagicMock(return_value={}),
'virt.network_define': define_mock
}):
ret.update({'changes': {'mynet': 'Network defined and started'},
'comment': 'Network mynet defined and started'})
self.assertDictEqual(virt.network_running('mynet',
'br2',
'bridge',
vport='openvswitch',
tag=180,
autostart=False,
connection='myconnection',
username='user',
password='secret'), ret)
define_mock.assert_called_with('mynet',
'br2',
'bridge',
'openvswitch',
tag=180,
autostart=False,
start=True,
connection='myconnection',
username='user',
password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.network_info': MagicMock(return_value={'active': True}),
'virt.network_define': define_mock,
}):
ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'})
self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
start_mock = MagicMock(return_value=True)
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.network_info': MagicMock(return_value={'active': False}),
'virt.network_start': start_mock,
'virt.network_define': define_mock,
}):
ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet started'})
self.assertDictEqual(virt.network_running('mynet',
'br2',
'bridge',
connection='myconnection',
username='user',
password='secret'), ret)
start_mock.assert_called_with('mynet', connection='myconnection', username='user', password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.network_info': MagicMock(return_value={}),
'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
}):
ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret)
def test_pool_running(self):
'''
pool_running state test cases.
'''
ret = {'name': 'mypool', 'changes': {}, 'result': True, 'comment': ''}
mocks = {mock: MagicMock(return_value=True) for mock in ['define', 'autostart', 'build', 'start']}
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.pool_info': MagicMock(return_value={}),
'virt.pool_define': mocks['define'],
'virt.pool_build': mocks['build'],
'virt.pool_start': mocks['start'],
'virt.pool_set_autostart': mocks['autostart']
}):
ret.update({'changes': {'mypool': 'Pool defined and started'},
'comment': 'Pool mypool defined and started'})
self.assertDictEqual(virt.pool_running('mypool',
ptype='logical',
target='/dev/base',
permissions={'mode': '0770',
'owner': 1000,
'group': 100,
'label': 'seclabel'},
source={'devices': [{'path': '/dev/sda'}]},
transient=True,
autostart=True,
connection='myconnection',
username='user',
password='secret'), ret)
mocks['define'].assert_called_with('mypool',
ptype='logical',
target='/dev/base',
permissions={'mode': '0770',
'owner': 1000,
'group': 100,
'label': 'seclabel'},
source_devices=[{'path': '/dev/sda'}],
source_dir=None,
source_adapter=None,
source_hosts=None,
source_auth=None,
source_name=None,
source_format=None,
transient=True,
start=True,
connection='myconnection',
username='user',
password='secret')
mocks['autostart'].assert_called_with('mypool',
state='on',
connection='myconnection',
username='user',
password='secret')
mocks['build'].assert_called_with('mypool',
connection='myconnection',
username='user',
password='secret')
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.pool_info': MagicMock(return_value={'state': 'running'}),
}):
ret.update({'changes': {}, 'comment': 'Pool mypool exists and is running'})
self.assertDictEqual(virt.pool_running('mypool',
ptype='logical',
target='/dev/base',
source={'devices': [{'path': '/dev/sda'}]}), ret)
for mock in mocks:
mocks[mock].reset_mock()
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.pool_info': MagicMock(return_value={'state': 'stopped'}),
'virt.pool_build': mocks['build'],
'virt.pool_start': mocks['start']
}):
ret.update({'changes': {'mypool': 'Pool started'}, 'comment': 'Pool mypool started'})
self.assertDictEqual(virt.pool_running('mypool',
ptype='logical',
target='/dev/base',
source={'devices': [{'path': '/dev/sda'}]}), ret)
mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None)
mocks['build'].assert_not_called()
with patch.dict(virt.__salt__, { # pylint: disable=no-member
'virt.pool_info': MagicMock(return_value={}),
'virt.pool_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error'))
}):
ret.update({'changes': {}, 'comment': 'Some error', 'result': False})
self.assertDictEqual(virt.pool_running('mypool',
ptype='logical',
target='/dev/base',
source={'devices': [{'path': '/dev/sda'}]}), ret)

View file

@ -662,8 +662,8 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
@with_tempdir()
def test_minion_id_lowercase(self, tempdir):
'''
This tests that setting `minion_id_lowercase: True` does lower case
the minion id. Lowercase does not operate on a static `id: KING_BOB`
This tests that setting `minion_id_lowercase: True` does lower case
the minion id. Lowercase does not operate on a static `id: KING_BOB`
setting, or a cached id.
'''
minion_config = os.path.join(tempdir, 'minion')

View file

@ -11,10 +11,11 @@ import os
# Import Salt libs
import salt.utils.path
import salt.utils.stringutils
# Import Salt Testing libs
from tests.support.unit import TestCase
from tests.support.paths import CODE_DIR
from tests.support.paths import CODE_DIR, test_mods
EXCLUDED_DIRS = [
os.path.join('tests', 'pkg'),
@ -93,3 +94,140 @@ class BadTestModuleNamesTestCase(TestCase):
error_msg += '\'EXCLUDED_DIRS\' or \'EXCLUDED_FILES\' in \'tests/unit/test_module_names.py\'.\n'
error_msg += 'If it is a tests module, then please rename as suggested.'
self.assertEqual([], bad_names, error_msg)
def test_module_name_source_match(self):
'''
Check all the test mods and check if they correspond to actual files in
the codebase. If this test fails, then a test module is likely not
named correctly, and should be adjusted.
If a test module doesn't have a natural name match (as does this very
file), then its should be included in the "ignore" tuple below.
However, if there is no matching source code file, then you should
consider mapping it to files manually via tests/filename_map.yml.
'''
ignore = (
'unit.test_doc',
'unit.test_mock',
'unit.test_module_names',
'unit.test_simple',
'unit.test_zypp_plugins',
'unit.cache.test_cache',
'unit.serializers.test_serializers',
'unit.states.test_postgres',
'integration.cli.test_custom_module',
'integration.cli.test_grains',
'integration.client.test_kwarg',
'integration.client.test_runner',
'integration.client.test_standard',
'integration.client.test_syndic',
'integration.cloud.test_cloud',
'integration.doc.test_man',
'integration.externalapi.test_venafiapi',
'integration.grains.test_custom',
'integration.loader.test_ext_grains',
'integration.loader.test_ext_modules',
'integration.logging.test_jid_logging',
'integration.minion.test_blackout',
'integration.minion.test_pillar',
'integration.minion.test_timeout',
'integration.modules.test_decorators',
'integration.modules.test_pkg',
'integration.modules.test_state_jinja_filters',
'integration.modules.test_sysctl',
'integration.netapi.test_client',
'integration.netapi.rest_tornado.test_app',
'integration.netapi.rest_cherrypy.test_app_pam',
'integration.output.test_output',
'integration.proxy.test_shell',
'integration.proxy.test_simple',
'integration.reactor.test_reactor',
'integration.runners.test_runner_returns',
'integration.scheduler.test_error',
'integration.scheduler.test_eval',
'integration.scheduler.test_postpone',
'integration.scheduler.test_skip',
'integration.shell.test_spm',
'integration.shell.test_cp',
'integration.shell.test_syndic',
'integration.shell.test_proxy',
'integration.shell.test_auth',
'integration.shell.test_call',
'integration.shell.test_arguments',
'integration.shell.test_matcher',
'integration.shell.test_master_tops',
'integration.shell.test_saltcli',
'integration.shell.test_master',
'integration.shell.test_key',
'integration.shell.test_runner',
'integration.shell.test_cloud',
'integration.shell.test_enabled',
'integration.shell.test_minion',
'integration.spm.test_build',
'integration.spm.test_files',
'integration.spm.test_info',
'integration.spm.test_install',
'integration.spm.test_remove',
'integration.spm.test_repo',
'integration.ssh.test_deploy',
'integration.ssh.test_grains',
'integration.ssh.test_jinja_filters',
'integration.ssh.test_master',
'integration.ssh.test_mine',
'integration.ssh.test_pillar',
'integration.ssh.test_raw',
'integration.ssh.test_state',
'integration.states.test_compiler',
'integration.states.test_handle_error',
'integration.states.test_handle_iorder',
'integration.states.test_match',
'integration.states.test_renderers',
'integration.wheel.test_client',
)
errors = []
def _format_errors(errors):
msg = (
'The following {0} test module(s) could not be matched to a '
'source code file:\n\n'.format(len(errors))
)
msg += ''.join(errors)
return msg
for mod_name in test_mods():
if mod_name in ignore:
# Test module is being ignored, skip it
continue
# Separate the test_foo away from the rest of the mod name, because
# we'll need to remove the "test_" from the beginning and add .py
stem, flower = mod_name.rsplit('.', 1)
# Lop off the integration/unit from the beginning of the mod name
try:
stem = stem.split('.', 1)[1]
except IndexError:
# This test mod was in the root of the unit/integration dir
stem = ''
# The path from the root of the repo
relpath = salt.utils.path.join(
'salt',
stem.replace('.', os.sep),
'.'.join((flower[5:], 'py')))
# The full path to the file we expect to find
abspath = salt.utils.path.join(CODE_DIR, relpath)
if not os.path.isfile(abspath):
# Maybe this is in a dunder init?
alt_relpath = salt.utils.path.join(relpath[:-3], '__init__.py')
alt_abspath = salt.utils.path.join(abspath[:-3], '__init__.py')
if os.path.isfile(alt_abspath):
# Yep, it is. Carry on!
continue
errors.append(
'{0} (expected: {1})\n'.format(mod_name, relpath)
)
assert not errors, _format_errors(errors)