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