mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 17:50:20 +00:00
Remove runtests.py
and it's supporting code
This commit is contained in:
parent
2627e6ca1c
commit
e47d5b0bd0
28 changed files with 27 additions and 4647 deletions
232
noxfile.py
232
noxfile.py
|
@ -359,85 +359,15 @@ def _run_with_coverage(session, *test_cmd, env=None):
|
|||
shutil.move(".coverage", os.path.join("artifacts", "coverage", ".coverage"))
|
||||
|
||||
|
||||
def _runtests(session, coverage, cmd_args):
|
||||
# Create required artifacts directories
|
||||
_create_ci_directories()
|
||||
env = {}
|
||||
if IS_DARWIN:
|
||||
# Don't nuke our multiprocessing efforts objc!
|
||||
# https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr
|
||||
env["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES"
|
||||
try:
|
||||
if coverage is True:
|
||||
_run_with_coverage(
|
||||
session,
|
||||
"coverage",
|
||||
"run",
|
||||
os.path.join("tests", "runtests.py"),
|
||||
*cmd_args,
|
||||
env=env
|
||||
)
|
||||
else:
|
||||
cmd_args = ["python", os.path.join("tests", "runtests.py")] + list(cmd_args)
|
||||
session.run(*cmd_args, env=env)
|
||||
except CommandFailed: # pylint: disable=try-except-raise
|
||||
# Disabling re-running failed tests for the time being
|
||||
raise
|
||||
|
||||
# pylint: disable=unreachable
|
||||
names_file_path = os.path.join("artifacts", "failed-tests.txt")
|
||||
session.log("Re-running failed tests if possible")
|
||||
session.install(
|
||||
"--progress-bar=off", "xunitparser==1.3.3", silent=PIP_INSTALL_SILENT
|
||||
def _runtests(session):
|
||||
session.error(
|
||||
"""\n\nruntests.py support has been removed from Salt. Please try `nox -e '{0}'` """
|
||||
"""or `nox -e '{0}' -- --help` to know more about the supported CLI flags.\n"""
|
||||
"For more information, please check "
|
||||
"https://docs.saltproject.io/en/latest/topics/development/tests/index.html#running-the-tests\n..".format(
|
||||
session._runner.global_config.sessions[0].replace("runtests", "pytest")
|
||||
)
|
||||
session.run(
|
||||
"python",
|
||||
os.path.join(
|
||||
"tests", "support", "generate-names-file-from-failed-test-reports.py"
|
||||
),
|
||||
names_file_path,
|
||||
)
|
||||
if not os.path.exists(names_file_path):
|
||||
session.log(
|
||||
"Failed tests file(%s) was not found. Not rerunning failed tests.",
|
||||
names_file_path,
|
||||
)
|
||||
# raise the original exception
|
||||
raise
|
||||
with open(names_file_path) as rfh:
|
||||
contents = rfh.read().strip()
|
||||
if not contents:
|
||||
session.log(
|
||||
"The failed tests file(%s) is empty. Not rerunning failed tests.",
|
||||
names_file_path,
|
||||
)
|
||||
# raise the original exception
|
||||
raise
|
||||
failed_tests_count = len(contents.splitlines())
|
||||
if failed_tests_count > 500:
|
||||
# 500 test failures?! Something else must have gone wrong, don't even bother
|
||||
session.error(
|
||||
"Total failed tests({}) > 500. No point on re-running the failed tests".format(
|
||||
failed_tests_count
|
||||
)
|
||||
)
|
||||
|
||||
for idx, flag in enumerate(cmd_args[:]):
|
||||
if "--names-file=" in flag:
|
||||
cmd_args.pop(idx)
|
||||
break
|
||||
elif flag == "--names-file":
|
||||
cmd_args.pop(idx) # pop --names-file
|
||||
cmd_args.pop(idx) # pop the actual names file
|
||||
break
|
||||
cmd_args.append("--names-file={}".format(names_file_path))
|
||||
if coverage is True:
|
||||
_run_with_coverage(
|
||||
session, "coverage", "run", "-m", "tests.runtests", *cmd_args
|
||||
)
|
||||
else:
|
||||
session.run("python", os.path.join("tests", "runtests.py"), *cmd_args)
|
||||
# pylint: enable=unreachable
|
||||
)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-parametrized")
|
||||
|
@ -448,33 +378,7 @@ def runtests_parametrized(session, coverage, transport, crypto):
|
|||
"""
|
||||
DO NOT CALL THIS NOX SESSION DIRECTLY
|
||||
"""
|
||||
# Install requirements
|
||||
_install_requirements(session, transport, "unittest-xml-reporting==2.5.2")
|
||||
|
||||
if crypto:
|
||||
session.run(
|
||||
"pip",
|
||||
"uninstall",
|
||||
"-y",
|
||||
"m2crypto",
|
||||
"pycrypto",
|
||||
"pycryptodome",
|
||||
"pycryptodomex",
|
||||
silent=True,
|
||||
)
|
||||
install_command = [
|
||||
"--progress-bar=off",
|
||||
"--constraint",
|
||||
_get_pip_requirements_file(session, transport, crypto=True),
|
||||
]
|
||||
install_command.append(crypto)
|
||||
session.install(*install_command, silent=PIP_INSTALL_SILENT)
|
||||
|
||||
cmd_args = [
|
||||
"--tests-logfile={}".format(RUNTESTS_LOGFILE),
|
||||
"--transport={}".format(transport),
|
||||
] + session.posargs
|
||||
_runtests(session, coverage, cmd_args)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS)
|
||||
|
@ -483,15 +387,7 @@ def runtests(session, coverage):
|
|||
"""
|
||||
runtests.py session with zeromq transport and default crypto
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto=None,
|
||||
transport="zeromq",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-tcp")
|
||||
|
@ -500,15 +396,7 @@ def runtests_tcp(session, coverage):
|
|||
"""
|
||||
runtests.py session with TCP transport and default crypto
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto=None,
|
||||
transport="tcp",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-zeromq")
|
||||
|
@ -517,15 +405,7 @@ def runtests_zeromq(session, coverage):
|
|||
"""
|
||||
runtests.py session with zeromq transport and default crypto
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto=None,
|
||||
transport="zeromq",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-m2crypto")
|
||||
|
@ -534,15 +414,7 @@ def runtests_m2crypto(session, coverage):
|
|||
"""
|
||||
runtests.py session with zeromq transport and m2crypto
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto="m2crypto",
|
||||
transport="zeromq",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-tcp-m2crypto")
|
||||
|
@ -551,15 +423,7 @@ def runtests_tcp_m2crypto(session, coverage):
|
|||
"""
|
||||
runtests.py session with TCP transport and m2crypto
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto="m2crypto",
|
||||
transport="tco",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-zeromq-m2crypto")
|
||||
|
@ -568,15 +432,7 @@ def runtests_zeromq_m2crypto(session, coverage):
|
|||
"""
|
||||
runtests.py session with zeromq transport and m2crypto
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto="m2crypto",
|
||||
transport="zeromq",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-pycryptodome")
|
||||
|
@ -585,15 +441,7 @@ def runtests_pycryptodome(session, coverage):
|
|||
"""
|
||||
runtests.py session with zeromq transport and pycryptodome
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto="pycryptodome",
|
||||
transport="zeromq",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-tcp-pycryptodome")
|
||||
|
@ -602,15 +450,7 @@ def runtests_tcp_pycryptodome(session, coverage):
|
|||
"""
|
||||
runtests.py session with TCP transport and pycryptodome
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto="pycryptodome",
|
||||
transport="tcp",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-zeromq-pycryptodome")
|
||||
|
@ -619,15 +459,7 @@ def runtests_zeromq_pycryptodome(session, coverage):
|
|||
"""
|
||||
runtests.py session with zeromq transport and pycryptodome
|
||||
"""
|
||||
session.notify(
|
||||
find_session_runner(
|
||||
session,
|
||||
"runtests-parametrized-{}".format(session.python),
|
||||
coverage=coverage,
|
||||
crypto="pycryptodome",
|
||||
transport="zeromq",
|
||||
)
|
||||
)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-cloud")
|
||||
|
@ -636,21 +468,7 @@ def runtests_cloud(session, coverage):
|
|||
"""
|
||||
runtests.py cloud tests session
|
||||
"""
|
||||
# Install requirements
|
||||
_install_requirements(session, "zeromq", "unittest-xml-reporting==2.2.1")
|
||||
|
||||
requirements_file = os.path.join(
|
||||
"requirements", "static", "ci", _get_pydir(session), "cloud.txt"
|
||||
)
|
||||
|
||||
install_command = ["--progress-bar=off", "-r", requirements_file]
|
||||
session.install(*install_command, silent=PIP_INSTALL_SILENT)
|
||||
|
||||
cmd_args = [
|
||||
"--tests-logfile={}".format(RUNTESTS_LOGFILE),
|
||||
"--cloud-provider-tests",
|
||||
] + session.posargs
|
||||
_runtests(session, coverage, cmd_args)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="runtests-tornado")
|
||||
|
@ -659,13 +477,7 @@ def runtests_tornado(session, coverage):
|
|||
"""
|
||||
runtests.py tornado tests session
|
||||
"""
|
||||
# Install requirements
|
||||
_install_requirements(session, "zeromq", "unittest-xml-reporting==2.2.1")
|
||||
session.install("--progress-bar=off", "tornado==5.0.2", silent=PIP_INSTALL_SILENT)
|
||||
session.install("--progress-bar=off", "pyzmq==17.0.0", silent=PIP_INSTALL_SILENT)
|
||||
|
||||
cmd_args = ["--tests-logfile={}".format(RUNTESTS_LOGFILE)] + session.posargs
|
||||
_runtests(session, coverage, cmd_args)
|
||||
_runtests(session)
|
||||
|
||||
|
||||
@nox.session(python=_PYTHON_VERSIONS, name="pytest-parametrized")
|
||||
|
@ -921,10 +733,6 @@ def _pytest(session, coverage, cmd_args):
|
|||
# Create required artifacts directories
|
||||
_create_ci_directories()
|
||||
|
||||
session.run(
|
||||
"pip", "uninstall", "-y", "pytest-salt", silent=True,
|
||||
)
|
||||
|
||||
env = {"PYTEST_SESSION": "1", "CI_RUN": "1" if CI_RUN else "0"}
|
||||
if IS_DARWIN:
|
||||
# Don't nuke our multiprocessing efforts objc!
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
mock >= 3.0.0
|
||||
# PyTest
|
||||
pytest >= 6.1.0
|
||||
pytest-salt
|
||||
pytest-salt-factories >= 0.120.2
|
||||
pytest-tempdir >= 2019.10.12
|
||||
pytest-helpers-namespace >= 2019.1.8
|
||||
|
|
|
@ -189,7 +189,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -194,7 +194,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -194,7 +194,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -92,7 +92,6 @@ pyparsing==2.4.5 # via packaging
|
|||
pyrsistent==0.17.3 # via jsonschema
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -188,7 +188,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -193,7 +193,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -193,7 +193,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -91,7 +91,6 @@ pyparsing==2.4.5 # via packaging
|
|||
pyrsistent==0.17.3 # via jsonschema
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -189,7 +189,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -194,7 +194,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -194,7 +194,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -87,7 +87,6 @@ pyparsing==2.4.5 # via packaging
|
|||
pyrsistent==0.17.3 # via jsonschema
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -188,7 +188,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -193,7 +193,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -193,7 +193,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -188,7 +188,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -193,7 +193,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
|
@ -194,7 +194,6 @@ pyrsistent==0.17.3 # via jsonschema
|
|||
pyserial==3.4 # via junos-eznc, netmiko
|
||||
pytest-helpers-namespace==2019.1.8
|
||||
pytest-salt-factories==0.120.2
|
||||
pytest-salt==2020.1.27
|
||||
pytest-subtests==0.4.0
|
||||
pytest-tempdir==2019.10.12
|
||||
pytest==6.1.2
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,11 +27,6 @@
|
|||
$ salt-ssh localhost state.sls custom_module
|
||||
localhost:
|
||||
olleh
|
||||
|
||||
|
||||
This test can be run in a small test suite with:
|
||||
|
||||
$ python tests/runtests.py -C --ssh
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
:codeauthor: Pedro Algarvio (pedro@algarvio.me)
|
||||
:copyright: Copyright 2015 by the SaltStack Team, see AUTHORS for more details.
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
|
||||
|
||||
pytestsalt.engines.pytest_engine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Simple salt engine which will setup a socket to accept connections allowing us to know
|
||||
when a daemon is up and running
|
||||
"""
|
||||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
|
||||
import salt.utils.asynchronous
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils.event
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext.tornado import gen, ioloop, iostream, netutil
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = "salt_runtests"
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if __opts__["__role"] != "master":
|
||||
return False
|
||||
return "runtests_conn_check_port" in __opts__ # pylint: disable=undefined-variable
|
||||
|
||||
|
||||
def start():
|
||||
pytest_engine = PyTestEngine(__opts__) # pylint: disable=undefined-variable
|
||||
pytest_engine.start()
|
||||
|
||||
|
||||
class PyTestEngine(object):
|
||||
def __init__(self, opts):
|
||||
self.opts = opts
|
||||
self.sock = None
|
||||
self.stop_sending_events_file = opts.get("pytest_stop_sending_events_file")
|
||||
|
||||
def start(self):
|
||||
self.io_loop = ioloop.IOLoop()
|
||||
self.io_loop.make_current()
|
||||
self.io_loop.add_callback(self._start)
|
||||
self.io_loop.start()
|
||||
|
||||
@gen.coroutine
|
||||
def _start(self):
|
||||
port = int(self.opts["runtests_conn_check_port"])
|
||||
log.info(
|
||||
"Starting Pytest Engine(role=%s, id=%s) on port %s",
|
||||
self.opts["__role"],
|
||||
self.opts["id"],
|
||||
port,
|
||||
)
|
||||
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.sock.setblocking(0)
|
||||
# bind the socket to localhost on the config provided port
|
||||
self.sock.bind(("localhost", port))
|
||||
# become a server socket
|
||||
self.sock.listen(5)
|
||||
with salt.utils.asynchronous.current_ioloop(self.io_loop):
|
||||
netutil.add_accept_handler(
|
||||
self.sock, self.handle_connection,
|
||||
)
|
||||
|
||||
if self.opts["__role"] == "master":
|
||||
yield self.fire_master_started_event()
|
||||
|
||||
def handle_connection(self, connection, address):
|
||||
log.warning(
|
||||
"Accepted connection from %s. Role: %s", address, self.opts["__role"]
|
||||
)
|
||||
# We just need to know that the daemon running the engine is alive...
|
||||
try:
|
||||
connection.shutdown(socket.SHUT_RDWR) # pylint: disable=no-member
|
||||
connection.close()
|
||||
except socket.error as exc:
|
||||
if not sys.platform.startswith("darwin"):
|
||||
raise
|
||||
try:
|
||||
if exc.errno != errno.ENOTCONN:
|
||||
raise
|
||||
except AttributeError:
|
||||
# This is not macOS !?
|
||||
pass
|
||||
|
||||
@gen.coroutine
|
||||
def fire_master_started_event(self):
|
||||
log.info("Firing salt-%s started event...", self.opts["__role"])
|
||||
start_event_tag = "salt/{}/{}/start".format(
|
||||
self.opts["__role"], self.opts["id"]
|
||||
)
|
||||
log.info(
|
||||
"Firing salt-%s started event. Tag: %s",
|
||||
self.opts["__role"],
|
||||
start_event_tag,
|
||||
)
|
||||
load = {"id": self.opts["id"], "tag": start_event_tag, "data": {}}
|
||||
# One minute should be more than enough to fire these events every second in order
|
||||
# for pytest-salt to pickup that the master is running
|
||||
with salt.utils.event.get_master_event(
|
||||
self.opts, self.opts["sock_dir"], listen=False
|
||||
) as event_bus:
|
||||
timeout = 30
|
||||
while True:
|
||||
if self.stop_sending_events_file and not os.path.exists(
|
||||
self.stop_sending_events_file
|
||||
):
|
||||
log.info(
|
||||
'The stop sending events file "marker" is done. Stop sending events...'
|
||||
)
|
||||
break
|
||||
timeout -= 1
|
||||
try:
|
||||
event_bus.fire_event(load, start_event_tag, timeout=500)
|
||||
if timeout <= 0:
|
||||
break
|
||||
yield gen.sleep(1)
|
||||
except iostream.StreamClosedError:
|
||||
break
|
|
@ -1,110 +0,0 @@
|
|||
"""
|
||||
:codeauthor: Pedro Algarvio (pedro@algarvio.me)
|
||||
:copyright: Copyright 2016 by the SaltStack Team, see AUTHORS for more details.
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
|
||||
|
||||
pytestsalt.salt.log_handlers.pytest_log_handler
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Salt External Logging Handler
|
||||
"""
|
||||
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import socket
|
||||
import threading
|
||||
from multiprocessing import Queue
|
||||
|
||||
import salt.log.setup
|
||||
import salt.utils.msgpack
|
||||
from salt.utils.platform import is_darwin
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = "runtests_log_handler"
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if "runtests_log_port" not in __opts__:
|
||||
return False, "'runtests_log_port' not in options"
|
||||
return (
|
||||
False,
|
||||
"runtests external logging handler is temporarily disabled for Python 3 tests",
|
||||
)
|
||||
|
||||
|
||||
def setup_handlers():
|
||||
port = __opts__["runtests_log_port"]
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
sock.connect(("localhost", port))
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ECONNREFUSED:
|
||||
log.warning("Failed to connect to log server")
|
||||
return
|
||||
finally:
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
except OSError:
|
||||
pass
|
||||
sock.close()
|
||||
|
||||
# One million log messages is more than enough to queue.
|
||||
# Above that value, if `process_queue` can't process fast enough,
|
||||
# start dropping. This will contain a memory leak in case `process_queue`
|
||||
# can't process fast enough of in case it can't deliver the log records at all.
|
||||
if is_darwin():
|
||||
queue_size = 32767
|
||||
else:
|
||||
queue_size = 10000000
|
||||
queue = Queue(queue_size)
|
||||
handler = salt.log.setup.QueueHandler(queue)
|
||||
level = salt.log.setup.LOG_LEVELS[
|
||||
(__opts__.get("runtests_log_level") or "error").lower()
|
||||
]
|
||||
handler.setLevel(level)
|
||||
process_queue_thread = threading.Thread(target=process_queue, args=(port, queue))
|
||||
process_queue_thread.daemon = True
|
||||
process_queue_thread.start()
|
||||
return handler
|
||||
|
||||
|
||||
def process_queue(port, queue):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
sock.connect(("localhost", port))
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ECONNREFUSED:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
log.warning("Failed to connect to log server")
|
||||
return
|
||||
|
||||
while True:
|
||||
try:
|
||||
record = queue.get()
|
||||
if record is None:
|
||||
# A sentinel to stop processing the queue
|
||||
break
|
||||
# Just log everything, filtering will happen on the main process
|
||||
# logging handlers
|
||||
sock.sendall(salt.utils.msgpack.dumps(record.__dict__, use_bin_type=True))
|
||||
except (OSError, EOFError, KeyboardInterrupt, SystemExit):
|
||||
if hasattr(exc, "errno") and exc.errno != errno.EPIPE:
|
||||
log.exception(exc)
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
except OSError:
|
||||
pass
|
||||
break
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
log.warning(
|
||||
"An exception occurred in the pytest salt logging " "queue thread: %s",
|
||||
exc,
|
||||
exc_info_on_loglevel=logging.DEBUG,
|
||||
)
|
|
@ -1,975 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
"""
|
||||
Discover all instances of unittest.TestCase in this directory.
|
||||
"""
|
||||
# pylint: disable=file-perms
|
||||
|
||||
import collections
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__)))
|
||||
if os.name == "nt":
|
||||
TESTS_DIR = TESTS_DIR.replace("\\", "\\\\")
|
||||
CODE_DIR = os.path.dirname(TESTS_DIR)
|
||||
|
||||
# Let's inject CODE_DIR so salt is importable if not there already
|
||||
if "" in sys.path:
|
||||
sys.path.remove("")
|
||||
if TESTS_DIR in sys.path:
|
||||
sys.path.remove(TESTS_DIR)
|
||||
if CODE_DIR in sys.path and sys.path[0] != CODE_DIR:
|
||||
sys.path.remove(CODE_DIR)
|
||||
if CODE_DIR not in sys.path:
|
||||
sys.path.insert(0, CODE_DIR)
|
||||
if TESTS_DIR not in sys.path:
|
||||
sys.path.insert(1, TESTS_DIR)
|
||||
|
||||
try:
|
||||
import tests
|
||||
|
||||
if not tests.__file__.startswith(CODE_DIR):
|
||||
print("Found tests module not from salt in {}".format(tests.__file__))
|
||||
sys.modules.pop("tests")
|
||||
module_dir = os.path.dirname(tests.__file__)
|
||||
if module_dir in sys.path:
|
||||
sys.path.remove(module_dir)
|
||||
del tests
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
from tests.support.paths import TMP, SYS_TMP_DIR, INTEGRATION_TEST_DIR
|
||||
from tests.support.paths import CODE_DIR as SALT_ROOT
|
||||
except ImportError as exc:
|
||||
try:
|
||||
import tests
|
||||
|
||||
print("Found tests module not from salt in {}".format(tests.__file__))
|
||||
except ImportError:
|
||||
print("Unable to import salt test module")
|
||||
print("PYTHONPATH:", os.environ.get("PYTHONPATH"))
|
||||
print("Current sys.path:")
|
||||
import pprint
|
||||
|
||||
pprint.pprint(sys.path)
|
||||
raise
|
||||
|
||||
from tests.integration import TestDaemon, TestDaemonStartFailed # isort:skip
|
||||
import salt.utils.platform # isort:skip
|
||||
|
||||
if not salt.utils.platform.is_windows():
|
||||
import resource
|
||||
|
||||
from tests.support.parser import PNUM, print_header # isort:skip
|
||||
from tests.support.parser.cover import SaltCoverageTestingParser # isort:skip
|
||||
|
||||
XML_OUTPUT_DIR = os.environ.get(
|
||||
"SALT_XML_TEST_REPORTS_DIR", os.path.join(TMP, "xml-test-reports")
|
||||
)
|
||||
HTML_OUTPUT_DIR = os.environ.get(
|
||||
"SALT_HTML_TEST_REPORTS_DIR", os.path.join(TMP, "html-test-reports")
|
||||
)
|
||||
|
||||
TEST_DIR = os.path.dirname(INTEGRATION_TEST_DIR)
|
||||
try:
|
||||
if SALT_ROOT:
|
||||
os.chdir(SALT_ROOT)
|
||||
except OSError as err:
|
||||
print("Failed to change directory to salt's source: {}".format(err))
|
||||
|
||||
# Soft and hard limits on max open filehandles
|
||||
MAX_OPEN_FILES = {
|
||||
"integration": {"soft_limit": 3072, "hard_limit": 4096},
|
||||
"unit": {"soft_limit": 1024, "hard_limit": 2048},
|
||||
}
|
||||
|
||||
# Combine info from command line options and test suite directories. A test
|
||||
# suite is a python package of test modules relative to the tests directory.
|
||||
TEST_SUITES_UNORDERED = {
|
||||
"unit": {"display_name": "Unit", "path": "unit"},
|
||||
"kitchen": {"display_name": "Kitchen", "path": "kitchen"},
|
||||
"multimaster": {"display_name": "Multimaster", "path": "multimaster"},
|
||||
"module": {"display_name": "Module", "path": "integration/modules"},
|
||||
"state": {"display_name": "State", "path": "integration/states"},
|
||||
"cli": {"display_name": "CLI", "path": "integration/cli"},
|
||||
"client": {"display_name": "Client", "path": "integration/client"},
|
||||
"doc": {"display_name": "Documentation", "path": "integration/doc"},
|
||||
"ext_pillar": {"display_name": "External Pillar", "path": "integration/pillar"},
|
||||
"grains": {"display_name": "Grains", "path": "integration/grains"},
|
||||
"shell": {"display_name": "Shell", "path": "integration/shell"},
|
||||
"runners": {"display_name": "Runners", "path": "integration/runners"},
|
||||
"renderers": {"display_name": "Renderers", "path": "integration/renderers"},
|
||||
"returners": {"display_name": "Returners", "path": "integration/returners"},
|
||||
"ssh-int": {"display_name": "SSH Integration", "path": "integration/ssh"},
|
||||
"spm": {"display_name": "SPM", "path": "integration/spm"},
|
||||
"loader": {"display_name": "Loader", "path": "integration/loader"},
|
||||
"outputter": {"display_name": "Outputter", "path": "integration/output"},
|
||||
"fileserver": {"display_name": "Fileserver", "path": "integration/fileserver"},
|
||||
"wheel": {"display_name": "Wheel", "path": "integration/wheel"},
|
||||
"api": {"display_name": "NetAPI", "path": "integration/netapi"},
|
||||
"cloud_provider": {
|
||||
"display_name": "Cloud Provider",
|
||||
"path": "integration/cloud/clouds",
|
||||
},
|
||||
"minion": {"display_name": "Minion", "path": "integration/minion"},
|
||||
"proxy": {"display_name": "Proxy", "path": "integration/proxy"},
|
||||
"external_api": {"display_name": "ExternalAPIs", "path": "integration/externalapi"},
|
||||
"daemons": {"display_name": "Daemon", "path": "integration/daemons"},
|
||||
"sdb": {"display_name": "Sdb", "path": "integration/sdb"},
|
||||
}
|
||||
|
||||
TEST_SUITES = collections.OrderedDict(
|
||||
sorted(TEST_SUITES_UNORDERED.items(), key=lambda x: x[0])
|
||||
)
|
||||
|
||||
|
||||
class SaltTestsuiteParser(SaltCoverageTestingParser):
|
||||
support_docker_execution = True
|
||||
support_destructive_tests_selection = True
|
||||
support_expensive_tests_selection = True
|
||||
source_code_basedir = SALT_ROOT
|
||||
|
||||
def _get_suites(
|
||||
self,
|
||||
include_unit=False,
|
||||
include_cloud_provider=False,
|
||||
include_proxy=False,
|
||||
include_kitchen=False,
|
||||
include_multimaster=False,
|
||||
):
|
||||
"""
|
||||
Return a set of all test suites except unit and cloud provider tests
|
||||
unless requested
|
||||
"""
|
||||
suites = set(TEST_SUITES.keys())
|
||||
if not include_unit:
|
||||
suites -= {"unit"}
|
||||
if not include_cloud_provider:
|
||||
suites -= {"cloud_provider"}
|
||||
if not include_proxy:
|
||||
suites -= {"proxy"}
|
||||
if not include_kitchen:
|
||||
suites -= {"kitchen"}
|
||||
# Multimaster tests now run under PyTest
|
||||
suites -= {"multimaster"}
|
||||
|
||||
return suites
|
||||
|
||||
def _check_enabled_suites(
|
||||
self,
|
||||
include_unit=False,
|
||||
include_cloud_provider=False,
|
||||
include_proxy=False,
|
||||
include_kitchen=False,
|
||||
include_multimaster=False,
|
||||
):
|
||||
"""
|
||||
Query whether test suites have been enabled
|
||||
"""
|
||||
suites = self._get_suites(
|
||||
include_unit=include_unit,
|
||||
include_cloud_provider=include_cloud_provider,
|
||||
include_proxy=include_proxy,
|
||||
include_kitchen=include_kitchen,
|
||||
include_multimaster=include_multimaster,
|
||||
)
|
||||
|
||||
return any([getattr(self.options, suite) for suite in suites])
|
||||
|
||||
def _enable_suites(
|
||||
self,
|
||||
include_unit=False,
|
||||
include_cloud_provider=False,
|
||||
include_proxy=False,
|
||||
include_kitchen=False,
|
||||
include_multimaster=False,
|
||||
):
|
||||
"""
|
||||
Enable test suites for current test run
|
||||
"""
|
||||
suites = self._get_suites(
|
||||
include_unit=include_unit,
|
||||
include_cloud_provider=include_cloud_provider,
|
||||
include_proxy=include_proxy,
|
||||
include_kitchen=include_kitchen,
|
||||
include_multimaster=include_multimaster,
|
||||
)
|
||||
|
||||
for suite in suites:
|
||||
setattr(self.options, suite, True)
|
||||
|
||||
def setup_additional_options(self):
|
||||
self.add_option(
|
||||
"--sysinfo",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Print some system information.",
|
||||
)
|
||||
self.add_option(
|
||||
"--transport",
|
||||
default="zeromq",
|
||||
choices=("zeromq", "tcp"),
|
||||
help=(
|
||||
"Select which transport to run the integration tests with, "
|
||||
"zeromq or tcp. Default: %default"
|
||||
),
|
||||
)
|
||||
self.add_option(
|
||||
"--interactive",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Do not run any tests. Simply start the daemons.",
|
||||
)
|
||||
self.output_options_group.add_option(
|
||||
"--no-colors",
|
||||
"--no-colours",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Disable colour printing.",
|
||||
)
|
||||
|
||||
self.test_selection_group.add_option(
|
||||
"-m",
|
||||
"--module",
|
||||
"--module-tests",
|
||||
dest="module",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for modules",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-S",
|
||||
"--state",
|
||||
"--state-tests",
|
||||
dest="state",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for states",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-C",
|
||||
"--cli",
|
||||
"--cli-tests",
|
||||
dest="cli",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for cli",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-c",
|
||||
"--client",
|
||||
"--client-tests",
|
||||
dest="client",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for client",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-d",
|
||||
"--doc",
|
||||
"--doc-tests",
|
||||
dest="doc",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for documentation",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-I",
|
||||
"--ext-pillar",
|
||||
"--ext-pillar-tests",
|
||||
dest="ext_pillar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run ext_pillar tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-G",
|
||||
"--grains",
|
||||
"--grains-tests",
|
||||
dest="grains",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for grains",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-s",
|
||||
"--shell",
|
||||
"--shell-tests",
|
||||
dest="shell",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run shell tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-r",
|
||||
"--runners",
|
||||
"--runner-tests",
|
||||
dest="runners",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run salt/runners/*.py tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-R",
|
||||
"--renderers",
|
||||
"--renderer-tests",
|
||||
dest="renderers",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run salt/renderers/*.py tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--reactor",
|
||||
dest="reactor",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run salt/reactor/*.py tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--minion",
|
||||
"--minion-tests",
|
||||
dest="minion",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests for minion",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--returners",
|
||||
dest="returners",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run salt/returners/*.py tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--spm",
|
||||
dest="spm",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run spm integration tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--setup",
|
||||
dest="setup",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run setup integration tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-l",
|
||||
"--loader",
|
||||
"--loader-tests",
|
||||
dest="loader",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run loader tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-u",
|
||||
"--unit",
|
||||
"--unit-tests",
|
||||
dest="unit",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run unit tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-k",
|
||||
"--kitchen",
|
||||
"--kitchen-tests",
|
||||
dest="kitchen",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run kitchen tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--fileserver",
|
||||
"--fileserver-tests",
|
||||
dest="fileserver",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run Fileserver tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-w",
|
||||
"--wheel",
|
||||
"--wheel-tests",
|
||||
dest="wheel",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run wheel tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-o",
|
||||
"--outputter",
|
||||
"--outputter-tests",
|
||||
dest="outputter",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run outputter tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--cloud-provider",
|
||||
"--cloud-provider-tests",
|
||||
dest="cloud_provider",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=(
|
||||
"Run cloud provider tests. These tests create and delete "
|
||||
"instances on cloud providers. Must provide valid credentials "
|
||||
"in salt/tests/integration/files/conf/cloud.*.d to run tests."
|
||||
),
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--ssh",
|
||||
"--ssh-tests",
|
||||
dest="ssh",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run salt-ssh tests. These tests will spin up a temporary "
|
||||
"SSH server on your machine. In certain environments, this "
|
||||
"may be insecure! Default: False",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--ssh-int",
|
||||
dest="ssh-int",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run salt-ssh integration tests. Requires to be run with --ssh"
|
||||
"to spin up the SSH server on your machine.",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-A",
|
||||
"--api",
|
||||
"--api-tests",
|
||||
dest="api",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run salt-api tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--sdb",
|
||||
"--sdb-tests",
|
||||
dest="sdb",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run sdb tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"-P",
|
||||
"--proxy",
|
||||
"--proxy-tests",
|
||||
dest="proxy",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run salt-proxy tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--external",
|
||||
"--external-api",
|
||||
"--external-api-tests",
|
||||
dest="external_api",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run venafi runner tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--daemons",
|
||||
"--daemon-tests",
|
||||
dest="daemons",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run salt/daemons/*.py tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--scheduler",
|
||||
dest="scheduler",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run scheduler integration tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--logging",
|
||||
dest="logging",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run logging integration tests",
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
"--multimaster",
|
||||
dest="multimaster",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Start multimaster daemons and run multimaster integration tests",
|
||||
)
|
||||
|
||||
def validate_options(self):
|
||||
if self.options.cloud_provider or self.options.external_api:
|
||||
# Turn on expensive tests execution
|
||||
os.environ["EXPENSIVE_TESTS"] = "True"
|
||||
|
||||
# This fails even with salt.utils.platform imported in the global
|
||||
# scope, unless we import it again here.
|
||||
import salt.utils.platform
|
||||
|
||||
if salt.utils.platform.is_windows():
|
||||
import salt.utils.win_functions
|
||||
|
||||
current_user = salt.utils.win_functions.get_current_user()
|
||||
if current_user == "SYSTEM":
|
||||
is_admin = True
|
||||
else:
|
||||
is_admin = salt.utils.win_functions.is_admin(current_user)
|
||||
if (
|
||||
self.options.coverage
|
||||
and any(
|
||||
(self.options.name, not is_admin, not self.options.run_destructive)
|
||||
)
|
||||
and self._check_enabled_suites(include_unit=True)
|
||||
):
|
||||
warnings.warn("Test suite not running with elevated priviledges")
|
||||
else:
|
||||
is_admin = os.geteuid() == 0
|
||||
|
||||
if (
|
||||
self.options.coverage
|
||||
and any(
|
||||
(self.options.name, not is_admin, not self.options.run_destructive)
|
||||
)
|
||||
and self._check_enabled_suites(include_unit=True)
|
||||
):
|
||||
self.error(
|
||||
"No sense in generating the tests coverage report when "
|
||||
"not running the full test suite, including the "
|
||||
"destructive tests, as 'root'. It would only produce "
|
||||
"incorrect results."
|
||||
)
|
||||
|
||||
# When no tests are specifically enumerated on the command line, setup
|
||||
# a default run: +unit -cloud_provider
|
||||
if not self.options.name and not self._check_enabled_suites(
|
||||
include_unit=True,
|
||||
include_cloud_provider=True,
|
||||
include_proxy=True,
|
||||
include_kitchen=True,
|
||||
include_multimaster=True,
|
||||
):
|
||||
self._enable_suites(include_unit=True, include_multimaster=True)
|
||||
|
||||
self.start_coverage(
|
||||
branch=True, source=[os.path.join(SALT_ROOT, "salt")],
|
||||
)
|
||||
|
||||
# Print out which version of python this test suite is running on
|
||||
print(" * Python Version: {}".format(" ".join(sys.version.split())))
|
||||
|
||||
# Transplant configuration
|
||||
TestDaemon.transplant_configs(transport=self.options.transport)
|
||||
|
||||
def post_execution_cleanup(self):
|
||||
SaltCoverageTestingParser.post_execution_cleanup(self)
|
||||
if self.options.clean:
|
||||
TestDaemon.clean()
|
||||
|
||||
def run_integration_suite(self, path="", display_name=""):
|
||||
"""
|
||||
Run an integration test suite
|
||||
"""
|
||||
full_path = os.path.join(TEST_DIR, path)
|
||||
return self.run_suite(
|
||||
full_path, display_name, suffix="test_*.py", failfast=self.options.failfast,
|
||||
)
|
||||
|
||||
def start_daemons_only(self):
|
||||
if not salt.utils.platform.is_windows():
|
||||
self.set_filehandle_limits("integration")
|
||||
try:
|
||||
print_header(
|
||||
" * Setting up Salt daemons for interactive use",
|
||||
top=False,
|
||||
width=getattr(self.options, "output_columns", PNUM),
|
||||
)
|
||||
except TypeError:
|
||||
print_header(" * Setting up Salt daemons for interactive use", top=False)
|
||||
|
||||
try:
|
||||
with TestDaemon(self):
|
||||
print_header(" * Salt daemons started")
|
||||
master_conf = TestDaemon.config("master")
|
||||
minion_conf = TestDaemon.config("minion")
|
||||
proxy_conf = TestDaemon.config("proxy")
|
||||
sub_minion_conf = TestDaemon.config("sub_minion")
|
||||
syndic_conf = TestDaemon.config("syndic")
|
||||
syndic_master_conf = TestDaemon.config("syndic_master")
|
||||
|
||||
print_header(" * Syndic master configuration values (MoM)", top=False)
|
||||
print("interface: {}".format(syndic_master_conf["interface"]))
|
||||
print("publish port: {}".format(syndic_master_conf["publish_port"]))
|
||||
print("return port: {}".format(syndic_master_conf["ret_port"]))
|
||||
print("\n")
|
||||
|
||||
print_header(" * Syndic configuration values", top=True)
|
||||
print("interface: {}".format(syndic_conf["interface"]))
|
||||
print("syndic master: {}".format(syndic_conf["syndic_master"]))
|
||||
print(
|
||||
"syndic master port: {}".format(syndic_conf["syndic_master_port"])
|
||||
)
|
||||
print("\n")
|
||||
|
||||
print_header(" * Master configuration values", top=True)
|
||||
print("interface: {}".format(master_conf["interface"]))
|
||||
print("publish port: {}".format(master_conf["publish_port"]))
|
||||
print("return port: {}".format(master_conf["ret_port"]))
|
||||
print("\n")
|
||||
|
||||
print_header(" * Minion configuration values", top=True)
|
||||
print("interface: {}".format(minion_conf["interface"]))
|
||||
print("master: {}".format(minion_conf["master"]))
|
||||
print("master port: {}".format(minion_conf["master_port"]))
|
||||
if minion_conf["ipc_mode"] == "tcp":
|
||||
print("tcp pub port: {}".format(minion_conf["tcp_pub_port"]))
|
||||
print("tcp pull port: {}".format(minion_conf["tcp_pull_port"]))
|
||||
print("\n")
|
||||
|
||||
print_header(" * Sub Minion configuration values", top=True)
|
||||
print("interface: {}".format(sub_minion_conf["interface"]))
|
||||
print("master: {}".format(sub_minion_conf["master"]))
|
||||
print("master port: {}".format(sub_minion_conf["master_port"]))
|
||||
if sub_minion_conf["ipc_mode"] == "tcp":
|
||||
print("tcp pub port: {}".format(sub_minion_conf["tcp_pub_port"]))
|
||||
print("tcp pull port: {}".format(sub_minion_conf["tcp_pull_port"]))
|
||||
print("\n")
|
||||
|
||||
print_header(" * Proxy Minion configuration values", top=True)
|
||||
print("interface: {}".format(proxy_conf["interface"]))
|
||||
print("master: {}".format(proxy_conf["master"]))
|
||||
print("master port: {}".format(proxy_conf["master_port"]))
|
||||
if minion_conf["ipc_mode"] == "tcp":
|
||||
print("tcp pub port: {}".format(proxy_conf["tcp_pub_port"]))
|
||||
print("tcp pull port: {}".format(proxy_conf["tcp_pull_port"]))
|
||||
print("\n")
|
||||
|
||||
print_header(
|
||||
" Your client configuration is at {}".format(
|
||||
TestDaemon.config_location()
|
||||
)
|
||||
)
|
||||
print(
|
||||
"To access the minion: salt -c {} minion test.ping".format(
|
||||
TestDaemon.config_location()
|
||||
)
|
||||
)
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except TestDaemonStartFailed:
|
||||
self.exit(status=2)
|
||||
|
||||
def set_filehandle_limits(self, limits="integration"):
|
||||
"""
|
||||
Set soft and hard limits on open file handles at required thresholds
|
||||
for integration tests or unit tests
|
||||
"""
|
||||
# Get current limits
|
||||
if salt.utils.platform.is_windows():
|
||||
import win32file
|
||||
|
||||
prev_hard = win32file._getmaxstdio()
|
||||
prev_soft = 512
|
||||
else:
|
||||
prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE)
|
||||
|
||||
# Get required limits
|
||||
min_soft = MAX_OPEN_FILES[limits]["soft_limit"]
|
||||
min_hard = MAX_OPEN_FILES[limits]["hard_limit"]
|
||||
|
||||
# Check minimum required limits
|
||||
set_limits = False
|
||||
if prev_soft < min_soft:
|
||||
soft = min_soft
|
||||
set_limits = True
|
||||
else:
|
||||
soft = prev_soft
|
||||
|
||||
if prev_hard < min_hard:
|
||||
hard = min_hard
|
||||
set_limits = True
|
||||
else:
|
||||
hard = prev_hard
|
||||
|
||||
# Increase limits
|
||||
if set_limits:
|
||||
print(
|
||||
" * Max open files settings is too low (soft: {}, hard: {}) "
|
||||
"for running the tests".format(prev_soft, prev_hard)
|
||||
)
|
||||
print(
|
||||
" * Trying to raise the limits to soft: "
|
||||
"{}, hard: {}".format(soft, hard)
|
||||
)
|
||||
try:
|
||||
if salt.utils.platform.is_windows():
|
||||
hard = 2048 if hard > 2048 else hard
|
||||
win32file._setmaxstdio(hard)
|
||||
else:
|
||||
resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
print(
|
||||
"ERROR: Failed to raise the max open files settings -> "
|
||||
"{}".format(err)
|
||||
)
|
||||
print("Please issue the following command on your console:")
|
||||
print(" ulimit -n {}".format(soft))
|
||||
self.exit()
|
||||
finally:
|
||||
print("~" * getattr(self.options, "output_columns", PNUM))
|
||||
|
||||
def run_integration_tests(self):
|
||||
"""
|
||||
Execute the integration tests suite
|
||||
"""
|
||||
named_tests = []
|
||||
named_unit_test = []
|
||||
|
||||
if self.options.name:
|
||||
for test in self.options.name:
|
||||
if test.startswith(
|
||||
(
|
||||
"tests.unit.",
|
||||
"unit.",
|
||||
"test.kitchen.",
|
||||
"kitchen.",
|
||||
"test.multimaster.",
|
||||
"multimaster.",
|
||||
)
|
||||
):
|
||||
named_unit_test.append(test)
|
||||
continue
|
||||
named_tests.append(test)
|
||||
|
||||
if (
|
||||
(
|
||||
self.options.unit
|
||||
or self.options.kitchen
|
||||
or self.options.multimaster
|
||||
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")
|
||||
|
||||
try:
|
||||
print_header(
|
||||
" * Setting up Salt daemons to execute tests",
|
||||
top=False,
|
||||
width=getattr(self.options, "output_columns", PNUM),
|
||||
)
|
||||
except TypeError:
|
||||
print_header(" * Setting up Salt daemons to execute tests", top=False)
|
||||
|
||||
status = []
|
||||
# Return an empty status if no tests have been enabled
|
||||
if (
|
||||
not self._check_enabled_suites(
|
||||
include_cloud_provider=True, include_proxy=True
|
||||
)
|
||||
and not self.options.name
|
||||
):
|
||||
return status
|
||||
|
||||
try:
|
||||
with TestDaemon(self):
|
||||
if self.options.name:
|
||||
for name in self.options.name:
|
||||
name = name.strip()
|
||||
if not name:
|
||||
continue
|
||||
if os.path.isfile(name):
|
||||
if not name.endswith(".py"):
|
||||
continue
|
||||
if name.startswith(
|
||||
(
|
||||
os.path.join("tests", "unit"),
|
||||
os.path.join("tests", "multimaster"),
|
||||
)
|
||||
):
|
||||
continue
|
||||
results = self.run_suite(
|
||||
os.path.dirname(name),
|
||||
name,
|
||||
suffix=os.path.basename(name),
|
||||
failfast=self.options.failfast,
|
||||
load_from_name=False,
|
||||
)
|
||||
status.append(results)
|
||||
continue
|
||||
if name.startswith(
|
||||
(
|
||||
"tests.unit.",
|
||||
"unit.",
|
||||
"tests.multimaster.",
|
||||
"multimaster.",
|
||||
)
|
||||
):
|
||||
continue
|
||||
results = self.run_suite(
|
||||
"",
|
||||
name,
|
||||
suffix="test_*.py",
|
||||
load_from_name=True,
|
||||
failfast=self.options.failfast,
|
||||
)
|
||||
status.append(results)
|
||||
return status
|
||||
for suite in TEST_SUITES:
|
||||
if (
|
||||
suite != "unit"
|
||||
and suite != "multimaster"
|
||||
and getattr(self.options, suite)
|
||||
):
|
||||
status.append(self.run_integration_suite(**TEST_SUITES[suite]))
|
||||
return status
|
||||
except TestDaemonStartFailed:
|
||||
self.exit(status=2)
|
||||
|
||||
def run_unit_tests(self):
|
||||
"""
|
||||
Execute the unit tests
|
||||
"""
|
||||
named_unit_test = []
|
||||
if self.options.name:
|
||||
for test in self.options.name:
|
||||
if not test.startswith(("tests.unit.", "unit.")):
|
||||
continue
|
||||
named_unit_test.append(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 (or derived via --from-filenames) is a
|
||||
# unit test.
|
||||
return [True]
|
||||
|
||||
status = []
|
||||
if self.options.unit:
|
||||
# MacOS needs more open filehandles for running unit test suite
|
||||
self.set_filehandle_limits("unit")
|
||||
|
||||
results = self.run_suite(
|
||||
os.path.join(TEST_DIR, "unit"),
|
||||
"Unit",
|
||||
suffix="test_*.py",
|
||||
failfast=self.options.failfast,
|
||||
)
|
||||
status.append(results)
|
||||
# We executed ALL unittests, we can skip running unittests by name
|
||||
# below
|
||||
return status
|
||||
|
||||
for name in named_unit_test:
|
||||
results = self.run_suite(
|
||||
os.path.join(TEST_DIR, "unit"),
|
||||
name,
|
||||
suffix="test_*.py",
|
||||
load_from_name=True,
|
||||
failfast=self.options.failfast,
|
||||
)
|
||||
status.append(results)
|
||||
return status
|
||||
|
||||
def run_kitchen_tests(self):
|
||||
"""
|
||||
Execute the kitchen tests
|
||||
"""
|
||||
named_kitchen_test = []
|
||||
if self.options.name:
|
||||
for test in self.options.name:
|
||||
if not test.startswith(("tests.kitchen.", "kitchen.")):
|
||||
continue
|
||||
named_kitchen_test.append(test)
|
||||
|
||||
if not self.options.kitchen and not named_kitchen_test:
|
||||
# We are not explicitly running the unit tests and none of the
|
||||
# names passed to --name is a unit test.
|
||||
return [True]
|
||||
|
||||
status = []
|
||||
if self.options.kitchen:
|
||||
results = self.run_suite(
|
||||
os.path.join(TEST_DIR, "kitchen"), "Kitchen", suffix="test_*.py"
|
||||
)
|
||||
status.append(results)
|
||||
# We executed ALL unittests, we can skip running unittests by name
|
||||
# below
|
||||
return status
|
||||
|
||||
for name in named_kitchen_test:
|
||||
results = self.run_suite(
|
||||
os.path.join(TEST_DIR, "kitchen"),
|
||||
name,
|
||||
suffix="test_*.py",
|
||||
load_from_name=True,
|
||||
)
|
||||
status.append(results)
|
||||
return status
|
||||
|
||||
|
||||
def main(**kwargs):
|
||||
"""
|
||||
Parse command line options for running specific tests
|
||||
"""
|
||||
try:
|
||||
parser = SaltTestsuiteParser(
|
||||
TEST_DIR,
|
||||
xml_output_dir=XML_OUTPUT_DIR,
|
||||
tests_logfile=os.path.join(SYS_TMP_DIR, "salt-runtests.log"),
|
||||
)
|
||||
parser.parse_args()
|
||||
|
||||
# Override parser options (helpful when importing runtests.py and
|
||||
# running from within a REPL). Using kwargs.items() to avoid importing
|
||||
# six, as this feature will rarely be used.
|
||||
for key, val in kwargs.items():
|
||||
setattr(parser.options, key, val)
|
||||
|
||||
overall_status = []
|
||||
if parser.options.interactive:
|
||||
if parser.options.multimaster:
|
||||
print(
|
||||
"Multimaster tests now run under PyTest",
|
||||
file=sys.stderr,
|
||||
flush=True,
|
||||
)
|
||||
else:
|
||||
parser.start_daemons_only()
|
||||
status = parser.run_integration_tests()
|
||||
overall_status.extend(status)
|
||||
status = parser.run_unit_tests()
|
||||
overall_status.extend(status)
|
||||
status = parser.run_kitchen_tests()
|
||||
overall_status.extend(status)
|
||||
false_count = overall_status.count(False)
|
||||
|
||||
if false_count > 0:
|
||||
parser.finalize(1)
|
||||
parser.finalize(0)
|
||||
except KeyboardInterrupt:
|
||||
print("\nCaught keyboard interrupt. Exiting.\n")
|
||||
exit(0)
|
||||
def main():
|
||||
print(
|
||||
"\nruntests.py support has been removed from Salt. Please try `nox -e 'pytest-3(coverage=True)'` "
|
||||
"or `nox -e 'pytest-3(coverage=True)' -- --help` to know more about the supported CLI flags.\n"
|
||||
"For more information, please check https://docs.saltproject.io/en/latest/topics/development/tests/index.html#running-the-tests",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,245 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
tests.support.parser.cover
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Code coverage aware testing parser
|
||||
|
||||
:codeauthor: Pedro Algarvio (pedro@algarvio.me)
|
||||
:copyright: Copyright 2013 by the SaltStack Team, see AUTHORS for more details.
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
"""
|
||||
# pylint: disable=repr-flag-used-in-string
|
||||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.json
|
||||
|
||||
# Import salt testing libs
|
||||
from tests.support.parser import SaltTestingParser
|
||||
|
||||
# Import coverage libs
|
||||
try:
|
||||
import coverage
|
||||
|
||||
COVERAGE_AVAILABLE = True
|
||||
except ImportError:
|
||||
COVERAGE_AVAILABLE = False
|
||||
|
||||
try:
|
||||
import multiprocessing.util
|
||||
|
||||
# Force forked multiprocessing processes to be measured as well
|
||||
|
||||
def multiprocessing_stop(coverage_object):
|
||||
"""
|
||||
Save the multiprocessing process coverage object
|
||||
"""
|
||||
coverage_object.stop()
|
||||
coverage_object.save()
|
||||
|
||||
def multiprocessing_start(obj):
|
||||
coverage_options = salt.utils.json.loads(
|
||||
os.environ.get("COVERAGE_OPTIONS", "{}")
|
||||
)
|
||||
if not coverage_options:
|
||||
return
|
||||
|
||||
if coverage_options.get("data_suffix", False) is False:
|
||||
return
|
||||
|
||||
coverage_object = coverage.coverage(**coverage_options)
|
||||
coverage_object.start()
|
||||
|
||||
multiprocessing.util.Finalize(
|
||||
None, multiprocessing_stop, args=(coverage_object,), exitpriority=1000
|
||||
)
|
||||
|
||||
if COVERAGE_AVAILABLE:
|
||||
multiprocessing.util.register_after_fork(
|
||||
multiprocessing_start, multiprocessing_start
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if COVERAGE_AVAILABLE:
|
||||
# Cover any processes if the environ variables are present
|
||||
coverage.process_startup()
|
||||
|
||||
|
||||
class SaltCoverageTestingParser(SaltTestingParser):
|
||||
"""
|
||||
Code coverage aware testing option parser
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if (
|
||||
kwargs.pop("html_output_from_env", None) is not None
|
||||
or kwargs.pop("html_output_dir", None) is not None
|
||||
):
|
||||
warnings.warn(
|
||||
"The unit tests HTML support was removed from {0}. Please "
|
||||
"stop passing 'html_output_dir' or 'html_output_from_env' "
|
||||
"as arguments to {0}".format(self.__class__.__name__),
|
||||
category=DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
SaltTestingParser.__init__(self, *args, **kwargs)
|
||||
self.code_coverage = None
|
||||
|
||||
# Add the coverage related options
|
||||
self.output_options_group.add_option(
|
||||
"--coverage",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run tests and report code coverage",
|
||||
)
|
||||
self.output_options_group.add_option(
|
||||
"--no-processes-coverage",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Do not track subprocess and/or multiprocessing processes",
|
||||
)
|
||||
self.output_options_group.add_option(
|
||||
"--coverage-xml",
|
||||
default=None,
|
||||
help="If provided, the path to where a XML report of the code "
|
||||
"coverage will be written to",
|
||||
)
|
||||
self.output_options_group.add_option(
|
||||
"--coverage-html",
|
||||
default=None,
|
||||
help=(
|
||||
"The directory where the generated HTML coverage report "
|
||||
"will be saved to. The directory, if existing, will be "
|
||||
"deleted before the report is generated."
|
||||
),
|
||||
)
|
||||
|
||||
def _validate_options(self):
|
||||
if (
|
||||
self.options.coverage_xml or self.options.coverage_html
|
||||
) and not self.options.coverage:
|
||||
self.options.coverage = True
|
||||
|
||||
if self.options.coverage is True and COVERAGE_AVAILABLE is False:
|
||||
self.error(
|
||||
"Cannot run tests with coverage report. "
|
||||
"Please install coverage>=3.5.3"
|
||||
)
|
||||
|
||||
if self.options.coverage is True:
|
||||
coverage_version = tuple(
|
||||
[
|
||||
int(part)
|
||||
for part in re.search(r"([0-9.]+)", coverage.__version__)
|
||||
.group(0)
|
||||
.split(".")
|
||||
]
|
||||
)
|
||||
if coverage_version < (3, 5, 3):
|
||||
# Should we just print the error instead of exiting?
|
||||
self.error(
|
||||
"Versions lower than 3.5.3 of the coverage library are "
|
||||
"know to produce incorrect results. Please consider "
|
||||
"upgrading..."
|
||||
)
|
||||
SaltTestingParser._validate_options(self)
|
||||
|
||||
def pre_execution_cleanup(self):
|
||||
if self.options.coverage_html is not None:
|
||||
if os.path.isdir(self.options.coverage_html):
|
||||
shutil.rmtree(self.options.coverage_html)
|
||||
if self.options.coverage_xml is not None:
|
||||
if os.path.isfile(self.options.coverage_xml):
|
||||
os.unlink(self.options.coverage_xml)
|
||||
SaltTestingParser.pre_execution_cleanup(self)
|
||||
|
||||
def start_coverage(self, **coverage_options):
|
||||
"""
|
||||
Start code coverage.
|
||||
|
||||
You can pass any coverage options as keyword arguments. For the
|
||||
available options please see:
|
||||
http://nedbatchelder.com/code/coverage/api.html
|
||||
"""
|
||||
if self.options.coverage is False:
|
||||
return
|
||||
|
||||
if coverage_options.pop("track_processes", None) is not None:
|
||||
raise RuntimeWarning(
|
||||
"Please stop passing 'track_processes' to "
|
||||
"'start_coverage()'. It's now the default and "
|
||||
"'--no-processes-coverage' was added to the parser to "
|
||||
"disable it."
|
||||
)
|
||||
print(" * Starting Coverage")
|
||||
|
||||
if self.options.no_processes_coverage is False:
|
||||
# Update environ so that any subprocess started on tests are also
|
||||
# included in the report
|
||||
coverage_options["data_suffix"] = True
|
||||
os.environ["COVERAGE_PROCESS_START"] = ""
|
||||
os.environ["COVERAGE_OPTIONS"] = salt.utils.json.dumps(coverage_options)
|
||||
|
||||
# Setup coverage
|
||||
self.code_coverage = coverage.coverage(**coverage_options)
|
||||
self.code_coverage.start()
|
||||
|
||||
def stop_coverage(self, save_coverage=True):
|
||||
"""
|
||||
Stop code coverage.
|
||||
"""
|
||||
if self.options.coverage is False:
|
||||
return
|
||||
|
||||
# Clean up environment
|
||||
os.environ.pop("COVERAGE_OPTIONS", None)
|
||||
os.environ.pop("COVERAGE_PROCESS_START", None)
|
||||
|
||||
print(" * Stopping coverage")
|
||||
self.code_coverage.stop()
|
||||
if save_coverage:
|
||||
print(" * Saving coverage info")
|
||||
self.code_coverage.save()
|
||||
|
||||
if self.options.no_processes_coverage is False:
|
||||
# Combine any multiprocessing coverage data files
|
||||
sys.stdout.write(" * Combining multiple coverage info files ... ")
|
||||
sys.stdout.flush()
|
||||
self.code_coverage.combine()
|
||||
print("Done.")
|
||||
|
||||
if self.options.coverage_xml is not None:
|
||||
sys.stdout.write(
|
||||
" * Generating Coverage XML Report At {0!r} ... ".format(
|
||||
self.options.coverage_xml
|
||||
)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
self.code_coverage.xml_report(outfile=self.options.coverage_xml)
|
||||
print("Done.")
|
||||
|
||||
if self.options.coverage_html is not None:
|
||||
sys.stdout.write(
|
||||
" * Generating Coverage HTML Report Under {0!r} ... ".format(
|
||||
self.options.coverage_html
|
||||
)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
self.code_coverage.html_report(directory=self.options.coverage_html)
|
||||
print("Done.")
|
||||
|
||||
def finalize(self, exit_code=0):
|
||||
if self.options.coverage is True:
|
||||
self.stop_coverage(save_coverage=True)
|
||||
SaltTestingParser.finalize(self, exit_code)
|
|
@ -1,219 +0,0 @@
|
|||
"""
|
||||
:copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
|
||||
|
||||
tests.support.processes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Process handling utilities
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from saltfactories.utils.processes import ( # pylint: disable=unused-import
|
||||
collect_child_processes,
|
||||
terminate_process,
|
||||
terminate_process_list,
|
||||
)
|
||||
from tests.support.cli_scripts import ScriptPathMixin, get_script_path
|
||||
from tests.support.runtests import RUNTIME_VARS
|
||||
|
||||
try:
|
||||
from pytestsalt.fixtures.daemons import Salt as PytestSalt
|
||||
from pytestsalt.fixtures.daemons import SaltCall as PytestSaltCall
|
||||
from pytestsalt.fixtures.daemons import SaltKey as PytestSaltKey
|
||||
from pytestsalt.fixtures.daemons import SaltMaster as PytestSaltMaster
|
||||
from pytestsalt.fixtures.daemons import SaltMinion as PytestSaltMinion
|
||||
from pytestsalt.fixtures.daemons import SaltProxy as PytestSaltProxy
|
||||
from pytestsalt.fixtures.daemons import SaltRun as PytestSaltRun
|
||||
from pytestsalt.fixtures.daemons import SaltSyndic as PytestSaltSyndic
|
||||
except ImportError:
|
||||
# If this happens, we are running under pytest which uninstalls pytest-salt due to impatabilites
|
||||
# These imports won't actually work but these classes are only used when running under runtests,
|
||||
# so, we're just making sure we also don't hit NameError's
|
||||
from tests.support.saltfactories_compat import SaltCallCLI as PytestSaltCall
|
||||
from tests.support.saltfactories_compat import SaltCLI as PytestSalt
|
||||
from tests.support.saltfactories_compat import SaltKeyCLI as PytestSaltKey
|
||||
from tests.support.saltfactories_compat import SaltMaster as PytestSaltMaster
|
||||
from tests.support.saltfactories_compat import SaltMinion as PytestSaltMinion
|
||||
from tests.support.saltfactories_compat import SaltProxyMinion as PytestSaltProxy
|
||||
from tests.support.saltfactories_compat import SaltRunCLI as PytestSaltRun
|
||||
from tests.support.saltfactories_compat import SaltSyndic as PytestSaltSyndic
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GetSaltRunFixtureMixin(ScriptPathMixin):
|
||||
"""
|
||||
Override this classes `get_salt_run_fixture` because we're still not running under pytest
|
||||
"""
|
||||
|
||||
def get_salt_run_fixture(self):
|
||||
pass
|
||||
|
||||
|
||||
class Salt(ScriptPathMixin, PytestSalt):
|
||||
"""
|
||||
Class which runs salt-call commands
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(None, *args, **kwargs)
|
||||
|
||||
|
||||
class SaltCall(ScriptPathMixin, PytestSaltCall):
|
||||
"""
|
||||
Class which runs salt-call commands
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(None, *args, **kwargs)
|
||||
|
||||
|
||||
class SaltKey(ScriptPathMixin, PytestSaltKey):
|
||||
"""
|
||||
Class which runs salt-key commands
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(None, *args, **kwargs)
|
||||
|
||||
|
||||
class SaltRun(ScriptPathMixin, PytestSaltRun):
|
||||
"""
|
||||
Class which runs salt-run commands
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(None, *args, **kwargs)
|
||||
|
||||
|
||||
class SaltProxy(GetSaltRunFixtureMixin, PytestSaltProxy):
|
||||
"""
|
||||
Class which runs the salt-proxy daemon
|
||||
"""
|
||||
|
||||
|
||||
class SaltMinion(GetSaltRunFixtureMixin, PytestSaltMinion):
|
||||
"""
|
||||
Class which runs the salt-minion daemon
|
||||
"""
|
||||
|
||||
|
||||
class SaltMaster(GetSaltRunFixtureMixin, PytestSaltMaster):
|
||||
"""
|
||||
Class which runs the salt-master daemon
|
||||
"""
|
||||
|
||||
|
||||
class SaltSyndic(GetSaltRunFixtureMixin, PytestSaltSyndic):
|
||||
"""
|
||||
Class which runs the salt-syndic daemon
|
||||
"""
|
||||
|
||||
|
||||
def start_daemon(
|
||||
daemon_name=None,
|
||||
daemon_id=None,
|
||||
daemon_log_prefix=None,
|
||||
daemon_cli_script_name=None,
|
||||
daemon_config=None,
|
||||
daemon_config_dir=None,
|
||||
daemon_class=None,
|
||||
bin_dir_path=None,
|
||||
fail_hard=False,
|
||||
start_timeout=10,
|
||||
slow_stop=False,
|
||||
environ=None,
|
||||
cwd=None,
|
||||
event_listener_config_dir=None,
|
||||
):
|
||||
"""
|
||||
Returns a running salt daemon
|
||||
"""
|
||||
# Old config name
|
||||
daemon_config["pytest_port"] = daemon_config["runtests_conn_check_port"]
|
||||
# New config name
|
||||
daemon_config["pytest_engine_port"] = daemon_config["runtests_conn_check_port"]
|
||||
request = None
|
||||
if fail_hard:
|
||||
fail_method = RuntimeError
|
||||
else:
|
||||
fail_method = RuntimeWarning
|
||||
log.info("[%s] Starting pytest %s(%s)", daemon_name, daemon_log_prefix, daemon_id)
|
||||
attempts = 0
|
||||
process = None
|
||||
get_script_path(RUNTIME_VARS.TMP_SCRIPT_DIR, daemon_cli_script_name)
|
||||
while attempts <= 3: # pylint: disable=too-many-nested-blocks
|
||||
attempts += 1
|
||||
try:
|
||||
process = daemon_class(
|
||||
request=request,
|
||||
config=daemon_config,
|
||||
config_dir=daemon_config_dir,
|
||||
bin_dir_path=bin_dir_path,
|
||||
log_prefix=daemon_log_prefix,
|
||||
cli_script_name=daemon_cli_script_name,
|
||||
slow_stop=slow_stop,
|
||||
environ=environ,
|
||||
cwd=cwd,
|
||||
event_listener_config_dir=event_listener_config_dir,
|
||||
)
|
||||
except TypeError:
|
||||
process = daemon_class(
|
||||
request=request,
|
||||
config=daemon_config,
|
||||
config_dir=daemon_config_dir,
|
||||
bin_dir_path=bin_dir_path,
|
||||
log_prefix=daemon_log_prefix,
|
||||
cli_script_name=daemon_cli_script_name,
|
||||
slow_stop=slow_stop,
|
||||
environ=environ,
|
||||
cwd=cwd,
|
||||
)
|
||||
process.start()
|
||||
if process.is_alive():
|
||||
try:
|
||||
connectable = process.wait_until_running(timeout=start_timeout)
|
||||
if connectable is False:
|
||||
connectable = process.wait_until_running(timeout=start_timeout / 2)
|
||||
if connectable is False:
|
||||
process.terminate()
|
||||
if attempts >= 3:
|
||||
fail_method(
|
||||
"The pytest {}({}) has failed to confirm running status "
|
||||
"after {} attempts".format(
|
||||
daemon_name, daemon_id, attempts
|
||||
)
|
||||
)
|
||||
continue
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
log.exception("[%s] %s", daemon_log_prefix, exc, exc_info=True)
|
||||
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
|
||||
if attempts >= 3:
|
||||
raise fail_method(str(exc))
|
||||
continue
|
||||
log.info(
|
||||
"[%s] The pytest %s(%s) is running and accepting commands "
|
||||
"after %d attempts",
|
||||
daemon_log_prefix,
|
||||
daemon_name,
|
||||
daemon_id,
|
||||
attempts,
|
||||
)
|
||||
|
||||
break
|
||||
else:
|
||||
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
|
||||
continue
|
||||
else:
|
||||
if process is not None:
|
||||
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
|
||||
raise fail_method(
|
||||
"The pytest {}({}) has failed to start after {} attempts".format(
|
||||
daemon_name, daemon_id, attempts - 1
|
||||
)
|
||||
)
|
||||
return process
|
Loading…
Add table
Reference in a new issue