1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0+
3
4"""
5Tests for the Fdt module
6Copyright (c) 2018 Google, Inc
7Written by Simon Glass <sjg@chromium.org>
8"""
9
10from argparse import ArgumentParser
11import os
12import shutil
13import sys
14import tempfile
15import unittest
16
17# Bring in the patman libraries
18our_path = os.path.dirname(os.path.realpath(__file__))
19sys.path.insert(1, os.path.join(our_path, '..'))
20
21# Bring in the libfdt module
22sys.path.insert(2, 'scripts/dtc/pylibfdt')
23sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt'))
24sys.path.insert(2, os.path.join(our_path,
25                '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
26
27#pylint: disable=wrong-import-position
28from dtoc import fdt
29from dtoc import fdt_util
30from dtoc.fdt_util import fdt32_to_cpu, fdt64_to_cpu
31from dtoc.fdt import Type, BytesToValue
32import libfdt
33from u_boot_pylib import test_util
34from u_boot_pylib import tools
35
36#pylint: disable=protected-access
37
38def _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
61def 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
73class 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
157class 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
310class 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
620class 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        old_outdir = tools.outdir
788        try:
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
810def 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
821def 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
841def 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
864if __name__ == '__main__':
865    sys.exit(main())
866