blob: d86fc86187ef646341826d6055fd8d307422889b [file] [log] [blame]
Simon Glassf7ba5f02019-10-31 07:42:54 -06001#!/usr/bin/env python3
Simon Glass2ba98752018-07-06 10:27:24 -06002# 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
Simon Glassa004f292019-07-20 12:23:49 -060010import shutil
Simon Glass2ba98752018-07-06 10:27:24 -060011import sys
Simon Glassa004f292019-07-20 12:23:49 -060012import tempfile
Simon Glass2ba98752018-07-06 10:27:24 -060013import unittest
14
15# Bring in the patman libraries
16our_path = os.path.dirname(os.path.realpath(__file__))
Simon Glassb4fa9492020-04-17 18:09:05 -060017sys.path.insert(1, os.path.join(our_path, '..'))
Simon Glass2ba98752018-07-06 10:27:24 -060018
Simon Glassff139b62021-11-23 11:03:38 -070019# Bring in the libfdt module
20sys.path.insert(2, 'scripts/dtc/pylibfdt')
21sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
22sys.path.insert(2, os.path.join(our_path,
23 '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
24
Simon Glassbf776672020-04-17 18:09:04 -060025from dtoc import fdt
26from dtoc import fdt_util
27from dtoc.fdt_util import fdt32_to_cpu
Simon Glass5ea9dcc2020-11-08 20:36:17 -070028from fdt import Type, BytesToValue
Simon Glass2ba98752018-07-06 10:27:24 -060029import libfdt
Simon Glassbf776672020-04-17 18:09:04 -060030from patman import command
31from patman import test_util
32from patman import tools
Simon Glass2ba98752018-07-06 10:27:24 -060033
Simon Glassf9b88b32018-07-06 10:27:29 -060034def _GetPropertyValue(dtb, node, prop_name):
35 """Low-level function to get the property value based on its offset
36
37 This looks directly in the device tree at the property's offset to find
38 its value. It is useful as a check that the property is in the correct
39 place.
40
41 Args:
42 node: Node to look in
43 prop_name: Property name to find
44
45 Returns:
46 Tuple:
47 Prop object found
48 Value of property as a string (found using property offset)
49 """
50 prop = node.props[prop_name]
51
52 # Add 12, which is sizeof(struct fdt_property), to get to start of data
53 offset = prop.GetOffset() + 12
54 data = dtb.GetContents()[offset:offset + len(prop.value)]
Simon Glass479dd302020-11-08 20:36:20 -070055 return prop, [chr(x) for x in data]
Simon Glassf9b88b32018-07-06 10:27:29 -060056
Simon Glassdff51a52021-02-03 06:00:56 -070057def find_dtb_file(dts_fname):
58 """Locate a test file in the test/ directory
59
60 Args:
61 dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
62
63 Returns:
64 str: Path to the test filename
65 """
66 return os.path.join('tools/dtoc/test', dts_fname)
67
Simon Glassf9b88b32018-07-06 10:27:29 -060068
Simon Glass2ba98752018-07-06 10:27:24 -060069class TestFdt(unittest.TestCase):
70 """Tests for the Fdt module
71
72 This includes unit tests for some functions and functional tests for the fdt
73 module.
74 """
75 @classmethod
76 def setUpClass(cls):
77 tools.PrepareOutputDir(None)
78
79 @classmethod
80 def tearDownClass(cls):
Simon Glasse0e62752018-10-01 21:12:41 -060081 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -060082
83 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -070084 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -060085
86 def testFdt(self):
87 """Test that we can open an Fdt"""
88 self.dtb.Scan()
89 root = self.dtb.GetRoot()
90 self.assertTrue(isinstance(root, fdt.Node))
91
92 def testGetNode(self):
93 """Test the GetNode() method"""
94 node = self.dtb.GetNode('/spl-test')
95 self.assertTrue(isinstance(node, fdt.Node))
Simon Glasse44bc832019-07-20 12:23:39 -060096
Simon Glass2ba98752018-07-06 10:27:24 -060097 node = self.dtb.GetNode('/i2c@0/pmic@9')
98 self.assertTrue(isinstance(node, fdt.Node))
99 self.assertEqual('pmic@9', node.name)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600100 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600101
Simon Glasse44bc832019-07-20 12:23:39 -0600102 node = self.dtb.GetNode('/')
103 self.assertTrue(isinstance(node, fdt.Node))
104 self.assertEqual(0, node.Offset())
105
Simon Glass2ba98752018-07-06 10:27:24 -0600106 def testFlush(self):
107 """Check that we can flush the device tree out to its file"""
108 fname = self.dtb._fname
Simon Glass2ab6e132019-05-17 22:00:39 -0600109 with open(fname, 'rb') as fd:
Simon Glass2ba98752018-07-06 10:27:24 -0600110 data = fd.read()
111 os.remove(fname)
112 with self.assertRaises(IOError):
Simon Glass2ab6e132019-05-17 22:00:39 -0600113 open(fname, 'rb')
Simon Glass2ba98752018-07-06 10:27:24 -0600114 self.dtb.Flush()
Simon Glass2ab6e132019-05-17 22:00:39 -0600115 with open(fname, 'rb') as fd:
Simon Glass2ba98752018-07-06 10:27:24 -0600116 data = fd.read()
117
118 def testPack(self):
119 """Test that packing a device tree works"""
120 self.dtb.Pack()
121
122 def testGetFdt(self):
123 """Tetst that we can access the raw device-tree data"""
Simon Glass96066242018-07-06 10:27:27 -0600124 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass2ba98752018-07-06 10:27:24 -0600125
126 def testGetProps(self):
127 """Tests obtaining a list of properties"""
128 node = self.dtb.GetNode('/spl-test')
129 props = self.dtb.GetProps(node)
130 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
Simon Glasseec44c72021-07-28 19:23:11 -0600131 'intarray', 'intval', 'longbytearray',
132 'maybe-empty-int', 'notstring', 'stringarray',
133 'stringval', 'u-boot,dm-pre-reloc'],
Simon Glass2ba98752018-07-06 10:27:24 -0600134 sorted(props.keys()))
135
136 def testCheckError(self):
137 """Tests the ChecKError() function"""
138 with self.assertRaises(ValueError) as e:
Simon Glass2a2d91d2018-07-06 10:27:28 -0600139 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass2ba98752018-07-06 10:27:24 -0600140 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
141
Simon Glass94a7c602018-07-17 13:25:46 -0600142 def testGetFdt(self):
143 node = self.dtb.GetNode('/spl-test')
144 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass2ba98752018-07-06 10:27:24 -0600145
Simon Glassb5f0daf2019-05-17 22:00:41 -0600146 def testBytesToValue(self):
147 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700148 (Type.STRING, ['this', 'is']))
Simon Glassb5f0daf2019-05-17 22:00:41 -0600149
Simon Glass2ba98752018-07-06 10:27:24 -0600150class TestNode(unittest.TestCase):
151 """Test operation of the Node class"""
152
153 @classmethod
154 def setUpClass(cls):
155 tools.PrepareOutputDir(None)
156
157 @classmethod
158 def tearDownClass(cls):
Simon Glasse0e62752018-10-01 21:12:41 -0600159 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -0600160
161 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700162 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600163 self.node = self.dtb.GetNode('/spl-test')
Simon Glass76677dd2021-03-21 18:24:37 +1300164 self.fdt = self.dtb.GetFdtObj()
Simon Glass2ba98752018-07-06 10:27:24 -0600165
166 def testOffset(self):
167 """Tests that we can obtain the offset of a node"""
168 self.assertTrue(self.node.Offset() > 0)
169
170 def testDelete(self):
171 """Tests that we can delete a property"""
172 node2 = self.dtb.GetNode('/spl-test2')
173 offset1 = node2.Offset()
174 self.node.DeleteProp('intval')
175 offset2 = node2.Offset()
176 self.assertTrue(offset2 < offset1)
177 self.node.DeleteProp('intarray')
178 offset3 = node2.Offset()
179 self.assertTrue(offset3 < offset2)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600180 with self.assertRaises(libfdt.FdtException):
181 self.node.DeleteProp('missing')
Simon Glass2ba98752018-07-06 10:27:24 -0600182
Simon Glassf9b88b32018-07-06 10:27:29 -0600183 def testDeleteGetOffset(self):
184 """Test that property offset update when properties are deleted"""
185 self.node.DeleteProp('intval')
186 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
187 self.assertEqual(prop.value, value)
188
Simon Glass2ba98752018-07-06 10:27:24 -0600189 def testFindNode(self):
Simon Glass1d858882018-07-17 13:25:41 -0600190 """Tests that we can find a node using the FindNode() functoin"""
191 node = self.dtb.GetRoot().FindNode('i2c@0')
Simon Glass2ba98752018-07-06 10:27:24 -0600192 self.assertEqual('i2c@0', node.name)
Simon Glass1d858882018-07-17 13:25:41 -0600193 subnode = node.FindNode('pmic@9')
Simon Glass2ba98752018-07-06 10:27:24 -0600194 self.assertEqual('pmic@9', subnode.name)
Simon Glass1d858882018-07-17 13:25:41 -0600195 self.assertEqual(None, node.FindNode('missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600196
Simon Glassf9b88b32018-07-06 10:27:29 -0600197 def testRefreshMissingNode(self):
198 """Test refreshing offsets when an extra node is present in dtb"""
199 # Delete it from our tables, not the device tree
200 del self.dtb._root.subnodes[-1]
201 with self.assertRaises(ValueError) as e:
202 self.dtb.Refresh()
203 self.assertIn('Internal error, offset', str(e.exception))
204
205 def testRefreshExtraNode(self):
206 """Test refreshing offsets when an expected node is missing"""
207 # Delete it from the device tre, not our tables
Simon Glass76677dd2021-03-21 18:24:37 +1300208 self.fdt.del_node(self.node.Offset())
Simon Glassf9b88b32018-07-06 10:27:29 -0600209 with self.assertRaises(ValueError) as e:
210 self.dtb.Refresh()
211 self.assertIn('Internal error, node name mismatch '
212 'spl-test != spl-test2', str(e.exception))
213
214 def testRefreshMissingProp(self):
215 """Test refreshing offsets when an extra property is present in dtb"""
216 # Delete it from our tables, not the device tree
217 del self.node.props['notstring']
218 with self.assertRaises(ValueError) as e:
219 self.dtb.Refresh()
Simon Glassacd98612021-03-21 18:24:34 +1300220 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glassf9b88b32018-07-06 10:27:29 -0600221 str(e.exception))
222
Simon Glass94a7c602018-07-17 13:25:46 -0600223 def testLookupPhandle(self):
224 """Test looking up a single phandle"""
Simon Glassdff51a52021-02-03 06:00:56 -0700225 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600226 node = dtb.GetNode('/phandle-source2')
227 prop = node.props['clocks']
228 target = dtb.GetNode('/phandle-target')
229 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
230
Simon Glass76677dd2021-03-21 18:24:37 +1300231 def testAddNodeSpace(self):
232 """Test adding a single node when out of space"""
233 self.fdt.pack()
234 self.node.AddSubnode('subnode')
235 with self.assertRaises(libfdt.FdtException) as e:
236 self.dtb.Sync(auto_resize=False)
237 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
238
239 self.dtb.Sync(auto_resize=True)
240 offset = self.fdt.path_offset('/spl-test/subnode')
241 self.assertTrue(offset > 0)
242
243 def testAddNodes(self):
244 """Test adding various subnode and properies"""
245 node = self.dtb.GetNode('/i2c@0')
246
Simon Glassf6176652021-03-21 18:24:38 +1300247 # Add one more node next to the pmic one
248 sn1 = node.AddSubnode('node-one')
249 sn1.AddInt('integer-a', 12)
250 sn1.AddInt('integer-b', 23)
251
252 # Sync so that everything is clean
253 self.dtb.Sync(auto_resize=True)
254
255 # Add two subnodes next to pmic and node-one
256 sn2 = node.AddSubnode('node-two')
257 sn2.AddInt('integer-2a', 34)
258 sn2.AddInt('integer-2b', 45)
259
260 sn3 = node.AddSubnode('node-three')
261 sn3.AddInt('integer-3', 123)
262
Simon Glass76677dd2021-03-21 18:24:37 +1300263 # Add a property to the node after i2c@0 to check that this is not
264 # disturbed by adding a subnode to i2c@0
265 orig_node = self.dtb.GetNode('/orig-node')
266 orig_node.AddInt('integer-4', 456)
267
268 # Add a property to the pmic node to check that pmic properties are not
269 # disturbed
270 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
271 pmic.AddInt('integer-5', 567)
272
273 self.dtb.Sync(auto_resize=True)
274
Simon Glass5d1bec32021-03-21 18:24:39 +1300275 def testRefreshNameMismatch(self):
276 """Test name mismatch when syncing nodes and properties"""
277 prop = self.node.AddInt('integer-a', 12)
278
279 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
280 self.node._offset = wrong_offset
281 with self.assertRaises(ValueError) as e:
282 self.dtb.Sync()
283 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
284 str(e.exception))
285
286 with self.assertRaises(ValueError) as e:
287 self.node.Refresh(wrong_offset)
288 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
289 str(e.exception))
290
Simon Glass2ba98752018-07-06 10:27:24 -0600291
292class TestProp(unittest.TestCase):
293 """Test operation of the Prop class"""
294
295 @classmethod
296 def setUpClass(cls):
297 tools.PrepareOutputDir(None)
298
299 @classmethod
300 def tearDownClass(cls):
Simon Glasse0e62752018-10-01 21:12:41 -0600301 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -0600302
303 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700304 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600305 self.node = self.dtb.GetNode('/spl-test')
306 self.fdt = self.dtb.GetFdtObj()
307
Simon Glassb9066ff2018-07-06 10:27:30 -0600308 def testMissingNode(self):
309 self.assertEqual(None, self.dtb.GetNode('missing'))
310
Simon Glass2a2d91d2018-07-06 10:27:28 -0600311 def testPhandle(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700312 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass760b7172018-07-06 10:27:31 -0600313 node = dtb.GetNode('/phandle-source2')
314 prop = node.props['clocks']
315 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600316
317 def _ConvertProp(self, prop_name):
318 """Helper function to look up a property in self.node and return it
319
320 Args:
321 Property name to find
322
323 Return fdt.Prop object for this property
324 """
Simon Glass50c59522018-07-26 14:02:13 -0600325 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600326 return fdt.Prop(self.node, -1, prop_name, p)
327
328 def testMakeProp(self):
329 """Test we can convert all the the types that are supported"""
330 prop = self._ConvertProp('boolval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700331 self.assertEqual(Type.BOOL, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600332 self.assertEqual(True, prop.value)
333
334 prop = self._ConvertProp('intval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700335 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600336 self.assertEqual(1, fdt32_to_cpu(prop.value))
337
338 prop = self._ConvertProp('intarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700339 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600340 val = [fdt32_to_cpu(val) for val in prop.value]
341 self.assertEqual([2, 3, 4], val)
342
343 prop = self._ConvertProp('byteval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700344 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600345 self.assertEqual(5, ord(prop.value))
346
347 prop = self._ConvertProp('longbytearray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700348 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600349 val = [ord(val) for val in prop.value]
350 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
351
352 prop = self._ConvertProp('stringval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700353 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600354 self.assertEqual('message', prop.value)
355
356 prop = self._ConvertProp('stringarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700357 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600358 self.assertEqual(['multi-word', 'message'], prop.value)
359
360 prop = self._ConvertProp('notstring')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700361 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600362 val = [ord(val) for val in prop.value]
363 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
364
Simon Glass2ba98752018-07-06 10:27:24 -0600365 def testGetEmpty(self):
366 """Tests the GetEmpty() function for the various supported types"""
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700367 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
368 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
369 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
370 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass2ba98752018-07-06 10:27:24 -0600371
372 def testGetOffset(self):
373 """Test we can get the offset of a property"""
Simon Glassf9b88b32018-07-06 10:27:29 -0600374 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
375 self.assertEqual(prop.value, value)
Simon Glass2ba98752018-07-06 10:27:24 -0600376
377 def testWiden(self):
378 """Test widening of values"""
379 node2 = self.dtb.GetNode('/spl-test2')
Simon Glasse144caf2020-10-03 11:31:27 -0600380 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass2ba98752018-07-06 10:27:24 -0600381 prop = self.node.props['intval']
382
383 # No action
384 prop2 = node2.props['intval']
385 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700386 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600387 self.assertEqual(1, fdt32_to_cpu(prop.value))
388
Simon Glassca044942021-07-28 19:23:10 -0600389 # Convert single value to array
Simon Glass2ba98752018-07-06 10:27:24 -0600390 prop2 = self.node.props['intarray']
391 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700392 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600393 self.assertTrue(isinstance(prop.value, list))
394
395 # A 4-byte array looks like a single integer. When widened by a longer
396 # byte array, it should turn into an array.
397 prop = self.node.props['longbytearray']
398 prop2 = node2.props['longbytearray']
Simon Glasse144caf2020-10-03 11:31:27 -0600399 prop3 = node3.props['longbytearray']
Simon Glass2ba98752018-07-06 10:27:24 -0600400 self.assertFalse(isinstance(prop2.value, list))
401 self.assertEqual(4, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600402 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600403 prop2.Widen(prop)
404 self.assertTrue(isinstance(prop2.value, list))
405 self.assertEqual(9, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600406 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
407 '\0', '\0', '\0', '\0'], prop2.value)
408 prop3.Widen(prop)
409 self.assertTrue(isinstance(prop3.value, list))
410 self.assertEqual(9, len(prop3.value))
411 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
412 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600413
414 # Similarly for a string array
415 prop = self.node.props['stringval']
416 prop2 = node2.props['stringarray']
417 self.assertFalse(isinstance(prop.value, list))
418 self.assertEqual(7, len(prop.value))
419 prop.Widen(prop2)
420 self.assertTrue(isinstance(prop.value, list))
421 self.assertEqual(3, len(prop.value))
422
423 # Enlarging an existing array
424 prop = self.node.props['stringarray']
425 prop2 = node2.props['stringarray']
426 self.assertTrue(isinstance(prop.value, list))
427 self.assertEqual(2, len(prop.value))
428 prop.Widen(prop2)
429 self.assertTrue(isinstance(prop.value, list))
430 self.assertEqual(3, len(prop.value))
431
Simon Glassca044942021-07-28 19:23:10 -0600432 # Widen an array of ints with an int (should do nothing)
433 prop = self.node.props['intarray']
Simon Glasse679f392021-08-02 07:37:54 -0600434 prop2 = node2.props['intval']
Simon Glassca044942021-07-28 19:23:10 -0600435 self.assertEqual(Type.INT, prop.type)
436 self.assertEqual(3, len(prop.value))
437 prop.Widen(prop2)
438 self.assertEqual(Type.INT, prop.type)
439 self.assertEqual(3, len(prop.value))
440
Simon Glasseec44c72021-07-28 19:23:11 -0600441 # Widen an empty bool to an int
442 prop = self.node.props['maybe-empty-int']
443 prop3 = node3.props['maybe-empty-int']
444 self.assertEqual(Type.BOOL, prop.type)
445 self.assertEqual(True, prop.value)
446 self.assertEqual(Type.INT, prop3.type)
447 self.assertFalse(isinstance(prop.value, list))
448 self.assertEqual(4, len(prop3.value))
449 prop.Widen(prop3)
450 self.assertEqual(Type.INT, prop.type)
451 self.assertTrue(isinstance(prop.value, list))
452 self.assertEqual(1, len(prop.value))
453
Simon Glass116adec2018-07-06 10:27:38 -0600454 def testAdd(self):
455 """Test adding properties"""
456 self.fdt.pack()
457 # This function should automatically expand the device tree
458 self.node.AddZeroProp('one')
459 self.node.AddZeroProp('two')
460 self.node.AddZeroProp('three')
Simon Glassfa80c252018-09-14 04:57:13 -0600461 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600462
463 # Updating existing properties should be OK, since the device-tree size
464 # does not change
465 self.fdt.pack()
466 self.node.SetInt('one', 1)
467 self.node.SetInt('two', 2)
468 self.node.SetInt('three', 3)
Simon Glassfa80c252018-09-14 04:57:13 -0600469 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600470
471 # This should fail since it would need to increase the device-tree size
Simon Glassfa80c252018-09-14 04:57:13 -0600472 self.node.AddZeroProp('four')
Simon Glass116adec2018-07-06 10:27:38 -0600473 with self.assertRaises(libfdt.FdtException) as e:
Simon Glassfa80c252018-09-14 04:57:13 -0600474 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600475 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glass64349612018-09-14 04:57:16 -0600476 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600477
Simon Glass64349612018-09-14 04:57:16 -0600478 def testAddMore(self):
479 """Test various other methods for adding and setting properties"""
480 self.node.AddZeroProp('one')
481 self.dtb.Sync(auto_resize=True)
482 data = self.fdt.getprop(self.node.Offset(), 'one')
483 self.assertEqual(0, fdt32_to_cpu(data))
484
485 self.node.SetInt('one', 1)
486 self.dtb.Sync(auto_resize=False)
487 data = self.fdt.getprop(self.node.Offset(), 'one')
488 self.assertEqual(1, fdt32_to_cpu(data))
489
Simon Glass6eb99322021-01-06 21:35:18 -0700490 val = 1234
491 self.node.AddInt('integer', val)
492 self.dtb.Sync(auto_resize=True)
493 data = self.fdt.getprop(self.node.Offset(), 'integer')
494 self.assertEqual(val, fdt32_to_cpu(data))
495
Simon Glass64349612018-09-14 04:57:16 -0600496 val = '123' + chr(0) + '456'
497 self.node.AddString('string', val)
498 self.dtb.Sync(auto_resize=True)
499 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassf6b64812019-05-17 22:00:36 -0600500 self.assertEqual(tools.ToBytes(val) + b'\0', data)
Simon Glass64349612018-09-14 04:57:16 -0600501
502 self.fdt.pack()
503 self.node.SetString('string', val + 'x')
504 with self.assertRaises(libfdt.FdtException) as e:
505 self.dtb.Sync(auto_resize=False)
506 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
507 self.node.SetString('string', val[:-1])
508
509 prop = self.node.props['string']
Simon Glassf6b64812019-05-17 22:00:36 -0600510 prop.SetData(tools.ToBytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600511 self.dtb.Sync(auto_resize=False)
512 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassf6b64812019-05-17 22:00:36 -0600513 self.assertEqual(tools.ToBytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600514
515 self.node.AddEmptyProp('empty', 5)
516 self.dtb.Sync(auto_resize=True)
517 prop = self.node.props['empty']
Simon Glassf6b64812019-05-17 22:00:36 -0600518 prop.SetData(tools.ToBytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600519 self.dtb.Sync(auto_resize=False)
520 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glassf6b64812019-05-17 22:00:36 -0600521 self.assertEqual(tools.ToBytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600522
Simon Glassf6b64812019-05-17 22:00:36 -0600523 self.node.SetData('empty', b'123')
524 self.assertEqual(b'123', prop.bytes)
Simon Glass64349612018-09-14 04:57:16 -0600525
Simon Glassc0639172020-07-09 18:39:44 -0600526 # Trying adding a lot of data at once
527 self.node.AddData('data', tools.GetBytes(65, 20000))
528 self.dtb.Sync(auto_resize=True)
529
Simon Glass746aee32018-09-14 04:57:17 -0600530 def testFromData(self):
531 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
532 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
533
534 self.node.AddEmptyProp('empty', 5)
535 self.dtb.Sync(auto_resize=True)
536 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
537
Simon Glassd9dad102019-07-20 12:23:37 -0600538 def testMissingSetInt(self):
539 """Test handling of a missing property with SetInt"""
540 with self.assertRaises(ValueError) as e:
541 self.node.SetInt('one', 1)
542 self.assertIn("node '/spl-test': Missing property 'one'",
543 str(e.exception))
544
545 def testMissingSetData(self):
546 """Test handling of a missing property with SetData"""
547 with self.assertRaises(ValueError) as e:
548 self.node.SetData('one', b'data')
549 self.assertIn("node '/spl-test': Missing property 'one'",
550 str(e.exception))
551
552 def testMissingSetString(self):
553 """Test handling of a missing property with SetString"""
554 with self.assertRaises(ValueError) as e:
555 self.node.SetString('one', 1)
556 self.assertIn("node '/spl-test': Missing property 'one'",
557 str(e.exception))
558
Simon Glassf6e02492019-07-20 12:24:08 -0600559 def testGetFilename(self):
560 """Test the dtb filename can be provided"""
561 self.assertEqual(tools.GetOutputFilename('source.dtb'),
562 self.dtb.GetFilename())
563
Simon Glass2ba98752018-07-06 10:27:24 -0600564
Simon Glass2a2d91d2018-07-06 10:27:28 -0600565class TestFdtUtil(unittest.TestCase):
566 """Tests for the fdt_util module
567
568 This module will likely be mostly replaced at some point, once upstream
569 libfdt has better Python support. For now, this provides tests for current
570 functionality.
571 """
572 @classmethod
573 def setUpClass(cls):
574 tools.PrepareOutputDir(None)
575
Simon Glasse0e62752018-10-01 21:12:41 -0600576 @classmethod
577 def tearDownClass(cls):
578 tools.FinaliseOutputDir()
579
Simon Glass2a2d91d2018-07-06 10:27:28 -0600580 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700581 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600582 self.node = self.dtb.GetNode('/spl-test')
583
584 def testGetInt(self):
585 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
586 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
587
588 with self.assertRaises(ValueError) as e:
589 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
590 self.assertIn("property 'intarray' has list value: expecting a single "
591 'integer', str(e.exception))
592
593 def testGetString(self):
594 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
595 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
596 'test'))
597
598 with self.assertRaises(ValueError) as e:
599 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
600 self.assertIn("property 'stringarray' has list value: expecting a "
601 'single string', str(e.exception))
602
603 def testGetBool(self):
604 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
605 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
606 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
607 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
608
Simon Glass3af8e492018-07-17 13:25:40 -0600609 def testGetByte(self):
610 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
611 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
612
613 with self.assertRaises(ValueError) as e:
614 fdt_util.GetByte(self.node, 'longbytearray')
615 self.assertIn("property 'longbytearray' has list value: expecting a "
616 'single byte', str(e.exception))
617
618 with self.assertRaises(ValueError) as e:
619 fdt_util.GetByte(self.node, 'intval')
620 self.assertIn("property 'intval' has length 4, expecting 1",
621 str(e.exception))
622
Simon Glass94a7c602018-07-17 13:25:46 -0600623 def testGetPhandleList(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700624 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600625 node = dtb.GetNode('/phandle-source2')
626 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
627 node = dtb.GetNode('/phandle-source')
628 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
629 fdt_util.GetPhandleList(node, 'clocks'))
630 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
631
Simon Glass53af22a2018-07-17 13:25:32 -0600632 def testGetDataType(self):
633 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
634 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
635 str))
636 with self.assertRaises(ValueError) as e:
637 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
638 bool))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600639 def testFdtCellsToCpu(self):
640 val = self.node.props['intarray'].value
641 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
642 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
643
Simon Glassdff51a52021-02-03 06:00:56 -0700644 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glasse66d3182019-05-17 22:00:40 -0600645 node1 = dtb2.GetNode('/test1')
646 val = node1.props['reg'].value
Simon Glass2a2d91d2018-07-06 10:27:28 -0600647 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
648
Simon Glasse66d3182019-05-17 22:00:40 -0600649 node2 = dtb2.GetNode('/test2')
650 val = node2.props['reg'].value
651 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
652 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
653 2))
654 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
655
Simon Glass2a2d91d2018-07-06 10:27:28 -0600656 def testEnsureCompiled(self):
Simon Glassa004f292019-07-20 12:23:49 -0600657 """Test a degenerate case of this function (file already compiled)"""
Simon Glassdff51a52021-02-03 06:00:56 -0700658 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600659 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
660
Simon Glassa004f292019-07-20 12:23:49 -0600661 def testEnsureCompiledTmpdir(self):
662 """Test providing a temporary directory"""
663 try:
664 old_outdir = tools.outdir
665 tools.outdir= None
666 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glassdff51a52021-02-03 06:00:56 -0700667 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassa004f292019-07-20 12:23:49 -0600668 tmpdir)
669 self.assertEqual(tmpdir, os.path.dirname(dtb))
670 shutil.rmtree(tmpdir)
671 finally:
672 tools.outdir= old_outdir
673
Simon Glass2a2d91d2018-07-06 10:27:28 -0600674
675def RunTestCoverage():
676 """Run the tests and check that we get 100% coverage"""
677 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
678 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
679
680
Simon Glass2ba98752018-07-06 10:27:24 -0600681def RunTests(args):
682 """Run all the test we have for the fdt model
683
684 Args:
685 args: List of positional args provided to fdt. This can hold a test
686 name to execute (as in 'fdt -t testFdt', for example)
687 """
688 result = unittest.TestResult()
689 sys.argv = [sys.argv[0]]
690 test_name = args and args[0] or None
Simon Glass2a2d91d2018-07-06 10:27:28 -0600691 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass2ba98752018-07-06 10:27:24 -0600692 if test_name:
693 try:
694 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
695 except AttributeError:
696 continue
697 else:
698 suite = unittest.TestLoader().loadTestsFromTestCase(module)
699 suite.run(result)
700
Simon Glass90a81322019-05-17 22:00:31 -0600701 print(result)
Simon Glass2ba98752018-07-06 10:27:24 -0600702 for _, err in result.errors:
Simon Glass90a81322019-05-17 22:00:31 -0600703 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600704 for _, err in result.failures:
Simon Glass90a81322019-05-17 22:00:31 -0600705 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600706
707if __name__ != '__main__':
708 sys.exit(1)
709
710parser = OptionParser()
Simon Glass2a2d91d2018-07-06 10:27:28 -0600711parser.add_option('-B', '--build-dir', type='string', default='b',
712 help='Directory containing the build output')
Simon Glass11ae93e2018-10-01 21:12:47 -0600713parser.add_option('-P', '--processes', type=int,
714 help='set number of processes to use for running tests')
Simon Glass2ba98752018-07-06 10:27:24 -0600715parser.add_option('-t', '--test', action='store_true', dest='test',
716 default=False, help='run tests')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600717parser.add_option('-T', '--test-coverage', action='store_true',
718 default=False, help='run tests and check for 100% coverage')
Simon Glass2ba98752018-07-06 10:27:24 -0600719(options, args) = parser.parse_args()
720
721# Run our meagre tests
722if options.test:
723 RunTests(args)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600724elif options.test_coverage:
725 RunTestCoverage()