test_doc.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. # -*- coding: utf-8 -*-
  2. '''
  3. tests.unit.doc_test
  4. ~~~~~~~~~~~~~~~~~~~~
  5. '''
  6. # Import Python libs
  7. from __future__ import absolute_import
  8. import os
  9. import re
  10. import logging
  11. # Import Salt Testing libs
  12. from tests.support.unit import TestCase
  13. from tests.support.runtests import RUNTIME_VARS
  14. # Import Salt libs
  15. import salt.modules.cmdmod
  16. from tests.support.unit import skipIf, WAR_ROOM_SKIP # WAR ROOM temp import
  17. import salt.utils.platform
  18. log = logging.getLogger(__name__)
  19. @skipIf(WAR_ROOM_SKIP, 'WAR ROOM TEMPORARY SKIP')
  20. class DocTestCase(TestCase):
  21. '''
  22. Unit test case for testing doc files and strings.
  23. '''
  24. def test_check_for_doc_inline_markup(self):
  25. '''
  26. We should not be using the ``:doc:`` inline markup option when
  27. cross-referencing locations. Use ``:ref:`` or ``:mod:`` instead.
  28. This test checks for reference to ``:doc:`` usage.
  29. See Issue #12788 for more information.
  30. https://github.com/saltstack/salt/issues/12788
  31. '''
  32. salt_dir = RUNTIME_VARS.CODE_DIR
  33. if salt.utils.platform.is_windows():
  34. if salt.utils.path.which('bash'):
  35. # Use grep from git-bash when it exists.
  36. cmd = 'bash -c \'grep -r :doc: ./salt/'
  37. grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd, cwd=salt_dir).split(os.linesep)
  38. else:
  39. # No grep in Windows, use findstr
  40. # findstr in windows doesn't prepend 'Binary` to binary files, so
  41. # use the '/P' switch to skip files with unprintable characters
  42. cmd = 'findstr /C:":doc:" /S /P {0}\\*'.format(salt_dir)
  43. grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep)
  44. else:
  45. salt_dir += '/'
  46. cmd = 'grep -r :doc: ' + salt_dir
  47. grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep)
  48. test_ret = {}
  49. for line in grep_call:
  50. # Skip any .pyc files that may be present
  51. if line.startswith('Binary'):
  52. continue
  53. # Only split on colons not followed by a '\' as is the case with
  54. # Windows Drives
  55. regex = re.compile(r':(?!\\)')
  56. try:
  57. key, val = regex.split(line, 1)
  58. except ValueError:
  59. log.error("Could not split line: %s", line)
  60. continue
  61. # Don't test man pages, this file, the tox or nox virtualenv files,
  62. # the page that documents to not use ":doc:", the doc/conf.py file
  63. # or the artifacts directory on nox CI test runs
  64. if 'man' in key \
  65. or '.tox{}'.format(os.sep) in key \
  66. or '.nox{}'.format(os.sep) in key \
  67. or 'artifacts{}'.format(os.sep) in key \
  68. or key.endswith('test_doc.py') \
  69. or key.endswith(os.sep.join(['doc', 'conf.py'])) \
  70. or key.endswith(os.sep.join(['conventions', 'documentation.rst'])) \
  71. or key.endswith(os.sep.join(['doc', 'topics', 'releases', '2016.11.2.rst'])) \
  72. or key.endswith(os.sep.join(['doc', 'topics', 'releases', '2016.11.3.rst'])) \
  73. or key.endswith(os.sep.join(['doc', 'topics', 'releases', '2016.3.5.rst'])):
  74. continue
  75. # Set up test return dict
  76. if test_ret.get(key) is None:
  77. test_ret[key] = [val.strip()]
  78. else:
  79. test_ret[key].append(val.strip())
  80. # Allow test results to show files with :doc: ref, rather than truncating
  81. self.maxDiff = None
  82. # test_ret should be empty, otherwise there are :doc: references present
  83. self.assertEqual(test_ret, {})
  84. def _check_doc_files(self, module_skip, module_dir, doc_skip, module_doc_dir):
  85. '''
  86. Ensure various salt modules have associated documentation
  87. '''
  88. salt_dir = RUNTIME_VARS.CODE_DIR
  89. # Build list of module files
  90. module_files = []
  91. skip_module_files = module_skip
  92. full_module_dir = os.path.join(salt_dir, *module_dir)
  93. for file in os.listdir(full_module_dir):
  94. if file.endswith(".py"):
  95. module_name = os.path.splitext(file)[0]
  96. if module_name not in skip_module_files:
  97. module_files.append(module_name)
  98. # Build list of beacon documentation files
  99. module_docs = []
  100. skip_doc_files = doc_skip
  101. full_module_doc_dir = os.path.join(salt_dir, *module_doc_dir)
  102. doc_prefix = '.'.join(module_dir) + '.'
  103. for file in os.listdir(full_module_doc_dir):
  104. if file.endswith(".rst"):
  105. doc_name = os.path.splitext(file)[0]
  106. if doc_name.startswith(doc_prefix):
  107. doc_name = doc_name[len(doc_prefix):]
  108. if doc_name not in skip_doc_files:
  109. module_docs.append(doc_name)
  110. # Check that every beacon has associated documentaiton file
  111. for module in module_files:
  112. self.assertIn(module,
  113. module_docs,
  114. 'module file {0} is missing documentation in {1}'.format(module,
  115. full_module_doc_dir))
  116. for doc_file in module_docs:
  117. self.assertIn(doc_file,
  118. module_files,
  119. 'Doc file {0} is missing associated module in {1}'.format(doc_file,
  120. full_module_dir))
  121. def test_auth_doc_files(self):
  122. '''
  123. Ensure auth modules have associated documentation
  124. doc example: doc/ref/auth/all/salt.auth.rest.rst
  125. auth module example: salt/auth/rest.py
  126. '''
  127. skip_files = ['__init__']
  128. module_dir = ['salt', 'auth']
  129. skip_doc_files = ['index', 'all']
  130. doc_dir = ['doc', 'ref', 'auth', 'all']
  131. self._check_doc_files(skip_files, module_dir, skip_doc_files, doc_dir)
  132. def test_beacon_doc_files(self):
  133. '''
  134. Ensure beacon modules have associated documentation
  135. doc example: doc/ref/beacons/all/salt.beacon.rest.rst
  136. beacon module example: salt/beacons/rest.py
  137. '''
  138. skip_files = ['__init__']
  139. module_dir = ['salt', 'beacons']
  140. skip_doc_files = ['index', 'all']
  141. doc_dir = ['doc', 'ref', 'beacons', 'all']
  142. self._check_doc_files(skip_files, module_dir, skip_doc_files, doc_dir)
  143. def test_cache_doc_files(self):
  144. '''
  145. Ensure cache modules have associated documentation
  146. doc example: doc/ref/cache/all/salt.cache.consul.rst
  147. cache module example: salt/cache/consul.py
  148. '''
  149. skip_module_files = ['__init__']
  150. module_dir = ['salt', 'cache']
  151. skip_doc_files = ['index', 'all']
  152. doc_dir = ['doc', 'ref', 'cache', 'all']
  153. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  154. def test_cloud_doc_files(self):
  155. '''
  156. Ensure cloud modules have associated documentation
  157. doc example: doc/ref/clouds/all/salt.cloud.gce.rst
  158. cloud module example: salt/cloud/clouds/gce.py
  159. '''
  160. skip_module_files = ['__init__']
  161. module_dir = ['salt', 'cloud', 'clouds']
  162. skip_doc_files = ['index', 'all']
  163. doc_dir = ['doc', 'ref', 'clouds', 'all']
  164. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  165. def test_engine_doc_files(self):
  166. '''
  167. Ensure engine modules have associated documentation
  168. doc example: doc/ref/engines/all/salt.engines.docker_events.rst
  169. engine module example: salt/engines/docker_events.py
  170. '''
  171. skip_module_files = ['__init__']
  172. module_dir = ['salt', 'engines']
  173. skip_doc_files = ['index', 'all']
  174. doc_dir = ['doc', 'ref', 'engines', 'all']
  175. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  176. def test_fileserver_doc_files(self):
  177. '''
  178. Ensure fileserver modules have associated documentation
  179. doc example: doc/ref/fileserver/all/salt.fileserver.gitfs.rst
  180. module example: salt/fileserver/gitfs.py
  181. '''
  182. skip_module_files = ['__init__']
  183. module_dir = ['salt', 'fileserver']
  184. skip_doc_files = ['index', 'all']
  185. doc_dir = ['doc', 'ref', 'file_server', 'all']
  186. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  187. def test_grain_doc_files(self):
  188. '''
  189. Ensure grain modules have associated documentation
  190. doc example: doc/ref/grains/all/salt.grains.core.rst
  191. module example: salt/grains/core.py
  192. '''
  193. skip_module_files = ['__init__']
  194. module_dir = ['salt', 'grains']
  195. skip_doc_files = ['index', 'all']
  196. doc_dir = ['doc', 'ref', 'grains', 'all']
  197. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  198. def test_module_doc_files(self):
  199. '''
  200. Ensure modules have associated documentation
  201. doc example: doc/ref/modules/all/salt.modules.zabbix.rst
  202. execution module example: salt/modules/zabbix.py
  203. '''
  204. skip_module_files = ['__init__']
  205. module_dir = ['salt', 'modules']
  206. skip_doc_files = ['index', 'group', 'inspectlib', 'inspectlib.collector', 'inspectlib.dbhandle',
  207. 'inspectlib.entities', 'inspectlib.exceptions', 'inspectlib.fsdb',
  208. 'inspectlib.kiwiproc', 'inspectlib.query', 'kernelpkg', 'pkg', 'user']
  209. doc_dir = ['doc', 'ref', 'modules', 'all']
  210. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  211. def test_output_doc_files(self):
  212. '''
  213. Ensure output modules have associated documentation
  214. doc example: doc/ref/output/all/salt.output.highstate.rst
  215. module example: salt/output/highstate.py
  216. '''
  217. skip_module_files = ['__init__']
  218. module_dir = ['salt', 'output']
  219. skip_doc_files = ['index', 'all']
  220. doc_dir = ['doc', 'ref', 'output', 'all']
  221. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  222. def test_pillar_doc_files(self):
  223. '''
  224. Ensure pillar modules have associated documentation
  225. doc example: doc/ref/pillar/all/salt.pillar.cobbler.rst
  226. module example: salt/pillar/cobbler.py
  227. '''
  228. skip_module_files = ['__init__']
  229. module_dir = ['salt', 'pillar']
  230. skip_doc_files = ['index', 'all']
  231. doc_dir = ['doc', 'ref', 'pillar', 'all']
  232. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  233. def test_proxy_doc_files(self):
  234. '''
  235. Ensure proxy modules have associated documentation
  236. doc example: doc/ref/proxy/all/salt.proxy.docker.rst
  237. module example: salt/proxy/docker.py
  238. '''
  239. skip_module_files = ['__init__']
  240. module_dir = ['salt', 'proxy']
  241. skip_doc_files = ['index', 'all']
  242. doc_dir = ['doc', 'ref', 'proxy', 'all']
  243. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  244. def test_queues_doc_files(self):
  245. '''
  246. Ensure queue modules have associated documentation
  247. doc example: doc/ref/queues/all/salt.queues.sqlite_queue.rst
  248. module example: salt/queues/sqlite_queue.py
  249. '''
  250. skip_module_files = ['__init__']
  251. module_dir = ['salt', 'queues']
  252. skip_doc_files = ['index', 'all']
  253. doc_dir = ['doc', 'ref', 'queues', 'all']
  254. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  255. def test_renderers_doc_files(self):
  256. '''
  257. Ensure render modules have associated documentation
  258. doc example: doc/ref/renderers/all/salt.renderers.json.rst
  259. module example: salt/renderers/json.py
  260. '''
  261. skip_module_files = ['__init__']
  262. module_dir = ['salt', 'renderers']
  263. skip_doc_files = ['index', 'all']
  264. doc_dir = ['doc', 'ref', 'renderers', 'all']
  265. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  266. def test_returners_doc_files(self):
  267. '''
  268. Ensure return modules have associated documentation
  269. doc example: doc/ref/returners/all/salt.returners.cassandra_return.rst
  270. module example: salt/returners/cassandra_return.py
  271. '''
  272. skip_module_files = ['__init__']
  273. module_dir = ['salt', 'returners']
  274. skip_doc_files = ['index', 'all']
  275. doc_dir = ['doc', 'ref', 'returners', 'all']
  276. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  277. def test_runners_doc_files(self):
  278. '''
  279. Ensure runner modules have associated documentation
  280. doc example: doc/ref/runners/all/salt.runners.auth.rst
  281. module example: salt/runners/auth.py
  282. '''
  283. skip_module_files = ['__init__']
  284. module_dir = ['salt', 'runners']
  285. skip_doc_files = ['index', 'all']
  286. doc_dir = ['doc', 'ref', 'runners', 'all']
  287. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  288. def test_sdb_doc_files(self):
  289. '''
  290. Ensure sdb modules have associated documentation
  291. doc example: doc/ref/sdb/all/salt.sdb.rest.rst
  292. module example: salt/sdb/rest.py
  293. '''
  294. skip_module_files = ['__init__']
  295. module_dir = ['salt', 'sdb']
  296. skip_doc_files = ['index', 'all']
  297. doc_dir = ['doc', 'ref', 'sdb', 'all']
  298. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  299. def test_serializers_doc_files(self):
  300. '''
  301. Ensure serializer modules have associated documentation
  302. doc example: doc/ref/serializers/all/salt.serializers.yaml.rst
  303. module example: salt/serializers/yaml.py
  304. '''
  305. skip_module_files = ['__init__']
  306. module_dir = ['salt', 'serializers']
  307. skip_doc_files = ['index', 'all']
  308. doc_dir = ['doc', 'ref', 'serializers', 'all']
  309. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  310. def test_states_doc_files(self):
  311. '''
  312. Ensure states have associated documentation
  313. doc example: doc/ref/states/all/salt.states.zabbix_host.rst
  314. module example: salt/states/zabbix_host.py
  315. '''
  316. skip_module_files = ['__init__']
  317. module_dir = ['salt', 'states']
  318. skip_doc_files = ['index', 'all']
  319. doc_dir = ['doc', 'ref', 'states', 'all']
  320. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  321. def test_thorium_doc_files(self):
  322. '''
  323. Ensure thorium modules have associated documentation
  324. doc example: doc/ref/thorium/all/salt.thorium.calc.rst
  325. module example: salt/thorium/calc.py
  326. '''
  327. skip_module_files = ['__init__']
  328. module_dir = ['salt', 'thorium']
  329. skip_doc_files = ['index', 'all']
  330. doc_dir = ['doc', 'ref', 'thorium', 'all']
  331. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  332. def test_tops_doc_files(self):
  333. '''
  334. Ensure top modules have associated documentation
  335. doc example: doc/ref/tops/all/salt.tops.saltclass.rst
  336. module example: salt/tops/saltclass.py
  337. '''
  338. skip_module_files = ['__init__']
  339. module_dir = ['salt', 'tops']
  340. skip_doc_files = ['index', 'all']
  341. doc_dir = ['doc', 'ref', 'tops', 'all']
  342. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)
  343. def test_wheel_doc_files(self):
  344. '''
  345. Ensure wheel modules have associated documentation
  346. doc example: doc/ref/wheel/all/salt.wheel.key.rst
  347. module example: salt/wheel/key.py
  348. '''
  349. skip_module_files = ['__init__']
  350. module_dir = ['salt', 'wheel']
  351. skip_doc_files = ['index', 'all']
  352. doc_dir = ['doc', 'ref', 'wheel', 'all']
  353. self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir)