1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright 2020 Google LLC
5 """Tests for the src_scan module
7 This includes unit tests for scanning of the source code
15 from unittest import mock
17 from dtoc import src_scan
18 from patman import test_util
19 from patman import tools
21 OUR_PATH = os.path.dirname(os.path.realpath(__file__))
23 EXPECT_WARN = {'rockchip_rk3288_grf':
24 {'WARNING: the driver rockchip_rk3288_grf was not found in the driver list'}}
27 """Fake Node object for testing"""
33 """Fake Prop object for testing"""
38 # This is a test so is allowed to access private things in the module it is
40 # pylint: disable=W0212
42 class TestSrcScan(unittest.TestCase):
43 """Tests for src_scan"""
46 tools.prepare_output_dir(None)
49 def tearDownClass(cls):
50 tools.finalise_output_dir()
52 def test_simple(self):
53 """Simple test of scanning drivers"""
54 scan = src_scan.Scanner(None, None)
56 self.assertIn('sandbox_gpio', scan._drivers)
57 self.assertIn('sandbox_gpio_alias', scan._driver_aliases)
58 self.assertEqual('sandbox_gpio',
59 scan._driver_aliases['sandbox_gpio_alias'])
60 self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases)
62 def test_additional(self):
63 """Test with additional drivers to scan"""
64 scan = src_scan.Scanner(
65 None, [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx'])
67 self.assertIn('sandbox_gpio_alias2', scan._driver_aliases)
68 self.assertEqual('sandbox_gpio',
69 scan._driver_aliases['sandbox_gpio_alias2'])
71 def test_unicode_error(self):
72 """Test running dtoc with an invalid unicode file
74 To be able to perform this test without adding a weird text file which
75 would produce issues when using checkpatch.pl or patman, generate the
76 file at runtime and then process it.
78 driver_fn = '/tmp/' + next(tempfile._get_candidate_names())
79 with open(driver_fn, 'wb+') as fout:
82 scan = src_scan.Scanner(None, [driver_fn])
83 with test_util.capture_sys_output() as (stdout, _):
85 self.assertRegex(stdout.getvalue(),
86 r"Skipping file '.*' due to unicode error\s*")
88 def test_driver(self):
89 """Test the Driver class"""
91 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
92 'rockchip,rk3288-srf': None}
93 drv1 = src_scan.Driver('fred', 'fred.c')
94 drv2 = src_scan.Driver('mary', 'mary.c')
95 drv3 = src_scan.Driver('fred', 'fred.c')
103 "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', "
104 "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', "
105 "'rockchip,rk3288-srf': None}, priv=)", str(drv1))
106 self.assertEqual(drv1, drv3)
107 self.assertNotEqual(drv1, drv2)
108 self.assertNotEqual(drv2, drv3)
110 def test_scan_dirs(self):
111 """Test scanning of source directories"""
113 pathname = os.path.join(indir, fname)
114 dirname = os.path.dirname(pathname)
115 os.makedirs(dirname, exist_ok=True)
116 tools.write_file(pathname, '', binary=False)
117 fname_list.append(pathname)
120 indir = tempfile.mkdtemp(prefix='dtoc.')
124 add_file('.git/ignoreme.c')
125 add_file('dir/fname2.c')
126 add_file('build-sandbox/ignoreme2.c')
128 # Mock out scan_driver and check that it is called with the
130 with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked:
131 scan = src_scan.Scanner(indir, None)
133 self.assertEqual(2, len(mocked.mock_calls))
134 self.assertEqual(mock.call(fname_list[0]),
135 mocked.mock_calls[0])
136 # .git file should be ignored
137 self.assertEqual(mock.call(fname_list[2]),
138 mocked.mock_calls[1])
143 """Test scanning of a driver"""
144 fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c')
145 buff = tools.read_file(fname, False)
146 scan = src_scan.Scanner(None, None)
147 scan._parse_driver(fname, buff)
148 self.assertIn('i2c_tegra', scan._drivers)
149 drv = scan._drivers['i2c_tegra']
150 self.assertEqual('i2c_tegra', drv.name)
151 self.assertEqual('UCLASS_I2C', drv.uclass_id)
153 {'nvidia,tegra114-i2c': 'TYPE_114',
154 'nvidia,tegra124-i2c': 'TYPE_114',
155 'nvidia,tegra20-i2c': 'TYPE_STD',
156 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
157 self.assertEqual('i2c_bus', drv.priv)
158 self.assertEqual(1, len(scan._drivers))
159 self.assertEqual({}, scan._warnings)
161 def test_normalized_name(self):
162 """Test operation of get_normalized_compat_name()"""
164 prop.name = 'compatible'
165 prop.value = 'rockchip,rk3288-grf'
167 node.props = {'compatible': prop}
169 # get_normalized_compat_name() uses this to check for root node
170 node.parent = FakeNode()
172 scan = src_scan.Scanner(None, None)
173 with test_util.capture_sys_output() as (stdout, _):
174 name, aliases = scan.get_normalized_compat_name(node)
175 self.assertEqual('rockchip_rk3288_grf', name)
176 self.assertEqual([], aliases)
177 self.assertEqual(1, len(scan._missing_drivers))
178 self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers)
179 self.assertEqual('', stdout.getvalue().strip())
180 self.assertEqual(EXPECT_WARN, scan._warnings)
183 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
184 'rockchip,rk3288-srf': None}
185 drv = src_scan.Driver('fred', 'fred.c')
188 scan._drivers['rockchip_rk3288_grf'] = drv
190 scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf'
192 with test_util.capture_sys_output() as (stdout, _):
193 name, aliases = scan.get_normalized_compat_name(node)
194 self.assertEqual('', stdout.getvalue().strip())
195 self.assertEqual('rockchip_rk3288_grf', name)
196 self.assertEqual([], aliases)
197 self.assertEqual(EXPECT_WARN, scan._warnings)
199 prop.value = 'rockchip,rk3288-srf'
200 with test_util.capture_sys_output() as (stdout, _):
201 name, aliases = scan.get_normalized_compat_name(node)
202 self.assertEqual('', stdout.getvalue().strip())
203 self.assertEqual('rockchip_rk3288_grf', name)
204 self.assertEqual(['rockchip_rk3288_srf'], aliases)
205 self.assertEqual(EXPECT_WARN, scan._warnings)
207 def test_scan_errors(self):
208 """Test detection of scanning errors"""
210 static const struct udevice_id tegra_i2c_ids2[] = {
211 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
215 U_BOOT_DRIVER(i2c_tegra) = {
218 .of_match = tegra_i2c_ids,
221 scan = src_scan.Scanner(None, None)
222 with self.assertRaises(ValueError) as exc:
223 scan._parse_driver('file.c', buff)
225 "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)",
228 def test_of_match(self):
229 """Test detection of of_match_ptr() member"""
231 static const struct udevice_id tegra_i2c_ids[] = {
232 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
236 U_BOOT_DRIVER(i2c_tegra) = {
239 .of_match = of_match_ptr(tegra_i2c_ids),
242 scan = src_scan.Scanner(None, None)
243 scan._parse_driver('file.c', buff)
244 self.assertIn('i2c_tegra', scan._drivers)
245 drv = scan._drivers['i2c_tegra']
246 self.assertEqual('i2c_tegra', drv.name)
247 self.assertEqual('', drv.phase)
248 self.assertEqual([], drv.headers)
251 """Test collection of struct info from drivers"""
253 static const struct udevice_id test_ids[] = {
254 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
258 U_BOOT_DRIVER(testing) = {
261 .of_match = test_ids,
262 .priv_auto = sizeof(struct some_priv),
263 .plat_auto = sizeof(struct some_plat),
264 .per_child_auto = sizeof(struct some_cpriv),
265 .per_child_plat_auto = sizeof(struct some_cplat),
268 DM_HEADER(<asm/clk.h>)
271 scan = src_scan.Scanner(None, None)
272 scan._parse_driver('file.c', buff)
273 self.assertIn('testing', scan._drivers)
274 drv = scan._drivers['testing']
275 self.assertEqual('testing', drv.name)
276 self.assertEqual('UCLASS_I2C', drv.uclass_id)
278 {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat)
279 self.assertEqual('some_priv', drv.priv)
280 self.assertEqual('some_plat', drv.plat)
281 self.assertEqual('some_cpriv', drv.child_priv)
282 self.assertEqual('some_cplat', drv.child_plat)
283 self.assertEqual('tpl', drv.phase)
284 self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers)
285 self.assertEqual(1, len(scan._drivers))
287 def test_uclass_scan(self):
288 """Test collection of uclass-driver info"""
290 UCLASS_DRIVER(i2c) = {
293 .flags = DM_UC_FLAG_SEQ_ALIAS,
294 .priv_auto = sizeof(struct some_priv),
295 .per_device_auto = sizeof(struct per_dev_priv),
296 .per_device_plat_auto = sizeof(struct per_dev_plat),
297 .per_child_auto = sizeof(struct per_child_priv),
298 .per_child_plat_auto = sizeof(struct per_child_plat),
299 .child_post_bind = i2c_child_post_bind,
303 scan = src_scan.Scanner(None, None)
304 scan._parse_uclass_driver('file.c', buff)
305 self.assertIn('UCLASS_I2C', scan._uclass)
306 drv = scan._uclass['UCLASS_I2C']
307 self.assertEqual('i2c', drv.name)
308 self.assertEqual('UCLASS_I2C', drv.uclass_id)
309 self.assertEqual('some_priv', drv.priv)
310 self.assertEqual('per_dev_priv', drv.per_dev_priv)
311 self.assertEqual('per_dev_plat', drv.per_dev_plat)
312 self.assertEqual('per_child_priv', drv.per_child_priv)
313 self.assertEqual('per_child_plat', drv.per_child_plat)
314 self.assertEqual(1, len(scan._uclass))
316 drv2 = copy.deepcopy(drv)
317 self.assertEqual(drv, drv2)
318 drv2.priv = 'other_priv'
319 self.assertNotEqual(drv, drv2)
321 # The hashes only depend on the uclass ID, so should be equal
322 self.assertEqual(drv.__hash__(), drv2.__hash__())
324 self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')",
327 def test_uclass_scan_errors(self):
328 """Test detection of uclass scanning errors"""
330 UCLASS_DRIVER(i2c) = {
335 scan = src_scan.Scanner(None, None)
336 with self.assertRaises(ValueError) as exc:
337 scan._parse_uclass_driver('file.c', buff)
338 self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'",
341 def test_struct_scan(self):
342 """Test collection of struct info"""
345 struct some_struct1 {
346 struct i2c_msg *msgs;
350 scan = src_scan.Scanner(None, None)
351 scan._basedir = os.path.join(OUR_PATH, '..', '..')
352 scan._parse_structs('arch/arm/include/asm/file.h', buff)
353 self.assertIn('some_struct1', scan._structs)
354 struc = scan._structs['some_struct1']
355 self.assertEqual('some_struct1', struc.name)
356 self.assertEqual('asm/file.h', struc.fname)
359 /* another comment */
360 struct another_struct {
362 int max_transaction_bytes;
365 scan._parse_structs('include/file2.h', buff)
366 self.assertIn('another_struct', scan._structs)
367 struc = scan._structs['another_struct']
368 self.assertEqual('another_struct', struc.name)
369 self.assertEqual('file2.h', struc.fname)
371 self.assertEqual(2, len(scan._structs))
373 self.assertEqual("Struct(name='another_struct', fname='file2.h')",
376 def test_struct_scan_errors(self):
377 """Test scanning a header file with an invalid unicode file"""
378 output = tools.get_output_filename('output.h')
379 tools.write_file(output, b'struct this is a test \x81 of bad unicode')
381 scan = src_scan.Scanner(None, None)
382 with test_util.capture_sys_output() as (stdout, _):
383 scan.scan_header(output)
384 self.assertIn('due to unicode error', stdout.getvalue())
386 def setup_dup_drivers(self, name, phase=''):
387 """Set up for a duplcate test
392 Driver record for first driver
393 Text of second driver declaration
397 static const struct udevice_id test_ids[] = {
398 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
402 U_BOOT_DRIVER(%s) = {
405 .of_match = test_ids,
408 ''' % (name, 'DM_PHASE(%s)' % phase if phase else '')
410 static const struct udevice_id test_ids[] = {
411 { .compatible = "nvidia,tegra114-dvc" },
415 U_BOOT_DRIVER(%s) = {
418 .of_match = test_ids,
421 scan = src_scan.Scanner(None, None, phase)
422 scan._parse_driver('file1.c', driver1)
423 self.assertIn(name, scan._drivers)
424 drv1 = scan._drivers[name]
427 prop.name = 'compatible'
428 prop.value = 'nvidia,tegra114-i2c'
430 node.name = 'testing'
431 node.props = {'compatible': prop}
433 # get_normalized_compat_name() uses this to check for root node
434 node.parent = FakeNode()
436 return scan, drv1, driver2, node
438 def test_dup_drivers(self):
439 """Test handling of duplicate drivers"""
440 name = 'nvidia_tegra114_i2c'
441 scan, drv1, driver2, node = self.setup_dup_drivers(name)
442 self.assertEqual('', drv1.phase)
444 # The driver should not have a duplicate yet
445 self.assertEqual([], drv1.dups)
447 scan._parse_driver('file2.c', driver2)
449 # The first driver should now be a duplicate of the second
450 drv2 = scan._drivers[name]
451 self.assertEqual('', drv2.phase)
452 self.assertEqual(1, len(drv2.dups))
453 self.assertEqual([drv1], drv2.dups)
455 # There is no way to distinguish them, so we should expect a warning
456 self.assertTrue(drv2.warn_dups)
458 # We should see a warning
459 with test_util.capture_sys_output() as (stdout, _):
460 scan.mark_used([node])
462 "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)",
463 stdout.getvalue().strip())
465 def test_dup_drivers_phase(self):
466 """Test handling of duplicate drivers but with different phases"""
467 name = 'nvidia_tegra114_i2c'
468 scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl')
469 scan._parse_driver('file2.c', driver2)
470 self.assertEqual('spl', drv1.phase)
472 # The second driver should now be a duplicate of the second
473 self.assertEqual(1, len(drv1.dups))
476 # The phase is different, so we should not warn of dups
477 self.assertFalse(drv1.warn_dups)
479 # We should not see a warning
480 with test_util.capture_sys_output() as (stdout, _):
481 scan.mark_used([node])
482 self.assertEqual('', stdout.getvalue().strip())
484 def test_sequence(self):
485 """Test assignment of sequence numnbers"""
486 scan = src_scan.Scanner(None, None, '')
488 uc = src_scan.UclassDriver('UCLASS_I2C')
493 uc.alias_num_to_node[2] = node
495 # This should assign 3 (after the 2 that exists)
496 seq = scan.assign_seq(node)
497 self.assertEqual(3, seq)
498 self.assertEqual({'mypath': 3}, uc.alias_path_to_num)
499 self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)
501 def test_scan_warnings(self):
502 """Test detection of scanning warnings"""
504 static const struct udevice_id tegra_i2c_ids[] = {
505 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
509 U_BOOT_DRIVER(i2c_tegra) = {
512 .of_match = tegra_i2c_ids + 1,
515 # The '+ 1' above should generate a warning
518 prop.name = 'compatible'
519 prop.value = 'rockchip,rk3288-grf'
521 node.props = {'compatible': prop}
523 # get_normalized_compat_name() uses this to check for root node
524 node.parent = FakeNode()
526 scan = src_scan.Scanner(None, None)
527 scan._parse_driver('file.c', buff)
530 {"file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'"}},
534 tprop.name = 'compatible'
535 tprop.value = 'nvidia,tegra114-i2c'
537 tnode.props = {'compatible': tprop}
539 # get_normalized_compat_name() uses this to check for root node
540 tnode.parent = FakeNode()
542 with test_util.capture_sys_output() as (stdout, _):
543 scan.get_normalized_compat_name(node)
544 scan.get_normalized_compat_name(tnode)
545 self.assertEqual('', stdout.getvalue().strip())
547 self.assertEqual(2, len(scan._missing_drivers))
548 self.assertEqual({'rockchip_rk3288_grf', 'nvidia_tegra114_i2c'},
549 scan._missing_drivers)
550 with test_util.capture_sys_output() as (stdout, _):
552 self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
554 # This should show just the rockchip warning, since the tegra driver
555 # is not in self._missing_drivers
556 scan._missing_drivers.remove('nvidia_tegra114_i2c')
557 with test_util.capture_sys_output() as (stdout, _):
559 self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
560 self.assertNotIn('tegra_i2c_ids', stdout.getvalue())
562 # Do a similar thing with used drivers. By marking the tegra driver as
563 # used, the warning related to that driver will be shown
564 drv = scan._drivers['i2c_tegra']
566 with test_util.capture_sys_output() as (stdout, _):
568 self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
569 self.assertIn('tegra_i2c_ids', stdout.getvalue())
571 # Add a warning to make sure multiple warnings are shown
572 scan._warnings['i2c_tegra'].update(
573 scan._warnings['nvidia_tegra114_i2c'])
574 del scan._warnings['nvidia_tegra114_i2c']
575 with test_util.capture_sys_output() as (stdout, _):
577 self.assertEqual('''i2c_tegra: WARNING: the driver nvidia_tegra114_i2c was not found in the driver list
578 : file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'
580 rockchip_rk3288_grf: WARNING: the driver rockchip_rk3288_grf was not found in the driver list
584 self.assertIn('tegra_i2c_ids', stdout.getvalue())
586 def scan_uclass_warning(self):
587 """Test a missing .uclass in the driver"""
589 static const struct udevice_id tegra_i2c_ids[] = {
590 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
594 U_BOOT_DRIVER(i2c_tegra) = {
596 .of_match = tegra_i2c_ids,
599 scan = src_scan.Scanner(None, None)
600 scan._parse_driver('file.c', buff)
602 {'i2c_tegra': {'Missing .uclass in file.c'}},
605 def scan_compat_warning(self):
606 """Test a missing .compatible in the driver"""
608 static const struct udevice_id tegra_i2c_ids[] = {
609 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
613 U_BOOT_DRIVER(i2c_tegra) = {
618 scan = src_scan.Scanner(None, None)
619 scan._parse_driver('file.c', buff)
621 {'i2c_tegra': {'Missing .compatible in file.c'}},