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