dtoc: Adjust code for Python 3
[platform/kernel/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
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, [chr(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) as fd:
89             data = fd.read()
90         os.remove(fname)
91         with self.assertRaises(IOError):
92             open(fname)
93         self.dtb.Flush()
94         with open(fname) 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 class TestNode(unittest.TestCase):
125     """Test operation of the Node class"""
126
127     @classmethod
128     def setUpClass(cls):
129         tools.PrepareOutputDir(None)
130
131     @classmethod
132     def tearDownClass(cls):
133         tools.FinaliseOutputDir()
134
135     def setUp(self):
136         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
137         self.node = self.dtb.GetNode('/spl-test')
138
139     def testOffset(self):
140         """Tests that we can obtain the offset of a node"""
141         self.assertTrue(self.node.Offset() > 0)
142
143     def testDelete(self):
144         """Tests that we can delete a property"""
145         node2 = self.dtb.GetNode('/spl-test2')
146         offset1 = node2.Offset()
147         self.node.DeleteProp('intval')
148         offset2 = node2.Offset()
149         self.assertTrue(offset2 < offset1)
150         self.node.DeleteProp('intarray')
151         offset3 = node2.Offset()
152         self.assertTrue(offset3 < offset2)
153         with self.assertRaises(libfdt.FdtException):
154             self.node.DeleteProp('missing')
155
156     def testDeleteGetOffset(self):
157         """Test that property offset update when properties are deleted"""
158         self.node.DeleteProp('intval')
159         prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
160         self.assertEqual(prop.value, value)
161
162     def testFindNode(self):
163         """Tests that we can find a node using the FindNode() functoin"""
164         node = self.dtb.GetRoot().FindNode('i2c@0')
165         self.assertEqual('i2c@0', node.name)
166         subnode = node.FindNode('pmic@9')
167         self.assertEqual('pmic@9', subnode.name)
168         self.assertEqual(None, node.FindNode('missing'))
169
170     def testRefreshMissingNode(self):
171         """Test refreshing offsets when an extra node is present in dtb"""
172         # Delete it from our tables, not the device tree
173         del self.dtb._root.subnodes[-1]
174         with self.assertRaises(ValueError) as e:
175             self.dtb.Refresh()
176         self.assertIn('Internal error, offset', str(e.exception))
177
178     def testRefreshExtraNode(self):
179         """Test refreshing offsets when an expected node is missing"""
180         # Delete it from the device tre, not our tables
181         self.dtb.GetFdtObj().del_node(self.node.Offset())
182         with self.assertRaises(ValueError) as e:
183             self.dtb.Refresh()
184         self.assertIn('Internal error, node name mismatch '
185                       'spl-test != spl-test2', str(e.exception))
186
187     def testRefreshMissingProp(self):
188         """Test refreshing offsets when an extra property is present in dtb"""
189         # Delete it from our tables, not the device tree
190         del self.node.props['notstring']
191         with self.assertRaises(ValueError) as e:
192             self.dtb.Refresh()
193         self.assertIn("Internal error, property 'notstring' missing, offset ",
194                       str(e.exception))
195
196     def testLookupPhandle(self):
197         """Test looking up a single phandle"""
198         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
199         node = dtb.GetNode('/phandle-source2')
200         prop = node.props['clocks']
201         target = dtb.GetNode('/phandle-target')
202         self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
203
204
205 class TestProp(unittest.TestCase):
206     """Test operation of the Prop class"""
207
208     @classmethod
209     def setUpClass(cls):
210         tools.PrepareOutputDir(None)
211
212     @classmethod
213     def tearDownClass(cls):
214         tools.FinaliseOutputDir()
215
216     def setUp(self):
217         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
218         self.node = self.dtb.GetNode('/spl-test')
219         self.fdt = self.dtb.GetFdtObj()
220
221     def testMissingNode(self):
222         self.assertEqual(None, self.dtb.GetNode('missing'))
223
224     def testPhandle(self):
225         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
226         node = dtb.GetNode('/phandle-source2')
227         prop = node.props['clocks']
228         self.assertTrue(fdt32_to_cpu(prop.value) > 0)
229
230     def _ConvertProp(self, prop_name):
231         """Helper function to look up a property in self.node and return it
232
233         Args:
234             Property name to find
235
236         Return fdt.Prop object for this property
237         """
238         p = self.fdt.getprop(self.node.Offset(), prop_name)
239         return fdt.Prop(self.node, -1, prop_name, p)
240
241     def testMakeProp(self):
242         """Test we can convert all the the types that are supported"""
243         prop = self._ConvertProp('boolval')
244         self.assertEqual(fdt.TYPE_BOOL, prop.type)
245         self.assertEqual(True, prop.value)
246
247         prop = self._ConvertProp('intval')
248         self.assertEqual(fdt.TYPE_INT, prop.type)
249         self.assertEqual(1, fdt32_to_cpu(prop.value))
250
251         prop = self._ConvertProp('intarray')
252         self.assertEqual(fdt.TYPE_INT, prop.type)
253         val = [fdt32_to_cpu(val) for val in prop.value]
254         self.assertEqual([2, 3, 4], val)
255
256         prop = self._ConvertProp('byteval')
257         self.assertEqual(fdt.TYPE_BYTE, prop.type)
258         self.assertEqual(5, ord(prop.value))
259
260         prop = self._ConvertProp('longbytearray')
261         self.assertEqual(fdt.TYPE_BYTE, prop.type)
262         val = [ord(val) for val in prop.value]
263         self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
264
265         prop = self._ConvertProp('stringval')
266         self.assertEqual(fdt.TYPE_STRING, prop.type)
267         self.assertEqual('message', prop.value)
268
269         prop = self._ConvertProp('stringarray')
270         self.assertEqual(fdt.TYPE_STRING, prop.type)
271         self.assertEqual(['multi-word', 'message'], prop.value)
272
273         prop = self._ConvertProp('notstring')
274         self.assertEqual(fdt.TYPE_BYTE, prop.type)
275         val = [ord(val) for val in prop.value]
276         self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
277
278     def testGetEmpty(self):
279         """Tests the GetEmpty() function for the various supported types"""
280         self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
281         self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
282         self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
283         self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
284
285     def testGetOffset(self):
286         """Test we can get the offset of a property"""
287         prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
288         self.assertEqual(prop.value, value)
289
290     def testWiden(self):
291         """Test widening of values"""
292         node2 = self.dtb.GetNode('/spl-test2')
293         prop = self.node.props['intval']
294
295         # No action
296         prop2 = node2.props['intval']
297         prop.Widen(prop2)
298         self.assertEqual(fdt.TYPE_INT, prop.type)
299         self.assertEqual(1, fdt32_to_cpu(prop.value))
300
301         # Convert singla value to array
302         prop2 = self.node.props['intarray']
303         prop.Widen(prop2)
304         self.assertEqual(fdt.TYPE_INT, prop.type)
305         self.assertTrue(isinstance(prop.value, list))
306
307         # A 4-byte array looks like a single integer. When widened by a longer
308         # byte array, it should turn into an array.
309         prop = self.node.props['longbytearray']
310         prop2 = node2.props['longbytearray']
311         self.assertFalse(isinstance(prop2.value, list))
312         self.assertEqual(4, len(prop2.value))
313         prop2.Widen(prop)
314         self.assertTrue(isinstance(prop2.value, list))
315         self.assertEqual(9, len(prop2.value))
316
317         # Similarly for a string array
318         prop = self.node.props['stringval']
319         prop2 = node2.props['stringarray']
320         self.assertFalse(isinstance(prop.value, list))
321         self.assertEqual(7, len(prop.value))
322         prop.Widen(prop2)
323         self.assertTrue(isinstance(prop.value, list))
324         self.assertEqual(3, len(prop.value))
325
326         # Enlarging an existing array
327         prop = self.node.props['stringarray']
328         prop2 = node2.props['stringarray']
329         self.assertTrue(isinstance(prop.value, list))
330         self.assertEqual(2, len(prop.value))
331         prop.Widen(prop2)
332         self.assertTrue(isinstance(prop.value, list))
333         self.assertEqual(3, len(prop.value))
334
335     def testAdd(self):
336         """Test adding properties"""
337         self.fdt.pack()
338         # This function should automatically expand the device tree
339         self.node.AddZeroProp('one')
340         self.node.AddZeroProp('two')
341         self.node.AddZeroProp('three')
342         self.dtb.Sync(auto_resize=True)
343
344         # Updating existing properties should be OK, since the device-tree size
345         # does not change
346         self.fdt.pack()
347         self.node.SetInt('one', 1)
348         self.node.SetInt('two', 2)
349         self.node.SetInt('three', 3)
350         self.dtb.Sync(auto_resize=False)
351
352         # This should fail since it would need to increase the device-tree size
353         self.node.AddZeroProp('four')
354         with self.assertRaises(libfdt.FdtException) as e:
355             self.dtb.Sync(auto_resize=False)
356         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
357         self.dtb.Sync(auto_resize=True)
358
359     def testAddNode(self):
360         self.fdt.pack()
361         self.node.AddSubnode('subnode')
362         with self.assertRaises(libfdt.FdtException) as e:
363             self.dtb.Sync(auto_resize=False)
364         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
365
366         self.dtb.Sync(auto_resize=True)
367         offset = self.fdt.path_offset('/spl-test/subnode')
368         self.assertTrue(offset > 0)
369
370     def testAddMore(self):
371         """Test various other methods for adding and setting properties"""
372         self.node.AddZeroProp('one')
373         self.dtb.Sync(auto_resize=True)
374         data = self.fdt.getprop(self.node.Offset(), 'one')
375         self.assertEqual(0, fdt32_to_cpu(data))
376
377         self.node.SetInt('one', 1)
378         self.dtb.Sync(auto_resize=False)
379         data = self.fdt.getprop(self.node.Offset(), 'one')
380         self.assertEqual(1, fdt32_to_cpu(data))
381
382         val = '123' + chr(0) + '456'
383         self.node.AddString('string', val)
384         self.dtb.Sync(auto_resize=True)
385         data = self.fdt.getprop(self.node.Offset(), 'string')
386         self.assertEqual(val + '\0', data)
387
388         self.fdt.pack()
389         self.node.SetString('string', val + 'x')
390         with self.assertRaises(libfdt.FdtException) as e:
391             self.dtb.Sync(auto_resize=False)
392         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
393         self.node.SetString('string', val[:-1])
394
395         prop = self.node.props['string']
396         prop.SetData(val)
397         self.dtb.Sync(auto_resize=False)
398         data = self.fdt.getprop(self.node.Offset(), 'string')
399         self.assertEqual(val, data)
400
401         self.node.AddEmptyProp('empty', 5)
402         self.dtb.Sync(auto_resize=True)
403         prop = self.node.props['empty']
404         prop.SetData(val)
405         self.dtb.Sync(auto_resize=False)
406         data = self.fdt.getprop(self.node.Offset(), 'empty')
407         self.assertEqual(val, data)
408
409         self.node.SetData('empty', '123')
410         self.assertEqual('123', prop.bytes)
411
412     def testFromData(self):
413         dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
414         self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
415
416         self.node.AddEmptyProp('empty', 5)
417         self.dtb.Sync(auto_resize=True)
418         self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
419
420
421 class TestFdtUtil(unittest.TestCase):
422     """Tests for the fdt_util module
423
424     This module will likely be mostly replaced at some point, once upstream
425     libfdt has better Python support. For now, this provides tests for current
426     functionality.
427     """
428     @classmethod
429     def setUpClass(cls):
430         tools.PrepareOutputDir(None)
431
432     @classmethod
433     def tearDownClass(cls):
434         tools.FinaliseOutputDir()
435
436     def setUp(self):
437         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
438         self.node = self.dtb.GetNode('/spl-test')
439
440     def testGetInt(self):
441         self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
442         self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
443
444         with self.assertRaises(ValueError) as e:
445             self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
446         self.assertIn("property 'intarray' has list value: expecting a single "
447                       'integer', str(e.exception))
448
449     def testGetString(self):
450         self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
451         self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
452                                                     'test'))
453
454         with self.assertRaises(ValueError) as e:
455             self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
456         self.assertIn("property 'stringarray' has list value: expecting a "
457                       'single string', str(e.exception))
458
459     def testGetBool(self):
460         self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
461         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
462         self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
463         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
464
465     def testGetByte(self):
466         self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
467         self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
468
469         with self.assertRaises(ValueError) as e:
470             fdt_util.GetByte(self.node, 'longbytearray')
471         self.assertIn("property 'longbytearray' has list value: expecting a "
472                       'single byte', str(e.exception))
473
474         with self.assertRaises(ValueError) as e:
475             fdt_util.GetByte(self.node, 'intval')
476         self.assertIn("property 'intval' has length 4, expecting 1",
477                       str(e.exception))
478
479     def testGetPhandleList(self):
480         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
481         node = dtb.GetNode('/phandle-source2')
482         self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
483         node = dtb.GetNode('/phandle-source')
484         self.assertEqual([1, 2, 11, 3, 12, 13, 1],
485                          fdt_util.GetPhandleList(node, 'clocks'))
486         self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
487
488     def testGetDataType(self):
489         self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
490         self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
491                                                          str))
492         with self.assertRaises(ValueError) as e:
493             self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
494                                                      bool))
495     def testFdtCellsToCpu(self):
496         val = self.node.props['intarray'].value
497         self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
498         self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
499
500         dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
501         node2 = dtb2.GetNode('/test1')
502         val = node2.props['reg'].value
503         self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
504
505     def testEnsureCompiled(self):
506         """Test a degenerate case of this function"""
507         dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
508         self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
509
510     def testGetPlainBytes(self):
511         self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
512
513
514 def RunTestCoverage():
515     """Run the tests and check that we get 100% coverage"""
516     test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
517             ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
518
519
520 def RunTests(args):
521     """Run all the test we have for the fdt model
522
523     Args:
524         args: List of positional args provided to fdt. This can hold a test
525             name to execute (as in 'fdt -t testFdt', for example)
526     """
527     result = unittest.TestResult()
528     sys.argv = [sys.argv[0]]
529     test_name = args and args[0] or None
530     for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
531         if test_name:
532             try:
533                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
534             except AttributeError:
535                 continue
536         else:
537             suite = unittest.TestLoader().loadTestsFromTestCase(module)
538         suite.run(result)
539
540     print(result)
541     for _, err in result.errors:
542         print(err)
543     for _, err in result.failures:
544         print(err)
545
546 if __name__ != '__main__':
547     sys.exit(1)
548
549 parser = OptionParser()
550 parser.add_option('-B', '--build-dir', type='string', default='b',
551         help='Directory containing the build output')
552 parser.add_option('-P', '--processes', type=int,
553                   help='set number of processes to use for running tests')
554 parser.add_option('-t', '--test', action='store_true', dest='test',
555                   default=False, help='run tests')
556 parser.add_option('-T', '--test-coverage', action='store_true',
557                 default=False, help='run tests and check for 100% coverage')
558 (options, args) = parser.parse_args()
559
560 # Run our meagre tests
561 if options.test:
562     RunTests(args)
563 elif options.test_coverage:
564     RunTestCoverage()