runtests.py 31 KB

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