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'",
452 def testGetFilename(self):
453 """Test the dtb filename can be provided"""
454 self.assertEqual(tools.GetOutputFilename('source.dtb'),
455 self.dtb.GetFilename())
458 class TestFdtUtil(unittest.TestCase):
459 """Tests for the fdt_util module
461 This module will likely be mostly replaced at some point, once upstream
462 libfdt has better Python support. For now, this provides tests for current
467 tools.PrepareOutputDir(None)
470 def tearDownClass(cls):
471 tools.FinaliseOutputDir()
474 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
475 self.node = self.dtb.GetNode('/spl-test')
477 def testGetInt(self):
478 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
479 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
481 with self.assertRaises(ValueError) as e:
482 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
483 self.assertIn("property 'intarray' has list value: expecting a single "
484 'integer', str(e.exception))
486 def testGetString(self):
487 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
488 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
491 with self.assertRaises(ValueError) as e:
492 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
493 self.assertIn("property 'stringarray' has list value: expecting a "
494 'single string', str(e.exception))
496 def testGetBool(self):
497 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
498 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
499 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
500 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
502 def testGetByte(self):
503 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
504 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
506 with self.assertRaises(ValueError) as e:
507 fdt_util.GetByte(self.node, 'longbytearray')
508 self.assertIn("property 'longbytearray' has list value: expecting a "
509 'single byte', str(e.exception))
511 with self.assertRaises(ValueError) as e:
512 fdt_util.GetByte(self.node, 'intval')
513 self.assertIn("property 'intval' has length 4, expecting 1",
516 def testGetPhandleList(self):
517 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
518 node = dtb.GetNode('/phandle-source2')
519 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
520 node = dtb.GetNode('/phandle-source')
521 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
522 fdt_util.GetPhandleList(node, 'clocks'))
523 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
525 def testGetDataType(self):
526 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
527 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
529 with self.assertRaises(ValueError) as e:
530 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
532 def testFdtCellsToCpu(self):
533 val = self.node.props['intarray'].value
534 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
535 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
537 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
538 node1 = dtb2.GetNode('/test1')
539 val = node1.props['reg'].value
540 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
542 node2 = dtb2.GetNode('/test2')
543 val = node2.props['reg'].value
544 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
545 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
547 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
549 def testEnsureCompiled(self):
550 """Test a degenerate case of this function (file already compiled)"""
551 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
552 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
554 def testEnsureCompiledTmpdir(self):
555 """Test providing a temporary directory"""
557 old_outdir = tools.outdir
559 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
560 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts',
562 self.assertEqual(tmpdir, os.path.dirname(dtb))
563 shutil.rmtree(tmpdir)
565 tools.outdir= old_outdir
568 def RunTestCoverage():
569 """Run the tests and check that we get 100% coverage"""
570 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
571 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
575 """Run all the test we have for the fdt model
578 args: List of positional args provided to fdt. This can hold a test
579 name to execute (as in 'fdt -t testFdt', for example)
581 result = unittest.TestResult()
582 sys.argv = [sys.argv[0]]
583 test_name = args and args[0] or None
584 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
587 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
588 except AttributeError:
591 suite = unittest.TestLoader().loadTestsFromTestCase(module)
595 for _, err in result.errors:
597 for _, err in result.failures:
600 if __name__ != '__main__':
603 parser = OptionParser()
604 parser.add_option('-B', '--build-dir', type='string', default='b',
605 help='Directory containing the build output')
606 parser.add_option('-P', '--processes', type=int,
607 help='set number of processes to use for running tests')
608 parser.add_option('-t', '--test', action='store_true', dest='test',
609 default=False, help='run tests')
610 parser.add_option('-T', '--test-coverage', action='store_true',
611 default=False, help='run tests and check for 100% coverage')
612 (options, args) = parser.parse_args()
614 # Run our meagre tests
617 elif options.test_coverage: