| # -*- coding: utf-8 -*- |
| # Copyright 2015 Google Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Tests for yapf.file_resources.""" |
| |
| import codecs |
| import contextlib |
| import os |
| import shutil |
| import tempfile |
| import unittest |
| from io import BytesIO |
| |
| from yapf.yapflib import errors |
| from yapf.yapflib import file_resources |
| |
| from yapftests import utils |
| |
| |
| @contextlib.contextmanager |
| def _restore_working_dir(): |
| curdir = os.getcwd() |
| try: |
| yield |
| finally: |
| os.chdir(curdir) |
| |
| |
| @contextlib.contextmanager |
| def _exists_mocked_in_module(module, mock_implementation): |
| unmocked_exists = getattr(module, 'exists') |
| setattr(module, 'exists', mock_implementation) |
| try: |
| yield |
| finally: |
| setattr(module, 'exists', unmocked_exists) |
| |
| |
| class GetExcludePatternsForDir(unittest.TestCase): |
| |
| def setUp(self): # pylint: disable=g-missing-super-call |
| self.test_tmpdir = tempfile.mkdtemp() |
| |
| def tearDown(self): # pylint: disable=g-missing-super-call |
| shutil.rmtree(self.test_tmpdir) |
| |
| def test_get_exclude_file_patterns_from_yapfignore(self): |
| local_ignore_file = os.path.join(self.test_tmpdir, '.yapfignore') |
| ignore_patterns = ['temp/**/*.py', 'temp2/*.py'] |
| with open(local_ignore_file, 'w') as f: |
| f.writelines('\n'.join(ignore_patterns)) |
| |
| self.assertEqual( |
| sorted(file_resources.GetExcludePatternsForDir(self.test_tmpdir)), |
| sorted(ignore_patterns)) |
| |
| def test_get_exclude_file_patterns_from_yapfignore_with_wrong_syntax(self): |
| local_ignore_file = os.path.join(self.test_tmpdir, '.yapfignore') |
| ignore_patterns = ['temp/**/*.py', './wrong/syntax/*.py'] |
| with open(local_ignore_file, 'w') as f: |
| f.writelines('\n'.join(ignore_patterns)) |
| |
| with self.assertRaises(errors.YapfError): |
| file_resources.GetExcludePatternsForDir(self.test_tmpdir) |
| |
| def test_get_exclude_file_patterns_from_pyproject(self): |
| try: |
| import tomli |
| except ImportError: |
| return |
| local_ignore_file = os.path.join(self.test_tmpdir, 'pyproject.toml') |
| ignore_patterns = ['temp/**/*.py', 'temp2/*.py'] |
| with open(local_ignore_file, 'w') as f: |
| f.write('[tool.yapfignore]\n') |
| f.write('ignore_patterns=[') |
| f.writelines('\n,'.join(['"{}"'.format(p) for p in ignore_patterns])) |
| f.write(']') |
| |
| self.assertEqual( |
| sorted(file_resources.GetExcludePatternsForDir(self.test_tmpdir)), |
| sorted(ignore_patterns)) |
| |
| def test_get_exclude_file_patterns_from_pyproject_no_ignore_section(self): |
| try: |
| import tomli |
| except ImportError: |
| return |
| local_ignore_file = os.path.join(self.test_tmpdir, 'pyproject.toml') |
| ignore_patterns = [] |
| open(local_ignore_file, 'w').close() |
| |
| self.assertEqual( |
| sorted(file_resources.GetExcludePatternsForDir(self.test_tmpdir)), |
| sorted(ignore_patterns)) |
| |
| def test_get_exclude_file_patterns_from_pyproject_ignore_section_empty(self): |
| try: |
| import tomli |
| except ImportError: |
| return |
| local_ignore_file = os.path.join(self.test_tmpdir, 'pyproject.toml') |
| ignore_patterns = [] |
| with open(local_ignore_file, 'w') as f: |
| f.write('[tool.yapfignore]\n') |
| |
| self.assertEqual( |
| sorted(file_resources.GetExcludePatternsForDir(self.test_tmpdir)), |
| sorted(ignore_patterns)) |
| |
| def test_get_exclude_file_patterns_with_no_config_files(self): |
| ignore_patterns = [] |
| |
| self.assertEqual( |
| sorted(file_resources.GetExcludePatternsForDir(self.test_tmpdir)), |
| sorted(ignore_patterns)) |
| |
| |
| class GetDefaultStyleForDirTest(unittest.TestCase): |
| |
| def setUp(self): # pylint: disable=g-missing-super-call |
| self.test_tmpdir = tempfile.mkdtemp() |
| |
| def tearDown(self): # pylint: disable=g-missing-super-call |
| shutil.rmtree(self.test_tmpdir) |
| |
| def test_no_local_style(self): |
| test_file = os.path.join(self.test_tmpdir, 'file.py') |
| style_name = file_resources.GetDefaultStyleForDir(test_file) |
| self.assertEqual(style_name, 'pep8') |
| |
| def test_no_local_style_custom_default(self): |
| test_file = os.path.join(self.test_tmpdir, 'file.py') |
| style_name = file_resources.GetDefaultStyleForDir( |
| test_file, default_style='custom-default') |
| self.assertEqual(style_name, 'custom-default') |
| |
| def test_with_local_style(self): |
| # Create an empty .style.yapf file in test_tmpdir |
| style_file = os.path.join(self.test_tmpdir, '.style.yapf') |
| open(style_file, 'w').close() |
| |
| test_filename = os.path.join(self.test_tmpdir, 'file.py') |
| self.assertEqual(style_file, |
| file_resources.GetDefaultStyleForDir(test_filename)) |
| |
| test_filename = os.path.join(self.test_tmpdir, 'dir1', 'file.py') |
| self.assertEqual(style_file, |
| file_resources.GetDefaultStyleForDir(test_filename)) |
| |
| def test_setup_config(self): |
| # An empty setup.cfg file should not be used |
| setup_config = os.path.join(self.test_tmpdir, 'setup.cfg') |
| open(setup_config, 'w').close() |
| |
| test_dir = os.path.join(self.test_tmpdir, 'dir1') |
| style_name = file_resources.GetDefaultStyleForDir(test_dir) |
| self.assertEqual(style_name, 'pep8') |
| |
| # One with a '[yapf]' section should be used |
| with open(setup_config, 'w') as f: |
| f.write('[yapf]\n') |
| self.assertEqual(setup_config, |
| file_resources.GetDefaultStyleForDir(test_dir)) |
| |
| def test_pyproject_toml(self): |
| # An empty pyproject.toml file should not be used |
| try: |
| import tomli |
| except ImportError: |
| return |
| |
| pyproject_toml = os.path.join(self.test_tmpdir, 'pyproject.toml') |
| open(pyproject_toml, 'w').close() |
| |
| test_dir = os.path.join(self.test_tmpdir, 'dir1') |
| style_name = file_resources.GetDefaultStyleForDir(test_dir) |
| self.assertEqual(style_name, 'pep8') |
| |
| # One with a '[tool.yapf]' section should be used |
| with open(pyproject_toml, 'w') as f: |
| f.write('[tool.yapf]\n') |
| self.assertEqual(pyproject_toml, |
| file_resources.GetDefaultStyleForDir(test_dir)) |
| |
| def test_local_style_at_root(self): |
| # Test behavior of files located on the root, and under root. |
| rootdir = os.path.abspath(os.path.sep) |
| test_dir_at_root = os.path.join(rootdir, 'dir1') |
| test_dir_under_root = os.path.join(rootdir, 'dir1', 'dir2') |
| |
| # Fake placing only a style file at the root by mocking `os.path.exists`. |
| style_file = os.path.join(rootdir, '.style.yapf') |
| |
| def mock_exists_implementation(path): |
| return path == style_file |
| |
| with _exists_mocked_in_module(file_resources.os.path, |
| mock_exists_implementation): |
| # Both files should find the style file at the root. |
| default_style_at_root = file_resources.GetDefaultStyleForDir( |
| test_dir_at_root) |
| self.assertEqual(style_file, default_style_at_root) |
| default_style_under_root = file_resources.GetDefaultStyleForDir( |
| test_dir_under_root) |
| self.assertEqual(style_file, default_style_under_root) |
| |
| |
| def _touch_files(filenames): |
| for name in filenames: |
| open(name, 'a').close() |
| |
| |
| class GetCommandLineFilesTest(unittest.TestCase): |
| |
| def setUp(self): # pylint: disable=g-missing-super-call |
| self.test_tmpdir = tempfile.mkdtemp() |
| self.old_dir = os.getcwd() |
| |
| def tearDown(self): # pylint: disable=g-missing-super-call |
| os.chdir(self.old_dir) |
| shutil.rmtree(self.test_tmpdir) |
| |
| def _make_test_dir(self, name): |
| fullpath = os.path.normpath(os.path.join(self.test_tmpdir, name)) |
| os.makedirs(fullpath) |
| return fullpath |
| |
| def test_find_files_not_dirs(self): |
| tdir1 = self._make_test_dir('test1') |
| tdir2 = self._make_test_dir('test2') |
| file1 = os.path.join(tdir1, 'testfile1.py') |
| file2 = os.path.join(tdir2, 'testfile2.py') |
| _touch_files([file1, file2]) |
| |
| self.assertEqual( |
| file_resources.GetCommandLineFiles([file1, file2], |
| recursive=False, |
| exclude=None), [file1, file2]) |
| self.assertEqual( |
| file_resources.GetCommandLineFiles([file1, file2], |
| recursive=True, |
| exclude=None), [file1, file2]) |
| |
| def test_nonrecursive_find_in_dir(self): |
| tdir1 = self._make_test_dir('test1') |
| tdir2 = self._make_test_dir('test1/foo') |
| file1 = os.path.join(tdir1, 'testfile1.py') |
| file2 = os.path.join(tdir2, 'testfile2.py') |
| _touch_files([file1, file2]) |
| |
| self.assertRaises( |
| errors.YapfError, |
| file_resources.GetCommandLineFiles, |
| command_line_file_list=[tdir1], |
| recursive=False, |
| exclude=None) |
| |
| def test_recursive_find_in_dir(self): |
| tdir1 = self._make_test_dir('test1') |
| tdir2 = self._make_test_dir('test2/testinner/') |
| tdir3 = self._make_test_dir('test3/foo/bar/bas/xxx') |
| files = [ |
| os.path.join(tdir1, 'testfile1.py'), |
| os.path.join(tdir2, 'testfile2.py'), |
| os.path.join(tdir3, 'testfile3.py'), |
| ] |
| _touch_files(files) |
| |
| self.assertEqual( |
| sorted( |
| file_resources.GetCommandLineFiles([self.test_tmpdir], |
| recursive=True, |
| exclude=None)), sorted(files)) |
| |
| def test_recursive_find_in_dir_with_exclude(self): |
| tdir1 = self._make_test_dir('test1') |
| tdir2 = self._make_test_dir('test2/testinner/') |
| tdir3 = self._make_test_dir('test3/foo/bar/bas/xxx') |
| files = [ |
| os.path.join(tdir1, 'testfile1.py'), |
| os.path.join(tdir2, 'testfile2.py'), |
| os.path.join(tdir3, 'testfile3.py'), |
| ] |
| _touch_files(files) |
| |
| self.assertEqual( |
| sorted( |
| file_resources.GetCommandLineFiles([self.test_tmpdir], |
| recursive=True, |
| exclude=['*test*3.py'])), |
| sorted([ |
| os.path.join(tdir1, 'testfile1.py'), |
| os.path.join(tdir2, 'testfile2.py'), |
| ])) |
| |
| def test_find_with_excluded_hidden_dirs(self): |
| tdir1 = self._make_test_dir('.test1') |
| tdir2 = self._make_test_dir('test_2') |
| tdir3 = self._make_test_dir('test.3') |
| files = [ |
| os.path.join(tdir1, 'testfile1.py'), |
| os.path.join(tdir2, 'testfile2.py'), |
| os.path.join(tdir3, 'testfile3.py'), |
| ] |
| _touch_files(files) |
| |
| actual = file_resources.GetCommandLineFiles([self.test_tmpdir], |
| recursive=True, |
| exclude=['*.test1*']) |
| |
| self.assertEqual( |
| sorted(actual), |
| sorted([ |
| os.path.join(tdir2, 'testfile2.py'), |
| os.path.join(tdir3, 'testfile3.py'), |
| ])) |
| |
| def test_find_with_excluded_hidden_dirs_relative(self): |
| """Test find with excluded hidden dirs. |
| |
| A regression test against a specific case where a hidden directory (one |
| beginning with a period) is being excluded, but it is also an immediate |
| child of the current directory which has been specified in a relative |
| manner. |
| |
| At its core, the bug has to do with overzealous stripping of "./foo" so that |
| it removes too much from "./.foo" . |
| """ |
| tdir1 = self._make_test_dir('.test1') |
| tdir2 = self._make_test_dir('test_2') |
| tdir3 = self._make_test_dir('test.3') |
| files = [ |
| os.path.join(tdir1, 'testfile1.py'), |
| os.path.join(tdir2, 'testfile2.py'), |
| os.path.join(tdir3, 'testfile3.py'), |
| ] |
| _touch_files(files) |
| |
| # We must temporarily change the current directory, so that we test against |
| # patterns like ./.test1/file instead of /tmp/foo/.test1/file |
| with _restore_working_dir(): |
| |
| os.chdir(self.test_tmpdir) |
| actual = file_resources.GetCommandLineFiles( |
| [os.path.relpath(self.test_tmpdir)], |
| recursive=True, |
| exclude=['*.test1*']) |
| |
| self.assertEqual( |
| sorted(actual), |
| sorted([ |
| os.path.join( |
| os.path.relpath(self.test_tmpdir), os.path.basename(tdir2), |
| 'testfile2.py'), |
| os.path.join( |
| os.path.relpath(self.test_tmpdir), os.path.basename(tdir3), |
| 'testfile3.py'), |
| ])) |
| |
| def test_find_with_excluded_dirs(self): |
| tdir1 = self._make_test_dir('test1') |
| tdir2 = self._make_test_dir('test2/testinner/') |
| tdir3 = self._make_test_dir('test3/foo/bar/bas/xxx') |
| files = [ |
| os.path.join(tdir1, 'testfile1.py'), |
| os.path.join(tdir2, 'testfile2.py'), |
| os.path.join(tdir3, 'testfile3.py'), |
| ] |
| _touch_files(files) |
| |
| os.chdir(self.test_tmpdir) |
| |
| found = sorted( |
| file_resources.GetCommandLineFiles(['test1', 'test2', 'test3'], |
| recursive=True, |
| exclude=[ |
| 'test1', |
| 'test2/testinner/', |
| ])) |
| |
| self.assertEqual( |
| found, ['test3/foo/bar/bas/xxx/testfile3.py'.replace('/', os.path.sep)]) |
| |
| found = sorted( |
| file_resources.GetCommandLineFiles(['.'], |
| recursive=True, |
| exclude=[ |
| 'test1', |
| 'test3', |
| ])) |
| |
| self.assertEqual( |
| found, ['./test2/testinner/testfile2.py'.replace('/', os.path.sep)]) |
| |
| def test_find_with_excluded_current_dir(self): |
| with self.assertRaises(errors.YapfError): |
| file_resources.GetCommandLineFiles([], False, exclude=['./z']) |
| |
| |
| class IsPythonFileTest(unittest.TestCase): |
| |
| def setUp(self): # pylint: disable=g-missing-super-call |
| self.test_tmpdir = tempfile.mkdtemp() |
| |
| def tearDown(self): # pylint: disable=g-missing-super-call |
| shutil.rmtree(self.test_tmpdir) |
| |
| def test_with_py_extension(self): |
| file1 = os.path.join(self.test_tmpdir, 'testfile1.py') |
| self.assertTrue(file_resources.IsPythonFile(file1)) |
| |
| def test_empty_without_py_extension(self): |
| file1 = os.path.join(self.test_tmpdir, 'testfile1') |
| self.assertFalse(file_resources.IsPythonFile(file1)) |
| file2 = os.path.join(self.test_tmpdir, 'testfile1.rb') |
| self.assertFalse(file_resources.IsPythonFile(file2)) |
| |
| def test_python_shebang(self): |
| file1 = os.path.join(self.test_tmpdir, 'testfile1') |
| with open(file1, 'w') as f: |
| f.write('#!/usr/bin/python\n') |
| self.assertTrue(file_resources.IsPythonFile(file1)) |
| |
| file2 = os.path.join(self.test_tmpdir, 'testfile2.run') |
| with open(file2, 'w') as f: |
| f.write('#! /bin/python2\n') |
| self.assertTrue(file_resources.IsPythonFile(file1)) |
| |
| def test_with_latin_encoding(self): |
| file1 = os.path.join(self.test_tmpdir, 'testfile1') |
| with codecs.open(file1, mode='w', encoding='latin-1') as f: |
| f.write('#! /bin/python2\n') |
| self.assertTrue(file_resources.IsPythonFile(file1)) |
| |
| def test_with_invalid_encoding(self): |
| file1 = os.path.join(self.test_tmpdir, 'testfile1') |
| with open(file1, 'w') as f: |
| f.write('#! /bin/python2\n') |
| f.write('# -*- coding: iso-3-14159 -*-\n') |
| self.assertFalse(file_resources.IsPythonFile(file1)) |
| |
| |
| class IsIgnoredTest(unittest.TestCase): |
| |
| def test_root_path(self): |
| self.assertTrue(file_resources.IsIgnored('media', ['media'])) |
| self.assertFalse(file_resources.IsIgnored('media', ['media/*'])) |
| |
| def test_sub_path(self): |
| self.assertTrue(file_resources.IsIgnored('media/a', ['*/a'])) |
| self.assertTrue(file_resources.IsIgnored('media/b', ['media/*'])) |
| self.assertTrue(file_resources.IsIgnored('media/b/c', ['*/*/c'])) |
| |
| def test_trailing_slash(self): |
| self.assertTrue(file_resources.IsIgnored('z', ['z'])) |
| self.assertTrue(file_resources.IsIgnored('z', ['z' + os.path.sep])) |
| |
| |
| class BufferedByteStream(object): |
| |
| def __init__(self): |
| self.stream = BytesIO() |
| |
| def getvalue(self): # pylint: disable=invalid-name |
| return self.stream.getvalue().decode('utf-8') |
| |
| @property |
| def buffer(self): |
| return self.stream |
| |
| |
| class WriteReformattedCodeTest(unittest.TestCase): |
| |
| @classmethod |
| def setUpClass(cls): # pylint: disable=g-missing-super-call |
| cls.test_tmpdir = tempfile.mkdtemp() |
| |
| @classmethod |
| def tearDownClass(cls): # pylint: disable=g-missing-super-call |
| shutil.rmtree(cls.test_tmpdir) |
| |
| def test_write_to_file(self): |
| s = 'foobar\n' |
| with utils.NamedTempFile(dirname=self.test_tmpdir) as (f, fname): |
| file_resources.WriteReformattedCode( |
| fname, s, in_place=True, encoding='utf-8') |
| f.flush() |
| |
| with open(fname) as f2: |
| self.assertEqual(f2.read(), s) |
| |
| def test_write_to_stdout(self): |
| s = 'foobar' |
| stream = BufferedByteStream() |
| with utils.stdout_redirector(stream): |
| file_resources.WriteReformattedCode( |
| None, s, in_place=False, encoding='utf-8') |
| self.assertEqual(stream.getvalue(), s) |
| |
| def test_write_encoded_to_stdout(self): |
| s = '\ufeff# -*- coding: utf-8 -*-\nresult = "passed"\n' # pylint: disable=anomalous-unicode-escape-in-string # noqa |
| stream = BufferedByteStream() |
| with utils.stdout_redirector(stream): |
| file_resources.WriteReformattedCode( |
| None, s, in_place=False, encoding='utf-8') |
| self.assertEqual(stream.getvalue(), s) |
| |
| |
| class LineEndingTest(unittest.TestCase): |
| |
| def test_line_ending_linefeed(self): |
| lines = ['spam\n', 'spam\n'] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\n') |
| |
| def test_line_ending_carriage_return(self): |
| lines = ['spam\r', 'spam\r'] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\r') |
| |
| def test_line_ending_combo(self): |
| lines = ['spam\r\n', 'spam\r\n'] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\r\n') |
| |
| def test_line_ending_weighted(self): |
| lines = [ |
| 'spam\n', |
| 'spam\n', |
| 'spam\r', |
| 'spam\r\n', |
| ] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\n') |
| |
| def test_line_ending_empty(self): |
| lines = [] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\n') |
| |
| def test_line_ending_no_newline(self): |
| lines = ['spam'] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\n') |
| |
| def test_line_ending_tie(self): |
| lines = [ |
| 'spam\n', |
| 'spam\n', |
| 'spam\r\n', |
| 'spam\r\n', |
| ] |
| actual = file_resources.LineEnding(lines) |
| self.assertEqual(actual, '\n') |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |