blob: 62dea2a9612aed806f68326522fc092593efe178 [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')
Simon Glass36b22202021-02-03 06:00:52 -0700120 add_file('.git/ignoreme.c')
Simon Glass10ea9c02020-12-28 20:35:07 -0700121 add_file('dir/fname2.c')
Simon Glass36b22202021-02-03 06:00:52 -0700122 add_file('build-sandbox/ignoreme2.c')
Simon Glass10ea9c02020-12-28 20:35:07 -0700123
124 # Mock out scan_driver and check that it is called with the
125 # expected files
126 with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked:
127 scan = src_scan.Scanner(indir, True, None)
128 scan.scan_drivers()
129 self.assertEqual(2, len(mocked.mock_calls))
130 self.assertEqual(mock.call(fname_list[0]),
131 mocked.mock_calls[0])
Simon Glass36b22202021-02-03 06:00:52 -0700132 # .git file should be ignored
133 self.assertEqual(mock.call(fname_list[2]),
Simon Glass10ea9c02020-12-28 20:35:07 -0700134 mocked.mock_calls[1])
135 finally:
136 shutil.rmtree(indir)
Simon Glassc58662f2021-02-03 06:00:50 -0700137
138 def test_scan(self):
139 """Test scanning of a driver"""
140 fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c')
141 buff = tools.ReadFile(fname, False)
142 scan = src_scan.Scanner(None, False, None)
143 scan._parse_driver(fname, buff)
144 self.assertIn('i2c_tegra', scan._drivers)
145 drv = scan._drivers['i2c_tegra']
146 self.assertEqual('i2c_tegra', drv.name)
147 self.assertEqual('UCLASS_I2C', drv.uclass_id)
148 self.assertEqual(
149 {'nvidia,tegra114-i2c': 'TYPE_114',
150 'nvidia,tegra20-i2c': 'TYPE_STD',
151 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat)
152 self.assertEqual('i2c_bus', drv.priv)
153 self.assertEqual(1, len(scan._drivers))
154
155 def test_normalized_name(self):
156 """Test operation of get_normalized_compat_name()"""
157 prop = FakeProp()
158 prop.name = 'compatible'
159 prop.value = 'rockchip,rk3288-grf'
160 node = FakeNode()
161 node.props = {'compatible': prop}
162 scan = src_scan.Scanner(None, False, None)
163 with test_util.capture_sys_output() as (stdout, _):
164 name, aliases = scan.get_normalized_compat_name(node)
165 self.assertEqual('rockchip_rk3288_grf', name)
166 self.assertEqual([], aliases)
167 self.assertEqual(
168 'WARNING: the driver rockchip_rk3288_grf was not found in the driver list',
169 stdout.getvalue().strip())
170
171 i2c = 'I2C_UCLASS'
172 compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF',
173 'rockchip,rk3288-srf': None}
174 drv = src_scan.Driver('fred', 'fred.c')
175 drv.uclass_id = i2c
176 drv.compat = compat
177 scan._drivers['rockchip_rk3288_grf'] = drv
178
179 scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf'
180
181 with test_util.capture_sys_output() as (stdout, _):
182 name, aliases = scan.get_normalized_compat_name(node)
183 self.assertEqual('', stdout.getvalue().strip())
184 self.assertEqual('rockchip_rk3288_grf', name)
185 self.assertEqual([], aliases)
186
187 prop.value = 'rockchip,rk3288-srf'
188 with test_util.capture_sys_output() as (stdout, _):
189 name, aliases = scan.get_normalized_compat_name(node)
190 self.assertEqual('', stdout.getvalue().strip())
191 self.assertEqual('rockchip_rk3288_grf', name)
192 self.assertEqual(['rockchip_rk3288_srf'], aliases)
193
194 def test_scan_errors(self):
195 """Test detection of scanning errors"""
196 buff = '''
197static const struct udevice_id tegra_i2c_ids2[] = {
198 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
199 { }
200};
201
202U_BOOT_DRIVER(i2c_tegra) = {
203 .name = "i2c_tegra",
204 .id = UCLASS_I2C,
205 .of_match = tegra_i2c_ids,
206};
207'''
208 scan = src_scan.Scanner(None, False, None)
209 with self.assertRaises(ValueError) as exc:
210 scan._parse_driver('file.c', buff)
211 self.assertIn(
212 "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)",
213 str(exc.exception))
214
215 def test_of_match(self):
216 """Test detection of of_match_ptr() member"""
217 buff = '''
218static const struct udevice_id tegra_i2c_ids[] = {
219 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
220 { }
221};
222
223U_BOOT_DRIVER(i2c_tegra) = {
224 .name = "i2c_tegra",
225 .id = UCLASS_I2C,
226 .of_match = of_match_ptr(tegra_i2c_ids),
227};
228'''
229 scan = src_scan.Scanner(None, False, None)
230 scan._parse_driver('file.c', buff)
231 self.assertIn('i2c_tegra', scan._drivers)
232 drv = scan._drivers['i2c_tegra']
233 self.assertEqual('i2c_tegra', drv.name)
Simon Glassc8b19b02021-02-03 06:00:53 -0700234
235 def test_priv(self):
236 """Test collection of struct info from drivers"""
237 buff = '''
238static const struct udevice_id test_ids[] = {
239 { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 },
240 { }
241};
242
243U_BOOT_DRIVER(testing) = {
244 .name = "testing",
245 .id = UCLASS_I2C,
246 .of_match = test_ids,
247 .priv_auto = sizeof(struct some_priv),
248 .plat_auto = sizeof(struct some_plat),
249 .per_child_auto = sizeof(struct some_cpriv),
250 .per_child_plat_auto = sizeof(struct some_cplat),
251};
252'''
253 scan = src_scan.Scanner(None, False, None)
254 scan._parse_driver('file.c', buff)
255 self.assertIn('testing', scan._drivers)
256 drv = scan._drivers['testing']
257 self.assertEqual('testing', drv.name)
258 self.assertEqual('UCLASS_I2C', drv.uclass_id)
259 self.assertEqual(
260 {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat)
261 self.assertEqual('some_priv', drv.priv)
262 self.assertEqual('some_plat', drv.plat)
263 self.assertEqual('some_cpriv', drv.child_priv)
264 self.assertEqual('some_cplat', drv.child_plat)
265 self.assertEqual(1, len(scan._drivers))