Merge tag 'u-boot-stm32-20190712' of https://gitlab.denx.de/u-boot/custodians/u-boot-stm
[oweals/u-boot.git] / tools / dtoc / test_fdt.py
1 #!/usr/bin/python
2 # SPDX-License-Identifier: GPL-2.0+
3 # Copyright (c) 2018 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
5 #
6
7 from __future__ import print_function
8
9 from optparse import OptionParser
10 import glob
11 import os
12 import sys
13 import unittest
14
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))
19
20 import command
21 import fdt
22 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, BytesToValue
23 import fdt_util
24 from fdt_util import fdt32_to_cpu
25 import libfdt
26 import test_util
27 import tools
28
29 def _GetPropertyValue(dtb, node, prop_name):
30     """Low-level function to get the property value based on its offset
31
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
34     place.
35
36     Args:
37         node: Node to look in
38         prop_name: Property name to find
39
40     Returns:
41         Tuple:
42             Prop object found
43             Value of property as a string (found using property offset)
44     """
45     prop = node.props[prop_name]
46
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]
51
52
53 class TestFdt(unittest.TestCase):
54     """Tests for the Fdt module
55
56     This includes unit tests for some functions and functional tests for the fdt
57     module.
58     """
59     @classmethod
60     def setUpClass(cls):
61         tools.PrepareOutputDir(None)
62
63     @classmethod
64     def tearDownClass(cls):
65         tools.FinaliseOutputDir()
66
67     def setUp(self):
68         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
69
70     def testFdt(self):
71         """Test that we can open an Fdt"""
72         self.dtb.Scan()
73         root = self.dtb.GetRoot()
74         self.assertTrue(isinstance(root, fdt.Node))
75
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'))
84
85     def testFlush(self):
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:
89             data = fd.read()
90         os.remove(fname)
91         with self.assertRaises(IOError):
92             open(fname, 'rb')
93         self.dtb.Flush()
94         with open(fname, 'rb') as fd:
95             data = fd.read()
96
97     def testPack(self):
98         """Test that packing a device tree works"""
99         self.dtb.Pack()
100
101     def testGetFdt(self):
102         """Tetst that we can access the raw device-tree data"""
103         self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
104
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()))
113
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))
119
120     def testGetFdt(self):
121         node = self.dtb.GetNode('/spl-test')
122         self.assertEqual(self.dtb, node.GetFdt())
123
124     def testBytesToValue(self):
125         self.assertEqual(BytesToValue(b'this\0is\0'),
126                          (TYPE_STRING, ['this', 'is']))
127
128 class TestNode(unittest.TestCase):
129     """Test operation of the Node class"""
130
131     @classmethod
132     def setUpClass(cls):
133         tools.PrepareOutputDir(None)
134
135     @classmethod
136     def tearDownClass(cls):
137         tools.FinaliseOutputDir()
138
139     def setUp(self):
140         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
141         self.node = self.dtb.GetNode('/spl-test')
142
143     def testOffset(self):
144         """Tests that we can obtain the offset of a node"""
145         self.assertTrue(self.node.Offset() > 0)
146
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')
159
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)
165
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'))
173
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:
179             self.dtb.Refresh()
180         self.assertIn('Internal error, offset', str(e.exception))
181
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:
187             self.dtb.Refresh()
188         self.assertIn('Internal error, node name mismatch '
189                       'spl-test != spl-test2', str(e.exception))
190
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:
196             self.dtb.Refresh()
197         self.assertIn("Internal error, property 'notstring' missing, offset ",
198                       str(e.exception))
199
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)))
207
208
209 class TestProp(unittest.TestCase):
210     """Test operation of the Prop class"""
211
212     @classmethod
213     def setUpClass(cls):
214         tools.PrepareOutputDir(None)
215
216     @classmethod
217     def tearDownClass(cls):
218         tools.FinaliseOutputDir()
219
220     def setUp(self):
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()
224
225     def testMissingNode(self):
226         self.assertEqual(None, self.dtb.GetNode('missing'))
227
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)
233
234     def _ConvertProp(self, prop_name):
235         """Helper function to look up a property in self.node and return it
236
237         Args:
238             Property name to find
239
240         Return fdt.Prop object for this property
241         """
242         p = self.fdt.getprop(self.node.Offset(), prop_name)
243         return fdt.Prop(self.node, -1, prop_name, p)
244
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)
250
251         prop = self._ConvertProp('intval')
252         self.assertEqual(fdt.TYPE_INT, prop.type)
253         self.assertEqual(1, fdt32_to_cpu(prop.value))
254
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)
259
260         prop = self._ConvertProp('byteval')
261         self.assertEqual(fdt.TYPE_BYTE, prop.type)
262         self.assertEqual(5, ord(prop.value))
263
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)
268
269         prop = self._ConvertProp('stringval')
270         self.assertEqual(fdt.TYPE_STRING, prop.type)
271         self.assertEqual('message', prop.value)
272
273         prop = self._ConvertProp('stringarray')
274         self.assertEqual(fdt.TYPE_STRING, prop.type)
275         self.assertEqual(['multi-word', 'message'], prop.value)
276
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)
281
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))
288
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)
293
294     def testWiden(self):
295         """Test widening of values"""
296         node2 = self.dtb.GetNode('/spl-test2')
297         prop = self.node.props['intval']
298
299         # No action
300         prop2 = node2.props['intval']
301         prop.Widen(prop2)
302         self.assertEqual(fdt.TYPE_INT, prop.type)
303         self.assertEqual(1, fdt32_to_cpu(prop.value))
304
305         # Convert singla value to array
306         prop2 = self.node.props['intarray']
307         prop.Widen(prop2)
308         self.assertEqual(fdt.TYPE_INT, prop.type)
309         self.assertTrue(isinstance(prop.value, list))
310
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))
317         prop2.Widen(prop)
318         self.assertTrue(isinstance(prop2.value, list))
319         self.assertEqual(9, len(prop2.value))
320
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))
326         prop.Widen(prop2)
327         self.assertTrue(isinstance(prop.value, list))
328         self.assertEqual(3, len(prop.value))
329
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))
335         prop.Widen(prop2)
336         self.assertTrue(isinstance(prop.value, list))
337         self.assertEqual(3, len(prop.value))
338
339     def testAdd(self):
340         """Test adding properties"""
341         self.fdt.pack()
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)
347
348         # Updating existing properties should be OK, since the device-tree size
349         # does not change
350         self.fdt.pack()
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)
355
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)
362
363     def testAddNode(self):
364         self.fdt.pack()
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))
369
370         self.dtb.Sync(auto_resize=True)
371         offset = self.fdt.path_offset('/spl-test/subnode')
372         self.assertTrue(offset > 0)
373
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))
380
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))
385
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)
391
392         self.fdt.pack()
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])
398
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)
404
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)
412
413         self.node.SetData('empty', b'123')
414         self.assertEqual(b'123', prop.bytes)
415
416     def testFromData(self):
417         dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
418         self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
419
420         self.node.AddEmptyProp('empty', 5)
421         self.dtb.Sync(auto_resize=True)
422         self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
423
424
425 class TestFdtUtil(unittest.TestCase):
426     """Tests for the fdt_util module
427
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
430     functionality.
431     """
432     @classmethod
433     def setUpClass(cls):
434         tools.PrepareOutputDir(None)
435
436     @classmethod
437     def tearDownClass(cls):
438         tools.FinaliseOutputDir()
439
440     def setUp(self):
441         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
442         self.node = self.dtb.GetNode('/spl-test')
443
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))
447
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))
452
453     def testGetString(self):
454         self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
455         self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
456                                                     'test'))
457
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))
462
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))
468
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))
472
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))
477
478         with self.assertRaises(ValueError) as e:
479             fdt_util.GetByte(self.node, 'intval')
480         self.assertIn("property 'intval' has length 4, expecting 1",
481                       str(e.exception))
482
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'))
491
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',
495                                                          str))
496         with self.assertRaises(ValueError) as e:
497             self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
498                                                      bool))
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))
503
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))
508
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:],
513                                                                        2))
514         self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
515
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))
520
521
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)
526
527
528 def RunTests(args):
529     """Run all the test we have for the fdt model
530
531     Args:
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)
534     """
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):
539         if test_name:
540             try:
541                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
542             except AttributeError:
543                 continue
544         else:
545             suite = unittest.TestLoader().loadTestsFromTestCase(module)
546         suite.run(result)
547
548     print(result)
549     for _, err in result.errors:
550         print(err)
551     for _, err in result.failures:
552         print(err)
553
554 if __name__ != '__main__':
555     sys.exit(1)
556
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()
567
568 # Run our meagre tests
569 if options.test:
570     RunTests(args)
571 elif options.test_coverage:
572     RunTestCoverage()