blob: 25e4866f20139671fa4771a8b78b6718b077db7e [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
10import os
11import shutil
12import tempfile
13import unittest
14from unittest import mock
15
16from dtoc import src_scan
Simon Glass970349a2020-12-28 20:35:08 -070017from patman import test_util
Simon Glass10ea9c02020-12-28 20:35:07 -070018from patman import tools
19
Simon Glassc58662f2021-02-03 06:00:50 -070020OUR_PATH = os.path.dirname(os.path.realpath(__file__))
21
22class FakeNode:
23 """Fake Node object for testing"""
24 def __init__(self):
25 self.name = None
26 self.props = {}
27
28class FakeProp:
29 """Fake Prop object for testing"""
30 def __init__(self):
31 self.name = None
32 self.value = None
33
Simon Glass10ea9c02020-12-28 20:35:07 -070034# This is a test so is allowed to access private things in the module it is
35# testing
36# pylint: disable=W0212
37
38class TestSrcScan(unittest.TestCase):
39 """Tests for src_scan"""
40 @classmethod
41 def setUpClass(cls):
42 tools.PrepareOutputDir(None)
43
44 @classmethod
45 def tearDownClass(cls):
46 tools.FinaliseOutputDir()
47
Simon Glass970349a2020-12-28 20:35:08 -070048 def test_simple(self):
49 """Simple test of scanning drivers"""
50 scan = src_scan.Scanner(None, True, None)
51 scan.scan_drivers()
52 self.assertIn('sandbox_gpio', scan._drivers)
53 self.assertIn('sandbox_gpio_alias', scan._driver_aliases)
54 self.assertEqual('sandbox_gpio',
55 scan._driver_aliases['sandbox_gpio_alias'])
56 self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases)
57
58 def test_additional(self):
59 """Test with additional drivers to scan"""
Simon Glass10ea9c02020-12-28 20:35:07 -070060 scan = src_scan.Scanner(
61 None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx'])
62 scan.scan_drivers()
Simon Glass970349a2020-12-28 20:35:08 -070063 self.assertIn('sandbox_gpio_alias2', scan._driver_aliases)
64 self.assertEqual('sandbox_gpio',
65 scan._driver_aliases['sandbox_gpio_alias2'])
Simon Glass10ea9c02020-12-28 20:35:07 -070066
Simon Glass970349a2020-12-28 20:35:08 -070067 def test_unicode_error(self):
Simon Glass10ea9c02020-12-28 20:35:07 -070068 """Test running dtoc with an invalid unicode file
69
70 To be able to perform this test without adding a weird text file which
71 would produce issues when using checkpatch.pl or patman, generate the
72 file at runtime and then process it.
73 """
74 driver_fn = '/tmp/' + next(tempfile._get_candidate_names())
75 with open(driver_fn, 'wb+') as fout:
76 fout.write(b'\x81')
77
Simon Glass970349a2020-12-28 20:35:08 -070078 scan = src_scan.Scanner(None, True, [driver_fn])
79 with test_util.capture_sys_output() as (stdout, _):
80 scan.scan_drivers()
81 self.assertRegex(stdout.getvalue(),
82 r"Skipping file '.*' due to unicode error\s*")
Simon Glass10ea9c02020-12-28 20:35:07 -070083
84 def test_driver(self):
85 """Test the Driver class"""
Simon Glassc58662f2021-02-03 06:00:50 -070086 i2c = 'I2C_UCLASS'
87 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
88 'rockchip,rk3288-srf': None}
89 drv1 = src_scan.Driver('fred', 'fred.c')
90 drv2 = src_scan.Driver('mary', 'mary.c')
91 drv3 = src_scan.Driver('fred', 'fred.c')
92 drv1.uclass_id = i2c
93 drv1.compat = compat
94 drv2.uclass_id = i2c
95 drv2.compat = compat
96 drv3.uclass_id = i2c
97 drv3.compat = compat
98 self.assertEqual(
99 "Driver(name='fred', uclass_id='I2C_UCLASS', "
100 "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', "
101 "'rockchip,rk3288-srf': None}, priv=)", str(drv1))
Simon Glass10ea9c02020-12-28 20:35:07 -0700102 self.assertEqual(drv1, drv3)
103 self.assertNotEqual(drv1, drv2)
104 self.assertNotEqual(drv2, drv3)
105
106 def test_scan_dirs(self):
107 """Test scanning of source directories"""
108 def add_file(fname):
109 pathname = os.path.join(indir, fname)
110 dirname = os.path.dirname(pathname)
111 os.makedirs(dirname, exist_ok=True)
112 tools.WriteFile(pathname, '', binary=False)
113 fname_list.append(pathname)
114
115 try:
116 indir = tempfile.mkdtemp(prefix='dtoc.')
117
118 fname_list = []
119 add_file('fname.c')
120 add_file('dir/fname2.c')
121
122 # Mock out scan_driver and check that it is called with the
123 # expected files
124 with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked:
125 scan = src_scan.Scanner(indir, True, None)
126 scan.scan_drivers()
127 self.assertEqual(2, len(mocked.mock_calls))
128 self.assertEqual(mock.call(fname_list[0]),
129 mocked.mock_calls[0])
130 self.assertEqual(mock.call(fname_list[1]),
131 mocked.mock_calls[1])
132 finally:
133 shutil.rmtree(indir)
Simon Glassc58662f2021-02-03 06:00:50 -0700134
135 def test_scan(self):
136 """Test scanning of a driver"""
137 fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c')
138 buff = tools.ReadFile(fname, False)
139 scan = src_scan.Scanner(None, False, None)
140 scan._parse_driver(fname, buff)
141 self.assertIn('i2c_tegra', scan._drivers)
142 drv = scan._drivers['i2c_tegra']
143 self.assertEqual('i2c_tegra', drv.name)
144 self.assertEqual('UCLASS_I2C', drv.uclass_id)
145 self.assertEqual(
146 {'nvidia,tegra114-i2c': 'TYPE_114',
147 'nvidia,tegra20-i2c': 'TYPE_STD',
148 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
149 self.assertEqual('i2c_bus', drv.priv)
150 self.assertEqual(1, len(scan._drivers))
151
152 def test_normalized_name(self):
153 """Test operation of get_normalized_compat_name()"""
154 prop = FakeProp()
155 prop.name = 'compatible'
156 prop.value = 'rockchip,rk3288-grf'
157 node = FakeNode()
158 node.props = {'compatible': prop}
159 scan = src_scan.Scanner(None, False, None)
160 with test_util.capture_sys_output() as (stdout, _):
161 name, aliases = scan.get_normalized_compat_name(node)
162 self.assertEqual('rockchip_rk3288_grf', name)
163 self.assertEqual([], aliases)
164 self.assertEqual(
165 'WARNING: the driver rockchip_rk3288_grf was not found in the driver list',
166 stdout.getvalue().strip())
167
168 i2c = 'I2C_UCLASS'
169 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
170 'rockchip,rk3288-srf': None}
171 drv = src_scan.Driver('fred', 'fred.c')
172 drv.uclass_id = i2c
173 drv.compat = compat
174 scan._drivers['rockchip_rk3288_grf'] = drv
175
176 scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf'
177
178 with test_util.capture_sys_output() as (stdout, _):
179 name, aliases = scan.get_normalized_compat_name(node)
180 self.assertEqual('', stdout.getvalue().strip())
181 self.assertEqual('rockchip_rk3288_grf', name)
182 self.assertEqual([], aliases)
183
184 prop.value = 'rockchip,rk3288-srf'
185 with test_util.capture_sys_output() as (stdout, _):
186 name, aliases = scan.get_normalized_compat_name(node)
187 self.assertEqual('', stdout.getvalue().strip())
188 self.assertEqual('rockchip_rk3288_grf', name)
189 self.assertEqual(['rockchip_rk3288_srf'], aliases)
190
191 def test_scan_errors(self):
192 """Test detection of scanning errors"""
193 buff = '''
194static const struct udevice_id tegra_i2c_ids2[] = {
195 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
196 { }
197};
198
199U_BOOT_DRIVER(i2c_tegra) = {
200 .name = "i2c_tegra",
201 .id = UCLASS_I2C,
202 .of_match = tegra_i2c_ids,
203};
204'''
205 scan = src_scan.Scanner(None, False, None)
206 with self.assertRaises(ValueError) as exc:
207 scan._parse_driver('file.c', buff)
208 self.assertIn(
209 "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)",
210 str(exc.exception))
211
212 def test_of_match(self):
213 """Test detection of of_match_ptr() member"""
214 buff = '''
215static const struct udevice_id tegra_i2c_ids[] = {
216 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
217 { }
218};
219
220U_BOOT_DRIVER(i2c_tegra) = {
221 .name = "i2c_tegra",
222 .id = UCLASS_I2C,
223 .of_match = of_match_ptr(tegra_i2c_ids),
224};
225'''
226 scan = src_scan.Scanner(None, False, None)
227 scan._parse_driver('file.c', buff)
228 self.assertIn('i2c_tegra', scan._drivers)
229 drv = scan._drivers['i2c_tegra']
230 self.assertEqual('i2c_tegra', drv.name)