Merge tag 'u-boot-rockchip-20200501' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/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 sys.path.insert(1, os.path.join(our_path, '..'))
18
19 from dtoc import fdt
20 from dtoc import fdt_util
21 from dtoc.fdt_util import fdt32_to_cpu
22 from fdt import TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, BytesToValue
23 import libfdt
24 from patman import command
25 from patman import test_util
26 from patman import tools
27
28 def _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, [tools.ToChar(x) for x in data]
50
51
52 class 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
132 class 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
213 class 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(fdt.TYPE_BOOL, prop.type)
253         self.assertEqual(True, prop.value)
254
255         prop = self._ConvertProp('intval')
256         self.assertEqual(fdt.TYPE_INT, prop.type)
257         self.assertEqual(1, fdt32_to_cpu(prop.value))
258
259         prop = self._ConvertProp('intarray')
260         self.assertEqual(fdt.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(fdt.TYPE_BYTE, prop.type)
266         self.assertEqual(5, ord(prop.value))
267
268         prop = self._ConvertProp('longbytearray')
269         self.assertEqual(fdt.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(fdt.TYPE_STRING, prop.type)
275         self.assertEqual('message', prop.value)
276
277         prop = self._ConvertProp('stringarray')
278         self.assertEqual(fdt.TYPE_STRING, prop.type)
279         self.assertEqual(['multi-word', 'message'], prop.value)
280
281         prop = self._ConvertProp('notstring')
282         self.assertEqual(fdt.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(fdt.TYPE_BOOL))
289         self.assertEqual(chr(0), fdt.Prop.GetEmpty(fdt.TYPE_BYTE))
290         self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(fdt.TYPE_INT))
291         self.assertEqual('', fdt.Prop.GetEmpty(fdt.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         prop = self.node.props['intval']
302
303         # No action
304         prop2 = node2.props['intval']
305         prop.Widen(prop2)
306         self.assertEqual(fdt.TYPE_INT, prop.type)
307         self.assertEqual(1, fdt32_to_cpu(prop.value))
308
309         # Convert singla value to array
310         prop2 = self.node.props['intarray']
311         prop.Widen(prop2)
312         self.assertEqual(fdt.TYPE_INT, prop.type)
313         self.assertTrue(isinstance(prop.value, list))
314
315         # A 4-byte array looks like a single integer. When widened by a longer
316         # byte array, it should turn into an array.
317         prop = self.node.props['longbytearray']
318         prop2 = node2.props['longbytearray']
319         self.assertFalse(isinstance(prop2.value, list))
320         self.assertEqual(4, len(prop2.value))
321         prop2.Widen(prop)
322         self.assertTrue(isinstance(prop2.value, list))
323         self.assertEqual(9, len(prop2.value))
324
325         # Similarly for a string array
326         prop = self.node.props['stringval']
327         prop2 = node2.props['stringarray']
328         self.assertFalse(isinstance(prop.value, list))
329         self.assertEqual(7, len(prop.value))
330         prop.Widen(prop2)
331         self.assertTrue(isinstance(prop.value, list))
332         self.assertEqual(3, len(prop.value))
333
334         # Enlarging an existing array
335         prop = self.node.props['stringarray']
336         prop2 = node2.props['stringarray']
337         self.assertTrue(isinstance(prop.value, list))
338         self.assertEqual(2, len(prop.value))
339         prop.Widen(prop2)
340         self.assertTrue(isinstance(prop.value, list))
341         self.assertEqual(3, len(prop.value))
342
343     def testAdd(self):
344         """Test adding properties"""
345         self.fdt.pack()
346         # This function should automatically expand the device tree
347         self.node.AddZeroProp('one')
348         self.node.AddZeroProp('two')
349         self.node.AddZeroProp('three')
350         self.dtb.Sync(auto_resize=True)
351
352         # Updating existing properties should be OK, since the device-tree size
353         # does not change
354         self.fdt.pack()
355         self.node.SetInt('one', 1)
356         self.node.SetInt('two', 2)
357         self.node.SetInt('three', 3)
358         self.dtb.Sync(auto_resize=False)
359
360         # This should fail since it would need to increase the device-tree size
361         self.node.AddZeroProp('four')
362         with self.assertRaises(libfdt.FdtException) as e:
363             self.dtb.Sync(auto_resize=False)
364         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
365         self.dtb.Sync(auto_resize=True)
366
367     def testAddNode(self):
368         self.fdt.pack()
369         self.node.AddSubnode('subnode')
370         with self.assertRaises(libfdt.FdtException) as e:
371             self.dtb.Sync(auto_resize=False)
372         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
373
374         self.dtb.Sync(auto_resize=True)
375         offset = self.fdt.path_offset('/spl-test/subnode')
376         self.assertTrue(offset > 0)
377
378     def testAddMore(self):
379         """Test various other methods for adding and setting properties"""
380         self.node.AddZeroProp('one')
381         self.dtb.Sync(auto_resize=True)
382         data = self.fdt.getprop(self.node.Offset(), 'one')
383         self.assertEqual(0, fdt32_to_cpu(data))
384
385         self.node.SetInt('one', 1)
386         self.dtb.Sync(auto_resize=False)
387         data = self.fdt.getprop(self.node.Offset(), 'one')
388         self.assertEqual(1, fdt32_to_cpu(data))
389
390         val = '123' + chr(0) + '456'
391         self.node.AddString('string', val)
392         self.dtb.Sync(auto_resize=True)
393         data = self.fdt.getprop(self.node.Offset(), 'string')
394         self.assertEqual(tools.ToBytes(val) + b'\0', data)
395
396         self.fdt.pack()
397         self.node.SetString('string', val + 'x')
398         with self.assertRaises(libfdt.FdtException) as e:
399             self.dtb.Sync(auto_resize=False)
400         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
401         self.node.SetString('string', val[:-1])
402
403         prop = self.node.props['string']
404         prop.SetData(tools.ToBytes(val))
405         self.dtb.Sync(auto_resize=False)
406         data = self.fdt.getprop(self.node.Offset(), 'string')
407         self.assertEqual(tools.ToBytes(val), data)
408
409         self.node.AddEmptyProp('empty', 5)
410         self.dtb.Sync(auto_resize=True)
411         prop = self.node.props['empty']
412         prop.SetData(tools.ToBytes(val))
413         self.dtb.Sync(auto_resize=False)
414         data = self.fdt.getprop(self.node.Offset(), 'empty')
415         self.assertEqual(tools.ToBytes(val), data)
416
417         self.node.SetData('empty', b'123')
418         self.assertEqual(b'123', prop.bytes)
419
420     def testFromData(self):
421         dtb2 = fdt.Fdt.FromData(self.dtb.GetContents())
422         self.assertEqual(dtb2.GetContents(), self.dtb.GetContents())
423
424         self.node.AddEmptyProp('empty', 5)
425         self.dtb.Sync(auto_resize=True)
426         self.assertTrue(dtb2.GetContents() != self.dtb.GetContents())
427
428     def testMissingSetInt(self):
429         """Test handling of a missing property with SetInt"""
430         with self.assertRaises(ValueError) as e:
431             self.node.SetInt('one', 1)
432         self.assertIn("node '/spl-test': Missing property 'one'",
433                       str(e.exception))
434
435     def testMissingSetData(self):
436         """Test handling of a missing property with SetData"""
437         with self.assertRaises(ValueError) as e:
438             self.node.SetData('one', b'data')
439         self.assertIn("node '/spl-test': Missing property 'one'",
440                       str(e.exception))
441
442     def testMissingSetString(self):
443         """Test handling of a missing property with SetString"""
444         with self.assertRaises(ValueError) as e:
445             self.node.SetString('one', 1)
446         self.assertIn("node '/spl-test': Missing property 'one'",
447                       str(e.exception))
448
449     def testGetFilename(self):
450         """Test the dtb filename can be provided"""
451         self.assertEqual(tools.GetOutputFilename('source.dtb'),
452                          self.dtb.GetFilename())
453
454
455 class TestFdtUtil(unittest.TestCase):
456     """Tests for the fdt_util module
457
458     This module will likely be mostly replaced at some point, once upstream
459     libfdt has better Python support. For now, this provides tests for current
460     functionality.
461     """
462     @classmethod
463     def setUpClass(cls):
464         tools.PrepareOutputDir(None)
465
466     @classmethod
467     def tearDownClass(cls):
468         tools.FinaliseOutputDir()
469
470     def setUp(self):
471         self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts')
472         self.node = self.dtb.GetNode('/spl-test')
473
474     def testGetInt(self):
475         self.assertEqual(1, fdt_util.GetInt(self.node, 'intval'))
476         self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3))
477
478         with self.assertRaises(ValueError) as e:
479             self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray'))
480         self.assertIn("property 'intarray' has list value: expecting a single "
481                       'integer', str(e.exception))
482
483     def testGetString(self):
484         self.assertEqual('message', fdt_util.GetString(self.node, 'stringval'))
485         self.assertEqual('test', fdt_util.GetString(self.node, 'missing',
486                                                     'test'))
487
488         with self.assertRaises(ValueError) as e:
489             self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray'))
490         self.assertIn("property 'stringarray' has list value: expecting a "
491                       'single string', str(e.exception))
492
493     def testGetBool(self):
494         self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval'))
495         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing'))
496         self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
497         self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
498
499     def testGetByte(self):
500         self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
501         self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
502
503         with self.assertRaises(ValueError) as e:
504             fdt_util.GetByte(self.node, 'longbytearray')
505         self.assertIn("property 'longbytearray' has list value: expecting a "
506                       'single byte', str(e.exception))
507
508         with self.assertRaises(ValueError) as e:
509             fdt_util.GetByte(self.node, 'intval')
510         self.assertIn("property 'intval' has length 4, expecting 1",
511                       str(e.exception))
512
513     def testGetPhandleList(self):
514         dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
515         node = dtb.GetNode('/phandle-source2')
516         self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
517         node = dtb.GetNode('/phandle-source')
518         self.assertEqual([1, 2, 11, 3, 12, 13, 1],
519                          fdt_util.GetPhandleList(node, 'clocks'))
520         self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
521
522     def testGetDataType(self):
523         self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
524         self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
525                                                          str))
526         with self.assertRaises(ValueError) as e:
527             self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
528                                                      bool))
529     def testFdtCellsToCpu(self):
530         val = self.node.props['intarray'].value
531         self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))
532         self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
533
534         dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts')
535         node1 = dtb2.GetNode('/test1')
536         val = node1.props['reg'].value
537         self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2))
538
539         node2 = dtb2.GetNode('/test2')
540         val = node2.props['reg'].value
541         self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2))
542         self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:],
543                                                                        2))
544         self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1))
545
546     def testEnsureCompiled(self):
547         """Test a degenerate case of this function (file already compiled)"""
548         dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts')
549         self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
550
551     def testEnsureCompiledTmpdir(self):
552         """Test providing a temporary directory"""
553         try:
554             old_outdir = tools.outdir
555             tools.outdir= None
556             tmpdir = tempfile.mkdtemp(prefix='test_fdt.')
557             dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts',
558                                           tmpdir)
559             self.assertEqual(tmpdir, os.path.dirname(dtb))
560             shutil.rmtree(tmpdir)
561         finally:
562             tools.outdir= old_outdir
563
564
565 def RunTestCoverage():
566     """Run the tests and check that we get 100% coverage"""
567     test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None,
568             ['tools/patman/*.py', '*test_fdt.py'], options.build_dir)
569
570
571 def RunTests(args):
572     """Run all the test we have for the fdt model
573
574     Args:
575         args: List of positional args provided to fdt. This can hold a test
576             name to execute (as in 'fdt -t testFdt', for example)
577     """
578     result = unittest.TestResult()
579     sys.argv = [sys.argv[0]]
580     test_name = args and args[0] or None
581     for module in (TestFdt, TestNode, TestProp, TestFdtUtil):
582         if test_name:
583             try:
584                 suite = unittest.TestLoader().loadTestsFromName(test_name, module)
585             except AttributeError:
586                 continue
587         else:
588             suite = unittest.TestLoader().loadTestsFromTestCase(module)
589         suite.run(result)
590
591     print(result)
592     for _, err in result.errors:
593         print(err)
594     for _, err in result.failures:
595         print(err)
596
597 if __name__ != '__main__':
598     sys.exit(1)
599
600 parser = OptionParser()
601 parser.add_option('-B', '--build-dir', type='string', default='b',
602         help='Directory containing the build output')
603 parser.add_option('-P', '--processes', type=int,
604                   help='set number of processes to use for running tests')
605 parser.add_option('-t', '--test', action='store_true', dest='test',
606                   default=False, help='run tests')
607 parser.add_option('-T', '--test-coverage', action='store_true',
608                 default=False, help='run tests and check for 100% coverage')
609 (options, args) = parser.parse_args()
610
611 # Run our meagre tests
612 if options.test:
613     RunTests(args)
614 elif options.test_coverage:
615     RunTestCoverage()