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