3316757e61e3ede85355587edc4dd9c290c10838
[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 __future__ import print_function
8
9 from optparse import OptionParser
10 import glob
11 import os
12 import shutil
13 import sys
14 import tempfile
15 import unittest
16
17 # Bring in the patman libraries
18 our_path = os.path.dirname(os.path.realpath(__file__))
19 for dirname in ['../patman', '..']:
20     sys.path.insert(0, os.path.join(our_path, dirname))
21
22 import command
23 import fdt
24 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, BytesToValue
25 import fdt_util
26 from fdt_util import fdt32_to_cpu
27 import libfdt
28 import test_util
29 import tools
30
31 def _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
55 class 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
135 class 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
216 class 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
458 class 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
568 def 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
574 def 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
600 if __name__ != '__main__':
601     sys.exit(1)
602
603 parser = OptionParser()
604 parser.add_option('-B', '--build-dir', type='string', default='b',
605         help='Directory containing the build output')
606 parser.add_option('-P', '--processes', type=int,
607                   help='set number of processes to use for running tests')
608 parser.add_option('-t', '--test', action='store_true', dest='test',
609                   default=False, help='run tests')
610 parser.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
615 if options.test:
616     RunTests(args)
617 elif options.test_coverage:
618     RunTestCoverage()