blob: 856392b1bd9ad4a46a43cccfa82e997acedc3854 [file] [log] [blame]
Simon Glassfc017822019-10-31 07:42:54 -06001#!/usr/bin/env python3
Simon Glass0ed50752018-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 Glassb8d2daa2019-07-20 12:23:49 -060010import shutil
Simon Glass0ed50752018-07-06 10:27:24 -060011import sys
Simon Glassb8d2daa2019-07-20 12:23:49 -060012import tempfile
Simon Glass0ed50752018-07-06 10:27:24 -060013import unittest
14
15# Bring in the patman libraries
16our_path = os.path.dirname(os.path.realpath(__file__))
Simon Glass42143162020-04-17 18:09:05 -060017sys.path.insert(1, os.path.join(our_path, '..'))
Simon Glass0ed50752018-07-06 10:27:24 -060018
Simon Glassa997ea52020-04-17 18:09:04 -060019from dtoc import fdt
20from dtoc import fdt_util
21from dtoc.fdt_util import fdt32_to_cpu
Simon Glassc9a032c2020-11-08 20:36:17 -070022from fdt import Type, BytesToValue
Simon Glass0ed50752018-07-06 10:27:24 -060023import libfdt
Simon Glassa997ea52020-04-17 18:09:04 -060024from patman import command
25from patman import test_util
26from patman import tools
Simon Glass0ed50752018-07-06 10:27:24 -060027
Simon Glass4df8a0c2018-07-06 10:27:29 -060028def _GetPropertyValue(dtb, node, prop_name):
29 """Low-level function to get the property value based on its offset
30
31 This looks directly in the device tree at the property's offset to find
32 its value. It is useful as a check that the property is in the correct
33 place.
34
35 Args:
36 node: Node to look in
37 prop_name: Property name to find
38
39 Returns:
40 Tuple:
41 Prop object found
42 Value of property as a string (found using property offset)
43 """
44 prop = node.props[prop_name]
45
46 # Add 12, which is sizeof(struct fdt_property), to get to start of data
47 offset = prop.GetOffset() + 12
48 data = dtb.GetContents()[offset:offset + len(prop.value)]
Simon Glass632b84c2020-11-08 20:36:20 -070049 return prop, [chr(x) for x in data]
Simon Glass4df8a0c2018-07-06 10:27:29 -060050
Simon Glass4f4b2402021-02-03 06:00:56 -070051def find_dtb_file(dts_fname):
52 """Locate a test file in the test/ directory
53
54 Args:
55 dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
56
57 Returns:
58 str: Path to the test filename
59 """
60 return os.path.join('tools/dtoc/test', dts_fname)
61
Simon Glass4df8a0c2018-07-06 10:27:29 -060062
Simon Glass0ed50752018-07-06 10:27:24 -060063class TestFdt(unittest.TestCase):
64 """Tests for the Fdt module
65
66 This includes unit tests for some functions and functional tests for the fdt
67 module.
68 """
69 @classmethod
70 def setUpClass(cls):
71 tools.PrepareOutputDir(None)
72
73 @classmethod
74 def tearDownClass(cls):
Simon Glass752e7552018-10-01 21:12:41 -060075 tools.FinaliseOutputDir()
Simon Glass0ed50752018-07-06 10:27:24 -060076
77 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -070078 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -060079
80 def testFdt(self):
81 """Test that we can open an Fdt"""
82 self.dtb.Scan()
83 root = self.dtb.GetRoot()
84 self.assertTrue(isinstance(root, fdt.Node))
85
86 def testGetNode(self):
87 """Test the GetNode() method"""
88 node = self.dtb.GetNode('/spl-test')
89 self.assertTrue(isinstance(node, fdt.Node))
Simon Glass3b9a8292019-07-20 12:23:39 -060090
Simon Glass0ed50752018-07-06 10:27:24 -060091 node = self.dtb.GetNode('/i2c@0/pmic@9')
92 self.assertTrue(isinstance(node, fdt.Node))
93 self.assertEqual('pmic@9', node.name)
Simon Glass9c526332018-07-06 10:27:28 -060094 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass0ed50752018-07-06 10:27:24 -060095
Simon Glass3b9a8292019-07-20 12:23:39 -060096 node = self.dtb.GetNode('/')
97 self.assertTrue(isinstance(node, fdt.Node))
98 self.assertEqual(0, node.Offset())
99
Simon Glass0ed50752018-07-06 10:27:24 -0600100 def testFlush(self):
101 """Check that we can flush the device tree out to its file"""
102 fname = self.dtb._fname
Simon Glass80d54ee2019-05-17 22:00:39 -0600103 with open(fname, 'rb') as fd:
Simon Glass0ed50752018-07-06 10:27:24 -0600104 data = fd.read()
105 os.remove(fname)
106 with self.assertRaises(IOError):
Simon Glass80d54ee2019-05-17 22:00:39 -0600107 open(fname, 'rb')
Simon Glass0ed50752018-07-06 10:27:24 -0600108 self.dtb.Flush()
Simon Glass80d54ee2019-05-17 22:00:39 -0600109 with open(fname, 'rb') as fd:
Simon Glass0ed50752018-07-06 10:27:24 -0600110 data = fd.read()
111
112 def testPack(self):
113 """Test that packing a device tree works"""
114 self.dtb.Pack()
115
116 def testGetFdt(self):
117 """Tetst that we can access the raw device-tree data"""
Simon Glass792d2392018-07-06 10:27:27 -0600118 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass0ed50752018-07-06 10:27:24 -0600119
120 def testGetProps(self):
121 """Tests obtaining a list of properties"""
122 node = self.dtb.GetNode('/spl-test')
123 props = self.dtb.GetProps(node)
124 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
Simon Glass9c526332018-07-06 10:27:28 -0600125 'intarray', 'intval', 'longbytearray', 'notstring',
Simon Glass0ed50752018-07-06 10:27:24 -0600126 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
127 sorted(props.keys()))
128
129 def testCheckError(self):
130 """Tests the ChecKError() function"""
131 with self.assertRaises(ValueError) as e:
Simon Glass9c526332018-07-06 10:27:28 -0600132 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass0ed50752018-07-06 10:27:24 -0600133 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
134
Simon Glasse2d65282018-07-17 13:25:46 -0600135 def testGetFdt(self):
136 node = self.dtb.GetNode('/spl-test')
137 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass0ed50752018-07-06 10:27:24 -0600138
Simon Glass5872f0c2019-05-17 22:00:41 -0600139 def testBytesToValue(self):
140 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glassc9a032c2020-11-08 20:36:17 -0700141 (Type.STRING, ['this', 'is']))
Simon Glass5872f0c2019-05-17 22:00:41 -0600142
Simon Glass0ed50752018-07-06 10:27:24 -0600143class TestNode(unittest.TestCase):
144 """Test operation of the Node class"""
145
146 @classmethod
147 def setUpClass(cls):
148 tools.PrepareOutputDir(None)
149
150 @classmethod
151 def tearDownClass(cls):
Simon Glass752e7552018-10-01 21:12:41 -0600152 tools.FinaliseOutputDir()
Simon Glass0ed50752018-07-06 10:27:24 -0600153
154 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700155 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600156 self.node = self.dtb.GetNode('/spl-test')
Simon Glass244548f2021-03-21 18:24:37 +1300157 self.fdt = self.dtb.GetFdtObj()
Simon Glass0ed50752018-07-06 10:27:24 -0600158
159 def testOffset(self):
160 """Tests that we can obtain the offset of a node"""
161 self.assertTrue(self.node.Offset() > 0)
162
163 def testDelete(self):
164 """Tests that we can delete a property"""
165 node2 = self.dtb.GetNode('/spl-test2')
166 offset1 = node2.Offset()
167 self.node.DeleteProp('intval')
168 offset2 = node2.Offset()
169 self.assertTrue(offset2 < offset1)
170 self.node.DeleteProp('intarray')
171 offset3 = node2.Offset()
172 self.assertTrue(offset3 < offset2)
Simon Glass9c526332018-07-06 10:27:28 -0600173 with self.assertRaises(libfdt.FdtException):
174 self.node.DeleteProp('missing')
Simon Glass0ed50752018-07-06 10:27:24 -0600175
Simon Glass4df8a0c2018-07-06 10:27:29 -0600176 def testDeleteGetOffset(self):
177 """Test that property offset update when properties are deleted"""
178 self.node.DeleteProp('intval')
179 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
180 self.assertEqual(prop.value, value)
181
Simon Glass0ed50752018-07-06 10:27:24 -0600182 def testFindNode(self):
Simon Glassaa1a5d72018-07-17 13:25:41 -0600183 """Tests that we can find a node using the FindNode() functoin"""
184 node = self.dtb.GetRoot().FindNode('i2c@0')
Simon Glass0ed50752018-07-06 10:27:24 -0600185 self.assertEqual('i2c@0', node.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600186 subnode = node.FindNode('pmic@9')
Simon Glass0ed50752018-07-06 10:27:24 -0600187 self.assertEqual('pmic@9', subnode.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600188 self.assertEqual(None, node.FindNode('missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600189
Simon Glass4df8a0c2018-07-06 10:27:29 -0600190 def testRefreshMissingNode(self):
191 """Test refreshing offsets when an extra node is present in dtb"""
192 # Delete it from our tables, not the device tree
193 del self.dtb._root.subnodes[-1]
194 with self.assertRaises(ValueError) as e:
195 self.dtb.Refresh()
196 self.assertIn('Internal error, offset', str(e.exception))
197
198 def testRefreshExtraNode(self):
199 """Test refreshing offsets when an expected node is missing"""
200 # Delete it from the device tre, not our tables
Simon Glass244548f2021-03-21 18:24:37 +1300201 self.fdt.del_node(self.node.Offset())
Simon Glass4df8a0c2018-07-06 10:27:29 -0600202 with self.assertRaises(ValueError) as e:
203 self.dtb.Refresh()
204 self.assertIn('Internal error, node name mismatch '
205 'spl-test != spl-test2', str(e.exception))
206
207 def testRefreshMissingProp(self):
208 """Test refreshing offsets when an extra property is present in dtb"""
209 # Delete it from our tables, not the device tree
210 del self.node.props['notstring']
211 with self.assertRaises(ValueError) as e:
212 self.dtb.Refresh()
Simon Glass93f18a12021-03-21 18:24:34 +1300213 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glass4df8a0c2018-07-06 10:27:29 -0600214 str(e.exception))
215
Simon Glasse2d65282018-07-17 13:25:46 -0600216 def testLookupPhandle(self):
217 """Test looking up a single phandle"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700218 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600219 node = dtb.GetNode('/phandle-source2')
220 prop = node.props['clocks']
221 target = dtb.GetNode('/phandle-target')
222 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
223
Simon Glass244548f2021-03-21 18:24:37 +1300224 def testAddNodeSpace(self):
225 """Test adding a single node when out of space"""
226 self.fdt.pack()
227 self.node.AddSubnode('subnode')
228 with self.assertRaises(libfdt.FdtException) as e:
229 self.dtb.Sync(auto_resize=False)
230 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
231
232 self.dtb.Sync(auto_resize=True)
233 offset = self.fdt.path_offset('/spl-test/subnode')
234 self.assertTrue(offset > 0)
235
236 def testAddNodes(self):
237 """Test adding various subnode and properies"""
238 node = self.dtb.GetNode('/i2c@0')
239
Simon Glass3be798a2021-03-21 18:24:38 +1300240 # Add one more node next to the pmic one
241 sn1 = node.AddSubnode('node-one')
242 sn1.AddInt('integer-a', 12)
243 sn1.AddInt('integer-b', 23)
244
245 # Sync so that everything is clean
246 self.dtb.Sync(auto_resize=True)
247
248 # Add two subnodes next to pmic and node-one
249 sn2 = node.AddSubnode('node-two')
250 sn2.AddInt('integer-2a', 34)
251 sn2.AddInt('integer-2b', 45)
252
253 sn3 = node.AddSubnode('node-three')
254 sn3.AddInt('integer-3', 123)
255
Simon Glass244548f2021-03-21 18:24:37 +1300256 # Add a property to the node after i2c@0 to check that this is not
257 # disturbed by adding a subnode to i2c@0
258 orig_node = self.dtb.GetNode('/orig-node')
259 orig_node.AddInt('integer-4', 456)
260
261 # Add a property to the pmic node to check that pmic properties are not
262 # disturbed
263 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
264 pmic.AddInt('integer-5', 567)
265
266 self.dtb.Sync(auto_resize=True)
267
Simon Glassd8bee462021-03-21 18:24:39 +1300268 def testRefreshNameMismatch(self):
269 """Test name mismatch when syncing nodes and properties"""
270 prop = self.node.AddInt('integer-a', 12)
271
272 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
273 self.node._offset = wrong_offset
274 with self.assertRaises(ValueError) as e:
275 self.dtb.Sync()
276 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
277 str(e.exception))
278
279 with self.assertRaises(ValueError) as e:
280 self.node.Refresh(wrong_offset)
281 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
282 str(e.exception))
283
Simon Glass0ed50752018-07-06 10:27:24 -0600284
285class TestProp(unittest.TestCase):
286 """Test operation of the Prop class"""
287
288 @classmethod
289 def setUpClass(cls):
290 tools.PrepareOutputDir(None)
291
292 @classmethod
293 def tearDownClass(cls):
Simon Glass752e7552018-10-01 21:12:41 -0600294 tools.FinaliseOutputDir()
Simon Glass0ed50752018-07-06 10:27:24 -0600295
296 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700297 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600298 self.node = self.dtb.GetNode('/spl-test')
299 self.fdt = self.dtb.GetFdtObj()
300
Simon Glassc5eddc82018-07-06 10:27:30 -0600301 def testMissingNode(self):
302 self.assertEqual(None, self.dtb.GetNode('missing'))
303
Simon Glass9c526332018-07-06 10:27:28 -0600304 def testPhandle(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700305 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass609e2b12018-07-06 10:27:31 -0600306 node = dtb.GetNode('/phandle-source2')
307 prop = node.props['clocks']
308 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
Simon Glass9c526332018-07-06 10:27:28 -0600309
310 def _ConvertProp(self, prop_name):
311 """Helper function to look up a property in self.node and return it
312
313 Args:
314 Property name to find
315
316 Return fdt.Prop object for this property
317 """
Simon Glassb474c762018-07-26 14:02:13 -0600318 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass9c526332018-07-06 10:27:28 -0600319 return fdt.Prop(self.node, -1, prop_name, p)
320
321 def testMakeProp(self):
322 """Test we can convert all the the types that are supported"""
323 prop = self._ConvertProp('boolval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700324 self.assertEqual(Type.BOOL, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600325 self.assertEqual(True, prop.value)
326
327 prop = self._ConvertProp('intval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700328 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600329 self.assertEqual(1, fdt32_to_cpu(prop.value))
330
331 prop = self._ConvertProp('intarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700332 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600333 val = [fdt32_to_cpu(val) for val in prop.value]
334 self.assertEqual([2, 3, 4], val)
335
336 prop = self._ConvertProp('byteval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700337 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600338 self.assertEqual(5, ord(prop.value))
339
340 prop = self._ConvertProp('longbytearray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700341 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600342 val = [ord(val) for val in prop.value]
343 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
344
345 prop = self._ConvertProp('stringval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700346 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600347 self.assertEqual('message', prop.value)
348
349 prop = self._ConvertProp('stringarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700350 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600351 self.assertEqual(['multi-word', 'message'], prop.value)
352
353 prop = self._ConvertProp('notstring')
Simon Glassc9a032c2020-11-08 20:36:17 -0700354 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600355 val = [ord(val) for val in prop.value]
356 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
357
Simon Glass0ed50752018-07-06 10:27:24 -0600358 def testGetEmpty(self):
359 """Tests the GetEmpty() function for the various supported types"""
Simon Glassc9a032c2020-11-08 20:36:17 -0700360 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
361 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
362 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
363 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass0ed50752018-07-06 10:27:24 -0600364
365 def testGetOffset(self):
366 """Test we can get the offset of a property"""
Simon Glass4df8a0c2018-07-06 10:27:29 -0600367 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
368 self.assertEqual(prop.value, value)
Simon Glass0ed50752018-07-06 10:27:24 -0600369
370 def testWiden(self):
371 """Test widening of values"""
372 node2 = self.dtb.GetNode('/spl-test2')
Simon Glass8034e4d2020-10-03 11:31:27 -0600373 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass0ed50752018-07-06 10:27:24 -0600374 prop = self.node.props['intval']
375
376 # No action
377 prop2 = node2.props['intval']
378 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700379 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600380 self.assertEqual(1, fdt32_to_cpu(prop.value))
381
382 # Convert singla value to array
383 prop2 = self.node.props['intarray']
384 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700385 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600386 self.assertTrue(isinstance(prop.value, list))
387
388 # A 4-byte array looks like a single integer. When widened by a longer
389 # byte array, it should turn into an array.
390 prop = self.node.props['longbytearray']
391 prop2 = node2.props['longbytearray']
Simon Glass8034e4d2020-10-03 11:31:27 -0600392 prop3 = node3.props['longbytearray']
Simon Glass0ed50752018-07-06 10:27:24 -0600393 self.assertFalse(isinstance(prop2.value, list))
394 self.assertEqual(4, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600395 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600396 prop2.Widen(prop)
397 self.assertTrue(isinstance(prop2.value, list))
398 self.assertEqual(9, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600399 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
400 '\0', '\0', '\0', '\0'], prop2.value)
401 prop3.Widen(prop)
402 self.assertTrue(isinstance(prop3.value, list))
403 self.assertEqual(9, len(prop3.value))
404 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
405 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600406
407 # Similarly for a string array
408 prop = self.node.props['stringval']
409 prop2 = node2.props['stringarray']
410 self.assertFalse(isinstance(prop.value, list))
411 self.assertEqual(7, len(prop.value))
412 prop.Widen(prop2)
413 self.assertTrue(isinstance(prop.value, list))
414 self.assertEqual(3, len(prop.value))
415
416 # Enlarging an existing array
417 prop = self.node.props['stringarray']
418 prop2 = node2.props['stringarray']
419 self.assertTrue(isinstance(prop.value, list))
420 self.assertEqual(2, len(prop.value))
421 prop.Widen(prop2)
422 self.assertTrue(isinstance(prop.value, list))
423 self.assertEqual(3, len(prop.value))
424
Simon Glasse80c5562018-07-06 10:27:38 -0600425 def testAdd(self):
426 """Test adding properties"""
427 self.fdt.pack()
428 # This function should automatically expand the device tree
429 self.node.AddZeroProp('one')
430 self.node.AddZeroProp('two')
431 self.node.AddZeroProp('three')
Simon Glasseddd7292018-09-14 04:57:13 -0600432 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600433
434 # Updating existing properties should be OK, since the device-tree size
435 # does not change
436 self.fdt.pack()
437 self.node.SetInt('one', 1)
438 self.node.SetInt('two', 2)
439 self.node.SetInt('three', 3)
Simon Glasseddd7292018-09-14 04:57:13 -0600440 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600441
442 # This should fail since it would need to increase the device-tree size
Simon Glasseddd7292018-09-14 04:57:13 -0600443 self.node.AddZeroProp('four')
Simon Glasse80c5562018-07-06 10:27:38 -0600444 with self.assertRaises(libfdt.FdtException) as e:
Simon Glasseddd7292018-09-14 04:57:13 -0600445 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600446 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glassccd25262018-09-14 04:57:16 -0600447 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600448
Simon Glassccd25262018-09-14 04:57:16 -0600449 def testAddMore(self):
450 """Test various other methods for adding and setting properties"""
451 self.node.AddZeroProp('one')
452 self.dtb.Sync(auto_resize=True)
453 data = self.fdt.getprop(self.node.Offset(), 'one')
454 self.assertEqual(0, fdt32_to_cpu(data))
455
456 self.node.SetInt('one', 1)
457 self.dtb.Sync(auto_resize=False)
458 data = self.fdt.getprop(self.node.Offset(), 'one')
459 self.assertEqual(1, fdt32_to_cpu(data))
460
Simon Glassa2af7302021-01-06 21:35:18 -0700461 val = 1234
462 self.node.AddInt('integer', val)
463 self.dtb.Sync(auto_resize=True)
464 data = self.fdt.getprop(self.node.Offset(), 'integer')
465 self.assertEqual(val, fdt32_to_cpu(data))
466
Simon Glassccd25262018-09-14 04:57:16 -0600467 val = '123' + chr(0) + '456'
468 self.node.AddString('string', val)
469 self.dtb.Sync(auto_resize=True)
470 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass1cd40082019-05-17 22:00:36 -0600471 self.assertEqual(tools.ToBytes(val) + b'\0', data)
Simon Glassccd25262018-09-14 04:57:16 -0600472
473 self.fdt.pack()
474 self.node.SetString('string', val + 'x')
475 with self.assertRaises(libfdt.FdtException) as e:
476 self.dtb.Sync(auto_resize=False)
477 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
478 self.node.SetString('string', val[:-1])
479
480 prop = self.node.props['string']
Simon Glass1cd40082019-05-17 22:00:36 -0600481 prop.SetData(tools.ToBytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600482 self.dtb.Sync(auto_resize=False)
483 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass1cd40082019-05-17 22:00:36 -0600484 self.assertEqual(tools.ToBytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600485
486 self.node.AddEmptyProp('empty', 5)
487 self.dtb.Sync(auto_resize=True)
488 prop = self.node.props['empty']
Simon Glass1cd40082019-05-17 22:00:36 -0600489 prop.SetData(tools.ToBytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600490 self.dtb.Sync(auto_resize=False)
491 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glass1cd40082019-05-17 22:00:36 -0600492 self.assertEqual(tools.ToBytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600493
Simon Glass1cd40082019-05-17 22:00:36 -0600494 self.node.SetData('empty', b'123')
495 self.assertEqual(b'123', prop.bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600496
Simon Glassf67c99c2020-07-09 18:39:44 -0600497 # Trying adding a lot of data at once
498 self.node.AddData('data', tools.GetBytes(65, 20000))
499 self.dtb.Sync(auto_resize=True)
500
Simon Glassb8a49292018-09-14 04:57:17 -0600501 def testFromData(self):
502 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
503 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
504
505 self.node.AddEmptyProp('empty', 5)
506 self.dtb.Sync(auto_resize=True)
507 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
508
Simon Glassa683a5f2019-07-20 12:23:37 -0600509 def testMissingSetInt(self):
510 """Test handling of a missing property with SetInt"""
511 with self.assertRaises(ValueError) as e:
512 self.node.SetInt('one', 1)
513 self.assertIn("node '/spl-test': Missing property 'one'",
514 str(e.exception))
515
516 def testMissingSetData(self):
517 """Test handling of a missing property with SetData"""
518 with self.assertRaises(ValueError) as e:
519 self.node.SetData('one', b'data')
520 self.assertIn("node '/spl-test': Missing property 'one'",
521 str(e.exception))
522
523 def testMissingSetString(self):
524 """Test handling of a missing property with SetString"""
525 with self.assertRaises(ValueError) as e:
526 self.node.SetString('one', 1)
527 self.assertIn("node '/spl-test': Missing property 'one'",
528 str(e.exception))
529
Simon Glass74f5feb2019-07-20 12:24:08 -0600530 def testGetFilename(self):
531 """Test the dtb filename can be provided"""
532 self.assertEqual(tools.GetOutputFilename('source.dtb'),
533 self.dtb.GetFilename())
534
Simon Glass0ed50752018-07-06 10:27:24 -0600535
Simon Glass9c526332018-07-06 10:27:28 -0600536class TestFdtUtil(unittest.TestCase):
537 """Tests for the fdt_util module
538
539 This module will likely be mostly replaced at some point, once upstream
540 libfdt has better Python support. For now, this provides tests for current
541 functionality.
542 """
543 @classmethod
544 def setUpClass(cls):
545 tools.PrepareOutputDir(None)
546
Simon Glass752e7552018-10-01 21:12:41 -0600547 @classmethod
548 def tearDownClass(cls):
549 tools.FinaliseOutputDir()
550
Simon Glass9c526332018-07-06 10:27:28 -0600551 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700552 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600553 self.node = self.dtb.GetNode('/spl-test')
554
555 def testGetInt(self):
556 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
557 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
558
559 with self.assertRaises(ValueError) as e:
560 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
561 self.assertIn("property 'intarray' has list value: expecting a single "
562 'integer', str(e.exception))
563
564 def testGetString(self):
565 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
566 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
567 'test'))
568
569 with self.assertRaises(ValueError) as e:
570 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
571 self.assertIn("property 'stringarray' has list value: expecting a "
572 'single string', str(e.exception))
573
574 def testGetBool(self):
575 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
576 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
577 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
578 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
579
Simon Glass53f53992018-07-17 13:25:40 -0600580 def testGetByte(self):
581 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
582 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
583
584 with self.assertRaises(ValueError) as e:
585 fdt_util.GetByte(self.node, 'longbytearray')
586 self.assertIn("property 'longbytearray' has list value: expecting a "
587 'single byte', str(e.exception))
588
589 with self.assertRaises(ValueError) as e:
590 fdt_util.GetByte(self.node, 'intval')
591 self.assertIn("property 'intval' has length 4, expecting 1",
592 str(e.exception))
593
Simon Glasse2d65282018-07-17 13:25:46 -0600594 def testGetPhandleList(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700595 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600596 node = dtb.GetNode('/phandle-source2')
597 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
598 node = dtb.GetNode('/phandle-source')
599 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
600 fdt_util.GetPhandleList(node, 'clocks'))
601 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
602
Simon Glass91710b32018-07-17 13:25:32 -0600603 def testGetDataType(self):
604 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
605 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
606 str))
607 with self.assertRaises(ValueError) as e:
608 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
609 bool))
Simon Glass9c526332018-07-06 10:27:28 -0600610 def testFdtCellsToCpu(self):
611 val = self.node.props['intarray'].value
612 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
613 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
614
Simon Glass4f4b2402021-02-03 06:00:56 -0700615 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glassb0a34a42019-05-17 22:00:40 -0600616 node1 = dtb2.GetNode('/test1')
617 val = node1.props['reg'].value
Simon Glass9c526332018-07-06 10:27:28 -0600618 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
619
Simon Glassb0a34a42019-05-17 22:00:40 -0600620 node2 = dtb2.GetNode('/test2')
621 val = node2.props['reg'].value
622 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
623 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
624 2))
625 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
626
Simon Glass9c526332018-07-06 10:27:28 -0600627 def testEnsureCompiled(self):
Simon Glassb8d2daa2019-07-20 12:23:49 -0600628 """Test a degenerate case of this function (file already compiled)"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700629 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600630 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
631
Simon Glassb8d2daa2019-07-20 12:23:49 -0600632 def testEnsureCompiledTmpdir(self):
633 """Test providing a temporary directory"""
634 try:
635 old_outdir = tools.outdir
636 tools.outdir= None
637 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glass4f4b2402021-02-03 06:00:56 -0700638 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassb8d2daa2019-07-20 12:23:49 -0600639 tmpdir)
640 self.assertEqual(tmpdir, os.path.dirname(dtb))
641 shutil.rmtree(tmpdir)
642 finally:
643 tools.outdir= old_outdir
644
Simon Glass9c526332018-07-06 10:27:28 -0600645
646def RunTestCoverage():
647 """Run the tests and check that we get 100% coverage"""
648 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
649 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
650
651
Simon Glass0ed50752018-07-06 10:27:24 -0600652def RunTests(args):
653 """Run all the test we have for the fdt model
654
655 Args:
656 args: List of positional args provided to fdt. This can hold a test
657 name to execute (as in 'fdt -t testFdt', for example)
658 """
659 result = unittest.TestResult()
660 sys.argv = [sys.argv[0]]
661 test_name = args and args[0] or None
Simon Glass9c526332018-07-06 10:27:28 -0600662 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass0ed50752018-07-06 10:27:24 -0600663 if test_name:
664 try:
665 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
666 except AttributeError:
667 continue
668 else:
669 suite = unittest.TestLoader().loadTestsFromTestCase(module)
670 suite.run(result)
671
Simon Glass61b88e52019-05-17 22:00:31 -0600672 print(result)
Simon Glass0ed50752018-07-06 10:27:24 -0600673 for _, err in result.errors:
Simon Glass61b88e52019-05-17 22:00:31 -0600674 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600675 for _, err in result.failures:
Simon Glass61b88e52019-05-17 22:00:31 -0600676 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600677
678if __name__ != '__main__':
679 sys.exit(1)
680
681parser = OptionParser()
Simon Glass9c526332018-07-06 10:27:28 -0600682parser.add_option('-B', '--build-dir', type='string', default='b',
683 help='Directory containing the build output')
Simon Glass7057d022018-10-01 21:12:47 -0600684parser.add_option('-P', '--processes', type=int,
685 help='set number of processes to use for running tests')
Simon Glass0ed50752018-07-06 10:27:24 -0600686parser.add_option('-t', '--test', action='store_true', dest='test',
687 default=False, help='run tests')
Simon Glass9c526332018-07-06 10:27:28 -0600688parser.add_option('-T', '--test-coverage', action='store_true',
689 default=False, help='run tests and check for 100% coverage')
Simon Glass0ed50752018-07-06 10:27:24 -0600690(options, args) = parser.parse_args()
691
692# Run our meagre tests
693if options.test:
694 RunTests(args)
Simon Glass9c526332018-07-06 10:27:28 -0600695elif options.test_coverage:
696 RunTestCoverage()