2 # SPDX-License-Identifier: GPL-2.0+
5 Tests for the Fdt module
6 Copyright (c) 2018 Google, Inc
7 Written by Simon Glass <sjg@chromium.org>
10 from argparse import ArgumentParser
17 # Bring in the patman libraries
18 our_path = os.path.dirname(os.path.realpath(__file__))
19 sys.path.insert(1, os.path.join(our_path, '..'))
21 # Bring in the libfdt module
22 sys.path.insert(2, 'scripts/dtc/pylibfdt')
23 sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
24 sys.path.insert(2, os.path.join(our_path,
25 '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
27 #pylint: disable=wrong-import-position
29 from dtoc import fdt_util
30 from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
31 from dtoc.fdt import Type, BytesToValue
33 from u_boot_pylib import test_util
34 from u_boot_pylib import tools
35 from u_boot_pylib import tout
37 #pylint: disable=protected-access
39 def _get_property_value(dtb, node, prop_name):
40 """Low-level function to get the property value based on its offset
42 This looks directly in the device tree at the property's offset to find
43 its value. It is useful as a check that the property is in the correct
48 prop_name: Property name to find
53 Value of property as a string (found using property offset)
55 prop = node.props[prop_name]
57 # Add 12, which is sizeof(struct fdt_property), to get to start of data
58 offset = prop.GetOffset() + 12
59 data = dtb.GetContents()[offset:offset + len(prop.value)]
60 return prop, [chr(x) for x in data]
62 def find_dtb_file(dts_fname):
63 """Locate a test file in the test/ directory
66 dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
69 str: Path to the test filename
71 return os.path.join('tools/dtoc/test', dts_fname)
74 class TestFdt(unittest.TestCase):
75 """Tests for the Fdt module
77 This includes unit tests for some functions and functional tests for the fdt
82 tools.prepare_output_dir(None)
85 def tearDownClass(cls):
86 tools.finalise_output_dir()
89 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
92 """Test that we can open an Fdt"""
94 root = self.dtb.GetRoot()
95 self.assertTrue(isinstance(root, fdt.Node))
97 def test_get_node(self):
98 """Test the GetNode() method"""
99 node = self.dtb.GetNode('/spl-test')
100 self.assertTrue(isinstance(node, fdt.Node))
102 node = self.dtb.GetNode('/i2c@0/pmic@9')
103 self.assertTrue(isinstance(node, fdt.Node))
104 self.assertEqual('pmic@9', node.name)
105 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
107 node = self.dtb.GetNode('/')
108 self.assertTrue(isinstance(node, fdt.Node))
109 self.assertEqual(0, node.Offset())
111 def test_flush(self):
112 """Check that we can flush the device tree out to its file"""
113 fname = self.dtb._fname
114 with open(fname, 'rb') as inf:
117 with self.assertRaises(IOError):
118 with open(fname, 'rb'):
121 with open(fname, 'rb') as inf:
125 """Test that packing a device tree works"""
128 def test_get_fdt_raw(self):
129 """Tetst that we can access the raw device-tree data"""
130 self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
132 def test_get_props(self):
133 """Tests obtaining a list of properties"""
134 node = self.dtb.GetNode('/spl-test')
135 props = self.dtb.GetProps(node)
136 self.assertEqual(['boolval', 'bootph-all', 'bytearray', 'byteval',
137 'compatible', 'int64val', 'intarray', 'intval',
138 'longbytearray', 'maybe-empty-int', 'notstring',
139 'stringarray', 'stringval', ],
140 sorted(props.keys()))
142 def test_check_error(self):
143 """Tests the ChecKError() function"""
144 with self.assertRaises(ValueError) as exc:
145 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
146 self.assertIn('FDT_ERR_NOTFOUND: hello', str(exc.exception))
148 def test_get_fdt(self):
149 """Test getting an Fdt object from a node"""
150 node = self.dtb.GetNode('/spl-test')
151 self.assertEqual(self.dtb, node.GetFdt())
153 def test_bytes_to_value(self):
154 """Test converting a string list into Python"""
155 self.assertEqual(BytesToValue(b'this\0is\0'),
156 (Type.STRING, ['this', 'is']))
158 class TestNode(unittest.TestCase):
159 """Test operation of the Node class"""
163 tools.prepare_output_dir(None)
166 def tearDownClass(cls):
167 tools.finalise_output_dir()
170 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
171 self.node = self.dtb.GetNode('/spl-test')
172 self.fdt = self.dtb.GetFdtObj()
174 def test_offset(self):
175 """Tests that we can obtain the offset of a node"""
176 self.assertTrue(self.node.Offset() > 0)
178 def test_delete(self):
179 """Tests that we can delete a property"""
180 node2 = self.dtb.GetNode('/spl-test2')
181 offset1 = node2.Offset()
182 self.node.DeleteProp('intval')
183 offset2 = node2.Offset()
184 self.assertTrue(offset2 < offset1)
185 self.node.DeleteProp('intarray')
186 offset3 = node2.Offset()
187 self.assertTrue(offset3 < offset2)
188 with self.assertRaises(libfdt.FdtException):
189 self.node.DeleteProp('missing')
191 def test_delete_get_offset(self):
192 """Test that property offset update when properties are deleted"""
193 self.node.DeleteProp('intval')
194 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
195 self.assertEqual(prop.value, value)
197 def test_find_node(self):
198 """Tests that we can find a node using the FindNode() functoin"""
199 node = self.dtb.GetRoot().FindNode('i2c@0')
200 self.assertEqual('i2c@0', node.name)
201 subnode = node.FindNode('pmic@9')
202 self.assertEqual('pmic@9', subnode.name)
203 self.assertEqual(None, node.FindNode('missing'))
205 def test_refresh_missing_node(self):
206 """Test refreshing offsets when an extra node is present in dtb"""
207 # Delete it from our tables, not the device tree
208 del self.dtb._root.subnodes[-1]
209 with self.assertRaises(ValueError) as exc:
211 self.assertIn('Internal error, offset', str(exc.exception))
213 def test_refresh_extra_node(self):
214 """Test refreshing offsets when an expected node is missing"""
215 # Delete it from the device tre, not our tables
216 self.fdt.del_node(self.node.Offset())
217 with self.assertRaises(ValueError) as exc:
219 self.assertIn('Internal error, node name mismatch '
220 'spl-test != spl-test2', str(exc.exception))
222 def test_refresh_missing_prop(self):
223 """Test refreshing offsets when an extra property is present in dtb"""
224 # Delete it from our tables, not the device tree
225 del self.node.props['notstring']
226 with self.assertRaises(ValueError) as exc:
228 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
231 def test_lookup_phandle(self):
232 """Test looking up a single phandle"""
233 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
234 node = dtb.GetNode('/phandle-source2')
235 prop = node.props['clocks']
236 target = dtb.GetNode('/phandle-target')
237 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
239 def test_add_node_space(self):
240 """Test adding a single node when out of space"""
242 self.node.AddSubnode('subnode')
243 with self.assertRaises(libfdt.FdtException) as exc:
244 self.dtb.Sync(auto_resize=False)
245 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
247 self.dtb.Sync(auto_resize=True)
248 offset = self.fdt.path_offset('/spl-test/subnode')
249 self.assertTrue(offset > 0)
251 def test_add_nodes(self):
252 """Test adding various subnode and properies"""
253 node = self.dtb.GetNode('/i2c@0')
255 # Add one more node next to the pmic one
256 sn1 = node.AddSubnode('node-one')
257 sn1.AddInt('integer-a', 12)
258 sn1.AddInt('integer-b', 23)
260 # Sync so that everything is clean
261 self.dtb.Sync(auto_resize=True)
263 # Add two subnodes next to pmic and node-one
264 sn2 = node.AddSubnode('node-two')
265 sn2.AddInt('integer-2a', 34)
266 sn2.AddInt('integer-2b', 45)
268 sn3 = node.AddSubnode('node-three')
269 sn3.AddInt('integer-3', 123)
271 # Add a property to the node after i2c@0 to check that this is not
272 # disturbed by adding a subnode to i2c@0
273 orig_node = self.dtb.GetNode('/orig-node')
274 orig_node.AddInt('integer-4', 456)
276 # Add a property to the pmic node to check that pmic properties are not
278 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
279 pmic.AddInt('integer-5', 567)
281 self.dtb.Sync(auto_resize=True)
283 def test_add_one_node(self):
284 """Testing deleting and adding a subnode before syncing"""
285 subnode = self.node.AddSubnode('subnode')
286 self.node.AddSubnode('subnode2')
287 self.dtb.Sync(auto_resize=True)
289 # Delete a node and add a new one
291 self.node.AddSubnode('subnode3')
294 def test_refresh_name_mismatch(self):
295 """Test name mismatch when syncing nodes and properties"""
296 self.node.AddInt('integer-a', 12)
298 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
299 self.node._offset = wrong_offset
300 with self.assertRaises(ValueError) as exc:
302 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
305 with self.assertRaises(ValueError) as exc:
306 self.node.Refresh(wrong_offset)
307 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
310 def test_copy_node(self):
311 """Test copy_node() function"""
312 def do_copy_checks(dtb, dst, second1_ph_val, expect_none):
314 ['/dest/base', '/dest/first@0', '/dest/existing'],
315 [n.path for n in dst.subnodes])
317 chk = dtb.GetNode('/dest/base')
320 {'compatible', 'bootph-all', '#address-cells', '#size-cells'},
323 # Check the first property
324 prop = chk.props['bootph-all']
325 self.assertEqual('bootph-all', prop.name)
326 self.assertEqual(True, prop.value)
327 self.assertEqual(chk.path, prop._node.path)
329 # Check the second property
330 prop2 = chk.props['compatible']
331 self.assertEqual('compatible', prop2.name)
332 self.assertEqual('sandbox,i2c', prop2.value)
333 self.assertEqual(chk.path, prop2._node.path)
335 base = chk.FindNode('base')
338 first = dtb.GetNode('/dest/base/first@0')
339 self.assertTrue(first)
340 over = dtb.GetNode('/dest/base/over')
341 self.assertTrue(over)
343 # Make sure that the phandle for 'over' is copied
344 self.assertIn('phandle', over.props.keys())
346 second = dtb.GetNode('/dest/base/second')
347 self.assertTrue(second)
348 self.assertEqual([over.name, first.name, second.name],
349 [n.name for n in chk.subnodes])
350 self.assertEqual(chk, over.parent)
352 {'bootph-all', 'compatible', 'reg', 'low-power', 'phandle'},
356 self.assertIsNone(prop._offset)
357 self.assertIsNone(prop2._offset)
358 self.assertIsNone(over._offset)
360 self.assertTrue(prop._offset)
361 self.assertTrue(prop2._offset)
362 self.assertTrue(over._offset)
364 # Now check ordering of the subnodes
366 ['second1', 'second2', 'second3', 'second4'],
367 [n.name for n in second.subnodes])
369 # Check the 'second_1_bad' phandle is not copied over
370 second1 = second.FindNode('second1')
371 self.assertTrue(second1)
372 sph = second1.props.get('phandle')
374 self.assertEqual(second1_ph_val, sph.bytes)
377 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
378 tmpl = dtb.GetNode('/base')
379 dst = dtb.GetNode('/dest')
380 second1_ph_val = (dtb.GetNode('/dest/base/second/second1').
381 props['phandle'].bytes)
384 do_copy_checks(dtb, dst, second1_ph_val, expect_none=True)
386 dtb.Sync(auto_resize=True)
388 # Now check the resulting FDT. It should have duplicate phandles since
389 # 'over' has been copied to 'dest/base/over' but still exists in its old
391 new_dtb = fdt.Fdt.FromData(dtb.GetContents())
392 with self.assertRaises(ValueError) as exc:
395 'Duplicate phandle 1 in nodes /dest/base/over and /base/over',
398 # Remove the source nodes for the copy
399 new_dtb.GetNode('/base').Delete()
401 # Now it should scan OK
404 dst = new_dtb.GetNode('/dest')
405 do_copy_checks(new_dtb, dst, second1_ph_val, expect_none=False)
407 def test_copy_subnodes_from_phandles(self):
408 """Test copy_node() function"""
409 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
411 orig = dtb.GetNode('/')
412 node_list = fdt_util.GetPhandleList(orig, 'copy-list')
414 dst = dtb.GetNode('/dest')
415 dst.copy_subnodes_from_phandles(node_list)
417 pmic = dtb.GetNode('/dest/over')
418 self.assertTrue(pmic)
420 subn = dtb.GetNode('/dest/first@0')
421 self.assertTrue(subn)
422 self.assertEqual({'a-prop', 'b-prop', 'reg'}, subn.props.keys())
425 ['/dest/earlier', '/dest/later', '/dest/over', '/dest/first@0',
426 '/dest/second', '/dest/existing', '/dest/base'],
427 [n.path for n in dst.subnodes])
429 # Make sure that the phandle for 'over' is not copied
430 over = dst.FindNode('over')
431 tout.debug(f'keys: {over.props.keys()}')
432 self.assertNotIn('phandle', over.props.keys())
434 # Check the merged properties, first the base ones in '/dest'
435 expect = {'bootph-all', 'compatible', 'stringarray', 'longbytearray',
438 # Properties from 'base'
439 expect.update({'#address-cells', '#size-cells'})
441 # Properties from 'another'
442 expect.add('new-prop')
444 self.assertEqual(expect, set(dst.props.keys()))
447 class TestProp(unittest.TestCase):
448 """Test operation of the Prop class"""
452 tools.prepare_output_dir(None)
455 def tearDownClass(cls):
456 tools.finalise_output_dir()
459 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
460 self.node = self.dtb.GetNode('/spl-test')
461 self.fdt = self.dtb.GetFdtObj()
463 def test_missing_node(self):
464 """Test GetNode() when the node is missing"""
465 self.assertEqual(None, self.dtb.GetNode('missing'))
467 def test_phandle(self):
468 """Test GetNode() on a phandle"""
469 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
470 node = dtb.GetNode('/phandle-source2')
471 prop = node.props['clocks']
472 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
474 def _convert_prop(self, prop_name):
475 """Helper function to look up a property in self.node and return it
478 str: Property name to find
481 fdt.Prop: object for this property
483 prop = self.fdt.getprop(self.node.Offset(), prop_name)
484 return fdt.Prop(self.node, -1, prop_name, prop)
486 def test_make_prop(self):
487 """Test we can convert all the the types that are supported"""
488 prop = self._convert_prop('boolval')
489 self.assertEqual(Type.BOOL, prop.type)
490 self.assertEqual(True, prop.value)
492 prop = self._convert_prop('intval')
493 self.assertEqual(Type.INT, prop.type)
494 self.assertEqual(1, fdt32_to_cpu(prop.value))
496 prop = self._convert_prop('int64val')
497 self.assertEqual(Type.INT, prop.type)
498 self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
500 prop = self._convert_prop('intarray')
501 self.assertEqual(Type.INT, prop.type)
502 val = [fdt32_to_cpu(val) for val in prop.value]
503 self.assertEqual([2, 3, 4], val)
505 prop = self._convert_prop('byteval')
506 self.assertEqual(Type.BYTE, prop.type)
507 self.assertEqual(5, ord(prop.value))
509 prop = self._convert_prop('longbytearray')
510 self.assertEqual(Type.BYTE, prop.type)
511 val = [ord(val) for val in prop.value]
512 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
514 prop = self._convert_prop('stringval')
515 self.assertEqual(Type.STRING, prop.type)
516 self.assertEqual('message', prop.value)
518 prop = self._convert_prop('stringarray')
519 self.assertEqual(Type.STRING, prop.type)
520 self.assertEqual(['multi-word', 'message'], prop.value)
522 prop = self._convert_prop('notstring')
523 self.assertEqual(Type.BYTE, prop.type)
524 val = [ord(val) for val in prop.value]
525 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
527 def test_get_empty(self):
528 """Tests the GetEmpty() function for the various supported types"""
529 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
530 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
531 self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
532 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
534 def test_get_offset(self):
535 """Test we can get the offset of a property"""
536 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
537 self.assertEqual(prop.value, value)
539 def test_widen(self):
540 """Test widening of values"""
541 node2 = self.dtb.GetNode('/spl-test2')
542 node3 = self.dtb.GetNode('/spl-test3')
543 prop = self.node.props['intval']
546 prop2 = node2.props['intval']
548 self.assertEqual(Type.INT, prop.type)
549 self.assertEqual(1, fdt32_to_cpu(prop.value))
551 # Convert single value to array
552 prop2 = self.node.props['intarray']
554 self.assertEqual(Type.INT, prop.type)
555 self.assertTrue(isinstance(prop.value, list))
557 # A 4-byte array looks like a single integer. When widened by a longer
558 # byte array, it should turn into an array.
559 prop = self.node.props['longbytearray']
560 prop2 = node2.props['longbytearray']
561 prop3 = node3.props['longbytearray']
562 self.assertFalse(isinstance(prop2.value, list))
563 self.assertEqual(4, len(prop2.value))
564 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
566 self.assertTrue(isinstance(prop2.value, list))
567 self.assertEqual(9, len(prop2.value))
568 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
569 '\0', '\0', '\0', '\0'], prop2.value)
571 self.assertTrue(isinstance(prop3.value, list))
572 self.assertEqual(9, len(prop3.value))
573 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
574 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
576 def test_widen_more(self):
577 """More tests of widening values"""
578 node2 = self.dtb.GetNode('/spl-test2')
579 node3 = self.dtb.GetNode('/spl-test3')
580 prop = self.node.props['intval']
582 # Test widening a single string into a string array
583 prop = self.node.props['stringval']
584 prop2 = node2.props['stringarray']
585 self.assertFalse(isinstance(prop.value, list))
586 self.assertEqual(7, len(prop.value))
588 self.assertTrue(isinstance(prop.value, list))
589 self.assertEqual(3, len(prop.value))
591 # Enlarging an existing array
592 prop = self.node.props['stringarray']
593 prop2 = node2.props['stringarray']
594 self.assertTrue(isinstance(prop.value, list))
595 self.assertEqual(2, len(prop.value))
597 self.assertTrue(isinstance(prop.value, list))
598 self.assertEqual(3, len(prop.value))
600 # Widen an array of ints with an int (should do nothing)
601 prop = self.node.props['intarray']
602 prop2 = node2.props['intval']
603 self.assertEqual(Type.INT, prop.type)
604 self.assertEqual(3, len(prop.value))
606 self.assertEqual(Type.INT, prop.type)
607 self.assertEqual(3, len(prop.value))
609 # Widen an empty bool to an int
610 prop = self.node.props['maybe-empty-int']
611 prop3 = node3.props['maybe-empty-int']
612 self.assertEqual(Type.BOOL, prop.type)
613 self.assertEqual(True, prop.value)
614 self.assertEqual(Type.INT, prop3.type)
615 self.assertFalse(isinstance(prop.value, list))
616 self.assertEqual(4, len(prop3.value))
618 self.assertEqual(Type.INT, prop.type)
619 self.assertTrue(isinstance(prop.value, list))
620 self.assertEqual(1, len(prop.value))
623 """Test adding properties"""
625 # This function should automatically expand the device tree
626 self.node.AddZeroProp('one')
627 self.node.AddZeroProp('two')
628 self.node.AddZeroProp('three')
629 self.dtb.Sync(auto_resize=True)
631 # Updating existing properties should be OK, since the device-tree size
634 self.node.SetInt('one', 1)
635 self.node.SetInt('two', 2)
636 self.node.SetInt('three', 3)
637 self.dtb.Sync(auto_resize=False)
639 # This should fail since it would need to increase the device-tree size
640 self.node.AddZeroProp('four')
641 with self.assertRaises(libfdt.FdtException) as exc:
642 self.dtb.Sync(auto_resize=False)
643 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
644 self.dtb.Sync(auto_resize=True)
646 def test_add_more(self):
647 """Test various other methods for adding and setting properties"""
648 self.node.AddZeroProp('one')
649 self.dtb.Sync(auto_resize=True)
650 data = self.fdt.getprop(self.node.Offset(), 'one')
651 self.assertEqual(0, fdt32_to_cpu(data))
653 self.node.SetInt('one', 1)
654 self.dtb.Sync(auto_resize=False)
655 data = self.fdt.getprop(self.node.Offset(), 'one')
656 self.assertEqual(1, fdt32_to_cpu(data))
659 self.node.AddInt('integer', val)
660 self.dtb.Sync(auto_resize=True)
661 data = self.fdt.getprop(self.node.Offset(), 'integer')
662 self.assertEqual(val, fdt32_to_cpu(data))
664 val = '123' + chr(0) + '456'
665 self.node.AddString('string', val)
666 self.dtb.Sync(auto_resize=True)
667 data = self.fdt.getprop(self.node.Offset(), 'string')
668 self.assertEqual(tools.to_bytes(val) + b'\0', data)
671 self.node.SetString('string', val + 'x')
672 with self.assertRaises(libfdt.FdtException) as exc:
673 self.dtb.Sync(auto_resize=False)
674 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
675 self.node.SetString('string', val[:-1])
677 prop = self.node.props['string']
678 prop.SetData(tools.to_bytes(val))
679 self.dtb.Sync(auto_resize=False)
680 data = self.fdt.getprop(self.node.Offset(), 'string')
681 self.assertEqual(tools.to_bytes(val), data)
683 self.node.AddEmptyProp('empty', 5)
684 self.dtb.Sync(auto_resize=True)
685 prop = self.node.props['empty']
686 prop.SetData(tools.to_bytes(val))
687 self.dtb.Sync(auto_resize=False)
688 data = self.fdt.getprop(self.node.Offset(), 'empty')
689 self.assertEqual(tools.to_bytes(val), data)
691 self.node.SetData('empty', b'123')
692 self.assertEqual(b'123', prop.bytes)
694 # Trying adding a lot of data at once
695 self.node.AddData('data', tools.get_bytes(65, 20000))
696 self.dtb.Sync(auto_resize=True)
698 def test_string_list(self):
699 """Test adding string-list property to a node"""
701 self.node.AddStringList('stringlist', val)
702 self.dtb.Sync(auto_resize=True)
703 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
704 self.assertEqual(b'123\x00456\0', data)
707 self.node.AddStringList('stringlist', val)
708 self.dtb.Sync(auto_resize=True)
709 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
710 self.assertEqual(b'', data)
712 def test_delete_node(self):
713 """Test deleting a node"""
714 old_offset = self.fdt.path_offset('/spl-test')
715 self.assertGreater(old_offset, 0)
718 new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
719 self.assertEqual(-libfdt.NOTFOUND, new_offset)
721 def test_from_data(self):
722 """Test creating an FDT from data"""
723 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
724 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
726 self.node.AddEmptyProp('empty', 5)
727 self.dtb.Sync(auto_resize=True)
728 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
730 def test_missing_set_int(self):
731 """Test handling of a missing property with SetInt"""
732 with self.assertRaises(ValueError) as exc:
733 self.node.SetInt('one', 1)
734 self.assertIn("node '/spl-test': Missing property 'one'",
737 def test_missing_set_data(self):
738 """Test handling of a missing property with SetData"""
739 with self.assertRaises(ValueError) as exc:
740 self.node.SetData('one', b'data')
741 self.assertIn("node '/spl-test': Missing property 'one'",
744 def test_missing_set_string(self):
745 """Test handling of a missing property with SetString"""
746 with self.assertRaises(ValueError) as exc:
747 self.node.SetString('one', 1)
748 self.assertIn("node '/spl-test': Missing property 'one'",
751 def test_get_filename(self):
752 """Test the dtb filename can be provided"""
753 self.assertEqual(tools.get_output_filename('source.dtb'),
754 self.dtb.GetFilename())
757 class TestFdtUtil(unittest.TestCase):
758 """Tests for the fdt_util module
760 This module will likely be mostly replaced at some point, once upstream
761 libfdt has better Python support. For now, this provides tests for current
766 tools.prepare_output_dir(None)
769 def tearDownClass(cls):
770 tools.finalise_output_dir()
773 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
774 self.node = self.dtb.GetNode('/spl-test')
776 def test_get_int(self):
777 """Test getting an int from a node"""
778 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
779 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
781 with self.assertRaises(ValueError) as exc:
782 fdt_util.GetInt(self.node, 'intarray')
783 self.assertIn("property 'intarray' has list value: expecting a single "
784 'integer', str(exc.exception))
786 def test_get_int64(self):
787 """Test getting a 64-bit int from a node"""
788 self.assertEqual(0x123456789abcdef0,
789 fdt_util.GetInt64(self.node, 'int64val'))
790 self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
792 with self.assertRaises(ValueError) as exc:
793 fdt_util.GetInt64(self.node, 'intarray')
795 "property 'intarray' should be a list with 2 items for 64-bit values",
798 def test_get_string(self):
799 """Test getting a string from a node"""
800 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
801 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
803 self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
805 with self.assertRaises(ValueError) as exc:
806 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
807 self.assertIn("property 'stringarray' has list value: expecting a "
808 'single string', str(exc.exception))
810 def test_get_string_list(self):
811 """Test getting a string list from a node"""
812 self.assertEqual(['message'],
813 fdt_util.GetStringList(self.node, 'stringval'))
815 ['multi-word', 'message'],
816 fdt_util.GetStringList(self.node, 'stringarray'))
817 self.assertEqual(['test'],
818 fdt_util.GetStringList(self.node, 'missing', ['test']))
819 self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
821 def test_get_args(self):
822 """Test getting arguments from a node"""
823 node = self.dtb.GetNode('/orig-node')
824 self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
826 ['multi-word', 'message'],
827 fdt_util.GetArgs(self.node, 'stringarray'))
828 self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
829 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
830 fdt_util.GetArgs(node, 'args'))
831 self.assertEqual(['a space', 'there'],
832 fdt_util.GetArgs(node, 'args2'))
833 self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
834 fdt_util.GetArgs(node, 'args3'))
835 with self.assertRaises(ValueError) as exc:
836 fdt_util.GetArgs(self.node, 'missing')
838 "Node '/spl-test': Expected property 'missing'",
841 def test_get_bool(self):
842 """Test getting a bool from a node"""
843 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
844 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
845 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
846 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
848 def test_get_byte(self):
849 """Test getting a byte from a node"""
850 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
851 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
853 with self.assertRaises(ValueError) as exc:
854 fdt_util.GetByte(self.node, 'longbytearray')
855 self.assertIn("property 'longbytearray' has list value: expecting a "
856 'single byte', str(exc.exception))
858 with self.assertRaises(ValueError) as exc:
859 fdt_util.GetByte(self.node, 'intval')
860 self.assertIn("property 'intval' has length 4, expecting 1",
863 def test_get_bytes(self):
864 """Test getting multiple bytes from a node"""
865 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
866 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
868 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
870 with self.assertRaises(ValueError) as exc:
871 fdt_util.GetBytes(self.node, 'longbytearray', 7)
873 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
877 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
879 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
881 def test_get_phandle_list(self):
882 """Test getting a list of phandles from a node"""
883 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
884 node = dtb.GetNode('/phandle-source2')
885 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
886 node = dtb.GetNode('/phandle-source')
887 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
888 fdt_util.GetPhandleList(node, 'clocks'))
889 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
891 def test_get_data_type(self):
892 """Test getting a value of a particular type from a node"""
893 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
894 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
896 with self.assertRaises(ValueError):
897 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
899 def test_fdt_cells_to_cpu(self):
900 """Test getting cells with the correct endianness"""
901 val = self.node.props['intarray'].value
902 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
903 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
905 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
906 node1 = dtb2.GetNode('/test1')
907 val = node1.props['reg'].value
908 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
910 node2 = dtb2.GetNode('/test2')
911 val = node2.props['reg'].value
912 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
913 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
915 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
917 def test_ensure_compiled(self):
918 """Test a degenerate case of this function (file already compiled)"""
919 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
920 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
922 def test_ensure_compiled_tmpdir(self):
923 """Test providing a temporary directory"""
924 old_outdir = tools.outdir
927 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
928 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
930 self.assertEqual(tmpdir, os.path.dirname(dtb))
931 shutil.rmtree(tmpdir)
933 tools.outdir = old_outdir
935 def test_get_phandle_name_offset(self):
936 val = fdt_util.GetPhandleNameOffset(self.node, 'missing')
937 self.assertIsNone(val)
939 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
940 node = dtb.GetNode('/phandle-source')
941 node, name, offset = fdt_util.GetPhandleNameOffset(node,
942 'phandle-name-offset')
943 self.assertEqual('phandle3-target', node.name)
944 self.assertEqual('fred', name)
945 self.assertEqual(123, offset)
947 def run_test_coverage(build_dir):
948 """Run the tests and check that we get 100% coverage
951 build_dir (str): Directory containing the build output
953 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
954 ['tools/patman/*.py', 'tools/u_boot_pylib/*', '*test_fdt.py'],
958 def run_tests(names, processes):
959 """Run all the test we have for the fdt model
962 names (list of str): List of test names provided. Only the first is used
963 processes (int): Number of processes to use (None means as many as there
964 are CPUs on the system. This must be set to 1 when running under
965 the python3-coverage tool
968 int: Return code, 0 on success
970 test_name = names[0] if names else None
971 result = test_util.run_test_suites(
972 'test_fdt', False, False, False, processes, test_name, None,
973 [TestFdt, TestNode, TestProp, TestFdtUtil])
975 return (0 if result.wasSuccessful() else 1)
979 """Main program for this tool"""
980 parser = ArgumentParser()
981 parser.add_argument('-B', '--build-dir', type=str, default='b',
982 help='Directory containing the build output')
983 parser.add_argument('-P', '--processes', type=int,
984 help='set number of processes to use for running tests')
985 parser.add_argument('-t', '--test', action='store_true', dest='test',
986 default=False, help='run tests')
987 parser.add_argument('-T', '--test-coverage', action='store_true',
989 help='run tests and check for 100% coverage')
990 parser.add_argument('name', nargs='*')
991 args = parser.parse_args()
993 # Run our meagre tests
995 ret_code = run_tests(args.name, args.processes)
997 if args.test_coverage:
998 run_test_coverage(args.build_dir)
1001 if __name__ == '__main__':