]> git.ipfire.org Git - thirdparty/u-boot.git/blob - tools/dtoc/test_fdt.py
Merge branch 'next'
[thirdparty/u-boot.git] / tools / dtoc / test_fdt.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: GPL-2.0+
3
4 """
5 Tests for the Fdt module
6 Copyright (c) 2018 Google, Inc
7 Written by Simon Glass <sjg@chromium.org>
8 """
9
10 from argparse import ArgumentParser
11 import os
12 import shutil
13 import sys
14 import tempfile
15 import unittest
16
17 # Bring in the patman libraries
18 our_path = os.path.dirname(os.path.realpath(__file__))
19 sys.path.insert(1, os.path.join(our_path, '..'))
20
21 # Bring in the libfdt module
22 sys.path.insert(2, 'scripts/dtc/pylibfdt')
23 sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
24 sys.path.insert(2, os.path.join(our_path,
25 '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
26
27 #pylint: disable=wrong-import-position
28 from dtoc import fdt
29 from dtoc import fdt_util
30 from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
31 from dtoc.fdt import Type, BytesToValue
32 import libfdt
33 from u_boot_pylib import test_util
34 from u_boot_pylib import tools
35
36 #pylint: disable=protected-access
37
38 def _get_property_value(dtb, node, prop_name):
39 """Low-level function to get the property value based on its offset
40
41 This looks directly in the device tree at the property's offset to find
42 its value. It is useful as a check that the property is in the correct
43 place.
44
45 Args:
46 node: Node to look in
47 prop_name: Property name to find
48
49 Returns:
50 Tuple:
51 Prop object found
52 Value of property as a string (found using property offset)
53 """
54 prop = node.props[prop_name]
55
56 # Add 12, which is sizeof(struct fdt_property), to get to start of data
57 offset = prop.GetOffset() + 12
58 data = dtb.GetContents()[offset:offset + len(prop.value)]
59 return prop, [chr(x) for x in data]
60
61 def find_dtb_file(dts_fname):
62 """Locate a test file in the test/ directory
63
64 Args:
65 dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts]
66
67 Returns:
68 str: Path to the test filename
69 """
70 return os.path.join('tools/dtoc/test', dts_fname)
71
72
73 class TestFdt(unittest.TestCase):
74 """Tests for the Fdt module
75
76 This includes unit tests for some functions and functional tests for the fdt
77 module.
78 """
79 @classmethod
80 def setUpClass(cls):
81 tools.prepare_output_dir(None)
82
83 @classmethod
84 def tearDownClass(cls):
85 tools.finalise_output_dir()
86
87 def setUp(self):
88 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
89
90 def test_fdt(self):
91 """Test that we can open an Fdt"""
92 self.dtb.Scan()
93 root = self.dtb.GetRoot()
94 self.assertTrue(isinstance(root, fdt.Node))
95
96 def test_get_node(self):
97 """Test the GetNode() method"""
98 node = self.dtb.GetNode('/spl-test')
99 self.assertTrue(isinstance(node, fdt.Node))
100
101 node = self.dtb.GetNode('/i2c@0/pmic@9')
102 self.assertTrue(isinstance(node, fdt.Node))
103 self.assertEqual('pmic@9', node.name)
104 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
105
106 node = self.dtb.GetNode('/')
107 self.assertTrue(isinstance(node, fdt.Node))
108 self.assertEqual(0, node.Offset())
109
110 def test_flush(self):
111 """Check that we can flush the device tree out to its file"""
112 fname = self.dtb._fname
113 with open(fname, 'rb') as inf:
114 inf.read()
115 os.remove(fname)
116 with self.assertRaises(IOError):
117 with open(fname, 'rb'):
118 pass
119 self.dtb.Flush()
120 with open(fname, 'rb') as inf:
121 inf.read()
122
123 def test_pack(self):
124 """Test that packing a device tree works"""
125 self.dtb.Pack()
126
127 def test_get_fdt_raw(self):
128 """Tetst that we can access the raw device-tree data"""
129 self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
130
131 def test_get_props(self):
132 """Tests obtaining a list of properties"""
133 node = self.dtb.GetNode('/spl-test')
134 props = self.dtb.GetProps(node)
135 self.assertEqual(['boolval', 'bootph-all', 'bytearray', 'byteval',
136 'compatible', 'int64val', 'intarray', 'intval',
137 'longbytearray', 'maybe-empty-int', 'notstring',
138 'stringarray', 'stringval', ],
139 sorted(props.keys()))
140
141 def test_check_error(self):
142 """Tests the ChecKError() function"""
143 with self.assertRaises(ValueError) as exc:
144 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
145 self.assertIn('FDT_ERR_NOTFOUND: hello', str(exc.exception))
146
147 def test_get_fdt(self):
148 """Test getting an Fdt object from a node"""
149 node = self.dtb.GetNode('/spl-test')
150 self.assertEqual(self.dtb, node.GetFdt())
151
152 def test_bytes_to_value(self):
153 """Test converting a string list into Python"""
154 self.assertEqual(BytesToValue(b'this\0is\0'),
155 (Type.STRING, ['this', 'is']))
156
157 class TestNode(unittest.TestCase):
158 """Test operation of the Node class"""
159
160 @classmethod
161 def setUpClass(cls):
162 tools.prepare_output_dir(None)
163
164 @classmethod
165 def tearDownClass(cls):
166 tools.finalise_output_dir()
167
168 def setUp(self):
169 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
170 self.node = self.dtb.GetNode('/spl-test')
171 self.fdt = self.dtb.GetFdtObj()
172
173 def test_offset(self):
174 """Tests that we can obtain the offset of a node"""
175 self.assertTrue(self.node.Offset() > 0)
176
177 def test_delete(self):
178 """Tests that we can delete a property"""
179 node2 = self.dtb.GetNode('/spl-test2')
180 offset1 = node2.Offset()
181 self.node.DeleteProp('intval')
182 offset2 = node2.Offset()
183 self.assertTrue(offset2 < offset1)
184 self.node.DeleteProp('intarray')
185 offset3 = node2.Offset()
186 self.assertTrue(offset3 < offset2)
187 with self.assertRaises(libfdt.FdtException):
188 self.node.DeleteProp('missing')
189
190 def test_delete_get_offset(self):
191 """Test that property offset update when properties are deleted"""
192 self.node.DeleteProp('intval')
193 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
194 self.assertEqual(prop.value, value)
195
196 def test_find_node(self):
197 """Tests that we can find a node using the FindNode() functoin"""
198 node = self.dtb.GetRoot().FindNode('i2c@0')
199 self.assertEqual('i2c@0', node.name)
200 subnode = node.FindNode('pmic@9')
201 self.assertEqual('pmic@9', subnode.name)
202 self.assertEqual(None, node.FindNode('missing'))
203
204 def test_refresh_missing_node(self):
205 """Test refreshing offsets when an extra node is present in dtb"""
206 # Delete it from our tables, not the device tree
207 del self.dtb._root.subnodes[-1]
208 with self.assertRaises(ValueError) as exc:
209 self.dtb.Refresh()
210 self.assertIn('Internal error, offset', str(exc.exception))
211
212 def test_refresh_extra_node(self):
213 """Test refreshing offsets when an expected node is missing"""
214 # Delete it from the device tre, not our tables
215 self.fdt.del_node(self.node.Offset())
216 with self.assertRaises(ValueError) as exc:
217 self.dtb.Refresh()
218 self.assertIn('Internal error, node name mismatch '
219 'spl-test != spl-test2', str(exc.exception))
220
221 def test_refresh_missing_prop(self):
222 """Test refreshing offsets when an extra property is present in dtb"""
223 # Delete it from our tables, not the device tree
224 del self.node.props['notstring']
225 with self.assertRaises(ValueError) as exc:
226 self.dtb.Refresh()
227 self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
228 str(exc.exception))
229
230 def test_lookup_phandle(self):
231 """Test looking up a single phandle"""
232 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
233 node = dtb.GetNode('/phandle-source2')
234 prop = node.props['clocks']
235 target = dtb.GetNode('/phandle-target')
236 self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
237
238 def test_add_node_space(self):
239 """Test adding a single node when out of space"""
240 self.fdt.pack()
241 self.node.AddSubnode('subnode')
242 with self.assertRaises(libfdt.FdtException) as exc:
243 self.dtb.Sync(auto_resize=False)
244 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
245
246 self.dtb.Sync(auto_resize=True)
247 offset = self.fdt.path_offset('/spl-test/subnode')
248 self.assertTrue(offset > 0)
249
250 def test_add_nodes(self):
251 """Test adding various subnode and properies"""
252 node = self.dtb.GetNode('/i2c@0')
253
254 # Add one more node next to the pmic one
255 sn1 = node.AddSubnode('node-one')
256 sn1.AddInt('integer-a', 12)
257 sn1.AddInt('integer-b', 23)
258
259 # Sync so that everything is clean
260 self.dtb.Sync(auto_resize=True)
261
262 # Add two subnodes next to pmic and node-one
263 sn2 = node.AddSubnode('node-two')
264 sn2.AddInt('integer-2a', 34)
265 sn2.AddInt('integer-2b', 45)
266
267 sn3 = node.AddSubnode('node-three')
268 sn3.AddInt('integer-3', 123)
269
270 # Add a property to the node after i2c@0 to check that this is not
271 # disturbed by adding a subnode to i2c@0
272 orig_node = self.dtb.GetNode('/orig-node')
273 orig_node.AddInt('integer-4', 456)
274
275 # Add a property to the pmic node to check that pmic properties are not
276 # disturbed
277 pmic = self.dtb.GetNode('/i2c@0/pmic@9')
278 pmic.AddInt('integer-5', 567)
279
280 self.dtb.Sync(auto_resize=True)
281
282 def test_add_one_node(self):
283 """Testing deleting and adding a subnode before syncing"""
284 subnode = self.node.AddSubnode('subnode')
285 self.node.AddSubnode('subnode2')
286 self.dtb.Sync(auto_resize=True)
287
288 # Delete a node and add a new one
289 subnode.Delete()
290 self.node.AddSubnode('subnode3')
291 self.dtb.Sync()
292
293 def test_refresh_name_mismatch(self):
294 """Test name mismatch when syncing nodes and properties"""
295 self.node.AddInt('integer-a', 12)
296
297 wrong_offset = self.dtb.GetNode('/i2c@0')._offset
298 self.node._offset = wrong_offset
299 with self.assertRaises(ValueError) as exc:
300 self.dtb.Sync()
301 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
302 str(exc.exception))
303
304 with self.assertRaises(ValueError) as exc:
305 self.node.Refresh(wrong_offset)
306 self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
307 str(exc.exception))
308
309
310 class TestProp(unittest.TestCase):
311 """Test operation of the Prop class"""
312
313 @classmethod
314 def setUpClass(cls):
315 tools.prepare_output_dir(None)
316
317 @classmethod
318 def tearDownClass(cls):
319 tools.finalise_output_dir()
320
321 def setUp(self):
322 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
323 self.node = self.dtb.GetNode('/spl-test')
324 self.fdt = self.dtb.GetFdtObj()
325
326 def test_missing_node(self):
327 """Test GetNode() when the node is missing"""
328 self.assertEqual(None, self.dtb.GetNode('missing'))
329
330 def test_phandle(self):
331 """Test GetNode() on a phandle"""
332 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
333 node = dtb.GetNode('/phandle-source2')
334 prop = node.props['clocks']
335 self.assertTrue(fdt32_to_cpu(prop.value) > 0)
336
337 def _convert_prop(self, prop_name):
338 """Helper function to look up a property in self.node and return it
339
340 Args:
341 str: Property name to find
342
343 Returns:
344 fdt.Prop: object for this property
345 """
346 prop = self.fdt.getprop(self.node.Offset(), prop_name)
347 return fdt.Prop(self.node, -1, prop_name, prop)
348
349 def test_make_prop(self):
350 """Test we can convert all the the types that are supported"""
351 prop = self._convert_prop('boolval')
352 self.assertEqual(Type.BOOL, prop.type)
353 self.assertEqual(True, prop.value)
354
355 prop = self._convert_prop('intval')
356 self.assertEqual(Type.INT, prop.type)
357 self.assertEqual(1, fdt32_to_cpu(prop.value))
358
359 prop = self._convert_prop('int64val')
360 self.assertEqual(Type.INT, prop.type)
361 self.assertEqual(0x123456789abcdef0, fdt64_to_cpu(prop.value))
362
363 prop = self._convert_prop('intarray')
364 self.assertEqual(Type.INT, prop.type)
365 val = [fdt32_to_cpu(val) for val in prop.value]
366 self.assertEqual([2, 3, 4], val)
367
368 prop = self._convert_prop('byteval')
369 self.assertEqual(Type.BYTE, prop.type)
370 self.assertEqual(5, ord(prop.value))
371
372 prop = self._convert_prop('longbytearray')
373 self.assertEqual(Type.BYTE, prop.type)
374 val = [ord(val) for val in prop.value]
375 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
376
377 prop = self._convert_prop('stringval')
378 self.assertEqual(Type.STRING, prop.type)
379 self.assertEqual('message', prop.value)
380
381 prop = self._convert_prop('stringarray')
382 self.assertEqual(Type.STRING, prop.type)
383 self.assertEqual(['multi-word', 'message'], prop.value)
384
385 prop = self._convert_prop('notstring')
386 self.assertEqual(Type.BYTE, prop.type)
387 val = [ord(val) for val in prop.value]
388 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
389
390 def test_get_empty(self):
391 """Tests the GetEmpty() function for the various supported types"""
392 self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL))
393 self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE))
394 self.assertEqual(tools.get_bytes(0, 4), fdt.Prop.GetEmpty(Type.INT))
395 self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING))
396
397 def test_get_offset(self):
398 """Test we can get the offset of a property"""
399 prop, value = _get_property_value(self.dtb, self.node, 'longbytearray')
400 self.assertEqual(prop.value, value)
401
402 def test_widen(self):
403 """Test widening of values"""
404 node2 = self.dtb.GetNode('/spl-test2')
405 node3 = self.dtb.GetNode('/spl-test3')
406 prop = self.node.props['intval']
407
408 # No action
409 prop2 = node2.props['intval']
410 prop.Widen(prop2)
411 self.assertEqual(Type.INT, prop.type)
412 self.assertEqual(1, fdt32_to_cpu(prop.value))
413
414 # Convert single value to array
415 prop2 = self.node.props['intarray']
416 prop.Widen(prop2)
417 self.assertEqual(Type.INT, prop.type)
418 self.assertTrue(isinstance(prop.value, list))
419
420 # A 4-byte array looks like a single integer. When widened by a longer
421 # byte array, it should turn into an array.
422 prop = self.node.props['longbytearray']
423 prop2 = node2.props['longbytearray']
424 prop3 = node3.props['longbytearray']
425 self.assertFalse(isinstance(prop2.value, list))
426 self.assertEqual(4, len(prop2.value))
427 self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value)
428 prop2.Widen(prop)
429 self.assertTrue(isinstance(prop2.value, list))
430 self.assertEqual(9, len(prop2.value))
431 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0',
432 '\0', '\0', '\0', '\0'], prop2.value)
433 prop3.Widen(prop)
434 self.assertTrue(isinstance(prop3.value, list))
435 self.assertEqual(9, len(prop3.value))
436 self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d',
437 '\x0e', '\x0f', '\x10', '\0'], prop3.value)
438
439 def test_widen_more(self):
440 """More tests of widening values"""
441 node2 = self.dtb.GetNode('/spl-test2')
442 node3 = self.dtb.GetNode('/spl-test3')
443 prop = self.node.props['intval']
444
445 # Test widening a single string into a string array
446 prop = self.node.props['stringval']
447 prop2 = node2.props['stringarray']
448 self.assertFalse(isinstance(prop.value, list))
449 self.assertEqual(7, len(prop.value))
450 prop.Widen(prop2)
451 self.assertTrue(isinstance(prop.value, list))
452 self.assertEqual(3, len(prop.value))
453
454 # Enlarging an existing array
455 prop = self.node.props['stringarray']
456 prop2 = node2.props['stringarray']
457 self.assertTrue(isinstance(prop.value, list))
458 self.assertEqual(2, len(prop.value))
459 prop.Widen(prop2)
460 self.assertTrue(isinstance(prop.value, list))
461 self.assertEqual(3, len(prop.value))
462
463 # Widen an array of ints with an int (should do nothing)
464 prop = self.node.props['intarray']
465 prop2 = node2.props['intval']
466 self.assertEqual(Type.INT, prop.type)
467 self.assertEqual(3, len(prop.value))
468 prop.Widen(prop2)
469 self.assertEqual(Type.INT, prop.type)
470 self.assertEqual(3, len(prop.value))
471
472 # Widen an empty bool to an int
473 prop = self.node.props['maybe-empty-int']
474 prop3 = node3.props['maybe-empty-int']
475 self.assertEqual(Type.BOOL, prop.type)
476 self.assertEqual(True, prop.value)
477 self.assertEqual(Type.INT, prop3.type)
478 self.assertFalse(isinstance(prop.value, list))
479 self.assertEqual(4, len(prop3.value))
480 prop.Widen(prop3)
481 self.assertEqual(Type.INT, prop.type)
482 self.assertTrue(isinstance(prop.value, list))
483 self.assertEqual(1, len(prop.value))
484
485 def test_add(self):
486 """Test adding properties"""
487 self.fdt.pack()
488 # This function should automatically expand the device tree
489 self.node.AddZeroProp('one')
490 self.node.AddZeroProp('two')
491 self.node.AddZeroProp('three')
492 self.dtb.Sync(auto_resize=True)
493
494 # Updating existing properties should be OK, since the device-tree size
495 # does not change
496 self.fdt.pack()
497 self.node.SetInt('one', 1)
498 self.node.SetInt('two', 2)
499 self.node.SetInt('three', 3)
500 self.dtb.Sync(auto_resize=False)
501
502 # This should fail since it would need to increase the device-tree size
503 self.node.AddZeroProp('four')
504 with self.assertRaises(libfdt.FdtException) as exc:
505 self.dtb.Sync(auto_resize=False)
506 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
507 self.dtb.Sync(auto_resize=True)
508
509 def test_add_more(self):
510 """Test various other methods for adding and setting properties"""
511 self.node.AddZeroProp('one')
512 self.dtb.Sync(auto_resize=True)
513 data = self.fdt.getprop(self.node.Offset(), 'one')
514 self.assertEqual(0, fdt32_to_cpu(data))
515
516 self.node.SetInt('one', 1)
517 self.dtb.Sync(auto_resize=False)
518 data = self.fdt.getprop(self.node.Offset(), 'one')
519 self.assertEqual(1, fdt32_to_cpu(data))
520
521 val = 1234
522 self.node.AddInt('integer', val)
523 self.dtb.Sync(auto_resize=True)
524 data = self.fdt.getprop(self.node.Offset(), 'integer')
525 self.assertEqual(val, fdt32_to_cpu(data))
526
527 val = '123' + chr(0) + '456'
528 self.node.AddString('string', val)
529 self.dtb.Sync(auto_resize=True)
530 data = self.fdt.getprop(self.node.Offset(), 'string')
531 self.assertEqual(tools.to_bytes(val) + b'\0', data)
532
533 self.fdt.pack()
534 self.node.SetString('string', val + 'x')
535 with self.assertRaises(libfdt.FdtException) as exc:
536 self.dtb.Sync(auto_resize=False)
537 self.assertIn('FDT_ERR_NOSPACE', str(exc.exception))
538 self.node.SetString('string', val[:-1])
539
540 prop = self.node.props['string']
541 prop.SetData(tools.to_bytes(val))
542 self.dtb.Sync(auto_resize=False)
543 data = self.fdt.getprop(self.node.Offset(), 'string')
544 self.assertEqual(tools.to_bytes(val), data)
545
546 self.node.AddEmptyProp('empty', 5)
547 self.dtb.Sync(auto_resize=True)
548 prop = self.node.props['empty']
549 prop.SetData(tools.to_bytes(val))
550 self.dtb.Sync(auto_resize=False)
551 data = self.fdt.getprop(self.node.Offset(), 'empty')
552 self.assertEqual(tools.to_bytes(val), data)
553
554 self.node.SetData('empty', b'123')
555 self.assertEqual(b'123', prop.bytes)
556
557 # Trying adding a lot of data at once
558 self.node.AddData('data', tools.get_bytes(65, 20000))
559 self.dtb.Sync(auto_resize=True)
560
561 def test_string_list(self):
562 """Test adding string-list property to a node"""
563 val = ['123', '456']
564 self.node.AddStringList('stringlist', val)
565 self.dtb.Sync(auto_resize=True)
566 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
567 self.assertEqual(b'123\x00456\0', data)
568
569 val = []
570 self.node.AddStringList('stringlist', val)
571 self.dtb.Sync(auto_resize=True)
572 data = self.fdt.getprop(self.node.Offset(), 'stringlist')
573 self.assertEqual(b'', data)
574
575 def test_delete_node(self):
576 """Test deleting a node"""
577 old_offset = self.fdt.path_offset('/spl-test')
578 self.assertGreater(old_offset, 0)
579 self.node.Delete()
580 self.dtb.Sync()
581 new_offset = self.fdt.path_offset('/spl-test', libfdt.QUIET_NOTFOUND)
582 self.assertEqual(-libfdt.NOTFOUND, new_offset)
583
584 def test_from_data(self):
585 """Test creating an FDT from data"""
586 dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
587 self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
588
589 self.node.AddEmptyProp('empty', 5)
590 self.dtb.Sync(auto_resize=True)
591 self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
592
593 def test_missing_set_int(self):
594 """Test handling of a missing property with SetInt"""
595 with self.assertRaises(ValueError) as exc:
596 self.node.SetInt('one', 1)
597 self.assertIn("node '/spl-test': Missing property 'one'",
598 str(exc.exception))
599
600 def test_missing_set_data(self):
601 """Test handling of a missing property with SetData"""
602 with self.assertRaises(ValueError) as exc:
603 self.node.SetData('one', b'data')
604 self.assertIn("node '/spl-test': Missing property 'one'",
605 str(exc.exception))
606
607 def test_missing_set_string(self):
608 """Test handling of a missing property with SetString"""
609 with self.assertRaises(ValueError) as exc:
610 self.node.SetString('one', 1)
611 self.assertIn("node '/spl-test': Missing property 'one'",
612 str(exc.exception))
613
614 def test_get_filename(self):
615 """Test the dtb filename can be provided"""
616 self.assertEqual(tools.get_output_filename('source.dtb'),
617 self.dtb.GetFilename())
618
619
620 class TestFdtUtil(unittest.TestCase):
621 """Tests for the fdt_util module
622
623 This module will likely be mostly replaced at some point, once upstream
624 libfdt has better Python support. For now, this provides tests for current
625 functionality.
626 """
627 @classmethod
628 def setUpClass(cls):
629 tools.prepare_output_dir(None)
630
631 @classmethod
632 def tearDownClass(cls):
633 tools.finalise_output_dir()
634
635 def setUp(self):
636 self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
637 self.node = self.dtb.GetNode('/spl-test')
638
639 def test_get_int(self):
640 """Test getting an int from a node"""
641 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
642 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
643
644 with self.assertRaises(ValueError) as exc:
645 fdt_util.GetInt(self.node, 'intarray')
646 self.assertIn("property 'intarray' has list value: expecting a single "
647 'integer', str(exc.exception))
648
649 def test_get_int64(self):
650 """Test getting a 64-bit int from a node"""
651 self.assertEqual(0x123456789abcdef0,
652 fdt_util.GetInt64(self.node, 'int64val'))
653 self.assertEqual(3, fdt_util.GetInt64(self.node, 'missing', 3))
654
655 with self.assertRaises(ValueError) as exc:
656 fdt_util.GetInt64(self.node, 'intarray')
657 self.assertIn(
658 "property 'intarray' should be a list with 2 items for 64-bit values",
659 str(exc.exception))
660
661 def test_get_string(self):
662 """Test getting a string from a node"""
663 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
664 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
665 'test'))
666 self.assertEqual('', fdt_util.GetString(self.node, 'boolval'))
667
668 with self.assertRaises(ValueError) as exc:
669 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
670 self.assertIn("property 'stringarray' has list value: expecting a "
671 'single string', str(exc.exception))
672
673 def test_get_string_list(self):
674 """Test getting a string list from a node"""
675 self.assertEqual(['message'],
676 fdt_util.GetStringList(self.node, 'stringval'))
677 self.assertEqual(
678 ['multi-word', 'message'],
679 fdt_util.GetStringList(self.node, 'stringarray'))
680 self.assertEqual(['test'],
681 fdt_util.GetStringList(self.node, 'missing', ['test']))
682 self.assertEqual([], fdt_util.GetStringList(self.node, 'boolval'))
683
684 def test_get_args(self):
685 """Test getting arguments from a node"""
686 node = self.dtb.GetNode('/orig-node')
687 self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
688 self.assertEqual(
689 ['multi-word', 'message'],
690 fdt_util.GetArgs(self.node, 'stringarray'))
691 self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
692 self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
693 fdt_util.GetArgs(node, 'args'))
694 self.assertEqual(['a space', 'there'],
695 fdt_util.GetArgs(node, 'args2'))
696 self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
697 fdt_util.GetArgs(node, 'args3'))
698 with self.assertRaises(ValueError) as exc:
699 fdt_util.GetArgs(self.node, 'missing')
700 self.assertIn(
701 "Node '/spl-test': Expected property 'missing'",
702 str(exc.exception))
703
704 def test_get_bool(self):
705 """Test getting a bool from a node"""
706 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
707 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
708 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
709 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
710
711 def test_get_byte(self):
712 """Test getting a byte from a node"""
713 self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
714 self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
715
716 with self.assertRaises(ValueError) as exc:
717 fdt_util.GetByte(self.node, 'longbytearray')
718 self.assertIn("property 'longbytearray' has list value: expecting a "
719 'single byte', str(exc.exception))
720
721 with self.assertRaises(ValueError) as exc:
722 fdt_util.GetByte(self.node, 'intval')
723 self.assertIn("property 'intval' has length 4, expecting 1",
724 str(exc.exception))
725
726 def test_get_bytes(self):
727 """Test getting multiple bytes from a node"""
728 self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
729 self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
730 self.assertEqual(
731 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
732
733 with self.assertRaises(ValueError) as exc:
734 fdt_util.GetBytes(self.node, 'longbytearray', 7)
735 self.assertIn(
736 "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
737 str(exc.exception))
738
739 self.assertEqual(
740 bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
741 self.assertEqual(
742 bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3, bytes([3])))
743
744 def test_get_phandle_list(self):
745 """Test getting a list of phandles from a node"""
746 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
747 node = dtb.GetNode('/phandle-source2')
748 self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
749 node = dtb.GetNode('/phandle-source')
750 self.assertEqual([1, 2, 11, 3, 12, 13, 1],
751 fdt_util.GetPhandleList(node, 'clocks'))
752 self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
753
754 def test_get_data_type(self):
755 """Test getting a value of a particular type from a node"""
756 self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
757 self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
758 str))
759 with self.assertRaises(ValueError):
760 self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
761 bool))
762 def test_fdt_cells_to_cpu(self):
763 """Test getting cells with the correct endianness"""
764 val = self.node.props['intarray'].value
765 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
766 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
767
768 dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
769 node1 = dtb2.GetNode('/test1')
770 val = node1.props['reg'].value
771 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
772
773 node2 = dtb2.GetNode('/test2')
774 val = node2.props['reg'].value
775 self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
776 self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
777 2))
778 self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
779
780 def test_ensure_compiled(self):
781 """Test a degenerate case of this function (file already compiled)"""
782 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
783 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
784
785 def test_ensure_compiled_tmpdir(self):
786 """Test providing a temporary directory"""
787 try:
788 old_outdir = tools.outdir
789 tools.outdir= None
790 tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
791 dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
792 tmpdir)
793 self.assertEqual(tmpdir, os.path.dirname(dtb))
794 shutil.rmtree(tmpdir)
795 finally:
796 tools.outdir= old_outdir
797
798 def test_get_phandle_name_offset(self):
799 val = fdt_util.GetPhandleNameOffset(self.node, 'missing')
800 self.assertIsNone(val)
801
802 dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
803 node = dtb.GetNode('/phandle-source')
804 node, name, offset = fdt_util.GetPhandleNameOffset(node,
805 'phandle-name-offset')
806 self.assertEqual('phandle3-target', node.name)
807 self.assertEqual('fred', name)
808 self.assertEqual(123, offset)
809
810 def run_test_coverage(build_dir):
811 """Run the tests and check that we get 100% coverage
812
813 Args:
814 build_dir (str): Directory containing the build output
815 """
816 test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
817 ['tools/patman/*.py', 'tools/u_boot_pylib/*', '*test_fdt.py'],
818 build_dir)
819
820
821 def run_tests(names, processes):
822 """Run all the test we have for the fdt model
823
824 Args:
825 names (list of str): List of test names provided. Only the first is used
826 processes (int): Number of processes to use (None means as many as there
827 are CPUs on the system. This must be set to 1 when running under
828 the python3-coverage tool
829
830 Returns:
831 int: Return code, 0 on success
832 """
833 test_name = names[0] if names else None
834 result = test_util.run_test_suites(
835 'test_fdt', False, False, False, processes, test_name, None,
836 [TestFdt, TestNode, TestProp, TestFdtUtil])
837
838 return (0 if result.wasSuccessful() else 1)
839
840
841 def main():
842 """Main program for this tool"""
843 parser = ArgumentParser()
844 parser.add_argument('-B', '--build-dir', type=str, default='b',
845 help='Directory containing the build output')
846 parser.add_argument('-P', '--processes', type=int,
847 help='set number of processes to use for running tests')
848 parser.add_argument('-t', '--test', action='store_true', dest='test',
849 default=False, help='run tests')
850 parser.add_argument('-T', '--test-coverage', action='store_true',
851 default=False,
852 help='run tests and check for 100% coverage')
853 parser.add_argument('name', nargs='*')
854 args = parser.parse_args()
855
856 # Run our meagre tests
857 if args.test:
858 ret_code = run_tests(args.name, args.processes)
859 return ret_code
860 if args.test_coverage:
861 run_test_coverage(args.build_dir)
862 return 0
863
864 if __name__ == '__main__':
865 sys.exit(main())