Rewrite the tests_valid_docs test

This uses a function in the runtests_helpers custom module to perform
all the logic, and only returns what failed the test. This saves us from
having to return the entire contents of sys.doc (as well as log all of
the function calls), and also removes the need to run sys.doc in batches
to get around the "max message size" issue.
This commit is contained in:
Erik Johnson 2017-02-26 12:21:00 -06:00
parent a29a7be7f8
commit 5a3c099e4f
6 changed files with 102 additions and 79 deletions

View file

@ -679,7 +679,13 @@ class SMinion(MinionBase):
def gen_modules(self, initial_load=False):
'''
Load all of the modules for the minion
Tell the minion to reload the execution modules
CLI Example:
.. code-block:: bash
salt '*' sys.reload_modules
'''
self.opts['pillar'] = salt.pillar.get_pillar(
self.opts,
@ -734,7 +740,13 @@ class MasterMinion(object):
def gen_modules(self, initial_load=False):
'''
Load all of the modules for the minion
Tell the minion to reload the execution modules
CLI Example:
.. code-block:: bash
salt '*' sys.reload_modules
'''
self.utils = salt.loader.utils(self.opts)
self.functions = salt.loader.minion_mods(

View file

@ -421,8 +421,10 @@ def reload_modules():
salt '*' sys.reload_modules
'''
# This is handled inside the minion.py file, the function is caught before
# it ever gets here
# This function is actually handled inside the minion.py file, the function
# is caught before it ever gets here. Therefore, the docstring above is
# only for the online docs, and ANY CHANGES made to it must also be made in
# each of the gen_modules() funcs in minion.py.
return True

View file

@ -1138,6 +1138,13 @@ class TestDaemon(object):
TMP_PRODENV_STATE_TREE
]
}
master_opts.setdefault('reactor', []).append(
{
'salt/minion/*/start': [
os.path.join(FILES, 'reactor-sync-minion.sls')
],
}
)
for opts_dict in (master_opts, syndic_master_opts):
if 'ext_pillar' not in opts_dict:
opts_dict['ext_pillar'] = []

View file

@ -9,12 +9,16 @@
# Import python libs
from __future__ import absolute_import
import fnmatch
import os
import re
import tempfile
# Import salt libs
import salt.utils
# Import 3rd-party libs
import salt.ext.six as six
SYS_TMP_DIR = os.path.realpath(
# Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long
@ -29,9 +33,75 @@ TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir')
def get_salt_temp_dir():
return TMP
def get_salt_temp_dir_for_path(*path):
return os.path.join(TMP, *path)
def get_sys_temp_dir_for_path(*path):
return os.path.join(SYS_TMP_DIR, *path)
def get_invalid_docs():
'''
Outputs the functions which do not have valid CLI example, or are missing a
docstring.
'''
allow_failure = (
'cmd.win_runas',
'cp.recv',
'glance.warn_until',
'ipset.long_range',
'libcloud_dns.get_driver',
'log.critical',
'log.debug',
'log.error',
'log.exception',
'log.info',
'log.warning',
'lowpkg.bin_pkg_info',
'lxc.run_cmd',
'nspawn.restart',
'nspawn.stop',
'pkg.expand_repo_def',
'pip.iteritems',
'runtests_decorators.depends',
'runtests_decorators.depends_will_fallback',
'runtests_decorators.missing_depends',
'runtests_decorators.missing_depends_will_fallback',
'state.apply',
'status.list2cmdline',
'swift.head',
'travisci.parse_qs',
'vsphere.clean_kwargs',
'vsphere.disconnect',
'vsphere.get_service_instance_via_proxy',
'vsphere.gets_service_instance_via_proxy',
'vsphere.supports_proxies',
'vsphere.test_vcenter_connection',
'vsphere.wraps',
)
allow_failure_glob = (
'runtests_helpers.*',
)
nodoc = set()
noexample = set()
for fun, docstring in six.iteritems(__salt__['sys.doc']()):
if fun in allow_failure:
continue
else:
for pat in allow_failure_glob:
if fnmatch.fnmatch(fun, pat):
matched_glob = True
break
else:
matched_glob = False
if matched_glob:
continue
if not isinstance(docstring, six.string_types):
nodoc.add(fun)
elif not re.search(r'([E|e]xample(?:s)?)+(?:.*):?', docstring):
noexample.add(fun)
return {'missing_docstring': sorted(nodoc),
'missing_cli_example': sorted(noexample)}

View file

@ -0,0 +1,3 @@
sync_minion:
local.saltutil.sync_all:
- tgt: {{ data['id'] }}

View file

@ -2,10 +2,6 @@
# Import python libs
from __future__ import absolute_import
import logging
import re
log = logging.getLogger(__name__)
# Import Salt Testing libs
from salttesting.helpers import ensure_in_syspath
@ -14,9 +10,6 @@ ensure_in_syspath('../../')
# Import salt libs
import integration
# Import 3rd-party libs
import salt.ext.six as six
class SysModuleTest(integration.ModuleCase):
'''
@ -26,79 +19,15 @@ class SysModuleTest(integration.ModuleCase):
'''
Make sure no functions are exposed that don't have valid docstrings
'''
mods = self.run_function('sys.list_modules')
nodoc = set()
noexample = set()
allow_failure = (
'cp.recv',
'libcloud_dns.get_driver',
'lxc.run_cmd',
'ipset.long_range',
'pkg.expand_repo_def',
'runtests_decorators.depends',
'runtests_decorators.depends_will_fallback',
'runtests_decorators.missing_depends',
'runtests_decorators.missing_depends_will_fallback',
'swift.head',
'glance.warn_until',
'yumpkg.expand_repo_def',
'yumpkg5.expand_repo_def',
'container_resource.run',
'nspawn.stop',
'nspawn.restart',
'lowpkg.bin_pkg_info',
'state.apply',
'pip.iteritems',
'cmd.win_runas',
'status.list2cmdline'
)
batches = 2
mod_count = len(mods)
batch_size = mod_count / float(batches)
if batch_size.is_integer():
batch_size = int(batch_size)
else:
# Check if the module count is evenly divisible by the number of
# batches. If not, increase the batch_size by the number of batches
# being run. This ensures that we get the correct number of
# batches, and that we don't end up running sys.doc an extra time
# to cover the remainder. For example, if we had a batch count of 2
# and 121 modules, if we just divided by 2 we'd end up running
# sys.doc 3 times.
batch_size = int(batch_size) + batches
log.debug('test_valid_docs batch size = %s', batch_size)
start = 0
end = batch_size
while start <= mod_count:
log.debug('running sys.doc on mods[%s:%s]', start, end)
docs = self.run_function('sys.doc', mods[start:end])
if docs == 'VALUE TRIMMED':
self.fail(
'sys.doc output trimmed. It may be necessary to increase '
'the number of batches'
)
for fun in docs:
if fun.startswith('runtests_helpers'):
continue
if fun in allow_failure:
continue
if not isinstance(docs[fun], six.string_types):
nodoc.add(fun)
elif not re.search(r'([E|e]xample(?:s)?)+(?:.*)::?', docs[fun]):
noexample.add(fun)
start += batch_size
end += batch_size
if not nodoc and not noexample:
ret = self.run_function('runtests_helpers.get_invalid_docs')
if ret == {'missing_docstring': [], 'missing_cli_example': []}:
return
raise AssertionError(
'There are some functions which do not have a docstring or do not '
'have an example:\nNo docstring:\n{0}\nNo example:\n{1}\n'.format(
'\n'.join([' - {0}'.format(f) for f in sorted(nodoc)]),
'\n'.join([' - {0}'.format(f) for f in sorted(noexample)]),
'\n'.join([' - {0}'.format(f) for f in ret['missing_docstring']]),
'\n'.join([' - {0}'.format(f) for f in ret['missing_cli_example']]),
)
)