]> git.ipfire.org Git - thirdparty/u-boot.git/blame - tools/dtoc/test_fdt.py
libfdt: Update to latest pylibfdt implementation
[thirdparty/u-boot.git] / tools / dtoc / test_fdt.py
CommitLineData
2ba98753
SG
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
7from optparse import OptionParser
8import glob
9import os
10import sys
11import unittest
12
13# Bring in the patman libraries
14our_path = os.path.dirname(os.path.realpath(__file__))
15for dirname in ['../patman', '..']:
16 sys.path.insert(0, os.path.join(our_path, dirname))
17
18import command
19import fdt
20from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
2a2d91d0 21import fdt_util
2ba98753
SG
22from fdt_util import fdt32_to_cpu
23import libfdt
24import test_util
25import tools
26
f9b88b3a
SG
27def _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
2ba98753
SG
51class 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)
2a2d91d0 81 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
2ba98753
SG
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"""
96066240 101 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
2ba98753
SG
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',
2a2d91d0 108 'intarray', 'intval', 'longbytearray', 'notstring',
2ba98753
SG
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:
2a2d91d0 115 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
2ba98753
SG
116 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
117
94a7c603
SG
118 def testGetFdt(self):
119 node = self.dtb.GetNode('/spl-test')
120 self.assertEqual(self.dtb, node.GetFdt())
2ba98753
SG
121
122class 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)
2a2d91d0
SG
151 with self.assertRaises(libfdt.FdtException):
152 self.node.DeleteProp('missing')
2ba98753 153
f9b88b3a
SG
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
2ba98753 160 def testFindNode(self):
1d85888c
SG
161 """Tests that we can find a node using the FindNode() functoin"""
162 node = self.dtb.GetRoot().FindNode('i2c@0')
2ba98753 163 self.assertEqual('i2c@0', node.name)
1d85888c 164 subnode = node.FindNode('pmic@9')
2ba98753 165 self.assertEqual('pmic@9', subnode.name)
1d85888c 166 self.assertEqual(None, node.FindNode('missing'))
2ba98753 167
f9b88b3a
SG
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
94a7c603
SG
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
2ba98753
SG
202
203class 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
b9066ffc
SG
219 def testMissingNode(self):
220 self.assertEqual(None, self.dtb.GetNode('missing'))
221
2a2d91d0
SG
222 def testPhandle(self):
223 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
760b7170
SG
224 node = dtb.GetNode('/phandle-source2')
225 prop = node.props['clocks']
226 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
2a2d91d0
SG
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 """
50c59522 236 p = self.fdt.getprop(self.node.Offset(), prop_name)
2a2d91d0
SG
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
2ba98753
SG
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"""
f9b88b3a
SG
285 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
286 self.assertEqual(prop.value, value)
2ba98753
SG
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
116adecb
SG
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
2ba98753 353
2a2d91d0
SG
354class 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
3af8e49c
SG
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
94a7c603
SG
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
53af22a9
SG
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))
2a2d91d0
SG
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
443def 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
2ba98753
SG
449def 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
2a2d91d0 459 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
2ba98753
SG
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
475if __name__ != '__main__':
476 sys.exit(1)
477
478parser = OptionParser()
2a2d91d0
SG
479parser.add_option('-B', '--build-dir', type='string', default='b',
480 help='Directory containing the build output')
2ba98753
SG
481parser.add_option('-t', '--test', action='store_true', dest='test',
482 default=False, help='run tests')
2a2d91d0
SG
483parser.add_option('-T', '--test-coverage', action='store_true',
484 default=False, help='run tests and check for 100% coverage')
2ba98753
SG
485(options, args) = parser.parse_args()
486
487# Run our meagre tests
488if options.test:
489 RunTests(args)
2a2d91d0
SG
490elif options.test_coverage:
491 RunTestCoverage()