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