xref: /aosp_15_r20/external/yapf/yapftests/style_test.py (revision 7249d1a64f4850ccf838e62a46276f891f72998e)
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(u'''\
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(u'''\
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(u'''\
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(u'''\
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(u'''\
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(u'''\
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(u'''\
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(u'''\
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    try:
233      import toml
234    except ImportError:
235      return
236
237    filepath = os.path.join(self.test_tmpdir, 'pyproject.toml')
238    _ = open(filepath, 'w')
239    with self.assertRaisesRegex(style.StyleConfigError,
240                                'Unable to find section'):
241      style.CreateStyleFromConfig(filepath)
242
243  def testPyprojectTomlParseYapfSection(self):
244    try:
245      import toml
246    except ImportError:
247      return
248
249    cfg = textwrap.dedent(u'''\
250        [tool.yapf]
251        based_on_style = "pep8"
252        continuation_indent_width = 40
253        ''')
254    filepath = os.path.join(self.test_tmpdir, 'pyproject.toml')
255    with open(filepath, 'w') as f:
256      f.write(cfg)
257    cfg = style.CreateStyleFromConfig(filepath)
258    self.assertTrue(_LooksLikePEP8Style(cfg))
259    self.assertEqual(cfg['CONTINUATION_INDENT_WIDTH'], 40)
260
261
262class StyleFromDict(yapf_test_helper.YAPFTest):
263
264  @classmethod
265  def setUpClass(cls):  # pylint: disable=g-missing-super-call
266    style.SetGlobalStyle(style.CreatePEP8Style())
267
268  def testDefaultBasedOnStyle(self):
269    config_dict = {
270        'based_on_style': 'pep8',
271        'indent_width': 2,
272        'blank_line_before_nested_class_or_def': True
273    }
274    cfg = style.CreateStyleFromConfig(config_dict)
275    self.assertTrue(_LooksLikePEP8Style(cfg))
276    self.assertEqual(cfg['INDENT_WIDTH'], 2)
277
278  def testDefaultBasedOnStyleBadDict(self):
279    self.assertRaisesRegex(style.StyleConfigError, 'Unknown style option',
280                           style.CreateStyleFromConfig,
281                           {'based_on_styl': 'pep8'})
282    self.assertRaisesRegex(style.StyleConfigError, 'not a valid',
283                           style.CreateStyleFromConfig,
284                           {'INDENT_WIDTH': 'FOUR'})
285
286
287class StyleFromCommandLine(yapf_test_helper.YAPFTest):
288
289  @classmethod
290  def setUpClass(cls):  # pylint: disable=g-missing-super-call
291    style.SetGlobalStyle(style.CreatePEP8Style())
292
293  def testDefaultBasedOnStyle(self):
294    cfg = style.CreateStyleFromConfig(
295        '{based_on_style: pep8,'
296        ' indent_width: 2,'
297        ' blank_line_before_nested_class_or_def: True}')
298    self.assertTrue(_LooksLikePEP8Style(cfg))
299    self.assertEqual(cfg['INDENT_WIDTH'], 2)
300
301  def testDefaultBasedOnStyleNotStrict(self):
302    cfg = style.CreateStyleFromConfig(
303        '{based_on_style : pep8'
304        ' ,indent_width=2'
305        ' blank_line_before_nested_class_or_def:True}')
306    self.assertTrue(_LooksLikePEP8Style(cfg))
307    self.assertEqual(cfg['INDENT_WIDTH'], 2)
308
309  def testDefaultBasedOnExplicitlyUnicodeTypeString(self):
310    cfg = style.CreateStyleFromConfig(u'{}')
311    self.assertIsInstance(cfg, dict)
312
313  def testDefaultBasedOnDetaultTypeString(self):
314    cfg = style.CreateStyleFromConfig('{}')
315    self.assertIsInstance(cfg, dict)
316
317  def testDefaultBasedOnStyleBadString(self):
318    self.assertRaisesRegex(style.StyleConfigError, 'Unknown style option',
319                           style.CreateStyleFromConfig, '{based_on_styl: pep8}')
320    self.assertRaisesRegex(style.StyleConfigError, 'not a valid',
321                           style.CreateStyleFromConfig, '{INDENT_WIDTH: FOUR}')
322    self.assertRaisesRegex(style.StyleConfigError, 'Invalid style dict',
323                           style.CreateStyleFromConfig, '{based_on_style: pep8')
324
325
326class StyleHelp(yapf_test_helper.YAPFTest):
327
328  def testHelpKeys(self):
329    settings = sorted(style.Help())
330    expected = sorted(style._style)
331    self.assertListEqual(settings, expected)
332
333
334if __name__ == '__main__':
335  unittest.main()
336