1
0

runtests.py 39 KB

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