dtoc: Add a function to obtain a list of phandles
[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 optparse import OptionParser
8 import glob
9 import os
10 import sys
11 import unittest
12
13 # Bring in the patman libraries
14 our_path = os.path.dirname(os.path.realpath(__file__))
15 for dirname in ['../patman', '..']:
16     sys.path.insert(0, os.path.join(our_path, dirname))
17
18 import command
19 import fdt
20 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
21 import fdt_util
22 from fdt_util import fdt32_to_cpu
23 import libfdt
24 import test_util
25 import tools
26
27 def _GetPropertyValue(dtb, node, prop_name):
28     """Low-level function to get the property value based on its offset
29
30     This looks directly in the device tree at the property's offset to find
31     its value. It is useful as a check that the property is in the correct
32     place.
33
34     Args:
35         node: Node to look in
36         prop_name: Property name to find
37
38     Returns:
39         Tuple:
40             Prop object found
41             Value of property as a string (found using property offset)
42     """
43     prop = node.props[prop_name]
44
45     # Add 12, which is sizeof(struct fdt_property), to get to start of data
46     offset = prop.GetOffset() + 12
47     data = dtb.GetContents()[offset:offset + len(prop.value)]
48     return prop, [chr(x) for x in data]
49
50
51 class TestFdt(unittest.TestCase):
52     """Tests for the Fdt module
53
54     This includes unit tests for some functions and functional tests for the fdt
55     module.
56     """
57     @classmethod
58     def setUpClass(cls):
59         tools.PrepareOutputDir(None)
60
61     @classmethod
62     def tearDownClass(cls):
63         tools._FinaliseForTest()
64
65     def setUp(self):
66         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
67
68     def testFdt(self):
69         """Test that we can open an Fdt"""
70         self.dtb.Scan()
71         root = self.dtb.GetRoot()
72         self.assertTrue(isinstance(root, fdt.Node))
73
74     def testGetNode(self):
75         """Test the GetNode() method"""
76         node = self.dtb.GetNode('/spl-test')
77         self.assertTrue(isinstance(node, fdt.Node))
78         node = self.dtb.GetNode('/i2c@0/pmic@9')
79         self.assertTrue(isinstance(node, fdt.Node))
80         self.assertEqual('pmic@9', node.name)
81         self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
82
83     def testFlush(self):
84         """Check that we can flush the device tree out to its file"""
85         fname = self.dtb._fname
86         with open(fname) as fd:
87             data = fd.read()
88         os.remove(fname)
89         with self.assertRaises(IOError):
90             open(fname)
91         self.dtb.Flush()
92         with open(fname) as fd:
93             data = fd.read()
94
95     def testPack(self):
96         """Test that packing a device tree works"""
97         self.dtb.Pack()
98
99     def testGetFdt(self):
100         """Tetst that we can access the raw device-tree data"""
101         self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
102
103     def testGetProps(self):
104         """Tests obtaining a list of properties"""
105         node = self.dtb.GetNode('/spl-test')
106         props = self.dtb.GetProps(node)
107         self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
108                           'intarray', 'intval', 'longbytearray', 'notstring',
109                           'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
110                          sorted(props.keys()))
111
112     def testCheckError(self):
113         """Tests the ChecKError() function"""
114         with self.assertRaises(ValueError) as e:
115             fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
116         self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
117
118     def testGetFdt(self):
119         node = self.dtb.GetNode('/spl-test')
120         self.assertEqual(self.dtb, node.GetFdt())
121
122 class TestNode(unittest.TestCase):
123     """Test operation of the Node class"""
124
125     @classmethod
126     def setUpClass(cls):
127         tools.PrepareOutputDir(None)
128
129     @classmethod
130     def tearDownClass(cls):
131         tools._FinaliseForTest()
132
133     def setUp(self):
134         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
135         self.node = self.dtb.GetNode('/spl-test')
136
137     def testOffset(self):
138         """Tests that we can obtain the offset of a node"""
139         self.assertTrue(self.node.Offset() > 0)
140
141     def testDelete(self):
142         """Tests that we can delete a property"""
143         node2 = self.dtb.GetNode('/spl-test2')
144         offset1 = node2.Offset()
145         self.node.DeleteProp('intval')
146         offset2 = node2.Offset()
147         self.assertTrue(offset2 < offset1)
148         self.node.DeleteProp('intarray')
149         offset3 = node2.Offset()
150         self.assertTrue(offset3 < offset2)
151         with self.assertRaises(libfdt.FdtException):
152             self.node.DeleteProp('missing')
153
154     def testDeleteGetOffset(self):
155         """Test that property offset update when properties are deleted"""
156         self.node.DeleteProp('intval')
157         prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
158         self.assertEqual(prop.value, value)
159
160     def testFindNode(self):
161         """Tests that we can find a node using the FindNode() functoin"""
162         node = self.dtb.GetRoot().FindNode('i2c@0')
163         self.assertEqual('i2c@0', node.name)
164         subnode = node.FindNode('pmic@9')
165         self.assertEqual('pmic@9', subnode.name)
166         self.assertEqual(None, node.FindNode('missing'))
167
168     def testRefreshMissingNode(self):
169         """Test refreshing offsets when an extra node is present in dtb"""
170         # Delete it from our tables, not the device tree
171         del self.dtb._root.subnodes[-1]
172         with self.assertRaises(ValueError) as e:
173             self.dtb.Refresh()
174         self.assertIn('Internal error, offset', str(e.exception))
175
176     def testRefreshExtraNode(self):
177         """Test refreshing offsets when an expected node is missing"""
178         # Delete it from the device tre, not our tables
179         self.dtb.GetFdtObj().del_node(self.node.Offset())
180         with self.assertRaises(ValueError) as e:
181             self.dtb.Refresh()
182         self.assertIn('Internal error, node name mismatch '
183                       'spl-test != spl-test2', str(e.exception))
184
185     def testRefreshMissingProp(self):
186         """Test refreshing offsets when an extra property is present in dtb"""
187         # Delete it from our tables, not the device tree
188         del self.node.props['notstring']
189         with self.assertRaises(ValueError) as e:
190             self.dtb.Refresh()
191         self.assertIn("Internal error, property 'notstring' missing, offset ",
192                       str(e.exception))
193
194     def testLookupPhandle(self):
195         """Test looking up a single phandle"""
196         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
197         node = dtb.GetNode('/phandle-source2')
198         prop = node.props['clocks']
199         target = dtb.GetNode('/phandle-target')
200         self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
201
202
203 class TestProp(unittest.TestCase):
204     """Test operation of the Prop class"""
205
206     @classmethod
207     def setUpClass(cls):
208         tools.PrepareOutputDir(None)
209
210     @classmethod
211     def tearDownClass(cls):
212         tools._FinaliseForTest()
213
214     def setUp(self):
215         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
216         self.node = self.dtb.GetNode('/spl-test')
217         self.fdt = self.dtb.GetFdtObj()
218
219     def testMissingNode(self):
220         self.assertEqual(None, self.dtb.GetNode('missing'))
221
222     def testPhandle(self):
223         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
224         node = dtb.GetNode('/phandle-source2')
225         prop = node.props['clocks']
226         self.assertTrue(fdt32_to_cpu(prop.value) > 0)
227
228     def _ConvertProp(self, prop_name):
229         """Helper function to look up a property in self.node and return it
230
231         Args:
232             Property name to find
233
234         Return fdt.Prop object for this property
235         """
236         p = self.fdt.get_property(self.node.Offset(), prop_name)
237         return fdt.Prop(self.node, -1, prop_name, p)
238
239     def testMakeProp(self):
240         """Test we can convert all the the types that are supported"""
241         prop = self._ConvertProp('boolval')
242         self.assertEqual(fdt.TYPE_BOOL, prop.type)
243         self.assertEqual(True, prop.value)
244
245         prop = self._ConvertProp('intval')
246         self.assertEqual(fdt.TYPE_INT, prop.type)
247         self.assertEqual(1, fdt32_to_cpu(prop.value))
248
249         prop = self._ConvertProp('intarray')
250         self.assertEqual(fdt.TYPE_INT, prop.type)
251         val = [fdt32_to_cpu(val) for val in prop.value]
252         self.assertEqual([2, 3, 4], val)
253
254         prop = self._ConvertProp('byteval')
255         self.assertEqual(fdt.TYPE_BYTE, prop.type)
256         self.assertEqual(5, ord(prop.value))
257
258         prop = self._ConvertProp('longbytearray')
259         self.assertEqual(fdt.TYPE_BYTE, prop.type)
260         val = [ord(val) for val in prop.value]
261         self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
262
263         prop = self._ConvertProp('stringval')
264         self.assertEqual(fdt.TYPE_STRING, prop.type)
265         self.assertEqual('message', prop.value)
266
267         prop = self._ConvertProp('stringarray')
268         self.assertEqual(fdt.TYPE_STRING, prop.type)
269         self.assertEqual(['multi-word', 'message'], prop.value)
270
271         prop = self._ConvertProp('notstring')
272         self.assertEqual(fdt.TYPE_BYTE, prop.type)
273         val = [ord(val) for val in prop.value]
274         self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
275
276     def testGetEmpty(self):
277         """Tests the GetEmpty() function for the various supported types"""
278         self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
279         self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
280         self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
281         self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
282
283     def testGetOffset(self):
284         """Test we can get the offset of a property"""
285         prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
286         self.assertEqual(prop.value, value)
287
288     def testWiden(self):
289         """Test widening of values"""
290         node2 = self.dtb.GetNode('/spl-test2')
291         prop = self.node.props['intval']
292
293         # No action
294         prop2 = node2.props['intval']
295         prop.Widen(prop2)
296         self.assertEqual(fdt.TYPE_INT, prop.type)
297         self.assertEqual(1, fdt32_to_cpu(prop.value))
298
299         # Convert singla value to array
300         prop2 = self.node.props['intarray']
301         prop.Widen(prop2)
302         self.assertEqual(fdt.TYPE_INT, prop.type)
303         self.assertTrue(isinstance(prop.value, list))
304
305         # A 4-byte array looks like a single integer. When widened by a longer
306         # byte array, it should turn into an array.
307         prop = self.node.props['longbytearray']
308         prop2 = node2.props['longbytearray']
309         self.assertFalse(isinstance(prop2.value, list))
310         self.assertEqual(4, len(prop2.value))
311         prop2.Widen(prop)
312         self.assertTrue(isinstance(prop2.value, list))
313         self.assertEqual(9, len(prop2.value))
314
315         # Similarly for a string array
316         prop = self.node.props['stringval']
317         prop2 = node2.props['stringarray']
318         self.assertFalse(isinstance(prop.value, list))
319         self.assertEqual(7, len(prop.value))
320         prop.Widen(prop2)
321         self.assertTrue(isinstance(prop.value, list))
322         self.assertEqual(3, len(prop.value))
323
324         # Enlarging an existing array
325         prop = self.node.props['stringarray']
326         prop2 = node2.props['stringarray']
327         self.assertTrue(isinstance(prop.value, list))
328         self.assertEqual(2, len(prop.value))
329         prop.Widen(prop2)
330         self.assertTrue(isinstance(prop.value, list))
331         self.assertEqual(3, len(prop.value))
332
333     def testAdd(self):
334         """Test adding properties"""
335         self.fdt.pack()
336         # This function should automatically expand the device tree
337         self.node.AddZeroProp('one')
338         self.node.AddZeroProp('two')
339         self.node.AddZeroProp('three')
340
341         # Updating existing properties should be OK, since the device-tree size
342         # does not change
343         self.fdt.pack()
344         self.node.SetInt('one', 1)
345         self.node.SetInt('two', 2)
346         self.node.SetInt('three', 3)
347
348         # This should fail since it would need to increase the device-tree size
349         with self.assertRaises(libfdt.FdtException) as e:
350             self.node.SetInt('four', 4)
351         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
352
353
354 class TestFdtUtil(unittest.TestCase):
355     """Tests for the fdt_util module
356
357     This module will likely be mostly replaced at some point, once upstream
358     libfdt has better Python support. For now, this provides tests for current
359     functionality.
360     """
361     @classmethod
362     def setUpClass(cls):
363         tools.PrepareOutputDir(None)
364
365     def setUp(self):
366         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
367         self.node = self.dtb.GetNode('/spl-test')
368
369     def testGetInt(self):
370         self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
371         self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
372
373         with self.assertRaises(ValueError) as e:
374             self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
375         self.assertIn("property 'intarray' has list value: expecting a single "
376                       'integer', str(e.exception))
377
378     def testGetString(self):
379         self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
380         self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
381                                                     'test'))
382
383         with self.assertRaises(ValueError) as e:
384             self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
385         self.assertIn("property 'stringarray' has list value: expecting a "
386                       'single string', str(e.exception))
387
388     def testGetBool(self):
389         self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
390         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
391         self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
392         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
393
394     def testGetByte(self):
395         self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
396         self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
397
398         with self.assertRaises(ValueError) as e:
399             fdt_util.GetByte(self.node, 'longbytearray')
400         self.assertIn("property 'longbytearray' has list value: expecting a "
401                       'single byte', str(e.exception))
402
403         with self.assertRaises(ValueError) as e:
404             fdt_util.GetByte(self.node, 'intval')
405         self.assertIn("property 'intval' has length 4, expecting 1",
406                       str(e.exception))
407
408     def testGetPhandleList(self):
409         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
410         node = dtb.GetNode('/phandle-source2')
411         self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
412         node = dtb.GetNode('/phandle-source')
413         self.assertEqual([1, 2, 11, 3, 12, 13, 1],
414                          fdt_util.GetPhandleList(node, 'clocks'))
415         self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
416
417     def testGetDataType(self):
418         self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
419         self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
420                                                          str))
421         with self.assertRaises(ValueError) as e:
422             self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
423                                                      bool))
424     def testFdtCellsToCpu(self):
425         val = self.node.props['intarray'].value
426         self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
427         self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
428
429         dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
430         node2 = dtb2.GetNode('/test1')
431         val = node2.props['reg'].value
432         self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
433
434     def testEnsureCompiled(self):
435         """Test a degenerate case of this function"""
436         dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
437         self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
438
439     def testGetPlainBytes(self):
440         self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
441
442
443 def RunTestCoverage():
444     """Run the tests and check that we get 100% coverage"""
445     test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
446             ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
447
448
449 def RunTests(args):
450     """Run all the test we have for the fdt model
451
452     Args:
453         args: List of positional args provided to fdt. This can hold a test
454             name to execute (as in 'fdt -t testFdt', for example)
455     """
456     result = unittest.TestResult()
457     sys.argv = [sys.argv[0]]
458     test_name = args and args[0] or None
459     for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
460         if test_name:
461             try:
462                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
463             except AttributeError:
464                 continue
465         else:
466             suite = unittest.TestLoader().loadTestsFromTestCase(module)
467         suite.run(result)
468
469     print result
470     for _, err in result.errors:
471         print err
472     for _, err in result.failures:
473         print err
474
475 if __name__ != '__main__':
476     sys.exit(1)
477
478 parser = OptionParser()
479 parser.add_option('-B', '--build-dir', type='string', default='b',
480         help='Directory containing the build output')
481 parser.add_option('-t', '--test', action='store_true', dest='test',
482                   default=False, help='run tests')
483 parser.add_option('-T', '--test-coverage', action='store_true',
484                 default=False, help='run tests and check for 100% coverage')
485 (options, args) = parser.parse_args()
486
487 # Run our meagre tests
488 if options.test:
489     RunTests(args)
490 elif options.test_coverage:
491     RunTestCoverage()