style_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2015 Google Inc. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """Tests for yapf.style."""
  16. import os
  17. import shutil
  18. import tempfile
  19. import textwrap
  20. import unittest
  21. from yapf.yapflib import style
  22. from yapftests import utils
  23. from yapftests import yapf_test_helper
  24. class UtilsTest(yapf_test_helper.YAPFTest):
  25. def testContinuationAlignStyleStringConverter(self):
  26. for cont_align_space in ('', 'space', '"space"', '\'space\''):
  27. self.assertEqual(
  28. style._ContinuationAlignStyleStringConverter(cont_align_space),
  29. 'SPACE')
  30. for cont_align_fixed in ('fixed', '"fixed"', '\'fixed\''):
  31. self.assertEqual(
  32. style._ContinuationAlignStyleStringConverter(cont_align_fixed),
  33. 'FIXED')
  34. for cont_align_valignright in (
  35. 'valign-right',
  36. '"valign-right"',
  37. '\'valign-right\'',
  38. 'valign_right',
  39. '"valign_right"',
  40. '\'valign_right\'',
  41. ):
  42. self.assertEqual(
  43. style._ContinuationAlignStyleStringConverter(cont_align_valignright),
  44. 'VALIGN-RIGHT')
  45. with self.assertRaises(ValueError) as ctx:
  46. style._ContinuationAlignStyleStringConverter('blahblah')
  47. self.assertIn("unknown continuation align style: 'blahblah'",
  48. str(ctx.exception))
  49. def testStringListConverter(self):
  50. self.assertEqual(style._StringListConverter('foo, bar'), ['foo', 'bar'])
  51. self.assertEqual(style._StringListConverter('foo,bar'), ['foo', 'bar'])
  52. self.assertEqual(style._StringListConverter(' foo'), ['foo'])
  53. self.assertEqual(
  54. style._StringListConverter('joe ,foo, bar'), ['joe', 'foo', 'bar'])
  55. def testBoolConverter(self):
  56. self.assertEqual(style._BoolConverter('true'), True)
  57. self.assertEqual(style._BoolConverter('1'), True)
  58. self.assertEqual(style._BoolConverter('false'), False)
  59. self.assertEqual(style._BoolConverter('0'), False)
  60. def testIntListConverter(self):
  61. self.assertEqual(style._IntListConverter('1, 2, 3'), [1, 2, 3])
  62. self.assertEqual(style._IntListConverter('[ 1, 2, 3 ]'), [1, 2, 3])
  63. self.assertEqual(style._IntListConverter('[ 1, 2, 3, ]'), [1, 2, 3])
  64. def testIntOrIntListConverter(self):
  65. self.assertEqual(style._IntOrIntListConverter('10'), 10)
  66. self.assertEqual(style._IntOrIntListConverter('1, 2, 3'), [1, 2, 3])
  67. def _LooksLikeGoogleStyle(cfg):
  68. return cfg['COLUMN_LIMIT'] == 80 and cfg['SPLIT_COMPLEX_COMPREHENSION']
  69. def _LooksLikePEP8Style(cfg):
  70. return cfg['COLUMN_LIMIT'] == 79
  71. def _LooksLikeFacebookStyle(cfg):
  72. return cfg['DEDENT_CLOSING_BRACKETS']
  73. def _LooksLikeYapfStyle(cfg):
  74. return cfg['SPLIT_BEFORE_DOT']
  75. class PredefinedStylesByNameTest(yapf_test_helper.YAPFTest):
  76. @classmethod
  77. def setUpClass(cls): # pylint: disable=g-missing-super-call
  78. style.SetGlobalStyle(style.CreatePEP8Style())
  79. def testDefault(self):
  80. # default is PEP8
  81. cfg = style.CreateStyleFromConfig(None)
  82. self.assertTrue(_LooksLikePEP8Style(cfg))
  83. def testPEP8ByName(self):
  84. for pep8_name in ('PEP8', 'pep8', 'Pep8'):
  85. cfg = style.CreateStyleFromConfig(pep8_name)
  86. self.assertTrue(_LooksLikePEP8Style(cfg))
  87. def testGoogleByName(self):
  88. for google_name in ('google', 'Google', 'GOOGLE'):
  89. cfg = style.CreateStyleFromConfig(google_name)
  90. self.assertTrue(_LooksLikeGoogleStyle(cfg))
  91. def testYapfByName(self):
  92. for yapf_name in ('yapf', 'YAPF'):
  93. cfg = style.CreateStyleFromConfig(yapf_name)
  94. self.assertTrue(_LooksLikeYapfStyle(cfg))
  95. def testFacebookByName(self):
  96. for fb_name in ('facebook', 'FACEBOOK', 'Facebook'):
  97. cfg = style.CreateStyleFromConfig(fb_name)
  98. self.assertTrue(_LooksLikeFacebookStyle(cfg))
  99. class StyleFromFileTest(yapf_test_helper.YAPFTest):
  100. @classmethod
  101. def setUpClass(cls): # pylint: disable=g-missing-super-call
  102. cls.test_tmpdir = tempfile.mkdtemp()
  103. style.SetGlobalStyle(style.CreatePEP8Style())
  104. @classmethod
  105. def tearDownClass(cls): # pylint: disable=g-missing-super-call
  106. shutil.rmtree(cls.test_tmpdir)
  107. def testDefaultBasedOnStyle(self):
  108. cfg = textwrap.dedent('''\
  109. [style]
  110. continuation_indent_width = 20
  111. ''')
  112. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  113. cfg = style.CreateStyleFromConfig(filepath)
  114. self.assertTrue(_LooksLikePEP8Style(cfg))
  115. self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 20)
  116. def testDefaultBasedOnPEP8Style(self):
  117. cfg = textwrap.dedent('''\
  118. [style]
  119. based_on_style = pep8
  120. continuation_indent_width = 40
  121. ''')
  122. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  123. cfg = style.CreateStyleFromConfig(filepath)
  124. self.assertTrue(_LooksLikePEP8Style(cfg))
  125. self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 40)
  126. def testDefaultBasedOnGoogleStyle(self):
  127. cfg = textwrap.dedent('''\
  128. [style]
  129. based_on_style = google
  130. continuation_indent_width = 20
  131. ''')
  132. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  133. cfg = style.CreateStyleFromConfig(filepath)
  134. self.assertTrue(_LooksLikeGoogleStyle(cfg))
  135. self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 20)
  136. def testDefaultBasedOnFacebookStyle(self):
  137. cfg = textwrap.dedent('''\
  138. [style]
  139. based_on_style = facebook
  140. continuation_indent_width = 20
  141. ''')
  142. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  143. cfg = style.CreateStyleFromConfig(filepath)
  144. self.assertTrue(_LooksLikeFacebookStyle(cfg))
  145. self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 20)
  146. def testBoolOptionValue(self):
  147. cfg = textwrap.dedent('''\
  148. [style]
  149. based_on_style = pep8
  150. SPLIT_BEFORE_NAMED_ASSIGNS=False
  151. split_before_logical_operator = true
  152. ''')
  153. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  154. cfg = style.CreateStyleFromConfig(filepath)
  155. self.assertTrue(_LooksLikePEP8Style(cfg))
  156. self.assertEqual(cfg['SPLIT_BEFORE_NAMED_ASSIGNS'], False)
  157. self.assertEqual(cfg['SPLIT_BEFORE_LOGICAL_OPERATOR'], True)
  158. def testStringListOptionValue(self):
  159. cfg = textwrap.dedent('''\
  160. [style]
  161. based_on_style = pep8
  162. I18N_FUNCTION_CALL = N_, V_, T_
  163. ''')
  164. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  165. cfg = style.CreateStyleFromConfig(filepath)
  166. self.assertTrue(_LooksLikePEP8Style(cfg))
  167. self.assertEqual(cfg['I18N_FUNCTION_CALL'], ['N_', 'V_', 'T_'])
  168. def testErrorNoStyleFile(self):
  169. with self.assertRaisesRegex(style.StyleConfigError,
  170. 'is not a valid style or file path'):
  171. style.CreateStyleFromConfig('/8822/xyznosuchfile')
  172. def testErrorNoStyleSection(self):
  173. cfg = textwrap.dedent('''\
  174. [s]
  175. indent_width=2
  176. ''')
  177. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  178. with self.assertRaisesRegex(style.StyleConfigError,
  179. 'Unable to find section'):
  180. style.CreateStyleFromConfig(filepath)
  181. def testErrorUnknownStyleOption(self):
  182. cfg = textwrap.dedent('''\
  183. [style]
  184. indent_width=2
  185. hummus=2
  186. ''')
  187. with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
  188. with self.assertRaisesRegex(style.StyleConfigError,
  189. 'Unknown style option'):
  190. style.CreateStyleFromConfig(filepath)
  191. def testPyprojectTomlNoYapfSection(self):
  192. try:
  193. import tomli # noqa: F401
  194. except ImportError:
  195. return
  196. filepath = os.path.join(self.test_tmpdir, 'pyproject.toml')
  197. _ = open(filepath, 'w')
  198. with self.assertRaisesRegex(style.StyleConfigError,
  199. 'Unable to find section'):
  200. style.CreateStyleFromConfig(filepath)
  201. def testPyprojectTomlParseYapfSection(self):
  202. try:
  203. import tomli # noqa: F401
  204. except ImportError:
  205. return
  206. cfg = textwrap.dedent('''\
  207. [tool.yapf]
  208. based_on_style = "pep8"
  209. continuation_indent_width = 40
  210. ''')
  211. filepath = os.path.join(self.test_tmpdir, 'pyproject.toml')
  212. with open(filepath, 'w') as f:
  213. f.write(cfg)
  214. cfg = style.CreateStyleFromConfig(filepath)
  215. self.assertTrue(_LooksLikePEP8Style(cfg))
  216. self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 40)
  217. class StyleFromDict(yapf_test_helper.YAPFTest):
  218. @classmethod
  219. def setUpClass(cls): # pylint: disable=g-missing-super-call
  220. style.SetGlobalStyle(style.CreatePEP8Style())
  221. def testDefaultBasedOnStyle(self):
  222. config_dict = {
  223. 'based_on_style': 'pep8',
  224. 'indent_width': 2,
  225. 'blank_line_before_nested_class_or_def': True
  226. }
  227. cfg = style.CreateStyleFromConfig(config_dict)
  228. self.assertTrue(_LooksLikePEP8Style(cfg))
  229. self.assertEqual(cfg['INDENT_WIDTH'], 2)
  230. def testDefaultBasedOnStyleBadDict(self):
  231. self.assertRaisesRegex(style.StyleConfigError, 'Unknown style option',
  232. style.CreateStyleFromConfig,
  233. {'based_on_styl': 'pep8'})
  234. self.assertRaisesRegex(style.StyleConfigError, 'not a valid',
  235. style.CreateStyleFromConfig,
  236. {'INDENT_WIDTH': 'FOUR'})
  237. class StyleFromCommandLine(yapf_test_helper.YAPFTest):
  238. @classmethod
  239. def setUpClass(cls): # pylint: disable=g-missing-super-call
  240. style.SetGlobalStyle(style.CreatePEP8Style())
  241. def testDefaultBasedOnStyle(self):
  242. cfg = style.CreateStyleFromConfig(
  243. '{based_on_style: pep8,'
  244. ' indent_width: 2,'
  245. ' blank_line_before_nested_class_or_def: True}')
  246. self.assertTrue(_LooksLikePEP8Style(cfg))
  247. self.assertEqual(cfg['INDENT_WIDTH'], 2)
  248. def testDefaultBasedOnStyleNotStrict(self):
  249. cfg = style.CreateStyleFromConfig(
  250. '{based_on_style : pep8'
  251. ' ,indent_width=2'
  252. ' blank_line_before_nested_class_or_def:True}')
  253. self.assertTrue(_LooksLikePEP8Style(cfg))
  254. self.assertEqual(cfg['INDENT_WIDTH'], 2)
  255. def testDefaultBasedOnExplicitlyUnicodeTypeString(self):
  256. cfg = style.CreateStyleFromConfig('{}')
  257. self.assertIsInstance(cfg, dict)
  258. def testDefaultBasedOnDetaultTypeString(self):
  259. cfg = style.CreateStyleFromConfig('{}')
  260. self.assertIsInstance(cfg, dict)
  261. def testDefaultBasedOnStyleBadString(self):
  262. self.assertRaisesRegex(style.StyleConfigError, 'Unknown style option',
  263. style.CreateStyleFromConfig, '{based_on_styl: pep8}')
  264. self.assertRaisesRegex(style.StyleConfigError, 'not a valid',
  265. style.CreateStyleFromConfig, '{INDENT_WIDTH: FOUR}')
  266. self.assertRaisesRegex(style.StyleConfigError, 'Invalid style dict',
  267. style.CreateStyleFromConfig, '{based_on_style: pep8')
  268. class StyleHelp(yapf_test_helper.YAPFTest):
  269. def testHelpKeys(self):
  270. settings = sorted(style.Help())
  271. expected = sorted(style._style)
  272. self.assertListEqual(settings, expected)
  273. if __name__ == '__main__':
  274. unittest.main()