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_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, False, 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_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, False, 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_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_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_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 run_steps(args, dtb_file, include_disabled, output, output_dirs, warning_disabled=False, drivers_additional=None, basedir=None): """Run all the steps of the dtoc tool Args: args (list): List of non-option arguments provided to the problem dtb_file (str): Filename of dtb file to process include_disabled (bool): True to include disabled nodes output (str): Name of output file (None for stdout) output_dirs (tuple of str): Directory to put C output files Directory to put H output files warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during scanning basedir (str): Base directory of U-Boot source code. Defaults to the grandparent of this file's directory Raises: ValueError: if args has no command, or an unknown command """ if not args: raise ValueError('Please specify a command: struct, platdata, all') if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both') scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) plat = DtbPlatdata(scan, dtb_file, include_disabled) scan.scan_drivers() plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() cmds = args[0].split(',') if 'all' in cmds: cmds = sorted(OUTPUT_FILES.keys()) for cmd in cmds: outfile = OUTPUT_FILES.get(cmd) if not outfile: raise ValueError("Unknown command '%s': (use: %s)" % (cmd, ', '.join(sorted(OUTPUT_FILES.keys())))) plat.setup_output(outfile.ftype, outfile.fname if output_dirs else output) plat.out_header(outfile) outfile.method(plat) plat.finish_output()
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 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_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_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_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 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)
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))
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_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 run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, instantiate, warning_disabled=False, drivers_additional=None, basedir=None, scan=None): """Run all the steps of the dtoc tool Args: args (list): List of non-option arguments provided to the problem dtb_file (str): Filename of dtb file to process include_disabled (bool): True to include disabled nodes output (str): Name of output file (None for stdout) output_dirs (tuple of str): Directory to put C output files Directory to put H output files phase: The phase of U-Boot that we are generating data for, e.g. 'spl' or 'tpl'. None if not known instantiate: Instantiate devices so they don't need to be bound at run-time warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during scanning basedir (str): Base directory of U-Boot source code. Defaults to the grandparent of this file's directory scan (src_src.Scanner): Scanner from a previous run. This can help speed up tests. Use None for normal operation Returns: DtbPlatdata object Raises: ValueError: if args has no command, or an unknown command """ if not args: raise ValueError('Please specify a command: struct, platdata, all') if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both') if not scan: scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional, phase) scan.scan_drivers() do_process = True else: do_process = False plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate) plat.scan_dtb() plat.scan_tree(add_root=instantiate) plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() plat.process_nodes(instantiate) plat.read_aliases() plat.assign_seqs() # Figure out what output files we plan to generate output_files = OUTPUT_FILES_COMMON if instantiate: output_files.update(OUTPUT_FILES_INST) else: output_files.update(OUTPUT_FILES_NOINST) cmds = args[0].split(',') if 'all' in cmds: cmds = sorted(output_files.keys()) for cmd in cmds: outfile = output_files.get(cmd) if not outfile: raise ValueError("Unknown command '%s': (use: %s)" % (cmd, ', '.join(sorted(output_files.keys())))) plat.setup_output(outfile.ftype, outfile.fname if output_dirs else output) plat.out_header(outfile) outfile.method(plat) plat.finish_output() return plat
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())