salt/tests/runtests.py
2020-04-03 13:05:41 -05:00

1116 lines
40 KiB
Python
Executable file

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Discover all instances of unittest.TestCase in this directory.
"""
# pylint: disable=file-perms
# Import python libs
from __future__ import absolute_import, print_function
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
# Import salt libs
from salt.ext import six # isort:skip
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)
six.reraise(*sys.exc_info())
from tests.integration import TestDaemon, TestDaemonStartFailed # isort:skip
from tests.multimaster import MultimasterTestDaemon # isort:skip
import salt.utils.platform # isort:skip
if not salt.utils.platform.is_windows():
import resource
# Import Salt Testing libs
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: {0}".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"},
"setup": {"display_name": "Setup", "path": "integration/setup"},
"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"},
"reactor": {"display_name": "Reactor", "path": "integration/reactor"},
"proxy": {"display_name": "Proxy", "path": "integration/proxy"},
"external_api": {"display_name": "ExternalAPIs", "path": "integration/externalapi"},
"daemons": {"display_name": "Daemon", "path": "integration/daemons"},
"scheduler": {"display_name": "Scheduler", "path": "integration/scheduler"},
"sdb": {"display_name": "Sdb", "path": "integration/sdb"},
"logging": {"display_name": "Logging", "path": "integration/logging"},
}
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
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 -= set(["unit"])
if not include_cloud_provider:
suites -= set(["cloud_provider"])
if not include_proxy:
suites -= set(["proxy"])
if not include_kitchen:
suites -= set(["kitchen"])
if not include_multimaster:
suites -= set(["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: {0}".format(" ".join(sys.version.split())))
# Transplant configuration
TestDaemon.transplant_configs(transport=self.options.transport)
MultimasterTestDaemon.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: {0}".format(syndic_master_conf["interface"]))
print("publish port: {0}".format(syndic_master_conf["publish_port"]))
print("return port: {0}".format(syndic_master_conf["ret_port"]))
print("\n")
print_header(" * Syndic configuration values", top=True)
print("interface: {0}".format(syndic_conf["interface"]))
print("syndic master: {0}".format(syndic_conf["syndic_master"]))
print(
"syndic master port: {0}".format(syndic_conf["syndic_master_port"])
)
print("\n")
print_header(" * Master configuration values", top=True)
print("interface: {0}".format(master_conf["interface"]))
print("publish port: {0}".format(master_conf["publish_port"]))
print("return port: {0}".format(master_conf["ret_port"]))
print("\n")
print_header(" * Minion configuration values", top=True)
print("interface: {0}".format(minion_conf["interface"]))
print("master: {0}".format(minion_conf["master"]))
print("master port: {0}".format(minion_conf["master_port"]))
if minion_conf["ipc_mode"] == "tcp":
print("tcp pub port: {0}".format(minion_conf["tcp_pub_port"]))
print("tcp pull port: {0}".format(minion_conf["tcp_pull_port"]))
print("\n")
print_header(" * Sub Minion configuration values", top=True)
print("interface: {0}".format(sub_minion_conf["interface"]))
print("master: {0}".format(sub_minion_conf["master"]))
print("master port: {0}".format(sub_minion_conf["master_port"]))
if sub_minion_conf["ipc_mode"] == "tcp":
print("tcp pub port: {0}".format(sub_minion_conf["tcp_pub_port"]))
print("tcp pull port: {0}".format(sub_minion_conf["tcp_pull_port"]))
print("\n")
print_header(" * Proxy Minion configuration values", top=True)
print("interface: {0}".format(proxy_conf["interface"]))
print("master: {0}".format(proxy_conf["master"]))
print("master port: {0}".format(proxy_conf["master_port"]))
if minion_conf["ipc_mode"] == "tcp":
print("tcp pub port: {0}".format(proxy_conf["tcp_pub_port"]))
print("tcp pull port: {0}".format(proxy_conf["tcp_pull_port"]))
print("\n")
print_header(
" Your client configuration is at {0}".format(
TestDaemon.config_location()
)
)
print(
"To access the minion: salt -c {0} minion test.ping".format(
TestDaemon.config_location()
)
)
while True:
time.sleep(1)
except TestDaemonStartFailed:
self.exit(status=2)
def start_multimaster_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 MultimasterTestDaemon(self):
print_header(" * Salt daemons started")
master_conf = MultimasterTestDaemon.config("mm_master")
sub_master_conf = MultimasterTestDaemon.config("mm_sub_master")
minion_conf = MultimasterTestDaemon.config("mm_minion")
sub_minion_conf = MultimasterTestDaemon.config("mm_sub_minion")
print_header(" * Master configuration values", top=True)
print("interface: {0}".format(master_conf["interface"]))
print("publish port: {0}".format(master_conf["publish_port"]))
print("return port: {0}".format(master_conf["ret_port"]))
print("\n")
print_header(" * Second master configuration values", top=True)
print("interface: {0}".format(sub_master_conf["interface"]))
print("publish port: {0}".format(sub_master_conf["publish_port"]))
print("return port: {0}".format(sub_master_conf["ret_port"]))
print("\n")
print_header(" * Minion configuration values", top=True)
print("interface: {0}".format(minion_conf["interface"]))
print("masters: {0}".format(", ".join(minion_conf["master"])))
if minion_conf["ipc_mode"] == "tcp":
print("tcp pub port: {0}".format(minion_conf["tcp_pub_port"]))
print("tcp pull port: {0}".format(minion_conf["tcp_pull_port"]))
print("\n")
print_header(" * Sub Minion configuration values", top=True)
print("interface: {0}".format(sub_minion_conf["interface"]))
print("masters: {0}".format(", ".join(sub_minion_conf["master"])))
if sub_minion_conf["ipc_mode"] == "tcp":
print("tcp pub port: {0}".format(sub_minion_conf["tcp_pub_port"]))
print("tcp pull port: {0}".format(sub_minion_conf["tcp_pull_port"]))
print("\n")
print_header(
" Your client configurations are at {0}".format(
", ".join(MultimasterTestDaemon.config_location())
)
)
print("To access minions from different masters use:")
for location in MultimasterTestDaemon.config_location():
print(" salt -c {0} minion test.ping".format(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: {0}, hard: {1}) "
"for running the tests".format(prev_soft, prev_hard)
)
print(
" * Trying to raise the limits to soft: "
"{0}, hard: {1}".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 -> "
"{0}".format(err)
)
print("Please issue the following command on your console:")
print(" ulimit -n {0}".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_multimaster_tests(self):
"""
Execute the multimaster tests suite
"""
named_tests = []
named_unit_test = []
if self.options.name:
for test in self.options.name:
if test.startswith(("tests.multimaster.", "multimaster.")):
named_tests.append(test)
# TODO: check 'from_filenames'
if not self.options.multimaster and not named_tests:
# We're not running any multimaster test suites.
return [True]
if not salt.utils.platform.is_windows():
self.set_filehandle_limits("integration")
try:
print_header(
" * Setting up multimaster Salt daemons to execute tests",
top=False,
width=getattr(self.options, "output_columns", PNUM),
)
except TypeError:
print_header(
" * Setting up multimaster Salt daemons to execute tests", top=False
)
status = []
try:
with MultimasterTestDaemon(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 not name.startswith(
os.path.join("tests", "multimaster")
):
continue
results = self.run_suite(
os.path.dirname(name),
name,
suffix=os.path.basename(name),
load_from_name=False,
)
status.append(results)
continue
if not name.startswith(("tests.multimaster.", "multimaster.")):
continue
results = self.run_suite(
"", name, suffix="test_*.py", load_from_name=True
)
status.append(results)
return status
status.append(self.run_integration_suite(**TEST_SUITES["multimaster"]))
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:
parser.start_multimaster_daemons_only()
else:
parser.start_daemons_only()
status = parser.run_integration_tests()
overall_status.extend(status)
status = parser.run_multimaster_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)
if __name__ == "__main__":
main()