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