2 # SPDX-License-Identifier: GPL-2.0+
3 # Copyright (c) 2018 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
7 from __future__ import print_function
9 from optparse import OptionParser
15 # Bring in the patman libraries
16 our_path = os.path.dirname(os.path.realpath(__file__))
17 for dirname in ['../patman', '..']:
18 sys.path.insert(0, os.path.join(our_path, dirname))
22 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
24 from fdt_util import fdt32_to_cpu
29 def _GetPropertyValue(dtb, node, prop_name):
30 """Low-level function to get the property value based on its offset
32 This looks directly in the device tree at the property's offset to find
33 its value. It is useful as a check that the property is in the correct
38 prop_name: Property name to find
43 Value of property as a string (found using property offset)
45 prop = node.props[prop_name]
47 # Add 12, which is sizeof(struct fdt_property), to get to start of data
48 offset = prop.GetOffset() + 12
49 data = dtb.GetContents()[offset:offset + len(prop.value)]
50 return prop, [chr(x) for x in data]
53 class TestFdt(unittest.TestCase):
54 """Tests for the Fdt module
56 This includes unit tests for some functions and functional tests for the fdt
61 tools.PrepareOutputDir(None)
64 def tearDownClass(cls):
65 tools.FinaliseOutputDir()
68 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
71 """Test that we can open an Fdt"""
73 root = self.dtb.GetRoot()
74 self.assertTrue(isinstance(root, fdt.Node))
76 def testGetNode(self):
77 """Test the GetNode() method"""
78 node = self.dtb.GetNode('/spl-test')
79 self.assertTrue(isinstance(node, fdt.Node))
80 node = self.dtb.GetNode('/i2c@0/pmic@9')
81 self.assertTrue(isinstance(node, fdt.Node))
82 self.assertEqual('pmic@9', node.name)
83 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
86 """Check that we can flush the device tree out to its file"""
87 fname = self.dtb._fname
88 with open(fname) as fd:
91 with self.assertRaises(IOError):
94 with open(fname) as fd:
98 """Test that packing a device tree works"""
101 def testGetFdt(self):
102 """Tetst that we can access the raw device-tree data"""
103 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
105 def testGetProps(self):
106 """Tests obtaining a list of properties"""
107 node = self.dtb.GetNode('/spl-test')
108 props = self.dtb.GetProps(node)
109 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
110 'intarray', 'intval', 'longbytearray', 'notstring',
111 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
112 sorted(props.keys()))
114 def testCheckError(self):
115 """Tests the ChecKError() function"""
116 with self.assertRaises(ValueError) as e:
117 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
118 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
120 def testGetFdt(self):
121 node = self.dtb.GetNode('/spl-test')
122 self.assertEqual(self.dtb, node.GetFdt())
124 class TestNode(unittest.TestCase):
125 """Test operation of the Node class"""
129 tools.PrepareOutputDir(None)
132 def tearDownClass(cls):
133 tools.FinaliseOutputDir()
136 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
137 self.node = self.dtb.GetNode('/spl-test')
139 def testOffset(self):
140 """Tests that we can obtain the offset of a node"""
141 self.assertTrue(self.node.Offset() > 0)
143 def testDelete(self):
144 """Tests that we can delete a property"""
145 node2 = self.dtb.GetNode('/spl-test2')
146 offset1 = node2.Offset()
147 self.node.DeleteProp('intval')
148 offset2 = node2.Offset()
149 self.assertTrue(offset2 < offset1)
150 self.node.DeleteProp('intarray')
151 offset3 = node2.Offset()
152 self.assertTrue(offset3 < offset2)
153 with self.assertRaises(libfdt.FdtException):
154 self.node.DeleteProp('missing')
156 def testDeleteGetOffset(self):
157 """Test that property offset update when properties are deleted"""
158 self.node.DeleteProp('intval')
159 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
160 self.assertEqual(prop.value, value)
162 def testFindNode(self):
163 """Tests that we can find a node using the FindNode() functoin"""
164 node = self.dtb.GetRoot().FindNode('i2c@0')
165 self.assertEqual('i2c@0', node.name)
166 subnode = node.FindNode('pmic@9')
167 self.assertEqual('pmic@9', subnode.name)
168 self.assertEqual(None, node.FindNode('missing'))
170 def testRefreshMissingNode(self):
171 """Test refreshing offsets when an extra node is present in dtb"""
172 # Delete it from our tables, not the device tree
173 del self.dtb._root.subnodes[-1]
174 with self.assertRaises(ValueError) as e:
176 self.assertIn('Internal error, offset', str(e.exception))
178 def testRefreshExtraNode(self):
179 """Test refreshing offsets when an expected node is missing"""
180 # Delete it from the device tre, not our tables
181 self.dtb.GetFdtObj().del_node(self.node.Offset())
182 with self.assertRaises(ValueError) as e:
184 self.assertIn('Internal error, node name mismatch '
185 'spl-test != spl-test2', str(e.exception))
187 def testRefreshMissingProp(self):
188 """Test refreshing offsets when an extra property is present in dtb"""
189 # Delete it from our tables, not the device tree
190 del self.node.props['notstring']
191 with self.assertRaises(ValueError) as e:
193 self.assertIn("Internal error, property 'notstring' missing, offset ",
196 def testLookupPhandle(self):
197 """Test looking up a single phandle"""
198 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
199 node = dtb.GetNode('/phandle-source2')
200 prop = node.props['clocks']
201 target = dtb.GetNode('/phandle-target')
202 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
205 class TestProp(unittest.TestCase):
206 """Test operation of the Prop class"""
210 tools.PrepareOutputDir(None)
213 def tearDownClass(cls):
214 tools.FinaliseOutputDir()
217 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
218 self.node = self.dtb.GetNode('/spl-test')
219 self.fdt = self.dtb.GetFdtObj()
221 def testMissingNode(self):
222 self.assertEqual(None, self.dtb.GetNode('missing'))
224 def testPhandle(self):
225 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
226 node = dtb.GetNode('/phandle-source2')
227 prop = node.props['clocks']
228 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
230 def _ConvertProp(self, prop_name):
231 """Helper function to look up a property in self.node and return it
234 Property name to find
236 Return fdt.Prop object for this property
238 p = self.fdt.getprop(self.node.Offset(), prop_name)
239 return fdt.Prop(self.node, -1, prop_name, p)
241 def testMakeProp(self):
242 """Test we can convert all the the types that are supported"""
243 prop = self._ConvertProp('boolval')
244 self.assertEqual(fdt.TYPE_BOOL, prop.type)
245 self.assertEqual(True, prop.value)
247 prop = self._ConvertProp('intval')
248 self.assertEqual(fdt.TYPE_INT, prop.type)
249 self.assertEqual(1, fdt32_to_cpu(prop.value))
251 prop = self._ConvertProp('intarray')
252 self.assertEqual(fdt.TYPE_INT, prop.type)
253 val = [fdt32_to_cpu(val) for val in prop.value]
254 self.assertEqual([2, 3, 4], val)
256 prop = self._ConvertProp('byteval')
257 self.assertEqual(fdt.TYPE_BYTE, prop.type)
258 self.assertEqual(5, ord(prop.value))
260 prop = self._ConvertProp('longbytearray')
261 self.assertEqual(fdt.TYPE_BYTE, prop.type)
262 val = [ord(val) for val in prop.value]
263 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
265 prop = self._ConvertProp('stringval')
266 self.assertEqual(fdt.TYPE_STRING, prop.type)
267 self.assertEqual('message', prop.value)
269 prop = self._ConvertProp('stringarray')
270 self.assertEqual(fdt.TYPE_STRING, prop.type)
271 self.assertEqual(['multi-word', 'message'], prop.value)
273 prop = self._ConvertProp('notstring')
274 self.assertEqual(fdt.TYPE_BYTE, prop.type)
275 val = [ord(val) for val in prop.value]
276 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
278 def testGetEmpty(self):
279 """Tests the GetEmpty() function for the various supported types"""
280 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
281 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
282 self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
283 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
285 def testGetOffset(self):
286 """Test we can get the offset of a property"""
287 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
288 self.assertEqual(prop.value, value)
291 """Test widening of values"""
292 node2 = self.dtb.GetNode('/spl-test2')
293 prop = self.node.props['intval']
296 prop2 = node2.props['intval']
298 self.assertEqual(fdt.TYPE_INT, prop.type)
299 self.assertEqual(1, fdt32_to_cpu(prop.value))
301 # Convert singla value to array
302 prop2 = self.node.props['intarray']
304 self.assertEqual(fdt.TYPE_INT, prop.type)
305 self.assertTrue(isinstance(prop.value, list))
307 # A 4-byte array looks like a single integer. When widened by a longer
308 # byte array, it should turn into an array.
309 prop = self.node.props['longbytearray']
310 prop2 = node2.props['longbytearray']
311 self.assertFalse(isinstance(prop2.value, list))
312 self.assertEqual(4, len(prop2.value))
314 self.assertTrue(isinstance(prop2.value, list))
315 self.assertEqual(9, len(prop2.value))
317 # Similarly for a string array
318 prop = self.node.props['stringval']
319 prop2 = node2.props['stringarray']
320 self.assertFalse(isinstance(prop.value, list))
321 self.assertEqual(7, len(prop.value))
323 self.assertTrue(isinstance(prop.value, list))
324 self.assertEqual(3, len(prop.value))
326 # Enlarging an existing array
327 prop = self.node.props['stringarray']
328 prop2 = node2.props['stringarray']
329 self.assertTrue(isinstance(prop.value, list))
330 self.assertEqual(2, len(prop.value))
332 self.assertTrue(isinstance(prop.value, list))
333 self.assertEqual(3, len(prop.value))
336 """Test adding properties"""
338 # This function should automatically expand the device tree
339 self.node.AddZeroProp('one')
340 self.node.AddZeroProp('two')
341 self.node.AddZeroProp('three')
342 self.dtb.Sync(auto_resize=True)
344 # Updating existing properties should be OK, since the device-tree size
347 self.node.SetInt('one', 1)
348 self.node.SetInt('two', 2)
349 self.node.SetInt('three', 3)
350 self.dtb.Sync(auto_resize=False)
352 # This should fail since it would need to increase the device-tree size
353 self.node.AddZeroProp('four')
354 with self.assertRaises(libfdt.FdtException) as e:
355 self.dtb.Sync(auto_resize=False)
356 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
357 self.dtb.Sync(auto_resize=True)
359 def testAddNode(self):
361 self.node.AddSubnode('subnode')
362 with self.assertRaises(libfdt.FdtException) as e:
363 self.dtb.Sync(auto_resize=False)
364 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
366 self.dtb.Sync(auto_resize=True)
367 offset = self.fdt.path_offset('/spl-test/subnode')
368 self.assertTrue(offset > 0)
370 def testAddMore(self):
371 """Test various other methods for adding and setting properties"""
372 self.node.AddZeroProp('one')
373 self.dtb.Sync(auto_resize=True)
374 data = self.fdt.getprop(self.node.Offset(), 'one')
375 self.assertEqual(0, fdt32_to_cpu(data))
377 self.node.SetInt('one', 1)
378 self.dtb.Sync(auto_resize=False)
379 data = self.fdt.getprop(self.node.Offset(), 'one')
380 self.assertEqual(1, fdt32_to_cpu(data))
382 val = '123' + chr(0) + '456'
383 self.node.AddString('string', val)
384 self.dtb.Sync(auto_resize=True)
385 data = self.fdt.getprop(self.node.Offset(), 'string')
386 self.assertEqual(val + '\0', data)
389 self.node.SetString('string', val + 'x')
390 with self.assertRaises(libfdt.FdtException) as e:
391 self.dtb.Sync(auto_resize=False)
392 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
393 self.node.SetString('string', val[:-1])
395 prop = self.node.props['string']
397 self.dtb.Sync(auto_resize=False)
398 data = self.fdt.getprop(self.node.Offset(), 'string')
399 self.assertEqual(val, data)
401 self.node.AddEmptyProp('empty', 5)
402 self.dtb.Sync(auto_resize=True)
403 prop = self.node.props['empty']
405 self.dtb.Sync(auto_resize=False)
406 data = self.fdt.getprop(self.node.Offset(), 'empty')
407 self.assertEqual(val, data)
409 self.node.SetData('empty', '123')
410 self.assertEqual('123', prop.bytes)
412 def testFromData(self):
413 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
414 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
416 self.node.AddEmptyProp('empty', 5)
417 self.dtb.Sync(auto_resize=True)
418 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
421 class TestFdtUtil(unittest.TestCase):
422 """Tests for the fdt_util module
424 This module will likely be mostly replaced at some point, once upstream
425 libfdt has better Python support. For now, this provides tests for current
430 tools.PrepareOutputDir(None)
433 def tearDownClass(cls):
434 tools.FinaliseOutputDir()
437 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
438 self.node = self.dtb.GetNode('/spl-test')
440 def testGetInt(self):
441 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
442 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
444 with self.assertRaises(ValueError) as e:
445 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
446 self.assertIn("property 'intarray' has list value: expecting a single "
447 'integer', str(e.exception))
449 def testGetString(self):
450 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
451 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
454 with self.assertRaises(ValueError) as e:
455 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
456 self.assertIn("property 'stringarray' has list value: expecting a "
457 'single string', str(e.exception))
459 def testGetBool(self):
460 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
461 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
462 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
463 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
465 def testGetByte(self):
466 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
467 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
469 with self.assertRaises(ValueError) as e:
470 fdt_util.GetByte(self.node, 'longbytearray')
471 self.assertIn("property 'longbytearray' has list value: expecting a "
472 'single byte', str(e.exception))
474 with self.assertRaises(ValueError) as e:
475 fdt_util.GetByte(self.node, 'intval')
476 self.assertIn("property 'intval' has length 4, expecting 1",
479 def testGetPhandleList(self):
480 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
481 node = dtb.GetNode('/phandle-source2')
482 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
483 node = dtb.GetNode('/phandle-source')
484 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
485 fdt_util.GetPhandleList(node, 'clocks'))
486 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
488 def testGetDataType(self):
489 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
490 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
492 with self.assertRaises(ValueError) as e:
493 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
495 def testFdtCellsToCpu(self):
496 val = self.node.props['intarray'].value
497 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
498 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
500 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
501 node2 = dtb2.GetNode('/test1')
502 val = node2.props['reg'].value
503 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
505 def testEnsureCompiled(self):
506 """Test a degenerate case of this function"""
507 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
508 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
510 def testGetPlainBytes(self):
511 self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
514 def RunTestCoverage():
515 """Run the tests and check that we get 100% coverage"""
516 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
517 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
521 """Run all the test we have for the fdt model
524 args: List of positional args provided to fdt. This can hold a test
525 name to execute (as in 'fdt -t testFdt', for example)
527 result = unittest.TestResult()
528 sys.argv = [sys.argv[0]]
529 test_name = args and args[0] or None
530 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
533 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
534 except AttributeError:
537 suite = unittest.TestLoader().loadTestsFromTestCase(module)
541 for _, err in result.errors:
543 for _, err in result.failures:
546 if __name__ != '__main__':
549 parser = OptionParser()
550 parser.add_option('-B', '--build-dir', type='string', default='b',
551 help='Directory containing the build output')
552 parser.add_option('-P', '--processes', type=int,
553 help='set number of processes to use for running tests')
554 parser.add_option('-t', '--test', action='store_true', dest='test',
555 default=False, help='run tests')
556 parser.add_option('-T', '--test-coverage', action='store_true',
557 default=False, help='run tests and check for 100% coverage')
558 (options, args) = parser.parse_args()
560 # Run our meagre tests
563 elif options.test_coverage: