1 # -*- coding: utf-8 -*-
2 # Copyright 2015 Google Inc. All Rights Reserved.
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.file_resources."""
23 from io
import BytesIO
25 from yapf
.yapflib
import errors
26 from yapf
.yapflib
import file_resources
28 from yapftests
import utils
29 from yapftests
import yapf_test_helper
32 @contextlib.contextmanager
33 def _restore_working_dir():
41 @contextlib.contextmanager
42 def _exists_mocked_in_module(module
, mock_implementation
):
43 unmocked_exists
= getattr(module
, 'exists')
44 setattr(module
, 'exists', mock_implementation
)
48 setattr(module
, 'exists', unmocked_exists
)
51 class GetExcludePatternsForDir(yapf_test_helper
.YAPFTest
):
53 def setUp(self
): # pylint: disable=g-missing-super-call
54 self
.test_tmpdir
= tempfile
.mkdtemp()
56 def tearDown(self
): # pylint: disable=g-missing-super-call
57 shutil
.rmtree(self
.test_tmpdir
)
59 def test_get_exclude_file_patterns_from_yapfignore(self
):
60 local_ignore_file
= os
.path
.join(self
.test_tmpdir
, '.yapfignore')
61 ignore_patterns
= ['temp/**/*.py', 'temp2/*.py']
62 with
open(local_ignore_file
, 'w') as f
:
63 f
.writelines('\n'.join(ignore_patterns
))
66 sorted(file_resources
.GetExcludePatternsForDir(self
.test_tmpdir
)),
67 sorted(ignore_patterns
))
69 def test_get_exclude_file_patterns_from_yapfignore_with_wrong_syntax(self
):
70 local_ignore_file
= os
.path
.join(self
.test_tmpdir
, '.yapfignore')
71 ignore_patterns
= ['temp/**/*.py', './wrong/syntax/*.py']
72 with
open(local_ignore_file
, 'w') as f
:
73 f
.writelines('\n'.join(ignore_patterns
))
75 with self
.assertRaises(errors
.YapfError
):
76 file_resources
.GetExcludePatternsForDir(self
.test_tmpdir
)
78 def test_get_exclude_file_patterns_from_pyproject(self
):
83 local_ignore_file
= os
.path
.join(self
.test_tmpdir
, 'pyproject.toml')
84 ignore_patterns
= ['temp/**/*.py', 'temp2/*.py']
85 with
open(local_ignore_file
, 'w') as f
:
86 f
.write('[tool.yapfignore]\n')
87 f
.write('ignore_patterns=[')
88 f
.writelines('\n,'.join(['"{}"'.format(p
) for p
in ignore_patterns
]))
92 sorted(file_resources
.GetExcludePatternsForDir(self
.test_tmpdir
)),
93 sorted(ignore_patterns
))
95 def test_get_exclude_file_patterns_from_pyproject_no_ignore_section(self
):
100 local_ignore_file
= os
.path
.join(self
.test_tmpdir
, 'pyproject.toml')
102 open(local_ignore_file
, 'w').close()
105 sorted(file_resources
.GetExcludePatternsForDir(self
.test_tmpdir
)),
106 sorted(ignore_patterns
))
108 def test_get_exclude_file_patterns_from_pyproject_ignore_section_empty(self
):
113 local_ignore_file
= os
.path
.join(self
.test_tmpdir
, 'pyproject.toml')
115 with
open(local_ignore_file
, 'w') as f
:
116 f
.write('[tool.yapfignore]\n')
119 sorted(file_resources
.GetExcludePatternsForDir(self
.test_tmpdir
)),
120 sorted(ignore_patterns
))
122 def test_get_exclude_file_patterns_with_no_config_files(self
):
126 sorted(file_resources
.GetExcludePatternsForDir(self
.test_tmpdir
)),
127 sorted(ignore_patterns
))
130 class GetDefaultStyleForDirTest(yapf_test_helper
.YAPFTest
):
132 def setUp(self
): # pylint: disable=g-missing-super-call
133 self
.test_tmpdir
= tempfile
.mkdtemp()
135 def tearDown(self
): # pylint: disable=g-missing-super-call
136 shutil
.rmtree(self
.test_tmpdir
)
138 def test_no_local_style(self
):
139 test_file
= os
.path
.join(self
.test_tmpdir
, 'file.py')
140 style_name
= file_resources
.GetDefaultStyleForDir(test_file
)
141 self
.assertEqual(style_name
, 'pep8')
143 def test_no_local_style_custom_default(self
):
144 test_file
= os
.path
.join(self
.test_tmpdir
, 'file.py')
145 style_name
= file_resources
.GetDefaultStyleForDir(
146 test_file
, default_style
='custom-default')
147 self
.assertEqual(style_name
, 'custom-default')
149 def test_with_local_style(self
):
150 # Create an empty .style.yapf file in test_tmpdir
151 style_file
= os
.path
.join(self
.test_tmpdir
, '.style.yapf')
152 open(style_file
, 'w').close()
154 test_filename
= os
.path
.join(self
.test_tmpdir
, 'file.py')
155 self
.assertEqual(style_file
,
156 file_resources
.GetDefaultStyleForDir(test_filename
))
158 test_filename
= os
.path
.join(self
.test_tmpdir
, 'dir1', 'file.py')
159 self
.assertEqual(style_file
,
160 file_resources
.GetDefaultStyleForDir(test_filename
))
162 def test_setup_config(self
):
163 # An empty setup.cfg file should not be used
164 setup_config
= os
.path
.join(self
.test_tmpdir
, 'setup.cfg')
165 open(setup_config
, 'w').close()
167 test_dir
= os
.path
.join(self
.test_tmpdir
, 'dir1')
168 style_name
= file_resources
.GetDefaultStyleForDir(test_dir
)
169 self
.assertEqual(style_name
, 'pep8')
171 # One with a '[yapf]' section should be used
172 with
open(setup_config
, 'w') as f
:
174 self
.assertEqual(setup_config
,
175 file_resources
.GetDefaultStyleForDir(test_dir
))
177 def test_pyproject_toml(self
):
178 # An empty pyproject.toml file should not be used
184 pyproject_toml
= os
.path
.join(self
.test_tmpdir
, 'pyproject.toml')
185 open(pyproject_toml
, 'w').close()
187 test_dir
= os
.path
.join(self
.test_tmpdir
, 'dir1')
188 style_name
= file_resources
.GetDefaultStyleForDir(test_dir
)
189 self
.assertEqual(style_name
, 'pep8')
191 # One with a '[tool.yapf]' section should be used
192 with
open(pyproject_toml
, 'w') as f
:
193 f
.write('[tool.yapf]\n')
194 self
.assertEqual(pyproject_toml
,
195 file_resources
.GetDefaultStyleForDir(test_dir
))
197 def test_local_style_at_root(self
):
198 # Test behavior of files located on the root, and under root.
199 rootdir
= os
.path
.abspath(os
.path
.sep
)
200 test_dir_at_root
= os
.path
.join(rootdir
, 'dir1')
201 test_dir_under_root
= os
.path
.join(rootdir
, 'dir1', 'dir2')
203 # Fake placing only a style file at the root by mocking `os.path.exists`.
204 style_file
= os
.path
.join(rootdir
, '.style.yapf')
206 def mock_exists_implementation(path
):
207 return path
== style_file
209 with
_exists_mocked_in_module(file_resources
.os
.path
,
210 mock_exists_implementation
):
211 # Both files should find the style file at the root.
212 default_style_at_root
= file_resources
.GetDefaultStyleForDir(
214 self
.assertEqual(style_file
, default_style_at_root
)
215 default_style_under_root
= file_resources
.GetDefaultStyleForDir(
217 self
.assertEqual(style_file
, default_style_under_root
)
220 def _touch_files(filenames
):
221 for name
in filenames
:
222 open(name
, 'a').close()
225 class GetCommandLineFilesTest(yapf_test_helper
.YAPFTest
):
227 def setUp(self
): # pylint: disable=g-missing-super-call
228 self
.test_tmpdir
= tempfile
.mkdtemp()
229 self
.old_dir
= os
.getcwd()
231 def tearDown(self
): # pylint: disable=g-missing-super-call
232 os
.chdir(self
.old_dir
)
233 shutil
.rmtree(self
.test_tmpdir
)
235 def _make_test_dir(self
, name
):
236 fullpath
= os
.path
.normpath(os
.path
.join(self
.test_tmpdir
, name
))
237 os
.makedirs(fullpath
)
240 def test_find_files_not_dirs(self
):
241 tdir1
= self
._make
_test
_dir
('test1')
242 tdir2
= self
._make
_test
_dir
('test2')
243 file1
= os
.path
.join(tdir1
, 'testfile1.py')
244 file2
= os
.path
.join(tdir2
, 'testfile2.py')
245 _touch_files([file1
, file2
])
248 file_resources
.GetCommandLineFiles([file1
, file2
],
250 exclude
=None), [file1
, file2
])
252 file_resources
.GetCommandLineFiles([file1
, file2
],
254 exclude
=None), [file1
, file2
])
256 def test_nonrecursive_find_in_dir(self
):
257 tdir1
= self
._make
_test
_dir
('test1')
258 tdir2
= self
._make
_test
_dir
('test1/foo')
259 file1
= os
.path
.join(tdir1
, 'testfile1.py')
260 file2
= os
.path
.join(tdir2
, 'testfile2.py')
261 _touch_files([file1
, file2
])
265 file_resources
.GetCommandLineFiles
,
266 command_line_file_list
=[tdir1
],
270 def test_recursive_find_in_dir(self
):
271 tdir1
= self
._make
_test
_dir
('test1')
272 tdir2
= self
._make
_test
_dir
('test2/testinner/')
273 tdir3
= self
._make
_test
_dir
('test3/foo/bar/bas/xxx')
275 os
.path
.join(tdir1
, 'testfile1.py'),
276 os
.path
.join(tdir2
, 'testfile2.py'),
277 os
.path
.join(tdir3
, 'testfile3.py'),
283 file_resources
.GetCommandLineFiles([self
.test_tmpdir
],
285 exclude
=None)), sorted(files
))
287 def test_recursive_find_in_dir_with_exclude(self
):
288 tdir1
= self
._make
_test
_dir
('test1')
289 tdir2
= self
._make
_test
_dir
('test2/testinner/')
290 tdir3
= self
._make
_test
_dir
('test3/foo/bar/bas/xxx')
292 os
.path
.join(tdir1
, 'testfile1.py'),
293 os
.path
.join(tdir2
, 'testfile2.py'),
294 os
.path
.join(tdir3
, 'testfile3.py'),
300 file_resources
.GetCommandLineFiles([self
.test_tmpdir
],
302 exclude
=['*test*3.py'])),
304 os
.path
.join(tdir1
, 'testfile1.py'),
305 os
.path
.join(tdir2
, 'testfile2.py'),
308 def test_find_with_excluded_hidden_dirs(self
):
309 tdir1
= self
._make
_test
_dir
('.test1')
310 tdir2
= self
._make
_test
_dir
('test_2')
311 tdir3
= self
._make
_test
_dir
('test.3')
313 os
.path
.join(tdir1
, 'testfile1.py'),
314 os
.path
.join(tdir2
, 'testfile2.py'),
315 os
.path
.join(tdir3
, 'testfile3.py'),
319 actual
= file_resources
.GetCommandLineFiles([self
.test_tmpdir
],
321 exclude
=['*.test1*'])
326 os
.path
.join(tdir2
, 'testfile2.py'),
327 os
.path
.join(tdir3
, 'testfile3.py'),
330 def test_find_with_excluded_hidden_dirs_relative(self
):
331 """Test find with excluded hidden dirs.
333 A regression test against a specific case where a hidden directory (one
334 beginning with a period) is being excluded, but it is also an immediate
335 child of the current directory which has been specified in a relative
338 At its core, the bug has to do with overzealous stripping of "./foo" so that
339 it removes too much from "./.foo" .
341 tdir1
= self
._make
_test
_dir
('.test1')
342 tdir2
= self
._make
_test
_dir
('test_2')
343 tdir3
= self
._make
_test
_dir
('test.3')
345 os
.path
.join(tdir1
, 'testfile1.py'),
346 os
.path
.join(tdir2
, 'testfile2.py'),
347 os
.path
.join(tdir3
, 'testfile3.py'),
351 # We must temporarily change the current directory, so that we test against
352 # patterns like ./.test1/file instead of /tmp/foo/.test1/file
353 with
_restore_working_dir():
355 os
.chdir(self
.test_tmpdir
)
356 actual
= file_resources
.GetCommandLineFiles(
357 [os
.path
.relpath(self
.test_tmpdir
)],
359 exclude
=['*.test1*'])
365 os
.path
.relpath(self
.test_tmpdir
), os
.path
.basename(tdir2
),
368 os
.path
.relpath(self
.test_tmpdir
), os
.path
.basename(tdir3
),
372 def test_find_with_excluded_dirs(self
):
373 tdir1
= self
._make
_test
_dir
('test1')
374 tdir2
= self
._make
_test
_dir
('test2/testinner/')
375 tdir3
= self
._make
_test
_dir
('test3/foo/bar/bas/xxx')
377 os
.path
.join(tdir1
, 'testfile1.py'),
378 os
.path
.join(tdir2
, 'testfile2.py'),
379 os
.path
.join(tdir3
, 'testfile3.py'),
383 os
.chdir(self
.test_tmpdir
)
386 file_resources
.GetCommandLineFiles(['test1', 'test2', 'test3'],
394 found
, ['test3/foo/bar/bas/xxx/testfile3.py'.replace('/', os
.path
.sep
)])
397 file_resources
.GetCommandLineFiles(['.'],
405 found
, ['./test2/testinner/testfile2.py'.replace('/', os
.path
.sep
)])
407 def test_find_with_excluded_current_dir(self
):
408 with self
.assertRaises(errors
.YapfError
):
409 file_resources
.GetCommandLineFiles([], False, exclude
=['./z'])
412 class IsPythonFileTest(yapf_test_helper
.YAPFTest
):
414 def setUp(self
): # pylint: disable=g-missing-super-call
415 self
.test_tmpdir
= tempfile
.mkdtemp()
417 def tearDown(self
): # pylint: disable=g-missing-super-call
418 shutil
.rmtree(self
.test_tmpdir
)
420 def test_with_py_extension(self
):
421 file1
= os
.path
.join(self
.test_tmpdir
, 'testfile1.py')
422 self
.assertTrue(file_resources
.IsPythonFile(file1
))
424 def test_empty_without_py_extension(self
):
425 file1
= os
.path
.join(self
.test_tmpdir
, 'testfile1')
426 self
.assertFalse(file_resources
.IsPythonFile(file1
))
427 file2
= os
.path
.join(self
.test_tmpdir
, 'testfile1.rb')
428 self
.assertFalse(file_resources
.IsPythonFile(file2
))
430 def test_python_shebang(self
):
431 file1
= os
.path
.join(self
.test_tmpdir
, 'testfile1')
432 with
open(file1
, 'w') as f
:
433 f
.write('#!/usr/bin/python\n')
434 self
.assertTrue(file_resources
.IsPythonFile(file1
))
436 file2
= os
.path
.join(self
.test_tmpdir
, 'testfile2.run')
437 with
open(file2
, 'w') as f
:
438 f
.write('#! /bin/python2\n')
439 self
.assertTrue(file_resources
.IsPythonFile(file1
))
441 def test_with_latin_encoding(self
):
442 file1
= os
.path
.join(self
.test_tmpdir
, 'testfile1')
443 with codecs
.open(file1
, mode
='w', encoding
='latin-1') as f
:
444 f
.write('#! /bin/python2\n')
445 self
.assertTrue(file_resources
.IsPythonFile(file1
))
447 def test_with_invalid_encoding(self
):
448 file1
= os
.path
.join(self
.test_tmpdir
, 'testfile1')
449 with
open(file1
, 'w') as f
:
450 f
.write('#! /bin/python2\n')
451 f
.write('# -*- coding: iso-3-14159 -*-\n')
452 self
.assertFalse(file_resources
.IsPythonFile(file1
))
455 class IsIgnoredTest(yapf_test_helper
.YAPFTest
):
457 def test_root_path(self
):
458 self
.assertTrue(file_resources
.IsIgnored('media', ['media']))
459 self
.assertFalse(file_resources
.IsIgnored('media', ['media/*']))
461 def test_sub_path(self
):
462 self
.assertTrue(file_resources
.IsIgnored('media/a', ['*/a']))
463 self
.assertTrue(file_resources
.IsIgnored('media/b', ['media/*']))
464 self
.assertTrue(file_resources
.IsIgnored('media/b/c', ['*/*/c']))
466 def test_trailing_slash(self
):
467 self
.assertTrue(file_resources
.IsIgnored('z', ['z']))
468 self
.assertTrue(file_resources
.IsIgnored('z', ['z' + os
.path
.sep
]))
471 class BufferedByteStream(object):
474 self
.stream
= BytesIO()
476 def getvalue(self
): # pylint: disable=invalid-name
477 return self
.stream
.getvalue().decode('utf-8')
484 class WriteReformattedCodeTest(yapf_test_helper
.YAPFTest
):
487 def setUpClass(cls
): # pylint: disable=g-missing-super-call
488 cls
.test_tmpdir
= tempfile
.mkdtemp()
491 def tearDownClass(cls
): # pylint: disable=g-missing-super-call
492 shutil
.rmtree(cls
.test_tmpdir
)
494 def test_write_to_file(self
):
496 with utils
.NamedTempFile(dirname
=self
.test_tmpdir
) as (f
, fname
):
497 file_resources
.WriteReformattedCode(
498 fname
, s
, in_place
=True, encoding
='utf-8')
501 with
open(fname
) as f2
:
502 self
.assertEqual(f2
.read(), s
)
504 def test_write_to_stdout(self
):
506 stream
= BufferedByteStream()
507 with utils
.stdout_redirector(stream
):
508 file_resources
.WriteReformattedCode(
509 None, s
, in_place
=False, encoding
='utf-8')
510 self
.assertEqual(stream
.getvalue(), s
)
512 def test_write_encoded_to_stdout(self
):
513 s
= '\ufeff# -*- coding: utf-8 -*-\nresult = "passed"\n' # pylint: disable=anomalous-unicode-escape-in-string # noqa
514 stream
= BufferedByteStream()
515 with utils
.stdout_redirector(stream
):
516 file_resources
.WriteReformattedCode(
517 None, s
, in_place
=False, encoding
='utf-8')
518 self
.assertEqual(stream
.getvalue(), s
)
521 class LineEndingTest(yapf_test_helper
.YAPFTest
):
523 def test_line_ending_linefeed(self
):
524 lines
= ['spam\n', 'spam\n']
525 actual
= file_resources
.LineEnding(lines
)
526 self
.assertEqual(actual
, '\n')
528 def test_line_ending_carriage_return(self
):
529 lines
= ['spam\r', 'spam\r']
530 actual
= file_resources
.LineEnding(lines
)
531 self
.assertEqual(actual
, '\r')
533 def test_line_ending_combo(self
):
534 lines
= ['spam\r\n', 'spam\r\n']
535 actual
= file_resources
.LineEnding(lines
)
536 self
.assertEqual(actual
, '\r\n')
538 def test_line_ending_weighted(self
):
545 actual
= file_resources
.LineEnding(lines
)
546 self
.assertEqual(actual
, '\n')
548 def test_line_ending_empty(self
):
550 actual
= file_resources
.LineEnding(lines
)
551 self
.assertEqual(actual
, '\n')
553 def test_line_ending_no_newline(self
):
555 actual
= file_resources
.LineEnding(lines
)
556 self
.assertEqual(actual
, '\n')
558 def test_line_ending_tie(self
):
565 actual
= file_resources
.LineEnding(lines
)
566 self
.assertEqual(actual
, '\n')
569 if __name__
== '__main__':