runtests.py 33 KB

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