blob: 857861c14edfea3b0f2dc73cf91cf2f0852b709b [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 Glassbf776672020-04-17 18:09:04 -060019from dtoc import fdt
20from dtoc import fdt_util
21from dtoc.fdt_util import fdt32_to_cpu
Simon Glass5ea9dcc2020-11-08 20:36:17 -070022from fdt import Type, BytesToValue
Simon Glass2ba98752018-07-06 10:27:24 -060023import libfdt
Simon Glassbf776672020-04-17 18:09:04 -060024from patman import command
25from patman import test_util
26from patman import tools
Simon Glass2ba98752018-07-06 10:27:24 -060027
Simon Glassf9b88b32018-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 Glass479dd302020-11-08 20:36:20 -070049 return prop, [chr(x) for x in data]
Simon Glassf9b88b32018-07-06 10:27:29 -060050
Simon Glassdff51a52021-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 Glassf9b88b32018-07-06 10:27:29 -060062
Simon Glass2ba98752018-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 Glasse0e62752018-10-01 21:12:41 -060075 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -060076
77 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -070078 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-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 Glasse44bc832019-07-20 12:23:39 -060090
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -060094 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass2ba98752018-07-06 10:27:24 -060095
Simon Glasse44bc832019-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 Glass2ba98752018-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 Glass2ab6e132019-05-17 22:00:39 -0600103 with open(fname, 'rb') as fd:
Simon Glass2ba98752018-07-06 10:27:24 -0600104 data = fd.read()
105 os.remove(fname)
106 with self.assertRaises(IOError):
Simon Glass2ab6e132019-05-17 22:00:39 -0600107 open(fname, 'rb')
Simon Glass2ba98752018-07-06 10:27:24 -0600108 self.dtb.Flush()
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
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 Glass96066242018-07-06 10:27:27 -0600118 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -0600125 'intarray', 'intval', 'longbytearray', 'notstring',
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -0600132 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass2ba98752018-07-06 10:27:24 -0600133 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
134
Simon Glass94a7c602018-07-17 13:25:46 -0600135 def testGetFdt(self):
136 node = self.dtb.GetNode('/spl-test')
137 self.assertEqual(self.dtb, node.GetFdt())
Simon Glass2ba98752018-07-06 10:27:24 -0600138
Simon Glassb5f0daf2019-05-17 22:00:41 -0600139 def testBytesToValue(self):
140 self.assertEqual(BytesToValue(b'this\0is\0'),
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700141 (Type.STRING, ['this', 'is']))
Simon Glassb5f0daf2019-05-17 22:00:41 -0600142
Simon Glass2ba98752018-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 Glasse0e62752018-10-01 21:12:41 -0600152 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -0600153
154 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700155 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600156 self.node = self.dtb.GetNode('/spl-test')
Simon Glass76677dd2021-03-21 18:24:37 +1300157 self.fdt = self.dtb.GetFdtObj()
Simon Glass2ba98752018-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 Glass2a2d91d2018-07-06 10:27:28 -0600173 with self.assertRaises(libfdt.FdtException):
174 self.node.DeleteProp('missing')
Simon Glass2ba98752018-07-06 10:27:24 -0600175
Simon Glassf9b88b32018-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 Glass2ba98752018-07-06 10:27:24 -0600182 def testFindNode(self):
Simon Glass1d858882018-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 Glass2ba98752018-07-06 10:27:24 -0600185 self.assertEqual('i2c@0', node.name)
Simon Glass1d858882018-07-17 13:25:41 -0600186 subnode = node.FindNode('pmic@9')
Simon Glass2ba98752018-07-06 10:27:24 -0600187 self.assertEqual('pmic@9', subnode.name)
Simon Glass1d858882018-07-17 13:25:41 -0600188 self.assertEqual(None, node.FindNode('missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600189
Simon Glassf9b88b32018-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 Glass76677dd2021-03-21 18:24:37 +1300201 self.fdt.del_node(self.node.Offset())
Simon Glassf9b88b32018-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 Glassacd98612021-03-21 18:24:34 +1300213 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
Simon Glassf9b88b32018-07-06 10:27:29 -0600214 str(e.exception))
215
Simon Glass94a7c602018-07-17 13:25:46 -0600216 def testLookupPhandle(self):
217 """Test looking up a single phandle"""
Simon Glassdff51a52021-02-03 06:00:56 -0700218 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-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 Glass76677dd2021-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 Glassf6176652021-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 Glass76677dd2021-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 Glass5d1bec32021-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 Glass2ba98752018-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 Glasse0e62752018-10-01 21:12:41 -0600294 tools.FinaliseOutputDir()
Simon Glass2ba98752018-07-06 10:27:24 -0600295
296 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700297 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2ba98752018-07-06 10:27:24 -0600298 self.node = self.dtb.GetNode('/spl-test')
299 self.fdt = self.dtb.GetFdtObj()
300
Simon Glassb9066ff2018-07-06 10:27:30 -0600301 def testMissingNode(self):
302 self.assertEqual(None, self.dtb.GetNode('missing'))
303
Simon Glass2a2d91d2018-07-06 10:27:28 -0600304 def testPhandle(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700305 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass760b7172018-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 Glass2a2d91d2018-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 Glass50c59522018-07-26 14:02:13 -0600318 p = self.fdt.getprop(self.node.Offset(), prop_name)
Simon Glass2a2d91d2018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700324 self.assertEqual(Type.BOOL, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600325 self.assertEqual(True, prop.value)
326
327 prop = self._ConvertProp('intval')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700328 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600329 self.assertEqual(1, fdt32_to_cpu(prop.value))
330
331 prop = self._ConvertProp('intarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700332 self.assertEqual(Type.INT, prop.type)
Simon Glass2a2d91d2018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700337 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600338 self.assertEqual(5, ord(prop.value))
339
340 prop = self._ConvertProp('longbytearray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700341 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700346 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600347 self.assertEqual('message', prop.value)
348
349 prop = self._ConvertProp('stringarray')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700350 self.assertEqual(Type.STRING, prop.type)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600351 self.assertEqual(['multi-word', 'message'], prop.value)
352
353 prop = self._ConvertProp('notstring')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700354 self.assertEqual(Type.BYTE, prop.type)
Simon Glass2a2d91d2018-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 Glass2ba98752018-07-06 10:27:24 -0600358 def testGetEmpty(self):
359 """Tests the GetEmpty() function for the various supported types"""
Simon Glass5ea9dcc2020-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 Glass2ba98752018-07-06 10:27:24 -0600364
365 def testGetOffset(self):
366 """Test we can get the offset of a property"""
Simon Glassf9b88b32018-07-06 10:27:29 -0600367 prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray')
368 self.assertEqual(prop.value, value)
Simon Glass2ba98752018-07-06 10:27:24 -0600369
370 def testWiden(self):
371 """Test widening of values"""
372 node2 = self.dtb.GetNode('/spl-test2')
Simon Glasse144caf2020-10-03 11:31:27 -0600373 node3 = self.dtb.GetNode('/spl-test3')
Simon Glass2ba98752018-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 Glass5ea9dcc2020-11-08 20:36:17 -0700379 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-07-06 10:27:24 -0600380 self.assertEqual(1, fdt32_to_cpu(prop.value))
381
Simon Glassca044942021-07-28 19:23:10 -0600382 # Convert single value to array
Simon Glass2ba98752018-07-06 10:27:24 -0600383 prop2 = self.node.props['intarray']
384 prop.Widen(prop2)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700385 self.assertEqual(Type.INT, prop.type)
Simon Glass2ba98752018-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 Glasse144caf2020-10-03 11:31:27 -0600392 prop3 = node3.props['longbytearray']
Simon Glass2ba98752018-07-06 10:27:24 -0600393 self.assertFalse(isinstance(prop2.value, list))
394 self.assertEqual(4, len(prop2.value))
Simon Glasse144caf2020-10-03 11:31:27 -0600395 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
Simon Glass2ba98752018-07-06 10:27:24 -0600396 prop2.Widen(prop)
397 self.assertTrue(isinstance(prop2.value, list))
398 self.assertEqual(9, len(prop2.value))
Simon Glasse144caf2020-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 Glass2ba98752018-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 Glassca044942021-07-28 19:23:10 -0600425 # Widen an array of ints with an int (should do nothing)
426 prop = self.node.props['intarray']
427 prop2 = node2.props['intarray']
428 self.assertEqual(Type.INT, prop.type)
429 self.assertEqual(3, len(prop.value))
430 prop.Widen(prop2)
431 self.assertEqual(Type.INT, prop.type)
432 self.assertEqual(3, len(prop.value))
433
Simon Glass116adec2018-07-06 10:27:38 -0600434 def testAdd(self):
435 """Test adding properties"""
436 self.fdt.pack()
437 # This function should automatically expand the device tree
438 self.node.AddZeroProp('one')
439 self.node.AddZeroProp('two')
440 self.node.AddZeroProp('three')
Simon Glassfa80c252018-09-14 04:57:13 -0600441 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600442
443 # Updating existing properties should be OK, since the device-tree size
444 # does not change
445 self.fdt.pack()
446 self.node.SetInt('one', 1)
447 self.node.SetInt('two', 2)
448 self.node.SetInt('three', 3)
Simon Glassfa80c252018-09-14 04:57:13 -0600449 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600450
451 # This should fail since it would need to increase the device-tree size
Simon Glassfa80c252018-09-14 04:57:13 -0600452 self.node.AddZeroProp('four')
Simon Glass116adec2018-07-06 10:27:38 -0600453 with self.assertRaises(libfdt.FdtException) as e:
Simon Glassfa80c252018-09-14 04:57:13 -0600454 self.dtb.Sync(auto_resize=False)
Simon Glass116adec2018-07-06 10:27:38 -0600455 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
Simon Glass64349612018-09-14 04:57:16 -0600456 self.dtb.Sync(auto_resize=True)
Simon Glass116adec2018-07-06 10:27:38 -0600457
Simon Glass64349612018-09-14 04:57:16 -0600458 def testAddMore(self):
459 """Test various other methods for adding and setting properties"""
460 self.node.AddZeroProp('one')
461 self.dtb.Sync(auto_resize=True)
462 data = self.fdt.getprop(self.node.Offset(), 'one')
463 self.assertEqual(0, fdt32_to_cpu(data))
464
465 self.node.SetInt('one', 1)
466 self.dtb.Sync(auto_resize=False)
467 data = self.fdt.getprop(self.node.Offset(), 'one')
468 self.assertEqual(1, fdt32_to_cpu(data))
469
Simon Glass6eb99322021-01-06 21:35:18 -0700470 val = 1234
471 self.node.AddInt('integer', val)
472 self.dtb.Sync(auto_resize=True)
473 data = self.fdt.getprop(self.node.Offset(), 'integer')
474 self.assertEqual(val, fdt32_to_cpu(data))
475
Simon Glass64349612018-09-14 04:57:16 -0600476 val = '123' + chr(0) + '456'
477 self.node.AddString('string', val)
478 self.dtb.Sync(auto_resize=True)
479 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassf6b64812019-05-17 22:00:36 -0600480 self.assertEqual(tools.ToBytes(val) + b'\0', data)
Simon Glass64349612018-09-14 04:57:16 -0600481
482 self.fdt.pack()
483 self.node.SetString('string', val + 'x')
484 with self.assertRaises(libfdt.FdtException) as e:
485 self.dtb.Sync(auto_resize=False)
486 self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
487 self.node.SetString('string', val[:-1])
488
489 prop = self.node.props['string']
Simon Glassf6b64812019-05-17 22:00:36 -0600490 prop.SetData(tools.ToBytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600491 self.dtb.Sync(auto_resize=False)
492 data = self.fdt.getprop(self.node.Offset(), 'string')
Simon Glassf6b64812019-05-17 22:00:36 -0600493 self.assertEqual(tools.ToBytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600494
495 self.node.AddEmptyProp('empty', 5)
496 self.dtb.Sync(auto_resize=True)
497 prop = self.node.props['empty']
Simon Glassf6b64812019-05-17 22:00:36 -0600498 prop.SetData(tools.ToBytes(val))
Simon Glass64349612018-09-14 04:57:16 -0600499 self.dtb.Sync(auto_resize=False)
500 data = self.fdt.getprop(self.node.Offset(), 'empty')
Simon Glassf6b64812019-05-17 22:00:36 -0600501 self.assertEqual(tools.ToBytes(val), data)
Simon Glass64349612018-09-14 04:57:16 -0600502
Simon Glassf6b64812019-05-17 22:00:36 -0600503 self.node.SetData('empty', b'123')
504 self.assertEqual(b'123', prop.bytes)
Simon Glass64349612018-09-14 04:57:16 -0600505
Simon Glassc0639172020-07-09 18:39:44 -0600506 # Trying adding a lot of data at once
507 self.node.AddData('data', tools.GetBytes(65, 20000))
508 self.dtb.Sync(auto_resize=True)
509
Simon Glass746aee32018-09-14 04:57:17 -0600510 def testFromData(self):
511 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
512 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
513
514 self.node.AddEmptyProp('empty', 5)
515 self.dtb.Sync(auto_resize=True)
516 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
517
Simon Glassd9dad102019-07-20 12:23:37 -0600518 def testMissingSetInt(self):
519 """Test handling of a missing property with SetInt"""
520 with self.assertRaises(ValueError) as e:
521 self.node.SetInt('one', 1)
522 self.assertIn("node '/spl-test': Missing property 'one'",
523 str(e.exception))
524
525 def testMissingSetData(self):
526 """Test handling of a missing property with SetData"""
527 with self.assertRaises(ValueError) as e:
528 self.node.SetData('one', b'data')
529 self.assertIn("node '/spl-test': Missing property 'one'",
530 str(e.exception))
531
532 def testMissingSetString(self):
533 """Test handling of a missing property with SetString"""
534 with self.assertRaises(ValueError) as e:
535 self.node.SetString('one', 1)
536 self.assertIn("node '/spl-test': Missing property 'one'",
537 str(e.exception))
538
Simon Glassf6e02492019-07-20 12:24:08 -0600539 def testGetFilename(self):
540 """Test the dtb filename can be provided"""
541 self.assertEqual(tools.GetOutputFilename('source.dtb'),
542 self.dtb.GetFilename())
543
Simon Glass2ba98752018-07-06 10:27:24 -0600544
Simon Glass2a2d91d2018-07-06 10:27:28 -0600545class TestFdtUtil(unittest.TestCase):
546 """Tests for the fdt_util module
547
548 This module will likely be mostly replaced at some point, once upstream
549 libfdt has better Python support. For now, this provides tests for current
550 functionality.
551 """
552 @classmethod
553 def setUpClass(cls):
554 tools.PrepareOutputDir(None)
555
Simon Glasse0e62752018-10-01 21:12:41 -0600556 @classmethod
557 def tearDownClass(cls):
558 tools.FinaliseOutputDir()
559
Simon Glass2a2d91d2018-07-06 10:27:28 -0600560 def setUp(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700561 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600562 self.node = self.dtb.GetNode('/spl-test')
563
564 def testGetInt(self):
565 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
566 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
567
568 with self.assertRaises(ValueError) as e:
569 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
570 self.assertIn("property 'intarray' has list value: expecting a single "
571 'integer', str(e.exception))
572
573 def testGetString(self):
574 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
575 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
576 'test'))
577
578 with self.assertRaises(ValueError) as e:
579 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
580 self.assertIn("property 'stringarray' has list value: expecting a "
581 'single string', str(e.exception))
582
583 def testGetBool(self):
584 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
585 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
586 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
587 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
588
Simon Glass3af8e492018-07-17 13:25:40 -0600589 def testGetByte(self):
590 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
591 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
592
593 with self.assertRaises(ValueError) as e:
594 fdt_util.GetByte(self.node, 'longbytearray')
595 self.assertIn("property 'longbytearray' has list value: expecting a "
596 'single byte', str(e.exception))
597
598 with self.assertRaises(ValueError) as e:
599 fdt_util.GetByte(self.node, 'intval')
600 self.assertIn("property 'intval' has length 4, expecting 1",
601 str(e.exception))
602
Simon Glass94a7c602018-07-17 13:25:46 -0600603 def testGetPhandleList(self):
Simon Glassdff51a52021-02-03 06:00:56 -0700604 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
Simon Glass94a7c602018-07-17 13:25:46 -0600605 node = dtb.GetNode('/phandle-source2')
606 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
607 node = dtb.GetNode('/phandle-source')
608 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
609 fdt_util.GetPhandleList(node, 'clocks'))
610 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
611
Simon Glass53af22a2018-07-17 13:25:32 -0600612 def testGetDataType(self):
613 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
614 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
615 str))
616 with self.assertRaises(ValueError) as e:
617 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
618 bool))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600619 def testFdtCellsToCpu(self):
620 val = self.node.props['intarray'].value
621 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
622 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
623
Simon Glassdff51a52021-02-03 06:00:56 -0700624 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
Simon Glasse66d3182019-05-17 22:00:40 -0600625 node1 = dtb2.GetNode('/test1')
626 val = node1.props['reg'].value
Simon Glass2a2d91d2018-07-06 10:27:28 -0600627 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
628
Simon Glasse66d3182019-05-17 22:00:40 -0600629 node2 = dtb2.GetNode('/test2')
630 val = node2.props['reg'].value
631 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
632 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
633 2))
634 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
635
Simon Glass2a2d91d2018-07-06 10:27:28 -0600636 def testEnsureCompiled(self):
Simon Glassa004f292019-07-20 12:23:49 -0600637 """Test a degenerate case of this function (file already compiled)"""
Simon Glassdff51a52021-02-03 06:00:56 -0700638 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
Simon Glass2a2d91d2018-07-06 10:27:28 -0600639 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
640
Simon Glassa004f292019-07-20 12:23:49 -0600641 def testEnsureCompiledTmpdir(self):
642 """Test providing a temporary directory"""
643 try:
644 old_outdir = tools.outdir
645 tools.outdir= None
646 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
Simon Glassdff51a52021-02-03 06:00:56 -0700647 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
Simon Glassa004f292019-07-20 12:23:49 -0600648 tmpdir)
649 self.assertEqual(tmpdir, os.path.dirname(dtb))
650 shutil.rmtree(tmpdir)
651 finally:
652 tools.outdir= old_outdir
653
Simon Glass2a2d91d2018-07-06 10:27:28 -0600654
655def RunTestCoverage():
656 """Run the tests and check that we get 100% coverage"""
657 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
658 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
659
660
Simon Glass2ba98752018-07-06 10:27:24 -0600661def RunTests(args):
662 """Run all the test we have for the fdt model
663
664 Args:
665 args: List of positional args provided to fdt. This can hold a test
666 name to execute (as in 'fdt -t testFdt', for example)
667 """
668 result = unittest.TestResult()
669 sys.argv = [sys.argv[0]]
670 test_name = args and args[0] or None
Simon Glass2a2d91d2018-07-06 10:27:28 -0600671 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass2ba98752018-07-06 10:27:24 -0600672 if test_name:
673 try:
674 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
675 except AttributeError:
676 continue
677 else:
678 suite = unittest.TestLoader().loadTestsFromTestCase(module)
679 suite.run(result)
680
Simon Glass90a81322019-05-17 22:00:31 -0600681 print(result)
Simon Glass2ba98752018-07-06 10:27:24 -0600682 for _, err in result.errors:
Simon Glass90a81322019-05-17 22:00:31 -0600683 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600684 for _, err in result.failures:
Simon Glass90a81322019-05-17 22:00:31 -0600685 print(err)
Simon Glass2ba98752018-07-06 10:27:24 -0600686
687if __name__ != '__main__':
688 sys.exit(1)
689
690parser = OptionParser()
Simon Glass2a2d91d2018-07-06 10:27:28 -0600691parser.add_option('-B', '--build-dir', type='string', default='b',
692 help='Directory containing the build output')
Simon Glass11ae93e2018-10-01 21:12:47 -0600693parser.add_option('-P', '--processes', type=int,
694 help='set number of processes to use for running tests')
Simon Glass2ba98752018-07-06 10:27:24 -0600695parser.add_option('-t', '--test', action='store_true', dest='test',
696 default=False, help='run tests')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600697parser.add_option('-T', '--test-coverage', action='store_true',
698 default=False, help='run tests and check for 100% coverage')
Simon Glass2ba98752018-07-06 10:27:24 -0600699(options, args) = parser.parse_args()
700
701# Run our meagre tests
702if options.test:
703 RunTests(args)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600704elif options.test_coverage:
705 RunTestCoverage()