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