blob: e57298dbe7cd4d22e83cf42138bb444462c0985e [file] [log] [blame]
Simon Glass2ba98752018-07-06 10:27:24 -06001#!/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
Simon Glass2a2d91d2018-07-06 10:27:28 -060021import fdt_util
Simon Glass2ba98752018-07-06 10:27:24 -060022from fdt_util import fdt32_to_cpu
23import libfdt
24import test_util
25import tools
26
Simon Glassf9b88b32018-07-06 10:27:29 -060027def _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
Simon Glass2ba98752018-07-06 10:27:24 -060051class 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)
Simon Glass2a2d91d2018-07-06 10:27:28 -060081 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass2ba98752018-07-06 10:27:24 -060082
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"""
Simon Glass96066242018-07-06 10:27:27 -0600101 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass2ba98752018-07-06 10:27:24 -0600102
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',
Simon Glass2a2d91d2018-07-06 10:27:28 -0600108 'intarray', 'intval', 'longbytearray', 'notstring',
Simon Glass2ba98752018-07-06 10:27:24 -0600109 '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:
Simon Glass2a2d91d2018-07-06 10:27:28 -0600115 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass2ba98752018-07-06 10:27:24 -0600116 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
117
118
119class 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)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600148 with self.assertRaises(libfdt.FdtException):
149 self.node.DeleteProp('missing')
Simon Glass2ba98752018-07-06 10:27:24 -0600150
Simon Glassf9b88b32018-07-06 10:27:29 -0600151 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
Simon Glass2ba98752018-07-06 10:27:24 -0600157 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)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600163 self.assertEqual(None, node._FindNode('missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600164
Simon Glassf9b88b32018-07-06 10:27:29 -0600165 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
Simon Glass2ba98752018-07-06 10:27:24 -0600191
192class 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
Simon Glass2a2d91d2018-07-06 10:27:28 -0600208 def testPhandle(self):
209 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
210 node = dtb.GetNode('/phandle-source')
211
212 def _ConvertProp(self, prop_name):
213 """Helper function to look up a property in self.node and return it
214
215 Args:
216 Property name to find
217
218 Return fdt.Prop object for this property
219 """
220 p = self.fdt.get_property(self.node.Offset(), prop_name)
221 return fdt.Prop(self.node, -1, prop_name, p)
222
223 def testMakeProp(self):
224 """Test we can convert all the the types that are supported"""
225 prop = self._ConvertProp('boolval')
226 self.assertEqual(fdt.TYPE_BOOL, prop.type)
227 self.assertEqual(True, prop.value)
228
229 prop = self._ConvertProp('intval')
230 self.assertEqual(fdt.TYPE_INT, prop.type)
231 self.assertEqual(1, fdt32_to_cpu(prop.value))
232
233 prop = self._ConvertProp('intarray')
234 self.assertEqual(fdt.TYPE_INT, prop.type)
235 val = [fdt32_to_cpu(val) for val in prop.value]
236 self.assertEqual([2, 3, 4], val)
237
238 prop = self._ConvertProp('byteval')
239 self.assertEqual(fdt.TYPE_BYTE, prop.type)
240 self.assertEqual(5, ord(prop.value))
241
242 prop = self._ConvertProp('longbytearray')
243 self.assertEqual(fdt.TYPE_BYTE, prop.type)
244 val = [ord(val) for val in prop.value]
245 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
246
247 prop = self._ConvertProp('stringval')
248 self.assertEqual(fdt.TYPE_STRING, prop.type)
249 self.assertEqual('message', prop.value)
250
251 prop = self._ConvertProp('stringarray')
252 self.assertEqual(fdt.TYPE_STRING, prop.type)
253 self.assertEqual(['multi-word', 'message'], prop.value)
254
255 prop = self._ConvertProp('notstring')
256 self.assertEqual(fdt.TYPE_BYTE, prop.type)
257 val = [ord(val) for val in prop.value]
258 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
259
Simon Glass2ba98752018-07-06 10:27:24 -0600260 def testGetEmpty(self):
261 """Tests the GetEmpty() function for the various supported types"""
262 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
263 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
264 self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
265 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
266
267 def testGetOffset(self):
268 """Test we can get the offset of a property"""
Simon Glassf9b88b32018-07-06 10:27:29 -0600269 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
270 self.assertEqual(prop.value, value)
Simon Glass2ba98752018-07-06 10:27:24 -0600271
272 def testWiden(self):
273 """Test widening of values"""
274 node2 = self.dtb.GetNode('/spl-test2')
275 prop = self.node.props['intval']
276
277 # No action
278 prop2 = node2.props['intval']
279 prop.Widen(prop2)
280 self.assertEqual(fdt.TYPE_INT, prop.type)
281 self.assertEqual(1, fdt32_to_cpu(prop.value))
282
283 # Convert singla value to array
284 prop2 = self.node.props['intarray']
285 prop.Widen(prop2)
286 self.assertEqual(fdt.TYPE_INT, prop.type)
287 self.assertTrue(isinstance(prop.value, list))
288
289 # A 4-byte array looks like a single integer. When widened by a longer
290 # byte array, it should turn into an array.
291 prop = self.node.props['longbytearray']
292 prop2 = node2.props['longbytearray']
293 self.assertFalse(isinstance(prop2.value, list))
294 self.assertEqual(4, len(prop2.value))
295 prop2.Widen(prop)
296 self.assertTrue(isinstance(prop2.value, list))
297 self.assertEqual(9, len(prop2.value))
298
299 # Similarly for a string array
300 prop = self.node.props['stringval']
301 prop2 = node2.props['stringarray']
302 self.assertFalse(isinstance(prop.value, list))
303 self.assertEqual(7, len(prop.value))
304 prop.Widen(prop2)
305 self.assertTrue(isinstance(prop.value, list))
306 self.assertEqual(3, len(prop.value))
307
308 # Enlarging an existing array
309 prop = self.node.props['stringarray']
310 prop2 = node2.props['stringarray']
311 self.assertTrue(isinstance(prop.value, list))
312 self.assertEqual(2, len(prop.value))
313 prop.Widen(prop2)
314 self.assertTrue(isinstance(prop.value, list))
315 self.assertEqual(3, len(prop.value))
316
317
Simon Glass2a2d91d2018-07-06 10:27:28 -0600318class TestFdtUtil(unittest.TestCase):
319 """Tests for the fdt_util module
320
321 This module will likely be mostly replaced at some point, once upstream
322 libfdt has better Python support. For now, this provides tests for current
323 functionality.
324 """
325 @classmethod
326 def setUpClass(cls):
327 tools.PrepareOutputDir(None)
328
329 def setUp(self):
330 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
331 self.node = self.dtb.GetNode('/spl-test')
332
333 def testGetInt(self):
334 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
335 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
336
337 with self.assertRaises(ValueError) as e:
338 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
339 self.assertIn("property 'intarray' has list value: expecting a single "
340 'integer', str(e.exception))
341
342 def testGetString(self):
343 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
344 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
345 'test'))
346
347 with self.assertRaises(ValueError) as e:
348 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
349 self.assertIn("property 'stringarray' has list value: expecting a "
350 'single string', str(e.exception))
351
352 def testGetBool(self):
353 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
354 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
355 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
356 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
357
358 def testFdtCellsToCpu(self):
359 val = self.node.props['intarray'].value
360 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
361 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
362
363 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
364 node2 = dtb2.GetNode('/test1')
365 val = node2.props['reg'].value
366 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
367
368 def testEnsureCompiled(self):
369 """Test a degenerate case of this function"""
370 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
371 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
372
373 def testGetPlainBytes(self):
374 self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
375
376
377def RunTestCoverage():
378 """Run the tests and check that we get 100% coverage"""
379 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
380 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
381
382
Simon Glass2ba98752018-07-06 10:27:24 -0600383def RunTests(args):
384 """Run all the test we have for the fdt model
385
386 Args:
387 args: List of positional args provided to fdt. This can hold a test
388 name to execute (as in 'fdt -t testFdt', for example)
389 """
390 result = unittest.TestResult()
391 sys.argv = [sys.argv[0]]
392 test_name = args and args[0] or None
Simon Glass2a2d91d2018-07-06 10:27:28 -0600393 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass2ba98752018-07-06 10:27:24 -0600394 if test_name:
395 try:
396 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
397 except AttributeError:
398 continue
399 else:
400 suite = unittest.TestLoader().loadTestsFromTestCase(module)
401 suite.run(result)
402
403 print result
404 for _, err in result.errors:
405 print err
406 for _, err in result.failures:
407 print err
408
409if __name__ != '__main__':
410 sys.exit(1)
411
412parser = OptionParser()
Simon Glass2a2d91d2018-07-06 10:27:28 -0600413parser.add_option('-B', '--build-dir', type='string', default='b',
414 help='Directory containing the build output')
Simon Glass2ba98752018-07-06 10:27:24 -0600415parser.add_option('-t', '--test', action='store_true', dest='test',
416 default=False, help='run tests')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600417parser.add_option('-T', '--test-coverage', action='store_true',
418 default=False, help='run tests and check for 100% coverage')
Simon Glass2ba98752018-07-06 10:27:24 -0600419(options, args) = parser.parse_args()
420
421# Run our meagre tests
422if options.test:
423 RunTests(args)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600424elif options.test_coverage:
425 RunTestCoverage()