mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Refactor `testprogram
` framework to minimize explicit setup and improve reuse.
* Add a ``testprogram.TestProgramCase()`` to simplify test classes + Has ``setUp()`` and ``tearDown()`` to create ephemeral test directory (really should be moved to salttesting.TestCase) + Common ``assert_exit_status()`` for validating and reporting failures in exit status. * Add missing ``testprogram.TestDaemonSaltProxy`` class * Remove specific setting of ``program`` parameter in initialization of ``TestDaemonSaltMinion`` since ``testprogram.TestSaltProgramMeta`` already handles that unless a specific override is required. * Automatically set ``user`` in config for master, minion and proxy test classes if a specific user is not specified. * Automatically set ``PYTHONPATH`` as ``sys.path`` unless it is specified.
This commit is contained in:
parent
7c6216c3fc
commit
68a9912d3a
2 changed files with 95 additions and 64 deletions
|
@ -9,14 +9,13 @@
|
|||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import getpass
|
||||
import os
|
||||
import sys
|
||||
import getpass
|
||||
import platform
|
||||
import yaml
|
||||
import signal
|
||||
import shutil
|
||||
import tempfile
|
||||
import logging
|
||||
|
||||
# Import Salt Testing libs
|
||||
|
@ -34,28 +33,17 @@ log = logging.getLogger(__name__)
|
|||
DEBUG = True
|
||||
|
||||
|
||||
class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
||||
class MinionTest(integration.ShellCase, testprogram.TestProgramCase, integration.ShellCaseCommonTestsMixIn):
|
||||
'''
|
||||
Various integration tests for the salt-minion executable.
|
||||
'''
|
||||
_call_binary_ = 'salt-minion'
|
||||
_test_dir = None
|
||||
|
||||
_test_minions = (
|
||||
'minion',
|
||||
'subminion',
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
# Setup for scripts
|
||||
self._test_dir = tempfile.mkdtemp(prefix='salt-testdaemon-')
|
||||
|
||||
def tearDown(self):
|
||||
# shutdown for scripts
|
||||
if self._test_dir and os.path.sep == self._test_dir[0]:
|
||||
shutil.rmtree(self._test_dir)
|
||||
self._test_dir = None
|
||||
|
||||
def test_issue_7754(self):
|
||||
old_cwd = os.getcwd()
|
||||
config_dir = os.path.join(integration.TMP, 'issue-7754')
|
||||
|
@ -165,7 +153,6 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
for mname in minions:
|
||||
minion = testprogram.TestDaemonSaltMinion(
|
||||
name=mname,
|
||||
config={'user': user},
|
||||
parent_dir=self._test_dir,
|
||||
)
|
||||
# Call setup here to ensure config and script exist
|
||||
|
@ -179,7 +166,6 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
sysconf_dir = os.path.dirname(_minions[0].config_dir)
|
||||
cmd_env = {
|
||||
'PATH': ':'.join([salt_call.script_dir, os.getenv('PATH')]),
|
||||
'PYTHONPATH': ':'.join(sys.path),
|
||||
'SALTMINION_DEBUG': '1' if DEBUG else '',
|
||||
'SALTMINION_PYTHON': sys.executable,
|
||||
'SALTMINION_SYSCONFDIR': sysconf_dir,
|
||||
|
@ -219,6 +205,10 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
minions, _, init_script = self._initscript_setup(self._test_minions[:1])
|
||||
|
||||
try:
|
||||
# These tests are grouped together, rather than split into individual test functions,
|
||||
# because subsequent tests leverage the state from the previous test which minimizes
|
||||
# setup for each test.
|
||||
|
||||
# I take visual readability with aligned columns over strict PEP8
|
||||
# (bad-whitespace) Exactly one space required after comma
|
||||
# pylint: disable=C0326
|
||||
|
@ -254,28 +244,6 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
for minion in minions:
|
||||
minion.shutdown()
|
||||
|
||||
def _assert_exit_status(self, status, ex_status, message=None, stdout=None, stderr=None):
|
||||
'''
|
||||
Helper function to verify exit status and emit failure information.
|
||||
'''
|
||||
|
||||
ex_val = getattr(salt.defaults.exitcodes, ex_status)
|
||||
_message = '' if not message else ' ({0})'.format(message)
|
||||
_stdout = '' if not stdout else '\nstdout: {0}'.format('\nstdout: '.join(stdout))
|
||||
_stderr = '' if not stderr else '\nstderr: {0}'.format('\nstderr: '.join(stderr))
|
||||
self.assertEqual(
|
||||
status,
|
||||
ex_val,
|
||||
'Exit status was {0}, must be {1} (salt.default.exitcodes.{2}){3}{4}{5}'.format(
|
||||
status,
|
||||
ex_val,
|
||||
ex_status,
|
||||
_message,
|
||||
_stderr,
|
||||
_stderr,
|
||||
)
|
||||
)
|
||||
|
||||
def test_exit_status_unknown_user(self):
|
||||
'''
|
||||
Ensure correct exit status when the minion is configured to run as an unknown user.
|
||||
|
@ -283,12 +251,8 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
|
||||
minion = testprogram.TestDaemonSaltMinion(
|
||||
name='unknown_user',
|
||||
program=os.path.join(integration.CODE_DIR, 'scripts', 'salt-minion'),
|
||||
config={'user': 'unknown'},
|
||||
parent_dir=self._test_dir,
|
||||
env={
|
||||
'PYTHONPATH': ':'.join(sys.path),
|
||||
},
|
||||
)
|
||||
# Call setup here to ensure config and script exist
|
||||
minion.setup()
|
||||
|
@ -297,13 +261,12 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
catch_stderr=True,
|
||||
with_retcode=True,
|
||||
)
|
||||
self._assert_exit_status(
|
||||
status,
|
||||
'EX_NOUSER',
|
||||
self.assert_exit_status(
|
||||
status, 'EX_NOUSER',
|
||||
message='unknown user not on system',
|
||||
stdout=stdout,
|
||||
stderr=stderr
|
||||
stdout=stdout, stderr=stderr
|
||||
)
|
||||
# minion.shutdown() should be unnecessary since the start-up should fail
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def test_exit_status_unknown_argument(self):
|
||||
|
@ -311,16 +274,9 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
Ensure correct exit status when an unknown argument is passed to salt-minion.
|
||||
'''
|
||||
|
||||
user = getpass.getuser()
|
||||
|
||||
minion = testprogram.TestDaemonSaltMinion(
|
||||
name='unknown_argument',
|
||||
program=os.path.join(integration.CODE_DIR, 'scripts', 'salt-minion'),
|
||||
config={'user': user},
|
||||
parent_dir=self._test_dir,
|
||||
env={
|
||||
'PYTHONPATH': ':'.join(sys.path),
|
||||
},
|
||||
)
|
||||
# Call setup here to ensure config and script exist
|
||||
minion.setup()
|
||||
|
@ -329,23 +285,21 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
catch_stderr=True,
|
||||
with_retcode=True,
|
||||
)
|
||||
self._assert_exit_status(status, 'EX_USAGE', message='unknown argument', stdout=stdout, stderr=stderr)
|
||||
self.assert_exit_status(
|
||||
status, 'EX_USAGE',
|
||||
message='unknown argument',
|
||||
stdout=stdout, stderr=stderr
|
||||
)
|
||||
# minion.shutdown() should be unnecessary since the start-up should fail
|
||||
|
||||
def test_exit_status_correct_usage(self):
|
||||
'''
|
||||
Ensure correct exit status when salt-minion starts correctly.
|
||||
'''
|
||||
|
||||
user = getpass.getuser()
|
||||
|
||||
minion = testprogram.TestDaemonSaltMinion(
|
||||
name='correct_usage',
|
||||
program=os.path.join(integration.CODE_DIR, 'scripts', 'salt-minion'),
|
||||
config={'user': user},
|
||||
parent_dir=self._test_dir,
|
||||
env={
|
||||
'PYTHONPATH': ':'.join(sys.path),
|
||||
},
|
||||
)
|
||||
# Call setup here to ensure config and script exist
|
||||
minion.setup()
|
||||
|
@ -354,7 +308,11 @@ class MinionTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
catch_stderr=True,
|
||||
with_retcode=True,
|
||||
)
|
||||
self._assert_exit_status(status, 'EX_OK', message='correct usage', stdout=stdout, stderr=stderr)
|
||||
self.assert_exit_status(
|
||||
status, 'EX_OK',
|
||||
message='correct usage',
|
||||
stdout=stdout, stderr=stderr
|
||||
)
|
||||
minion.shutdown()
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import atexit
|
|||
import copy
|
||||
from datetime import datetime, timedelta
|
||||
import errno
|
||||
import getpass
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
@ -24,6 +25,8 @@ import salt.utils.process
|
|||
import salt.utils.psutil_compat as psutils
|
||||
from salt.defaults import exitcodes
|
||||
|
||||
from salttesting import TestCase
|
||||
|
||||
import integration
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -46,6 +49,8 @@ class TestProgram(object):
|
|||
self.program = program or getattr(self, 'program', None)
|
||||
self.name = name or getattr(self, 'name', None)
|
||||
self.env = env or {}
|
||||
if 'PYTHONPATH' not in self.env:
|
||||
self.env['PYTHONPATH'] = ':'.join(sys.path)
|
||||
self.shell = shell
|
||||
self._parent_dir = parent_dir or None
|
||||
self.clean_on_exit = clean_on_exit
|
||||
|
@ -481,7 +486,11 @@ class TestDaemon(TestProgram):
|
|||
if not os.path.exists(self.config_dir):
|
||||
os.makedirs(self.config_dir)
|
||||
with open(self.config_path, 'w') as cfo:
|
||||
cfo.write(self.config_stringify())
|
||||
cfg = self.config_stringify()
|
||||
LOG.debug('Writing configuration for {0} to {1}:\n{2}'.format(
|
||||
self.name, self.config_path, cfg
|
||||
))
|
||||
cfo.write(cfg)
|
||||
cfo.flush()
|
||||
|
||||
def make_dirtree(self):
|
||||
|
@ -601,6 +610,11 @@ class TestDaemonSaltMaster(TestSaltDaemon):
|
|||
|
||||
config_file = 'master'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
cfg = kwargs.setdefault('config', {})
|
||||
_ = cfg.setdefault('user', getpass.getuser())
|
||||
super(TestDaemonSaltMaster, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class TestDaemonSaltMinion(TestSaltDaemon):
|
||||
'''
|
||||
|
@ -612,6 +626,11 @@ class TestDaemonSaltMinion(TestSaltDaemon):
|
|||
}
|
||||
config_file = 'minion'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
cfg = kwargs.setdefault('config', {})
|
||||
_ = cfg.setdefault('user', getpass.getuser())
|
||||
super(TestDaemonSaltMinion, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class TestDaemonSaltApi(TestSaltDaemon):
|
||||
'''
|
||||
|
@ -625,3 +644,57 @@ class TestDaemonSaltSyndic(TestSaltDaemon):
|
|||
Manager for salt-syndic daemon.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
class TestDaemonSaltProxy(TestSaltDaemon):
|
||||
'''
|
||||
Manager for salt-proxy daemon.
|
||||
'''
|
||||
|
||||
config_file = 'proxy'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
cfg = kwargs.setdefault('config', {})
|
||||
_ = cfg.setdefault('user', getpass.getuser())
|
||||
super(TestDaemonSaltProxy, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class TestProgramCase(TestCase):
|
||||
'''
|
||||
Utilities for unit tests that use TestProgram()
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
# Setup for scripts
|
||||
if not getattr(self, '_test_dir', None):
|
||||
self._test_dir = tempfile.mkdtemp(prefix='salt-testdaemon-')
|
||||
super(TestProgramCase, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
# shutdown for scripts
|
||||
if self._test_dir and os.path.sep == self._test_dir[0]:
|
||||
shutil.rmtree(self._test_dir)
|
||||
self._test_dir = None
|
||||
super(TestProgramCase, self).tearDown()
|
||||
|
||||
def assert_exit_status(self, status, ex_status, message=None, stdout=None, stderr=None):
|
||||
'''
|
||||
Helper function to verify exit status and emit failure information.
|
||||
'''
|
||||
|
||||
ex_val = getattr(exitcodes, ex_status)
|
||||
_message = '' if not message else ' ({0})'.format(message)
|
||||
_stdout = '' if not stdout else '\nstdout: {0}'.format('\nstdout: '.join(stdout))
|
||||
_stderr = '' if not stderr else '\nstderr: {0}'.format('\nstderr: '.join(stderr))
|
||||
self.assertEqual(
|
||||
status,
|
||||
ex_val,
|
||||
'Exit status was {0}, must be {1} (salt.default.exitcodes.{2}){3}{4}{5}'.format(
|
||||
status,
|
||||
ex_val,
|
||||
ex_status,
|
||||
_message,
|
||||
_stderr,
|
||||
_stderr,
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue