blob: 385efedc8516f7a862785423bbcfe917a32d43c4 [file] [log] [blame]
Simon Glassdf692c32020-12-28 20:35:07 -07001# SPDX-License-Identifier: GPL-2.0+
2# Copyright 2020 Google LLC
3#
4
5"""Tests for the src_scan module
6
7This includes unit tests for scanning of the source code
8"""
9
Simon Glass2e199ab2021-02-03 06:00:54 -070010import copy
Simon Glassdf692c32020-12-28 20:35:07 -070011import os
12import shutil
13import tempfile
14import unittest
15from unittest import mock
16
17from dtoc import src_scan
Simon Glass14d64e32025-04-29 07:21:59 -060018from u_boot_pylib import terminal
Simon Glass131444f2023-02-23 18:18:04 -070019from u_boot_pylib import test_util
20from u_boot_pylib import tools
Simon Glassdf692c32020-12-28 20:35:07 -070021
Simon Glass78933b72021-02-03 06:00:50 -070022OUR_PATH = os.path.dirname(os.path.realpath(__file__))
23
Simon Glass240f0b12021-07-04 12:19:48 -060024EXPECT_WARN = {'rockchip_rk3288_grf':
25 {'WARNING: the driver rockchip_rk3288_grf was not found in the driver list'}}
26
Simon Glass78933b72021-02-03 06:00:50 -070027class FakeNode:
28 """Fake Node object for testing"""
29 def __init__(self):
30 self.name = None
31 self.props = {}
32
33class FakeProp:
34 """Fake Prop object for testing"""
35 def __init__(self):
36 self.name = None
37 self.value = None
38
Simon Glassdf692c32020-12-28 20:35:07 -070039# This is a test so is allowed to access private things in the module it is
40# testing
41# pylint: disable=W0212
42
43class TestSrcScan(unittest.TestCase):
44 """Tests for src_scan"""
45 @classmethod
46 def setUpClass(cls):
Simon Glass80025522022-01-29 14:14:04 -070047 tools.prepare_output_dir(None)
Simon Glassdf692c32020-12-28 20:35:07 -070048
49 @classmethod
50 def tearDownClass(cls):
Simon Glass80025522022-01-29 14:14:04 -070051 tools.finalise_output_dir()
Simon Glassdf692c32020-12-28 20:35:07 -070052
Simon Glassb430e9e2020-12-28 20:35:08 -070053 def test_simple(self):
54 """Simple test of scanning drivers"""
Simon Glass695077d2021-03-26 16:17:25 +130055 scan = src_scan.Scanner(None, None)
Simon Glassb430e9e2020-12-28 20:35:08 -070056 scan.scan_drivers()
57 self.assertIn('sandbox_gpio', scan._drivers)
58 self.assertIn('sandbox_gpio_alias', scan._driver_aliases)
59 self.assertEqual('sandbox_gpio',
60 scan._driver_aliases['sandbox_gpio_alias'])
61 self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases)
62
63 def test_additional(self):
64 """Test with additional drivers to scan"""
Simon Glassdf692c32020-12-28 20:35:07 -070065 scan = src_scan.Scanner(
Simon Glass695077d2021-03-26 16:17:25 +130066 None, [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx'])
Simon Glassdf692c32020-12-28 20:35:07 -070067 scan.scan_drivers()
Simon Glassb430e9e2020-12-28 20:35:08 -070068 self.assertIn('sandbox_gpio_alias2', scan._driver_aliases)
69 self.assertEqual('sandbox_gpio',
70 scan._driver_aliases['sandbox_gpio_alias2'])
Simon Glassdf692c32020-12-28 20:35:07 -070071
Simon Glassb430e9e2020-12-28 20:35:08 -070072 def test_unicode_error(self):
Simon Glassdf692c32020-12-28 20:35:07 -070073 """Test running dtoc with an invalid unicode file
74
75 To be able to perform this test without adding a weird text file which
76 would produce issues when using checkpatch.pl or patman, generate the
77 file at runtime and then process it.
78 """
79 driver_fn = '/tmp/' + next(tempfile._get_candidate_names())
80 with open(driver_fn, 'wb+') as fout:
81 fout.write(b'\x81')
82
Simon Glass695077d2021-03-26 16:17:25 +130083 scan = src_scan.Scanner(None, [driver_fn])
Simon Glass14d64e32025-04-29 07:21:59 -060084 with terminal.capture() as (stdout, _):
Simon Glassb430e9e2020-12-28 20:35:08 -070085 scan.scan_drivers()
86 self.assertRegex(stdout.getvalue(),
87 r"Skipping file '.*' due to unicode error\s*")
Simon Glassdf692c32020-12-28 20:35:07 -070088
89 def test_driver(self):
90 """Test the Driver class"""
Simon Glass78933b72021-02-03 06:00:50 -070091 i2c = 'I2C_UCLASS'
92 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
93 'rockchip,rk3288-srf': None}
94 drv1 = src_scan.Driver('fred', 'fred.c')
95 drv2 = src_scan.Driver('mary', 'mary.c')
96 drv3 = src_scan.Driver('fred', 'fred.c')
97 drv1.uclass_id = i2c
98 drv1.compat = compat
99 drv2.uclass_id = i2c
100 drv2.compat = compat
101 drv3.uclass_id = i2c
102 drv3.compat = compat
103 self.assertEqual(
Simon Glasseb3c2492021-02-03 06:01:01 -0700104 "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', "
Simon Glass78933b72021-02-03 06:00:50 -0700105 "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', "
106 "'rockchip,rk3288-srf': None}, priv=)", str(drv1))
Simon Glassdf692c32020-12-28 20:35:07 -0700107 self.assertEqual(drv1, drv3)
108 self.assertNotEqual(drv1, drv2)
109 self.assertNotEqual(drv2, drv3)
110
111 def test_scan_dirs(self):
112 """Test scanning of source directories"""
113 def add_file(fname):
114 pathname = os.path.join(indir, fname)
115 dirname = os.path.dirname(pathname)
116 os.makedirs(dirname, exist_ok=True)
Simon Glass80025522022-01-29 14:14:04 -0700117 tools.write_file(pathname, '', binary=False)
Simon Glassdf692c32020-12-28 20:35:07 -0700118 fname_list.append(pathname)
119
120 try:
121 indir = tempfile.mkdtemp(prefix='dtoc.')
122
123 fname_list = []
124 add_file('fname.c')
Simon Glassdc37c812021-02-03 06:00:52 -0700125 add_file('.git/ignoreme.c')
Simon Glassdf692c32020-12-28 20:35:07 -0700126 add_file('dir/fname2.c')
Simon Glassdc37c812021-02-03 06:00:52 -0700127 add_file('build-sandbox/ignoreme2.c')
Simon Glassdf692c32020-12-28 20:35:07 -0700128
129 # Mock out scan_driver and check that it is called with the
130 # expected files
131 with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked:
Simon Glass695077d2021-03-26 16:17:25 +1300132 scan = src_scan.Scanner(indir, None)
Simon Glassdf692c32020-12-28 20:35:07 -0700133 scan.scan_drivers()
134 self.assertEqual(2, len(mocked.mock_calls))
135 self.assertEqual(mock.call(fname_list[0]),
136 mocked.mock_calls[0])
Simon Glassdc37c812021-02-03 06:00:52 -0700137 # .git file should be ignored
138 self.assertEqual(mock.call(fname_list[2]),
Simon Glassdf692c32020-12-28 20:35:07 -0700139 mocked.mock_calls[1])
140 finally:
141 shutil.rmtree(indir)
Simon Glass78933b72021-02-03 06:00:50 -0700142
143 def test_scan(self):
144 """Test scanning of a driver"""
145 fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c')
Simon Glass80025522022-01-29 14:14:04 -0700146 buff = tools.read_file(fname, False)
Simon Glass695077d2021-03-26 16:17:25 +1300147 scan = src_scan.Scanner(None, None)
Simon Glass78933b72021-02-03 06:00:50 -0700148 scan._parse_driver(fname, buff)
149 self.assertIn('i2c_tegra', scan._drivers)
150 drv = scan._drivers['i2c_tegra']
151 self.assertEqual('i2c_tegra', drv.name)
152 self.assertEqual('UCLASS_I2C', drv.uclass_id)
153 self.assertEqual(
154 {'nvidia,tegra114-i2c': 'TYPE_114',
Tom Rinieb6e70b2022-06-14 13:59:23 -0400155 'nvidia,tegra124-i2c': 'TYPE_114',
Simon Glass78933b72021-02-03 06:00:50 -0700156 'nvidia,tegra20-i2c': 'TYPE_STD',
157 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
158 self.assertEqual('i2c_bus', drv.priv)
159 self.assertEqual(1, len(scan._drivers))
Simon Glass240f0b12021-07-04 12:19:48 -0600160 self.assertEqual({}, scan._warnings)
Simon Glass78933b72021-02-03 06:00:50 -0700161
162 def test_normalized_name(self):
163 """Test operation of get_normalized_compat_name()"""
164 prop = FakeProp()
165 prop.name = 'compatible'
166 prop.value = 'rockchip,rk3288-grf'
167 node = FakeNode()
168 node.props = {'compatible': prop}
Simon Glassc14fd0c2021-02-03 06:01:11 -0700169
170 # get_normalized_compat_name() uses this to check for root node
171 node.parent = FakeNode()
172
Simon Glass695077d2021-03-26 16:17:25 +1300173 scan = src_scan.Scanner(None, None)
Simon Glass14d64e32025-04-29 07:21:59 -0600174 with terminal.capture() as (stdout, _):
Simon Glass78933b72021-02-03 06:00:50 -0700175 name, aliases = scan.get_normalized_compat_name(node)
176 self.assertEqual('rockchip_rk3288_grf', name)
177 self.assertEqual([], aliases)
Simon Glass695077d2021-03-26 16:17:25 +1300178 self.assertEqual(1, len(scan._missing_drivers))
179 self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers)
Simon Glass9dc87d32021-07-04 12:19:47 -0600180 self.assertEqual('', stdout.getvalue().strip())
Simon Glass240f0b12021-07-04 12:19:48 -0600181 self.assertEqual(EXPECT_WARN, scan._warnings)
Simon Glass78933b72021-02-03 06:00:50 -0700182
183 i2c = 'I2C_UCLASS'
184 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
185 'rockchip,rk3288-srf': None}
186 drv = src_scan.Driver('fred', 'fred.c')
187 drv.uclass_id = i2c
188 drv.compat = compat
189 scan._drivers['rockchip_rk3288_grf'] = drv
190
191 scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf'
192
Simon Glass14d64e32025-04-29 07:21:59 -0600193 with terminal.capture() as (stdout, _):
Simon Glass78933b72021-02-03 06:00:50 -0700194 name, aliases = scan.get_normalized_compat_name(node)
195 self.assertEqual('', stdout.getvalue().strip())
196 self.assertEqual('rockchip_rk3288_grf', name)
197 self.assertEqual([], aliases)
Simon Glass240f0b12021-07-04 12:19:48 -0600198 self.assertEqual(EXPECT_WARN, scan._warnings)
Simon Glass78933b72021-02-03 06:00:50 -0700199
200 prop.value = 'rockchip,rk3288-srf'
Simon Glass14d64e32025-04-29 07:21:59 -0600201 with terminal.capture() as (stdout, _):
Simon Glass78933b72021-02-03 06:00:50 -0700202 name, aliases = scan.get_normalized_compat_name(node)
203 self.assertEqual('', stdout.getvalue().strip())
204 self.assertEqual('rockchip_rk3288_grf', name)
205 self.assertEqual(['rockchip_rk3288_srf'], aliases)
Simon Glass240f0b12021-07-04 12:19:48 -0600206 self.assertEqual(EXPECT_WARN, scan._warnings)
Simon Glass78933b72021-02-03 06:00:50 -0700207
208 def test_scan_errors(self):
209 """Test detection of scanning errors"""
210 buff = '''
211static const struct udevice_id tegra_i2c_ids2[] = {
212 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
213 { }
214};
215
216U_BOOT_DRIVER(i2c_tegra) = {
217 .name = "i2c_tegra",
218 .id = UCLASS_I2C,
219 .of_match = tegra_i2c_ids,
220};
221'''
Simon Glass695077d2021-03-26 16:17:25 +1300222 scan = src_scan.Scanner(None, None)
Simon Glass78933b72021-02-03 06:00:50 -0700223 with self.assertRaises(ValueError) as exc:
224 scan._parse_driver('file.c', buff)
225 self.assertIn(
226 "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)",
227 str(exc.exception))
228
229 def test_of_match(self):
230 """Test detection of of_match_ptr() member"""
231 buff = '''
232static const struct udevice_id tegra_i2c_ids[] = {
233 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
234 { }
235};
236
237U_BOOT_DRIVER(i2c_tegra) = {
238 .name = "i2c_tegra",
239 .id = UCLASS_I2C,
240 .of_match = of_match_ptr(tegra_i2c_ids),
241};
242'''
Simon Glass695077d2021-03-26 16:17:25 +1300243 scan = src_scan.Scanner(None, None)
Simon Glass78933b72021-02-03 06:00:50 -0700244 scan._parse_driver('file.c', buff)
245 self.assertIn('i2c_tegra', scan._drivers)
246 drv = scan._drivers['i2c_tegra']
247 self.assertEqual('i2c_tegra', drv.name)
Simon Glassf303ee72021-02-03 06:01:02 -0700248 self.assertEqual('', drv.phase)
Simon Glassa7b1c772021-02-03 06:01:04 -0700249 self.assertEqual([], drv.headers)
Simon Glass0f3b1412021-02-03 06:00:53 -0700250
251 def test_priv(self):
252 """Test collection of struct info from drivers"""
253 buff = '''
254static const struct udevice_id test_ids[] = {
255 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
256 { }
257};
258
259U_BOOT_DRIVER(testing) = {
260 .name = "testing",
261 .id = UCLASS_I2C,
262 .of_match = test_ids,
263 .priv_auto = sizeof(struct some_priv),
264 .plat_auto = sizeof(struct some_plat),
265 .per_child_auto = sizeof(struct some_cpriv),
266 .per_child_plat_auto = sizeof(struct some_cplat),
Simon Glassf303ee72021-02-03 06:01:02 -0700267 DM_PHASE(tpl)
Simon Glassa7b1c772021-02-03 06:01:04 -0700268 DM_HEADER(<i2c.h>)
269 DM_HEADER(<asm/clk.h>)
Simon Glass0f3b1412021-02-03 06:00:53 -0700270};
271'''
Simon Glass695077d2021-03-26 16:17:25 +1300272 scan = src_scan.Scanner(None, None)
Simon Glass0f3b1412021-02-03 06:00:53 -0700273 scan._parse_driver('file.c', buff)
274 self.assertIn('testing', scan._drivers)
275 drv = scan._drivers['testing']
276 self.assertEqual('testing', drv.name)
277 self.assertEqual('UCLASS_I2C', drv.uclass_id)
278 self.assertEqual(
279 {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat)
280 self.assertEqual('some_priv', drv.priv)
281 self.assertEqual('some_plat', drv.plat)
282 self.assertEqual('some_cpriv', drv.child_priv)
283 self.assertEqual('some_cplat', drv.child_plat)
Simon Glassf303ee72021-02-03 06:01:02 -0700284 self.assertEqual('tpl', drv.phase)
Simon Glassa7b1c772021-02-03 06:01:04 -0700285 self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers)
Simon Glass0f3b1412021-02-03 06:00:53 -0700286 self.assertEqual(1, len(scan._drivers))
Simon Glass2e199ab2021-02-03 06:00:54 -0700287
288 def test_uclass_scan(self):
289 """Test collection of uclass-driver info"""
290 buff = '''
291UCLASS_DRIVER(i2c) = {
292 .id = UCLASS_I2C,
293 .name = "i2c",
294 .flags = DM_UC_FLAG_SEQ_ALIAS,
295 .priv_auto = sizeof(struct some_priv),
296 .per_device_auto = sizeof(struct per_dev_priv),
297 .per_device_plat_auto = sizeof(struct per_dev_plat),
298 .per_child_auto = sizeof(struct per_child_priv),
299 .per_child_plat_auto = sizeof(struct per_child_plat),
300 .child_post_bind = i2c_child_post_bind,
301};
302
303'''
Simon Glass695077d2021-03-26 16:17:25 +1300304 scan = src_scan.Scanner(None, None)
Simon Glass2e199ab2021-02-03 06:00:54 -0700305 scan._parse_uclass_driver('file.c', buff)
306 self.assertIn('UCLASS_I2C', scan._uclass)
307 drv = scan._uclass['UCLASS_I2C']
308 self.assertEqual('i2c', drv.name)
309 self.assertEqual('UCLASS_I2C', drv.uclass_id)
310 self.assertEqual('some_priv', drv.priv)
311 self.assertEqual('per_dev_priv', drv.per_dev_priv)
312 self.assertEqual('per_dev_plat', drv.per_dev_plat)
313 self.assertEqual('per_child_priv', drv.per_child_priv)
314 self.assertEqual('per_child_plat', drv.per_child_plat)
315 self.assertEqual(1, len(scan._uclass))
316
317 drv2 = copy.deepcopy(drv)
318 self.assertEqual(drv, drv2)
319 drv2.priv = 'other_priv'
320 self.assertNotEqual(drv, drv2)
321
322 # The hashes only depend on the uclass ID, so should be equal
323 self.assertEqual(drv.__hash__(), drv2.__hash__())
324
325 self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')",
326 str(drv))
327
328 def test_uclass_scan_errors(self):
329 """Test detection of uclass scanning errors"""
330 buff = '''
331UCLASS_DRIVER(i2c) = {
332 .name = "i2c",
333};
334
335'''
Simon Glass695077d2021-03-26 16:17:25 +1300336 scan = src_scan.Scanner(None, None)
Simon Glass2e199ab2021-02-03 06:00:54 -0700337 with self.assertRaises(ValueError) as exc:
338 scan._parse_uclass_driver('file.c', buff)
339 self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'",
340 str(exc.exception))
Simon Glass88bd5382021-02-03 06:00:55 -0700341
342 def test_struct_scan(self):
343 """Test collection of struct info"""
344 buff = '''
345/* some comment */
346struct some_struct1 {
347 struct i2c_msg *msgs;
348 uint nmsgs;
349};
350'''
Simon Glass695077d2021-03-26 16:17:25 +1300351 scan = src_scan.Scanner(None, None)
Simon Glass88bd5382021-02-03 06:00:55 -0700352 scan._basedir = os.path.join(OUR_PATH, '..', '..')
353 scan._parse_structs('arch/arm/include/asm/file.h', buff)
354 self.assertIn('some_struct1', scan._structs)
355 struc = scan._structs['some_struct1']
356 self.assertEqual('some_struct1', struc.name)
357 self.assertEqual('asm/file.h', struc.fname)
358
359 buff = '''
360/* another comment */
361struct another_struct {
362 int speed_hz;
363 int max_transaction_bytes;
364};
365'''
366 scan._parse_structs('include/file2.h', buff)
367 self.assertIn('another_struct', scan._structs)
368 struc = scan._structs['another_struct']
369 self.assertEqual('another_struct', struc.name)
370 self.assertEqual('file2.h', struc.fname)
371
372 self.assertEqual(2, len(scan._structs))
373
374 self.assertEqual("Struct(name='another_struct', fname='file2.h')",
375 str(struc))
376
377 def test_struct_scan_errors(self):
378 """Test scanning a header file with an invalid unicode file"""
Simon Glass80025522022-01-29 14:14:04 -0700379 output = tools.get_output_filename('output.h')
380 tools.write_file(output, b'struct this is a test \x81 of bad unicode')
Simon Glass88bd5382021-02-03 06:00:55 -0700381
Simon Glass695077d2021-03-26 16:17:25 +1300382 scan = src_scan.Scanner(None, None)
Simon Glass14d64e32025-04-29 07:21:59 -0600383 with terminal.capture() as (stdout, _):
Simon Glass88bd5382021-02-03 06:00:55 -0700384 scan.scan_header(output)
385 self.assertIn('due to unicode error', stdout.getvalue())
Simon Glass9b2eac02021-02-03 06:01:06 -0700386
387 def setup_dup_drivers(self, name, phase=''):
388 """Set up for a duplcate test
389
390 Returns:
391 tuple:
392 Scanner to use
393 Driver record for first driver
394 Text of second driver declaration
395 Node for driver 1
396 """
397 driver1 = '''
398static const struct udevice_id test_ids[] = {
399 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
400 { }
401};
402
403U_BOOT_DRIVER(%s) = {
404 .name = "testing",
405 .id = UCLASS_I2C,
406 .of_match = test_ids,
407 %s
408};
409''' % (name, 'DM_PHASE(%s)' % phase if phase else '')
410 driver2 = '''
411static const struct udevice_id test_ids[] = {
412 { .compatible = "nvidia,tegra114-dvc" },
413 { }
414};
415
416U_BOOT_DRIVER(%s) = {
417 .name = "testing",
418 .id = UCLASS_RAM,
419 .of_match = test_ids,
420};
421''' % name
Simon Glass695077d2021-03-26 16:17:25 +1300422 scan = src_scan.Scanner(None, None, phase)
Simon Glass9b2eac02021-02-03 06:01:06 -0700423 scan._parse_driver('file1.c', driver1)
424 self.assertIn(name, scan._drivers)
425 drv1 = scan._drivers[name]
426
427 prop = FakeProp()
428 prop.name = 'compatible'
429 prop.value = 'nvidia,tegra114-i2c'
430 node = FakeNode()
431 node.name = 'testing'
432 node.props = {'compatible': prop}
433
Simon Glassc14fd0c2021-02-03 06:01:11 -0700434 # get_normalized_compat_name() uses this to check for root node
435 node.parent = FakeNode()
436
Simon Glass9b2eac02021-02-03 06:01:06 -0700437 return scan, drv1, driver2, node
438
439 def test_dup_drivers(self):
440 """Test handling of duplicate drivers"""
441 name = 'nvidia_tegra114_i2c'
442 scan, drv1, driver2, node = self.setup_dup_drivers(name)
443 self.assertEqual('', drv1.phase)
444
445 # The driver should not have a duplicate yet
446 self.assertEqual([], drv1.dups)
447
448 scan._parse_driver('file2.c', driver2)
449
450 # The first driver should now be a duplicate of the second
451 drv2 = scan._drivers[name]
452 self.assertEqual('', drv2.phase)
453 self.assertEqual(1, len(drv2.dups))
454 self.assertEqual([drv1], drv2.dups)
455
456 # There is no way to distinguish them, so we should expect a warning
457 self.assertTrue(drv2.warn_dups)
458
459 # We should see a warning
Simon Glass14d64e32025-04-29 07:21:59 -0600460 with terminal.capture() as (stdout, _):
Simon Glass9b2eac02021-02-03 06:01:06 -0700461 scan.mark_used([node])
462 self.assertEqual(
463 "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)",
464 stdout.getvalue().strip())
465
466 def test_dup_drivers_phase(self):
467 """Test handling of duplicate drivers but with different phases"""
468 name = 'nvidia_tegra114_i2c'
469 scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl')
470 scan._parse_driver('file2.c', driver2)
471 self.assertEqual('spl', drv1.phase)
472
473 # The second driver should now be a duplicate of the second
474 self.assertEqual(1, len(drv1.dups))
475 drv2 = drv1.dups[0]
476
477 # The phase is different, so we should not warn of dups
478 self.assertFalse(drv1.warn_dups)
479
480 # We should not see a warning
Simon Glass14d64e32025-04-29 07:21:59 -0600481 with terminal.capture() as (stdout, _):
Simon Glass9b2eac02021-02-03 06:01:06 -0700482 scan.mark_used([node])
483 self.assertEqual('', stdout.getvalue().strip())
Simon Glass80d782c42021-02-03 06:01:10 -0700484
485 def test_sequence(self):
486 """Test assignment of sequence numnbers"""
Simon Glass695077d2021-03-26 16:17:25 +1300487 scan = src_scan.Scanner(None, None, '')
Simon Glass80d782c42021-02-03 06:01:10 -0700488 node = FakeNode()
489 uc = src_scan.UclassDriver('UCLASS_I2C')
490 node.uclass = uc
491 node.driver = True
492 node.seq = -1
493 node.path = 'mypath'
494 uc.alias_num_to_node[2] = node
495
496 # This should assign 3 (after the 2 that exists)
497 seq = scan.assign_seq(node)
498 self.assertEqual(3, seq)
499 self.assertEqual({'mypath': 3}, uc.alias_path_to_num)
500 self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)
Simon Glass240f0b12021-07-04 12:19:48 -0600501
502 def test_scan_warnings(self):
503 """Test detection of scanning warnings"""
504 buff = '''
505static const struct udevice_id tegra_i2c_ids[] = {
506 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
507 { }
508};
509
510U_BOOT_DRIVER(i2c_tegra) = {
511 .name = "i2c_tegra",
512 .id = UCLASS_I2C,
513 .of_match = tegra_i2c_ids + 1,
514};
515'''
516 # The '+ 1' above should generate a warning
517
518 prop = FakeProp()
519 prop.name = 'compatible'
520 prop.value = 'rockchip,rk3288-grf'
521 node = FakeNode()
522 node.props = {'compatible': prop}
523
524 # get_normalized_compat_name() uses this to check for root node
525 node.parent = FakeNode()
526
527 scan = src_scan.Scanner(None, None)
528 scan._parse_driver('file.c', buff)
529 self.assertEqual(
530 {'i2c_tegra':
531 {"file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'"}},
532 scan._warnings)
533
534 tprop = FakeProp()
535 tprop.name = 'compatible'
536 tprop.value = 'nvidia,tegra114-i2c'
537 tnode = FakeNode()
538 tnode.props = {'compatible': tprop}
539
540 # get_normalized_compat_name() uses this to check for root node
541 tnode.parent = FakeNode()
542
Simon Glass14d64e32025-04-29 07:21:59 -0600543 with terminal.capture() as (stdout, _):
Simon Glass240f0b12021-07-04 12:19:48 -0600544 scan.get_normalized_compat_name(node)
545 scan.get_normalized_compat_name(tnode)
546 self.assertEqual('', stdout.getvalue().strip())
547
548 self.assertEqual(2, len(scan._missing_drivers))
549 self.assertEqual({'rockchip_rk3288_grf', 'nvidia_tegra114_i2c'},
550 scan._missing_drivers)
Simon Glass14d64e32025-04-29 07:21:59 -0600551 with terminal.capture() as (stdout, _):
Simon Glass240f0b12021-07-04 12:19:48 -0600552 scan.show_warnings()
553 self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
554
555 # This should show just the rockchip warning, since the tegra driver
556 # is not in self._missing_drivers
557 scan._missing_drivers.remove('nvidia_tegra114_i2c')
Simon Glass14d64e32025-04-29 07:21:59 -0600558 with terminal.capture() as (stdout, _):
Simon Glass240f0b12021-07-04 12:19:48 -0600559 scan.show_warnings()
560 self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
561 self.assertNotIn('tegra_i2c_ids', stdout.getvalue())
562
563 # Do a similar thing with used drivers. By marking the tegra driver as
564 # used, the warning related to that driver will be shown
565 drv = scan._drivers['i2c_tegra']
566 drv.used = True
Simon Glass14d64e32025-04-29 07:21:59 -0600567 with terminal.capture() as (stdout, _):
Simon Glass240f0b12021-07-04 12:19:48 -0600568 scan.show_warnings()
569 self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
570 self.assertIn('tegra_i2c_ids', stdout.getvalue())
571
572 # Add a warning to make sure multiple warnings are shown
573 scan._warnings['i2c_tegra'].update(
574 scan._warnings['nvidia_tegra114_i2c'])
575 del scan._warnings['nvidia_tegra114_i2c']
Simon Glass14d64e32025-04-29 07:21:59 -0600576 with terminal.capture() as (stdout, _):
Simon Glass240f0b12021-07-04 12:19:48 -0600577 scan.show_warnings()
578 self.assertEqual('''i2c_tegra: WARNING: the driver nvidia_tegra114_i2c was not found in the driver list
579 : file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'
580
581rockchip_rk3288_grf: WARNING: the driver rockchip_rk3288_grf was not found in the driver list
582
583''',
584 stdout.getvalue())
585 self.assertIn('tegra_i2c_ids', stdout.getvalue())
Simon Glass57c325a2021-07-04 12:19:49 -0600586
587 def scan_uclass_warning(self):
588 """Test a missing .uclass in the driver"""
589 buff = '''
590static const struct udevice_id tegra_i2c_ids[] = {
591 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
592 { }
593};
594
595U_BOOT_DRIVER(i2c_tegra) = {
596 .name = "i2c_tegra",
597 .of_match = tegra_i2c_ids,
598};
599'''
600 scan = src_scan.Scanner(None, None)
601 scan._parse_driver('file.c', buff)
602 self.assertEqual(
603 {'i2c_tegra': {'Missing .uclass in file.c'}},
604 scan._warnings)
605
606 def scan_compat_warning(self):
607 """Test a missing .compatible in the driver"""
608 buff = '''
609static const struct udevice_id tegra_i2c_ids[] = {
610 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
611 { }
612};
613
614U_BOOT_DRIVER(i2c_tegra) = {
615 .name = "i2c_tegra",
616 .id = UCLASS_I2C,
617};
618'''
619 scan = src_scan.Scanner(None, None)
620 scan._parse_driver('file.c', buff)
621 self.assertEqual(
622 {'i2c_tegra': {'Missing .compatible in file.c'}},
623 scan._warnings)