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))
119 class TestNode(unittest.TestCase):
120 """Test operation of the Node class"""
124 tools.PrepareOutputDir(None)
127 def tearDownClass(cls):
128 tools._FinaliseForTest()
131 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
132 self.node = self.dtb.GetNode('/spl-test')
134 def testOffset(self):
135 """Tests that we can obtain the offset of a node"""
136 self.assertTrue(self.node.Offset() > 0)
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')
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)
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'))
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:
171 self.assertIn('Internal error, offset', str(e.exception))
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:
179 self.assertIn('Internal error, node name mismatch '
180 'spl-test != spl-test2', str(e.exception))
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:
188 self.assertIn("Internal error, property 'notstring' missing, offset ",
192 class TestProp(unittest.TestCase):
193 """Test operation of the Prop class"""
197 tools.PrepareOutputDir(None)
200 def tearDownClass(cls):
201 tools._FinaliseForTest()
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()
208 def testMissingNode(self):
209 self.assertEqual(None, self.dtb.GetNode('missing'))
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)
217 def _ConvertProp(self, prop_name):
218 """Helper function to look up a property in self.node and return it
221 Property name to find
223 Return fdt.Prop object for this property
225 p = self.fdt.get_property(self.node.Offset(), prop_name)
226 return fdt.Prop(self.node, -1, prop_name, p)
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)
234 prop = self._ConvertProp('intval')
235 self.assertEqual(fdt.TYPE_INT, prop.type)
236 self.assertEqual(1, fdt32_to_cpu(prop.value))
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)
243 prop = self._ConvertProp('byteval')
244 self.assertEqual(fdt.TYPE_BYTE, prop.type)
245 self.assertEqual(5, ord(prop.value))
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)
252 prop = self._ConvertProp('stringval')
253 self.assertEqual(fdt.TYPE_STRING, prop.type)
254 self.assertEqual('message', prop.value)
256 prop = self._ConvertProp('stringarray')
257 self.assertEqual(fdt.TYPE_STRING, prop.type)
258 self.assertEqual(['multi-word', 'message'], prop.value)
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)
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))
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)
278 """Test widening of values"""
279 node2 = self.dtb.GetNode('/spl-test2')
280 prop = self.node.props['intval']
283 prop2 = node2.props['intval']
285 self.assertEqual(fdt.TYPE_INT, prop.type)
286 self.assertEqual(1, fdt32_to_cpu(prop.value))
288 # Convert singla value to array
289 prop2 = self.node.props['intarray']
291 self.assertEqual(fdt.TYPE_INT, prop.type)
292 self.assertTrue(isinstance(prop.value, list))
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))
301 self.assertTrue(isinstance(prop2.value, list))
302 self.assertEqual(9, len(prop2.value))
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))
310 self.assertTrue(isinstance(prop.value, list))
311 self.assertEqual(3, len(prop.value))
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))
319 self.assertTrue(isinstance(prop.value, list))
320 self.assertEqual(3, len(prop.value))
323 class TestFdtUtil(unittest.TestCase):
324 """Tests for the fdt_util module
326 This module will likely be mostly replaced at some point, once upstream
327 libfdt has better Python support. For now, this provides tests for current
332 tools.PrepareOutputDir(None)
335 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
336 self.node = self.dtb.GetNode('/spl-test')
338 def testGetInt(self):
339 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
340 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
342 with self.assertRaises(ValueError) as e:
343 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
344 self.assertIn("property 'intarray' has list value: expecting a single "
345 'integer', str(e.exception))
347 def testGetString(self):
348 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
349 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
352 with self.assertRaises(ValueError) as e:
353 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
354 self.assertIn("property 'stringarray' has list value: expecting a "
355 'single string', str(e.exception))
357 def testGetBool(self):
358 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
359 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
360 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
361 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
363 def testFdtCellsToCpu(self):
364 val = self.node.props['intarray'].value
365 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
366 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
368 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
369 node2 = dtb2.GetNode('/test1')
370 val = node2.props['reg'].value
371 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
373 def testEnsureCompiled(self):
374 """Test a degenerate case of this function"""
375 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
376 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
378 def testGetPlainBytes(self):
379 self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
382 def RunTestCoverage():
383 """Run the tests and check that we get 100% coverage"""
384 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
385 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
389 """Run all the test we have for the fdt model
392 args: List of positional args provided to fdt. This can hold a test
393 name to execute (as in 'fdt -t testFdt', for example)
395 result = unittest.TestResult()
396 sys.argv = [sys.argv[0]]
397 test_name = args and args[0] or None
398 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
401 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
402 except AttributeError:
405 suite = unittest.TestLoader().loadTestsFromTestCase(module)
409 for _, err in result.errors:
411 for _, err in result.failures:
414 if __name__ != '__main__':
417 parser = OptionParser()
418 parser.add_option('-B', '--build-dir', type='string', default='b',
419 help='Directory containing the build output')
420 parser.add_option('-t', '--test', action='store_true', dest='test',
421 default=False, help='run tests')
422 parser.add_option('-T', '--test-coverage', action='store_true',
423 default=False, help='run tests and check for 100% coverage')
424 (options, args) = parser.parse_args()
426 # Run our meagre tests
429 elif options.test_coverage: