base_tests.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import os
  2. import tempfile
  3. import unittest
  4. from tempfile import TemporaryDirectory
  5. from mkdocs import exceptions
  6. from mkdocs.config import base, defaults
  7. from mkdocs.config.config_options import BaseConfigOption
  8. class ConfigBaseTests(unittest.TestCase):
  9. def test_unrecognised_keys(self):
  10. c = base.Config(schema=defaults.DEFAULT_SCHEMA)
  11. c.load_dict({
  12. 'not_a_valid_config_option': "test"
  13. })
  14. failed, warnings = c.validate()
  15. self.assertEqual(warnings, [
  16. ('not_a_valid_config_option',
  17. 'Unrecognised configuration name: not_a_valid_config_option')
  18. ])
  19. def test_missing_required(self):
  20. c = base.Config(schema=defaults.DEFAULT_SCHEMA)
  21. errors, warnings = c.validate()
  22. self.assertEqual(len(errors), 1)
  23. self.assertEqual(errors[0][0], 'site_name')
  24. self.assertEqual(str(errors[0][1]), 'Required configuration not provided.')
  25. self.assertEqual(len(warnings), 0)
  26. def test_load_from_file(self):
  27. """
  28. Users can explicitly set the config file using the '--config' option.
  29. Allows users to specify a config other than the default `mkdocs.yml`.
  30. """
  31. temp_dir = TemporaryDirectory()
  32. config_file = open(os.path.join(temp_dir.name, 'mkdocs.yml'), 'w')
  33. os.mkdir(os.path.join(temp_dir.name, 'docs'))
  34. try:
  35. config_file.write("site_name: MkDocs Test\n")
  36. config_file.flush()
  37. config_file.close()
  38. cfg = base.load_config(config_file=config_file.name)
  39. self.assertTrue(isinstance(cfg, base.Config))
  40. self.assertEqual(cfg['site_name'], 'MkDocs Test')
  41. finally:
  42. os.remove(config_file.name)
  43. temp_dir.cleanup()
  44. def test_load_from_missing_file(self):
  45. self.assertRaises(exceptions.ConfigurationError,
  46. base.load_config, config_file='missing_file.yml')
  47. def test_load_from_open_file(self):
  48. """
  49. `load_config` can accept an open file descriptor.
  50. """
  51. temp_dir = TemporaryDirectory()
  52. temp_path = temp_dir.name
  53. config_fname = os.path.join(temp_path, 'mkdocs.yml')
  54. config_file = open(config_fname, 'w+')
  55. os.mkdir(os.path.join(temp_path, 'docs'))
  56. try:
  57. config_file.write("site_name: MkDocs Test\n")
  58. config_file.flush()
  59. cfg = base.load_config(config_file=config_file)
  60. self.assertTrue(isinstance(cfg, base.Config))
  61. self.assertEqual(cfg['site_name'], 'MkDocs Test')
  62. # load_config will always close the file
  63. self.assertTrue(config_file.closed)
  64. finally:
  65. temp_dir.cleanup()
  66. def test_load_from_closed_file(self):
  67. """
  68. The `serve` command with auto-reload may pass in a closed file descriptor.
  69. Ensure `load_config` reloads the closed file.
  70. """
  71. temp_dir = TemporaryDirectory()
  72. config_file = open(os.path.join(temp_dir.name, 'mkdocs.yml'), 'w')
  73. os.mkdir(os.path.join(temp_dir.name, 'docs'))
  74. try:
  75. config_file.write("site_name: MkDocs Test\n")
  76. config_file.flush()
  77. config_file.close()
  78. cfg = base.load_config(config_file=config_file)
  79. self.assertTrue(isinstance(cfg, base.Config))
  80. self.assertEqual(cfg['site_name'], 'MkDocs Test')
  81. finally:
  82. temp_dir.cleanup()
  83. def test_load_from_deleted_file(self):
  84. """
  85. Deleting the config file could trigger a server reload.
  86. """
  87. config_file = tempfile.NamedTemporaryFile('w', delete=False)
  88. try:
  89. config_file.write("site_name: MkDocs Test\n")
  90. config_file.flush()
  91. config_file.close()
  92. finally:
  93. os.remove(config_file.name)
  94. self.assertRaises(exceptions.ConfigurationError,
  95. base.load_config, config_file=config_file)
  96. def test_load_missing_required(self):
  97. """
  98. `site_name` is a required setting.
  99. """
  100. config_file = tempfile.NamedTemporaryFile('w', delete=False)
  101. try:
  102. config_file.write(
  103. "site_dir: output\nsite_uri: https://www.mkdocs.org\n")
  104. config_file.flush()
  105. config_file.close()
  106. self.assertRaises(exceptions.ConfigurationError,
  107. base.load_config, config_file=config_file.name)
  108. finally:
  109. os.remove(config_file.name)
  110. def test_pre_validation_error(self):
  111. class InvalidConfigOption(BaseConfigOption):
  112. def pre_validation(self, config, key_name):
  113. raise base.ValidationError('pre_validation error')
  114. c = base.Config(schema=(('invalid_option', InvalidConfigOption()), ))
  115. errors, warnings = c.validate()
  116. self.assertEqual(len(errors), 1)
  117. self.assertEqual(errors[0][0], 'invalid_option')
  118. self.assertEqual(str(errors[0][1]), 'pre_validation error')
  119. self.assertTrue(isinstance(errors[0][1], base.ValidationError))
  120. self.assertEqual(len(warnings), 0)
  121. def test_run_validation_error(self):
  122. class InvalidConfigOption(BaseConfigOption):
  123. def run_validation(self, value):
  124. raise base.ValidationError('run_validation error')
  125. c = base.Config(schema=(('invalid_option', InvalidConfigOption()), ))
  126. errors, warnings = c.validate()
  127. self.assertEqual(len(errors), 1)
  128. self.assertEqual(errors[0][0], 'invalid_option')
  129. self.assertEqual(str(errors[0][1]), 'run_validation error')
  130. self.assertTrue(isinstance(errors[0][1], base.ValidationError))
  131. self.assertEqual(len(warnings), 0)
  132. def test_post_validation_error(self):
  133. class InvalidConfigOption(BaseConfigOption):
  134. def post_validation(self, config, key_name):
  135. raise base.ValidationError('post_validation error')
  136. c = base.Config(schema=(('invalid_option', InvalidConfigOption()), ))
  137. errors, warnings = c.validate()
  138. self.assertEqual(len(errors), 1)
  139. self.assertEqual(errors[0][0], 'invalid_option')
  140. self.assertEqual(str(errors[0][1]), 'post_validation error')
  141. self.assertTrue(isinstance(errors[0][1], base.ValidationError))
  142. self.assertEqual(len(warnings), 0)
  143. def test_pre_and_run_validation_errors(self):
  144. """ A pre_validation error does not stop run_validation from running. """
  145. class InvalidConfigOption(BaseConfigOption):
  146. def pre_validation(self, config, key_name):
  147. raise base.ValidationError('pre_validation error')
  148. def run_validation(self, value):
  149. raise base.ValidationError('run_validation error')
  150. c = base.Config(schema=(('invalid_option', InvalidConfigOption()), ))
  151. errors, warnings = c.validate()
  152. self.assertEqual(len(errors), 2)
  153. self.assertEqual(errors[0][0], 'invalid_option')
  154. self.assertEqual(str(errors[0][1]), 'pre_validation error')
  155. self.assertTrue(isinstance(errors[0][1], base.ValidationError))
  156. self.assertEqual(errors[1][0], 'invalid_option')
  157. self.assertEqual(str(errors[1][1]), 'run_validation error')
  158. self.assertTrue(isinstance(errors[1][1], base.ValidationError))
  159. self.assertEqual(len(warnings), 0)
  160. def test_run_and_post_validation_errors(self):
  161. """ A run_validation error stops post_validation from running. """
  162. class InvalidConfigOption(BaseConfigOption):
  163. def run_validation(self, value):
  164. raise base.ValidationError('run_validation error')
  165. def post_validation(self, config, key_name):
  166. raise base.ValidationError('post_validation error')
  167. c = base.Config(schema=(('invalid_option', InvalidConfigOption()), ))
  168. errors, warnings = c.validate()
  169. self.assertEqual(len(errors), 1)
  170. self.assertEqual(errors[0][0], 'invalid_option')
  171. self.assertEqual(str(errors[0][1]), 'run_validation error')
  172. self.assertTrue(isinstance(errors[0][1], base.ValidationError))
  173. self.assertEqual(len(warnings), 0)
  174. def test_validation_warnings(self):
  175. class InvalidConfigOption(BaseConfigOption):
  176. def pre_validation(self, config, key_name):
  177. self.warnings.append('pre_validation warning')
  178. def run_validation(self, value):
  179. self.warnings.append('run_validation warning')
  180. def post_validation(self, config, key_name):
  181. self.warnings.append('post_validation warning')
  182. c = base.Config(schema=(('invalid_option', InvalidConfigOption()), ))
  183. errors, warnings = c.validate()
  184. self.assertEqual(len(errors), 0)
  185. self.assertEqual(warnings, [
  186. ('invalid_option', 'pre_validation warning'),
  187. ('invalid_option', 'run_validation warning'),
  188. ('invalid_option', 'post_validation warning'),
  189. ])
  190. def test_load_from_file_with_relative_paths(self):
  191. """
  192. When explicitly setting a config file, paths should be relative to the
  193. config file, not the working directory.
  194. """
  195. config_dir = TemporaryDirectory()
  196. config_fname = os.path.join(config_dir.name, 'mkdocs.yml')
  197. docs_dir = os.path.join(config_dir.name, 'src')
  198. os.mkdir(docs_dir)
  199. config_file = open(config_fname, 'w')
  200. try:
  201. config_file.write("docs_dir: src\nsite_name: MkDocs Test\n")
  202. config_file.flush()
  203. config_file.close()
  204. cfg = base.load_config(config_file=config_file)
  205. self.assertTrue(isinstance(cfg, base.Config))
  206. self.assertEqual(cfg['site_name'], 'MkDocs Test')
  207. self.assertEqual(cfg['docs_dir'], docs_dir)
  208. self.assertEqual(cfg.config_file_path, config_fname)
  209. self.assertIsInstance(cfg.config_file_path, str)
  210. finally:
  211. config_dir.cleanup()