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