• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17import os
18import shutil
19import tempfile
20import textwrap
21import unittest
22
23from yapf.yapflib import style
24
25from yapftests import utils
26from yapftests import yapf_test_helper
27
28
29class UtilsTest(yapf_test_helper.YAPFTest):
30
31  def testContinuationAlignStyleStringConverter(self):
32    for cont_align_space in ('', 'space', '"space"', '\'space\''):
33      self.assertEqual(
34          style._ContinuationAlignStyleStringConverter(cont_align_space),
35          'SPACE')
36    for cont_align_fixed in ('fixed', '"fixed"', '\'fixed\''):
37      self.assertEqual(
38          style._ContinuationAlignStyleStringConverter(cont_align_fixed),
39          'FIXED')
40    for cont_align_valignright in (
41        'valign-right',
42        '"valign-right"',
43        '\'valign-right\'',
44        'valign_right',
45        '"valign_right"',
46        '\'valign_right\'',
47    ):
48      self.assertEqual(
49          style._ContinuationAlignStyleStringConverter(cont_align_valignright),
50          'VALIGN-RIGHT')
51    with self.assertRaises(ValueError) as ctx:
52      style._ContinuationAlignStyleStringConverter('blahblah')
53    self.assertIn("unknown continuation align style: 'blahblah'",
54                  str(ctx.exception))
55
56  def testStringListConverter(self):
57    self.assertEqual(style._StringListConverter('foo, bar'), ['foo', 'bar'])
58    self.assertEqual(style._StringListConverter('foo,bar'), ['foo', 'bar'])
59    self.assertEqual(style._StringListConverter('  foo'), ['foo'])
60    self.assertEqual(
61        style._StringListConverter('joe  ,foo,  bar'), ['joe', 'foo', 'bar'])
62
63  def testBoolConverter(self):
64    self.assertEqual(style._BoolConverter('true'), True)
65    self.assertEqual(style._BoolConverter('1'), True)
66    self.assertEqual(style._BoolConverter('false'), False)
67    self.assertEqual(style._BoolConverter('0'), False)
68
69  def testIntListConverter(self):
70    self.assertEqual(style._IntListConverter('1, 2, 3'), [1, 2, 3])
71    self.assertEqual(style._IntListConverter('[ 1, 2, 3 ]'), [1, 2, 3])
72    self.assertEqual(style._IntListConverter('[ 1, 2, 3, ]'), [1, 2, 3])
73
74  def testIntOrIntListConverter(self):
75    self.assertEqual(style._IntOrIntListConverter('10'), 10)
76    self.assertEqual(style._IntOrIntListConverter('1, 2, 3'), [1, 2, 3])
77
78
79def _LooksLikeGoogleStyle(cfg):
80  return cfg['COLUMN_LIMIT'] == 80 and cfg['SPLIT_COMPLEX_COMPREHENSION']
81
82
83def _LooksLikePEP8Style(cfg):
84  return cfg['COLUMN_LIMIT'] == 79
85
86
87def _LooksLikeFacebookStyle(cfg):
88  return cfg['DEDENT_CLOSING_BRACKETS']
89
90
91def _LooksLikeYapfStyle(cfg):
92  return cfg['SPLIT_BEFORE_DOT']
93
94
95class PredefinedStylesByNameTest(yapf_test_helper.YAPFTest):
96
97  @classmethod
98  def setUpClass(cls):  # pylint: disable=g-missing-super-call
99    style.SetGlobalStyle(style.CreatePEP8Style())
100
101  def testDefault(self):
102    # default is PEP8
103    cfg = style.CreateStyleFromConfig(None)
104    self.assertTrue(_LooksLikePEP8Style(cfg))
105
106  def testPEP8ByName(self):
107    for pep8_name in ('PEP8', 'pep8', 'Pep8'):
108      cfg = style.CreateStyleFromConfig(pep8_name)
109      self.assertTrue(_LooksLikePEP8Style(cfg))
110
111  def testGoogleByName(self):
112    for google_name in ('google', 'Google', 'GOOGLE'):
113      cfg = style.CreateStyleFromConfig(google_name)
114      self.assertTrue(_LooksLikeGoogleStyle(cfg))
115
116  def testYapfByName(self):
117    for yapf_name in ('yapf', 'YAPF'):
118      cfg = style.CreateStyleFromConfig(yapf_name)
119      self.assertTrue(_LooksLikeYapfStyle(cfg))
120
121  def testFacebookByName(self):
122    for fb_name in ('facebook', 'FACEBOOK', 'Facebook'):
123      cfg = style.CreateStyleFromConfig(fb_name)
124      self.assertTrue(_LooksLikeFacebookStyle(cfg))
125
126
127class StyleFromFileTest(yapf_test_helper.YAPFTest):
128
129  @classmethod
130  def setUpClass(cls):  # pylint: disable=g-missing-super-call
131    cls.test_tmpdir = tempfile.mkdtemp()
132    style.SetGlobalStyle(style.CreatePEP8Style())
133
134  @classmethod
135  def tearDownClass(cls):  # pylint: disable=g-missing-super-call
136    shutil.rmtree(cls.test_tmpdir)
137
138  def testDefaultBasedOnStyle(self):
139    cfg = textwrap.dedent("""\
140        [style]
141        continuation_indent_width = 20
142    """)
143    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
144      cfg = style.CreateStyleFromConfig(filepath)
145      self.assertTrue(_LooksLikePEP8Style(cfg))
146      self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 20)
147
148  def testDefaultBasedOnPEP8Style(self):
149    cfg = textwrap.dedent("""\
150        [style]
151        based_on_style = pep8
152        continuation_indent_width = 40
153    """)
154    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
155      cfg = style.CreateStyleFromConfig(filepath)
156      self.assertTrue(_LooksLikePEP8Style(cfg))
157      self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 40)
158
159  def testDefaultBasedOnGoogleStyle(self):
160    cfg = textwrap.dedent("""\
161        [style]
162        based_on_style = google
163        continuation_indent_width = 20
164    """)
165    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
166      cfg = style.CreateStyleFromConfig(filepath)
167      self.assertTrue(_LooksLikeGoogleStyle(cfg))
168      self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 20)
169
170  def testDefaultBasedOnFacebookStyle(self):
171    cfg = textwrap.dedent("""\
172        [style]
173        based_on_style = facebook
174        continuation_indent_width = 20
175    """)
176    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
177      cfg = style.CreateStyleFromConfig(filepath)
178      self.assertTrue(_LooksLikeFacebookStyle(cfg))
179      self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 20)
180
181  def testBoolOptionValue(self):
182    cfg = textwrap.dedent("""\
183        [style]
184        based_on_style = pep8
185        SPLIT_BEFORE_NAMED_ASSIGNS=False
186        split_before_logical_operator = true
187    """)
188    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
189      cfg = style.CreateStyleFromConfig(filepath)
190      self.assertTrue(_LooksLikePEP8Style(cfg))
191      self.assertEqual(cfg['SPLIT_BEFORE_NAMED_ASSIGNS'], False)
192      self.assertEqual(cfg['SPLIT_BEFORE_LOGICAL_OPERATOR'], True)
193
194  def testStringListOptionValue(self):
195    cfg = textwrap.dedent("""\
196        [style]
197        based_on_style = pep8
198        I18N_FUNCTION_CALL = N_, V_, T_
199    """)
200    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
201      cfg = style.CreateStyleFromConfig(filepath)
202      self.assertTrue(_LooksLikePEP8Style(cfg))
203      self.assertEqual(cfg['I18N_FUNCTION_CALL'], ['N_', 'V_', 'T_'])
204
205  def testErrorNoStyleFile(self):
206    with self.assertRaisesRegex(style.StyleConfigError,
207                                'is not a valid style or file path'):
208      style.CreateStyleFromConfig('/8822/xyznosuchfile')
209
210  def testErrorNoStyleSection(self):
211    cfg = textwrap.dedent("""\
212        [s]
213        indent_width=2
214    """)
215    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
216      with self.assertRaisesRegex(style.StyleConfigError,
217                                  'Unable to find section'):
218        style.CreateStyleFromConfig(filepath)
219
220  def testErrorUnknownStyleOption(self):
221    cfg = textwrap.dedent("""\
222        [style]
223        indent_width=2
224        hummus=2
225    """)
226    with utils.TempFileContents(self.test_tmpdir, cfg) as filepath:
227      with self.assertRaisesRegex(style.StyleConfigError,
228                                  'Unknown style option'):
229        style.CreateStyleFromConfig(filepath)
230
231  def testPyprojectTomlNoYapfSection(self):
232    filepath = os.path.join(self.test_tmpdir, 'pyproject.toml')
233    _ = open(filepath, 'w')
234    with self.assertRaisesRegex(style.StyleConfigError,
235                                'Unable to find section'):
236      style.CreateStyleFromConfig(filepath)
237
238  def testPyprojectTomlParseYapfSection(self):
239
240    cfg = textwrap.dedent("""\
241        [tool.yapf]
242        based_on_style = "pep8"
243        continuation_indent_width = 40
244    """)
245    filepath = os.path.join(self.test_tmpdir, 'pyproject.toml')
246    with open(filepath, 'w') as f:
247      f.write(cfg)
248    cfg = style.CreateStyleFromConfig(filepath)
249    self.assertTrue(_LooksLikePEP8Style(cfg))
250    self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 40)
251
252
253class StyleFromDict(yapf_test_helper.YAPFTest):
254
255  @classmethod
256  def setUpClass(cls):  # pylint: disable=g-missing-super-call
257    style.SetGlobalStyle(style.CreatePEP8Style())
258
259  def testDefaultBasedOnStyle(self):
260    config_dict = {
261        'based_on_style': 'pep8',
262        'indent_width': 2,
263        'blank_line_before_nested_class_or_def': True
264    }
265    cfg = style.CreateStyleFromConfig(config_dict)
266    self.assertTrue(_LooksLikePEP8Style(cfg))
267    self.assertEqual(cfg['INDENT_WIDTH'], 2)
268
269  def testDefaultBasedOnStyleBadDict(self):
270    self.assertRaisesRegex(style.StyleConfigError, 'Unknown style option',
271                           style.CreateStyleFromConfig,
272                           {'based_on_styl': 'pep8'})
273    self.assertRaisesRegex(style.StyleConfigError, 'not a valid',
274                           style.CreateStyleFromConfig,
275                           {'INDENT_WIDTH': 'FOUR'})
276
277
278class StyleFromCommandLine(yapf_test_helper.YAPFTest):
279
280  @classmethod
281  def setUpClass(cls):  # pylint: disable=g-missing-super-call
282    style.SetGlobalStyle(style.CreatePEP8Style())
283
284  def testDefaultBasedOnStyle(self):
285    cfg = style.CreateStyleFromConfig(
286        '{based_on_style: pep8,'
287        ' indent_width: 2,'
288        ' blank_line_before_nested_class_or_def: True}')
289    self.assertTrue(_LooksLikePEP8Style(cfg))
290    self.assertEqual(cfg['INDENT_WIDTH'], 2)
291
292  def testDefaultBasedOnStyleNotStrict(self):
293    cfg = style.CreateStyleFromConfig(
294        '{based_on_style : pep8,'
295        ' indent_width=2'
296        ' blank_line_before_nested_class_or_def:True}')
297    self.assertTrue(_LooksLikePEP8Style(cfg))
298    self.assertEqual(cfg['INDENT_WIDTH'], 2)
299
300  def testDefaultBasedOnExplicitlyUnicodeTypeString(self):
301    cfg = style.CreateStyleFromConfig('{}')
302    self.assertIsInstance(cfg, dict)
303
304  def testDefaultBasedOnDetaultTypeString(self):
305    cfg = style.CreateStyleFromConfig('{}')
306    self.assertIsInstance(cfg, dict)
307
308  def testDefaultBasedOnStyleBadString(self):
309    self.assertRaisesRegex(style.StyleConfigError, 'Unknown style option',
310                           style.CreateStyleFromConfig, '{based_on_styl: pep8}')
311    self.assertRaisesRegex(style.StyleConfigError, 'not a valid',
312                           style.CreateStyleFromConfig, '{INDENT_WIDTH: FOUR}')
313    self.assertRaisesRegex(style.StyleConfigError, 'Invalid style dict',
314                           style.CreateStyleFromConfig, '{based_on_style: pep8')
315
316
317class StyleHelp(yapf_test_helper.YAPFTest):
318
319  def testHelpKeys(self):
320    settings = sorted(style.Help())
321    expected = sorted(style._style)
322    self.assertListEqual(settings, expected)
323
324
325if __name__ == '__main__':
326  unittest.main()
327