123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- # SPDX-License-Identifier: GPL-2.0+
- # Copyright 2020 Google LLC
- #
- """Tests for the src_scan module
- This includes unit tests for scanning of the source code
- """
- import copy
- import os
- import shutil
- import tempfile
- import unittest
- from unittest import mock
- from dtoc import src_scan
- from patman import test_util
- from patman import tools
- OUR_PATH = os.path.dirname(os.path.realpath(__file__))
- EXPECT_WARN = {'rockchip_rk3288_grf':
- {'WARNING: the driver rockchip_rk3288_grf was not found in the driver list'}}
- class FakeNode:
- """Fake Node object for testing"""
- def __init__(self):
- self.name = None
- self.props = {}
- class FakeProp:
- """Fake Prop object for testing"""
- def __init__(self):
- self.name = None
- self.value = None
- # This is a test so is allowed to access private things in the module it is
- # testing
- # pylint: disable=W0212
- class TestSrcScan(unittest.TestCase):
- """Tests for src_scan"""
- @classmethod
- def setUpClass(cls):
- tools.PrepareOutputDir(None)
- @classmethod
- def tearDownClass(cls):
- tools.FinaliseOutputDir()
- def test_simple(self):
- """Simple test of scanning drivers"""
- scan = src_scan.Scanner(None, None)
- scan.scan_drivers()
- self.assertIn('sandbox_gpio', scan._drivers)
- self.assertIn('sandbox_gpio_alias', scan._driver_aliases)
- self.assertEqual('sandbox_gpio',
- scan._driver_aliases['sandbox_gpio_alias'])
- self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases)
- def test_additional(self):
- """Test with additional drivers to scan"""
- scan = src_scan.Scanner(
- None, [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx'])
- scan.scan_drivers()
- self.assertIn('sandbox_gpio_alias2', scan._driver_aliases)
- self.assertEqual('sandbox_gpio',
- scan._driver_aliases['sandbox_gpio_alias2'])
- def test_unicode_error(self):
- """Test running dtoc with an invalid unicode file
- To be able to perform this test without adding a weird text file which
- would produce issues when using checkpatch.pl or patman, generate the
- file at runtime and then process it.
- """
- driver_fn = '/tmp/' + next(tempfile._get_candidate_names())
- with open(driver_fn, 'wb+') as fout:
- fout.write(b'\x81')
- scan = src_scan.Scanner(None, [driver_fn])
- with test_util.capture_sys_output() as (stdout, _):
- scan.scan_drivers()
- self.assertRegex(stdout.getvalue(),
- r"Skipping file '.*' due to unicode error\s*")
- def test_driver(self):
- """Test the Driver class"""
- i2c = 'I2C_UCLASS'
- compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
- 'rockchip,rk3288-srf': None}
- drv1 = src_scan.Driver('fred', 'fred.c')
- drv2 = src_scan.Driver('mary', 'mary.c')
- drv3 = src_scan.Driver('fred', 'fred.c')
- drv1.uclass_id = i2c
- drv1.compat = compat
- drv2.uclass_id = i2c
- drv2.compat = compat
- drv3.uclass_id = i2c
- drv3.compat = compat
- self.assertEqual(
- "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', "
- "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', "
- "'rockchip,rk3288-srf': None}, priv=)", str(drv1))
- self.assertEqual(drv1, drv3)
- self.assertNotEqual(drv1, drv2)
- self.assertNotEqual(drv2, drv3)
- def test_scan_dirs(self):
- """Test scanning of source directories"""
- def add_file(fname):
- pathname = os.path.join(indir, fname)
- dirname = os.path.dirname(pathname)
- os.makedirs(dirname, exist_ok=True)
- tools.WriteFile(pathname, '', binary=False)
- fname_list.append(pathname)
- try:
- indir = tempfile.mkdtemp(prefix='dtoc.')
- fname_list = []
- add_file('fname.c')
- add_file('.git/ignoreme.c')
- add_file('dir/fname2.c')
- add_file('build-sandbox/ignoreme2.c')
- # Mock out scan_driver and check that it is called with the
- # expected files
- with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked:
- scan = src_scan.Scanner(indir, None)
- scan.scan_drivers()
- self.assertEqual(2, len(mocked.mock_calls))
- self.assertEqual(mock.call(fname_list[0]),
- mocked.mock_calls[0])
- # .git file should be ignored
- self.assertEqual(mock.call(fname_list[2]),
- mocked.mock_calls[1])
- finally:
- shutil.rmtree(indir)
- def test_scan(self):
- """Test scanning of a driver"""
- fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c')
- buff = tools.ReadFile(fname, False)
- scan = src_scan.Scanner(None, None)
- scan._parse_driver(fname, buff)
- self.assertIn('i2c_tegra', scan._drivers)
- drv = scan._drivers['i2c_tegra']
- self.assertEqual('i2c_tegra', drv.name)
- self.assertEqual('UCLASS_I2C', drv.uclass_id)
- self.assertEqual(
- {'nvidia,tegra114-i2c': 'TYPE_114',
- 'nvidia,tegra20-i2c': 'TYPE_STD',
- 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
- self.assertEqual('i2c_bus', drv.priv)
- self.assertEqual(1, len(scan._drivers))
- self.assertEqual({}, scan._warnings)
- def test_normalized_name(self):
- """Test operation of get_normalized_compat_name()"""
- prop = FakeProp()
- prop.name = 'compatible'
- prop.value = 'rockchip,rk3288-grf'
- node = FakeNode()
- node.props = {'compatible': prop}
- # get_normalized_compat_name() uses this to check for root node
- node.parent = FakeNode()
- scan = src_scan.Scanner(None, None)
- with test_util.capture_sys_output() as (stdout, _):
- name, aliases = scan.get_normalized_compat_name(node)
- self.assertEqual('rockchip_rk3288_grf', name)
- self.assertEqual([], aliases)
- self.assertEqual(1, len(scan._missing_drivers))
- self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers)
- self.assertEqual('', stdout.getvalue().strip())
- self.assertEqual(EXPECT_WARN, scan._warnings)
- i2c = 'I2C_UCLASS'
- compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
- 'rockchip,rk3288-srf': None}
- drv = src_scan.Driver('fred', 'fred.c')
- drv.uclass_id = i2c
- drv.compat = compat
- scan._drivers['rockchip_rk3288_grf'] = drv
- scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf'
- with test_util.capture_sys_output() as (stdout, _):
- name, aliases = scan.get_normalized_compat_name(node)
- self.assertEqual('', stdout.getvalue().strip())
- self.assertEqual('rockchip_rk3288_grf', name)
- self.assertEqual([], aliases)
- self.assertEqual(EXPECT_WARN, scan._warnings)
- prop.value = 'rockchip,rk3288-srf'
- with test_util.capture_sys_output() as (stdout, _):
- name, aliases = scan.get_normalized_compat_name(node)
- self.assertEqual('', stdout.getvalue().strip())
- self.assertEqual('rockchip_rk3288_grf', name)
- self.assertEqual(['rockchip_rk3288_srf'], aliases)
- self.assertEqual(EXPECT_WARN, scan._warnings)
- def test_scan_errors(self):
- """Test detection of scanning errors"""
- buff = '''
- static const struct udevice_id tegra_i2c_ids2[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(i2c_tegra) = {
- .name = "i2c_tegra",
- .id = UCLASS_I2C,
- .of_match = tegra_i2c_ids,
- };
- '''
- scan = src_scan.Scanner(None, None)
- with self.assertRaises(ValueError) as exc:
- scan._parse_driver('file.c', buff)
- self.assertIn(
- "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)",
- str(exc.exception))
- def test_of_match(self):
- """Test detection of of_match_ptr() member"""
- buff = '''
- static const struct udevice_id tegra_i2c_ids[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(i2c_tegra) = {
- .name = "i2c_tegra",
- .id = UCLASS_I2C,
- .of_match = of_match_ptr(tegra_i2c_ids),
- };
- '''
- scan = src_scan.Scanner(None, None)
- scan._parse_driver('file.c', buff)
- self.assertIn('i2c_tegra', scan._drivers)
- drv = scan._drivers['i2c_tegra']
- self.assertEqual('i2c_tegra', drv.name)
- self.assertEqual('', drv.phase)
- self.assertEqual([], drv.headers)
- def test_priv(self):
- """Test collection of struct info from drivers"""
- buff = '''
- static const struct udevice_id test_ids[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(testing) = {
- .name = "testing",
- .id = UCLASS_I2C,
- .of_match = test_ids,
- .priv_auto = sizeof(struct some_priv),
- .plat_auto = sizeof(struct some_plat),
- .per_child_auto = sizeof(struct some_cpriv),
- .per_child_plat_auto = sizeof(struct some_cplat),
- DM_PHASE(tpl)
- DM_HEADER(<i2c.h>)
- DM_HEADER(<asm/clk.h>)
- };
- '''
- scan = src_scan.Scanner(None, None)
- scan._parse_driver('file.c', buff)
- self.assertIn('testing', scan._drivers)
- drv = scan._drivers['testing']
- self.assertEqual('testing', drv.name)
- self.assertEqual('UCLASS_I2C', drv.uclass_id)
- self.assertEqual(
- {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat)
- self.assertEqual('some_priv', drv.priv)
- self.assertEqual('some_plat', drv.plat)
- self.assertEqual('some_cpriv', drv.child_priv)
- self.assertEqual('some_cplat', drv.child_plat)
- self.assertEqual('tpl', drv.phase)
- self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers)
- self.assertEqual(1, len(scan._drivers))
- def test_uclass_scan(self):
- """Test collection of uclass-driver info"""
- buff = '''
- UCLASS_DRIVER(i2c) = {
- .id = UCLASS_I2C,
- .name = "i2c",
- .flags = DM_UC_FLAG_SEQ_ALIAS,
- .priv_auto = sizeof(struct some_priv),
- .per_device_auto = sizeof(struct per_dev_priv),
- .per_device_plat_auto = sizeof(struct per_dev_plat),
- .per_child_auto = sizeof(struct per_child_priv),
- .per_child_plat_auto = sizeof(struct per_child_plat),
- .child_post_bind = i2c_child_post_bind,
- };
- '''
- scan = src_scan.Scanner(None, None)
- scan._parse_uclass_driver('file.c', buff)
- self.assertIn('UCLASS_I2C', scan._uclass)
- drv = scan._uclass['UCLASS_I2C']
- self.assertEqual('i2c', drv.name)
- self.assertEqual('UCLASS_I2C', drv.uclass_id)
- self.assertEqual('some_priv', drv.priv)
- self.assertEqual('per_dev_priv', drv.per_dev_priv)
- self.assertEqual('per_dev_plat', drv.per_dev_plat)
- self.assertEqual('per_child_priv', drv.per_child_priv)
- self.assertEqual('per_child_plat', drv.per_child_plat)
- self.assertEqual(1, len(scan._uclass))
- drv2 = copy.deepcopy(drv)
- self.assertEqual(drv, drv2)
- drv2.priv = 'other_priv'
- self.assertNotEqual(drv, drv2)
- # The hashes only depend on the uclass ID, so should be equal
- self.assertEqual(drv.__hash__(), drv2.__hash__())
- self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')",
- str(drv))
- def test_uclass_scan_errors(self):
- """Test detection of uclass scanning errors"""
- buff = '''
- UCLASS_DRIVER(i2c) = {
- .name = "i2c",
- };
- '''
- scan = src_scan.Scanner(None, None)
- with self.assertRaises(ValueError) as exc:
- scan._parse_uclass_driver('file.c', buff)
- self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'",
- str(exc.exception))
- def test_struct_scan(self):
- """Test collection of struct info"""
- buff = '''
- /* some comment */
- struct some_struct1 {
- struct i2c_msg *msgs;
- uint nmsgs;
- };
- '''
- scan = src_scan.Scanner(None, None)
- scan._basedir = os.path.join(OUR_PATH, '..', '..')
- scan._parse_structs('arch/arm/include/asm/file.h', buff)
- self.assertIn('some_struct1', scan._structs)
- struc = scan._structs['some_struct1']
- self.assertEqual('some_struct1', struc.name)
- self.assertEqual('asm/file.h', struc.fname)
- buff = '''
- /* another comment */
- struct another_struct {
- int speed_hz;
- int max_transaction_bytes;
- };
- '''
- scan._parse_structs('include/file2.h', buff)
- self.assertIn('another_struct', scan._structs)
- struc = scan._structs['another_struct']
- self.assertEqual('another_struct', struc.name)
- self.assertEqual('file2.h', struc.fname)
- self.assertEqual(2, len(scan._structs))
- self.assertEqual("Struct(name='another_struct', fname='file2.h')",
- str(struc))
- def test_struct_scan_errors(self):
- """Test scanning a header file with an invalid unicode file"""
- output = tools.GetOutputFilename('output.h')
- tools.WriteFile(output, b'struct this is a test \x81 of bad unicode')
- scan = src_scan.Scanner(None, None)
- with test_util.capture_sys_output() as (stdout, _):
- scan.scan_header(output)
- self.assertIn('due to unicode error', stdout.getvalue())
- def setup_dup_drivers(self, name, phase=''):
- """Set up for a duplcate test
- Returns:
- tuple:
- Scanner to use
- Driver record for first driver
- Text of second driver declaration
- Node for driver 1
- """
- driver1 = '''
- static const struct udevice_id test_ids[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(%s) = {
- .name = "testing",
- .id = UCLASS_I2C,
- .of_match = test_ids,
- %s
- };
- ''' % (name, 'DM_PHASE(%s)' % phase if phase else '')
- driver2 = '''
- static const struct udevice_id test_ids[] = {
- { .compatible = "nvidia,tegra114-dvc" },
- { }
- };
- U_BOOT_DRIVER(%s) = {
- .name = "testing",
- .id = UCLASS_RAM,
- .of_match = test_ids,
- };
- ''' % name
- scan = src_scan.Scanner(None, None, phase)
- scan._parse_driver('file1.c', driver1)
- self.assertIn(name, scan._drivers)
- drv1 = scan._drivers[name]
- prop = FakeProp()
- prop.name = 'compatible'
- prop.value = 'nvidia,tegra114-i2c'
- node = FakeNode()
- node.name = 'testing'
- node.props = {'compatible': prop}
- # get_normalized_compat_name() uses this to check for root node
- node.parent = FakeNode()
- return scan, drv1, driver2, node
- def test_dup_drivers(self):
- """Test handling of duplicate drivers"""
- name = 'nvidia_tegra114_i2c'
- scan, drv1, driver2, node = self.setup_dup_drivers(name)
- self.assertEqual('', drv1.phase)
- # The driver should not have a duplicate yet
- self.assertEqual([], drv1.dups)
- scan._parse_driver('file2.c', driver2)
- # The first driver should now be a duplicate of the second
- drv2 = scan._drivers[name]
- self.assertEqual('', drv2.phase)
- self.assertEqual(1, len(drv2.dups))
- self.assertEqual([drv1], drv2.dups)
- # There is no way to distinguish them, so we should expect a warning
- self.assertTrue(drv2.warn_dups)
- # We should see a warning
- with test_util.capture_sys_output() as (stdout, _):
- scan.mark_used([node])
- self.assertEqual(
- "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)",
- stdout.getvalue().strip())
- def test_dup_drivers_phase(self):
- """Test handling of duplicate drivers but with different phases"""
- name = 'nvidia_tegra114_i2c'
- scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl')
- scan._parse_driver('file2.c', driver2)
- self.assertEqual('spl', drv1.phase)
- # The second driver should now be a duplicate of the second
- self.assertEqual(1, len(drv1.dups))
- drv2 = drv1.dups[0]
- # The phase is different, so we should not warn of dups
- self.assertFalse(drv1.warn_dups)
- # We should not see a warning
- with test_util.capture_sys_output() as (stdout, _):
- scan.mark_used([node])
- self.assertEqual('', stdout.getvalue().strip())
- def test_sequence(self):
- """Test assignment of sequence numnbers"""
- scan = src_scan.Scanner(None, None, '')
- node = FakeNode()
- uc = src_scan.UclassDriver('UCLASS_I2C')
- node.uclass = uc
- node.driver = True
- node.seq = -1
- node.path = 'mypath'
- uc.alias_num_to_node[2] = node
- # This should assign 3 (after the 2 that exists)
- seq = scan.assign_seq(node)
- self.assertEqual(3, seq)
- self.assertEqual({'mypath': 3}, uc.alias_path_to_num)
- self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)
- def test_scan_warnings(self):
- """Test detection of scanning warnings"""
- buff = '''
- static const struct udevice_id tegra_i2c_ids[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(i2c_tegra) = {
- .name = "i2c_tegra",
- .id = UCLASS_I2C,
- .of_match = tegra_i2c_ids + 1,
- };
- '''
- # The '+ 1' above should generate a warning
- prop = FakeProp()
- prop.name = 'compatible'
- prop.value = 'rockchip,rk3288-grf'
- node = FakeNode()
- node.props = {'compatible': prop}
- # get_normalized_compat_name() uses this to check for root node
- node.parent = FakeNode()
- scan = src_scan.Scanner(None, None)
- scan._parse_driver('file.c', buff)
- self.assertEqual(
- {'i2c_tegra':
- {"file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'"}},
- scan._warnings)
- tprop = FakeProp()
- tprop.name = 'compatible'
- tprop.value = 'nvidia,tegra114-i2c'
- tnode = FakeNode()
- tnode.props = {'compatible': tprop}
- # get_normalized_compat_name() uses this to check for root node
- tnode.parent = FakeNode()
- with test_util.capture_sys_output() as (stdout, _):
- scan.get_normalized_compat_name(node)
- scan.get_normalized_compat_name(tnode)
- self.assertEqual('', stdout.getvalue().strip())
- self.assertEqual(2, len(scan._missing_drivers))
- self.assertEqual({'rockchip_rk3288_grf', 'nvidia_tegra114_i2c'},
- scan._missing_drivers)
- with test_util.capture_sys_output() as (stdout, _):
- scan.show_warnings()
- self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
- # This should show just the rockchip warning, since the tegra driver
- # is not in self._missing_drivers
- scan._missing_drivers.remove('nvidia_tegra114_i2c')
- with test_util.capture_sys_output() as (stdout, _):
- scan.show_warnings()
- self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
- self.assertNotIn('tegra_i2c_ids', stdout.getvalue())
- # Do a similar thing with used drivers. By marking the tegra driver as
- # used, the warning related to that driver will be shown
- drv = scan._drivers['i2c_tegra']
- drv.used = True
- with test_util.capture_sys_output() as (stdout, _):
- scan.show_warnings()
- self.assertIn('rockchip_rk3288_grf', stdout.getvalue())
- self.assertIn('tegra_i2c_ids', stdout.getvalue())
- # Add a warning to make sure multiple warnings are shown
- scan._warnings['i2c_tegra'].update(
- scan._warnings['nvidia_tegra114_i2c'])
- del scan._warnings['nvidia_tegra114_i2c']
- with test_util.capture_sys_output() as (stdout, _):
- scan.show_warnings()
- self.assertEqual('''i2c_tegra: WARNING: the driver nvidia_tegra114_i2c was not found in the driver list
- : file.c: Warning: unexpected suffix ' + 1' on .of_match line for compat 'tegra_i2c_ids'
- rockchip_rk3288_grf: WARNING: the driver rockchip_rk3288_grf was not found in the driver list
- ''',
- stdout.getvalue())
- self.assertIn('tegra_i2c_ids', stdout.getvalue())
- def scan_uclass_warning(self):
- """Test a missing .uclass in the driver"""
- buff = '''
- static const struct udevice_id tegra_i2c_ids[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(i2c_tegra) = {
- .name = "i2c_tegra",
- .of_match = tegra_i2c_ids,
- };
- '''
- scan = src_scan.Scanner(None, None)
- scan._parse_driver('file.c', buff)
- self.assertEqual(
- {'i2c_tegra': {'Missing .uclass in file.c'}},
- scan._warnings)
- def scan_compat_warning(self):
- """Test a missing .compatible in the driver"""
- buff = '''
- static const struct udevice_id tegra_i2c_ids[] = {
- { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
- { }
- };
- U_BOOT_DRIVER(i2c_tegra) = {
- .name = "i2c_tegra",
- .id = UCLASS_I2C,
- };
- '''
- scan = src_scan.Scanner(None, None)
- scan._parse_driver('file.c', buff)
- self.assertEqual(
- {'i2c_tegra': {'Missing .compatible in file.c'}},
- scan._warnings)
|