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 sys.path.insert(1, os.path.join(our_path, '..'))
20 from dtoc import fdt_util
21 from dtoc.fdt_util import fdt32_to_cpu
22 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, BytesToValue
24 from patman import command
25 from patman import test_util
26 from patman import tools
28 def _GetPropertyValue(dtb, node, prop_name):
29 """Low-level function to get the property value based on its offset
31 This looks directly in the device tree at the property's offset to find
32 its value. It is useful as a check that the property is in the correct
37 prop_name: Property name to find
42 Value of property as a string (found using property offset)
44 prop = node.props[prop_name]
46 # Add 12, which is sizeof(struct fdt_property), to get to start of data
47 offset = prop.GetOffset() + 12
48 data = dtb.GetContents()[offset:offset + len(prop.value)]
49 return prop, [tools.ToChar(x) for x in data]
52 class TestFdt(unittest.TestCase):
53 """Tests for the Fdt module
55 This includes unit tests for some functions and functional tests for the fdt
60 tools.PrepareOutputDir(None)
63 def tearDownClass(cls):
64 tools.FinaliseOutputDir()
67 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
70 """Test that we can open an Fdt"""
72 root = self.dtb.GetRoot()
73 self.assertTrue(isinstance(root, fdt.Node))
75 def testGetNode(self):
76 """Test the GetNode() method"""
77 node = self.dtb.GetNode('/spl-test')
78 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'))
85 node = self.dtb.GetNode('/')
86 self.assertTrue(isinstance(node, fdt.Node))
87 self.assertEqual(0, node.Offset())
90 """Check that we can flush the device tree out to its file"""
91 fname = self.dtb._fname
92 with open(fname, 'rb') as fd:
95 with self.assertRaises(IOError):
98 with open(fname, 'rb') as fd:
102 """Test that packing a device tree works"""
105 def testGetFdt(self):
106 """Tetst that we can access the raw device-tree data"""
107 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
109 def testGetProps(self):
110 """Tests obtaining a list of properties"""
111 node = self.dtb.GetNode('/spl-test')
112 props = self.dtb.GetProps(node)
113 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
114 'intarray', 'intval', 'longbytearray', 'notstring',
115 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
116 sorted(props.keys()))
118 def testCheckError(self):
119 """Tests the ChecKError() function"""
120 with self.assertRaises(ValueError) as e:
121 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
122 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
124 def testGetFdt(self):
125 node = self.dtb.GetNode('/spl-test')
126 self.assertEqual(self.dtb, node.GetFdt())
128 def testBytesToValue(self):
129 self.assertEqual(BytesToValue(b'this\0is\0'),
130 (TYPE_STRING, ['this', 'is']))
132 class TestNode(unittest.TestCase):
133 """Test operation of the Node class"""
137 tools.PrepareOutputDir(None)
140 def tearDownClass(cls):
141 tools.FinaliseOutputDir()
144 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
145 self.node = self.dtb.GetNode('/spl-test')
147 def testOffset(self):
148 """Tests that we can obtain the offset of a node"""
149 self.assertTrue(self.node.Offset() > 0)
151 def testDelete(self):
152 """Tests that we can delete a property"""
153 node2 = self.dtb.GetNode('/spl-test2')
154 offset1 = node2.Offset()
155 self.node.DeleteProp('intval')
156 offset2 = node2.Offset()
157 self.assertTrue(offset2 < offset1)
158 self.node.DeleteProp('intarray')
159 offset3 = node2.Offset()
160 self.assertTrue(offset3 < offset2)
161 with self.assertRaises(libfdt.FdtException):
162 self.node.DeleteProp('missing')
164 def testDeleteGetOffset(self):
165 """Test that property offset update when properties are deleted"""
166 self.node.DeleteProp('intval')
167 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
168 self.assertEqual(prop.value, value)
170 def testFindNode(self):
171 """Tests that we can find a node using the FindNode() functoin"""
172 node = self.dtb.GetRoot().FindNode('i2c@0')
173 self.assertEqual('i2c@0', node.name)
174 subnode = node.FindNode('pmic@9')
175 self.assertEqual('pmic@9', subnode.name)
176 self.assertEqual(None, node.FindNode('missing'))
178 def testRefreshMissingNode(self):
179 """Test refreshing offsets when an extra node is present in dtb"""
180 # Delete it from our tables, not the device tree
181 del self.dtb._root.subnodes[-1]
182 with self.assertRaises(ValueError) as e:
184 self.assertIn('Internal error, offset', str(e.exception))
186 def testRefreshExtraNode(self):
187 """Test refreshing offsets when an expected node is missing"""
188 # Delete it from the device tre, not our tables
189 self.dtb.GetFdtObj().del_node(self.node.Offset())
190 with self.assertRaises(ValueError) as e:
192 self.assertIn('Internal error, node name mismatch '
193 'spl-test != spl-test2', str(e.exception))
195 def testRefreshMissingProp(self):
196 """Test refreshing offsets when an extra property is present in dtb"""
197 # Delete it from our tables, not the device tree
198 del self.node.props['notstring']
199 with self.assertRaises(ValueError) as e:
201 self.assertIn("Internal error, property 'notstring' missing, offset ",
204 def testLookupPhandle(self):
205 """Test looking up a single phandle"""
206 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
207 node = dtb.GetNode('/phandle-source2')
208 prop = node.props['clocks']
209 target = dtb.GetNode('/phandle-target')
210 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
213 class TestProp(unittest.TestCase):
214 """Test operation of the Prop class"""
218 tools.PrepareOutputDir(None)
221 def tearDownClass(cls):
222 tools.FinaliseOutputDir()
225 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
226 self.node = self.dtb.GetNode('/spl-test')
227 self.fdt = self.dtb.GetFdtObj()
229 def testMissingNode(self):
230 self.assertEqual(None, self.dtb.GetNode('missing'))
232 def testPhandle(self):
233 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
234 node = dtb.GetNode('/phandle-source2')
235 prop = node.props['clocks']
236 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
238 def _ConvertProp(self, prop_name):
239 """Helper function to look up a property in self.node and return it
242 Property name to find
244 Return fdt.Prop object for this property
246 p = self.fdt.getprop(self.node.Offset(), prop_name)
247 return fdt.Prop(self.node, -1, prop_name, p)
249 def testMakeProp(self):
250 """Test we can convert all the the types that are supported"""
251 prop = self._ConvertProp('boolval')
252 self.assertEqual(fdt.TYPE_BOOL, prop.type)
253 self.assertEqual(True, prop.value)
255 prop = self._ConvertProp('intval')
256 self.assertEqual(fdt.TYPE_INT, prop.type)
257 self.assertEqual(1, fdt32_to_cpu(prop.value))
259 prop = self._ConvertProp('intarray')
260 self.assertEqual(fdt.TYPE_INT, prop.type)
261 val = [fdt32_to_cpu(val) for val in prop.value]
262 self.assertEqual([2, 3, 4], val)
264 prop = self._ConvertProp('byteval')
265 self.assertEqual(fdt.TYPE_BYTE, prop.type)
266 self.assertEqual(5, ord(prop.value))
268 prop = self._ConvertProp('longbytearray')
269 self.assertEqual(fdt.TYPE_BYTE, prop.type)
270 val = [ord(val) for val in prop.value]
271 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
273 prop = self._ConvertProp('stringval')
274 self.assertEqual(fdt.TYPE_STRING, prop.type)
275 self.assertEqual('message', prop.value)
277 prop = self._ConvertProp('stringarray')
278 self.assertEqual(fdt.TYPE_STRING, prop.type)
279 self.assertEqual(['multi-word', 'message'], prop.value)
281 prop = self._ConvertProp('notstring')
282 self.assertEqual(fdt.TYPE_BYTE, prop.type)
283 val = [ord(val) for val in prop.value]
284 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
286 def testGetEmpty(self):
287 """Tests the GetEmpty() function for the various supported types"""
288 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
289 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
290 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(fdt.TYPE_INT))
291 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
293 def testGetOffset(self):
294 """Test we can get the offset of a property"""
295 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
296 self.assertEqual(prop.value, value)
299 """Test widening of values"""
300 node2 = self.dtb.GetNode('/spl-test2')
301 node3 = self.dtb.GetNode('/spl-test3')
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 prop3 = node3.props['longbytearray']
321 self.assertFalse(isinstance(prop2.value, list))
322 self.assertEqual(4, len(prop2.value))
323 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
325 self.assertTrue(isinstance(prop2.value, list))
326 self.assertEqual(9, len(prop2.value))
327 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
328 '\0', '\0', '\0', '\0'], prop2.value)
330 self.assertTrue(isinstance(prop3.value, list))
331 self.assertEqual(9, len(prop3.value))
332 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
333 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
335 # Similarly for a string array
336 prop = self.node.props['stringval']
337 prop2 = node2.props['stringarray']
338 self.assertFalse(isinstance(prop.value, list))
339 self.assertEqual(7, len(prop.value))
341 self.assertTrue(isinstance(prop.value, list))
342 self.assertEqual(3, len(prop.value))
344 # Enlarging an existing array
345 prop = self.node.props['stringarray']
346 prop2 = node2.props['stringarray']
347 self.assertTrue(isinstance(prop.value, list))
348 self.assertEqual(2, len(prop.value))
350 self.assertTrue(isinstance(prop.value, list))
351 self.assertEqual(3, len(prop.value))
354 """Test adding properties"""
356 # This function should automatically expand the device tree
357 self.node.AddZeroProp('one')
358 self.node.AddZeroProp('two')
359 self.node.AddZeroProp('three')
360 self.dtb.Sync(auto_resize=True)
362 # Updating existing properties should be OK, since the device-tree size
365 self.node.SetInt('one', 1)
366 self.node.SetInt('two', 2)
367 self.node.SetInt('three', 3)
368 self.dtb.Sync(auto_resize=False)
370 # This should fail since it would need to increase the device-tree size
371 self.node.AddZeroProp('four')
372 with self.assertRaises(libfdt.FdtException) as e:
373 self.dtb.Sync(auto_resize=False)
374 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
375 self.dtb.Sync(auto_resize=True)
377 def testAddNode(self):
379 self.node.AddSubnode('subnode')
380 with self.assertRaises(libfdt.FdtException) as e:
381 self.dtb.Sync(auto_resize=False)
382 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
384 self.dtb.Sync(auto_resize=True)
385 offset = self.fdt.path_offset('/spl-test/subnode')
386 self.assertTrue(offset > 0)
388 def testAddMore(self):
389 """Test various other methods for adding and setting properties"""
390 self.node.AddZeroProp('one')
391 self.dtb.Sync(auto_resize=True)
392 data = self.fdt.getprop(self.node.Offset(), 'one')
393 self.assertEqual(0, fdt32_to_cpu(data))
395 self.node.SetInt('one', 1)
396 self.dtb.Sync(auto_resize=False)
397 data = self.fdt.getprop(self.node.Offset(), 'one')
398 self.assertEqual(1, fdt32_to_cpu(data))
400 val = '123' + chr(0) + '456'
401 self.node.AddString('string', val)
402 self.dtb.Sync(auto_resize=True)
403 data = self.fdt.getprop(self.node.Offset(), 'string')
404 self.assertEqual(tools.ToBytes(val) + b'\0', data)
407 self.node.SetString('string', val + 'x')
408 with self.assertRaises(libfdt.FdtException) as e:
409 self.dtb.Sync(auto_resize=False)
410 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
411 self.node.SetString('string', val[:-1])
413 prop = self.node.props['string']
414 prop.SetData(tools.ToBytes(val))
415 self.dtb.Sync(auto_resize=False)
416 data = self.fdt.getprop(self.node.Offset(), 'string')
417 self.assertEqual(tools.ToBytes(val), data)
419 self.node.AddEmptyProp('empty', 5)
420 self.dtb.Sync(auto_resize=True)
421 prop = self.node.props['empty']
422 prop.SetData(tools.ToBytes(val))
423 self.dtb.Sync(auto_resize=False)
424 data = self.fdt.getprop(self.node.Offset(), 'empty')
425 self.assertEqual(tools.ToBytes(val), data)
427 self.node.SetData('empty', b'123')
428 self.assertEqual(b'123', prop.bytes)
430 # Trying adding a lot of data at once
431 self.node.AddData('data', tools.GetBytes(65, 20000))
432 self.dtb.Sync(auto_resize=True)
434 def testFromData(self):
435 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
436 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
438 self.node.AddEmptyProp('empty', 5)
439 self.dtb.Sync(auto_resize=True)
440 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
442 def testMissingSetInt(self):
443 """Test handling of a missing property with SetInt"""
444 with self.assertRaises(ValueError) as e:
445 self.node.SetInt('one', 1)
446 self.assertIn("node '/spl-test': Missing property 'one'",
449 def testMissingSetData(self):
450 """Test handling of a missing property with SetData"""
451 with self.assertRaises(ValueError) as e:
452 self.node.SetData('one', b'data')
453 self.assertIn("node '/spl-test': Missing property 'one'",
456 def testMissingSetString(self):
457 """Test handling of a missing property with SetString"""
458 with self.assertRaises(ValueError) as e:
459 self.node.SetString('one', 1)
460 self.assertIn("node '/spl-test': Missing property 'one'",
463 def testGetFilename(self):
464 """Test the dtb filename can be provided"""
465 self.assertEqual(tools.GetOutputFilename('source.dtb'),
466 self.dtb.GetFilename())
469 class TestFdtUtil(unittest.TestCase):
470 """Tests for the fdt_util module
472 This module will likely be mostly replaced at some point, once upstream
473 libfdt has better Python support. For now, this provides tests for current
478 tools.PrepareOutputDir(None)
481 def tearDownClass(cls):
482 tools.FinaliseOutputDir()
485 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
486 self.node = self.dtb.GetNode('/spl-test')
488 def testGetInt(self):
489 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
490 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
492 with self.assertRaises(ValueError) as e:
493 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
494 self.assertIn("property 'intarray' has list value: expecting a single "
495 'integer', str(e.exception))
497 def testGetString(self):
498 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
499 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
502 with self.assertRaises(ValueError) as e:
503 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
504 self.assertIn("property 'stringarray' has list value: expecting a "
505 'single string', str(e.exception))
507 def testGetBool(self):
508 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
509 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
510 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
511 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
513 def testGetByte(self):
514 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
515 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
517 with self.assertRaises(ValueError) as e:
518 fdt_util.GetByte(self.node, 'longbytearray')
519 self.assertIn("property 'longbytearray' has list value: expecting a "
520 'single byte', str(e.exception))
522 with self.assertRaises(ValueError) as e:
523 fdt_util.GetByte(self.node, 'intval')
524 self.assertIn("property 'intval' has length 4, expecting 1",
527 def testGetPhandleList(self):
528 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
529 node = dtb.GetNode('/phandle-source2')
530 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
531 node = dtb.GetNode('/phandle-source')
532 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
533 fdt_util.GetPhandleList(node, 'clocks'))
534 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
536 def testGetDataType(self):
537 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
538 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
540 with self.assertRaises(ValueError) as e:
541 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
543 def testFdtCellsToCpu(self):
544 val = self.node.props['intarray'].value
545 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
546 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
548 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
549 node1 = dtb2.GetNode('/test1')
550 val = node1.props['reg'].value
551 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
553 node2 = dtb2.GetNode('/test2')
554 val = node2.props['reg'].value
555 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
556 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
558 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
560 def testEnsureCompiled(self):
561 """Test a degenerate case of this function (file already compiled)"""
562 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
563 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
565 def testEnsureCompiledTmpdir(self):
566 """Test providing a temporary directory"""
568 old_outdir = tools.outdir
570 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
571 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts',
573 self.assertEqual(tmpdir, os.path.dirname(dtb))
574 shutil.rmtree(tmpdir)
576 tools.outdir= old_outdir
579 def RunTestCoverage():
580 """Run the tests and check that we get 100% coverage"""
581 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
582 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
586 """Run all the test we have for the fdt model
589 args: List of positional args provided to fdt. This can hold a test
590 name to execute (as in 'fdt -t testFdt', for example)
592 result = unittest.TestResult()
593 sys.argv = [sys.argv[0]]
594 test_name = args and args[0] or None
595 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
598 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
599 except AttributeError:
602 suite = unittest.TestLoader().loadTestsFromTestCase(module)
606 for _, err in result.errors:
608 for _, err in result.failures:
611 if __name__ != '__main__':
614 parser = OptionParser()
615 parser.add_option('-B', '--build-dir', type='string', default='b',
616 help='Directory containing the build output')
617 parser.add_option('-P', '--processes', type=int,
618 help='set number of processes to use for running tests')
619 parser.add_option('-t', '--test', action='store_true', dest='test',
620 default=False, help='run tests')
621 parser.add_option('-T', '--test-coverage', action='store_true',
622 default=False, help='run tests and check for 100% coverage')
623 (options, args) = parser.parse_args()
625 # Run our meagre tests
628 elif options.test_coverage: