blob: 69f7a0363f360375e3d62c8040d73cd64c7c6f63 [file] [log] [blame]
Ryan Beltrancfc5c362021-03-02 18:36:18 +00001# Copyright 2021 The Chromium OS Authors. All rights reserved.
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_cargo_tidy.py."""
6
7import json
Ryan Beltrancfc5c362021-03-02 18:36:18 +00008
9from chromite.lib import cros_test_lib
10from chromite.scripts import tricium_cargo_clippy
11
Ryan Beltrancfc5c362021-03-02 18:36:18 +000012
13# These test cases were made by:
14# 1) modifying trace_events to lint poorly
15# 2) running USE="rust_clippy" emerge trace_events
16# 3) removing null fields
17# 4) replacing strings with shorter test strings
18valid_test_cases = {
19 json.dumps({
20 'reason': 'compiler-message',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000021 'package_id': 'pkg 0.1.0 (path+file:///path/to/repo/pkg)',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000022 'target': {
23 'kind': ['lib'],
24 'crate_types': ['lib'],
25 'name': 'trace_events',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000026 'src_path': '/path/to/repo/pkg/src/main_file',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000027 'edition': '2018',
28 'doctest': True,
29 'test': True
30 },
31 'message': {
32 'rendered': 'warning: rendered message 1',
33 'children': [
34 {
35 'children': [],
36 'level': 'note',
37 'message': '`#[warn(bare_trait_objects)]` message 1',
38 'spans': []
39 },
40 {
41 'children': [],
42 'level': 'help',
43 'message': 'sub message 1',
44 'spans': [{
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000045 'file_name': 'src/file_name_1',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000046 'byte_end': 7342, 'byte_start': 7336,
47 'column_end': 35, 'column_start': 29,
48 'is_primary': True,
49 'line_end': 262, 'line_start': 262,
50 'suggested_replacement': 'dyn Tracer',
51 'suggestion_applicability': 'MachineApplicable',
52 'text': [{
53 'highlight_end': 35, 'highlight_start': 29,
54 'text': 'highlight 1'
55 }]
56 }]
57 }
58 ],
59 'code': {'code': 'bare_trait_objects'},
60 'level': 'warning',
61 'message': 'message 1',
62 'spans': [{
63 'byte_end': 7342, 'byte_start': 7336,
64 'column_end': 35, 'column_start': 29,
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000065 'file_name': 'src/file_name_1',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000066 'is_primary': True,
67 'line_end': 262, 'line_start': 262,
68 'text': [{
69 'highlight_end': 35, 'highlight_start': 29,
70 'text': 'highlight 1'
71 }]
72 }]
73 }
74 }): {
Ryan Beltrancfc5c362021-03-02 18:36:18 +000075 'locations': [
76 tricium_cargo_clippy.CodeLocation(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000077 file_path='pkg/src/file_name_1',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000078 line_start=262,
79 line_end=262,
80 column_start=29,
81 column_end=35
82 )
83 ],
84 'level': 'warning',
85 'message': 'warning: rendered message 1',
86 },
87 json.dumps({
88 'reason': 'compiler-message',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000089 'package_id': 'pkg 1.2.3 (path+file:///path/to/repo/pkg)',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000090 'target': {
91 'kind': ['lib'],
92 'crate_types': ['lib'],
93 'name': 'trace_events',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +000094 'src_path': '/path/to/repo/pkg/src/main',
Ryan Beltrancfc5c362021-03-02 18:36:18 +000095 'edition': '2018', 'doctest': True, 'test': True
96 },
97 'message': {
98 'rendered': 'warning: rendered message 2',
99 'children': [
100 {
101 'level': 'note',
102 'children': [],
103 'message': 'submessage 2.1',
104 'spans': []
105 },
106 {
107 'level': 'help',
108 'children': [],
109 'message': 'submessage 2.2',
110 'spans': []
111 },
112 {
113 'level': 'help',
114 'children': [],
115 'message': 'submessage 2.3',
116 'spans': [{
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000117 'file_name': 'src/file_name_2',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000118 'byte_end': 7342, 'byte_start': 7327,
119 'column_end': 35, 'column_start': 20,
120 'line_end': 262, 'line_start': 262,
121 'is_primary': True,
122 'suggested_replacement': '&Tracer',
123 'suggestion_applicability': 'MachineApplicable',
124 'text': [{
125 'highlight_end': 35,
126 'highlight_start': 20,
127 'text': 'highlight 2'
128 }]
129 }]
130 }
131 ],
132 'code': {'code': 'clippy::redundant_static_lifetimes'},
133 'level': 'warning',
134 'message': 'message 2',
135 'spans': [{
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000136 'file_name': 'src/file_name_2',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000137 'byte_end': 7335, 'byte_start': 7328,
138 'column_end': 28, 'column_start': 21,
139 'line_end': 262, 'line_start': 262,
140 'is_primary': True,
141 'text': [{
142 'highlight_end': 28,
143 'highlight_start': 21,
144 'text': 'highlight 2'
145 }]
146 }]
147 }
148 }): {
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000149 'locations': [
150 tricium_cargo_clippy.CodeLocation(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000151 file_path='pkg/src/file_name_2',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000152 line_start=262,
153 line_end=262,
154 column_start=21,
155 column_end=28
156 ),
157 tricium_cargo_clippy.CodeLocation(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000158 file_path='pkg/src/file_name_2',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000159 line_start=262,
160 line_end=262,
161 column_start=20,
162 column_end=35
163 )
164 ],
165 'level': 'warning',
166 'message': 'warning: rendered message 2',
167 },
168 json.dumps({
169 'reason': 'compiler-message',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000170 'package_id': 'pkg 0.1.0 (path+file:///path/to/repo/pkg)',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000171 'target': {
172 'kind': ['lib'],
173 'crate_types': ['lib'],
174 'name': 'trace_events',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000175 'src_path': '/path/to/repo/pkg/path/to/main',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000176 'edition': '2018', 'doctest': True, 'test': True
177 },
178 'message': {
179 'rendered': 'warning: rendered message 3',
180 'children': [
181 {
182 'children': [],
183 'level': 'note',
184 'message': 'submessage 3.1',
185 'spans': []
186 },
187 {
188 'children': [],
189 'level': 'help',
190 'message': 'submessage 3.2',
191 'spans': [
192 {
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000193 'file_name': 'path/to/file_name_3',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000194 'byte_end': 448, 'byte_start': 447,
195 'column_end': 8, 'column_start': 7,
196 'line_end': 14, 'line_start': 14,
197 'is_primary': True,
198 'suggested_replacement': '_x',
199 'suggestion_applicability': 'MachineApplicable',
200 'text': [{
201 'highlight_end': 8, 'highlight_start': 7,
202 'text': 'highlight 3'
203 }]
204 }
205 ]
206 }
207 ],
208 'code': {'code': 'unused_variables'},
209 'level': 'warning',
210 'message': 'message 3',
211 'spans': [{
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000212 'file_name': 'path/to/file_name_3',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000213 'byte_end': 448, 'byte_start': 447,
214 'column_end': 8, 'column_start': 7,
215 'line_end': 14, 'line_start': 14,
216 'is_primary': True,
217 'text': [{
218 'highlight_end': 8,
219 'highlight_start': 7,
220 'text': 'highlight 3'
221 }]
222 }]
223 }
224 }): {
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000225 'locations': [
226 tricium_cargo_clippy.CodeLocation(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000227 file_path='pkg/path/to/file_name_3',
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000228 line_start=14,
229 line_end=14,
230 column_start=7,
231 column_end=8
232 )
233 ],
234 'level': 'warning',
235 'message': 'warning: rendered message 3',
236 },
237 json.dumps({'reason': 'build-script-executed'}): {
238 'skipped': True
239 },
240 json.dumps({'reason': 'compiler-artifact'}): {
241 'skipped': True
242 }
243}
244
245invalid_test_cases = {
246 'not json': ['json'],
247 r'{}': ['reason'],
248 json.dumps({
249 'reason': 'compiler-message',
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000250 }): ['level', 'message'],
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000251 json.dumps({
252 'reason': 'compiler-message',
253 'target': {
254 'src_path': 'file path'
255 },
256 'message': {
257 'rendered': 'warning: a message'
258 }
259 }): ['level'],
260 json.dumps({
261 'reason': 'compiler-message',
262 'level': 'warning',
263 'target': {
264 'src_path': 'file path'
265 },
266 }): ['message'],
267}
268
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000269test_git_repo = '/path/to/repo'
270test_package_path = '/path/to/repo/pkg'
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000271
272class TriciumCargoClippyTests(cros_test_lib.LoggingTestCase):
273 """Tests for Cargo Clippy."""
274
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000275 def test_parse_locations(self):
276 """Tests that parse_locations is as expected."""
Ryan Beltran43a00662021-05-17 16:55:24 +0000277 for test_case, exp_results in valid_test_cases.items():
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000278 if 'locations' not in exp_results:
279 continue
280 test_json = json.loads(test_case)
281 locations = list(tricium_cargo_clippy.parse_locations(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000282 test_json, test_package_path, test_git_repo))
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000283 self.assertEqual(locations, exp_results['locations'])
284
Ryan Beltrana4b45a32021-08-11 08:26:38 +0000285 def test_parse_locations_ebuild_directories(self):
286 """Tests that parse_locations strips ebuild work directories."""
287 expected_location = 'src/foo'
288 example_finding = json.dumps({
289 'reason': 'compiler-message',
290 'level': 'warning',
291 'message': {
292 'rendered': 'warning: a message',
293 'spans': [{
294 'file_name': expected_location,
295 'column_end': 1,
296 'column_start': 2,
297 'line_end': 3,
298 'line_start': 4,}]}})
299 for work_dir in (
300 '/build/atlas/tmp/portage/dev-rust/pkg-0.12.3-r3/work/pkg-0.12.3',
301 '/build/atlas/tmp/portage/dev-rust/pkg-99999/work/pkg-9999'):
302 inputs = [json.dumps({'package_path': work_dir}), example_finding]
303 diagnostic = next(tricium_cargo_clippy.parse_diagnostics('', inputs, ''))
304 self.assertEqual(next(diagnostic.locations).file_path, expected_location)
305
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000306 def test_parse_level(self):
307 """Tests that parse_level is as expected."""
308 for i, (test_case, exp_results) in enumerate(valid_test_cases.items()):
309 if 'level' not in exp_results:
310 continue
311 test_json = json.loads(test_case)
312 level = tricium_cargo_clippy.parse_level('valid', i, test_json)
313 self.assertEqual(level, exp_results['level'])
314
315 def test_parse_message(self):
316 """Tests that parse_message is as expected."""
317 for i, (test_case, exp_results) in enumerate(valid_test_cases.items()):
318 if 'message' not in exp_results:
319 continue
320 test_json = json.loads(test_case)
321 message = tricium_cargo_clippy.parse_message('valid', i, test_json)
322 self.assertEqual(message, exp_results['message'])
323
324 def test_parse_diagnostics(self):
325 """Tests that parse_diagnostics yields correct diagnostics."""
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000326 package_path_json = json.dumps({'package_path': test_package_path})
327 orig_jsons = [package_path_json] + list(valid_test_cases.keys())
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000328 diags = list(tricium_cargo_clippy.parse_diagnostics(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000329 'valid_test_cases', orig_jsons, test_git_repo))
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000330
331 # Verify parse_diagnostics retrieved correct amount of diagnostics
332 exp_len = len([
333 values for values in valid_test_cases.values()
334 if not values.get('skipped')
335 ])
336 self.assertEqual(len(diags), exp_len)
337
338 # Verify diagnostics are from correct source
339 for i, diag in enumerate(diags):
340 locations = list(diag.locations)
341 expected_locations = list(valid_test_cases.values())[i].get('locations')
342 self.assertEqual(locations, expected_locations)
343
344 def test_logs_invalid_parse_diagnostic_cases(self):
345 """Tests that parse_diagnostics logs proper exceptions."""
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000346 package_path_json = json.dumps({'package_path': test_package_path})
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000347 for invalid_case, exp_errors in invalid_test_cases.items():
348 with self.assertRaises(
349 tricium_cargo_clippy.Error,
350 msg=f'Expected error parsing {invalid_case} but got none.') as ctx:
Ryan Beltran923a1312021-07-30 00:28:13 +0000351 list(tricium_cargo_clippy.parse_diagnostics(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000352 'invalid', [package_path_json, invalid_case], test_git_repo))
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000353 if 'json' in exp_errors:
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000354 exp_error = tricium_cargo_clippy.CargoClippyJSONError('invalid', 1)
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000355 elif 'reason' in exp_errors:
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000356 exp_error = tricium_cargo_clippy.CargoClippyReasonError('invalid', 1)
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000357 else:
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000358 for field in ('locations', 'level', 'message'):
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000359 if field in exp_errors:
360 exp_error = tricium_cargo_clippy.CargoClippyFieldError(
Ryan Beltranc0fa16a2021-08-05 20:45:14 +0000361 'invalid', 1, field)
Ryan Beltrancfc5c362021-03-02 18:36:18 +0000362 break
363 self.assertIs(type(ctx.exception), type(exp_error))
364 self.assertEqual(ctx.exception.args, exp_error.args)
365
Ryan Beltran43a00662021-05-17 16:55:24 +0000366 def test_filter_diagnostics(self):
Ryan Beltran923a1312021-07-30 00:28:13 +0000367 file_path = 'some_filepath.json'
Ryan Beltran43a00662021-05-17 16:55:24 +0000368 example_code_location = tricium_cargo_clippy.CodeLocation(
Ryan Beltran923a1312021-07-30 00:28:13 +0000369 file_path=file_path,
Ryan Beltran43a00662021-05-17 16:55:24 +0000370 line_start=1,
371 line_end=4,
372 column_start=0,
373 column_end=12
374 )
375 accepted_diags = [
376 tricium_cargo_clippy.ClippyDiagnostic(
Ryan Beltran43a00662021-05-17 16:55:24 +0000377 locations=[example_code_location],
378 level='warning',
379 message='warning: be warned.'
380 )
381 ]
382 ignored_diags = [
Ryan Beltran43a00662021-05-17 16:55:24 +0000383 # "aborting due to previous error" messages
384 tricium_cargo_clippy.ClippyDiagnostic(
Ryan Beltran43a00662021-05-17 16:55:24 +0000385 locations=[example_code_location],
386 level='warning',
387 message='warning: aborting due to previous error...'
388 ),
389 # No locations provided
390 tricium_cargo_clippy.ClippyDiagnostic(
Ryan Beltran43a00662021-05-17 16:55:24 +0000391 locations=[],
392 level='warning',
393 message='warning: 6 warnings emitted.'
394 )
395 ]
396 filtered_diags = list(tricium_cargo_clippy.filter_diagnostics(
Ryan Beltran923a1312021-07-30 00:28:13 +0000397 accepted_diags + ignored_diags))
Ryan Beltran43a00662021-05-17 16:55:24 +0000398 self.assertEqual(filtered_diags, accepted_diags)