patman: Convert camel case in tools.py
[platform/kernel/u-boot.git] / tools / dtoc / test_src_scan.py
index 62dea2a..bdfa669 100644 (file)
@@ -7,6 +7,7 @@
 This includes unit tests for scanning of the source code
 """
 
+import copy
 import os
 import shutil
 import tempfile
@@ -19,6 +20,9 @@ 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):
@@ -39,15 +43,15 @@ class TestSrcScan(unittest.TestCase):
     """Tests for src_scan"""
     @classmethod
     def setUpClass(cls):
-        tools.PrepareOutputDir(None)
+        tools.prepare_output_dir(None)
 
     @classmethod
     def tearDownClass(cls):
-        tools.FinaliseOutputDir()
+        tools.finalise_output_dir()
 
     def test_simple(self):
         """Simple test of scanning drivers"""
-        scan = src_scan.Scanner(None, True, None)
+        scan = src_scan.Scanner(None, None)
         scan.scan_drivers()
         self.assertIn('sandbox_gpio', scan._drivers)
         self.assertIn('sandbox_gpio_alias', scan._driver_aliases)
@@ -58,7 +62,7 @@ class TestSrcScan(unittest.TestCase):
     def test_additional(self):
         """Test with additional drivers to scan"""
         scan = src_scan.Scanner(
-            None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx'])
+            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',
@@ -75,7 +79,7 @@ class TestSrcScan(unittest.TestCase):
         with open(driver_fn, 'wb+') as fout:
             fout.write(b'\x81')
 
-        scan = src_scan.Scanner(None, True, [driver_fn])
+        scan = src_scan.Scanner(None, [driver_fn])
         with test_util.capture_sys_output() as (stdout, _):
             scan.scan_drivers()
         self.assertRegex(stdout.getvalue(),
@@ -96,7 +100,7 @@ class TestSrcScan(unittest.TestCase):
         drv3.uclass_id = i2c
         drv3.compat = compat
         self.assertEqual(
-            "Driver(name='fred', uclass_id='I2C_UCLASS', "
+            "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)
@@ -109,7 +113,7 @@ class TestSrcScan(unittest.TestCase):
             pathname = os.path.join(indir, fname)
             dirname = os.path.dirname(pathname)
             os.makedirs(dirname, exist_ok=True)
-            tools.WriteFile(pathname, '', binary=False)
+            tools.write_file(pathname, '', binary=False)
             fname_list.append(pathname)
 
         try:
@@ -124,7 +128,7 @@ class TestSrcScan(unittest.TestCase):
             # 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, True, None)
+                scan = src_scan.Scanner(indir, None)
                 scan.scan_drivers()
             self.assertEqual(2, len(mocked.mock_calls))
             self.assertEqual(mock.call(fname_list[0]),
@@ -138,8 +142,8 @@ class TestSrcScan(unittest.TestCase):
     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, False, None)
+        buff = tools.read_file(fname, False)
+        scan = src_scan.Scanner(None, None)
         scan._parse_driver(fname, buff)
         self.assertIn('i2c_tegra', scan._drivers)
         drv = scan._drivers['i2c_tegra']
@@ -151,6 +155,7 @@ class TestSrcScan(unittest.TestCase):
              '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()"""
@@ -159,14 +164,19 @@ class TestSrcScan(unittest.TestCase):
         prop.value = 'rockchip,rk3288-grf'
         node = FakeNode()
         node.props = {'compatible': prop}
-        scan = src_scan.Scanner(None, False, None)
+
+        # 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(
-            'WARNING: the driver rockchip_rk3288_grf was not found in the driver list',
-            stdout.getvalue().strip())
+        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',
@@ -183,6 +193,7 @@ class TestSrcScan(unittest.TestCase):
         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, _):
@@ -190,6 +201,7 @@ class TestSrcScan(unittest.TestCase):
         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"""
@@ -205,7 +217,7 @@ U_BOOT_DRIVER(i2c_tegra) = {
        .of_match = tegra_i2c_ids,
 };
 '''
-        scan = src_scan.Scanner(None, False, None)
+        scan = src_scan.Scanner(None, None)
         with self.assertRaises(ValueError) as exc:
             scan._parse_driver('file.c', buff)
         self.assertIn(
@@ -226,11 +238,13 @@ U_BOOT_DRIVER(i2c_tegra) = {
        .of_match = of_match_ptr(tegra_i2c_ids),
 };
 '''
-        scan = src_scan.Scanner(None, False, None)
+        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"""
@@ -248,9 +262,12 @@ U_BOOT_DRIVER(testing) = {
        .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 = src_scan.Scanner(None, None)
         scan._parse_driver('file.c', buff)
         self.assertIn('testing', scan._drivers)
         drv = scan._drivers['testing']
@@ -262,4 +279,343 @@ U_BOOT_DRIVER(testing) = {
         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.get_output_filename('output.h')
+        tools.write_file(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)