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
17 # Bring in the patman libraries
18 our_path = os.path.dirname(os.path.realpath(__file__))
19 for dirname in ['../patman', '..']:
20 sys.path.insert(0, os.path.join(our_path, dirname))
24 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, BytesToValue
26 from fdt_util import fdt32_to_cpu
31 def _GetPropertyValue(dtb, node, prop_name):
32 """Low-level function to get the property value based on its offset
34 This looks directly in the device tree at the property's offset to find
35 its value. It is useful as a check that the property is in the correct
40 prop_name: Property name to find
45 Value of property as a string (found using property offset)
47 prop = node.props[prop_name]
49 # Add 12, which is sizeof(struct fdt_property), to get to start of data
50 offset = prop.GetOffset() + 12
51 data = dtb.GetContents()[offset:offset + len(prop.value)]
52 return prop, [tools.ToChar(x) for x in data]
55 class TestFdt(unittest.TestCase):
56 """Tests for the Fdt module
58 This includes unit tests for some functions and functional tests for the fdt
63 tools.PrepareOutputDir(None)
66 def tearDownClass(cls):
67 tools.FinaliseOutputDir()
70 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
73 """Test that we can open an Fdt"""
75 root = self.dtb.GetRoot()
76 self.assertTrue(isinstance(root, fdt.Node))
78 def testGetNode(self):
79 """Test the GetNode() method"""
80 node = self.dtb.GetNode('/spl-test')
81 self.assertTrue(isinstance(node, fdt.Node))
83 node = self.dtb.GetNode('/i2c@0/pmic@9')
84 self.assertTrue(isinstance(node, fdt.Node))
85 self.assertEqual('pmic@9', node.name)
86 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
88 node = self.dtb.GetNode('/')
89 self.assertTrue(isinstance(node, fdt.Node))
90 self.assertEqual(0, node.Offset())
93 """Check that we can flush the device tree out to its file"""
94 fname = self.dtb._fname
95 with open(fname, 'rb') as fd:
98 with self.assertRaises(IOError):
101 with open(fname, 'rb') as fd:
105 """Test that packing a device tree works"""
108 def testGetFdt(self):
109 """Tetst that we can access the raw device-tree data"""
110 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
112 def testGetProps(self):
113 """Tests obtaining a list of properties"""
114 node = self.dtb.GetNode('/spl-test')
115 props = self.dtb.GetProps(node)
116 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
117 'intarray', 'intval', 'longbytearray', 'notstring',
118 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
119 sorted(props.keys()))
121 def testCheckError(self):
122 """Tests the ChecKError() function"""
123 with self.assertRaises(ValueError) as e:
124 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
125 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
127 def testGetFdt(self):
128 node = self.dtb.GetNode('/spl-test')
129 self.assertEqual(self.dtb, node.GetFdt())
131 def testBytesToValue(self):
132 self.assertEqual(BytesToValue(b'this\0is\0'),
133 (TYPE_STRING, ['this', 'is']))
135 class TestNode(unittest.TestCase):
136 """Test operation of the Node class"""
140 tools.PrepareOutputDir(None)
143 def tearDownClass(cls):
144 tools.FinaliseOutputDir()
147 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
148 self.node = self.dtb.GetNode('/spl-test')
150 def testOffset(self):
151 """Tests that we can obtain the offset of a node"""
152 self.assertTrue(self.node.Offset() > 0)
154 def testDelete(self):
155 """Tests that we can delete a property"""
156 node2 = self.dtb.GetNode('/spl-test2')
157 offset1 = node2.Offset()
158 self.node.DeleteProp('intval')
159 offset2 = node2.Offset()
160 self.assertTrue(offset2 < offset1)
161 self.node.DeleteProp('intarray')
162 offset3 = node2.Offset()
163 self.assertTrue(offset3 < offset2)
164 with self.assertRaises(libfdt.FdtException):
165 self.node.DeleteProp('missing')
167 def testDeleteGetOffset(self):
168 """Test that property offset update when properties are deleted"""
169 self.node.DeleteProp('intval')
170 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
171 self.assertEqual(prop.value, value)
173 def testFindNode(self):
174 """Tests that we can find a node using the FindNode() functoin"""
175 node = self.dtb.GetRoot().FindNode('i2c@0')
176 self.assertEqual('i2c@0', node.name)
177 subnode = node.FindNode('pmic@9')
178 self.assertEqual('pmic@9', subnode.name)
179 self.assertEqual(None, node.FindNode('missing'))
181 def testRefreshMissingNode(self):
182 """Test refreshing offsets when an extra node is present in dtb"""
183 # Delete it from our tables, not the device tree
184 del self.dtb._root.subnodes[-1]
185 with self.assertRaises(ValueError) as e:
187 self.assertIn('Internal error, offset', str(e.exception))
189 def testRefreshExtraNode(self):
190 """Test refreshing offsets when an expected node is missing"""
191 # Delete it from the device tre, not our tables
192 self.dtb.GetFdtObj().del_node(self.node.Offset())
193 with self.assertRaises(ValueError) as e:
195 self.assertIn('Internal error, node name mismatch '
196 'spl-test != spl-test2', str(e.exception))
198 def testRefreshMissingProp(self):
199 """Test refreshing offsets when an extra property is present in dtb"""
200 # Delete it from our tables, not the device tree
201 del self.node.props['notstring']
202 with self.assertRaises(ValueError) as e:
204 self.assertIn("Internal error, property 'notstring' missing, offset ",
207 def testLookupPhandle(self):
208 """Test looking up a single phandle"""
209 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
210 node = dtb.GetNode('/phandle-source2')
211 prop = node.props['clocks']
212 target = dtb.GetNode('/phandle-target')
213 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
216 class TestProp(unittest.TestCase):
217 """Test operation of the Prop class"""
221 tools.PrepareOutputDir(None)
224 def tearDownClass(cls):
225 tools.FinaliseOutputDir()
228 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
229 self.node = self.dtb.GetNode('/spl-test')
230 self.fdt = self.dtb.GetFdtObj()
232 def testMissingNode(self):
233 self.assertEqual(None, self.dtb.GetNode('missing'))
235 def testPhandle(self):
236 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
237 node = dtb.GetNode('/phandle-source2')
238 prop = node.props['clocks']
239 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
241 def _ConvertProp(self, prop_name):
242 """Helper function to look up a property in self.node and return it
245 Property name to find
247 Return fdt.Prop object for this property
249 p = self.fdt.getprop(self.node.Offset(), prop_name)
250 return fdt.Prop(self.node, -1, prop_name, p)
252 def testMakeProp(self):
253 """Test we can convert all the the types that are supported"""
254 prop = self._ConvertProp('boolval')
255 self.assertEqual(fdt.TYPE_BOOL, prop.type)
256 self.assertEqual(True, prop.value)
258 prop = self._ConvertProp('intval')
259 self.assertEqual(fdt.TYPE_INT, prop.type)
260 self.assertEqual(1, fdt32_to_cpu(prop.value))
262 prop = self._ConvertProp('intarray')
263 self.assertEqual(fdt.TYPE_INT, prop.type)
264 val = [fdt32_to_cpu(val) for val in prop.value]
265 self.assertEqual([2, 3, 4], val)
267 prop = self._ConvertProp('byteval')
268 self.assertEqual(fdt.TYPE_BYTE, prop.type)
269 self.assertEqual(5, ord(prop.value))
271 prop = self._ConvertProp('longbytearray')
272 self.assertEqual(fdt.TYPE_BYTE, prop.type)
273 val = [ord(val) for val in prop.value]
274 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
276 prop = self._ConvertProp('stringval')
277 self.assertEqual(fdt.TYPE_STRING, prop.type)
278 self.assertEqual('message', prop.value)
280 prop = self._ConvertProp('stringarray')
281 self.assertEqual(fdt.TYPE_STRING, prop.type)
282 self.assertEqual(['multi-word', 'message'], prop.value)
284 prop = self._ConvertProp('notstring')
285 self.assertEqual(fdt.TYPE_BYTE, prop.type)
286 val = [ord(val) for val in prop.value]
287 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
289 def testGetEmpty(self):
290 """Tests the GetEmpty() function for the various supported types"""
291 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
292 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
293 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(fdt.TYPE_INT))
294 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
296 def testGetOffset(self):
297 """Test we can get the offset of a property"""
298 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
299 self.assertEqual(prop.value, value)
302 """Test widening of values"""
303 node2 = self.dtb.GetNode('/spl-test2')
304 prop = self.node.props['intval']
307 prop2 = node2.props['intval']
309 self.assertEqual(fdt.TYPE_INT, prop.type)
310 self.assertEqual(1, fdt32_to_cpu(prop.value))
312 # Convert singla value to array
313 prop2 = self.node.props['intarray']
315 self.assertEqual(fdt.TYPE_INT, prop.type)
316 self.assertTrue(isinstance(prop.value, list))
318 # A 4-byte array looks like a single integer. When widened by a longer
319 # byte array, it should turn into an array.
320 prop = self.node.props['longbytearray']
321 prop2 = node2.props['longbytearray']
322 self.assertFalse(isinstance(prop2.value, list))
323 self.assertEqual(4, len(prop2.value))
325 self.assertTrue(isinstance(prop2.value, list))
326 self.assertEqual(9, len(prop2.value))
328 # Similarly for a string array
329 prop = self.node.props['stringval']
330 prop2 = node2.props['stringarray']
331 self.assertFalse(isinstance(prop.value, list))
332 self.assertEqual(7, len(prop.value))
334 self.assertTrue(isinstance(prop.value, list))
335 self.assertEqual(3, len(prop.value))
337 # Enlarging an existing array
338 prop = self.node.props['stringarray']
339 prop2 = node2.props['stringarray']
340 self.assertTrue(isinstance(prop.value, list))
341 self.assertEqual(2, len(prop.value))
343 self.assertTrue(isinstance(prop.value, list))
344 self.assertEqual(3, len(prop.value))
347 """Test adding properties"""
349 # This function should automatically expand the device tree
350 self.node.AddZeroProp('one')
351 self.node.AddZeroProp('two')
352 self.node.AddZeroProp('three')
353 self.dtb.Sync(auto_resize=True)
355 # Updating existing properties should be OK, since the device-tree size
358 self.node.SetInt('one', 1)
359 self.node.SetInt('two', 2)
360 self.node.SetInt('three', 3)
361 self.dtb.Sync(auto_resize=False)
363 # This should fail since it would need to increase the device-tree size
364 self.node.AddZeroProp('four')
365 with self.assertRaises(libfdt.FdtException) as e:
366 self.dtb.Sync(auto_resize=False)
367 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
368 self.dtb.Sync(auto_resize=True)
370 def testAddNode(self):
372 self.node.AddSubnode('subnode')
373 with self.assertRaises(libfdt.FdtException) as e:
374 self.dtb.Sync(auto_resize=False)
375 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
377 self.dtb.Sync(auto_resize=True)
378 offset = self.fdt.path_offset('/spl-test/subnode')
379 self.assertTrue(offset > 0)
381 def testAddMore(self):
382 """Test various other methods for adding and setting properties"""
383 self.node.AddZeroProp('one')
384 self.dtb.Sync(auto_resize=True)
385 data = self.fdt.getprop(self.node.Offset(), 'one')
386 self.assertEqual(0, fdt32_to_cpu(data))
388 self.node.SetInt('one', 1)
389 self.dtb.Sync(auto_resize=False)
390 data = self.fdt.getprop(self.node.Offset(), 'one')
391 self.assertEqual(1, fdt32_to_cpu(data))
393 val = '123' + chr(0) + '456'
394 self.node.AddString('string', val)
395 self.dtb.Sync(auto_resize=True)
396 data = self.fdt.getprop(self.node.Offset(), 'string')
397 self.assertEqual(tools.ToBytes(val) + b'\0', data)
400 self.node.SetString('string', val + 'x')
401 with self.assertRaises(libfdt.FdtException) as e:
402 self.dtb.Sync(auto_resize=False)
403 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
404 self.node.SetString('string', val[:-1])
406 prop = self.node.props['string']
407 prop.SetData(tools.ToBytes(val))
408 self.dtb.Sync(auto_resize=False)
409 data = self.fdt.getprop(self.node.Offset(), 'string')
410 self.assertEqual(tools.ToBytes(val), data)
412 self.node.AddEmptyProp('empty', 5)
413 self.dtb.Sync(auto_resize=True)
414 prop = self.node.props['empty']
415 prop.SetData(tools.ToBytes(val))
416 self.dtb.Sync(auto_resize=False)
417 data = self.fdt.getprop(self.node.Offset(), 'empty')
418 self.assertEqual(tools.ToBytes(val), data)
420 self.node.SetData('empty', b'123')
421 self.assertEqual(b'123', prop.bytes)
423 def testFromData(self):
424 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
425 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
427 self.node.AddEmptyProp('empty', 5)
428 self.dtb.Sync(auto_resize=True)
429 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
431 def testMissingSetInt(self):
432 """Test handling of a missing property with SetInt"""
433 with self.assertRaises(ValueError) as e:
434 self.node.SetInt('one', 1)
435 self.assertIn("node '/spl-test': Missing property 'one'",
438 def testMissingSetData(self):
439 """Test handling of a missing property with SetData"""
440 with self.assertRaises(ValueError) as e:
441 self.node.SetData('one', b'data')
442 self.assertIn("node '/spl-test': Missing property 'one'",
445 def testMissingSetString(self):
446 """Test handling of a missing property with SetString"""
447 with self.assertRaises(ValueError) as e:
448 self.node.SetString('one', 1)
449 self.assertIn("node '/spl-test': Missing property 'one'",
453 class TestFdtUtil(unittest.TestCase):
454 """Tests for the fdt_util module
456 This module will likely be mostly replaced at some point, once upstream
457 libfdt has better Python support. For now, this provides tests for current
462 tools.PrepareOutputDir(None)
465 def tearDownClass(cls):
466 tools.FinaliseOutputDir()
469 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
470 self.node = self.dtb.GetNode('/spl-test')
472 def testGetInt(self):
473 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
474 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
476 with self.assertRaises(ValueError) as e:
477 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
478 self.assertIn("property 'intarray' has list value: expecting a single "
479 'integer', str(e.exception))
481 def testGetString(self):
482 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
483 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
486 with self.assertRaises(ValueError) as e:
487 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
488 self.assertIn("property 'stringarray' has list value: expecting a "
489 'single string', str(e.exception))
491 def testGetBool(self):
492 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
493 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
494 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
495 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
497 def testGetByte(self):
498 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
499 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
501 with self.assertRaises(ValueError) as e:
502 fdt_util.GetByte(self.node, 'longbytearray')
503 self.assertIn("property 'longbytearray' has list value: expecting a "
504 'single byte', str(e.exception))
506 with self.assertRaises(ValueError) as e:
507 fdt_util.GetByte(self.node, 'intval')
508 self.assertIn("property 'intval' has length 4, expecting 1",
511 def testGetPhandleList(self):
512 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
513 node = dtb.GetNode('/phandle-source2')
514 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
515 node = dtb.GetNode('/phandle-source')
516 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
517 fdt_util.GetPhandleList(node, 'clocks'))
518 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
520 def testGetDataType(self):
521 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
522 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
524 with self.assertRaises(ValueError) as e:
525 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
527 def testFdtCellsToCpu(self):
528 val = self.node.props['intarray'].value
529 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
530 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
532 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
533 node1 = dtb2.GetNode('/test1')
534 val = node1.props['reg'].value
535 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
537 node2 = dtb2.GetNode('/test2')
538 val = node2.props['reg'].value
539 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
540 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
542 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
544 def testEnsureCompiled(self):
545 """Test a degenerate case of this function (file already compiled)"""
546 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
547 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
549 def testEnsureCompiledTmpdir(self):
550 """Test providing a temporary directory"""
552 old_outdir = tools.outdir
554 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
555 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts',
557 self.assertEqual(tmpdir, os.path.dirname(dtb))
558 shutil.rmtree(tmpdir)
560 tools.outdir= old_outdir
563 def RunTestCoverage():
564 """Run the tests and check that we get 100% coverage"""
565 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
566 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
570 """Run all the test we have for the fdt model
573 args: List of positional args provided to fdt. This can hold a test
574 name to execute (as in 'fdt -t testFdt', for example)
576 result = unittest.TestResult()
577 sys.argv = [sys.argv[0]]
578 test_name = args and args[0] or None
579 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
582 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
583 except AttributeError:
586 suite = unittest.TestLoader().loadTestsFromTestCase(module)
590 for _, err in result.errors:
592 for _, err in result.failures:
595 if __name__ != '__main__':
598 parser = OptionParser()
599 parser.add_option('-B', '--build-dir', type='string', default='b',
600 help='Directory containing the build output')
601 parser.add_option('-P', '--processes', type=int,
602 help='set number of processes to use for running tests')
603 parser.add_option('-t', '--test', action='store_true', dest='test',
604 default=False, help='run tests')
605 parser.add_option('-T', '--test-coverage', action='store_true',
606 default=False, help='run tests and check for 100% coverage')
607 (options, args) = parser.parse_args()
609 # Run our meagre tests
612 elif options.test_coverage: