runtests.py 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Discover all instances of unittest.TestCase in this directory.
  5. """
  6. # pylint: disable=file-perms
  7. # Import python libs
  8. from __future__ import absolute_import, print_function
  9. import collections
  10. import os
  11. import sys
  12. import time
  13. import warnings
  14. TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__)))
  15. if os.name == "nt":
  16. TESTS_DIR = TESTS_DIR.replace("\\", "\\\\")
  17. CODE_DIR = os.path.dirname(TESTS_DIR)
  18. # Let's inject CODE_DIR so salt is importable if not there already
  19. if "" in sys.path:
  20. sys.path.remove("")
  21. if TESTS_DIR in sys.path:
  22. sys.path.remove(TESTS_DIR)
  23. if CODE_DIR in sys.path and sys.path[0] != CODE_DIR:
  24. sys.path.remove(CODE_DIR)
  25. if CODE_DIR not in sys.path:
  26. sys.path.insert(0, CODE_DIR)
  27. if TESTS_DIR not in sys.path:
  28. sys.path.insert(1, TESTS_DIR)
  29. try:
  30. import tests
  31. if not tests.__file__.startswith(CODE_DIR):
  32. print("Found tests module not from salt in {}".format(tests.__file__))
  33. sys.modules.pop("tests")
  34. module_dir = os.path.dirname(tests.__file__)
  35. if module_dir in sys.path:
  36. sys.path.remove(module_dir)
  37. del tests
  38. except ImportError:
  39. pass
  40. # Import salt libs
  41. from salt.ext import six # isort:skip
  42. try:
  43. from tests.support.paths import TMP, SYS_TMP_DIR, INTEGRATION_TEST_DIR
  44. from tests.support.paths import CODE_DIR as SALT_ROOT
  45. except ImportError as exc:
  46. try:
  47. import tests
  48. print("Found tests module not from salt in {}".format(tests.__file__))
  49. except ImportError:
  50. print("Unable to import salt test module")
  51. print("PYTHONPATH:", os.environ.get("PYTHONPATH"))
  52. print("Current sys.path:")
  53. import pprint
  54. pprint.pprint(sys.path)
  55. six.reraise(*sys.exc_info())
  56. from tests.integration import TestDaemon, TestDaemonStartFailed # isort:skip
  57. from tests.multimaster import MultimasterTestDaemon # isort:skip
  58. import salt.utils.platform # isort:skip
  59. if not salt.utils.platform.is_windows():
  60. import resource
  61. # Import Salt Testing libs
  62. from tests.support.parser import PNUM, print_header # isort:skip
  63. from tests.support.parser.cover import SaltCoverageTestingParser # isort:skip
  64. XML_OUTPUT_DIR = os.environ.get(
  65. "SALT_XML_TEST_REPORTS_DIR", os.path.join(TMP, "xml-test-reports")
  66. )
  67. HTML_OUTPUT_DIR = os.environ.get(
  68. "SALT_HTML_TEST_REPORTS_DIR", os.path.join(TMP, "html-test-reports")
  69. )
  70. TEST_DIR = os.path.dirname(INTEGRATION_TEST_DIR)
  71. try:
  72. if SALT_ROOT:
  73. os.chdir(SALT_ROOT)
  74. except OSError as err:
  75. print("Failed to change directory to salt's source: {0}".format(err))
  76. # Soft and hard limits on max open filehandles
  77. MAX_OPEN_FILES = {
  78. "integration": {"soft_limit": 3072, "hard_limit": 4096},
  79. "unit": {"soft_limit": 1024, "hard_limit": 2048},
  80. }
  81. # Combine info from command line options and test suite directories. A test
  82. # suite is a python package of test modules relative to the tests directory.
  83. TEST_SUITES_UNORDERED = {
  84. "unit": {"display_name": "Unit", "path": "unit"},
  85. "kitchen": {"display_name": "Kitchen", "path": "kitchen"},
  86. "multimaster": {"display_name": "Multimaster", "path": "multimaster"},
  87. "module": {"display_name": "Module", "path": "integration/modules"},
  88. "state": {"display_name": "State", "path": "integration/states"},
  89. "cli": {"display_name": "CLI", "path": "integration/cli"},
  90. "client": {"display_name": "Client", "path": "integration/client"},
  91. "doc": {"display_name": "Documentation", "path": "integration/doc"},
  92. "ext_pillar": {"display_name": "External Pillar", "path": "integration/pillar"},
  93. "grains": {"display_name": "Grains", "path": "integration/grains"},
  94. "shell": {"display_name": "Shell", "path": "integration/shell"},
  95. "runners": {"display_name": "Runners", "path": "integration/runners"},
  96. "renderers": {"display_name": "Renderers", "path": "integration/renderers"},
  97. "returners": {"display_name": "Returners", "path": "integration/returners"},
  98. "setup": {"display_name": "Setup", "path": "integration/setup"},
  99. "ssh-int": {"display_name": "SSH Integration", "path": "integration/ssh"},
  100. "spm": {"display_name": "SPM", "path": "integration/spm"},
  101. "loader": {"display_name": "Loader", "path": "integration/loader"},
  102. "outputter": {"display_name": "Outputter", "path": "integration/output"},
  103. "fileserver": {"display_name": "Fileserver", "path": "integration/fileserver"},
  104. "wheel": {"display_name": "Wheel", "path": "integration/wheel"},
  105. "api": {"display_name": "NetAPI", "path": "integration/netapi"},
  106. "cloud_provider": {
  107. "display_name": "Cloud Provider",
  108. "path": "integration/cloud/clouds",
  109. },
  110. "minion": {"display_name": "Minion", "path": "integration/minion"},
  111. "reactor": {"display_name": "Reactor", "path": "integration/reactor"},
  112. "proxy": {"display_name": "Proxy", "path": "integration/proxy"},
  113. "external_api": {"display_name": "ExternalAPIs", "path": "integration/externalapi"},
  114. "daemons": {"display_name": "Daemon", "path": "integration/daemons"},
  115. "sdb": {"display_name": "Sdb", "path": "integration/sdb"},
  116. "logging": {"display_name": "Logging", "path": "integration/logging"},
  117. }
  118. TEST_SUITES = collections.OrderedDict(
  119. sorted(TEST_SUITES_UNORDERED.items(), key=lambda x: x[0])
  120. )
  121. class SaltTestsuiteParser(SaltCoverageTestingParser):
  122. support_docker_execution = True
  123. support_destructive_tests_selection = True
  124. support_expensive_tests_selection = True
  125. source_code_basedir = SALT_ROOT
  126. def _get_suites(
  127. self,
  128. include_unit=False,
  129. include_cloud_provider=False,
  130. include_proxy=False,
  131. include_kitchen=False,
  132. include_multimaster=False,
  133. ):
  134. """
  135. Return a set of all test suites except unit and cloud provider tests
  136. unless requested
  137. """
  138. suites = set(TEST_SUITES.keys())
  139. if not include_unit:
  140. suites -= set(["unit"])
  141. if not include_cloud_provider:
  142. suites -= set(["cloud_provider"])
  143. if not include_proxy:
  144. suites -= set(["proxy"])
  145. if not include_kitchen:
  146. suites -= set(["kitchen"])
  147. if not include_multimaster:
  148. suites -= set(["multimaster"])
  149. return suites
  150. def _check_enabled_suites(
  151. self,
  152. include_unit=False,
  153. include_cloud_provider=False,
  154. include_proxy=False,
  155. include_kitchen=False,
  156. include_multimaster=False,
  157. ):
  158. """
  159. Query whether test suites have been enabled
  160. """
  161. suites = self._get_suites(
  162. include_unit=include_unit,
  163. include_cloud_provider=include_cloud_provider,
  164. include_proxy=include_proxy,
  165. include_kitchen=include_kitchen,
  166. include_multimaster=include_multimaster,
  167. )
  168. return any([getattr(self.options, suite) for suite in suites])
  169. def _enable_suites(
  170. self,
  171. include_unit=False,
  172. include_cloud_provider=False,
  173. include_proxy=False,
  174. include_kitchen=False,
  175. include_multimaster=False,
  176. ):
  177. """
  178. Enable test suites for current test run
  179. """
  180. suites = self._get_suites(
  181. include_unit=include_unit,
  182. include_cloud_provider=include_cloud_provider,
  183. include_proxy=include_proxy,
  184. include_kitchen=include_kitchen,
  185. include_multimaster=include_multimaster,
  186. )
  187. for suite in suites:
  188. setattr(self.options, suite, True)
  189. def setup_additional_options(self):
  190. self.add_option(
  191. "--sysinfo",
  192. default=False,
  193. action="store_true",
  194. help="Print some system information.",
  195. )
  196. self.add_option(
  197. "--transport",
  198. default="zeromq",
  199. choices=("zeromq", "tcp"),
  200. help=(
  201. "Select which transport to run the integration tests with, "
  202. "zeromq or tcp. Default: %default"
  203. ),
  204. )
  205. self.add_option(
  206. "--interactive",
  207. default=False,
  208. action="store_true",
  209. help="Do not run any tests. Simply start the daemons.",
  210. )
  211. self.output_options_group.add_option(
  212. "--no-colors",
  213. "--no-colours",
  214. default=False,
  215. action="store_true",
  216. help="Disable colour printing.",
  217. )
  218. self.test_selection_group.add_option(
  219. "-m",
  220. "--module",
  221. "--module-tests",
  222. dest="module",
  223. default=False,
  224. action="store_true",
  225. help="Run tests for modules",
  226. )
  227. self.test_selection_group.add_option(
  228. "-S",
  229. "--state",
  230. "--state-tests",
  231. dest="state",
  232. default=False,
  233. action="store_true",
  234. help="Run tests for states",
  235. )
  236. self.test_selection_group.add_option(
  237. "-C",
  238. "--cli",
  239. "--cli-tests",
  240. dest="cli",
  241. default=False,
  242. action="store_true",
  243. help="Run tests for cli",
  244. )
  245. self.test_selection_group.add_option(
  246. "-c",
  247. "--client",
  248. "--client-tests",
  249. dest="client",
  250. default=False,
  251. action="store_true",
  252. help="Run tests for client",
  253. )
  254. self.test_selection_group.add_option(
  255. "-d",
  256. "--doc",
  257. "--doc-tests",
  258. dest="doc",
  259. default=False,
  260. action="store_true",
  261. help="Run tests for documentation",
  262. )
  263. self.test_selection_group.add_option(
  264. "-I",
  265. "--ext-pillar",
  266. "--ext-pillar-tests",
  267. dest="ext_pillar",
  268. default=False,
  269. action="store_true",
  270. help="Run ext_pillar tests",
  271. )
  272. self.test_selection_group.add_option(
  273. "-G",
  274. "--grains",
  275. "--grains-tests",
  276. dest="grains",
  277. default=False,
  278. action="store_true",
  279. help="Run tests for grains",
  280. )
  281. self.test_selection_group.add_option(
  282. "-s",
  283. "--shell",
  284. "--shell-tests",
  285. dest="shell",
  286. default=False,
  287. action="store_true",
  288. help="Run shell tests",
  289. )
  290. self.test_selection_group.add_option(
  291. "-r",
  292. "--runners",
  293. "--runner-tests",
  294. dest="runners",
  295. default=False,
  296. action="store_true",
  297. help="Run salt/runners/*.py tests",
  298. )
  299. self.test_selection_group.add_option(
  300. "-R",
  301. "--renderers",
  302. "--renderer-tests",
  303. dest="renderers",
  304. default=False,
  305. action="store_true",
  306. help="Run salt/renderers/*.py tests",
  307. )
  308. self.test_selection_group.add_option(
  309. "--reactor",
  310. dest="reactor",
  311. default=False,
  312. action="store_true",
  313. help="Run salt/reactor/*.py tests",
  314. )
  315. self.test_selection_group.add_option(
  316. "--minion",
  317. "--minion-tests",
  318. dest="minion",
  319. default=False,
  320. action="store_true",
  321. help="Run tests for minion",
  322. )
  323. self.test_selection_group.add_option(
  324. "--returners",
  325. dest="returners",
  326. default=False,
  327. action="store_true",
  328. help="Run salt/returners/*.py tests",
  329. )
  330. self.test_selection_group.add_option(
  331. "--spm",
  332. dest="spm",
  333. default=False,
  334. action="store_true",
  335. help="Run spm integration tests",
  336. )
  337. self.test_selection_group.add_option(
  338. "--setup",
  339. dest="setup",
  340. default=False,
  341. action="store_true",
  342. help="Run setup integration tests",
  343. )
  344. self.test_selection_group.add_option(
  345. "-l",
  346. "--loader",
  347. "--loader-tests",
  348. dest="loader",
  349. default=False,
  350. action="store_true",
  351. help="Run loader tests",
  352. )
  353. self.test_selection_group.add_option(
  354. "-u",
  355. "--unit",
  356. "--unit-tests",
  357. dest="unit",
  358. default=False,
  359. action="store_true",
  360. help="Run unit tests",
  361. )
  362. self.test_selection_group.add_option(
  363. "-k",
  364. "--kitchen",
  365. "--kitchen-tests",
  366. dest="kitchen",
  367. default=False,
  368. action="store_true",
  369. help="Run kitchen tests",
  370. )
  371. self.test_selection_group.add_option(
  372. "--fileserver",
  373. "--fileserver-tests",
  374. dest="fileserver",
  375. default=False,
  376. action="store_true",
  377. help="Run Fileserver tests",
  378. )
  379. self.test_selection_group.add_option(
  380. "-w",
  381. "--wheel",
  382. "--wheel-tests",
  383. dest="wheel",
  384. action="store_true",
  385. default=False,
  386. help="Run wheel tests",
  387. )
  388. self.test_selection_group.add_option(
  389. "-o",
  390. "--outputter",
  391. "--outputter-tests",
  392. dest="outputter",
  393. action="store_true",
  394. default=False,
  395. help="Run outputter tests",
  396. )
  397. self.test_selection_group.add_option(
  398. "--cloud-provider",
  399. "--cloud-provider-tests",
  400. dest="cloud_provider",
  401. action="store_true",
  402. default=False,
  403. help=(
  404. "Run cloud provider tests. These tests create and delete "
  405. "instances on cloud providers. Must provide valid credentials "
  406. "in salt/tests/integration/files/conf/cloud.*.d to run tests."
  407. ),
  408. )
  409. self.test_selection_group.add_option(
  410. "--ssh",
  411. "--ssh-tests",
  412. dest="ssh",
  413. action="store_true",
  414. default=False,
  415. help="Run salt-ssh tests. These tests will spin up a temporary "
  416. "SSH server on your machine. In certain environments, this "
  417. "may be insecure! Default: False",
  418. )
  419. self.test_selection_group.add_option(
  420. "--ssh-int",
  421. dest="ssh-int",
  422. action="store_true",
  423. default=False,
  424. help="Run salt-ssh integration tests. Requires to be run with --ssh"
  425. "to spin up the SSH server on your machine.",
  426. )
  427. self.test_selection_group.add_option(
  428. "-A",
  429. "--api",
  430. "--api-tests",
  431. dest="api",
  432. action="store_true",
  433. default=False,
  434. help="Run salt-api tests",
  435. )
  436. self.test_selection_group.add_option(
  437. "--sdb",
  438. "--sdb-tests",
  439. dest="sdb",
  440. action="store_true",
  441. default=False,
  442. help="Run sdb tests",
  443. )
  444. self.test_selection_group.add_option(
  445. "-P",
  446. "--proxy",
  447. "--proxy-tests",
  448. dest="proxy",
  449. action="store_true",
  450. default=False,
  451. help="Run salt-proxy tests",
  452. )
  453. self.test_selection_group.add_option(
  454. "--external",
  455. "--external-api",
  456. "--external-api-tests",
  457. dest="external_api",
  458. action="store_true",
  459. default=False,
  460. help="Run venafi runner tests",
  461. )
  462. self.test_selection_group.add_option(
  463. "--daemons",
  464. "--daemon-tests",
  465. dest="daemons",
  466. action="store_true",
  467. default=False,
  468. help="Run salt/daemons/*.py tests",
  469. )
  470. self.test_selection_group.add_option(
  471. "--scheduler",
  472. dest="scheduler",
  473. action="store_true",
  474. default=False,
  475. help="Run scheduler integration tests",
  476. )
  477. self.test_selection_group.add_option(
  478. "--logging",
  479. dest="logging",
  480. action="store_true",
  481. default=False,
  482. help="Run logging integration tests",
  483. )
  484. self.test_selection_group.add_option(
  485. "--multimaster",
  486. dest="multimaster",
  487. action="store_true",
  488. default=False,
  489. help="Start multimaster daemons and run multimaster integration tests",
  490. )
  491. def validate_options(self):
  492. if self.options.cloud_provider or self.options.external_api:
  493. # Turn on expensive tests execution
  494. os.environ["EXPENSIVE_TESTS"] = "True"
  495. # This fails even with salt.utils.platform imported in the global
  496. # scope, unless we import it again here.
  497. import salt.utils.platform
  498. if salt.utils.platform.is_windows():
  499. import salt.utils.win_functions
  500. current_user = salt.utils.win_functions.get_current_user()
  501. if current_user == "SYSTEM":
  502. is_admin = True
  503. else:
  504. is_admin = salt.utils.win_functions.is_admin(current_user)
  505. if (
  506. self.options.coverage
  507. and any(
  508. (self.options.name, not is_admin, not self.options.run_destructive)
  509. )
  510. and self._check_enabled_suites(include_unit=True)
  511. ):
  512. warnings.warn("Test suite not running with elevated priviledges")
  513. else:
  514. is_admin = os.geteuid() == 0
  515. if (
  516. self.options.coverage
  517. and any(
  518. (self.options.name, not is_admin, not self.options.run_destructive)
  519. )
  520. and self._check_enabled_suites(include_unit=True)
  521. ):
  522. self.error(
  523. "No sense in generating the tests coverage report when "
  524. "not running the full test suite, including the "
  525. "destructive tests, as 'root'. It would only produce "
  526. "incorrect results."
  527. )
  528. # When no tests are specifically enumerated on the command line, setup
  529. # a default run: +unit -cloud_provider
  530. if not self.options.name and not self._check_enabled_suites(
  531. include_unit=True,
  532. include_cloud_provider=True,
  533. include_proxy=True,
  534. include_kitchen=True,
  535. include_multimaster=True,
  536. ):
  537. self._enable_suites(include_unit=True, include_multimaster=True)
  538. self.start_coverage(
  539. branch=True, source=[os.path.join(SALT_ROOT, "salt")],
  540. )
  541. # Print out which version of python this test suite is running on
  542. print(" * Python Version: {0}".format(" ".join(sys.version.split())))
  543. # Transplant configuration
  544. TestDaemon.transplant_configs(transport=self.options.transport)
  545. MultimasterTestDaemon.transplant_configs(transport=self.options.transport)
  546. def post_execution_cleanup(self):
  547. SaltCoverageTestingParser.post_execution_cleanup(self)
  548. if self.options.clean:
  549. TestDaemon.clean()
  550. def run_integration_suite(self, path="", display_name=""):
  551. """
  552. Run an integration test suite
  553. """
  554. full_path = os.path.join(TEST_DIR, path)
  555. return self.run_suite(
  556. full_path, display_name, suffix="test_*.py", failfast=self.options.failfast,
  557. )
  558. def start_daemons_only(self):
  559. if not salt.utils.platform.is_windows():
  560. self.set_filehandle_limits("integration")
  561. try:
  562. print_header(
  563. " * Setting up Salt daemons for interactive use",
  564. top=False,
  565. width=getattr(self.options, "output_columns", PNUM),
  566. )
  567. except TypeError:
  568. print_header(" * Setting up Salt daemons for interactive use", top=False)
  569. try:
  570. with TestDaemon(self):
  571. print_header(" * Salt daemons started")
  572. master_conf = TestDaemon.config("master")
  573. minion_conf = TestDaemon.config("minion")
  574. proxy_conf = TestDaemon.config("proxy")
  575. sub_minion_conf = TestDaemon.config("sub_minion")
  576. syndic_conf = TestDaemon.config("syndic")
  577. syndic_master_conf = TestDaemon.config("syndic_master")
  578. print_header(" * Syndic master configuration values (MoM)", top=False)
  579. print("interface: {0}".format(syndic_master_conf["interface"]))
  580. print("publish port: {0}".format(syndic_master_conf["publish_port"]))
  581. print("return port: {0}".format(syndic_master_conf["ret_port"]))
  582. print("\n")
  583. print_header(" * Syndic configuration values", top=True)
  584. print("interface: {0}".format(syndic_conf["interface"]))
  585. print("syndic master: {0}".format(syndic_conf["syndic_master"]))
  586. print(
  587. "syndic master port: {0}".format(syndic_conf["syndic_master_port"])
  588. )
  589. print("\n")
  590. print_header(" * Master configuration values", top=True)
  591. print("interface: {0}".format(master_conf["interface"]))
  592. print("publish port: {0}".format(master_conf["publish_port"]))
  593. print("return port: {0}".format(master_conf["ret_port"]))
  594. print("\n")
  595. print_header(" * Minion configuration values", top=True)
  596. print("interface: {0}".format(minion_conf["interface"]))
  597. print("master: {0}".format(minion_conf["master"]))
  598. print("master port: {0}".format(minion_conf["master_port"]))
  599. if minion_conf["ipc_mode"] == "tcp":
  600. print("tcp pub port: {0}".format(minion_conf["tcp_pub_port"]))
  601. print("tcp pull port: {0}".format(minion_conf["tcp_pull_port"]))
  602. print("\n")
  603. print_header(" * Sub Minion configuration values", top=True)
  604. print("interface: {0}".format(sub_minion_conf["interface"]))
  605. print("master: {0}".format(sub_minion_conf["master"]))
  606. print("master port: {0}".format(sub_minion_conf["master_port"]))
  607. if sub_minion_conf["ipc_mode"] == "tcp":
  608. print("tcp pub port: {0}".format(sub_minion_conf["tcp_pub_port"]))
  609. print("tcp pull port: {0}".format(sub_minion_conf["tcp_pull_port"]))
  610. print("\n")
  611. print_header(" * Proxy Minion configuration values", top=True)
  612. print("interface: {0}".format(proxy_conf["interface"]))
  613. print("master: {0}".format(proxy_conf["master"]))
  614. print("master port: {0}".format(proxy_conf["master_port"]))
  615. if minion_conf["ipc_mode"] == "tcp":
  616. print("tcp pub port: {0}".format(proxy_conf["tcp_pub_port"]))
  617. print("tcp pull port: {0}".format(proxy_conf["tcp_pull_port"]))
  618. print("\n")
  619. print_header(
  620. " Your client configuration is at {0}".format(
  621. TestDaemon.config_location()
  622. )
  623. )
  624. print(
  625. "To access the minion: salt -c {0} minion test.ping".format(
  626. TestDaemon.config_location()
  627. )
  628. )
  629. while True:
  630. time.sleep(1)
  631. except TestDaemonStartFailed:
  632. self.exit(status=2)
  633. def start_multimaster_daemons_only(self):
  634. if not salt.utils.platform.is_windows():
  635. self.set_filehandle_limits("integration")
  636. try:
  637. print_header(
  638. " * Setting up Salt daemons for interactive use",
  639. top=False,
  640. width=getattr(self.options, "output_columns", PNUM),
  641. )
  642. except TypeError:
  643. print_header(" * Setting up Salt daemons for interactive use", top=False)
  644. try:
  645. with MultimasterTestDaemon(self):
  646. print_header(" * Salt daemons started")
  647. master_conf = MultimasterTestDaemon.config("mm_master")
  648. sub_master_conf = MultimasterTestDaemon.config("mm_sub_master")
  649. minion_conf = MultimasterTestDaemon.config("mm_minion")
  650. sub_minion_conf = MultimasterTestDaemon.config("mm_sub_minion")
  651. print_header(" * Master configuration values", top=True)
  652. print("interface: {0}".format(master_conf["interface"]))
  653. print("publish port: {0}".format(master_conf["publish_port"]))
  654. print("return port: {0}".format(master_conf["ret_port"]))
  655. print("\n")
  656. print_header(" * Second master configuration values", top=True)
  657. print("interface: {0}".format(sub_master_conf["interface"]))
  658. print("publish port: {0}".format(sub_master_conf["publish_port"]))
  659. print("return port: {0}".format(sub_master_conf["ret_port"]))
  660. print("\n")
  661. print_header(" * Minion configuration values", top=True)
  662. print("interface: {0}".format(minion_conf["interface"]))
  663. print("masters: {0}".format(", ".join(minion_conf["master"])))
  664. if minion_conf["ipc_mode"] == "tcp":
  665. print("tcp pub port: {0}".format(minion_conf["tcp_pub_port"]))
  666. print("tcp pull port: {0}".format(minion_conf["tcp_pull_port"]))
  667. print("\n")
  668. print_header(" * Sub Minion configuration values", top=True)
  669. print("interface: {0}".format(sub_minion_conf["interface"]))
  670. print("masters: {0}".format(", ".join(sub_minion_conf["master"])))
  671. if sub_minion_conf["ipc_mode"] == "tcp":
  672. print("tcp pub port: {0}".format(sub_minion_conf["tcp_pub_port"]))
  673. print("tcp pull port: {0}".format(sub_minion_conf["tcp_pull_port"]))
  674. print("\n")
  675. print_header(
  676. " Your client configurations are at {0}".format(
  677. ", ".join(MultimasterTestDaemon.config_location())
  678. )
  679. )
  680. print("To access minions from different masters use:")
  681. for location in MultimasterTestDaemon.config_location():
  682. print(" salt -c {0} minion test.ping".format(location))
  683. while True:
  684. time.sleep(1)
  685. except TestDaemonStartFailed:
  686. self.exit(status=2)
  687. def set_filehandle_limits(self, limits="integration"):
  688. """
  689. Set soft and hard limits on open file handles at required thresholds
  690. for integration tests or unit tests
  691. """
  692. # Get current limits
  693. if salt.utils.platform.is_windows():
  694. import win32file
  695. prev_hard = win32file._getmaxstdio()
  696. prev_soft = 512
  697. else:
  698. prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE)
  699. # Get required limits
  700. min_soft = MAX_OPEN_FILES[limits]["soft_limit"]
  701. min_hard = MAX_OPEN_FILES[limits]["hard_limit"]
  702. # Check minimum required limits
  703. set_limits = False
  704. if prev_soft < min_soft:
  705. soft = min_soft
  706. set_limits = True
  707. else:
  708. soft = prev_soft
  709. if prev_hard < min_hard:
  710. hard = min_hard
  711. set_limits = True
  712. else:
  713. hard = prev_hard
  714. # Increase limits
  715. if set_limits:
  716. print(
  717. " * Max open files settings is too low (soft: {0}, hard: {1}) "
  718. "for running the tests".format(prev_soft, prev_hard)
  719. )
  720. print(
  721. " * Trying to raise the limits to soft: "
  722. "{0}, hard: {1}".format(soft, hard)
  723. )
  724. try:
  725. if salt.utils.platform.is_windows():
  726. hard = 2048 if hard > 2048 else hard
  727. win32file._setmaxstdio(hard)
  728. else:
  729. resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
  730. except Exception as err: # pylint: disable=broad-except
  731. print(
  732. "ERROR: Failed to raise the max open files settings -> "
  733. "{0}".format(err)
  734. )
  735. print("Please issue the following command on your console:")
  736. print(" ulimit -n {0}".format(soft))
  737. self.exit()
  738. finally:
  739. print("~" * getattr(self.options, "output_columns", PNUM))
  740. def run_integration_tests(self):
  741. """
  742. Execute the integration tests suite
  743. """
  744. named_tests = []
  745. named_unit_test = []
  746. if self.options.name:
  747. for test in self.options.name:
  748. if test.startswith(
  749. (
  750. "tests.unit.",
  751. "unit.",
  752. "test.kitchen.",
  753. "kitchen.",
  754. "test.multimaster.",
  755. "multimaster.",
  756. )
  757. ):
  758. named_unit_test.append(test)
  759. continue
  760. named_tests.append(test)
  761. if (
  762. (
  763. self.options.unit
  764. or self.options.kitchen
  765. or self.options.multimaster
  766. or named_unit_test
  767. )
  768. and not named_tests
  769. and (
  770. self.options.from_filenames
  771. or not self._check_enabled_suites(include_cloud_provider=True)
  772. )
  773. ):
  774. # We're either not running any integration test suites, or we're
  775. # only running unit tests by passing --unit or by passing only
  776. # `unit.<whatever>` to --name. We don't need the tests daemon
  777. # running
  778. return [True]
  779. if not salt.utils.platform.is_windows():
  780. self.set_filehandle_limits("integration")
  781. try:
  782. print_header(
  783. " * Setting up Salt daemons to execute tests",
  784. top=False,
  785. width=getattr(self.options, "output_columns", PNUM),
  786. )
  787. except TypeError:
  788. print_header(" * Setting up Salt daemons to execute tests", top=False)
  789. status = []
  790. # Return an empty status if no tests have been enabled
  791. if (
  792. not self._check_enabled_suites(
  793. include_cloud_provider=True, include_proxy=True
  794. )
  795. and not self.options.name
  796. ):
  797. return status
  798. try:
  799. with TestDaemon(self):
  800. if self.options.name:
  801. for name in self.options.name:
  802. name = name.strip()
  803. if not name:
  804. continue
  805. if os.path.isfile(name):
  806. if not name.endswith(".py"):
  807. continue
  808. if name.startswith(
  809. (
  810. os.path.join("tests", "unit"),
  811. os.path.join("tests", "multimaster"),
  812. )
  813. ):
  814. continue
  815. results = self.run_suite(
  816. os.path.dirname(name),
  817. name,
  818. suffix=os.path.basename(name),
  819. failfast=self.options.failfast,
  820. load_from_name=False,
  821. )
  822. status.append(results)
  823. continue
  824. if name.startswith(
  825. (
  826. "tests.unit.",
  827. "unit.",
  828. "tests.multimaster.",
  829. "multimaster.",
  830. )
  831. ):
  832. continue
  833. results = self.run_suite(
  834. "",
  835. name,
  836. suffix="test_*.py",
  837. load_from_name=True,
  838. failfast=self.options.failfast,
  839. )
  840. status.append(results)
  841. return status
  842. for suite in TEST_SUITES:
  843. if (
  844. suite != "unit"
  845. and suite != "multimaster"
  846. and getattr(self.options, suite)
  847. ):
  848. status.append(self.run_integration_suite(**TEST_SUITES[suite]))
  849. return status
  850. except TestDaemonStartFailed:
  851. self.exit(status=2)
  852. def run_multimaster_tests(self):
  853. """
  854. Execute the multimaster tests suite
  855. """
  856. named_tests = []
  857. named_unit_test = []
  858. if self.options.name:
  859. for test in self.options.name:
  860. if test.startswith(("tests.multimaster.", "multimaster.")):
  861. named_tests.append(test)
  862. # TODO: check 'from_filenames'
  863. if not self.options.multimaster and not named_tests:
  864. # We're not running any multimaster test suites.
  865. return [True]
  866. if not salt.utils.platform.is_windows():
  867. self.set_filehandle_limits("integration")
  868. try:
  869. print_header(
  870. " * Setting up multimaster Salt daemons to execute tests",
  871. top=False,
  872. width=getattr(self.options, "output_columns", PNUM),
  873. )
  874. except TypeError:
  875. print_header(
  876. " * Setting up multimaster Salt daemons to execute tests", top=False
  877. )
  878. status = []
  879. try:
  880. with MultimasterTestDaemon(self):
  881. if self.options.name:
  882. for name in self.options.name:
  883. name = name.strip()
  884. if not name:
  885. continue
  886. if os.path.isfile(name):
  887. if not name.endswith(".py"):
  888. continue
  889. if not name.startswith(
  890. os.path.join("tests", "multimaster")
  891. ):
  892. continue
  893. results = self.run_suite(
  894. os.path.dirname(name),
  895. name,
  896. suffix=os.path.basename(name),
  897. load_from_name=False,
  898. )
  899. status.append(results)
  900. continue
  901. if not name.startswith(("tests.multimaster.", "multimaster.")):
  902. continue
  903. results = self.run_suite(
  904. "", name, suffix="test_*.py", load_from_name=True
  905. )
  906. status.append(results)
  907. return status
  908. status.append(self.run_integration_suite(**TEST_SUITES["multimaster"]))
  909. return status
  910. except TestDaemonStartFailed:
  911. self.exit(status=2)
  912. def run_unit_tests(self):
  913. """
  914. Execute the unit tests
  915. """
  916. named_unit_test = []
  917. if self.options.name:
  918. for test in self.options.name:
  919. if not test.startswith(("tests.unit.", "unit.")):
  920. continue
  921. named_unit_test.append(test)
  922. if not named_unit_test and (
  923. self.options.from_filenames or not self.options.unit
  924. ):
  925. # We are not explicitly running the unit tests and none of the
  926. # names passed to --name (or derived via --from-filenames) is a
  927. # unit test.
  928. return [True]
  929. status = []
  930. if self.options.unit:
  931. # MacOS needs more open filehandles for running unit test suite
  932. self.set_filehandle_limits("unit")
  933. results = self.run_suite(
  934. os.path.join(TEST_DIR, "unit"),
  935. "Unit",
  936. suffix="test_*.py",
  937. failfast=self.options.failfast,
  938. )
  939. status.append(results)
  940. # We executed ALL unittests, we can skip running unittests by name
  941. # below
  942. return status
  943. for name in named_unit_test:
  944. results = self.run_suite(
  945. os.path.join(TEST_DIR, "unit"),
  946. name,
  947. suffix="test_*.py",
  948. load_from_name=True,
  949. failfast=self.options.failfast,
  950. )
  951. status.append(results)
  952. return status
  953. def run_kitchen_tests(self):
  954. """
  955. Execute the kitchen tests
  956. """
  957. named_kitchen_test = []
  958. if self.options.name:
  959. for test in self.options.name:
  960. if not test.startswith(("tests.kitchen.", "kitchen.")):
  961. continue
  962. named_kitchen_test.append(test)
  963. if not self.options.kitchen and not named_kitchen_test:
  964. # We are not explicitly running the unit tests and none of the
  965. # names passed to --name is a unit test.
  966. return [True]
  967. status = []
  968. if self.options.kitchen:
  969. results = self.run_suite(
  970. os.path.join(TEST_DIR, "kitchen"), "Kitchen", suffix="test_*.py"
  971. )
  972. status.append(results)
  973. # We executed ALL unittests, we can skip running unittests by name
  974. # below
  975. return status
  976. for name in named_kitchen_test:
  977. results = self.run_suite(
  978. os.path.join(TEST_DIR, "kitchen"),
  979. name,
  980. suffix="test_*.py",
  981. load_from_name=True,
  982. )
  983. status.append(results)
  984. return status
  985. def main(**kwargs):
  986. """
  987. Parse command line options for running specific tests
  988. """
  989. try:
  990. parser = SaltTestsuiteParser(
  991. TEST_DIR,
  992. xml_output_dir=XML_OUTPUT_DIR,
  993. tests_logfile=os.path.join(SYS_TMP_DIR, "salt-runtests.log"),
  994. )
  995. parser.parse_args()
  996. # Override parser options (helpful when importing runtests.py and
  997. # running from within a REPL). Using kwargs.items() to avoid importing
  998. # six, as this feature will rarely be used.
  999. for key, val in kwargs.items():
  1000. setattr(parser.options, key, val)
  1001. overall_status = []
  1002. if parser.options.interactive:
  1003. if parser.options.multimaster:
  1004. parser.start_multimaster_daemons_only()
  1005. else:
  1006. parser.start_daemons_only()
  1007. status = parser.run_integration_tests()
  1008. overall_status.extend(status)
  1009. status = parser.run_multimaster_tests()
  1010. overall_status.extend(status)
  1011. status = parser.run_unit_tests()
  1012. overall_status.extend(status)
  1013. status = parser.run_kitchen_tests()
  1014. overall_status.extend(status)
  1015. false_count = overall_status.count(False)
  1016. if false_count > 0:
  1017. parser.finalize(1)
  1018. parser.finalize(0)
  1019. except KeyboardInterrupt:
  1020. print("\nCaught keyboard interrupt. Exiting.\n")
  1021. exit(0)
  1022. if __name__ == "__main__":
  1023. main()