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