• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    def test_check_file(self):
36        tool_runner = CapturingToolRunner()
37        formatter = BlackFormatter(_BLACK_CONFIG_PATH)
38        formatter.run_tool = tool_runner
39
40        result = formatter.format_file_in_memory(
41            _TEST_SRC_FILE, _TEST_SRC_FILE.read_bytes()
42        )
43        self.assertTrue(result.ok)
44        self.assertEqual(result.error_message, None)
45        self.assertMultiLineEqual(
46            result.formatted_file_contents.decode(), _TEST_GOLDEN.read_text()
47        )
48
49        self.assertEqual(
50            tool_runner.command_history.pop(0),
51            ' '.join(
52                (
53                    'black',
54                    '--config',
55                    str(_BLACK_CONFIG_PATH),
56                    '-q',
57                    '-',
58                )
59            ),
60        )
61
62    def test_check_file_error(self):
63        tool_runner = CapturingToolRunner()
64        formatter = BlackFormatter(_BLACK_CONFIG_PATH)
65        formatter.run_tool = tool_runner
66
67        result = formatter.format_file_in_memory(
68            _TEST_MALFORMED, _TEST_MALFORMED.read_bytes()
69        )
70        self.assertFalse(result.ok)
71        self.assertEqual(result.formatted_file_contents, b'')
72        self.assertTrue(result.error_message.startswith('error: cannot format'))
73
74        self.assertEqual(
75            tool_runner.command_history.pop(0),
76            ' '.join(
77                (
78                    'black',
79                    '--config',
80                    str(_BLACK_CONFIG_PATH),
81                    '-q',
82                    '-',
83                )
84            ),
85        )
86
87    def test_fix_file(self):
88        """Tests that formatting is properly applied to files."""
89
90        tool_runner = CapturingToolRunner()
91        formatter = BlackFormatter(_BLACK_CONFIG_PATH)
92        formatter.run_tool = tool_runner
93
94        with TemporaryDirectory() as temp_dir:
95            file_to_fix = Path(temp_dir) / _TEST_SRC_FILE.name
96            file_to_fix.write_bytes(_TEST_SRC_FILE.read_bytes())
97
98            malformed_file = Path(temp_dir) / _TEST_MALFORMED.name
99            malformed_file.write_bytes(_TEST_MALFORMED.read_bytes())
100
101            errors = list(formatter.format_files([file_to_fix, malformed_file]))
102
103            # Should see three separate commands, one where we try to format
104            # both files together, and then the fallback logic that formats
105            # them individually to isolate errors.
106            self.assertEqual(
107                tool_runner.command_history.pop(0),
108                ' '.join(
109                    (
110                        'black',
111                        '--config',
112                        str(_BLACK_CONFIG_PATH),
113                        '-q',
114                        str(file_to_fix),
115                        str(malformed_file),
116                    )
117                ),
118            )
119
120            self.assertEqual(
121                tool_runner.command_history.pop(0),
122                ' '.join(
123                    (
124                        'black',
125                        '--config',
126                        str(_BLACK_CONFIG_PATH),
127                        '-q',
128                        str(file_to_fix),
129                    )
130                ),
131            )
132
133            self.assertEqual(
134                tool_runner.command_history.pop(0),
135                ' '.join(
136                    (
137                        'black',
138                        '--config',
139                        str(_BLACK_CONFIG_PATH),
140                        '-q',
141                        str(malformed_file),
142                    )
143                ),
144            )
145
146            # Check good build file.
147            self.assertMultiLineEqual(
148                file_to_fix.read_text(), _TEST_GOLDEN.read_text()
149            )
150
151            # Check malformed file.
152            self.assertEqual(len(errors), 1)
153            malformed_files = [malformed_file]
154            for file_path, error in errors:
155                self.assertIn(file_path, malformed_files)
156                self.assertFalse(error.ok)
157                self.assertTrue(
158                    error.error_message.startswith('error: cannot format')
159                )
160
161
162if __name__ == '__main__':
163    unittest.main()
164