blob: d44e4dd84231f3a07bcfbc646f3a1ca7a0c736c3 [file] [log] [blame]
Simon Glass2ba98752018-07-06 10:27:24 -06001#!/usr/bin/python
2# SPDX-License-Identifier: GPL-2.0+
3# Copyright (c) 2018 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6
7from optparse import OptionParser
8import glob
9import os
10import sys
11import unittest
12
13# Bring in the patman libraries
14our_path = os.path.dirname(os.path.realpath(__file__))
15for dirname in ['../patman', '..']:
16 sys.path.insert(0, os.path.join(our_path, dirname))
17
18import command
19import fdt
20from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL
Simon Glass2a2d91d2018-07-06 10:27:28 -060021import fdt_util
Simon Glass2ba98752018-07-06 10:27:24 -060022from fdt_util import fdt32_to_cpu
23import libfdt
24import test_util
25import tools
26
27class TestFdt(unittest.TestCase):
28 """Tests for the Fdt module
29
30 This includes unit tests for some functions and functional tests for the fdt
31 module.
32 """
33 @classmethod
34 def setUpClass(cls):
35 tools.PrepareOutputDir(None)
36
37 @classmethod
38 def tearDownClass(cls):
39 tools._FinaliseForTest()
40
41 def setUp(self):
42 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
43
44 def testFdt(self):
45 """Test that we can open an Fdt"""
46 self.dtb.Scan()
47 root = self.dtb.GetRoot()
48 self.assertTrue(isinstance(root, fdt.Node))
49
50 def testGetNode(self):
51 """Test the GetNode() method"""
52 node = self.dtb.GetNode('/spl-test')
53 self.assertTrue(isinstance(node, fdt.Node))
54 node = self.dtb.GetNode('/i2c@0/pmic@9')
55 self.assertTrue(isinstance(node, fdt.Node))
56 self.assertEqual('pmic@9', node.name)
Simon Glass2a2d91d2018-07-06 10:27:28 -060057 self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing'))
Simon Glass2ba98752018-07-06 10:27:24 -060058
59 def testFlush(self):
60 """Check that we can flush the device tree out to its file"""
61 fname = self.dtb._fname
62 with open(fname) as fd:
63 data = fd.read()
64 os.remove(fname)
65 with self.assertRaises(IOError):
66 open(fname)
67 self.dtb.Flush()
68 with open(fname) as fd:
69 data = fd.read()
70
71 def testPack(self):
72 """Test that packing a device tree works"""
73 self.dtb.Pack()
74
75 def testGetFdt(self):
76 """Tetst that we can access the raw device-tree data"""
Simon Glass96066242018-07-06 10:27:27 -060077 self.assertTrue(isinstance(self.dtb.GetContents(), bytearray))
Simon Glass2ba98752018-07-06 10:27:24 -060078
79 def testGetProps(self):
80 """Tests obtaining a list of properties"""
81 node = self.dtb.GetNode('/spl-test')
82 props = self.dtb.GetProps(node)
83 self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible',
Simon Glass2a2d91d2018-07-06 10:27:28 -060084 'intarray', 'intval', 'longbytearray', 'notstring',
Simon Glass2ba98752018-07-06 10:27:24 -060085 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'],
86 sorted(props.keys()))
87
88 def testCheckError(self):
89 """Tests the ChecKError() function"""
90 with self.assertRaises(ValueError) as e:
Simon Glass2a2d91d2018-07-06 10:27:28 -060091 fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
Simon Glass2ba98752018-07-06 10:27:24 -060092 self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
93
94
95class TestNode(unittest.TestCase):
96 """Test operation of the Node class"""
97
98 @classmethod
99 def setUpClass(cls):
100 tools.PrepareOutputDir(None)
101
102 @classmethod
103 def tearDownClass(cls):
104 tools._FinaliseForTest()
105
106 def setUp(self):
107 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
108 self.node = self.dtb.GetNode('/spl-test')
109
110 def testOffset(self):
111 """Tests that we can obtain the offset of a node"""
112 self.assertTrue(self.node.Offset() > 0)
113
114 def testDelete(self):
115 """Tests that we can delete a property"""
116 node2 = self.dtb.GetNode('/spl-test2')
117 offset1 = node2.Offset()
118 self.node.DeleteProp('intval')
119 offset2 = node2.Offset()
120 self.assertTrue(offset2 < offset1)
121 self.node.DeleteProp('intarray')
122 offset3 = node2.Offset()
123 self.assertTrue(offset3 < offset2)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600124 with self.assertRaises(libfdt.FdtException):
125 self.node.DeleteProp('missing')
Simon Glass2ba98752018-07-06 10:27:24 -0600126
127 def testFindNode(self):
128 """Tests that we can find a node using the _FindNode() functoin"""
129 node = self.dtb.GetRoot()._FindNode('i2c@0')
130 self.assertEqual('i2c@0', node.name)
131 subnode = node._FindNode('pmic@9')
132 self.assertEqual('pmic@9', subnode.name)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600133 self.assertEqual(None, node._FindNode('missing'))
Simon Glass2ba98752018-07-06 10:27:24 -0600134
135
136class TestProp(unittest.TestCase):
137 """Test operation of the Prop class"""
138
139 @classmethod
140 def setUpClass(cls):
141 tools.PrepareOutputDir(None)
142
143 @classmethod
144 def tearDownClass(cls):
145 tools._FinaliseForTest()
146
147 def setUp(self):
148 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
149 self.node = self.dtb.GetNode('/spl-test')
150 self.fdt = self.dtb.GetFdtObj()
151
Simon Glass2a2d91d2018-07-06 10:27:28 -0600152 def testPhandle(self):
153 dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
154 node = dtb.GetNode('/phandle-source')
155
156 def _ConvertProp(self, prop_name):
157 """Helper function to look up a property in self.node and return it
158
159 Args:
160 Property name to find
161
162 Return fdt.Prop object for this property
163 """
164 p = self.fdt.get_property(self.node.Offset(), prop_name)
165 return fdt.Prop(self.node, -1, prop_name, p)
166
167 def testMakeProp(self):
168 """Test we can convert all the the types that are supported"""
169 prop = self._ConvertProp('boolval')
170 self.assertEqual(fdt.TYPE_BOOL, prop.type)
171 self.assertEqual(True, prop.value)
172
173 prop = self._ConvertProp('intval')
174 self.assertEqual(fdt.TYPE_INT, prop.type)
175 self.assertEqual(1, fdt32_to_cpu(prop.value))
176
177 prop = self._ConvertProp('intarray')
178 self.assertEqual(fdt.TYPE_INT, prop.type)
179 val = [fdt32_to_cpu(val) for val in prop.value]
180 self.assertEqual([2, 3, 4], val)
181
182 prop = self._ConvertProp('byteval')
183 self.assertEqual(fdt.TYPE_BYTE, prop.type)
184 self.assertEqual(5, ord(prop.value))
185
186 prop = self._ConvertProp('longbytearray')
187 self.assertEqual(fdt.TYPE_BYTE, prop.type)
188 val = [ord(val) for val in prop.value]
189 self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val)
190
191 prop = self._ConvertProp('stringval')
192 self.assertEqual(fdt.TYPE_STRING, prop.type)
193 self.assertEqual('message', prop.value)
194
195 prop = self._ConvertProp('stringarray')
196 self.assertEqual(fdt.TYPE_STRING, prop.type)
197 self.assertEqual(['multi-word', 'message'], prop.value)
198
199 prop = self._ConvertProp('notstring')
200 self.assertEqual(fdt.TYPE_BYTE, prop.type)
201 val = [ord(val) for val in prop.value]
202 self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val)
203
Simon Glass2ba98752018-07-06 10:27:24 -0600204 def testGetEmpty(self):
205 """Tests the GetEmpty() function for the various supported types"""
206 self.assertEqual(True, fdt.Prop.GetEmpty(fdt.TYPE_BOOL))
207 self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
208 self.assertEqual(chr(0) * 4, fdt.Prop.GetEmpty(fdt.TYPE_INT))
209 self.assertEqual('', fdt.Prop.GetEmpty(fdt.TYPE_STRING))
210
211 def testGetOffset(self):
212 """Test we can get the offset of a property"""
213 prop = self.node.props['longbytearray']
214
215 # Add 12, which is sizeof(struct fdt_property), to get to start of data
216 offset = prop.GetOffset() + 12
Simon Glass96066242018-07-06 10:27:27 -0600217 data = self.dtb.GetContents()[offset:offset + len(prop.value)]
Simon Glass2ba98752018-07-06 10:27:24 -0600218 bytes = [chr(x) for x in data]
219 self.assertEqual(bytes, prop.value)
220
221 def testWiden(self):
222 """Test widening of values"""
223 node2 = self.dtb.GetNode('/spl-test2')
224 prop = self.node.props['intval']
225
226 # No action
227 prop2 = node2.props['intval']
228 prop.Widen(prop2)
229 self.assertEqual(fdt.TYPE_INT, prop.type)
230 self.assertEqual(1, fdt32_to_cpu(prop.value))
231
232 # Convert singla value to array
233 prop2 = self.node.props['intarray']
234 prop.Widen(prop2)
235 self.assertEqual(fdt.TYPE_INT, prop.type)
236 self.assertTrue(isinstance(prop.value, list))
237
238 # A 4-byte array looks like a single integer. When widened by a longer
239 # byte array, it should turn into an array.
240 prop = self.node.props['longbytearray']
241 prop2 = node2.props['longbytearray']
242 self.assertFalse(isinstance(prop2.value, list))
243 self.assertEqual(4, len(prop2.value))
244 prop2.Widen(prop)
245 self.assertTrue(isinstance(prop2.value, list))
246 self.assertEqual(9, len(prop2.value))
247
248 # Similarly for a string array
249 prop = self.node.props['stringval']
250 prop2 = node2.props['stringarray']
251 self.assertFalse(isinstance(prop.value, list))
252 self.assertEqual(7, len(prop.value))
253 prop.Widen(prop2)
254 self.assertTrue(isinstance(prop.value, list))
255 self.assertEqual(3, len(prop.value))
256
257 # Enlarging an existing array
258 prop = self.node.props['stringarray']
259 prop2 = node2.props['stringarray']
260 self.assertTrue(isinstance(prop.value, list))
261 self.assertEqual(2, len(prop.value))
262 prop.Widen(prop2)
263 self.assertTrue(isinstance(prop.value, list))
264 self.assertEqual(3, len(prop.value))
265
266
Simon Glass2a2d91d2018-07-06 10:27:28 -0600267class TestFdtUtil(unittest.TestCase):
268 """Tests for the fdt_util module
269
270 This module will likely be mostly replaced at some point, once upstream
271 libfdt has better Python support. For now, this provides tests for current
272 functionality.
273 """
274 @classmethod
275 def setUpClass(cls):
276 tools.PrepareOutputDir(None)
277
278 def setUp(self):
279 self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
280 self.node = self.dtb.GetNode('/spl-test')
281
282 def testGetInt(self):
283 self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
284 self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
285
286 with self.assertRaises(ValueError) as e:
287 self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
288 self.assertIn("property 'intarray' has list value: expecting a single "
289 'integer', str(e.exception))
290
291 def testGetString(self):
292 self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
293 self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
294 'test'))
295
296 with self.assertRaises(ValueError) as e:
297 self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
298 self.assertIn("property 'stringarray' has list value: expecting a "
299 'single string', str(e.exception))
300
301 def testGetBool(self):
302 self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
303 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
304 self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
305 self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
306
307 def testFdtCellsToCpu(self):
308 val = self.node.props['intarray'].value
309 self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
310 self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
311
312 dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
313 node2 = dtb2.GetNode('/test1')
314 val = node2.props['reg'].value
315 self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
316
317 def testEnsureCompiled(self):
318 """Test a degenerate case of this function"""
319 dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
320 self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
321
322 def testGetPlainBytes(self):
323 self.assertEqual('fred', fdt_util.get_plain_bytes('fred'))
324
325
326def RunTestCoverage():
327 """Run the tests and check that we get 100% coverage"""
328 test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
329 ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
330
331
Simon Glass2ba98752018-07-06 10:27:24 -0600332def RunTests(args):
333 """Run all the test we have for the fdt model
334
335 Args:
336 args: List of positional args provided to fdt. This can hold a test
337 name to execute (as in 'fdt -t testFdt', for example)
338 """
339 result = unittest.TestResult()
340 sys.argv = [sys.argv[0]]
341 test_name = args and args[0] or None
Simon Glass2a2d91d2018-07-06 10:27:28 -0600342 for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
Simon Glass2ba98752018-07-06 10:27:24 -0600343 if test_name:
344 try:
345 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
346 except AttributeError:
347 continue
348 else:
349 suite = unittest.TestLoader().loadTestsFromTestCase(module)
350 suite.run(result)
351
352 print result
353 for _, err in result.errors:
354 print err
355 for _, err in result.failures:
356 print err
357
358if __name__ != '__main__':
359 sys.exit(1)
360
361parser = OptionParser()
Simon Glass2a2d91d2018-07-06 10:27:28 -0600362parser.add_option('-B', '--build-dir', type='string', default='b',
363 help='Directory containing the build output')
Simon Glass2ba98752018-07-06 10:27:24 -0600364parser.add_option('-t', '--test', action='store_true', dest='test',
365 default=False, help='run tests')
Simon Glass2a2d91d2018-07-06 10:27:28 -0600366parser.add_option('-T', '--test-coverage', action='store_true',
367 default=False, help='run tests and check for 100% coverage')
Simon Glass2ba98752018-07-06 10:27:24 -0600368(options, args) = parser.parse_args()
369
370# Run our meagre tests
371if options.test:
372 RunTests(args)
Simon Glass2a2d91d2018-07-06 10:27:28 -0600373elif options.test_coverage:
374 RunTestCoverage()