runtests.py 39 KB

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