blob: d104f3c77450154442dee201784e93242f0632c2 [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 Glass43118322021-07-28 19:23:11 -0600125 'intarray', 'intval', 'longbytearray',
126 'maybe-empty-int', 'notstring', 'stringarray',
127 'stringval', 'u-boot,dm-pre-reloc'],
Simon Glass0ed50752018-07-06 10:27:24 -0600128 sorted(props.keys()))
129
130 def testCheckError(self):
131 """Tests the ChecKError() function"""
132 with self.assertRaises(ValueError) as e:
Simon Glass9c526332018-07-06 10:27:28 -0600133 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass0ed50752018-07-06 10:27:24 -0600134 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
135
Simon Glasse2d65282018-07-17 13:25:46 -0600136 def testGetFdt(self):
137 node = self.dtb.GetNode('/spl-test')
138 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass0ed50752018-07-06 10:27:24 -0600139
Simon Glass5872f0c2019-05-17 22:00:41 -0600140 def testBytesToValue(self):
141 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glassc9a032c2020-11-08 20:36:17 -0700142 (Type.STRING, ['this', 'is']))
Simon Glass5872f0c2019-05-17 22:00:41 -0600143
Simon Glass0ed50752018-07-06 10:27:24 -0600144class TestNode(unittest.TestCase):
145 """Test operation of the Node class"""
146
147 @classmethod
148 def setUpClass(cls):
149 tools.PrepareOutputDir(None)
150
151 @classmethod
152 def tearDownClass(cls):
Simon Glass752e7552018-10-01 21:12:41 -0600153 tools.FinaliseOutputDir()
Simon Glass0ed50752018-07-06 10:27:24 -0600154
155 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700156 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600157 self.node = self.dtb.GetNode('/spl-test')
Simon Glass244548f2021-03-21 18:24:37 +1300158 self.fdt = self.dtb.GetFdtObj()
Simon Glass0ed50752018-07-06 10:27:24 -0600159
160 def testOffset(self):
161 """Tests that we can obtain the offset of a node"""
162 self.assertTrue(self.node.Offset() > 0)
163
164 def testDelete(self):
165 """Tests that we can delete a property"""
166 node2 = self.dtb.GetNode('/spl-test2')
167 offset1 = node2.Offset()
168 self.node.DeleteProp('intval')
169 offset2 = node2.Offset()
170 self.assertTrue(offset2 < offset1)
171 self.node.DeleteProp('intarray')
172 offset3 = node2.Offset()
173 self.assertTrue(offset3 < offset2)
Simon Glass9c526332018-07-06 10:27:28 -0600174 with self.assertRaises(libfdt.FdtException):
175 self.node.DeleteProp('missing')
Simon Glass0ed50752018-07-06 10:27:24 -0600176
Simon Glass4df8a0c2018-07-06 10:27:29 -0600177 def testDeleteGetOffset(self):
178 """Test that property offset update when properties are deleted"""
179 self.node.DeleteProp('intval')
180 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
181 self.assertEqual(prop.value, value)
182
Simon Glass0ed50752018-07-06 10:27:24 -0600183 def testFindNode(self):
Simon Glassaa1a5d72018-07-17 13:25:41 -0600184 """Tests that we can find a node using the FindNode() functoin"""
185 node = self.dtb.GetRoot().FindNode('i2c@0')
Simon Glass0ed50752018-07-06 10:27:24 -0600186 self.assertEqual('i2c@0', node.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600187 subnode = node.FindNode('pmic@9')
Simon Glass0ed50752018-07-06 10:27:24 -0600188 self.assertEqual('pmic@9', subnode.name)
Simon Glassaa1a5d72018-07-17 13:25:41 -0600189 self.assertEqual(None, node.FindNode('missing'))
Simon Glass0ed50752018-07-06 10:27:24 -0600190
Simon Glass4df8a0c2018-07-06 10:27:29 -0600191 def testRefreshMissingNode(self):
192 """Test refreshing offsets when an extra node is present in dtb"""
193 # Delete it from our tables, not the device tree
194 del self.dtb._root.subnodes[-1]
195 with self.assertRaises(ValueError) as e:
196 self.dtb.Refresh()
197 self.assertIn('Internal error, offset', str(e.exception))
198
199 def testRefreshExtraNode(self):
200 """Test refreshing offsets when an expected node is missing"""
201 # Delete it from the device tre, not our tables
Simon Glass244548f2021-03-21 18:24:37 +1300202 self.fdt.del_node(self.node.Offset())
Simon Glass4df8a0c2018-07-06 10:27:29 -0600203 with self.assertRaises(ValueError) as e:
204 self.dtb.Refresh()
205 self.assertIn('Internal error, node name mismatch '
206 'spl-test != spl-test2', str(e.exception))
207
208 def testRefreshMissingProp(self):
209 """Test refreshing offsets when an extra property is present in dtb"""
210 # Delete it from our tables, not the device tree
211 del self.node.props['notstring']
212 with self.assertRaises(ValueError) as e:
213 self.dtb.Refresh()
Simon Glass93f18a12021-03-21 18:24:34 +1300214 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glass4df8a0c2018-07-06 10:27:29 -0600215 str(e.exception))
216
Simon Glasse2d65282018-07-17 13:25:46 -0600217 def testLookupPhandle(self):
218 """Test looking up a single phandle"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700219 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600220 node = dtb.GetNode('/phandle-source2')
221 prop = node.props['clocks']
222 target = dtb.GetNode('/phandle-target')
223 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
224
Simon Glass244548f2021-03-21 18:24:37 +1300225 def testAddNodeSpace(self):
226 """Test adding a single node when out of space"""
227 self.fdt.pack()
228 self.node.AddSubnode('subnode')
229 with self.assertRaises(libfdt.FdtException) as e:
230 self.dtb.Sync(auto_resize=False)
231 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
232
233 self.dtb.Sync(auto_resize=True)
234 offset = self.fdt.path_offset('/spl-test/subnode')
235 self.assertTrue(offset > 0)
236
237 def testAddNodes(self):
238 """Test adding various subnode and properies"""
239 node = self.dtb.GetNode('/i2c@0')
240
Simon Glass3be798a2021-03-21 18:24:38 +1300241 # Add one more node next to the pmic one
242 sn1 = node.AddSubnode('node-one')
243 sn1.AddInt('integer-a', 12)
244 sn1.AddInt('integer-b', 23)
245
246 # Sync so that everything is clean
247 self.dtb.Sync(auto_resize=True)
248
249 # Add two subnodes next to pmic and node-one
250 sn2 = node.AddSubnode('node-two')
251 sn2.AddInt('integer-2a', 34)
252 sn2.AddInt('integer-2b', 45)
253
254 sn3 = node.AddSubnode('node-three')
255 sn3.AddInt('integer-3', 123)
256
Simon Glass244548f2021-03-21 18:24:37 +1300257 # Add a property to the node after i2c@0 to check that this is not
258 # disturbed by adding a subnode to i2c@0
259 orig_node = self.dtb.GetNode('/orig-node')
260 orig_node.AddInt('integer-4', 456)
261
262 # Add a property to the pmic node to check that pmic properties are not
263 # disturbed
264 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
265 pmic.AddInt('integer-5', 567)
266
267 self.dtb.Sync(auto_resize=True)
268
Simon Glassd8bee462021-03-21 18:24:39 +1300269 def testRefreshNameMismatch(self):
270 """Test name mismatch when syncing nodes and properties"""
271 prop = self.node.AddInt('integer-a', 12)
272
273 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
274 self.node._offset = wrong_offset
275 with self.assertRaises(ValueError) as e:
276 self.dtb.Sync()
277 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
278 str(e.exception))
279
280 with self.assertRaises(ValueError) as e:
281 self.node.Refresh(wrong_offset)
282 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
283 str(e.exception))
284
Simon Glass0ed50752018-07-06 10:27:24 -0600285
286class TestProp(unittest.TestCase):
287 """Test operation of the Prop class"""
288
289 @classmethod
290 def setUpClass(cls):
291 tools.PrepareOutputDir(None)
292
293 @classmethod
294 def tearDownClass(cls):
Simon Glass752e7552018-10-01 21:12:41 -0600295 tools.FinaliseOutputDir()
Simon Glass0ed50752018-07-06 10:27:24 -0600296
297 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700298 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass0ed50752018-07-06 10:27:24 -0600299 self.node = self.dtb.GetNode('/spl-test')
300 self.fdt = self.dtb.GetFdtObj()
301
Simon Glassc5eddc82018-07-06 10:27:30 -0600302 def testMissingNode(self):
303 self.assertEqual(None, self.dtb.GetNode('missing'))
304
Simon Glass9c526332018-07-06 10:27:28 -0600305 def testPhandle(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700306 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass609e2b12018-07-06 10:27:31 -0600307 node = dtb.GetNode('/phandle-source2')
308 prop = node.props['clocks']
309 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
Simon Glass9c526332018-07-06 10:27:28 -0600310
311 def _ConvertProp(self, prop_name):
312 """Helper function to look up a property in self.node and return it
313
314 Args:
315 Property name to find
316
317 Return fdt.Prop object for this property
318 """
Simon Glassb474c762018-07-26 14:02:13 -0600319 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass9c526332018-07-06 10:27:28 -0600320 return fdt.Prop(self.node, -1, prop_name, p)
321
322 def testMakeProp(self):
323 """Test we can convert all the the types that are supported"""
324 prop = self._ConvertProp('boolval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700325 self.assertEqual(Type.BOOL, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600326 self.assertEqual(True, prop.value)
327
328 prop = self._ConvertProp('intval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700329 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600330 self.assertEqual(1, fdt32_to_cpu(prop.value))
331
332 prop = self._ConvertProp('intarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700333 self.assertEqual(Type.INT, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600334 val = [fdt32_to_cpu(val) for val in prop.value]
335 self.assertEqual([2, 3, 4], val)
336
337 prop = self._ConvertProp('byteval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700338 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600339 self.assertEqual(5, ord(prop.value))
340
341 prop = self._ConvertProp('longbytearray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700342 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600343 val = [ord(val) for val in prop.value]
344 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
345
346 prop = self._ConvertProp('stringval')
Simon Glassc9a032c2020-11-08 20:36:17 -0700347 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600348 self.assertEqual('message', prop.value)
349
350 prop = self._ConvertProp('stringarray')
Simon Glassc9a032c2020-11-08 20:36:17 -0700351 self.assertEqual(Type.STRING, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600352 self.assertEqual(['multi-word', 'message'], prop.value)
353
354 prop = self._ConvertProp('notstring')
Simon Glassc9a032c2020-11-08 20:36:17 -0700355 self.assertEqual(Type.BYTE, prop.type)
Simon Glass9c526332018-07-06 10:27:28 -0600356 val = [ord(val) for val in prop.value]
357 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
358
Simon Glass0ed50752018-07-06 10:27:24 -0600359 def testGetEmpty(self):
360 """Tests the GetEmpty() function for the various supported types"""
Simon Glassc9a032c2020-11-08 20:36:17 -0700361 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
362 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
363 self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
364 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
Simon Glass0ed50752018-07-06 10:27:24 -0600365
366 def testGetOffset(self):
367 """Test we can get the offset of a property"""
Simon Glass4df8a0c2018-07-06 10:27:29 -0600368 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
369 self.assertEqual(prop.value, value)
Simon Glass0ed50752018-07-06 10:27:24 -0600370
371 def testWiden(self):
372 """Test widening of values"""
373 node2 = self.dtb.GetNode('/spl-test2')
Simon Glass8034e4d2020-10-03 11:31:27 -0600374 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass0ed50752018-07-06 10:27:24 -0600375 prop = self.node.props['intval']
376
377 # No action
378 prop2 = node2.props['intval']
379 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700380 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600381 self.assertEqual(1, fdt32_to_cpu(prop.value))
382
Simon Glassa7d66982021-07-28 19:23:10 -0600383 # Convert single value to array
Simon Glass0ed50752018-07-06 10:27:24 -0600384 prop2 = self.node.props['intarray']
385 prop.Widen(prop2)
Simon Glassc9a032c2020-11-08 20:36:17 -0700386 self.assertEqual(Type.INT, prop.type)
Simon Glass0ed50752018-07-06 10:27:24 -0600387 self.assertTrue(isinstance(prop.value, list))
388
389 # A 4-byte array looks like a single integer. When widened by a longer
390 # byte array, it should turn into an array.
391 prop = self.node.props['longbytearray']
392 prop2 = node2.props['longbytearray']
Simon Glass8034e4d2020-10-03 11:31:27 -0600393 prop3 = node3.props['longbytearray']
Simon Glass0ed50752018-07-06 10:27:24 -0600394 self.assertFalse(isinstance(prop2.value, list))
395 self.assertEqual(4, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600396 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600397 prop2.Widen(prop)
398 self.assertTrue(isinstance(prop2.value, list))
399 self.assertEqual(9, len(prop2.value))
Simon Glass8034e4d2020-10-03 11:31:27 -0600400 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
401 '\0', '\0', '\0', '\0'], prop2.value)
402 prop3.Widen(prop)
403 self.assertTrue(isinstance(prop3.value, list))
404 self.assertEqual(9, len(prop3.value))
405 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
406 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
Simon Glass0ed50752018-07-06 10:27:24 -0600407
408 # Similarly for a string array
409 prop = self.node.props['stringval']
410 prop2 = node2.props['stringarray']
411 self.assertFalse(isinstance(prop.value, list))
412 self.assertEqual(7, len(prop.value))
413 prop.Widen(prop2)
414 self.assertTrue(isinstance(prop.value, list))
415 self.assertEqual(3, len(prop.value))
416
417 # Enlarging an existing array
418 prop = self.node.props['stringarray']
419 prop2 = node2.props['stringarray']
420 self.assertTrue(isinstance(prop.value, list))
421 self.assertEqual(2, len(prop.value))
422 prop.Widen(prop2)
423 self.assertTrue(isinstance(prop.value, list))
424 self.assertEqual(3, len(prop.value))
425
Simon Glassa7d66982021-07-28 19:23:10 -0600426 # Widen an array of ints with an int (should do nothing)
427 prop = self.node.props['intarray']
Simon Glassf42f26e2021-08-02 07:37:54 -0600428 prop2 = node2.props['intval']
Simon Glassa7d66982021-07-28 19:23:10 -0600429 self.assertEqual(Type.INT, prop.type)
430 self.assertEqual(3, len(prop.value))
431 prop.Widen(prop2)
432 self.assertEqual(Type.INT, prop.type)
433 self.assertEqual(3, len(prop.value))
434
Simon Glass43118322021-07-28 19:23:11 -0600435 # Widen an empty bool to an int
436 prop = self.node.props['maybe-empty-int']
437 prop3 = node3.props['maybe-empty-int']
438 self.assertEqual(Type.BOOL, prop.type)
439 self.assertEqual(True, prop.value)
440 self.assertEqual(Type.INT, prop3.type)
441 self.assertFalse(isinstance(prop.value, list))
442 self.assertEqual(4, len(prop3.value))
443 prop.Widen(prop3)
444 self.assertEqual(Type.INT, prop.type)
445 self.assertTrue(isinstance(prop.value, list))
446 self.assertEqual(1, len(prop.value))
447
Simon Glasse80c5562018-07-06 10:27:38 -0600448 def testAdd(self):
449 """Test adding properties"""
450 self.fdt.pack()
451 # This function should automatically expand the device tree
452 self.node.AddZeroProp('one')
453 self.node.AddZeroProp('two')
454 self.node.AddZeroProp('three')
Simon Glasseddd7292018-09-14 04:57:13 -0600455 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600456
457 # Updating existing properties should be OK, since the device-tree size
458 # does not change
459 self.fdt.pack()
460 self.node.SetInt('one', 1)
461 self.node.SetInt('two', 2)
462 self.node.SetInt('three', 3)
Simon Glasseddd7292018-09-14 04:57:13 -0600463 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600464
465 # This should fail since it would need to increase the device-tree size
Simon Glasseddd7292018-09-14 04:57:13 -0600466 self.node.AddZeroProp('four')
Simon Glasse80c5562018-07-06 10:27:38 -0600467 with self.assertRaises(libfdt.FdtException) as e:
Simon Glasseddd7292018-09-14 04:57:13 -0600468 self.dtb.Sync(auto_resize=False)
Simon Glasse80c5562018-07-06 10:27:38 -0600469 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glassccd25262018-09-14 04:57:16 -0600470 self.dtb.Sync(auto_resize=True)
Simon Glasse80c5562018-07-06 10:27:38 -0600471
Simon Glassccd25262018-09-14 04:57:16 -0600472 def testAddMore(self):
473 """Test various other methods for adding and setting properties"""
474 self.node.AddZeroProp('one')
475 self.dtb.Sync(auto_resize=True)
476 data = self.fdt.getprop(self.node.Offset(), 'one')
477 self.assertEqual(0, fdt32_to_cpu(data))
478
479 self.node.SetInt('one', 1)
480 self.dtb.Sync(auto_resize=False)
481 data = self.fdt.getprop(self.node.Offset(), 'one')
482 self.assertEqual(1, fdt32_to_cpu(data))
483
Simon Glassa2af7302021-01-06 21:35:18 -0700484 val = 1234
485 self.node.AddInt('integer', val)
486 self.dtb.Sync(auto_resize=True)
487 data = self.fdt.getprop(self.node.Offset(), 'integer')
488 self.assertEqual(val, fdt32_to_cpu(data))
489
Simon Glassccd25262018-09-14 04:57:16 -0600490 val = '123' + chr(0) + '456'
491 self.node.AddString('string', val)
492 self.dtb.Sync(auto_resize=True)
493 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass1cd40082019-05-17 22:00:36 -0600494 self.assertEqual(tools.ToBytes(val) + b'\0', data)
Simon Glassccd25262018-09-14 04:57:16 -0600495
496 self.fdt.pack()
497 self.node.SetString('string', val + 'x')
498 with self.assertRaises(libfdt.FdtException) as e:
499 self.dtb.Sync(auto_resize=False)
500 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
501 self.node.SetString('string', val[:-1])
502
503 prop = self.node.props['string']
Simon Glass1cd40082019-05-17 22:00:36 -0600504 prop.SetData(tools.ToBytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600505 self.dtb.Sync(auto_resize=False)
506 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glass1cd40082019-05-17 22:00:36 -0600507 self.assertEqual(tools.ToBytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600508
509 self.node.AddEmptyProp('empty', 5)
510 self.dtb.Sync(auto_resize=True)
511 prop = self.node.props['empty']
Simon Glass1cd40082019-05-17 22:00:36 -0600512 prop.SetData(tools.ToBytes(val))
Simon Glassccd25262018-09-14 04:57:16 -0600513 self.dtb.Sync(auto_resize=False)
514 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glass1cd40082019-05-17 22:00:36 -0600515 self.assertEqual(tools.ToBytes(val), data)
Simon Glassccd25262018-09-14 04:57:16 -0600516
Simon Glass1cd40082019-05-17 22:00:36 -0600517 self.node.SetData('empty', b'123')
518 self.assertEqual(b'123', prop.bytes)
Simon Glassccd25262018-09-14 04:57:16 -0600519
Simon Glassf67c99c2020-07-09 18:39:44 -0600520 # Trying adding a lot of data at once
521 self.node.AddData('data', tools.GetBytes(65, 20000))
522 self.dtb.Sync(auto_resize=True)
523
Simon Glassb8a49292018-09-14 04:57:17 -0600524 def testFromData(self):
525 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
526 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
527
528 self.node.AddEmptyProp('empty', 5)
529 self.dtb.Sync(auto_resize=True)
530 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
531
Simon Glassa683a5f2019-07-20 12:23:37 -0600532 def testMissingSetInt(self):
533 """Test handling of a missing property with SetInt"""
534 with self.assertRaises(ValueError) as e:
535 self.node.SetInt('one', 1)
536 self.assertIn("node '/spl-test': Missing property 'one'",
537 str(e.exception))
538
539 def testMissingSetData(self):
540 """Test handling of a missing property with SetData"""
541 with self.assertRaises(ValueError) as e:
542 self.node.SetData('one', b'data')
543 self.assertIn("node '/spl-test': Missing property 'one'",
544 str(e.exception))
545
546 def testMissingSetString(self):
547 """Test handling of a missing property with SetString"""
548 with self.assertRaises(ValueError) as e:
549 self.node.SetString('one', 1)
550 self.assertIn("node '/spl-test': Missing property 'one'",
551 str(e.exception))
552
Simon Glass74f5feb2019-07-20 12:24:08 -0600553 def testGetFilename(self):
554 """Test the dtb filename can be provided"""
555 self.assertEqual(tools.GetOutputFilename('source.dtb'),
556 self.dtb.GetFilename())
557
Simon Glass0ed50752018-07-06 10:27:24 -0600558
Simon Glass9c526332018-07-06 10:27:28 -0600559class TestFdtUtil(unittest.TestCase):
560 """Tests for the fdt_util module
561
562 This module will likely be mostly replaced at some point, once upstream
563 libfdt has better Python support. For now, this provides tests for current
564 functionality.
565 """
566 @classmethod
567 def setUpClass(cls):
568 tools.PrepareOutputDir(None)
569
Simon Glass752e7552018-10-01 21:12:41 -0600570 @classmethod
571 def tearDownClass(cls):
572 tools.FinaliseOutputDir()
573
Simon Glass9c526332018-07-06 10:27:28 -0600574 def setUp(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700575 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600576 self.node = self.dtb.GetNode('/spl-test')
577
578 def testGetInt(self):
579 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
580 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
581
582 with self.assertRaises(ValueError) as e:
583 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
584 self.assertIn("property 'intarray' has list value: expecting a single "
585 'integer', str(e.exception))
586
587 def testGetString(self):
588 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
589 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
590 'test'))
591
592 with self.assertRaises(ValueError) as e:
593 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
594 self.assertIn("property 'stringarray' has list value: expecting a "
595 'single string', str(e.exception))
596
597 def testGetBool(self):
598 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
599 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
600 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
601 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
602
Simon Glass53f53992018-07-17 13:25:40 -0600603 def testGetByte(self):
604 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
605 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
606
607 with self.assertRaises(ValueError) as e:
608 fdt_util.GetByte(self.node, 'longbytearray')
609 self.assertIn("property 'longbytearray' has list value: expecting a "
610 'single byte', str(e.exception))
611
612 with self.assertRaises(ValueError) as e:
613 fdt_util.GetByte(self.node, 'intval')
614 self.assertIn("property 'intval' has length 4, expecting 1",
615 str(e.exception))
616
Simon Glasse2d65282018-07-17 13:25:46 -0600617 def testGetPhandleList(self):
Simon Glass4f4b2402021-02-03 06:00:56 -0700618 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glasse2d65282018-07-17 13:25:46 -0600619 node = dtb.GetNode('/phandle-source2')
620 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
621 node = dtb.GetNode('/phandle-source')
622 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
623 fdt_util.GetPhandleList(node, 'clocks'))
624 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
625
Simon Glass91710b32018-07-17 13:25:32 -0600626 def testGetDataType(self):
627 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
628 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
629 str))
630 with self.assertRaises(ValueError) as e:
631 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
632 bool))
Simon Glass9c526332018-07-06 10:27:28 -0600633 def testFdtCellsToCpu(self):
634 val = self.node.props['intarray'].value
635 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
636 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
637
Simon Glass4f4b2402021-02-03 06:00:56 -0700638 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glassb0a34a42019-05-17 22:00:40 -0600639 node1 = dtb2.GetNode('/test1')
640 val = node1.props['reg'].value
Simon Glass9c526332018-07-06 10:27:28 -0600641 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
642
Simon Glassb0a34a42019-05-17 22:00:40 -0600643 node2 = dtb2.GetNode('/test2')
644 val = node2.props['reg'].value
645 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
646 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
647 2))
648 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
649
Simon Glass9c526332018-07-06 10:27:28 -0600650 def testEnsureCompiled(self):
Simon Glassb8d2daa2019-07-20 12:23:49 -0600651 """Test a degenerate case of this function (file already compiled)"""
Simon Glass4f4b2402021-02-03 06:00:56 -0700652 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass9c526332018-07-06 10:27:28 -0600653 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
654
Simon Glassb8d2daa2019-07-20 12:23:49 -0600655 def testEnsureCompiledTmpdir(self):
656 """Test providing a temporary directory"""
657 try:
658 old_outdir = tools.outdir
659 tools.outdir= None
660 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glass4f4b2402021-02-03 06:00:56 -0700661 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassb8d2daa2019-07-20 12:23:49 -0600662 tmpdir)
663 self.assertEqual(tmpdir, os.path.dirname(dtb))
664 shutil.rmtree(tmpdir)
665 finally:
666 tools.outdir= old_outdir
667
Simon Glass9c526332018-07-06 10:27:28 -0600668
669def RunTestCoverage():
670 """Run the tests and check that we get 100% coverage"""
671 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
672 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
673
674
Simon Glass0ed50752018-07-06 10:27:24 -0600675def RunTests(args):
676 """Run all the test we have for the fdt model
677
678 Args:
679 args: List of positional args provided to fdt. This can hold a test
680 name to execute (as in 'fdt -t testFdt', for example)
681 """
682 result = unittest.TestResult()
683 sys.argv = [sys.argv[0]]
684 test_name = args and args[0] or None
Simon Glass9c526332018-07-06 10:27:28 -0600685 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass0ed50752018-07-06 10:27:24 -0600686 if test_name:
687 try:
688 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
689 except AttributeError:
690 continue
691 else:
692 suite = unittest.TestLoader().loadTestsFromTestCase(module)
693 suite.run(result)
694
Simon Glass61b88e52019-05-17 22:00:31 -0600695 print(result)
Simon Glass0ed50752018-07-06 10:27:24 -0600696 for _, err in result.errors:
Simon Glass61b88e52019-05-17 22:00:31 -0600697 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600698 for _, err in result.failures:
Simon Glass61b88e52019-05-17 22:00:31 -0600699 print(err)
Simon Glass0ed50752018-07-06 10:27:24 -0600700
701if __name__ != '__main__':
702 sys.exit(1)
703
704parser = OptionParser()
Simon Glass9c526332018-07-06 10:27:28 -0600705parser.add_option('-B', '--build-dir', type='string', default='b',
706 help='Directory containing the build output')
Simon Glass7057d022018-10-01 21:12:47 -0600707parser.add_option('-P', '--processes', type=int,
708 help='set number of processes to use for running tests')
Simon Glass0ed50752018-07-06 10:27:24 -0600709parser.add_option('-t', '--test', action='store_true', dest='test',
710 default=False, help='run tests')
Simon Glass9c526332018-07-06 10:27:28 -0600711parser.add_option('-T', '--test-coverage', action='store_true',
712 default=False, help='run tests and check for 100% coverage')
Simon Glass0ed50752018-07-06 10:27:24 -0600713(options, args) = parser.parse_args()
714
715# Run our meagre tests
716if options.test:
717 RunTests(args)
Simon Glass9c526332018-07-06 10:27:28 -0600718elif options.test_coverage:
719 RunTestCoverage()