2 # SPDX-License-Identifier: GPL-2.0+
3 # Copyright (c) 2018 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
7 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))
21 from dtoc import fdt_util
22 from dtoc.fdt_util import fdt32_to_cpu
23 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, BytesToValue
25 from patman import command
26 from patman import test_util
27 from patman import tools
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))
81 node = self.dtb.GetNode('/i2c@0/pmic@9')
82 self.assertTrue(isinstance(node, fdt.Node))
83 self.assertEqual('pmic@9', node.name)
84 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
86 node = self.dtb.GetNode('/')
87 self.assertTrue(isinstance(node, fdt.Node))
88 self.assertEqual(0, node.Offset())
91 """Check that we can flush the device tree out to its file"""
92 fname = self.dtb._fname
93 with open(fname, 'rb') as fd:
96 with self.assertRaises(IOError):
99 with open(fname, 'rb') as fd:
103 """Test that packing a device tree works"""
106 def testGetFdt(self):
107 """Tetst that we can access the raw device-tree data"""
108 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
110 def testGetProps(self):
111 """Tests obtaining a list of properties"""
112 node = self.dtb.GetNode('/spl-test')
113 props = self.dtb.GetProps(node)
114 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
115 'intarray', 'intval', 'longbytearray', 'notstring',
116 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
117 sorted(props.keys()))
119 def testCheckError(self):
120 """Tests the ChecKError() function"""
121 with self.assertRaises(ValueError) as e:
122 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
123 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
125 def testGetFdt(self):
126 node = self.dtb.GetNode('/spl-test')
127 self.assertEqual(self.dtb, node.GetFdt())
129 def testBytesToValue(self):
130 self.assertEqual(BytesToValue(b'this\0is\0'),
131 (TYPE_STRING, ['this', 'is']))
133 class TestNode(unittest.TestCase):
134 """Test operation of the Node class"""
138 tools.PrepareOutputDir(None)
141 def tearDownClass(cls):
142 tools.FinaliseOutputDir()
145 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
146 self.node = self.dtb.GetNode('/spl-test')
148 def testOffset(self):
149 """Tests that we can obtain the offset of a node"""
150 self.assertTrue(self.node.Offset() > 0)
152 def testDelete(self):
153 """Tests that we can delete a property"""
154 node2 = self.dtb.GetNode('/spl-test2')
155 offset1 = node2.Offset()
156 self.node.DeleteProp('intval')
157 offset2 = node2.Offset()
158 self.assertTrue(offset2 < offset1)
159 self.node.DeleteProp('intarray')
160 offset3 = node2.Offset()
161 self.assertTrue(offset3 < offset2)
162 with self.assertRaises(libfdt.FdtException):
163 self.node.DeleteProp('missing')
165 def testDeleteGetOffset(self):
166 """Test that property offset update when properties are deleted"""
167 self.node.DeleteProp('intval')
168 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
169 self.assertEqual(prop.value, value)
171 def testFindNode(self):
172 """Tests that we can find a node using the FindNode() functoin"""
173 node = self.dtb.GetRoot().FindNode('i2c@0')
174 self.assertEqual('i2c@0', node.name)
175 subnode = node.FindNode('pmic@9')
176 self.assertEqual('pmic@9', subnode.name)
177 self.assertEqual(None, node.FindNode('missing'))
179 def testRefreshMissingNode(self):
180 """Test refreshing offsets when an extra node is present in dtb"""
181 # Delete it from our tables, not the device tree
182 del self.dtb._root.subnodes[-1]
183 with self.assertRaises(ValueError) as e:
185 self.assertIn('Internal error, offset', str(e.exception))
187 def testRefreshExtraNode(self):
188 """Test refreshing offsets when an expected node is missing"""
189 # Delete it from the device tre, not our tables
190 self.dtb.GetFdtObj().del_node(self.node.Offset())
191 with self.assertRaises(ValueError) as e:
193 self.assertIn('Internal error, node name mismatch '
194 'spl-test != spl-test2', str(e.exception))
196 def testRefreshMissingProp(self):
197 """Test refreshing offsets when an extra property is present in dtb"""
198 # Delete it from our tables, not the device tree
199 del self.node.props['notstring']
200 with self.assertRaises(ValueError) as e:
202 self.assertIn("Internal error, property 'notstring' missing, offset ",
205 def testLookupPhandle(self):
206 """Test looking up a single phandle"""
207 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
208 node = dtb.GetNode('/phandle-source2')
209 prop = node.props['clocks']
210 target = dtb.GetNode('/phandle-target')
211 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
214 class TestProp(unittest.TestCase):
215 """Test operation of the Prop class"""
219 tools.PrepareOutputDir(None)
222 def tearDownClass(cls):
223 tools.FinaliseOutputDir()
226 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
227 self.node = self.dtb.GetNode('/spl-test')
228 self.fdt = self.dtb.GetFdtObj()
230 def testMissingNode(self):
231 self.assertEqual(None, self.dtb.GetNode('missing'))
233 def testPhandle(self):
234 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
235 node = dtb.GetNode('/phandle-source2')
236 prop = node.props['clocks']
237 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
239 def _ConvertProp(self, prop_name):
240 """Helper function to look up a property in self.node and return it
243 Property name to find
245 Return fdt.Prop object for this property
247 p = self.fdt.getprop(self.node.Offset(), prop_name)
248 return fdt.Prop(self.node, -1, prop_name, p)
250 def testMakeProp(self):
251 """Test we can convert all the the types that are supported"""
252 prop = self._ConvertProp('boolval')
253 self.assertEqual(fdt.TYPE_BOOL, prop.type)
254 self.assertEqual(True, prop.value)
256 prop = self._ConvertProp('intval')
257 self.assertEqual(fdt.TYPE_INT, prop.type)
258 self.assertEqual(1, fdt32_to_cpu(prop.value))
260 prop = self._ConvertProp('intarray')
261 self.assertEqual(fdt.TYPE_INT, prop.type)
262 val = [fdt32_to_cpu(val) for val in prop.value]
263 self.assertEqual([2, 3, 4], val)
265 prop = self._ConvertProp('byteval')
266 self.assertEqual(fdt.TYPE_BYTE, prop.type)
267 self.assertEqual(5, ord(prop.value))
269 prop = self._ConvertProp('longbytearray')
270 self.assertEqual(fdt.TYPE_BYTE, prop.type)
271 val = [ord(val) for val in prop.value]
272 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
274 prop = self._ConvertProp('stringval')
275 self.assertEqual(fdt.TYPE_STRING, prop.type)
276 self.assertEqual('message', prop.value)
278 prop = self._ConvertProp('stringarray')
279 self.assertEqual(fdt.TYPE_STRING, prop.type)
280 self.assertEqual(['multi-word', 'message'], prop.value)
282 prop = self._ConvertProp('notstring')
283 self.assertEqual(fdt.TYPE_BYTE, prop.type)
284 val = [ord(val) for val in prop.value]
285 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
287 def testGetEmpty(self):
288 """Tests the GetEmpty() function for the various supported types"""
289 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
290 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
291 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(fdt.TYPE_INT))
292 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
294 def testGetOffset(self):
295 """Test we can get the offset of a property"""
296 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
297 self.assertEqual(prop.value, value)
300 """Test widening of values"""
301 node2 = self.dtb.GetNode('/spl-test2')
302 prop = self.node.props['intval']
305 prop2 = node2.props['intval']
307 self.assertEqual(fdt.TYPE_INT, prop.type)
308 self.assertEqual(1, fdt32_to_cpu(prop.value))
310 # Convert singla value to array
311 prop2 = self.node.props['intarray']
313 self.assertEqual(fdt.TYPE_INT, prop.type)
314 self.assertTrue(isinstance(prop.value, list))
316 # A 4-byte array looks like a single integer. When widened by a longer
317 # byte array, it should turn into an array.
318 prop = self.node.props['longbytearray']
319 prop2 = node2.props['longbytearray']
320 self.assertFalse(isinstance(prop2.value, list))
321 self.assertEqual(4, len(prop2.value))
323 self.assertTrue(isinstance(prop2.value, list))
324 self.assertEqual(9, len(prop2.value))
326 # Similarly for a string array
327 prop = self.node.props['stringval']
328 prop2 = node2.props['stringarray']
329 self.assertFalse(isinstance(prop.value, list))
330 self.assertEqual(7, len(prop.value))
332 self.assertTrue(isinstance(prop.value, list))
333 self.assertEqual(3, len(prop.value))
335 # Enlarging an existing array
336 prop = self.node.props['stringarray']
337 prop2 = node2.props['stringarray']
338 self.assertTrue(isinstance(prop.value, list))
339 self.assertEqual(2, len(prop.value))
341 self.assertTrue(isinstance(prop.value, list))
342 self.assertEqual(3, len(prop.value))
345 """Test adding properties"""
347 # This function should automatically expand the device tree
348 self.node.AddZeroProp('one')
349 self.node.AddZeroProp('two')
350 self.node.AddZeroProp('three')
351 self.dtb.Sync(auto_resize=True)
353 # Updating existing properties should be OK, since the device-tree size
356 self.node.SetInt('one', 1)
357 self.node.SetInt('two', 2)
358 self.node.SetInt('three', 3)
359 self.dtb.Sync(auto_resize=False)
361 # This should fail since it would need to increase the device-tree size
362 self.node.AddZeroProp('four')
363 with self.assertRaises(libfdt.FdtException) as e:
364 self.dtb.Sync(auto_resize=False)
365 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
366 self.dtb.Sync(auto_resize=True)
368 def testAddNode(self):
370 self.node.AddSubnode('subnode')
371 with self.assertRaises(libfdt.FdtException) as e:
372 self.dtb.Sync(auto_resize=False)
373 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
375 self.dtb.Sync(auto_resize=True)
376 offset = self.fdt.path_offset('/spl-test/subnode')
377 self.assertTrue(offset > 0)
379 def testAddMore(self):
380 """Test various other methods for adding and setting properties"""
381 self.node.AddZeroProp('one')
382 self.dtb.Sync(auto_resize=True)
383 data = self.fdt.getprop(self.node.Offset(), 'one')
384 self.assertEqual(0, fdt32_to_cpu(data))
386 self.node.SetInt('one', 1)
387 self.dtb.Sync(auto_resize=False)
388 data = self.fdt.getprop(self.node.Offset(), 'one')
389 self.assertEqual(1, fdt32_to_cpu(data))
391 val = '123' + chr(0) + '456'
392 self.node.AddString('string', val)
393 self.dtb.Sync(auto_resize=True)
394 data = self.fdt.getprop(self.node.Offset(), 'string')
395 self.assertEqual(tools.ToBytes(val) + b'\0', data)
398 self.node.SetString('string', val + 'x')
399 with self.assertRaises(libfdt.FdtException) as e:
400 self.dtb.Sync(auto_resize=False)
401 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
402 self.node.SetString('string', val[:-1])
404 prop = self.node.props['string']
405 prop.SetData(tools.ToBytes(val))
406 self.dtb.Sync(auto_resize=False)
407 data = self.fdt.getprop(self.node.Offset(), 'string')
408 self.assertEqual(tools.ToBytes(val), data)
410 self.node.AddEmptyProp('empty', 5)
411 self.dtb.Sync(auto_resize=True)
412 prop = self.node.props['empty']
413 prop.SetData(tools.ToBytes(val))
414 self.dtb.Sync(auto_resize=False)
415 data = self.fdt.getprop(self.node.Offset(), 'empty')
416 self.assertEqual(tools.ToBytes(val), data)
418 self.node.SetData('empty', b'123')
419 self.assertEqual(b'123', prop.bytes)
421 def testFromData(self):
422 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
423 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
425 self.node.AddEmptyProp('empty', 5)
426 self.dtb.Sync(auto_resize=True)
427 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
429 def testMissingSetInt(self):
430 """Test handling of a missing property with SetInt"""
431 with self.assertRaises(ValueError) as e:
432 self.node.SetInt('one', 1)
433 self.assertIn("node '/spl-test': Missing property 'one'",
436 def testMissingSetData(self):
437 """Test handling of a missing property with SetData"""
438 with self.assertRaises(ValueError) as e:
439 self.node.SetData('one', b'data')
440 self.assertIn("node '/spl-test': Missing property 'one'",
443 def testMissingSetString(self):
444 """Test handling of a missing property with SetString"""
445 with self.assertRaises(ValueError) as e:
446 self.node.SetString('one', 1)
447 self.assertIn("node '/spl-test': Missing property 'one'",
450 def testGetFilename(self):
451 """Test the dtb filename can be provided"""
452 self.assertEqual(tools.GetOutputFilename('source.dtb'),
453 self.dtb.GetFilename())
456 class TestFdtUtil(unittest.TestCase):
457 """Tests for the fdt_util module
459 This module will likely be mostly replaced at some point, once upstream
460 libfdt has better Python support. For now, this provides tests for current
465 tools.PrepareOutputDir(None)
468 def tearDownClass(cls):
469 tools.FinaliseOutputDir()
472 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
473 self.node = self.dtb.GetNode('/spl-test')
475 def testGetInt(self):
476 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
477 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
479 with self.assertRaises(ValueError) as e:
480 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
481 self.assertIn("property 'intarray' has list value: expecting a single "
482 'integer', str(e.exception))
484 def testGetString(self):
485 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
486 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
489 with self.assertRaises(ValueError) as e:
490 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
491 self.assertIn("property 'stringarray' has list value: expecting a "
492 'single string', str(e.exception))
494 def testGetBool(self):
495 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
496 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
497 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
498 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
500 def testGetByte(self):
501 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
502 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
504 with self.assertRaises(ValueError) as e:
505 fdt_util.GetByte(self.node, 'longbytearray')
506 self.assertIn("property 'longbytearray' has list value: expecting a "
507 'single byte', str(e.exception))
509 with self.assertRaises(ValueError) as e:
510 fdt_util.GetByte(self.node, 'intval')
511 self.assertIn("property 'intval' has length 4, expecting 1",
514 def testGetPhandleList(self):
515 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
516 node = dtb.GetNode('/phandle-source2')
517 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
518 node = dtb.GetNode('/phandle-source')
519 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
520 fdt_util.GetPhandleList(node, 'clocks'))
521 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
523 def testGetDataType(self):
524 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
525 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
527 with self.assertRaises(ValueError) as e:
528 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
530 def testFdtCellsToCpu(self):
531 val = self.node.props['intarray'].value
532 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
533 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
535 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
536 node1 = dtb2.GetNode('/test1')
537 val = node1.props['reg'].value
538 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
540 node2 = dtb2.GetNode('/test2')
541 val = node2.props['reg'].value
542 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
543 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
545 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
547 def testEnsureCompiled(self):
548 """Test a degenerate case of this function (file already compiled)"""
549 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
550 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
552 def testEnsureCompiledTmpdir(self):
553 """Test providing a temporary directory"""
555 old_outdir = tools.outdir
557 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
558 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts',
560 self.assertEqual(tmpdir, os.path.dirname(dtb))
561 shutil.rmtree(tmpdir)
563 tools.outdir= old_outdir
566 def RunTestCoverage():
567 """Run the tests and check that we get 100% coverage"""
568 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
569 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
573 """Run all the test we have for the fdt model
576 args: List of positional args provided to fdt. This can hold a test
577 name to execute (as in 'fdt -t testFdt', for example)
579 result = unittest.TestResult()
580 sys.argv = [sys.argv[0]]
581 test_name = args and args[0] or None
582 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
585 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
586 except AttributeError:
589 suite = unittest.TestLoader().loadTestsFromTestCase(module)
593 for _, err in result.errors:
595 for _, err in result.failures:
598 if __name__ != '__main__':
601 parser = OptionParser()
602 parser.add_option('-B', '--build-dir', type='string', default='b',
603 help='Directory containing the build output')
604 parser.add_option('-P', '--processes', type=int,
605 help='set number of processes to use for running tests')
606 parser.add_option('-t', '--test', action='store_true', dest='test',
607 default=False, help='run tests')
608 parser.add_option('-T', '--test-coverage', action='store_true',
609 default=False, help='run tests and check for 100% coverage')
610 (options, args) = parser.parse_args()
612 # Run our meagre tests
615 elif options.test_coverage: