blob: 4e38b25a2f8c7ce9f39f33be5cfa58cdc7c8032d [file] [log] [blame]
Simon Glass10ea9c02020-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 Glass1a8b4b92021-02-03 06:00:54 -070010import copy
Simon Glass10ea9c02020-12-28 20:35:07 -070011import os
12import shutil
13import tempfile
14import unittest
15from unittest import mock
16
17from dtoc import src_scan
Simon Glass970349a2020-12-28 20:35:08 -070018from patman import test_util
Simon Glass10ea9c02020-12-28 20:35:07 -070019from patman import tools
20
Simon Glassc58662f2021-02-03 06:00:50 -070021OUR_PATH = os.path.dirname(os.path.realpath(__file__))
22
23class FakeNode:
24 """Fake Node object for testing"""
25 def __init__(self):
26 self.name = None
27 self.props = {}
28
29class FakeProp:
30 """Fake Prop object for testing"""
31 def __init__(self):
32 self.name = None
33 self.value = None
34
Simon Glass10ea9c02020-12-28 20:35:07 -070035# This is a test so is allowed to access private things in the module it is
36# testing
37# pylint: disable=W0212
38
39class TestSrcScan(unittest.TestCase):
40 """Tests for src_scan"""
41 @classmethod
42 def setUpClass(cls):
43 tools.PrepareOutputDir(None)
44
45 @classmethod
46 def tearDownClass(cls):
47 tools.FinaliseOutputDir()
48
Simon Glass970349a2020-12-28 20:35:08 -070049 def test_simple(self):
50 """Simple test of scanning drivers"""
Simon Glass0c59ace2021-03-26 16:17:25 +130051 scan = src_scan.Scanner(None, None)
Simon Glass970349a2020-12-28 20:35:08 -070052 scan.scan_drivers()
53 self.assertIn('sandbox_gpio', scan._drivers)
54 self.assertIn('sandbox_gpio_alias', scan._driver_aliases)
55 self.assertEqual('sandbox_gpio',
56 scan._driver_aliases['sandbox_gpio_alias'])
57 self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases)
58
59 def test_additional(self):
60 """Test with additional drivers to scan"""
Simon Glass10ea9c02020-12-28 20:35:07 -070061 scan = src_scan.Scanner(
Simon Glass0c59ace2021-03-26 16:17:25 +130062 None, [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx'])
Simon Glass10ea9c02020-12-28 20:35:07 -070063 scan.scan_drivers()
Simon Glass970349a2020-12-28 20:35:08 -070064 self.assertIn('sandbox_gpio_alias2', scan._driver_aliases)
65 self.assertEqual('sandbox_gpio',
66 scan._driver_aliases['sandbox_gpio_alias2'])
Simon Glass10ea9c02020-12-28 20:35:07 -070067
Simon Glass970349a2020-12-28 20:35:08 -070068 def test_unicode_error(self):
Simon Glass10ea9c02020-12-28 20:35:07 -070069 """Test running dtoc with an invalid unicode file
70
71 To be able to perform this test without adding a weird text file which
72 would produce issues when using checkpatch.pl or patman, generate the
73 file at runtime and then process it.
74 """
75 driver_fn = '/tmp/' + next(tempfile._get_candidate_names())
76 with open(driver_fn, 'wb+') as fout:
77 fout.write(b'\x81')
78
Simon Glass0c59ace2021-03-26 16:17:25 +130079 scan = src_scan.Scanner(None, [driver_fn])
Simon Glass970349a2020-12-28 20:35:08 -070080 with test_util.capture_sys_output() as (stdout, _):
81 scan.scan_drivers()
82 self.assertRegex(stdout.getvalue(),
83 r"Skipping file '.*' due to unicode error\s*")
Simon Glass10ea9c02020-12-28 20:35:07 -070084
85 def test_driver(self):
86 """Test the Driver class"""
Simon Glassc58662f2021-02-03 06:00:50 -070087 i2c = 'I2C_UCLASS'
88 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
89 'rockchip,rk3288-srf': None}
90 drv1 = src_scan.Driver('fred', 'fred.c')
91 drv2 = src_scan.Driver('mary', 'mary.c')
92 drv3 = src_scan.Driver('fred', 'fred.c')
93 drv1.uclass_id = i2c
94 drv1.compat = compat
95 drv2.uclass_id = i2c
96 drv2.compat = compat
97 drv3.uclass_id = i2c
98 drv3.compat = compat
99 self.assertEqual(
Simon Glassb9319c42021-02-03 06:01:01 -0700100 "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', "
Simon Glassc58662f2021-02-03 06:00:50 -0700101 "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', "
102 "'rockchip,rk3288-srf': None}, priv=)", str(drv1))
Simon Glass10ea9c02020-12-28 20:35:07 -0700103 self.assertEqual(drv1, drv3)
104 self.assertNotEqual(drv1, drv2)
105 self.assertNotEqual(drv2, drv3)
106
107 def test_scan_dirs(self):
108 """Test scanning of source directories"""
109 def add_file(fname):
110 pathname = os.path.join(indir, fname)
111 dirname = os.path.dirname(pathname)
112 os.makedirs(dirname, exist_ok=True)
113 tools.WriteFile(pathname, '', binary=False)
114 fname_list.append(pathname)
115
116 try:
117 indir = tempfile.mkdtemp(prefix='dtoc.')
118
119 fname_list = []
120 add_file('fname.c')
Simon Glass36b22202021-02-03 06:00:52 -0700121 add_file('.git/ignoreme.c')
Simon Glass10ea9c02020-12-28 20:35:07 -0700122 add_file('dir/fname2.c')
Simon Glass36b22202021-02-03 06:00:52 -0700123 add_file('build-sandbox/ignoreme2.c')
Simon Glass10ea9c02020-12-28 20:35:07 -0700124
125 # Mock out scan_driver and check that it is called with the
126 # expected files
127 with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked:
Simon Glass0c59ace2021-03-26 16:17:25 +1300128 scan = src_scan.Scanner(indir, None)
Simon Glass10ea9c02020-12-28 20:35:07 -0700129 scan.scan_drivers()
130 self.assertEqual(2, len(mocked.mock_calls))
131 self.assertEqual(mock.call(fname_list[0]),
132 mocked.mock_calls[0])
Simon Glass36b22202021-02-03 06:00:52 -0700133 # .git file should be ignored
134 self.assertEqual(mock.call(fname_list[2]),
Simon Glass10ea9c02020-12-28 20:35:07 -0700135 mocked.mock_calls[1])
136 finally:
137 shutil.rmtree(indir)
Simon Glassc58662f2021-02-03 06:00:50 -0700138
139 def test_scan(self):
140 """Test scanning of a driver"""
141 fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c')
142 buff = tools.ReadFile(fname, False)
Simon Glass0c59ace2021-03-26 16:17:25 +1300143 scan = src_scan.Scanner(None, None)
Simon Glassc58662f2021-02-03 06:00:50 -0700144 scan._parse_driver(fname, buff)
145 self.assertIn('i2c_tegra', scan._drivers)
146 drv = scan._drivers['i2c_tegra']
147 self.assertEqual('i2c_tegra', drv.name)
148 self.assertEqual('UCLASS_I2C', drv.uclass_id)
149 self.assertEqual(
150 {'nvidia,tegra114-i2c': 'TYPE_114',
151 'nvidia,tegra20-i2c': 'TYPE_STD',
152 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
153 self.assertEqual('i2c_bus', drv.priv)
154 self.assertEqual(1, len(scan._drivers))
155
156 def test_normalized_name(self):
157 """Test operation of get_normalized_compat_name()"""
158 prop = FakeProp()
159 prop.name = 'compatible'
160 prop.value = 'rockchip,rk3288-grf'
161 node = FakeNode()
162 node.props = {'compatible': prop}
Simon Glass50aae3e2021-02-03 06:01:11 -0700163
164 # get_normalized_compat_name() uses this to check for root node
165 node.parent = FakeNode()
166
Simon Glass0c59ace2021-03-26 16:17:25 +1300167 scan = src_scan.Scanner(None, None)
Simon Glassc58662f2021-02-03 06:00:50 -0700168 with test_util.capture_sys_output() as (stdout, _):
169 name, aliases = scan.get_normalized_compat_name(node)
170 self.assertEqual('rockchip_rk3288_grf', name)
171 self.assertEqual([], aliases)
Simon Glass0c59ace2021-03-26 16:17:25 +1300172 self.assertEqual(1, len(scan._missing_drivers))
173 self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers)
Simon Glass4f1727a2021-07-04 12:19:47 -0600174 self.assertEqual('', stdout.getvalue().strip())
Simon Glassc58662f2021-02-03 06:00:50 -0700175
176 i2c = 'I2C_UCLASS'
177 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
178 'rockchip,rk3288-srf': None}
179 drv = src_scan.Driver('fred', 'fred.c')
180 drv.uclass_id = i2c
181 drv.compat = compat
182 scan._drivers['rockchip_rk3288_grf'] = drv
183
184 scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf'
185
186 with test_util.capture_sys_output() as (stdout, _):
187 name, aliases = scan.get_normalized_compat_name(node)
188 self.assertEqual('', stdout.getvalue().strip())
189 self.assertEqual('rockchip_rk3288_grf', name)
190 self.assertEqual([], aliases)
191
192 prop.value = 'rockchip,rk3288-srf'
193 with test_util.capture_sys_output() as (stdout, _):
194 name, aliases = scan.get_normalized_compat_name(node)
195 self.assertEqual('', stdout.getvalue().strip())
196 self.assertEqual('rockchip_rk3288_grf', name)
197 self.assertEqual(['rockchip_rk3288_srf'], aliases)
198
199 def test_scan_errors(self):
200 """Test detection of scanning errors"""
201 buff = '''
202static const struct udevice_id tegra_i2c_ids2[] = {
203 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
204 { }
205};
206
207U_BOOT_DRIVER(i2c_tegra) = {
208 .name = "i2c_tegra",
209 .id = UCLASS_I2C,
210 .of_match = tegra_i2c_ids,
211};
212'''
Simon Glass0c59ace2021-03-26 16:17:25 +1300213 scan = src_scan.Scanner(None, None)
Simon Glassc58662f2021-02-03 06:00:50 -0700214 with self.assertRaises(ValueError) as exc:
215 scan._parse_driver('file.c', buff)
216 self.assertIn(
217 "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)",
218 str(exc.exception))
219
220 def test_of_match(self):
221 """Test detection of of_match_ptr() member"""
222 buff = '''
223static const struct udevice_id tegra_i2c_ids[] = {
224 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
225 { }
226};
227
228U_BOOT_DRIVER(i2c_tegra) = {
229 .name = "i2c_tegra",
230 .id = UCLASS_I2C,
231 .of_match = of_match_ptr(tegra_i2c_ids),
232};
233'''
Simon Glass0c59ace2021-03-26 16:17:25 +1300234 scan = src_scan.Scanner(None, None)
Simon Glassc58662f2021-02-03 06:00:50 -0700235 scan._parse_driver('file.c', buff)
236 self.assertIn('i2c_tegra', scan._drivers)
237 drv = scan._drivers['i2c_tegra']
238 self.assertEqual('i2c_tegra', drv.name)
Simon Glassb00f0062021-02-03 06:01:02 -0700239 self.assertEqual('', drv.phase)
Simon Glass735ddfc2021-02-03 06:01:04 -0700240 self.assertEqual([], drv.headers)
Simon Glassc8b19b02021-02-03 06:00:53 -0700241
242 def test_priv(self):
243 """Test collection of struct info from drivers"""
244 buff = '''
245static const struct udevice_id test_ids[] = {
246 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
247 { }
248};
249
250U_BOOT_DRIVER(testing) = {
251 .name = "testing",
252 .id = UCLASS_I2C,
253 .of_match = test_ids,
254 .priv_auto = sizeof(struct some_priv),
255 .plat_auto = sizeof(struct some_plat),
256 .per_child_auto = sizeof(struct some_cpriv),
257 .per_child_plat_auto = sizeof(struct some_cplat),
Simon Glassb00f0062021-02-03 06:01:02 -0700258 DM_PHASE(tpl)
Simon Glass735ddfc2021-02-03 06:01:04 -0700259 DM_HEADER(<i2c.h>)
260 DM_HEADER(<asm/clk.h>)
Simon Glassc8b19b02021-02-03 06:00:53 -0700261};
262'''
Simon Glass0c59ace2021-03-26 16:17:25 +1300263 scan = src_scan.Scanner(None, None)
Simon Glassc8b19b02021-02-03 06:00:53 -0700264 scan._parse_driver('file.c', buff)
265 self.assertIn('testing', scan._drivers)
266 drv = scan._drivers['testing']
267 self.assertEqual('testing', drv.name)
268 self.assertEqual('UCLASS_I2C', drv.uclass_id)
269 self.assertEqual(
270 {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat)
271 self.assertEqual('some_priv', drv.priv)
272 self.assertEqual('some_plat', drv.plat)
273 self.assertEqual('some_cpriv', drv.child_priv)
274 self.assertEqual('some_cplat', drv.child_plat)
Simon Glassb00f0062021-02-03 06:01:02 -0700275 self.assertEqual('tpl', drv.phase)
Simon Glass735ddfc2021-02-03 06:01:04 -0700276 self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers)
Simon Glassc8b19b02021-02-03 06:00:53 -0700277 self.assertEqual(1, len(scan._drivers))
Simon Glass1a8b4b92021-02-03 06:00:54 -0700278
279 def test_uclass_scan(self):
280 """Test collection of uclass-driver info"""
281 buff = '''
282UCLASS_DRIVER(i2c) = {
283 .id = UCLASS_I2C,
284 .name = "i2c",
285 .flags = DM_UC_FLAG_SEQ_ALIAS,
286 .priv_auto = sizeof(struct some_priv),
287 .per_device_auto = sizeof(struct per_dev_priv),
288 .per_device_plat_auto = sizeof(struct per_dev_plat),
289 .per_child_auto = sizeof(struct per_child_priv),
290 .per_child_plat_auto = sizeof(struct per_child_plat),
291 .child_post_bind = i2c_child_post_bind,
292};
293
294'''
Simon Glass0c59ace2021-03-26 16:17:25 +1300295 scan = src_scan.Scanner(None, None)
Simon Glass1a8b4b92021-02-03 06:00:54 -0700296 scan._parse_uclass_driver('file.c', buff)
297 self.assertIn('UCLASS_I2C', scan._uclass)
298 drv = scan._uclass['UCLASS_I2C']
299 self.assertEqual('i2c', drv.name)
300 self.assertEqual('UCLASS_I2C', drv.uclass_id)
301 self.assertEqual('some_priv', drv.priv)
302 self.assertEqual('per_dev_priv', drv.per_dev_priv)
303 self.assertEqual('per_dev_plat', drv.per_dev_plat)
304 self.assertEqual('per_child_priv', drv.per_child_priv)
305 self.assertEqual('per_child_plat', drv.per_child_plat)
306 self.assertEqual(1, len(scan._uclass))
307
308 drv2 = copy.deepcopy(drv)
309 self.assertEqual(drv, drv2)
310 drv2.priv = 'other_priv'
311 self.assertNotEqual(drv, drv2)
312
313 # The hashes only depend on the uclass ID, so should be equal
314 self.assertEqual(drv.__hash__(), drv2.__hash__())
315
316 self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')",
317 str(drv))
318
319 def test_uclass_scan_errors(self):
320 """Test detection of uclass scanning errors"""
321 buff = '''
322UCLASS_DRIVER(i2c) = {
323 .name = "i2c",
324};
325
326'''
Simon Glass0c59ace2021-03-26 16:17:25 +1300327 scan = src_scan.Scanner(None, None)
Simon Glass1a8b4b92021-02-03 06:00:54 -0700328 with self.assertRaises(ValueError) as exc:
329 scan._parse_uclass_driver('file.c', buff)
330 self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'",
331 str(exc.exception))
Simon Glassacf5cb82021-02-03 06:00:55 -0700332
333 def test_struct_scan(self):
334 """Test collection of struct info"""
335 buff = '''
336/* some comment */
337struct some_struct1 {
338 struct i2c_msg *msgs;
339 uint nmsgs;
340};
341'''
Simon Glass0c59ace2021-03-26 16:17:25 +1300342 scan = src_scan.Scanner(None, None)
Simon Glassacf5cb82021-02-03 06:00:55 -0700343 scan._basedir = os.path.join(OUR_PATH, '..', '..')
344 scan._parse_structs('arch/arm/include/asm/file.h', buff)
345 self.assertIn('some_struct1', scan._structs)
346 struc = scan._structs['some_struct1']
347 self.assertEqual('some_struct1', struc.name)
348 self.assertEqual('asm/file.h', struc.fname)
349
350 buff = '''
351/* another comment */
352struct another_struct {
353 int speed_hz;
354 int max_transaction_bytes;
355};
356'''
357 scan._parse_structs('include/file2.h', buff)
358 self.assertIn('another_struct', scan._structs)
359 struc = scan._structs['another_struct']
360 self.assertEqual('another_struct', struc.name)
361 self.assertEqual('file2.h', struc.fname)
362
363 self.assertEqual(2, len(scan._structs))
364
365 self.assertEqual("Struct(name='another_struct', fname='file2.h')",
366 str(struc))
367
368 def test_struct_scan_errors(self):
369 """Test scanning a header file with an invalid unicode file"""
370 output = tools.GetOutputFilename('output.h')
371 tools.WriteFile(output, b'struct this is a test \x81 of bad unicode')
372
Simon Glass0c59ace2021-03-26 16:17:25 +1300373 scan = src_scan.Scanner(None, None)
Simon Glassacf5cb82021-02-03 06:00:55 -0700374 with test_util.capture_sys_output() as (stdout, _):
375 scan.scan_header(output)
376 self.assertIn('due to unicode error', stdout.getvalue())
Simon Glass1d972692021-02-03 06:01:06 -0700377
378 def setup_dup_drivers(self, name, phase=''):
379 """Set up for a duplcate test
380
381 Returns:
382 tuple:
383 Scanner to use
384 Driver record for first driver
385 Text of second driver declaration
386 Node for driver 1
387 """
388 driver1 = '''
389static const struct udevice_id test_ids[] = {
390 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
391 { }
392};
393
394U_BOOT_DRIVER(%s) = {
395 .name = "testing",
396 .id = UCLASS_I2C,
397 .of_match = test_ids,
398 %s
399};
400''' % (name, 'DM_PHASE(%s)' % phase if phase else '')
401 driver2 = '''
402static const struct udevice_id test_ids[] = {
403 { .compatible = "nvidia,tegra114-dvc" },
404 { }
405};
406
407U_BOOT_DRIVER(%s) = {
408 .name = "testing",
409 .id = UCLASS_RAM,
410 .of_match = test_ids,
411};
412''' % name
Simon Glass0c59ace2021-03-26 16:17:25 +1300413 scan = src_scan.Scanner(None, None, phase)
Simon Glass1d972692021-02-03 06:01:06 -0700414 scan._parse_driver('file1.c', driver1)
415 self.assertIn(name, scan._drivers)
416 drv1 = scan._drivers[name]
417
418 prop = FakeProp()
419 prop.name = 'compatible'
420 prop.value = 'nvidia,tegra114-i2c'
421 node = FakeNode()
422 node.name = 'testing'
423 node.props = {'compatible': prop}
424
Simon Glass50aae3e2021-02-03 06:01:11 -0700425 # get_normalized_compat_name() uses this to check for root node
426 node.parent = FakeNode()
427
Simon Glass1d972692021-02-03 06:01:06 -0700428 return scan, drv1, driver2, node
429
430 def test_dup_drivers(self):
431 """Test handling of duplicate drivers"""
432 name = 'nvidia_tegra114_i2c'
433 scan, drv1, driver2, node = self.setup_dup_drivers(name)
434 self.assertEqual('', drv1.phase)
435
436 # The driver should not have a duplicate yet
437 self.assertEqual([], drv1.dups)
438
439 scan._parse_driver('file2.c', driver2)
440
441 # The first driver should now be a duplicate of the second
442 drv2 = scan._drivers[name]
443 self.assertEqual('', drv2.phase)
444 self.assertEqual(1, len(drv2.dups))
445 self.assertEqual([drv1], drv2.dups)
446
447 # There is no way to distinguish them, so we should expect a warning
448 self.assertTrue(drv2.warn_dups)
449
450 # We should see a warning
451 with test_util.capture_sys_output() as (stdout, _):
452 scan.mark_used([node])
453 self.assertEqual(
454 "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)",
455 stdout.getvalue().strip())
456
457 def test_dup_drivers_phase(self):
458 """Test handling of duplicate drivers but with different phases"""
459 name = 'nvidia_tegra114_i2c'
460 scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl')
461 scan._parse_driver('file2.c', driver2)
462 self.assertEqual('spl', drv1.phase)
463
464 # The second driver should now be a duplicate of the second
465 self.assertEqual(1, len(drv1.dups))
466 drv2 = drv1.dups[0]
467
468 # The phase is different, so we should not warn of dups
469 self.assertFalse(drv1.warn_dups)
470
471 # We should not see a warning
472 with test_util.capture_sys_output() as (stdout, _):
473 scan.mark_used([node])
474 self.assertEqual('', stdout.getvalue().strip())
Simon Glass337d6972021-02-03 06:01:10 -0700475
476 def test_sequence(self):
477 """Test assignment of sequence numnbers"""
Simon Glass0c59ace2021-03-26 16:17:25 +1300478 scan = src_scan.Scanner(None, None, '')
Simon Glass337d6972021-02-03 06:01:10 -0700479 node = FakeNode()
480 uc = src_scan.UclassDriver('UCLASS_I2C')
481 node.uclass = uc
482 node.driver = True
483 node.seq = -1
484 node.path = 'mypath'
485 uc.alias_num_to_node[2] = node
486
487 # This should assign 3 (after the 2 that exists)
488 seq = scan.assign_seq(node)
489 self.assertEqual(3, seq)
490 self.assertEqual({'mypath': 3}, uc.alias_path_to_num)
491 self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)