2 # SPDX-License-Identifier: GPL-2.0+
3 # Copyright (c) 2018 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
7 from optparse import OptionParser
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))
20 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
22 from fdt_util import fdt32_to_cpu
27 def _GetPropertyValue(dtb, node, prop_name):
28 """Low-level function to get the property value based on its offset
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
36 prop_name: Property name to find
41 Value of property as a string (found using property offset)
43 prop = node.props[prop_name]
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]
51 class TestFdt(unittest.TestCase):
52 """Tests for the Fdt module
54 This includes unit tests for some functions and functional tests for the fdt
59 tools.PrepareOutputDir(None)
62 def tearDownClass(cls):
63 tools._FinaliseForTest()
66 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
69 """Test that we can open an Fdt"""
71 root = self.dtb.GetRoot()
72 self.assertTrue(isinstance(root, fdt.Node))
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'))
84 """Check that we can flush the device tree out to its file"""
85 fname = self.dtb._fname
86 with open(fname) as fd:
89 with self.assertRaises(IOError):
92 with open(fname) as fd:
96 """Test that packing a device tree works"""
100 """Tetst that we can access the raw device-tree data"""
101 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
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()))
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))
118 def testGetFdt(self):
119 node = self.dtb.GetNode('/spl-test')
120 self.assertEqual(self.dtb, node.GetFdt())
122 class TestNode(unittest.TestCase):
123 """Test operation of the Node class"""
127 tools.PrepareOutputDir(None)
130 def tearDownClass(cls):
131 tools._FinaliseForTest()
134 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
135 self.node = self.dtb.GetNode('/spl-test')
137 def testOffset(self):
138 """Tests that we can obtain the offset of a node"""
139 self.assertTrue(self.node.Offset() > 0)
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')
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)
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'))
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:
174 self.assertIn('Internal error, offset', str(e.exception))
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:
182 self.assertIn('Internal error, node name mismatch '
183 'spl-test != spl-test2', str(e.exception))
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:
191 self.assertIn("Internal error, property 'notstring' missing, offset ",
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)))
203 class TestProp(unittest.TestCase):
204 """Test operation of the Prop class"""
208 tools.PrepareOutputDir(None)
211 def tearDownClass(cls):
212 tools._FinaliseForTest()
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()
219 def testMissingNode(self):
220 self.assertEqual(None, self.dtb.GetNode('missing'))
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)
228 def _ConvertProp(self, prop_name):
229 """Helper function to look up a property in self.node and return it
232 Property name to find
234 Return fdt.Prop object for this property
236 p = self.fdt.getprop(self.node.Offset(), prop_name)
237 return fdt.Prop(self.node, -1, prop_name, p)
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)
245 prop = self._ConvertProp('intval')
246 self.assertEqual(fdt.TYPE_INT, prop.type)
247 self.assertEqual(1, fdt32_to_cpu(prop.value))
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)
254 prop = self._ConvertProp('byteval')
255 self.assertEqual(fdt.TYPE_BYTE, prop.type)
256 self.assertEqual(5, ord(prop.value))
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)
263 prop = self._ConvertProp('stringval')
264 self.assertEqual(fdt.TYPE_STRING, prop.type)
265 self.assertEqual('message', prop.value)
267 prop = self._ConvertProp('stringarray')
268 self.assertEqual(fdt.TYPE_STRING, prop.type)
269 self.assertEqual(['multi-word', 'message'], prop.value)
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)
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))
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)
289 """Test widening of values"""
290 node2 = self.dtb.GetNode('/spl-test2')
291 prop = self.node.props['intval']
294 prop2 = node2.props['intval']
296 self.assertEqual(fdt.TYPE_INT, prop.type)
297 self.assertEqual(1, fdt32_to_cpu(prop.value))
299 # Convert singla value to array
300 prop2 = self.node.props['intarray']
302 self.assertEqual(fdt.TYPE_INT, prop.type)
303 self.assertTrue(isinstance(prop.value, list))
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))
312 self.assertTrue(isinstance(prop2.value, list))
313 self.assertEqual(9, len(prop2.value))
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))
321 self.assertTrue(isinstance(prop.value, list))
322 self.assertEqual(3, len(prop.value))
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))
330 self.assertTrue(isinstance(prop.value, list))
331 self.assertEqual(3, len(prop.value))
334 """Test adding properties"""
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 self.dtb.Sync(auto_resize=True)
342 # Updating existing properties should be OK, since the device-tree size
345 self.node.SetInt('one', 1)
346 self.node.SetInt('two', 2)
347 self.node.SetInt('three', 3)
348 self.dtb.Sync(auto_resize=False)
350 # This should fail since it would need to increase the device-tree size
351 self.node.AddZeroProp('four')
352 with self.assertRaises(libfdt.FdtException) as e:
353 self.dtb.Sync(auto_resize=False)
354 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
355 self.dtb.Sync(auto_resize=True)
357 def testAddNode(self):
359 self.node.AddSubnode('subnode')
360 with self.assertRaises(libfdt.FdtException) as e:
361 self.dtb.Sync(auto_resize=False)
362 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
364 self.dtb.Sync(auto_resize=True)
365 offset = self.fdt.path_offset('/spl-test/subnode')
366 self.assertTrue(offset > 0)
368 def testAddMore(self):
369 """Test various other methods for adding and setting properties"""
370 self.node.AddZeroProp('one')
371 self.dtb.Sync(auto_resize=True)
372 data = self.fdt.getprop(self.node.Offset(), 'one')
373 self.assertEqual(0, fdt32_to_cpu(data))
375 self.node.SetInt('one', 1)
376 self.dtb.Sync(auto_resize=False)
377 data = self.fdt.getprop(self.node.Offset(), 'one')
378 self.assertEqual(1, fdt32_to_cpu(data))
380 val = '123' + chr(0) + '456'
381 self.node.AddString('string', val)
382 self.dtb.Sync(auto_resize=True)
383 data = self.fdt.getprop(self.node.Offset(), 'string')
384 self.assertEqual(val + '\0', data)
387 self.node.SetString('string', val + 'x')
388 with self.assertRaises(libfdt.FdtException) as e:
389 self.dtb.Sync(auto_resize=False)
390 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
391 self.node.SetString('string', val[:-1])
393 prop = self.node.props['string']
395 self.dtb.Sync(auto_resize=False)
396 data = self.fdt.getprop(self.node.Offset(), 'string')
397 self.assertEqual(val, data)
399 self.node.AddEmptyProp('empty', 5)
400 self.dtb.Sync(auto_resize=True)
401 prop = self.node.props['empty']
403 self.dtb.Sync(auto_resize=False)
404 data = self.fdt.getprop(self.node.Offset(), 'empty')
405 self.assertEqual(val, data)
407 self.node.SetData('empty', '123')
408 self.assertEqual('123', prop.bytes)
411 class TestFdtUtil(unittest.TestCase):
412 """Tests for the fdt_util module
414 This module will likely be mostly replaced at some point, once upstream
415 libfdt has better Python support. For now, this provides tests for current
420 tools.PrepareOutputDir(None)
423 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
424 self.node = self.dtb.GetNode('/spl-test')
426 def testGetInt(self):
427 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
428 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
430 with self.assertRaises(ValueError) as e:
431 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
432 self.assertIn("property 'intarray' has list value: expecting a single "
433 'integer', str(e.exception))
435 def testGetString(self):
436 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
437 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
440 with self.assertRaises(ValueError) as e:
441 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
442 self.assertIn("property 'stringarray' has list value: expecting a "
443 'single string', str(e.exception))
445 def testGetBool(self):
446 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
447 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
448 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
449 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
451 def testGetByte(self):
452 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
453 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
455 with self.assertRaises(ValueError) as e:
456 fdt_util.GetByte(self.node, 'longbytearray')
457 self.assertIn("property 'longbytearray' has list value: expecting a "
458 'single byte', str(e.exception))
460 with self.assertRaises(ValueError) as e:
461 fdt_util.GetByte(self.node, 'intval')
462 self.assertIn("property 'intval' has length 4, expecting 1",
465 def testGetPhandleList(self):
466 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
467 node = dtb.GetNode('/phandle-source2')
468 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
469 node = dtb.GetNode('/phandle-source')
470 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
471 fdt_util.GetPhandleList(node, 'clocks'))
472 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
474 def testGetDataType(self):
475 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
476 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
478 with self.assertRaises(ValueError) as e:
479 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
481 def testFdtCellsToCpu(self):
482 val = self.node.props['intarray'].value
483 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
484 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
486 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
487 node2 = dtb2.GetNode('/test1')
488 val = node2.props['reg'].value
489 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
491 def testEnsureCompiled(self):
492 """Test a degenerate case of this function"""
493 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
494 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
496 def testGetPlainBytes(self):
497 self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
500 def RunTestCoverage():
501 """Run the tests and check that we get 100% coverage"""
502 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
503 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
507 """Run all the test we have for the fdt model
510 args: List of positional args provided to fdt. This can hold a test
511 name to execute (as in 'fdt -t testFdt', for example)
513 result = unittest.TestResult()
514 sys.argv = [sys.argv[0]]
515 test_name = args and args[0] or None
516 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
519 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
520 except AttributeError:
523 suite = unittest.TestLoader().loadTestsFromTestCase(module)
527 for _, err in result.errors:
529 for _, err in result.failures:
532 if __name__ != '__main__':
535 parser = OptionParser()
536 parser.add_option('-B', '--build-dir', type='string', default='b',
537 help='Directory containing the build output')
538 parser.add_option('-t', '--test', action='store_true', dest='test',
539 default=False, help='run tests')
540 parser.add_option('-T', '--test-coverage', action='store_true',
541 default=False, help='run tests and check for 100% coverage')
542 (options, args) = parser.parse_args()
544 # Run our meagre tests
547 elif options.test_coverage: