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