xref: /aosp_15_r20/external/pigweed/pw_presubmit/py/python_format_test.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2024 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Tests for Python formatters."""
15
16import importlib.resources
17from pathlib import Path
18from tempfile import TemporaryDirectory
19import unittest
20
21from format_testing_utils import CapturingToolRunner
22from pw_presubmit.format.python import BlackFormatter
23
24
25_TEST_DATA_FILES = importlib.resources.files('pw_presubmit.format.test_data')
26_TEST_SRC_FILE = _TEST_DATA_FILES / 'python_test_data.py'
27_TEST_GOLDEN = _TEST_DATA_FILES / 'python_test_data_golden.py'
28_TEST_MALFORMED = _TEST_DATA_FILES / 'malformed_file.txt'
29_BLACK_CONFIG_PATH = _TEST_DATA_FILES / 'black_config.toml'
30
31
32class TestBlackFormatter(unittest.TestCase):
33    """Tests for the BlackFormatter."""
34
35    maxDiff = None
36
37    def test_check_file(self):
38        tool_runner = CapturingToolRunner()
39        formatter = BlackFormatter(_BLACK_CONFIG_PATH)
40        formatter.run_tool = tool_runner
41
42        result = formatter.format_file_in_memory(
43            _TEST_SRC_FILE, _TEST_SRC_FILE.read_bytes()
44        )
45        self.assertTrue(result.ok)
46        self.assertEqual(result.error_message, None)
47        self.assertMultiLineEqual(
48            result.formatted_file_contents.decode(), _TEST_GOLDEN.read_text()
49        )
50
51        self.assertEqual(
52            tool_runner.command_history.pop(0),
53            ' '.join(
54                (
55                    'black',
56                    '--config',
57                    str(_BLACK_CONFIG_PATH),
58                    '-q',
59                    '-',
60                )
61            ),
62        )
63
64    def test_check_file_error(self):
65        tool_runner = CapturingToolRunner()
66        formatter = BlackFormatter(_BLACK_CONFIG_PATH)
67        formatter.run_tool = tool_runner
68
69        result = formatter.format_file_in_memory(
70            _TEST_MALFORMED, _TEST_MALFORMED.read_bytes()
71        )
72        self.assertFalse(result.ok)
73        self.assertEqual(result.formatted_file_contents, b'')
74        self.assertTrue(result.error_message.startswith('error: cannot format'))
75
76        self.assertEqual(
77            tool_runner.command_history.pop(0),
78            ' '.join(
79                (
80                    'black',
81                    '--config',
82                    str(_BLACK_CONFIG_PATH),
83                    '-q',
84                    '-',
85                )
86            ),
87        )
88
89    def test_fix_file(self):
90        """Tests that formatting is properly applied to files."""
91
92        tool_runner = CapturingToolRunner()
93        formatter = BlackFormatter(_BLACK_CONFIG_PATH)
94        formatter.run_tool = tool_runner
95
96        with TemporaryDirectory() as temp_dir:
97            file_to_fix = Path(temp_dir) / _TEST_SRC_FILE.name
98            file_to_fix.write_bytes(_TEST_SRC_FILE.read_bytes())
99
100            malformed_file = Path(temp_dir) / _TEST_MALFORMED.name
101            malformed_file.write_bytes(_TEST_MALFORMED.read_bytes())
102
103            errors = list(formatter.format_files([file_to_fix, malformed_file]))
104
105            # Should see three separate commands, one where we try to format
106            # both files together, and then the fallback logic that formats
107            # them individually to isolate errors.
108            self.assertEqual(
109                tool_runner.command_history.pop(0),
110                ' '.join(
111                    (
112                        'black',
113                        '--config',
114                        str(_BLACK_CONFIG_PATH),
115                        '-q',
116                        str(file_to_fix),
117                        str(malformed_file),
118                    )
119                ),
120            )
121
122            self.assertEqual(
123                tool_runner.command_history.pop(0),
124                ' '.join(
125                    (
126                        'black',
127                        '--config',
128                        str(_BLACK_CONFIG_PATH),
129                        '-q',
130                        str(file_to_fix),
131                    )
132                ),
133            )
134
135            self.assertEqual(
136                tool_runner.command_history.pop(0),
137                ' '.join(
138                    (
139                        'black',
140                        '--config',
141                        str(_BLACK_CONFIG_PATH),
142                        '-q',
143                        str(malformed_file),
144                    )
145                ),
146            )
147
148            # Check good build file.
149            self.assertMultiLineEqual(
150                file_to_fix.read_text(), _TEST_GOLDEN.read_text()
151            )
152
153            # Check malformed file.
154            self.assertEqual(len(errors), 1)
155            malformed_files = [malformed_file]
156            for file_path, error in errors:
157                self.assertIn(file_path, malformed_files)
158                self.assertFalse(error.ok)
159                self.assertTrue(
160                    error.error_message.startswith('error: cannot format')
161                )
162
163
164if __name__ == '__main__':
165    unittest.main()
166