blob: 9fef8ed5496ec0bcae2ab8732eac531d6265623f [file] [log] [blame]
Simon Glass0ed50752018-07-06 10:27:24 -06001#!/usr/bin/python
2# SPDX-License-Identifier: GPL-2.0+
3# Copyright (c) 2018 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6
7from optparse import OptionParser
8import glob
9import os
10import sys
11import unittest
12
13# Bring in the patman libraries
14our_path = os.path.dirname(os.path.realpath(__file__))
15for dirname in ['../patman', '..']:
16 sys.path.insert(0, os.path.join(our_path, dirname))
17
18import command
19import fdt
20from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
Simon Glass9c526332018-07-06 10:27:28 -060021import fdt_util
Simon Glass0ed50752018-07-06 10:27:24 -060022from fdt_util import fdt32_to_cpu
23import libfdt
24import test_util
25import tools
26
Simon Glass4df8a0c2018-07-06 10:27:29 -060027def _GetPropertyValue(dtb, node, prop_name):
28 """Low-level function to get the property value based on its offset
29
30 This looks directly in the device tree at the property's offset to find
31 its value. It is useful as a check that the property is in the correct
32 place.
33
34 Args:
35 node: Node to look in
36 prop_name: Property name to find
37
38 Returns:
39 Tuple:
40 Prop object found
41 Value of property as a string (found using property offset)
42 """
43 prop = node.props[prop_name]
44
45 # Add 12, which is sizeof(struct fdt_property), to get to start of data
46 offset = prop.GetOffset() + 12
47 data = dtb.GetContents()[offset:offset + len(prop.value)]
48 return prop, [chr(x) for x in data]
49
50
Simon Glass0ed50752018-07-06 10:27:24 -060051class TestFdt(unittest.TestCase):
52 """Tests for the Fdt module
53
54 This includes unit tests for some functions and functional tests for the fdt
55 module.
56 """
57 @classmethod
58 def setUpClass(cls):
59 tools.PrepareOutputDir(None)
60
61 @classmethod
62 def tearDownClass(cls):
63 tools._FinaliseForTest()
64
65 def setUp(self):
66 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
67
68 def testFdt(self):
69 """Test that we can open an Fdt"""
70 self.dtb.Scan()
71 root = self.dtb.GetRoot()
72 self.assertTrue(isinstance(root, fdt.Node))
73
74 def testGetNode(self):
75 """Test the GetNode() method"""
76 node = self.dtb.GetNode('/spl-test')
77 self.assertTrue(isinstance(node, fdt.Node))
78 node = self.dtb.GetNode('/i2c@0/pmic@9')
79 self.assertTrue(isinstance(node, fdt.Node))
80 self.assertEqual('pmic@9', node.name)
Simon Glass9c526332018-07-06 10:27:28 -060081 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass0ed50752018-07-06 10:27:24 -060082
83 def testFlush(self):
84 """Check that we can flush the device tree out to its file"""
85 fname = self.dtb._fname
86 with open(fname) as fd:
87 data = fd.read()
88 os.remove(fname)
89 with self.assertRaises(IOError):
90 open(fname)
91 self.dtb.Flush()
92 with open(fname) as fd:
93 data = fd.read()
94
95 def testPack(self):
96 """Test that packing a device tree works"""
97 self.dtb.Pack()
98
99 def testGetFdt(self):
100 """Tetst that we can access the raw device-tree data"""
Simon Glass792d2392018-07-06 10:27:27 -0600101 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass0ed50752018-07-06 10:27:24 -0600102
103 def testGetProps(self):
104 """Tests obtaining a list of properties"""
105 node = self.dtb.GetNode('/spl-test')
106 props = self.dtb.GetProps(node)
107 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
Simon Glass9c526332018-07-06 10:27:28 -0600108 'intarray', 'intval', 'longbytearray', 'notstring',
Simon Glass0ed50752018-07-06 10:27:24 -0600109 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
110 sorted(props.keys()))
111
112 def testCheckError(self):
113 """Tests the ChecKError() function"""
114 with self.assertRaises(ValueError) as e:
Simon Glass9c526332018-07-06 10:27:28 -0600115 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass0ed50752018-07-06 10:27:24 -0600116 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
117
118
119class TestNode(unittest.TestCase):
120 """Test operation of the Node class"""
121
122 @classmethod
123 def setUpClass(cls):
124 tools.PrepareOutputDir(None)
125
126 @classmethod
127 def tearDownClass(cls):
128 tools._FinaliseForTest()
129
130 def setUp(self):
131 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
132 self.node = self.dtb.GetNode('/spl-test')
133
134 def testOffset(self):
135 """Tests that we can obtain the offset of a node"""
136 self.assertTrue(self.node.Offset() > 0)
137
138 def testDelete(self):
139 """Tests that we can delete a property"""
140 node2 = self.dtb.GetNode('/spl-test2')
141 offset1 = node2.Offset()
142 self.node.DeleteProp('intval')
143 offset2 = node2.Offset()
144 self.assertTrue(offset2 < offset1)
145 self.node.DeleteProp('intarray')
146 offset3 = node2.Offset()
147 self.assertTrue(offset3 < offset2)
Simon Glass9c526332018-07-06 10:27:28 -0600148 with self.assertRaises(libfdt.FdtException):
149 self.node.DeleteProp('missing')
Simon Glass0ed50752018-07-06 10:27:24 -0600150
Simon Glass4df8a0c2018-07-06 10:27:29 -0600151 def testDeleteGetOffset(self):
152 """Test that property offset update when properties are deleted"""
153 self.node.DeleteProp('intval')
154 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
155 self.assertEqual(prop.value, value)
156
Simon Glass0ed50752018-07-06 10:27:24 -0600157 def testFindNode(self):
158 """Tests that we can find a node using the _FindNode() functoin"""
159 node = self.dtb.GetRoot()._FindNode('i2c@0')
160 self.assertEqual('i2c@0', node.name)
161 subnode = node._FindNode('pmic@9')
162 self.assertEqual('pmic@9', subnode.name)
Simon Glass9c526332018-07-06 10:27:28 -0600163 self.assertEqual(None, node._FindNode('missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600164
Simon Glass4df8a0c2018-07-06 10:27:29 -0600165 def testRefreshMissingNode(self):
166 """Test refreshing offsets when an extra node is present in dtb"""
167 # Delete it from our tables, not the device tree
168 del self.dtb._root.subnodes[-1]
169 with self.assertRaises(ValueError) as e:
170 self.dtb.Refresh()
171 self.assertIn('Internal error, offset', str(e.exception))
172
173 def testRefreshExtraNode(self):
174 """Test refreshing offsets when an expected node is missing"""
175 # Delete it from the device tre, not our tables
176 self.dtb.GetFdtObj().del_node(self.node.Offset())
177 with self.assertRaises(ValueError) as e:
178 self.dtb.Refresh()
179 self.assertIn('Internal error, node name mismatch '
180 'spl-test != spl-test2', str(e.exception))
181
182 def testRefreshMissingProp(self):
183 """Test refreshing offsets when an extra property is present in dtb"""
184 # Delete it from our tables, not the device tree
185 del self.node.props['notstring']
186 with self.assertRaises(ValueError) as e:
187 self.dtb.Refresh()
188 self.assertIn("Internal error, property 'notstring' missing, offset ",
189 str(e.exception))
190
Simon Glass0ed50752018-07-06 10:27:24 -0600191
192class TestProp(unittest.TestCase):
193 """Test operation of the Prop class"""
194
195 @classmethod
196 def setUpClass(cls):
197 tools.PrepareOutputDir(None)
198
199 @classmethod
200 def tearDownClass(cls):
201 tools._FinaliseForTest()
202
203 def setUp(self):
204 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
205 self.node = self.dtb.GetNode('/spl-test')
206 self.fdt = self.dtb.GetFdtObj()
207
Simon Glassc5eddc82018-07-06 10:27:30 -0600208 def testMissingNode(self):
209 self.assertEqual(None, self.dtb.GetNode('missing'))
210
Simon Glass9c526332018-07-06 10:27:28 -0600211 def testPhandle(self):
212 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
213 node = dtb.GetNode('/phandle-source')
214
215 def _ConvertProp(self, prop_name):
216 """Helper function to look up a property in self.node and return it
217
218 Args:
219 Property name to find
220
221 Return fdt.Prop object for this property
222 """
223 p = self.fdt.get_property(self.node.Offset(), prop_name)
224 return fdt.Prop(self.node, -1, prop_name, p)
225
226 def testMakeProp(self):
227 """Test we can convert all the the types that are supported"""
228 prop = self._ConvertProp('boolval')
229 self.assertEqual(fdt.TYPE_BOOL, prop.type)
230 self.assertEqual(True, prop.value)
231
232 prop = self._ConvertProp('intval')
233 self.assertEqual(fdt.TYPE_INT, prop.type)
234 self.assertEqual(1, fdt32_to_cpu(prop.value))
235
236 prop = self._ConvertProp('intarray')
237 self.assertEqual(fdt.TYPE_INT, prop.type)
238 val = [fdt32_to_cpu(val) for val in prop.value]
239 self.assertEqual([2, 3, 4], val)
240
241 prop = self._ConvertProp('byteval')
242 self.assertEqual(fdt.TYPE_BYTE, prop.type)
243 self.assertEqual(5, ord(prop.value))
244
245 prop = self._ConvertProp('longbytearray')
246 self.assertEqual(fdt.TYPE_BYTE, prop.type)
247 val = [ord(val) for val in prop.value]
248 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
249
250 prop = self._ConvertProp('stringval')
251 self.assertEqual(fdt.TYPE_STRING, prop.type)
252 self.assertEqual('message', prop.value)
253
254 prop = self._ConvertProp('stringarray')
255 self.assertEqual(fdt.TYPE_STRING, prop.type)
256 self.assertEqual(['multi-word', 'message'], prop.value)
257
258 prop = self._ConvertProp('notstring')
259 self.assertEqual(fdt.TYPE_BYTE, prop.type)
260 val = [ord(val) for val in prop.value]
261 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
262
Simon Glass0ed50752018-07-06 10:27:24 -0600263 def testGetEmpty(self):
264 """Tests the GetEmpty() function for the various supported types"""
265 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
266 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
267 self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
268 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
269
270 def testGetOffset(self):
271 """Test we can get the offset of a property"""
Simon Glass4df8a0c2018-07-06 10:27:29 -0600272 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
273 self.assertEqual(prop.value, value)
Simon Glass0ed50752018-07-06 10:27:24 -0600274
275 def testWiden(self):
276 """Test widening of values"""
277 node2 = self.dtb.GetNode('/spl-test2')
278 prop = self.node.props['intval']
279
280 # No action
281 prop2 = node2.props['intval']
282 prop.Widen(prop2)
283 self.assertEqual(fdt.TYPE_INT, prop.type)
284 self.assertEqual(1, fdt32_to_cpu(prop.value))
285
286 # Convert singla value to array
287 prop2 = self.node.props['intarray']
288 prop.Widen(prop2)
289 self.assertEqual(fdt.TYPE_INT, prop.type)
290 self.assertTrue(isinstance(prop.value, list))
291
292 # A 4-byte array looks like a single integer. When widened by a longer
293 # byte array, it should turn into an array.
294 prop = self.node.props['longbytearray']
295 prop2 = node2.props['longbytearray']
296 self.assertFalse(isinstance(prop2.value, list))
297 self.assertEqual(4, len(prop2.value))
298 prop2.Widen(prop)
299 self.assertTrue(isinstance(prop2.value, list))
300 self.assertEqual(9, len(prop2.value))
301
302 # Similarly for a string array
303 prop = self.node.props['stringval']
304 prop2 = node2.props['stringarray']
305 self.assertFalse(isinstance(prop.value, list))
306 self.assertEqual(7, len(prop.value))
307 prop.Widen(prop2)
308 self.assertTrue(isinstance(prop.value, list))
309 self.assertEqual(3, len(prop.value))
310
311 # Enlarging an existing array
312 prop = self.node.props['stringarray']
313 prop2 = node2.props['stringarray']
314 self.assertTrue(isinstance(prop.value, list))
315 self.assertEqual(2, len(prop.value))
316 prop.Widen(prop2)
317 self.assertTrue(isinstance(prop.value, list))
318 self.assertEqual(3, len(prop.value))
319
320
Simon Glass9c526332018-07-06 10:27:28 -0600321class TestFdtUtil(unittest.TestCase):
322 """Tests for the fdt_util module
323
324 This module will likely be mostly replaced at some point, once upstream
325 libfdt has better Python support. For now, this provides tests for current
326 functionality.
327 """
328 @classmethod
329 def setUpClass(cls):
330 tools.PrepareOutputDir(None)
331
332 def setUp(self):
333 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
334 self.node = self.dtb.GetNode('/spl-test')
335
336 def testGetInt(self):
337 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
338 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
339
340 with self.assertRaises(ValueError) as e:
341 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
342 self.assertIn("property 'intarray' has list value: expecting a single "
343 'integer', str(e.exception))
344
345 def testGetString(self):
346 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
347 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
348 'test'))
349
350 with self.assertRaises(ValueError) as e:
351 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
352 self.assertIn("property 'stringarray' has list value: expecting a "
353 'single string', str(e.exception))
354
355 def testGetBool(self):
356 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
357 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
358 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
359 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
360
361 def testFdtCellsToCpu(self):
362 val = self.node.props['intarray'].value
363 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
364 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
365
366 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
367 node2 = dtb2.GetNode('/test1')
368 val = node2.props['reg'].value
369 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
370
371 def testEnsureCompiled(self):
372 """Test a degenerate case of this function"""
373 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
374 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
375
376 def testGetPlainBytes(self):
377 self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
378
379
380def RunTestCoverage():
381 """Run the tests and check that we get 100% coverage"""
382 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
383 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
384
385
Simon Glass0ed50752018-07-06 10:27:24 -0600386def RunTests(args):
387 """Run all the test we have for the fdt model
388
389 Args:
390 args: List of positional args provided to fdt. This can hold a test
391 name to execute (as in 'fdt -t testFdt', for example)
392 """
393 result = unittest.TestResult()
394 sys.argv = [sys.argv[0]]
395 test_name = args and args[0] or None
Simon Glass9c526332018-07-06 10:27:28 -0600396 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass0ed50752018-07-06 10:27:24 -0600397 if test_name:
398 try:
399 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
400 except AttributeError:
401 continue
402 else:
403 suite = unittest.TestLoader().loadTestsFromTestCase(module)
404 suite.run(result)
405
406 print result
407 for _, err in result.errors:
408 print err
409 for _, err in result.failures:
410 print err
411
412if __name__ != '__main__':
413 sys.exit(1)
414
415parser = OptionParser()
Simon Glass9c526332018-07-06 10:27:28 -0600416parser.add_option('-B', '--build-dir', type='string', default='b',
417 help='Directory containing the build output')
Simon Glass0ed50752018-07-06 10:27:24 -0600418parser.add_option('-t', '--test', action='store_true', dest='test',
419 default=False, help='run tests')
Simon Glass9c526332018-07-06 10:27:28 -0600420parser.add_option('-T', '--test-coverage', action='store_true',
421 default=False, help='run tests and check for 100% coverage')
Simon Glass0ed50752018-07-06 10:27:24 -0600422(options, args) = parser.parse_args()
423
424# Run our meagre tests
425if options.test:
426 RunTests(args)
Simon Glass9c526332018-07-06 10:27:28 -0600427elif options.test_coverage:
428 RunTestCoverage()