Mike Frysinger | f1ba7ad | 2022-09-12 05:42:57 -0400 | [diff] [blame] | 1 | # Copyright 2020 The ChromiumOS Authors |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """Unit tests for tricium_clang_tidy.py.""" |
| 6 | |
| 7 | import io |
| 8 | import json |
| 9 | import multiprocessing |
| 10 | import os |
| 11 | from pathlib import Path |
| 12 | import subprocess |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 13 | import tempfile |
| 14 | from typing import NamedTuple |
| 15 | from unittest import mock |
| 16 | |
| 17 | from chromite.lib import cros_test_lib |
| 18 | from chromite.lib import osutils |
| 19 | from chromite.scripts import tricium_clang_tidy |
| 20 | |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 21 | |
| 22 | class Replacement(NamedTuple): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 23 | """A YAML `tricium_clang_tidy.TidyReplacement`. |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 24 | |
Trent Apted | c20bb6d | 2023-05-10 15:00:03 +1000 | [diff] [blame^] | 25 | The data contained in YAML is slightly different than what |
| 26 | `TidyReplacement`s |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 27 | carry. |
| 28 | """ |
| 29 | |
| 30 | file_path: str |
| 31 | text: str |
| 32 | offset: int |
| 33 | length: int |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 34 | |
| 35 | |
| 36 | class Note(NamedTuple): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 37 | """A clang-tidy `note` from the YAML file.""" |
| 38 | |
| 39 | message: str |
| 40 | file_path: str |
| 41 | file_offset: int |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 42 | |
| 43 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 44 | def default_tidy_diagnostic( |
| 45 | file_path="/tidy/file.c", |
| 46 | line_number=1, |
| 47 | diag_name="${diag_name}", |
| 48 | message="${message}", |
| 49 | replacements=(), |
| 50 | expansion_locs=(), |
| 51 | ): |
| 52 | """Creates a TidyDiagnostic with reasonable defaults. |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 53 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 54 | Defaults here and yaml_diagnostic are generally intended to match where |
| 55 | possible. |
| 56 | """ |
| 57 | return tricium_clang_tidy.TidyDiagnostic( |
| 58 | file_path=file_path, |
| 59 | line_number=line_number, |
| 60 | diag_name=diag_name, |
| 61 | message=message, |
| 62 | replacements=replacements, |
| 63 | expansion_locs=expansion_locs, |
| 64 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 65 | |
| 66 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 67 | def yaml_diagnostic( |
| 68 | name="${diag_name}", |
| 69 | message="${message}", |
| 70 | file_path="/tidy/file.c", |
| 71 | file_offset=1, |
| 72 | replacements=(), |
| 73 | notes=(), |
| 74 | ): |
| 75 | """Creates a diagnostic serializable as YAML with reasonable defaults.""" |
| 76 | result = { |
| 77 | "DiagnosticName": name, |
| 78 | "DiagnosticMessage": { |
| 79 | "Message": message, |
| 80 | "FilePath": file_path, |
| 81 | "FileOffset": file_offset, |
| 82 | }, |
| 83 | } |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 84 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 85 | if replacements: |
| 86 | result["DiagnosticMessage"]["Replacements"] = [ |
| 87 | { |
| 88 | "FilePath": x.file_path, |
| 89 | "Offset": x.offset, |
| 90 | "Length": x.length, |
| 91 | "ReplacementText": x.text, |
| 92 | } |
| 93 | for x in replacements |
| 94 | ] |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 95 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 96 | if notes: |
| 97 | result["Notes"] = [ |
| 98 | { |
| 99 | "Message": x.message, |
| 100 | "FilePath": x.file_path, |
| 101 | "FileOffset": x.file_offset, |
| 102 | } |
| 103 | for x in notes |
| 104 | ] |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 105 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 106 | return result |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 107 | |
| 108 | |
| 109 | def mocked_nop_realpath(f): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 110 | """Mocks os.path.realpath to just return its argument.""" |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 111 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 112 | @mock.patch.object(os.path, "realpath") |
| 113 | @mock.patch.object(Path, "resolve") |
| 114 | def inner(self, replace_mock, realpath_mock, *args, **kwargs): |
| 115 | """Mocker for realpath.""" |
| 116 | identity = lambda x: x |
| 117 | realpath_mock.side_effect = identity |
| 118 | replace_mock.side_effect = identity |
| 119 | return f(self, *args, **kwargs) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 120 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 121 | return inner |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 122 | |
| 123 | |
| 124 | def mocked_readonly_open(contents=None, default=None): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 125 | """Mocks out open() so it always returns things from |contents|. |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 126 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 127 | Writing to open'ed files is not supported. |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 128 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 129 | Args: |
Trent Apted | c20bb6d | 2023-05-10 15:00:03 +1000 | [diff] [blame^] | 130 | contents: a |dict| mapping |file_path| => file_contents. |
| 131 | default: a default string to return if the given |file_path| doesn't |
| 132 | exist in |contents|. |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 133 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 134 | Returns: |
Trent Apted | c20bb6d | 2023-05-10 15:00:03 +1000 | [diff] [blame^] | 135 | |contents[file_path]| if it exists; otherwise, |default|. |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 136 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 137 | Raises: |
Trent Apted | c20bb6d | 2023-05-10 15:00:03 +1000 | [diff] [blame^] | 138 | If |default| is None and |contents[file_path]| does not exist, this will |
| 139 | raise a |ValueError|. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 140 | """ |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 141 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 142 | if contents is None: |
| 143 | contents = {} |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 144 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 145 | def inner(f): |
| 146 | """mocked_open impl.""" |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 147 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 148 | @mock.mock_open() |
| 149 | def inner_inner(self, open_mock, *args, **kwargs): |
| 150 | """the impl of mocked_readonly_open's impl!""" |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 151 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 152 | def get_data(file_path, mode="r", encoding=None): |
| 153 | """the impl of the impl of mocked_readonly_open's impl!!""" |
| 154 | data = contents.get(file_path, default) |
| 155 | if data is None: |
| 156 | raise ValueError( |
| 157 | "No %r file was found; options were %r" |
| 158 | % (file_path, sorted(contents.keys())) |
| 159 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 160 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 161 | assert mode == "r", f"File mode {mode} isn't supported." |
| 162 | if encoding is None: |
| 163 | return io.BytesIO(data) |
| 164 | return io.StringIO(data) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 165 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 166 | open_mock.side_effect = get_data |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 167 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 168 | def get_data_stream(file_path): |
| 169 | return io.StringIO(get_data(file_path)) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 170 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 171 | open_mock.side_effect = get_data_stream |
| 172 | return f(self, *args, **kwargs) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 173 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 174 | return inner_inner |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 175 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 176 | return inner |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 177 | |
| 178 | |
| 179 | class TriciumClangTidyTests(cros_test_lib.MockTestCase): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 180 | """Various tests for tricium support.""" |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 181 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 182 | def test_tidy_diagnostic_path_normalization(self): |
| 183 | expanded_from = tricium_clang_tidy.TidyExpandedFrom( |
| 184 | file_path=Path("/old2/foo"), |
| 185 | line_number=2, |
| 186 | ) |
| 187 | diag = default_tidy_diagnostic( |
| 188 | file_path=Path("/old/foo"), |
| 189 | expansion_locs=(expanded_from,), |
| 190 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 191 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 192 | normalized = diag.normalize_paths_to("/new") |
| 193 | self.assertEqual( |
| 194 | normalized, |
| 195 | diag._replace( |
| 196 | file_path=Path("../old/foo"), |
| 197 | expansion_locs=( |
| 198 | expanded_from._replace(file_path=Path("../old2/foo")), |
| 199 | ), |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 200 | ), |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 201 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 202 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 203 | def test_line_offest_map_works(self): |
| 204 | # (input_char, line_number_of_char, line_offset_of_char) |
| 205 | line_offset_pairs = [ |
| 206 | ("a", 1, 0), |
| 207 | ("b", 1, 1), |
| 208 | ("\n", 1, 2), |
| 209 | ("c", 2, 0), |
| 210 | ("\n", 2, 1), |
| 211 | ("\n", 3, 0), |
| 212 | ("d", 4, 0), |
| 213 | ("", 4, 1), |
| 214 | ("", 4, 2), |
| 215 | ] |
| 216 | text = tricium_clang_tidy.LineOffsetMap.for_text( |
| 217 | "".join(x for x, _, _ in line_offset_pairs) |
| 218 | ) |
| 219 | for offset, (_, line_number, line_offset) in enumerate( |
| 220 | line_offset_pairs |
| 221 | ): |
| 222 | self.assertEqual(text.get_line_number(offset), line_number) |
| 223 | self.assertEqual(text.get_line_offset(offset), line_offset) |
| 224 | |
| 225 | def test_package_ebuild_resolution(self): |
| 226 | run_mock = self.StartPatcher(cros_test_lib.RunCommandMock()) |
| 227 | run_mock.SetDefaultCmdResult(stdout="${package1_ebuild}\n") |
| 228 | ebuilds = tricium_clang_tidy.resolve_package_ebuilds( |
| 229 | "${board}", |
| 230 | [ |
| 231 | "package1", |
| 232 | "package2.ebuild", |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 233 | ], |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 234 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 235 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 236 | run_mock.assertCommandContains( |
| 237 | ["equery-${board}", "w", "package1"], |
| 238 | check=True, |
| 239 | stdout=subprocess.PIPE, |
| 240 | encoding="utf-8", |
| 241 | ) |
| 242 | self.assertEqual(ebuilds, ["${package1_ebuild}", "package2.ebuild"]) |
| 243 | |
| 244 | @mocked_readonly_open(default="") |
| 245 | def test_parse_tidy_invocation_returns_exception_on_error( |
| 246 | self, read_file_mock |
| 247 | ): |
| 248 | oh_no = ValueError("${oh_no}!") |
| 249 | read_file_mock.side_effect = oh_no |
| 250 | result = tricium_clang_tidy.parse_tidy_invocation( |
| 251 | Path("/some/file/that/doesnt/exist.json") |
| 252 | ) |
| 253 | self.assertIn(str(oh_no), str(result)) |
| 254 | |
| 255 | @mocked_readonly_open( |
| 256 | { |
| 257 | "/file/path.json": json.dumps( |
| 258 | { |
| 259 | "exit_code": 1, |
| 260 | "executable": "${clang_tidy}", |
| 261 | "args": ["foo", "bar"], |
| 262 | "lint_target": "${target}", |
| 263 | "stdstreams": "brrrrrrr", |
| 264 | "wd": "/path/to/wd", |
| 265 | } |
| 266 | ), |
| 267 | # |yaml.dumps| doesn't exist, but json parses cleanly as yaml, so... |
| 268 | "/file/path.yaml": json.dumps( |
| 269 | { |
| 270 | "Diagnostics": [ |
| 271 | yaml_diagnostic( |
| 272 | name="some-diag", |
| 273 | message="${message}", |
| 274 | file_path="", |
| 275 | ), |
| 276 | ] |
| 277 | } |
| 278 | ), |
| 279 | } |
| 280 | ) |
| 281 | def test_parse_tidy_invocation_functions_on_success(self): |
| 282 | result = tricium_clang_tidy.parse_tidy_invocation("/file/path.json") |
| 283 | # If we got an |Exception|, print it out. |
| 284 | self.assertNotIsInstance(result, tricium_clang_tidy.Error) |
| 285 | meta, info = result |
| 286 | self.assertEqual( |
| 287 | meta, |
| 288 | tricium_clang_tidy.InvocationMetadata( |
| 289 | exit_code=1, |
| 290 | invocation=["${clang_tidy}", "foo", "bar"], |
| 291 | lint_target="${target}", |
| 292 | stdstreams="brrrrrrr", |
| 293 | wd="/path/to/wd", |
| 294 | ), |
| 295 | ) |
| 296 | |
| 297 | self.assertEqual( |
| 298 | info, |
| 299 | [ |
| 300 | default_tidy_diagnostic( |
| 301 | diag_name="some-diag", |
| 302 | message="${message}", |
| 303 | file_path="", |
| 304 | ), |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 305 | ], |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 306 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 307 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 308 | @mocked_nop_realpath |
| 309 | @mocked_readonly_open(default="") |
| 310 | def test_parse_fixes_file_absolutizes_paths(self): |
| 311 | results = tricium_clang_tidy.parse_tidy_fixes_file( |
| 312 | "/tidy", |
| 313 | { |
| 314 | "Diagnostics": [ |
| 315 | yaml_diagnostic(file_path="foo.c"), |
| 316 | yaml_diagnostic(file_path="/tidy/bar.c"), |
| 317 | yaml_diagnostic(file_path=""), |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 318 | ], |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 319 | }, |
| 320 | ) |
| 321 | file_paths = [x.file_path for x in results] |
| 322 | self.assertEqual(file_paths, ["/tidy/foo.c", "/tidy/bar.c", ""]) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 323 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 324 | @mocked_nop_realpath |
| 325 | @mocked_readonly_open( |
| 326 | { |
| 327 | "/tidy/foo.c": "", |
| 328 | "/tidy/foo.h": "a\n\n", |
| 329 | } |
| 330 | ) |
| 331 | def test_parse_fixes_file_interprets_offsets_correctly(self): |
| 332 | results = tricium_clang_tidy.parse_tidy_fixes_file( |
| 333 | "/tidy", |
| 334 | { |
| 335 | "Diagnostics": [ |
| 336 | yaml_diagnostic(file_path="/tidy/foo.c", file_offset=1), |
| 337 | yaml_diagnostic(file_path="/tidy/foo.c", file_offset=2), |
| 338 | yaml_diagnostic(file_path="/tidy/foo.h", file_offset=1), |
| 339 | yaml_diagnostic(file_path="/tidy/foo.h", file_offset=2), |
| 340 | yaml_diagnostic(file_path="/tidy/foo.h", file_offset=3), |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 341 | ], |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 342 | }, |
| 343 | ) |
| 344 | file_locations = [(x.file_path, x.line_number) for x in results] |
| 345 | self.assertEqual( |
| 346 | file_locations, |
| 347 | [ |
| 348 | ("/tidy/foo.c", 1), |
| 349 | ("/tidy/foo.c", 1), |
| 350 | ("/tidy/foo.h", 1), |
| 351 | ("/tidy/foo.h", 2), |
| 352 | ("/tidy/foo.h", 3), |
| 353 | ], |
| 354 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 355 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 356 | @mocked_nop_realpath |
| 357 | @mocked_readonly_open({"/tidy/foo.c": "a \n\n"}) |
| 358 | def test_parse_fixes_file_handles_replacements(self): |
| 359 | results = list( |
| 360 | tricium_clang_tidy.parse_tidy_fixes_file( |
| 361 | "/tidy", |
| 362 | { |
| 363 | "Diagnostics": [ |
| 364 | yaml_diagnostic( |
| 365 | file_path="/tidy/foo.c", |
| 366 | file_offset=1, |
| 367 | replacements=[ |
| 368 | Replacement( |
| 369 | file_path="foo.c", |
| 370 | text="whee", |
| 371 | offset=2, |
| 372 | length=2, |
| 373 | ), |
| 374 | ], |
| 375 | ), |
| 376 | ], |
| 377 | }, |
| 378 | ) |
| 379 | ) |
| 380 | self.assertEqual(len(results), 1, results) |
| 381 | self.assertEqual( |
| 382 | results[0].replacements, |
| 383 | ( |
| 384 | tricium_clang_tidy.TidyReplacement( |
| 385 | new_text="whee", |
| 386 | start_line=1, |
| 387 | end_line=3, |
| 388 | start_char=2, |
| 389 | end_char=0, |
| 390 | ), |
| 391 | ), |
| 392 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 393 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 394 | @mocked_nop_realpath |
| 395 | @mocked_readonly_open({"/whee.c": "", "/whee.h": "\n\n"}) |
| 396 | def test_parse_fixes_file_handles_macro_expansions(self): |
| 397 | results = list( |
| 398 | tricium_clang_tidy.parse_tidy_fixes_file( |
| 399 | "/tidy", |
| 400 | { |
| 401 | "Diagnostics": [ |
| 402 | yaml_diagnostic( |
| 403 | file_path="/whee.c", |
| 404 | file_offset=1, |
| 405 | notes=[ |
| 406 | Note( |
| 407 | message="not relevant", |
| 408 | file_path="/whee.c", |
| 409 | file_offset=1, |
| 410 | ), |
| 411 | Note( |
| 412 | message='expanded from macro "Foo"', |
| 413 | file_path="/whee.h", |
| 414 | file_offset=9, |
| 415 | ), |
| 416 | ], |
| 417 | ), |
| 418 | ], |
| 419 | }, |
| 420 | ) |
| 421 | ) |
| 422 | self.assertEqual(len(results), 1, results) |
| 423 | self.assertEqual( |
| 424 | results[0].expansion_locs, |
| 425 | ( |
| 426 | tricium_clang_tidy.TidyExpandedFrom( |
| 427 | file_path="/whee.h", |
| 428 | line_number=3, |
| 429 | ), |
| 430 | ), |
| 431 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 432 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 433 | @mock.patch.object(Path, "glob") |
| 434 | @mock.patch.object(tricium_clang_tidy, "parse_tidy_invocation") |
| 435 | def test_collect_lints_functions(self, parse_invocation_mock, glob_mock): |
| 436 | glob_mock.return_value = ("/lint/foo.json", "/lint/bar.json") |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 437 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 438 | diag_1 = default_tidy_diagnostic() |
| 439 | diag_2 = diag_1._replace(line_number=diag_1.line_number + 1) |
| 440 | diag_3 = diag_2._replace(line_number=diag_2.line_number + 1) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 441 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 442 | # Because we want to test unique'ing, ensure these aren't equal. |
| 443 | all_diags = [diag_1, diag_2, diag_3] |
| 444 | self.assertEqual(sorted(all_diags), sorted(set(all_diags))) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 445 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 446 | per_file_lints = { |
| 447 | "/lint/foo.json": {diag_1, diag_2}, |
| 448 | "/lint/bar.json": {diag_2, diag_3}, |
| 449 | } |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 450 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 451 | def parse_invocation_side_effect(json_file): |
| 452 | self.assertIn(json_file, per_file_lints) |
| 453 | meta = mock.Mock() |
| 454 | meta.exit_code = 0 |
| 455 | return meta, per_file_lints[json_file] |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 456 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 457 | parse_invocation_mock.side_effect = parse_invocation_side_effect |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 458 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 459 | with multiprocessing.pool.ThreadPool(1) as yaml_pool: |
| 460 | lints = tricium_clang_tidy.collect_lints(Path("/lint"), yaml_pool) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 461 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 462 | self.assertEqual(set(all_diags), lints) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 463 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 464 | def test_filter_tidy_lints_filters_nothing_by_default(self): |
| 465 | basis = default_tidy_diagnostic() |
| 466 | diag2 = default_tidy_diagnostic(line_number=basis.line_number + 1) |
| 467 | diags = [basis, diag2] |
| 468 | diags.sort() |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 469 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 470 | self.assertEqual( |
| 471 | diags, |
| 472 | tricium_clang_tidy.filter_tidy_lints( |
| 473 | only_files=None, |
| 474 | git_repo_base=None, |
| 475 | diags=diags, |
| 476 | ), |
| 477 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 478 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 479 | def test_filter_tidy_lints_filters_paths_outside_of_only_files(self): |
| 480 | in_only_files = default_tidy_diagnostic(file_path="foo.c") |
| 481 | out_of_only_files = default_tidy_diagnostic(file_path="bar.c") |
| 482 | self.assertEqual( |
| 483 | [in_only_files], |
| 484 | tricium_clang_tidy.filter_tidy_lints( |
| 485 | only_files={Path("foo.c")}, |
| 486 | git_repo_base=None, |
| 487 | diags=[in_only_files, out_of_only_files], |
| 488 | ), |
| 489 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 490 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 491 | def test_filter_tidy_lints_normalizes_to_git_repo_baes(self): |
| 492 | git = default_tidy_diagnostic(file_path="/git/foo.c") |
| 493 | nogit = default_tidy_diagnostic(file_path="/nogit/bar.c") |
| 494 | self.assertEqual( |
| 495 | [git.normalize_paths_to("/git")], |
| 496 | tricium_clang_tidy.filter_tidy_lints( |
| 497 | only_files=None, |
| 498 | git_repo_base=Path("/git"), |
| 499 | diags=[git, nogit], |
| 500 | ), |
| 501 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 502 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 503 | def test_filter_tidy_lints_normalizes_and_restricts_properly(self): |
| 504 | git_and_only = default_tidy_diagnostic(file_path="/git/foo.c") |
| 505 | git_and_noonly = default_tidy_diagnostic(file_path="/git/bar.c") |
| 506 | self.assertEqual( |
| 507 | [git_and_only.normalize_paths_to("/git")], |
| 508 | tricium_clang_tidy.filter_tidy_lints( |
| 509 | only_files={Path("/git/foo.c")}, |
| 510 | git_repo_base=Path("/git"), |
| 511 | diags=[git_and_only, git_and_noonly], |
| 512 | ), |
| 513 | ) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 514 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 515 | @mock.patch.object(osutils, "CopyDirContents") |
| 516 | @mock.patch.object(osutils, "SafeMakedirs") |
| 517 | def test_lint_generation_functions( |
| 518 | self, safe_makedirs_mock, copy_dir_contents_mock |
| 519 | ): |
| 520 | run_mock = self.StartPatcher(cros_test_lib.PopenMock()) |
| 521 | run_mock.SetDefaultCmdResult() |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 522 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 523 | # Mock mkdtemp last, since PopenMock() makes a tempdir. |
| 524 | mkdtemp_mock = self.PatchObject(tempfile, "mkdtemp") |
| 525 | mkdtemp_path = "/path/to/temp/dir" |
| 526 | mkdtemp_mock.return_value = mkdtemp_path |
| 527 | with mock.patch.object(osutils, "RmDir") as rmdir_mock: |
| 528 | dir_name = str( |
| 529 | tricium_clang_tidy.generate_lints( |
| 530 | "${board}", "/path/to/the.ebuild" |
| 531 | ) |
| 532 | ) |
| 533 | self.assertEqual(mkdtemp_path, dir_name) |
George Burgess IV | 853d65b | 2020-02-25 13:13:15 -0800 | [diff] [blame] | 534 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 535 | rmdir_mock.assert_called_with( |
| 536 | tricium_clang_tidy.LINT_BASE, ignore_missing=True, sudo=True |
| 537 | ) |
| 538 | safe_makedirs_mock.assert_called_with( |
| 539 | tricium_clang_tidy.LINT_BASE, 0o777, sudo=True |
| 540 | ) |
| 541 | |
| 542 | desired_env = dict(os.environ) |
| 543 | desired_env["WITH_TIDY"] = "tricium" |
Alex Klein | e913c5e | 2023-01-11 16:24:54 -0700 | [diff] [blame] | 544 | # cros_build_lib.run adds LC_MESSAGES to the environment by default, so |
| 545 | # it is always in the actual env. It isn't guaranteed to be set in the |
| 546 | # ambient environment, so desired_env doesn't always have it, causing |
| 547 | # flakes. Explicitly set it to make sure it matches. |
| 548 | desired_env["LC_MESSAGES"] = "C" |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 549 | run_mock.assertCommandContains( |
| 550 | ["ebuild-${board}", "/path/to/the.ebuild", "clean", "compile"], |
| 551 | env=desired_env, |
| 552 | ) |
| 553 | |
| 554 | copy_dir_contents_mock.assert_called_with( |
| 555 | tricium_clang_tidy.LINT_BASE, dir_name |
| 556 | ) |