dtoc: Make GetArgs() more flexible
[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 dtoc.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 testGetFdtRaw(self):
123         """Tetst that we can access the raw device-tree data"""
124         self.assertTrue(isinstance(self.dtb.GetContents(), bytes))
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 testGetArgs(self):
656         node = self.dtb.GetNode('/orig-node')
657         self.assertEqual(['message'], fdt_util.GetArgs(self.node, 'stringval'))
658         self.assertEqual(
659             ['multi-word', 'message'],
660             fdt_util.GetArgs(self.node, 'stringarray'))
661         self.assertEqual([], fdt_util.GetArgs(self.node, 'boolval'))
662         self.assertEqual(['-n first', 'second', '-p', '123,456', '-x'],
663                          fdt_util.GetArgs(node, 'args'))
664         self.assertEqual(['a space', 'there'],
665                          fdt_util.GetArgs(node, 'args2'))
666         self.assertEqual(['-n', 'first', 'second', '-p', '123,456', '-x'],
667                          fdt_util.GetArgs(node, 'args3'))
668         with self.assertRaises(ValueError) as exc:
669             fdt_util.GetArgs(self.node, 'missing')
670         self.assertIn(
671             "Node '/spl-test': Expected property 'missing'",
672             str(exc.exception))
673
674     def testGetBool(self):
675         self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
676         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
677         self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
678         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
679
680     def testGetByte(self):
681         self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
682         self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
683
684         with self.assertRaises(ValueError) as e:
685             fdt_util.GetByte(self.node, 'longbytearray')
686         self.assertIn("property 'longbytearray' has list value: expecting a "
687                       'single byte', str(e.exception))
688
689         with self.assertRaises(ValueError) as e:
690             fdt_util.GetByte(self.node, 'intval')
691         self.assertIn("property 'intval' has length 4, expecting 1",
692                       str(e.exception))
693
694     def testGetBytes(self):
695         self.assertEqual(bytes([5]), fdt_util.GetBytes(self.node, 'byteval', 1))
696         self.assertEqual(None, fdt_util.GetBytes(self.node, 'missing', 3))
697         self.assertEqual(
698             bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3,  bytes([3])))
699
700         with self.assertRaises(ValueError) as e:
701             fdt_util.GetBytes(self.node, 'longbytearray', 7)
702         self.assertIn(
703             "Node 'spl-test' property 'longbytearray' has length 9, expecting 7",
704              str(e.exception))
705
706         self.assertEqual(
707             bytes([0, 0, 0, 1]), fdt_util.GetBytes(self.node, 'intval', 4))
708         self.assertEqual(
709             bytes([3]), fdt_util.GetBytes(self.node, 'missing', 3,  bytes([3])))
710
711     def testGetPhandleList(self):
712         dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts'))
713         node = dtb.GetNode('/phandle-source2')
714         self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
715         node = dtb.GetNode('/phandle-source')
716         self.assertEqual([1, 2, 11, 3, 12, 13, 1],
717                          fdt_util.GetPhandleList(node, 'clocks'))
718         self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
719
720     def testGetDataType(self):
721         self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
722         self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
723                                                          str))
724         with self.assertRaises(ValueError) as e:
725             self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
726                                                      bool))
727     def testFdtCellsToCpu(self):
728         val = self.node.props['intarray'].value
729         self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
730         self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
731
732         dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts'))
733         node1 = dtb2.GetNode('/test1')
734         val = node1.props['reg'].value
735         self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
736
737         node2 = dtb2.GetNode('/test2')
738         val = node2.props['reg'].value
739         self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
740         self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
741                                                                        2))
742         self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
743
744     def testEnsureCompiled(self):
745         """Test a degenerate case of this function (file already compiled)"""
746         dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'))
747         self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
748
749     def testEnsureCompiledTmpdir(self):
750         """Test providing a temporary directory"""
751         try:
752             old_outdir = tools.outdir
753             tools.outdir= None
754             tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
755             dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'),
756                                           tmpdir)
757             self.assertEqual(tmpdir, os.path.dirname(dtb))
758             shutil.rmtree(tmpdir)
759         finally:
760             tools.outdir= old_outdir
761
762
763 def RunTestCoverage():
764     """Run the tests and check that we get 100% coverage"""
765     test_util.run_test_coverage('tools/dtoc/test_fdt.py', None,
766             ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
767
768
769 def RunTests(args):
770     """Run all the test we have for the fdt model
771
772     Args:
773         args: List of positional args provided to fdt. This can hold a test
774             name to execute (as in 'fdt -t testFdt', for example)
775     """
776     result = unittest.TestResult()
777     sys.argv = [sys.argv[0]]
778     test_name = args and args[0] or None
779     for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
780         if test_name:
781             try:
782                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
783             except AttributeError:
784                 continue
785         else:
786             suite = unittest.TestLoader().loadTestsFromTestCase(module)
787         suite.run(result)
788
789     print(result)
790     for _, err in result.errors:
791         print(err)
792     for _, err in result.failures:
793         print(err)
794
795 if __name__ != '__main__':
796     sys.exit(1)
797
798 parser = OptionParser()
799 parser.add_option('-B', '--build-dir', type='string', default='b',
800         help='Directory containing the build output')
801 parser.add_option('-P', '--processes', type=int,
802                   help='set number of processes to use for running tests')
803 parser.add_option('-t', '--test', action='store_true', dest='test',
804                   default=False, help='run tests')
805 parser.add_option('-T', '--test-coverage', action='store_true',
806                 default=False, help='run tests and check for 100% coverage')
807 (options, args) = parser.parse_args()
808
809 # Run our meagre tests
810 if options.test:
811     RunTests(args)
812 elif options.test_coverage:
813     RunTestCoverage()