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 the buildifier formatter.""" 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.bazel import BuildifierFormatter 23 24 25_TEST_DATA_FILES = importlib.resources.files('pw_presubmit.format.test_data') 26_TEST_SRC_FILE = _TEST_DATA_FILES / 'bazel_test_data.bazel' 27_TEST_GOLDEN = _TEST_DATA_FILES / 'bazel_test_data_golden.bazel' 28_TEST_MALFORMED = _TEST_DATA_FILES / 'malformed_file.txt' 29 30 31class TestBuildifierFormatter(unittest.TestCase): 32 """Tests for the BuildifierFormatter.""" 33 34 def test_check_file(self): 35 """Tests that a formatting check produces the formatted result.""" 36 tool_runner = CapturingToolRunner() 37 formatter = BuildifierFormatter() 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 'buildifier', 54 '--type=build', 55 '--lint=fix', 56 '--warnings=' 57 + ','.join( 58 ( 59 'load', 60 'load-on-top', 61 'native-build', 62 'same-origin-load', 63 'out-of-order-load', 64 'unsorted-dict-items', 65 ) 66 ), 67 ) 68 ), 69 ) 70 71 def test_check_file_error(self): 72 """Tests that a malformed build file propagates an error.""" 73 74 tool_runner = CapturingToolRunner() 75 formatter = BuildifierFormatter() 76 formatter.run_tool = tool_runner 77 78 result = formatter.format_file_in_memory( 79 _TEST_MALFORMED, _TEST_MALFORMED.read_bytes() 80 ) 81 self.assertFalse(result.ok) 82 self.assertEqual(result.formatted_file_contents, b'') 83 self.assertIn('syntax error', result.error_message) 84 85 self.assertEqual( 86 tool_runner.command_history.pop(0), 87 ' '.join( 88 ( 89 'buildifier', 90 '--type=default', 91 '--lint=fix', 92 '--warnings=' 93 + ','.join( 94 ( 95 'load', 96 'load-on-top', 97 'native-build', 98 'same-origin-load', 99 'out-of-order-load', 100 'unsorted-dict-items', 101 ) 102 ), 103 ) 104 ), 105 ) 106 107 def test_fix_file(self): 108 """Tests that formatting is properly applied to files.""" 109 110 tool_runner = CapturingToolRunner() 111 formatter = BuildifierFormatter() 112 formatter.run_tool = tool_runner 113 114 with TemporaryDirectory() as temp_dir: 115 file_to_fix = Path(temp_dir) / _TEST_SRC_FILE.name 116 file_to_fix.write_bytes(_TEST_SRC_FILE.read_bytes()) 117 118 malformed_file = Path(temp_dir) / _TEST_MALFORMED.name 119 malformed_file.write_bytes(_TEST_MALFORMED.read_bytes()) 120 121 errors = list(formatter.format_files([file_to_fix, malformed_file])) 122 123 # Should see three separate commands, one where we try to format 124 # the *.bazel files together, and two where we try to format the 125 # .txt file (which is considered an unknown type). 126 self.assertEqual( 127 tool_runner.command_history.pop(0), 128 ' '.join( 129 ( 130 'buildifier', 131 '--type=build', 132 '--lint=fix', 133 '--warnings=' 134 + ','.join( 135 ( 136 'load', 137 'load-on-top', 138 'native-build', 139 'same-origin-load', 140 'out-of-order-load', 141 'unsorted-dict-items', 142 ) 143 ), 144 str(file_to_fix), 145 ) 146 ), 147 ) 148 149 self.assertEqual( 150 tool_runner.command_history.pop(0), 151 ' '.join( 152 ( 153 'buildifier', 154 '--type=default', 155 '--lint=fix', 156 '--warnings=' 157 + ','.join( 158 ( 159 'load', 160 'load-on-top', 161 'native-build', 162 'same-origin-load', 163 'out-of-order-load', 164 'unsorted-dict-items', 165 ) 166 ), 167 str(malformed_file), 168 ) 169 ), 170 ) 171 172 self.assertEqual( 173 tool_runner.command_history.pop(0), 174 ' '.join( 175 ( 176 'buildifier', 177 '--type=default', 178 '--lint=fix', 179 '--warnings=' 180 + ','.join( 181 ( 182 'load', 183 'load-on-top', 184 'native-build', 185 'same-origin-load', 186 'out-of-order-load', 187 'unsorted-dict-items', 188 ) 189 ), 190 str(malformed_file), 191 ) 192 ), 193 ) 194 195 # Check good build file. 196 self.assertMultiLineEqual( 197 file_to_fix.read_text(), _TEST_GOLDEN.read_text() 198 ) 199 200 # Check malformed file. 201 self.assertEqual(len(errors), 1) 202 malformed_files = [malformed_file] 203 for file_path, error in errors: 204 self.assertIn(file_path, malformed_files) 205 self.assertFalse(error.ok) 206 self.assertIn('syntax error', error.error_message) 207 208 209if __name__ == '__main__': 210 unittest.main() 211