]> git.sur5r.net Git - u-boot/blob - tools/dtoc/test_fdt.py
net: sun8i-emac: support R40 GMAC
[u-boot] / 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
119 class TestNode(unittest.TestCase):
120     """Test operation of the Node class"""
121
122     @classmethod
123     def setUpClass(cls):
124         tools.PrepareOutputDir(None)
125
126     @classmethod
127     def tearDownClass(cls):
128         tools._FinaliseForTest()
129
130     def setUp(self):
131         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
132         self.node = self.dtb.GetNode('/spl-test')
133
134     def testOffset(self):
135         """Tests that we can obtain the offset of a node"""
136         self.assertTrue(self.node.Offset() > 0)
137
138     def testDelete(self):
139         """Tests that we can delete a property"""
140         node2 = self.dtb.GetNode('/spl-test2')
141         offset1 = node2.Offset()
142         self.node.DeleteProp('intval')
143         offset2 = node2.Offset()
144         self.assertTrue(offset2 < offset1)
145         self.node.DeleteProp('intarray')
146         offset3 = node2.Offset()
147         self.assertTrue(offset3 < offset2)
148         with self.assertRaises(libfdt.FdtException):
149             self.node.DeleteProp('missing')
150
151     def testDeleteGetOffset(self):
152         """Test that property offset update when properties are deleted"""
153         self.node.DeleteProp('intval')
154         prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
155         self.assertEqual(prop.value, value)
156
157     def testFindNode(self):
158         """Tests that we can find a node using the _FindNode() functoin"""
159         node = self.dtb.GetRoot()._FindNode('i2c@0')
160         self.assertEqual('i2c@0', node.name)
161         subnode = node._FindNode('pmic@9')
162         self.assertEqual('pmic@9', subnode.name)
163         self.assertEqual(None, node._FindNode('missing'))
164
165     def testRefreshMissingNode(self):
166         """Test refreshing offsets when an extra node is present in dtb"""
167         # Delete it from our tables, not the device tree
168         del self.dtb._root.subnodes[-1]
169         with self.assertRaises(ValueError) as e:
170             self.dtb.Refresh()
171         self.assertIn('Internal error, offset', str(e.exception))
172
173     def testRefreshExtraNode(self):
174         """Test refreshing offsets when an expected node is missing"""
175         # Delete it from the device tre, not our tables
176         self.dtb.GetFdtObj().del_node(self.node.Offset())
177         with self.assertRaises(ValueError) as e:
178             self.dtb.Refresh()
179         self.assertIn('Internal error, node name mismatch '
180                       'spl-test != spl-test2', str(e.exception))
181
182     def testRefreshMissingProp(self):
183         """Test refreshing offsets when an extra property is present in dtb"""
184         # Delete it from our tables, not the device tree
185         del self.node.props['notstring']
186         with self.assertRaises(ValueError) as e:
187             self.dtb.Refresh()
188         self.assertIn("Internal error, property 'notstring' missing, offset ",
189                       str(e.exception))
190
191
192 class TestProp(unittest.TestCase):
193     """Test operation of the Prop class"""
194
195     @classmethod
196     def setUpClass(cls):
197         tools.PrepareOutputDir(None)
198
199     @classmethod
200     def tearDownClass(cls):
201         tools._FinaliseForTest()
202
203     def setUp(self):
204         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
205         self.node = self.dtb.GetNode('/spl-test')
206         self.fdt = self.dtb.GetFdtObj()
207
208     def testMissingNode(self):
209         self.assertEqual(None, self.dtb.GetNode('missing'))
210
211     def testPhandle(self):
212         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
213         node = dtb.GetNode('/phandle-source2')
214         prop = node.props['clocks']
215         self.assertTrue(fdt32_to_cpu(prop.value) > 0)
216
217     def _ConvertProp(self, prop_name):
218         """Helper function to look up a property in self.node and return it
219
220         Args:
221             Property name to find
222
223         Return fdt.Prop object for this property
224         """
225         p = self.fdt.get_property(self.node.Offset(), prop_name)
226         return fdt.Prop(self.node, -1, prop_name, p)
227
228     def testMakeProp(self):
229         """Test we can convert all the the types that are supported"""
230         prop = self._ConvertProp('boolval')
231         self.assertEqual(fdt.TYPE_BOOL, prop.type)
232         self.assertEqual(True, prop.value)
233
234         prop = self._ConvertProp('intval')
235         self.assertEqual(fdt.TYPE_INT, prop.type)
236         self.assertEqual(1, fdt32_to_cpu(prop.value))
237
238         prop = self._ConvertProp('intarray')
239         self.assertEqual(fdt.TYPE_INT, prop.type)
240         val = [fdt32_to_cpu(val) for val in prop.value]
241         self.assertEqual([2, 3, 4], val)
242
243         prop = self._ConvertProp('byteval')
244         self.assertEqual(fdt.TYPE_BYTE, prop.type)
245         self.assertEqual(5, ord(prop.value))
246
247         prop = self._ConvertProp('longbytearray')
248         self.assertEqual(fdt.TYPE_BYTE, prop.type)
249         val = [ord(val) for val in prop.value]
250         self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
251
252         prop = self._ConvertProp('stringval')
253         self.assertEqual(fdt.TYPE_STRING, prop.type)
254         self.assertEqual('message', prop.value)
255
256         prop = self._ConvertProp('stringarray')
257         self.assertEqual(fdt.TYPE_STRING, prop.type)
258         self.assertEqual(['multi-word', 'message'], prop.value)
259
260         prop = self._ConvertProp('notstring')
261         self.assertEqual(fdt.TYPE_BYTE, prop.type)
262         val = [ord(val) for val in prop.value]
263         self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
264
265     def testGetEmpty(self):
266         """Tests the GetEmpty() function for the various supported types"""
267         self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
268         self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
269         self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
270         self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
271
272     def testGetOffset(self):
273         """Test we can get the offset of a property"""
274         prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
275         self.assertEqual(prop.value, value)
276
277     def testWiden(self):
278         """Test widening of values"""
279         node2 = self.dtb.GetNode('/spl-test2')
280         prop = self.node.props['intval']
281
282         # No action
283         prop2 = node2.props['intval']
284         prop.Widen(prop2)
285         self.assertEqual(fdt.TYPE_INT, prop.type)
286         self.assertEqual(1, fdt32_to_cpu(prop.value))
287
288         # Convert singla value to array
289         prop2 = self.node.props['intarray']
290         prop.Widen(prop2)
291         self.assertEqual(fdt.TYPE_INT, prop.type)
292         self.assertTrue(isinstance(prop.value, list))
293
294         # A 4-byte array looks like a single integer. When widened by a longer
295         # byte array, it should turn into an array.
296         prop = self.node.props['longbytearray']
297         prop2 = node2.props['longbytearray']
298         self.assertFalse(isinstance(prop2.value, list))
299         self.assertEqual(4, len(prop2.value))
300         prop2.Widen(prop)
301         self.assertTrue(isinstance(prop2.value, list))
302         self.assertEqual(9, len(prop2.value))
303
304         # Similarly for a string array
305         prop = self.node.props['stringval']
306         prop2 = node2.props['stringarray']
307         self.assertFalse(isinstance(prop.value, list))
308         self.assertEqual(7, len(prop.value))
309         prop.Widen(prop2)
310         self.assertTrue(isinstance(prop.value, list))
311         self.assertEqual(3, len(prop.value))
312
313         # Enlarging an existing array
314         prop = self.node.props['stringarray']
315         prop2 = node2.props['stringarray']
316         self.assertTrue(isinstance(prop.value, list))
317         self.assertEqual(2, len(prop.value))
318         prop.Widen(prop2)
319         self.assertTrue(isinstance(prop.value, list))
320         self.assertEqual(3, len(prop.value))
321
322     def testAdd(self):
323         """Test adding properties"""
324         self.fdt.pack()
325         # This function should automatically expand the device tree
326         self.node.AddZeroProp('one')
327         self.node.AddZeroProp('two')
328         self.node.AddZeroProp('three')
329
330         # Updating existing properties should be OK, since the device-tree size
331         # does not change
332         self.fdt.pack()
333         self.node.SetInt('one', 1)
334         self.node.SetInt('two', 2)
335         self.node.SetInt('three', 3)
336
337         # This should fail since it would need to increase the device-tree size
338         with self.assertRaises(libfdt.FdtException) as e:
339             self.node.SetInt('four', 4)
340         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
341
342
343 class TestFdtUtil(unittest.TestCase):
344     """Tests for the fdt_util module
345
346     This module will likely be mostly replaced at some point, once upstream
347     libfdt has better Python support. For now, this provides tests for current
348     functionality.
349     """
350     @classmethod
351     def setUpClass(cls):
352         tools.PrepareOutputDir(None)
353
354     def setUp(self):
355         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
356         self.node = self.dtb.GetNode('/spl-test')
357
358     def testGetInt(self):
359         self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
360         self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
361
362         with self.assertRaises(ValueError) as e:
363             self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
364         self.assertIn("property 'intarray' has list value: expecting a single "
365                       'integer', str(e.exception))
366
367     def testGetString(self):
368         self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
369         self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
370                                                     'test'))
371
372         with self.assertRaises(ValueError) as e:
373             self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
374         self.assertIn("property 'stringarray' has list value: expecting a "
375                       'single string', str(e.exception))
376
377     def testGetBool(self):
378         self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
379         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
380         self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
381         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
382
383     def testFdtCellsToCpu(self):
384         val = self.node.props['intarray'].value
385         self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
386         self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
387
388         dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
389         node2 = dtb2.GetNode('/test1')
390         val = node2.props['reg'].value
391         self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
392
393     def testEnsureCompiled(self):
394         """Test a degenerate case of this function"""
395         dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
396         self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
397
398     def testGetPlainBytes(self):
399         self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
400
401
402 def RunTestCoverage():
403     """Run the tests and check that we get 100% coverage"""
404     test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
405             ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
406
407
408 def RunTests(args):
409     """Run all the test we have for the fdt model
410
411     Args:
412         args: List of positional args provided to fdt. This can hold a test
413             name to execute (as in 'fdt -t testFdt', for example)
414     """
415     result = unittest.TestResult()
416     sys.argv = [sys.argv[0]]
417     test_name = args and args[0] or None
418     for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
419         if test_name:
420             try:
421                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
422             except AttributeError:
423                 continue
424         else:
425             suite = unittest.TestLoader().loadTestsFromTestCase(module)
426         suite.run(result)
427
428     print result
429     for _, err in result.errors:
430         print err
431     for _, err in result.failures:
432         print err
433
434 if __name__ != '__main__':
435     sys.exit(1)
436
437 parser = OptionParser()
438 parser.add_option('-B', '--build-dir', type='string', default='b',
439         help='Directory containing the build output')
440 parser.add_option('-t', '--test', action='store_true', dest='test',
441                   default=False, help='run tests')
442 parser.add_option('-T', '--test-coverage', action='store_true',
443                 default=False, help='run tests and check for 100% coverage')
444 (options, args) = parser.parse_args()
445
446 # Run our meagre tests
447 if options.test:
448     RunTests(args)
449 elif options.test_coverage:
450     RunTestCoverage()