runtests.py 32 KB

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