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())
425 class TestFdtUtil(unittest.TestCase):
426 """Tests for the fdt_util module
428 This module will likely be mostly replaced at some point, once upstream
429 libfdt has better Python support. For now, this provides tests for current
434 tools.PrepareOutputDir(None)
437 def tearDownClass(cls):
438 tools.FinaliseOutputDir()
441 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
442 self.node = self.dtb.GetNode('/spl-test')
444 def testGetInt(self):
445 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
446 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
448 with self.assertRaises(ValueError) as e:
449 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
450 self.assertIn("property 'intarray' has list value: expecting a single "
451 'integer', str(e.exception))
453 def testGetString(self):
454 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
455 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
458 with self.assertRaises(ValueError) as e:
459 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
460 self.assertIn("property 'stringarray' has list value: expecting a "
461 'single string', str(e.exception))
463 def testGetBool(self):
464 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
465 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
466 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
467 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
469 def testGetByte(self):
470 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
471 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
473 with self.assertRaises(ValueError) as e:
474 fdt_util.GetByte(self.node, 'longbytearray')
475 self.assertIn("property 'longbytearray' has list value: expecting a "
476 'single byte', str(e.exception))
478 with self.assertRaises(ValueError) as e:
479 fdt_util.GetByte(self.node, 'intval')
480 self.assertIn("property 'intval' has length 4, expecting 1",
483 def testGetPhandleList(self):
484 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
485 node = dtb.GetNode('/phandle-source2')
486 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
487 node = dtb.GetNode('/phandle-source')
488 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
489 fdt_util.GetPhandleList(node, 'clocks'))
490 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
492 def testGetDataType(self):
493 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
494 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
496 with self.assertRaises(ValueError) as e:
497 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
499 def testFdtCellsToCpu(self):
500 val = self.node.props['intarray'].value
501 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
502 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
504 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
505 node1 = dtb2.GetNode('/test1')
506 val = node1.props['reg'].value
507 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
509 node2 = dtb2.GetNode('/test2')
510 val = node2.props['reg'].value
511 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
512 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
514 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
516 def testEnsureCompiled(self):
517 """Test a degenerate case of this function"""
518 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
519 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
522 def RunTestCoverage():
523 """Run the tests and check that we get 100% coverage"""
524 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
525 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
529 """Run all the test we have for the fdt model
532 args: List of positional args provided to fdt. This can hold a test
533 name to execute (as in 'fdt -t testFdt', for example)
535 result = unittest.TestResult()
536 sys.argv = [sys.argv[0]]
537 test_name = args and args[0] or None
538 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
541 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
542 except AttributeError:
545 suite = unittest.TestLoader().loadTestsFromTestCase(module)
549 for _, err in result.errors:
551 for _, err in result.failures:
554 if __name__ != '__main__':
557 parser = OptionParser()
558 parser.add_option('-B', '--build-dir', type='string', default='b',
559 help='Directory containing the build output')
560 parser.add_option('-P', '--processes', type=int,
561 help='set number of processes to use for running tests')
562 parser.add_option('-t', '--test', action='store_true', dest='test',
563 default=False, help='run tests')
564 parser.add_option('-T', '--test-coverage', action='store_true',
565 default=False, help='run tests and check for 100% coverage')
566 (options, args) = parser.parse_args()
568 # Run our meagre tests
571 elif options.test_coverage: