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, BytesToValue
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, [tools.ToChar(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, 'rb') as fd:
91 with self.assertRaises(IOError):
94 with open(fname, 'rb') 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 def testBytesToValue(self):
125 self.assertEqual(BytesToValue(b'this\0is\0'),
126 (TYPE_STRING, ['this', 'is']))
128 class TestNode(unittest.TestCase):
129 """Test operation of the Node class"""
133 tools.PrepareOutputDir(None)
136 def tearDownClass(cls):
137 tools.FinaliseOutputDir()
140 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
141 self.node = self.dtb.GetNode('/spl-test')
143 def testOffset(self):
144 """Tests that we can obtain the offset of a node"""
145 self.assertTrue(self.node.Offset() > 0)
147 def testDelete(self):
148 """Tests that we can delete a property"""
149 node2 = self.dtb.GetNode('/spl-test2')
150 offset1 = node2.Offset()
151 self.node.DeleteProp('intval')
152 offset2 = node2.Offset()
153 self.assertTrue(offset2 < offset1)
154 self.node.DeleteProp('intarray')
155 offset3 = node2.Offset()
156 self.assertTrue(offset3 < offset2)
157 with self.assertRaises(libfdt.FdtException):
158 self.node.DeleteProp('missing')
160 def testDeleteGetOffset(self):
161 """Test that property offset update when properties are deleted"""
162 self.node.DeleteProp('intval')
163 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
164 self.assertEqual(prop.value, value)
166 def testFindNode(self):
167 """Tests that we can find a node using the FindNode() functoin"""
168 node = self.dtb.GetRoot().FindNode('i2c@0')
169 self.assertEqual('i2c@0', node.name)
170 subnode = node.FindNode('pmic@9')
171 self.assertEqual('pmic@9', subnode.name)
172 self.assertEqual(None, node.FindNode('missing'))
174 def testRefreshMissingNode(self):
175 """Test refreshing offsets when an extra node is present in dtb"""
176 # Delete it from our tables, not the device tree
177 del self.dtb._root.subnodes[-1]
178 with self.assertRaises(ValueError) as e:
180 self.assertIn('Internal error, offset', str(e.exception))
182 def testRefreshExtraNode(self):
183 """Test refreshing offsets when an expected node is missing"""
184 # Delete it from the device tre, not our tables
185 self.dtb.GetFdtObj().del_node(self.node.Offset())
186 with self.assertRaises(ValueError) as e:
188 self.assertIn('Internal error, node name mismatch '
189 'spl-test != spl-test2', str(e.exception))
191 def testRefreshMissingProp(self):
192 """Test refreshing offsets when an extra property is present in dtb"""
193 # Delete it from our tables, not the device tree
194 del self.node.props['notstring']
195 with self.assertRaises(ValueError) as e:
197 self.assertIn("Internal error, property 'notstring' missing, offset ",
200 def testLookupPhandle(self):
201 """Test looking up a single phandle"""
202 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
203 node = dtb.GetNode('/phandle-source2')
204 prop = node.props['clocks']
205 target = dtb.GetNode('/phandle-target')
206 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
209 class TestProp(unittest.TestCase):
210 """Test operation of the Prop class"""
214 tools.PrepareOutputDir(None)
217 def tearDownClass(cls):
218 tools.FinaliseOutputDir()
221 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
222 self.node = self.dtb.GetNode('/spl-test')
223 self.fdt = self.dtb.GetFdtObj()
225 def testMissingNode(self):
226 self.assertEqual(None, self.dtb.GetNode('missing'))
228 def testPhandle(self):
229 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
230 node = dtb.GetNode('/phandle-source2')
231 prop = node.props['clocks']
232 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
234 def _ConvertProp(self, prop_name):
235 """Helper function to look up a property in self.node and return it
238 Property name to find
240 Return fdt.Prop object for this property
242 p = self.fdt.getprop(self.node.Offset(), prop_name)
243 return fdt.Prop(self.node, -1, prop_name, p)
245 def testMakeProp(self):
246 """Test we can convert all the the types that are supported"""
247 prop = self._ConvertProp('boolval')
248 self.assertEqual(fdt.TYPE_BOOL, prop.type)
249 self.assertEqual(True, prop.value)
251 prop = self._ConvertProp('intval')
252 self.assertEqual(fdt.TYPE_INT, prop.type)
253 self.assertEqual(1, fdt32_to_cpu(prop.value))
255 prop = self._ConvertProp('intarray')
256 self.assertEqual(fdt.TYPE_INT, prop.type)
257 val = [fdt32_to_cpu(val) for val in prop.value]
258 self.assertEqual([2, 3, 4], val)
260 prop = self._ConvertProp('byteval')
261 self.assertEqual(fdt.TYPE_BYTE, prop.type)
262 self.assertEqual(5, ord(prop.value))
264 prop = self._ConvertProp('longbytearray')
265 self.assertEqual(fdt.TYPE_BYTE, prop.type)
266 val = [ord(val) for val in prop.value]
267 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
269 prop = self._ConvertProp('stringval')
270 self.assertEqual(fdt.TYPE_STRING, prop.type)
271 self.assertEqual('message', prop.value)
273 prop = self._ConvertProp('stringarray')
274 self.assertEqual(fdt.TYPE_STRING, prop.type)
275 self.assertEqual(['multi-word', 'message'], prop.value)
277 prop = self._ConvertProp('notstring')
278 self.assertEqual(fdt.TYPE_BYTE, prop.type)
279 val = [ord(val) for val in prop.value]
280 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
282 def testGetEmpty(self):
283 """Tests the GetEmpty() function for the various supported types"""
284 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
285 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
286 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(fdt.TYPE_INT))
287 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
289 def testGetOffset(self):
290 """Test we can get the offset of a property"""
291 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
292 self.assertEqual(prop.value, value)
295 """Test widening of values"""
296 node2 = self.dtb.GetNode('/spl-test2')
297 prop = self.node.props['intval']
300 prop2 = node2.props['intval']
302 self.assertEqual(fdt.TYPE_INT, prop.type)
303 self.assertEqual(1, fdt32_to_cpu(prop.value))
305 # Convert singla value to array
306 prop2 = self.node.props['intarray']
308 self.assertEqual(fdt.TYPE_INT, prop.type)
309 self.assertTrue(isinstance(prop.value, list))
311 # A 4-byte array looks like a single integer. When widened by a longer
312 # byte array, it should turn into an array.
313 prop = self.node.props['longbytearray']
314 prop2 = node2.props['longbytearray']
315 self.assertFalse(isinstance(prop2.value, list))
316 self.assertEqual(4, len(prop2.value))
318 self.assertTrue(isinstance(prop2.value, list))
319 self.assertEqual(9, len(prop2.value))
321 # Similarly for a string array
322 prop = self.node.props['stringval']
323 prop2 = node2.props['stringarray']
324 self.assertFalse(isinstance(prop.value, list))
325 self.assertEqual(7, len(prop.value))
327 self.assertTrue(isinstance(prop.value, list))
328 self.assertEqual(3, len(prop.value))
330 # Enlarging an existing array
331 prop = self.node.props['stringarray']
332 prop2 = node2.props['stringarray']
333 self.assertTrue(isinstance(prop.value, list))
334 self.assertEqual(2, len(prop.value))
336 self.assertTrue(isinstance(prop.value, list))
337 self.assertEqual(3, len(prop.value))
340 """Test adding properties"""
342 # This function should automatically expand the device tree
343 self.node.AddZeroProp('one')
344 self.node.AddZeroProp('two')
345 self.node.AddZeroProp('three')
346 self.dtb.Sync(auto_resize=True)
348 # Updating existing properties should be OK, since the device-tree size
351 self.node.SetInt('one', 1)
352 self.node.SetInt('two', 2)
353 self.node.SetInt('three', 3)
354 self.dtb.Sync(auto_resize=False)
356 # This should fail since it would need to increase the device-tree size
357 self.node.AddZeroProp('four')
358 with self.assertRaises(libfdt.FdtException) as e:
359 self.dtb.Sync(auto_resize=False)
360 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
361 self.dtb.Sync(auto_resize=True)
363 def testAddNode(self):
365 self.node.AddSubnode('subnode')
366 with self.assertRaises(libfdt.FdtException) as e:
367 self.dtb.Sync(auto_resize=False)
368 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
370 self.dtb.Sync(auto_resize=True)
371 offset = self.fdt.path_offset('/spl-test/subnode')
372 self.assertTrue(offset > 0)
374 def testAddMore(self):
375 """Test various other methods for adding and setting properties"""
376 self.node.AddZeroProp('one')
377 self.dtb.Sync(auto_resize=True)
378 data = self.fdt.getprop(self.node.Offset(), 'one')
379 self.assertEqual(0, fdt32_to_cpu(data))
381 self.node.SetInt('one', 1)
382 self.dtb.Sync(auto_resize=False)
383 data = self.fdt.getprop(self.node.Offset(), 'one')
384 self.assertEqual(1, fdt32_to_cpu(data))
386 val = '123' + chr(0) + '456'
387 self.node.AddString('string', val)
388 self.dtb.Sync(auto_resize=True)
389 data = self.fdt.getprop(self.node.Offset(), 'string')
390 self.assertEqual(tools.ToBytes(val) + b'\0', data)
393 self.node.SetString('string', val + 'x')
394 with self.assertRaises(libfdt.FdtException) as e:
395 self.dtb.Sync(auto_resize=False)
396 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
397 self.node.SetString('string', val[:-1])
399 prop = self.node.props['string']
400 prop.SetData(tools.ToBytes(val))
401 self.dtb.Sync(auto_resize=False)
402 data = self.fdt.getprop(self.node.Offset(), 'string')
403 self.assertEqual(tools.ToBytes(val), data)
405 self.node.AddEmptyProp('empty', 5)
406 self.dtb.Sync(auto_resize=True)
407 prop = self.node.props['empty']
408 prop.SetData(tools.ToBytes(val))
409 self.dtb.Sync(auto_resize=False)
410 data = self.fdt.getprop(self.node.Offset(), 'empty')
411 self.assertEqual(tools.ToBytes(val), data)
413 self.node.SetData('empty', b'123')
414 self.assertEqual(b'123', prop.bytes)
416 def testFromData(self):
417 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
418 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
420 self.node.AddEmptyProp('empty', 5)
421 self.dtb.Sync(auto_resize=True)
422 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
424 def testMissingSetInt(self):
425 """Test handling of a missing property with SetInt"""
426 with self.assertRaises(ValueError) as e:
427 self.node.SetInt('one', 1)
428 self.assertIn("node '/spl-test': Missing property 'one'",
431 def testMissingSetData(self):
432 """Test handling of a missing property with SetData"""
433 with self.assertRaises(ValueError) as e:
434 self.node.SetData('one', b'data')
435 self.assertIn("node '/spl-test': Missing property 'one'",
438 def testMissingSetString(self):
439 """Test handling of a missing property with SetString"""
440 with self.assertRaises(ValueError) as e:
441 self.node.SetString('one', 1)
442 self.assertIn("node '/spl-test': Missing property 'one'",
446 class TestFdtUtil(unittest.TestCase):
447 """Tests for the fdt_util module
449 This module will likely be mostly replaced at some point, once upstream
450 libfdt has better Python support. For now, this provides tests for current
455 tools.PrepareOutputDir(None)
458 def tearDownClass(cls):
459 tools.FinaliseOutputDir()
462 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
463 self.node = self.dtb.GetNode('/spl-test')
465 def testGetInt(self):
466 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
467 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
469 with self.assertRaises(ValueError) as e:
470 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
471 self.assertIn("property 'intarray' has list value: expecting a single "
472 'integer', str(e.exception))
474 def testGetString(self):
475 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
476 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
479 with self.assertRaises(ValueError) as e:
480 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
481 self.assertIn("property 'stringarray' has list value: expecting a "
482 'single string', str(e.exception))
484 def testGetBool(self):
485 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
486 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
487 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
488 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
490 def testGetByte(self):
491 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
492 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
494 with self.assertRaises(ValueError) as e:
495 fdt_util.GetByte(self.node, 'longbytearray')
496 self.assertIn("property 'longbytearray' has list value: expecting a "
497 'single byte', str(e.exception))
499 with self.assertRaises(ValueError) as e:
500 fdt_util.GetByte(self.node, 'intval')
501 self.assertIn("property 'intval' has length 4, expecting 1",
504 def testGetPhandleList(self):
505 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
506 node = dtb.GetNode('/phandle-source2')
507 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
508 node = dtb.GetNode('/phandle-source')
509 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
510 fdt_util.GetPhandleList(node, 'clocks'))
511 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
513 def testGetDataType(self):
514 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
515 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
517 with self.assertRaises(ValueError) as e:
518 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
520 def testFdtCellsToCpu(self):
521 val = self.node.props['intarray'].value
522 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
523 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
525 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
526 node1 = dtb2.GetNode('/test1')
527 val = node1.props['reg'].value
528 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
530 node2 = dtb2.GetNode('/test2')
531 val = node2.props['reg'].value
532 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
533 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
535 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
537 def testEnsureCompiled(self):
538 """Test a degenerate case of this function"""
539 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
540 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
543 def RunTestCoverage():
544 """Run the tests and check that we get 100% coverage"""
545 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
546 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
550 """Run all the test we have for the fdt model
553 args: List of positional args provided to fdt. This can hold a test
554 name to execute (as in 'fdt -t testFdt', for example)
556 result = unittest.TestResult()
557 sys.argv = [sys.argv[0]]
558 test_name = args and args[0] or None
559 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
562 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
563 except AttributeError:
566 suite = unittest.TestLoader().loadTestsFromTestCase(module)
570 for _, err in result.errors:
572 for _, err in result.failures:
575 if __name__ != '__main__':
578 parser = OptionParser()
579 parser.add_option('-B', '--build-dir', type='string', default='b',
580 help='Directory containing the build output')
581 parser.add_option('-P', '--processes', type=int,
582 help='set number of processes to use for running tests')
583 parser.add_option('-t', '--test', action='store_true', dest='test',
584 default=False, help='run tests')
585 parser.add_option('-T', '--test-coverage', action='store_true',
586 default=False, help='run tests and check for 100% coverage')
587 (options, args) = parser.parse_args()
589 # Run our meagre tests
592 elif options.test_coverage: