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