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_WARNINGS = ( 31 'load', 32 'native-build', 33 'unsorted-dict-items', 34) 35 36 37class TestBuildifierFormatter(unittest.TestCase): 38 """Tests for the BuildifierFormatter.""" 39 40 def test_check_file(self): 41 """Tests that a formatting check produces the formatted result.""" 42 tool_runner = CapturingToolRunner() 43 formatter = BuildifierFormatter() 44 formatter.run_tool = tool_runner 45 46 result = formatter.format_file_in_memory( 47 _TEST_SRC_FILE, _TEST_SRC_FILE.read_bytes() 48 ) 49 self.assertTrue(result.ok) 50 self.assertEqual(result.error_message, None) 51 self.assertMultiLineEqual( 52 result.formatted_file_contents.decode(), _TEST_GOLDEN.read_text() 53 ) 54 55 self.assertEqual( 56 tool_runner.command_history.pop(0), 57 ' '.join( 58 ( 59 'buildifier', 60 '--type=build', 61 '--lint=fix', 62 f'--warnings={",".join(_WARNINGS)}', 63 ) 64 ), 65 ) 66 67 def test_check_file_error(self): 68 """Tests that a malformed build file propagates an error.""" 69 70 tool_runner = CapturingToolRunner() 71 formatter = BuildifierFormatter() 72 formatter.run_tool = tool_runner 73 74 result = formatter.format_file_in_memory( 75 _TEST_MALFORMED, _TEST_MALFORMED.read_bytes() 76 ) 77 self.assertFalse(result.ok) 78 # stdout may differ depending on the version of buildifier used. 79 self.assertNotIn( 80 'This is just a text file with random contents!', 81 result.formatted_file_contents.decode(), 82 ) 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 f'--warnings={",".join(_WARNINGS)}', 93 ) 94 ), 95 ) 96 97 def test_fix_file(self): 98 """Tests that formatting is properly applied to files.""" 99 100 tool_runner = CapturingToolRunner() 101 formatter = BuildifierFormatter() 102 formatter.run_tool = tool_runner 103 104 with TemporaryDirectory() as temp_dir: 105 file_to_fix = Path(temp_dir) / _TEST_SRC_FILE.name 106 file_to_fix.write_bytes(_TEST_SRC_FILE.read_bytes()) 107 108 malformed_file = Path(temp_dir) / _TEST_MALFORMED.name 109 malformed_file.write_bytes(_TEST_MALFORMED.read_bytes()) 110 111 errors = list(formatter.format_files([file_to_fix, malformed_file])) 112 113 # Should see three separate commands, one where we try to format 114 # the *.bazel files together, and two where we try to format the 115 # .txt file (which is considered an unknown type). 116 self.assertEqual( 117 tool_runner.command_history.pop(0), 118 ' '.join( 119 ( 120 'buildifier', 121 '--type=build', 122 '--lint=fix', 123 '--warnings=' + ','.join(_WARNINGS), 124 str(file_to_fix), 125 ) 126 ), 127 ) 128 129 self.assertEqual( 130 tool_runner.command_history.pop(0), 131 ' '.join( 132 ( 133 'buildifier', 134 '--type=default', 135 '--lint=fix', 136 '--warnings=' + ','.join(_WARNINGS), 137 str(malformed_file), 138 ) 139 ), 140 ) 141 142 self.assertEqual( 143 tool_runner.command_history.pop(0), 144 ' '.join( 145 ( 146 'buildifier', 147 '--type=default', 148 '--lint=fix', 149 '--warnings=' + ','.join(_WARNINGS), 150 str(malformed_file), 151 ) 152 ), 153 ) 154 155 # Check good build file. 156 self.assertMultiLineEqual( 157 file_to_fix.read_text(), _TEST_GOLDEN.read_text() 158 ) 159 160 # Check malformed file. 161 self.assertEqual(len(errors), 1) 162 malformed_files = [malformed_file] 163 for file_path, error in errors: 164 self.assertIn(file_path, malformed_files) 165 self.assertFalse(error.ok) 166 self.assertIn('syntax error', error.error_message) 167 168 169if __name__ == '__main__': 170 unittest.main() 171